From 160258b6372f0fbebb4f5694bcd4c2684367ada7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20D=C3=B6singer?= Date: Fri, 31 Aug 2007 20:56:15 +0200 Subject: [PATCH] wined3d: Implement mipmap auto generation. --- dlls/d3d9/tests/texture.c | 68 +++++++++++++++++++++++ dlls/d3d9/tests/visual.c | 110 +++++++++++++++++++++++++++++++++++++ dlls/wined3d/basetexture.c | 38 +++++++++++++ dlls/wined3d/device.c | 36 +++++++++++- dlls/wined3d/directx.c | 10 ++++ include/wine/wined3d_gl.h | 8 +++ 6 files changed, 267 insertions(+), 3 deletions(-) diff --git a/dlls/d3d9/tests/texture.c b/dlls/d3d9/tests/texture.c index cb7b781d002..36e91430083 100644 --- a/dlls/d3d9/tests/texture.c +++ b/dlls/d3d9/tests/texture.c @@ -126,6 +126,73 @@ static void test_cube_textures(IDirect3DDevice9 *device_ptr, DWORD caps) test_cube_texture_from_pool(device_ptr, caps, D3DPOOL_SCRATCH, FALSE); } +static void test_mipmap_gen(IDirect3DDevice9 *device) +{ + HRESULT hr; + IDirect3D9 *d3d9; + IDirect3DTexture9 *texture; + IDirect3DSurface9 *surface; + DWORD levels; + D3DSURFACE_DESC desc; + int i; + D3DLOCKED_RECT lr; + + hr = IDirect3DDevice9_GetDirect3D(device, &d3d9); + ok(hr == D3D_OK, "IDirect3DDevice9_GetDirect3D returned %#x\n", hr); + + hr = IDirect3D9_CheckDeviceFormat(d3d9, 0, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, + D3DUSAGE_AUTOGENMIPMAP, + D3DRTYPE_TEXTURE, D3DFMT_X8R8G8B8); + if(FAILED(hr)) + { + skip("No mipmap generation support\n"); + } + + hr = IDirect3DDevice9_CreateTexture(device, 64, 64, 0, D3DUSAGE_AUTOGENMIPMAP, + D3DFMT_X8R8G8B8, D3DPOOL_MANAGED, &texture, 0); + ok(hr == D3D_OK, "IDirect3DDevice9_CreateTexture failed(%08x)\n", hr); + + levels = IDirect3DTexture9_GetLevelCount(texture); + ok(levels == 1, "Got %d levels, expected 1\n", levels); + + for(i = 0; i < 6 /* 64 = 2 ^ 6 */; i++) + { + surface = NULL; + hr = IDirect3DTexture9_GetSurfaceLevel(texture, i, &surface); + ok(hr == (i == 0 ? D3D_OK : D3DERR_INVALIDCALL), + "GetSurfaceLevel on level %d returned %#x\n", i, hr); + if(surface) IDirect3DSurface9_Release(surface); + + hr = IDirect3DTexture9_GetLevelDesc(texture, i, &desc); + ok(hr == (i == 0 ? D3D_OK : D3DERR_INVALIDCALL), + "GetLevelDesc on level %d returned %#x\n", i, hr); + + hr = IDirect3DTexture9_LockRect(texture, i, &lr, NULL, 0); + ok(hr == (i == 0 ? D3D_OK : D3DERR_INVALIDCALL), + "LockRect on level %d returned %#x\n", i, hr); + if(SUCCEEDED(hr)) + { + hr = IDirect3DTexture9_UnlockRect(texture, i); + ok(hr == D3D_OK, "Unlock returned %08x\n", hr); + } + } + IDirect3DTexture9_Release(texture); + + hr = IDirect3DDevice9_CreateTexture(device, 64, 64, 2 /* levels */, D3DUSAGE_AUTOGENMIPMAP, + D3DFMT_X8R8G8B8, D3DPOOL_MANAGED, &texture, 0); + ok(hr == D3DERR_INVALIDCALL, "IDirect3DDevice9_CreateTexture(levels = 2) returned %08x\n", hr); + hr = IDirect3DDevice9_CreateTexture(device, 64, 64, 6 /* levels */, D3DUSAGE_AUTOGENMIPMAP, + D3DFMT_X8R8G8B8, D3DPOOL_MANAGED, &texture, 0); + ok(hr == D3DERR_INVALIDCALL, "IDirect3DDevice9_CreateTexture(levels = 6) returned %08x\n", hr); + + hr = IDirect3DDevice9_CreateTexture(device, 64, 64, 1 /* levels */, D3DUSAGE_AUTOGENMIPMAP, + D3DFMT_X8R8G8B8, D3DPOOL_MANAGED, &texture, 0); + ok(hr == D3D_OK, "IDirect3DDevice9_CreateTexture(levels = 1) returned %08x\n", hr); + levels = IDirect3DTexture9_GetLevelCount(texture); + ok(levels == 1, "Got %d levels, expected 1\n", levels); + IDirect3DTexture9_Release(texture); +} + START_TEST(texture) { D3DCAPS9 caps; @@ -146,4 +213,5 @@ START_TEST(texture) test_texture_stage_states(device_ptr, caps.MaxTextureBlendStages); test_cube_textures(device_ptr, caps.TextureCaps); + test_mipmap_gen(device_ptr); } diff --git a/dlls/d3d9/tests/visual.c b/dlls/d3d9/tests/visual.c index 00506abedb2..2e8fdfed5fc 100644 --- a/dlls/d3d9/tests/visual.c +++ b/dlls/d3d9/tests/visual.c @@ -2822,6 +2822,115 @@ static void x8l8v8u8_test(IDirect3DDevice9 *device) IDirect3DTexture9_Release(texture); } +static void autogen_mipmap_test(IDirect3DDevice9 *device) +{ + HRESULT hr; + IDirect3D9 *d3d; + IDirect3DTexture9 *texture = NULL; + IDirect3DSurface9 *surface; + DWORD color; + const RECT r1 = {256, 256, 512, 512}; + const RECT r2 = {512, 256, 768, 512}; + const RECT r3 = {256, 512, 512, 768}; + const RECT r4 = {512, 512, 768, 768}; + unsigned int x, y; + D3DLOCKED_RECT lr; + memset(&lr, 0, sizeof(lr)); + + IDirect3DDevice9_GetDirect3D(device, &d3d); + if(IDirect3D9_CheckDeviceFormat(d3d, 0, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, + D3DUSAGE_AUTOGENMIPMAP, D3DRTYPE_TEXTURE, D3DFMT_X8R8G8B8) != D3D_OK) { + skip("No autogenmipmap support\n"); + IDirect3D9_Release(d3d); + return; + } + IDirect3D9_Release(d3d); + + hr = IDirect3DDevice9_Clear(device, 0, NULL, D3DCLEAR_TARGET, 0xffffff00, 0.0, 0); + ok(hr == D3D_OK, "IDirect3DDevice9_Clear returned %s\n", DXGetErrorString9(hr)); + + /* Make the mipmap big, so that a smaller mipmap is used + */ + hr = IDirect3DDevice9_CreateTexture(device, 1024, 1024, 0, D3DUSAGE_AUTOGENMIPMAP, + D3DFMT_X8R8G8B8, D3DPOOL_MANAGED, &texture, 0); + ok(hr == D3D_OK, "IDirect3DDevice9_CreateTexture returned %s\n", DXGetErrorString9(hr)); + + hr = IDirect3DTexture9_GetSurfaceLevel(texture, 0, &surface); + ok(hr == D3D_OK, "IDirect3DTexture9_GetSurfaceLevel returned %s\n", DXGetErrorString9(hr)); + hr = IDirect3DSurface9_LockRect(surface, &lr, NULL, 0); + ok(hr == D3D_OK, "IDirect3DSurface9_LockRect returned %s\n", DXGetErrorString9(hr)); + for(y = 0; y < 1024; y++) { + for(x = 0; x < 1024; x++) { + DWORD *dst = (DWORD *) (((BYTE *) lr.pBits) + y * lr.Pitch + x * 4); + POINT pt; + + pt.x = x; + pt.y = y; + if(PtInRect(&r1, pt)) { + *dst = 0xffff0000; + } else if(PtInRect(&r2, pt)) { + *dst = 0xff00ff00; + } else if(PtInRect(&r3, pt)) { + *dst = 0xff0000ff; + } else if(PtInRect(&r4, pt)) { + *dst = 0xff000000; + } else { + *dst = 0xffffffff; + } + } + } + hr = IDirect3DSurface9_UnlockRect(surface); + ok(hr == D3D_OK, "IDirect3DSurface9_UnlockRect returned %s\n", DXGetErrorString9(hr)); + IDirect3DSurface9_Release(surface); + + hr = IDirect3DDevice9_SetTexture(device, 0, (IDirect3DBaseTexture9 *) texture); + ok(hr == D3D_OK, "IDirect3DDevice9_SetTexture returned %s\n", DXGetErrorString9(hr)); + hr = IDirect3DDevice9_SetSamplerState(device, 0, D3DSAMP_MIPFILTER, D3DTEXF_POINT); + ok(hr == D3D_OK, "IDirect3DDevice9_SetSamplerState failed with %s\n", DXGetErrorString9(hr)); + + hr = IDirect3DDevice9_BeginScene(device); + ok(hr == D3D_OK, "IDirect3DDevice9_BeginScene returned %s\n", DXGetErrorString9(hr)); + if(SUCCEEDED(hr)) { + const float quad[] = { + -0.5, -0.5, 0.1, 0.0, 0.0, + -0.5, 0.5, 0.1, 0.0, 1.0, + 0.5, -0.5, 0.1, 1.0, 0.0, + 0.5, 0.5, 0.1, 1.0, 1.0 + }; + + hr = IDirect3DDevice9_SetFVF(device, D3DFVF_XYZ | D3DFVF_TEX1); + ok(hr == D3D_OK, "IDirect3DDevice9_SetFVF returned %s\n", DXGetErrorString9(hr)); + hr = IDirect3DDevice9_DrawPrimitiveUP(device, D3DPT_TRIANGLESTRIP, 2, quad, 5 * sizeof(float)); + ok(hr == D3D_OK, "DrawPrimitiveUP failed (%08x)\n", hr); + hr = IDirect3DDevice9_EndScene(device); + ok(hr == D3D_OK, "IDirect3DDevice9_EndScene returned %s\n", DXGetErrorString9(hr)); + } + hr = IDirect3DDevice9_SetTexture(device, 0, NULL); + ok(hr == D3D_OK, "IDirect3DDevice9_SetTexture returned %s\n", DXGetErrorString9(hr)); + hr = IDirect3DDevice9_SetSamplerState(device, 0, D3DSAMP_MIPFILTER, D3DTEXF_NONE); + ok(hr == D3D_OK, "IDirect3DDevice9_SetSamplerState failed with %s\n", DXGetErrorString9(hr)); + IDirect3DTexture9_Release(texture); + + hr = IDirect3DDevice9_Present(device, NULL, NULL, NULL, NULL); + ok(hr == D3D_OK, "IDirect3DDevice9_Present failed with %s\n", DXGetErrorString9(hr)); + color = getPixelColor(device, 200, 200); + ok(color == 0x00ffffff, "pixel 200/200 has color %08x, expected 0x00ffffff\n", color); + color = getPixelColor(device, 280, 200); + ok(color == 0x000000ff, "pixel 280/200 has color %08x, expected 0x000000ff\n", color); + color = getPixelColor(device, 360, 200); + ok(color == 0x00000000, "pixel 360/200 has color %08x, expected 0x00000000\n", color); + color = getPixelColor(device, 440, 200); + ok(color == 0x00ffffff, "pixel 440/200 has color %08x, expected 0x00ffffff\n", color); + color = getPixelColor(device, 200, 270); + ok(color == 0x00ffffff, "pixel 200/270 has color %08x, expected 0x00ffffff\n", color); + color = getPixelColor(device, 280, 270); + ok(color == 0x00ff0000, "pixel 280/270 has color %08x, expected 0x00ff0000\n", color); + color = getPixelColor(device, 360, 270); + ok(color == 0x0000ff00, "pixel 360/270 has color %08x, expected 0x0000ff00\n", color); + color = getPixelColor(device, 440, 270); + ok(color == 0x00ffffff, "pixel 440/200 has color %08x, expected 0x00ffffff\n", color); +} + START_TEST(visual) { IDirect3DDevice9 *device_ptr; @@ -2899,6 +3008,7 @@ START_TEST(visual) release_buffer_test(device_ptr); float_texture_test(device_ptr); texture_transform_flags_test(device_ptr); + autogen_mipmap_test(device_ptr); if (caps.VertexShaderVersion >= D3DVS_VERSION(2, 0)) diff --git a/dlls/wined3d/basetexture.c b/dlls/wined3d/basetexture.c index 8df0b3e8bc3..a1290f869ce 100644 --- a/dlls/wined3d/basetexture.c +++ b/dlls/wined3d/basetexture.c @@ -161,11 +161,39 @@ DWORD WINAPI IWineD3DBaseTextureImpl_GetLevelCount(IWineD3DBaseTexture *iface) { HRESULT WINAPI IWineD3DBaseTextureImpl_SetAutoGenFilterType(IWineD3DBaseTexture *iface, WINED3DTEXTUREFILTERTYPE FilterType) { IWineD3DBaseTextureImpl *This = (IWineD3DBaseTextureImpl *)iface; + IWineD3DDeviceImpl *device = This->resource.wineD3DDevice; + UINT textureDimensions = IWineD3DBaseTexture_GetTextureDimensions(iface); if (!(This->resource.usage & WINED3DUSAGE_AUTOGENMIPMAP)) { TRACE("(%p) : returning invalid call\n", This); return WINED3DERR_INVALIDCALL; } + if(This->baseTexture.filterType != FilterType) { + /* What about multithreading? Do we want all the context overhead just to set this value? + * Or should we delay the applying until the texture is used for drawing? For now, apply + * immediately. + */ + ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD); + ENTER_GL(); + glBindTexture(textureDimensions, This->baseTexture.textureName); + checkGLcall("glBindTexture"); + switch(FilterType) { + case WINED3DTEXF_NONE: + case WINED3DTEXF_POINT: + glTexParameteri(textureDimensions, GL_GENERATE_MIPMAP_HINT_SGIS, GL_FASTEST); + checkGLcall("glTexParameteri(textureDimensions, GL_GENERATE_MIPMAP_HINT_SGIS, GL_FASTEST)"); + + case WINED3DTEXF_LINEAR: + glTexParameteri(textureDimensions, GL_GENERATE_MIPMAP_HINT_SGIS, GL_NICEST); + checkGLcall("glTexParameteri(textureDimensions, GL_GENERATE_MIPMAP_HINT_SGIS, GL_NICEST)"); + + default: + WARN("Unexpected filter type %d, setting to GL_NICEST\n", FilterType); + glTexParameteri(textureDimensions, GL_GENERATE_MIPMAP_HINT_SGIS, GL_NICEST); + checkGLcall("glTexParameteri(textureDimensions, GL_GENERATE_MIPMAP_HINT_SGIS, GL_NICEST)"); + } + LEAVE_GL(); + } This->baseTexture.filterType = FilterType; TRACE("(%p) :\n", This); return WINED3D_OK; @@ -239,6 +267,16 @@ HRESULT WINAPI IWineD3DBaseTextureImpl_BindTexture(IWineD3DBaseTexture *iface) { This->baseTexture.states[WINED3DTEXSTA_TSSADDRESSW] = WINED3DTADDRESS_WRAP; IWineD3DBaseTexture_SetDirty(iface, TRUE); isNewTexture = TRUE; + + if(This->resource.usage & WINED3DUSAGE_AUTOGENMIPMAP) { + /* This means double binding the texture at creation, but keeps the code simpler all + * in all, and the run-time path free from additional checks + */ + glBindTexture(textureDimensions, This->baseTexture.textureName); + checkGLcall("glBindTexture"); + glTexParameteri(textureDimensions, GL_GENERATE_MIPMAP_SGIS, GL_TRUE); + checkGLcall("glTexParameteri(textureDimensions, GL_GENERATE_MIPMAP_SGIS, GL_TRUE)"); + } } /* Bind the texture */ diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c index 044e2af10b6..7ca99963b12 100644 --- a/dlls/wined3d/device.c +++ b/dlls/wined3d/device.c @@ -882,7 +882,17 @@ static HRESULT WINAPI IWineD3DDeviceImpl_CreateTexture(IWineD3DDevice *iface, U TRACE(" xf(%f) yf(%f)\n", object->pow2scalingFactorX, object->pow2scalingFactorY); /* Calculate levels for mip mapping */ - if (Levels == 0) { + if (Usage & WINED3DUSAGE_AUTOGENMIPMAP) { + if(!GL_SUPPORT(SGIS_GENERATE_MIPMAP)) { + WARN("No mipmap generation support, returning D3DERR_INVALIDCALL\n"); + return WINED3DERR_INVALIDCALL; + } + if(Levels > 1) { + WARN("D3DUSAGE_AUTOGENMIPMAP is set, and level count > 1, returning D3DERR_INVALIDCALL\n"); + return WINED3DERR_INVALIDCALL; + } + object->baseTexture.levels = 1; + } else if (Levels == 0) { TRACE("calculating levels %d\n", object->baseTexture.levels); object->baseTexture.levels++; tmpW = Width; @@ -956,7 +966,17 @@ static HRESULT WINAPI IWineD3DDeviceImpl_CreateVolumeTexture(IWineD3DDevice *ifa object->depth = Depth; /* Calculate levels for mip mapping */ - if (Levels == 0) { + if (Usage & WINED3DUSAGE_AUTOGENMIPMAP) { + if(!GL_SUPPORT(SGIS_GENERATE_MIPMAP)) { + WARN("No mipmap generation support, returning D3DERR_INVALIDCALL\n"); + return WINED3DERR_INVALIDCALL; + } + if(Levels > 1) { + WARN("D3DUSAGE_AUTOGENMIPMAP is set, and level count > 1, returning D3DERR_INVALIDCALL\n"); + return WINED3DERR_INVALIDCALL; + } + Levels = 1; + } else if (Levels == 0) { object->baseTexture.levels++; tmpW = Width; tmpH = Height; @@ -1076,7 +1096,17 @@ static HRESULT WINAPI IWineD3DDeviceImpl_CreateCubeTexture(IWineD3DDevice *iface object->pow2scalingFactor = ((float)EdgeLength) / ((float)pow2EdgeLength); /* Calculate levels for mip mapping */ - if (Levels == 0) { + if (Usage & WINED3DUSAGE_AUTOGENMIPMAP) { + if(!GL_SUPPORT(SGIS_GENERATE_MIPMAP)) { + WARN("No mipmap generation support, returning D3DERR_INVALIDCALL\n"); + return WINED3DERR_INVALIDCALL; + } + if(Levels > 1) { + WARN("D3DUSAGE_AUTOGENMIPMAP is set, and level count > 1, returning D3DERR_INVALIDCALL\n"); + return WINED3DERR_INVALIDCALL; + } + Levels = 1; + } else if (Levels == 0) { object->baseTexture.levels++; tmpW = EdgeLength; while (tmpW > 1) { diff --git a/dlls/wined3d/directx.c b/dlls/wined3d/directx.c index ce2c6cfe1a3..e77a237f8b1 100644 --- a/dlls/wined3d/directx.c +++ b/dlls/wined3d/directx.c @@ -117,6 +117,9 @@ static const struct { {"GL_NV_vertex_program1_1", NV_VERTEX_PROGRAM1_1}, {"GL_NV_vertex_program2", NV_VERTEX_PROGRAM2}, {"GL_NV_vertex_program3", NV_VERTEX_PROGRAM3}, + + /* SGI */ + {"GL_SGIS_generate_mipmap", SGIS_GENERATE_MIPMAP}, }; /********************************************************** @@ -1477,6 +1480,13 @@ static HRESULT WINAPI IWineD3DImpl_CheckDeviceFormat(IWineD3D *iface, UINT Adapt } } + if (Usage & WINED3DUSAGE_AUTOGENMIPMAP) { + if(!GL_SUPPORT(SGIS_GENERATE_MIPMAP)) { + TRACE_(d3d_caps)("[FAILED] - No mipmap generation support\n"); + return WINED3DERR_NOTAVAILABLE; + } + } + /* TODO: Check support against more of the WINED3DUSAGE_QUERY_* constants * See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/directx9_c/IDirect3D9__CheckDeviceFormat.asp * and http://msdn.microsoft.com/library/default.asp?url=/library/en-us/directx9_c/D3DUSAGE_QUERY.asp */ diff --git a/include/wine/wined3d_gl.h b/include/wine/wined3d_gl.h index de53640d44d..40d113dfd8d 100644 --- a/include/wine/wined3d_gl.h +++ b/include/wine/wined3d_gl.h @@ -2833,6 +2833,13 @@ typedef void (WINE_GLAPI * PGLFNGETTEXBUMPPARAMETERFVATIPROC) (GLenum, GLfloat * typedef int (WINE_GLAPI * PGLXFNGETVIDEOSYNCSGIPROC) (unsigned int *); typedef int (WINE_GLAPI * PGLXFNWAITVIDEOSYNCSGIPROC) (int, int, unsigned int *); +/* GL_SGIS_generate_mipmap */ +#ifndef GLX_SGIS_generate_mipmap +#define GL_GENERATE_MIPMAP_SGIS 0x8191 +#define GL_GENERATE_MIPMAP_HINT_SGIS 0x8192 +#define GLX_SGIS_generate_mipmap +#endif + /* GL_VERSION_2_0 */ #ifndef GL_VERSION_2_0 #define GL_VERSION_2_0 1 @@ -3181,6 +3188,7 @@ typedef enum _GL_SupportedExt { APPLE_CLIENT_STORAGE, /* SGI */ SGI_VIDEO_SYNC, + SGIS_GENERATE_MIPMAP, /* WGL extensions */ WGL_ARB_PBUFFER,