Skip to content

Instantly share code, notes, and snippets.

@HybridEidolon
Created November 24, 2015 06:51
Show Gist options
  • Save HybridEidolon/d80373b08f45e43f82e6 to your computer and use it in GitHub Desktop.
Save HybridEidolon/d80373b08f45e43f82e6 to your computer and use it in GitHub Desktop.
PSOBB Wine Mipmaps Patch

PSOBB Wine mipmap patch

For Phantasy Star Online: Blue Burst versions 1.25.10-1.25.13 and the Wine 1.7/1.8 series.

PSOBB tries to write all mipmaps at once on a single LockRect on mip level 0. This is undocumented behavior in Direct3D and PSOBB will clobber the implementation structs if this is not accounted for.

These patches, based on the PlayOnLinux League of Legends patch, do two things:

  • Allocates extra memory on every surface in wined3d (prevents clobbering)
  • Copies mip data outside the level 0 buffer into the actual mip surfaces for the texture, every time texture UnlockRect is called.

Caveats

The game will use considerably more memory for textures. Since PSOBB is so old, this is probably not a problem for most people. VRAM usage is not increased, only system memory for the intermediate maps.

There may be a performance hit for the additional memcpys every time a texture is loaded. My Macbook Air does not seem to have any problems.

Building

Grab the wine source and set up your build environment (I use a vagrant container of i386 Arch Linux for Linux builds), you only need the bare minimum necessary to compile the d3d8 and wined3d libraries, which I don't think is much (not even X11 deps).

Configure and make dlls/d3d8 dlls/wined3d. Copy the generated d3d8.dll.so and wined3d.dll.so without the .so suffix into your PSOBB directory.

Make sure you are using the wine source that matches with your wine distribution. I'm lazy and just pulled the master branch, but YMMV.

Other notes

Turn off Advanced Effects to improve performance dramatically.

Launchers that hack in extra resolutions should work.

diff --git a/dlls/d3d8/texture.c b/dlls/d3d8/texture.c
index 269ce4e..bcb41ae 100644
--- a/dlls/d3d8/texture.c
+++ b/dlls/d3d8/texture.c
@@ -335,6 +335,38 @@ static HRESULT WINAPI d3d8_texture_2d_UnlockRect(IDirect3DTexture8 *iface, UINT
surface_impl = wined3d_resource_get_parent(sub_resource);
hr = IDirect3DSurface8_UnlockRect(&surface_impl->IDirect3DSurface8_iface);
}
+ // PSO hack -- copy mips
+ if (level == 0)
+ {
+ int i = 0;
+ int total_levels = IDirect3DTexture8_GetLevelCount(iface);
+ D3DLOCKED_RECT mipcopy_lr;
+ D3DLOCKED_RECT main_lr;
+ D3DSURFACE_DESC mipsurface_desc;
+ D3DSURFACE_DESC mainsurface_desc;
+ HRESULT rs;
+ UINT size_accum = 0;
+
+ if (total_levels > 1)
+ {
+ IDirect3DTexture8_GetLevelDesc(iface, 0, &mainsurface_desc);
+ rs = IDirect3DSurface8_LockRect(&surface_impl->IDirect3DSurface8_iface, &main_lr, NULL, 0);
+ // Assume that the mip space was written to and copy to mip resources.
+ if (rs == D3D_OK)
+ for (i = 1; i < total_levels && rs == D3D_OK; i++)
+ {
+ rs = IDirect3DTexture8_GetLevelDesc(iface, i, &mipsurface_desc);
+ if (rs == D3D_OK) rs = IDirect3DTexture8_LockRect(iface, i, &mipcopy_lr, NULL, 0);
+ // write the whole mip
+ UINT size = mipsurface_desc.Size;
+ if (rs == D3D_OK) memcpy(mipcopy_lr.pBits, main_lr.pBits + mainsurface_desc.Size + size_accum, size);
+ size_accum += size;
+ // It won't infinitely recur here due to level > 1
+ if (rs == D3D_OK) IDirect3DTexture8_UnlockRect(iface, i);
+ }
+ IDirect3DSurface8_UnlockRect(&surface_impl->IDirect3DSurface8_iface);
+ }
+ }
wined3d_mutex_unlock();
return hr;
diff --git a/dlls/wined3d/resource.c b/dlls/wined3d/resource.c
index d466764..7dff8d8 100644
--- a/dlls/wined3d/resource.c
+++ b/dlls/wined3d/resource.c
@@ -316,8 +316,19 @@ BOOL wined3d_resource_allocate_sysmem(struct wined3d_resource *resource)
void **p;
SIZE_T align = RESOURCE_ALIGNMENT - 1 + sizeof(*p);
void *mem;
+ UINT size = resource->size;
+ int i;
+
+ // PSO hack -- minimize unnecessary allocation
+ // We don't know how many levels were requested, so we'll keep adding halves
+ // this should hit the maximum possible memory needed for all possible mips
+ // PSO always uses power of 2 textures, so this is fine.
+ for (i = size/2; i > 0; i /= 2)
+ {
+ size += i;
+ }
- if (!(mem = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, resource->size + align)))
+ if (!(mem = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size + align)))
return FALSE;
p = (void **)(((ULONG_PTR)mem + align) & ~(RESOURCE_ALIGNMENT - 1)) - 1;
@snaggletooth84
Copy link

Furyhunter, thanks for your work on this patch. I was using your patch for wine series 1.9 and it was working up until 1.9.4 but now i keep getting errors when compiling D3D8, I was wondering if you could point me what should I learn in order to be able to fix these thing in he future by myself, C? Direct3d api? Thanks Again

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment