diff --git a/dll/directx/d3d9/adapter.c b/dll/directx/d3d9/adapter.c index d62d97b3a40..ac11692ef5d 100644 --- a/dll/directx/d3d9/adapter.c +++ b/dll/directx/d3d9/adapter.c @@ -158,7 +158,7 @@ BOOL GetAdapterInfo(LPCSTR lpszDeviceName, D3DADAPTER_IDENTIFIER9* pIdentifier) AdapterIndex = 0; FoundDisplayDevice = FALSE; - while (EnumDisplayDevicesA(NULL, AdapterIndex, &DisplayDevice, 0) == TRUE) + while (EnumDisplayDevicesA(NULL, AdapterIndex, &DisplayDevice, 0) != FALSE) { if (_stricmp(lpszDeviceName, DisplayDevice.DeviceName) == 0) { @@ -176,7 +176,7 @@ BOOL GetAdapterInfo(LPCSTR lpszDeviceName, D3DADAPTER_IDENTIFIER9* pIdentifier) lstrcpynA(pIdentifier->Description, DisplayDevice.DeviceString, MAX_DEVICE_IDENTIFIER_STRING); lstrcpynA(pIdentifier->DeviceName, DisplayDevice.DeviceName, CCHDEVICENAME); - if (GetDriverName(&DisplayDevice, pIdentifier) == TRUE) + if (GetDriverName(&DisplayDevice, pIdentifier) != FALSE) GetDriverVersion(&DisplayDevice, pIdentifier); GetDeviceId(DisplayDevice.DeviceID, pIdentifier); diff --git a/dll/directx/d3d9/d3d9_create.c b/dll/directx/d3d9/d3d9_create.c index b92308f05e1..0d7b3617b6a 100644 --- a/dll/directx/d3d9/d3d9_create.c +++ b/dll/directx/d3d9/d3d9_create.c @@ -190,7 +190,7 @@ static BOOL GetDisplayDeviceInfo(IN OUT LPDIRECT3D9_INT pDirect3D9) D3D9_PrimaryDeviceName[0] = '\0'; AdapterIndex = 0; - while (EnumDisplayDevicesA(NULL, AdapterIndex, &DisplayDevice, 0) == TRUE && + while ((EnumDisplayDevicesA(NULL, AdapterIndex, &DisplayDevice, 0) != FALSE) && pDirect3D9->NumDisplayAdapters < D3D9_INT_MAX_NUM_ADAPTERS) { if ((DisplayDevice.StateFlags & (DISPLAY_DEVICE_DISCONNECT | DISPLAY_DEVICE_MIRRORING_DRIVER)) == 0 && @@ -209,7 +209,7 @@ static BOOL GetDisplayDeviceInfo(IN OUT LPDIRECT3D9_INT pDirect3D9) } AdapterIndex = 0; - while (EnumDisplayDevicesA(NULL, AdapterIndex, &DisplayDevice, 0) == TRUE && + while ((EnumDisplayDevicesA(NULL, AdapterIndex, &DisplayDevice, 0) != FALSE) && pDirect3D9->NumDisplayAdapters < D3D9_INT_MAX_NUM_ADAPTERS) { if ((DisplayDevice.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) != 0 && diff --git a/dll/directx/d3d9/d3d9_haldevice.c b/dll/directx/d3d9/d3d9_haldevice.c index 72f06a46c5d..781573cd8a0 100644 --- a/dll/directx/d3d9/d3d9_haldevice.c +++ b/dll/directx/d3d9/d3d9_haldevice.c @@ -23,7 +23,7 @@ /* IDirect3DDevice9 public interface */ HRESULT WINAPI IDirect3DDevice9HAL_GetTransform(LPDIRECT3DDEVICE9 iface, D3DTRANSFORMSTATETYPE State, D3DMATRIX* pMatrix) { - UNIMPLEMENTED; + UNIMPLEMENTED return D3D_OK; } @@ -37,14 +37,14 @@ HRESULT WINAPI IDirect3DDevice9HAL_GetMaterial(LPDIRECT3DDEVICE9 iface, D3DMATER HRESULT WINAPI IDirect3DDevice9HAL_GetLight(LPDIRECT3DDEVICE9 iface, DWORD Index, D3DLIGHT9* pLight) { - UNIMPLEMENTED; + UNIMPLEMENTED return D3D_OK; } HRESULT WINAPI IDirect3DDevice9HAL_GetLightEnable(LPDIRECT3DDEVICE9 iface, DWORD Index, BOOL* pEnable) { - UNIMPLEMENTED; + UNIMPLEMENTED return D3D_OK; } diff --git a/dll/directx/d3d9/d3d9_helpers.c b/dll/directx/d3d9/d3d9_helpers.c index 6231f4031c4..2747eab96de 100644 --- a/dll/directx/d3d9/d3d9_helpers.c +++ b/dll/directx/d3d9/d3d9_helpers.c @@ -48,6 +48,7 @@ HRESULT SafeFormatString(OUT LPSTR Buffer, IN DWORD BufferSize, IN LPCSTR Format va_start(vargs, FormatString); BytesWritten = _vsnprintf(Buffer, BufferSize-1, FormatString, vargs); + va_end(vargs); if (BytesWritten < BufferSize) return DDERR_GENERIC; diff --git a/dll/directx/d3d9/d3d9_impl.c b/dll/directx/d3d9/d3d9_impl.c index 1f4d73ed518..457b0896e50 100644 --- a/dll/directx/d3d9/d3d9_impl.c +++ b/dll/directx/d3d9/d3d9_impl.c @@ -412,8 +412,8 @@ static HRESULT WINAPI IDirect3D9Impl_CheckDeviceType(LPDIRECT3D9 iface, UINT Ada return D3DERR_INVALIDCALL; } - if (BackBufferFormat == D3DFMT_UNKNOWN && - Windowed == TRUE) + if ((BackBufferFormat == D3DFMT_UNKNOWN) && + (Windowed != FALSE)) { BackBufferFormat = DisplayFormat; } @@ -595,7 +595,7 @@ static HRESULT WINAPI IDirect3D9Impl_CheckDeviceFormat(LPDIRECT3D9 iface, UINT A } pDriverCaps = &This->DisplayAdapters[Adapter].DriverCaps; - if ((Usage & D3DUSAGE_DYNAMIC) != 0 && bIsTextureRType == TRUE) + if (((Usage & D3DUSAGE_DYNAMIC) != 0) && (bIsTextureRType != FALSE)) { if ((pDriverCaps->DriverCaps9.Caps2 & D3DCAPS2_DYNAMICTEXTURES) == 0) { diff --git a/dll/directx/d3d9/d3d9_private.h b/dll/directx/d3d9/d3d9_private.h index 774b319abcf..1b11bf58d9a 100644 --- a/dll/directx/d3d9/d3d9_private.h +++ b/dll/directx/d3d9/d3d9_private.h @@ -11,216 +11,12 @@ #include "d3d9_common.h" #include #include "d3d9_callbacks.h" -// #include "d3d9types.h" #define D3D9_INT_MAX_NUM_ADAPTERS 12 #define D3D9_INT_D3DCAPS8_VALID 1 #define D3D9_INT_D3DCAPS9_VALID 2 -typedef enum D3DQUERYTYPE { - D3DQUERYTYPE_VCACHE = 4, - D3DQUERYTYPE_ResourceManager = 5, - D3DQUERYTYPE_VERTEXSTATS = 6, - D3DQUERYTYPE_EVENT = 8, - D3DQUERYTYPE_OCCLUSION = 9, - D3DQUERYTYPE_TIMESTAMP = 10, - D3DQUERYTYPE_TIMESTAMPDISJOINT = 11, - D3DQUERYTYPE_TIMESTAMPFREQ = 12, - D3DQUERYTYPE_PIPELINETIMINGS = 13, - D3DQUERYTYPE_INTERFACETIMINGS = 14, - D3DQUERYTYPE_VERTEXTIMINGS = 15, - D3DQUERYTYPE_PIXELTIMINGS = 16, - D3DQUERYTYPE_BANDWIDTHTIMINGS = 17, - D3DQUERYTYPE_CACHEUTILIZATION = 18, - D3DQUERYTYPE_MEMORYPRESSURE = 19 -} D3DQUERYTYPE, *LPD3DQUERYTYPE; - -typedef enum _D3DFORMAT { - D3DFMT_UNKNOWN = 0, - - D3DFMT_R8G8B8 = 20, - D3DFMT_A8R8G8B8 = 21, - D3DFMT_X8R8G8B8 = 22, - D3DFMT_R5G6B5 = 23, - D3DFMT_X1R5G5B5 = 24, - D3DFMT_A1R5G5B5 = 25, - D3DFMT_A4R4G4B4 = 26, - D3DFMT_R3G3B2 = 27, - D3DFMT_A8 = 28, - D3DFMT_A8R3G3B2 = 29, - D3DFMT_X4R4G4B4 = 30, - D3DFMT_A2B10G10R10 = 31, - D3DFMT_A8B8G8R8 = 32, - D3DFMT_X8B8G8R8 = 33, - D3DFMT_G16R16 = 34, - D3DFMT_A2R10G10B10 = 35, - D3DFMT_A16B16G16R16 = 36, - - D3DFMT_A8P8 = 40, - D3DFMT_P8 = 41, - - D3DFMT_L8 = 50, - D3DFMT_A8L8 = 51, - D3DFMT_A4L4 = 52, - - D3DFMT_V8U8 = 60, - D3DFMT_L6V5U5 = 61, - D3DFMT_X8L8V8U8 = 62, - D3DFMT_Q8W8V8U8 = 63, - D3DFMT_V16U16 = 64, - D3DFMT_A2W10V10U10 = 67, - - D3DFMT_UYVY = MAKEFOURCC('U', 'Y', 'V', 'Y'), - D3DFMT_R8G8_B8G8 = MAKEFOURCC('R', 'G', 'B', 'G'), - D3DFMT_YUY2 = MAKEFOURCC('Y', 'U', 'Y', '2'), - D3DFMT_G8R8_G8B8 = MAKEFOURCC('G', 'R', 'G', 'B'), - D3DFMT_DXT1 = MAKEFOURCC('D', 'X', 'T', '1'), - D3DFMT_DXT2 = MAKEFOURCC('D', 'X', 'T', '2'), - D3DFMT_DXT3 = MAKEFOURCC('D', 'X', 'T', '3'), - D3DFMT_DXT4 = MAKEFOURCC('D', 'X', 'T', '4'), - D3DFMT_DXT5 = MAKEFOURCC('D', 'X', 'T', '5'), - - D3DFMT_D16_LOCKABLE = 70, - D3DFMT_D32 = 71, - D3DFMT_D15S1 = 73, - D3DFMT_D24S8 = 75, - D3DFMT_D24X8 = 77, - D3DFMT_D24X4S4 = 79, - D3DFMT_D16 = 80, - - D3DFMT_D32F_LOCKABLE = 82, - D3DFMT_D24FS8 = 83, - -#if !defined(D3D_DISABLE_9EX) - D3DFMT_D32_LOCKABLE = 84, - D3DFMT_S8_LOCKABLE = 85, -#endif // !D3D_DISABLE_9EX - - D3DFMT_L16 = 81, - - D3DFMT_VERTEXDATA =100, - D3DFMT_INDEX16 =101, - D3DFMT_INDEX32 =102, - - D3DFMT_Q16W16V16U16 =110, - - D3DFMT_MULTI2_ARGB8 = MAKEFOURCC('M','E','T','1'), - - D3DFMT_R16F = 111, - D3DFMT_G16R16F = 112, - D3DFMT_A16B16G16R16F = 113, - - D3DFMT_R32F = 114, - D3DFMT_G32R32F = 115, - D3DFMT_A32B32G32R32F = 116, - - D3DFMT_CxV8U8 = 117, - -#if !defined(D3D_DISABLE_9EX) - D3DFMT_A1 = 118, - D3DFMT_A2B10G10R10_XR_BIAS = 119, - D3DFMT_BINARYBUFFER = 199, -#endif // !D3D_DISABLE_9EX - - D3DFMT_FORCE_DWORD =0x7fffffff -} D3DFORMAT; - -typedef enum D3DDEVTYPE { - D3DDEVTYPE_HAL = 1, - D3DDEVTYPE_NULLREF = 4, - D3DDEVTYPE_REF = 2, - D3DDEVTYPE_SW = 3, - D3DDEVTYPE_FORCE_DWORD = 0xffffffff -} D3DDEVTYPE, *LPD3DDEVTYPE; - -typedef struct _D3DPSHADERCAPS2_0 { - DWORD Caps; - INT DynamicFlowControlDepth; - INT NumTemps; - INT StaticFlowControlDepth; - INT NumInstructionSlots; -} D3DPSHADERCAPS2_0; - -typedef struct _D3DVSHADERCAPS2_0 { - DWORD Caps; - INT DynamicFlowControlDepth; - INT NumTemps; - INT StaticFlowControlDepth; -} D3DVSHADERCAPS2_0; - -typedef struct _D3DCAPS9 { - D3DDEVTYPE DeviceType; - UINT AdapterOrdinal; - DWORD Caps; - DWORD Caps2; - DWORD Caps3; - DWORD PresentationIntervals; - DWORD CursorCaps; - DWORD DevCaps; - DWORD PrimitiveMiscCaps; - DWORD RasterCaps; - DWORD ZCmpCaps; - DWORD SrcBlendCaps; - DWORD DestBlendCaps; - DWORD AlphaCmpCaps; - DWORD ShadeCaps; - DWORD TextureCaps; - DWORD TextureFilterCaps; - DWORD CubeTextureFilterCaps; - DWORD VolumeTextureFilterCaps; - DWORD TextureAddressCaps; - DWORD VolumeTextureAddressCaps; - DWORD LineCaps; - DWORD MaxTextureWidth; - DWORD MaxTextureHeight; - DWORD MaxVolumeExtent; - DWORD MaxTextureRepeat; - DWORD MaxTextureAspectRatio; - DWORD MaxAnisotropy; - float MaxVertexW; - float GuardBandLeft; - float GuardBandTop; - float GuardBandRight; - float GuardBandBottom; - float ExtentsAdjust; - DWORD StencilCaps; - DWORD FVFCaps; - DWORD TextureOpCaps; - DWORD MaxTextureBlendStages; - DWORD MaxSimultaneousTextures; - DWORD VertexProcessingCaps; - DWORD MaxActiveLights; - DWORD MaxUserClipPlanes; - DWORD MaxVertexBlendMatrices; - DWORD MaxVertexBlendMatrixIndex; - float MaxPointSize; - DWORD MaxPrimitiveCount; - DWORD MaxVertexIndex; - DWORD MaxStreams; - DWORD MaxStreamStride; - DWORD VertexShaderVersion; - DWORD MaxVertexShaderConst; - DWORD PixelShaderVersion; - float PixelShader1xMaxValue; - DWORD DevCaps2; - float MaxNpatchTessellationLevel; - DWORD Reserved5; - UINT MasterAdapterOrdinal; - UINT AdapterOrdinalInGroup; - UINT NumberOfAdaptersInGroup; - DWORD DeclTypes; - DWORD NumSimultaneousRTs; - DWORD StretchRectFilterCaps; - D3DVSHADERCAPS2_0 VS20Caps; - D3DPSHADERCAPS2_0 PS20Caps; - DWORD VertexTextureFilterCaps; - DWORD MaxVShaderInstructionsExecuted; - DWORD MaxPShaderInstructionsExecuted; - DWORD MaxVertexShader30InstructionSlots; - DWORD MaxPixelShader30InstructionSlots; -} D3DCAPS9; - typedef struct _D3D9_Unknown6BC { /* 0x0000 */ HANDLE hDirectDrawLocal; @@ -267,13 +63,6 @@ typedef struct _D3D9_Unknown6BC /* 0x00BC */ DDGAMMARAMP GammaRamp; } D3D9_Unknown6BC; -typedef struct D3DDISPLAYMODE { - UINT Width; - UINT Height; - UINT RefreshRate; - D3DFORMAT Format; -} D3DDISPLAYMODE, *LPD3DDISPLAYMODE; - typedef struct _D3D9_DRIVERCAPS { /* 0x0000 */ D3DCAPS9 DriverCaps9; diff --git a/wrappers/directx/d3dwine_wrapper/CMakeLists.txt b/wrappers/directx/d3dwine_wrapper/CMakeLists.txt new file mode 100644 index 00000000000..1961e06c08d --- /dev/null +++ b/wrappers/directx/d3dwine_wrapper/CMakeLists.txt @@ -0,0 +1,56 @@ + +add_definitions( + -D__WINESRC__ + -D_USE_MATH_DEFINES + -DUSE_WIN32_OPENGL + -DSTAGING_CSMT + -Dcopysignf=_copysignf) + +include_directories(BEFORE ${REACTOS_SOURCE_DIR}/sdk/include/reactos/wine) + +# We name this d3dwine.dll, because the Virtualbox additions ship with a custom wined3d.dll +# and it breaks everything if it is installed. +spec2def(d3dwine.dll wined3d.spec ADD_IMPORTLIB) + +list(APPEND SOURCE + adapter_gl.c + adapter_vk.c + arb_program_shader.c + ati_fragment_shader.c + buffer.c + context.c + cs.c + device.c + directx.c + dxtn.c + gl_compat.c + glsl_shader.c + nvidia_texture_shader.c + palette.c + query.c + resource.c + sampler.c + shader.c + shader_sm1.c + shader_sm4.c + state.c + stateblock.c + surface.c + swapchain.c + texture.c + utils.c + vertexdeclaration.c + view.c + wined3d_main.c + precomp.h) + +add_library(d3dwine SHARED + ${SOURCE} + version.rc + ${CMAKE_CURRENT_BINARY_DIR}/d3dwine.def) + +set_module_type(d3dwine win32dll) +target_link_libraries(d3dwine wine) +add_importlibs(d3dwine user32 opengl32 gdi32 advapi32 msvcrt kernel32 ntdll) +add_pch(d3dwine precomp.h SOURCE) +add_cd_file(TARGET d3dwine DESTINATION reactos/system32 FOR all) diff --git a/wrappers/directx/d3dwine_wrapper/adapter_gl.c b/wrappers/directx/d3dwine_wrapper/adapter_gl.c new file mode 100644 index 00000000000..e12a1ff13b7 --- /dev/null +++ b/wrappers/directx/d3dwine_wrapper/adapter_gl.c @@ -0,0 +1,5284 @@ +/* + * Copyright 2002-2004 Jason Edmeades + * Copyright 2003-2004 Raphael Junqueira + * Copyright 2004 Christian Costa + * Copyright 2005 Oliver Stieber + * Copyright 2007-2008 Stefan Dösinger for CodeWeavers + * Copyright 2009-2011, 2018 Henri Verbeet for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "config.h" +#include "wine/port.h" + +#include + +#include "wined3d_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(d3d); +WINE_DECLARE_DEBUG_CHANNEL(d3d_perf); +WINE_DECLARE_DEBUG_CHANNEL(winediag); + +enum wined3d_gl_vendor +{ + GL_VENDOR_UNKNOWN, + GL_VENDOR_APPLE, + GL_VENDOR_FGLRX, + GL_VENDOR_MESA, + GL_VENDOR_NVIDIA, +}; + +struct wined3d_extension_map +{ + const char *extension_string; + enum wined3d_gl_extension extension; +}; + +static const struct wined3d_extension_map gl_extension_map[] = +{ + /* APPLE */ + {"GL_APPLE_fence", APPLE_FENCE }, + {"GL_APPLE_float_pixels", APPLE_FLOAT_PIXELS }, + {"GL_APPLE_flush_buffer_range", APPLE_FLUSH_BUFFER_RANGE }, + {"GL_APPLE_flush_render", APPLE_FLUSH_RENDER }, + {"GL_APPLE_rgb_422", APPLE_RGB_422 }, + {"GL_APPLE_ycbcr_422", APPLE_YCBCR_422 }, + + /* ARB */ + {"GL_ARB_base_instance", ARB_BASE_INSTANCE }, + {"GL_ARB_blend_func_extended", ARB_BLEND_FUNC_EXTENDED }, + {"GL_ARB_buffer_storage", ARB_BUFFER_STORAGE }, + {"GL_ARB_clear_buffer_object", ARB_CLEAR_BUFFER_OBJECT }, + {"GL_ARB_clear_texture", ARB_CLEAR_TEXTURE }, + {"GL_ARB_clip_control", ARB_CLIP_CONTROL }, + {"GL_ARB_color_buffer_float", ARB_COLOR_BUFFER_FLOAT }, + {"GL_ARB_compute_shader", ARB_COMPUTE_SHADER }, + {"GL_ARB_conservative_depth", ARB_CONSERVATIVE_DEPTH }, + {"GL_ARB_copy_buffer", ARB_COPY_BUFFER }, + {"GL_ARB_copy_image", ARB_COPY_IMAGE }, + {"GL_ARB_cull_distance", ARB_CULL_DISTANCE }, + {"GL_ARB_debug_output", ARB_DEBUG_OUTPUT }, + {"GL_ARB_depth_buffer_float", ARB_DEPTH_BUFFER_FLOAT }, + {"GL_ARB_depth_clamp", ARB_DEPTH_CLAMP }, + {"GL_ARB_depth_texture", ARB_DEPTH_TEXTURE }, + {"GL_ARB_derivative_control", ARB_DERIVATIVE_CONTROL }, + {"GL_ARB_draw_buffers", ARB_DRAW_BUFFERS }, + {"GL_ARB_draw_buffers_blend", ARB_DRAW_BUFFERS_BLEND }, + {"GL_ARB_draw_elements_base_vertex", ARB_DRAW_ELEMENTS_BASE_VERTEX }, + {"GL_ARB_draw_indirect", ARB_DRAW_INDIRECT }, + {"GL_ARB_draw_instanced", ARB_DRAW_INSTANCED }, + {"GL_ARB_ES2_compatibility", ARB_ES2_COMPATIBILITY }, + {"GL_ARB_ES3_compatibility", ARB_ES3_COMPATIBILITY }, + {"GL_ARB_explicit_attrib_location", ARB_EXPLICIT_ATTRIB_LOCATION }, + {"GL_ARB_fragment_coord_conventions", ARB_FRAGMENT_COORD_CONVENTIONS}, + {"GL_ARB_fragment_layer_viewport", ARB_FRAGMENT_LAYER_VIEWPORT }, + {"GL_ARB_fragment_program", ARB_FRAGMENT_PROGRAM }, + {"GL_ARB_fragment_shader", ARB_FRAGMENT_SHADER }, + {"GL_ARB_framebuffer_no_attachments", ARB_FRAMEBUFFER_NO_ATTACHMENTS}, + {"GL_ARB_framebuffer_object", ARB_FRAMEBUFFER_OBJECT }, + {"GL_ARB_framebuffer_sRGB", ARB_FRAMEBUFFER_SRGB }, + {"GL_ARB_geometry_shader4", ARB_GEOMETRY_SHADER4 }, + {"GL_ARB_gpu_shader5", ARB_GPU_SHADER5 }, + {"GL_ARB_half_float_pixel", ARB_HALF_FLOAT_PIXEL }, + {"GL_ARB_half_float_vertex", ARB_HALF_FLOAT_VERTEX }, + {"GL_ARB_instanced_arrays", ARB_INSTANCED_ARRAYS }, + {"GL_ARB_internalformat_query", ARB_INTERNALFORMAT_QUERY }, + {"GL_ARB_internalformat_query2", ARB_INTERNALFORMAT_QUERY2 }, + {"GL_ARB_map_buffer_alignment", ARB_MAP_BUFFER_ALIGNMENT }, + {"GL_ARB_map_buffer_range", ARB_MAP_BUFFER_RANGE }, + {"GL_ARB_multisample", ARB_MULTISAMPLE }, + {"GL_ARB_multitexture", ARB_MULTITEXTURE }, + {"GL_ARB_occlusion_query", ARB_OCCLUSION_QUERY }, + {"GL_ARB_pipeline_statistics_query", ARB_PIPELINE_STATISTICS_QUERY }, + {"GL_ARB_pixel_buffer_object", ARB_PIXEL_BUFFER_OBJECT }, + {"GL_ARB_point_parameters", ARB_POINT_PARAMETERS }, + {"GL_ARB_point_sprite", ARB_POINT_SPRITE }, + {"GL_ARB_polygon_offset_clamp", ARB_POLYGON_OFFSET_CLAMP }, + {"GL_ARB_provoking_vertex", ARB_PROVOKING_VERTEX }, + {"GL_ARB_query_buffer_object", ARB_QUERY_BUFFER_OBJECT }, + {"GL_ARB_sample_shading", ARB_SAMPLE_SHADING }, + {"GL_ARB_sampler_objects", ARB_SAMPLER_OBJECTS }, + {"GL_ARB_seamless_cube_map", ARB_SEAMLESS_CUBE_MAP }, + {"GL_ARB_shader_atomic_counters", ARB_SHADER_ATOMIC_COUNTERS }, + {"GL_ARB_shader_bit_encoding", ARB_SHADER_BIT_ENCODING }, + {"GL_ARB_shader_image_load_store", ARB_SHADER_IMAGE_LOAD_STORE }, + {"GL_ARB_shader_image_size", ARB_SHADER_IMAGE_SIZE }, + {"GL_ARB_shader_storage_buffer_object", ARB_SHADER_STORAGE_BUFFER_OBJECT}, + {"GL_ARB_shader_texture_image_samples", ARB_SHADER_TEXTURE_IMAGE_SAMPLES}, + {"GL_ARB_shader_texture_lod", ARB_SHADER_TEXTURE_LOD }, + {"GL_ARB_shader_viewport_layer_array", ARB_SHADER_VIEWPORT_LAYER_ARRAY}, + {"GL_ARB_shading_language_100", ARB_SHADING_LANGUAGE_100 }, + {"GL_ARB_shading_language_420pack", ARB_SHADING_LANGUAGE_420PACK }, + {"GL_ARB_shading_language_packing", ARB_SHADING_LANGUAGE_PACKING }, + {"GL_ARB_shadow", ARB_SHADOW }, + {"GL_ARB_stencil_texturing", ARB_STENCIL_TEXTURING }, + {"GL_ARB_sync", ARB_SYNC }, + {"GL_ARB_tessellation_shader", ARB_TESSELLATION_SHADER }, + {"GL_ARB_texture_border_clamp", ARB_TEXTURE_BORDER_CLAMP }, + {"GL_ARB_texture_buffer_object", ARB_TEXTURE_BUFFER_OBJECT }, + {"GL_ARB_texture_buffer_range", ARB_TEXTURE_BUFFER_RANGE }, + {"GL_ARB_texture_compression", ARB_TEXTURE_COMPRESSION }, + {"GL_ARB_texture_compression_bptc", ARB_TEXTURE_COMPRESSION_BPTC }, + {"GL_ARB_texture_compression_rgtc", ARB_TEXTURE_COMPRESSION_RGTC }, + {"GL_ARB_texture_cube_map", ARB_TEXTURE_CUBE_MAP }, + {"GL_ARB_texture_cube_map_array", ARB_TEXTURE_CUBE_MAP_ARRAY }, + {"GL_ARB_texture_env_combine", ARB_TEXTURE_ENV_COMBINE }, + {"GL_ARB_texture_env_dot3", ARB_TEXTURE_ENV_DOT3 }, + {"GL_ARB_texture_filter_anisotropic", ARB_TEXTURE_FILTER_ANISOTROPIC}, + {"GL_ARB_texture_float", ARB_TEXTURE_FLOAT }, + {"GL_ARB_texture_gather", ARB_TEXTURE_GATHER }, + {"GL_ARB_texture_mirrored_repeat", ARB_TEXTURE_MIRRORED_REPEAT }, + {"GL_ARB_texture_mirror_clamp_to_edge", ARB_TEXTURE_MIRROR_CLAMP_TO_EDGE}, + {"GL_ARB_texture_multisample", ARB_TEXTURE_MULTISAMPLE }, + {"GL_ARB_texture_non_power_of_two", ARB_TEXTURE_NON_POWER_OF_TWO }, + {"GL_ARB_texture_query_levels", ARB_TEXTURE_QUERY_LEVELS }, + {"GL_ARB_texture_rectangle", ARB_TEXTURE_RECTANGLE }, + {"GL_ARB_texture_rg", ARB_TEXTURE_RG }, + {"GL_ARB_texture_rgb10_a2ui", ARB_TEXTURE_RGB10_A2UI }, + {"GL_ARB_texture_storage", ARB_TEXTURE_STORAGE }, + {"GL_ARB_texture_storage_multisample", ARB_TEXTURE_STORAGE_MULTISAMPLE}, + {"GL_ARB_texture_swizzle", ARB_TEXTURE_SWIZZLE }, + {"GL_ARB_texture_view", ARB_TEXTURE_VIEW }, + {"GL_ARB_timer_query", ARB_TIMER_QUERY }, + {"GL_ARB_transform_feedback2", ARB_TRANSFORM_FEEDBACK2 }, + {"GL_ARB_transform_feedback3", ARB_TRANSFORM_FEEDBACK3 }, + {"GL_ARB_uniform_buffer_object", ARB_UNIFORM_BUFFER_OBJECT }, + {"GL_ARB_vertex_array_bgra", ARB_VERTEX_ARRAY_BGRA }, + {"GL_ARB_vertex_buffer_object", ARB_VERTEX_BUFFER_OBJECT }, + {"GL_ARB_vertex_program", ARB_VERTEX_PROGRAM }, + {"GL_ARB_vertex_shader", ARB_VERTEX_SHADER }, + {"GL_ARB_vertex_type_2_10_10_10_rev", ARB_VERTEX_TYPE_2_10_10_10_REV}, + {"GL_ARB_viewport_array", ARB_VIEWPORT_ARRAY }, + {"GL_ARB_texture_barrier", ARB_TEXTURE_BARRIER }, + + /* ATI */ + {"GL_ATI_fragment_shader", ATI_FRAGMENT_SHADER }, + {"GL_ATI_separate_stencil", ATI_SEPARATE_STENCIL }, + {"GL_ATI_texture_compression_3dc", ATI_TEXTURE_COMPRESSION_3DC }, + {"GL_ATI_texture_env_combine3", ATI_TEXTURE_ENV_COMBINE3 }, + {"GL_ATI_texture_mirror_once", ATI_TEXTURE_MIRROR_ONCE }, + + /* EXT */ + {"GL_EXT_blend_color", EXT_BLEND_COLOR }, + {"GL_EXT_blend_equation_separate", EXT_BLEND_EQUATION_SEPARATE }, + {"GL_EXT_blend_func_separate", EXT_BLEND_FUNC_SEPARATE }, + {"GL_EXT_blend_minmax", EXT_BLEND_MINMAX }, + {"GL_EXT_blend_subtract", EXT_BLEND_SUBTRACT }, + {"GL_EXT_depth_bounds_test", EXT_DEPTH_BOUNDS_TEST }, + {"GL_EXT_draw_buffers2", EXT_DRAW_BUFFERS2 }, + {"GL_EXT_fog_coord", EXT_FOG_COORD }, + {"GL_EXT_framebuffer_blit", EXT_FRAMEBUFFER_BLIT }, + {"GL_EXT_framebuffer_multisample", EXT_FRAMEBUFFER_MULTISAMPLE }, + {"GL_EXT_framebuffer_multisample_blit_scaled", + EXT_FRAMEBUFFER_MULTISAMPLE_BLIT_SCALED}, + {"GL_EXT_framebuffer_object", EXT_FRAMEBUFFER_OBJECT }, + {"GL_EXT_memory_object", EXT_MEMORY_OBJECT }, + {"GL_EXT_gpu_program_parameters", EXT_GPU_PROGRAM_PARAMETERS }, + {"GL_EXT_gpu_shader4", EXT_GPU_SHADER4 }, + {"GL_EXT_packed_depth_stencil", EXT_PACKED_DEPTH_STENCIL }, + {"GL_EXT_packed_float", EXT_PACKED_FLOAT }, + {"GL_EXT_point_parameters", EXT_POINT_PARAMETERS }, + {"GL_EXT_polygon_offset_clamp", ARB_POLYGON_OFFSET_CLAMP }, + {"GL_EXT_provoking_vertex", EXT_PROVOKING_VERTEX }, + {"GL_EXT_secondary_color", EXT_SECONDARY_COLOR }, + {"GL_EXT_stencil_two_side", EXT_STENCIL_TWO_SIDE }, + {"GL_EXT_stencil_wrap", EXT_STENCIL_WRAP }, + {"GL_EXT_texture3D", EXT_TEXTURE3D }, + {"GL_EXT_texture_array", EXT_TEXTURE_ARRAY }, + {"GL_EXT_texture_compression_rgtc", EXT_TEXTURE_COMPRESSION_RGTC }, + {"GL_EXT_texture_compression_s3tc", EXT_TEXTURE_COMPRESSION_S3TC }, + {"GL_EXT_texture_env_combine", EXT_TEXTURE_ENV_COMBINE }, + {"GL_EXT_texture_env_dot3", EXT_TEXTURE_ENV_DOT3 }, + {"GL_EXT_texture_filter_anisotropic", ARB_TEXTURE_FILTER_ANISOTROPIC}, + {"GL_EXT_texture_integer", EXT_TEXTURE_INTEGER }, + {"GL_EXT_texture_lod_bias", EXT_TEXTURE_LOD_BIAS }, + {"GL_EXT_texture_mirror_clamp", EXT_TEXTURE_MIRROR_CLAMP }, + {"GL_EXT_texture_shadow_lod", EXT_TEXTURE_SHADOW_LOD }, + {"GL_EXT_texture_shared_exponent", EXT_TEXTURE_SHARED_EXPONENT }, + {"GL_EXT_texture_snorm", EXT_TEXTURE_SNORM }, + {"GL_EXT_texture_sRGB", EXT_TEXTURE_SRGB }, + {"GL_EXT_texture_sRGB_decode", EXT_TEXTURE_SRGB_DECODE }, + {"GL_EXT_texture_swizzle", ARB_TEXTURE_SWIZZLE }, + {"GL_EXT_vertex_array_bgra", ARB_VERTEX_ARRAY_BGRA }, + + /* NV */ + {"GL_NV_fence", NV_FENCE }, + {"GL_NV_fog_distance", NV_FOG_DISTANCE }, + {"GL_NV_fragment_program", NV_FRAGMENT_PROGRAM }, + {"GL_NV_fragment_program2", NV_FRAGMENT_PROGRAM2 }, + {"GL_NV_fragment_program_option", NV_FRAGMENT_PROGRAM_OPTION }, + {"GL_NV_half_float", NV_HALF_FLOAT }, + {"GL_NV_light_max_exponent", NV_LIGHT_MAX_EXPONENT }, + {"GL_NV_point_sprite", NV_POINT_SPRITE }, + {"GL_NV_register_combiners", NV_REGISTER_COMBINERS }, + {"GL_NV_register_combiners2", NV_REGISTER_COMBINERS2 }, + {"GL_NV_texgen_reflection", NV_TEXGEN_REFLECTION }, + {"GL_NV_texture_env_combine4", NV_TEXTURE_ENV_COMBINE4 }, + {"GL_NV_texture_shader", NV_TEXTURE_SHADER }, + {"GL_NV_texture_shader2", NV_TEXTURE_SHADER2 }, + {"GL_NV_vertex_program", NV_VERTEX_PROGRAM }, + {"GL_NV_vertex_program1_1", NV_VERTEX_PROGRAM1_1 }, + {"GL_NV_vertex_program2", NV_VERTEX_PROGRAM2 }, + {"GL_NV_vertex_program2_option", NV_VERTEX_PROGRAM2_OPTION }, + {"GL_NV_vertex_program3", NV_VERTEX_PROGRAM3 }, + {"GL_NV_texture_barrier", NV_TEXTURE_BARRIER }, +}; + +static const struct wined3d_extension_map wgl_extension_map[] = +{ + {"WGL_ARB_pixel_format", WGL_ARB_PIXEL_FORMAT }, + {"WGL_EXT_swap_control", WGL_EXT_SWAP_CONTROL }, + {"WGL_WINE_pixel_format_passthrough", WGL_WINE_PIXEL_FORMAT_PASSTHROUGH}, + {"WGL_WINE_query_renderer", WGL_WINE_QUERY_RENDERER }, +}; + +static void wined3d_caps_gl_ctx_destroy(const struct wined3d_caps_gl_ctx *ctx) +{ + const struct wined3d_gl_info *gl_info = ctx->gl_info; + + TRACE("Destroying caps GL context.\n"); + + /* Both glDeleteProgram and glDeleteBuffers silently ignore 0 IDs but + * this function might be called before the relevant function pointers + * in gl_info are initialized. */ + if (ctx->test_program_id || ctx->test_vbo) + { + GL_EXTCALL(glDeleteProgram(ctx->test_program_id)); + GL_EXTCALL(glDeleteBuffers(1, &ctx->test_vbo)); + } + + if (!wglMakeCurrent(NULL, NULL)) + ERR("Failed to disable caps GL context.\n"); + + if (!wglDeleteContext(ctx->gl_ctx)) + { + DWORD err = GetLastError(); + ERR("wglDeleteContext(%p) failed, last error %#x.\n", ctx->gl_ctx, err); + } + + wined3d_release_dc(ctx->wnd, ctx->dc); + DestroyWindow(ctx->wnd); + + if (ctx->restore_gl_ctx && !wglMakeCurrent(ctx->restore_dc, ctx->restore_gl_ctx)) + ERR("Failed to restore previous GL context.\n"); +} + +static BOOL wined3d_caps_gl_ctx_create_attribs(struct wined3d_caps_gl_ctx *caps_gl_ctx, + struct wined3d_gl_info *gl_info) +{ + HGLRC new_ctx; + + if (!(gl_info->p_wglCreateContextAttribsARB = (void *)wglGetProcAddress("wglCreateContextAttribsARB"))) + return TRUE; + + if (!(new_ctx = context_create_wgl_attribs(gl_info, caps_gl_ctx->dc, NULL))) + { + gl_info->p_wglCreateContextAttribsARB = NULL; + return FALSE; + } + + if (!wglMakeCurrent(caps_gl_ctx->dc, new_ctx)) + { + ERR("Failed to make new context current, last error %#x.\n", GetLastError()); + if (!wglDeleteContext(new_ctx)) + ERR("Failed to delete new context, last error %#x.\n", GetLastError()); + gl_info->p_wglCreateContextAttribsARB = NULL; + return TRUE; + } + + if (!wglDeleteContext(caps_gl_ctx->gl_ctx)) + ERR("Failed to delete old context, last error %#x.\n", GetLastError()); + caps_gl_ctx->gl_ctx = new_ctx; + + return TRUE; +} + +static BOOL wined3d_caps_gl_ctx_create(struct wined3d_adapter *adapter, struct wined3d_caps_gl_ctx *ctx) +{ + PIXELFORMATDESCRIPTOR pfd; + int iPixelFormat; + + TRACE("getting context...\n"); + + ctx->restore_dc = wglGetCurrentDC(); + ctx->restore_gl_ctx = wglGetCurrentContext(); + + /* We need a fake window as a hdc retrieved using GetDC(0) can't be used for much GL purposes. */ + ctx->wnd = CreateWindowA(WINED3D_OPENGL_WINDOW_CLASS_NAME, "WineD3D fake window", + WS_OVERLAPPEDWINDOW, 10, 10, 10, 10, NULL, NULL, NULL, NULL); + if (!ctx->wnd) + { + ERR("Failed to create a window.\n"); + goto fail; + } + + ctx->dc = GetDC(ctx->wnd); + if (!ctx->dc) + { + ERR("Failed to get a DC.\n"); + goto fail; + } + + /* PixelFormat selection */ + ZeroMemory(&pfd, sizeof(pfd)); + pfd.nSize = sizeof(pfd); + pfd.nVersion = 1; + pfd.dwFlags = PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER | PFD_DRAW_TO_WINDOW; /* PFD_GENERIC_ACCELERATED */ + pfd.iPixelType = PFD_TYPE_RGBA; + pfd.cColorBits = 32; + pfd.iLayerType = PFD_MAIN_PLANE; + + if (!(iPixelFormat = ChoosePixelFormat(ctx->dc, &pfd))) + { + /* If this happens something is very wrong as ChoosePixelFormat barely fails. */ + ERR("Failed to find a suitable pixel format.\n"); + goto fail; + } + DescribePixelFormat(ctx->dc, iPixelFormat, sizeof(pfd), &pfd); + SetPixelFormat(ctx->dc, iPixelFormat, &pfd); + + /* Create a GL context. */ + if (!(ctx->gl_ctx = wglCreateContext(ctx->dc))) + { + WARN("Failed to create default context for capabilities initialization.\n"); + goto fail; + } + + /* Make it the current GL context. */ + if (!wglMakeCurrent(ctx->dc, ctx->gl_ctx)) + { + ERR("Failed to make caps GL context current.\n"); + goto fail; + } + + ctx->gl_info = &adapter->gl_info; + return TRUE; + +fail: + if (ctx->gl_ctx) wglDeleteContext(ctx->gl_ctx); + ctx->gl_ctx = NULL; + if (ctx->dc) ReleaseDC(ctx->wnd, ctx->dc); + ctx->dc = NULL; + if (ctx->wnd) DestroyWindow(ctx->wnd); + ctx->wnd = NULL; + if (ctx->restore_gl_ctx && !wglMakeCurrent(ctx->restore_dc, ctx->restore_gl_ctx)) + ERR("Failed to restore previous GL context.\n"); + + return FALSE; +} + +/* Context activation is done by the caller. */ +static BOOL test_arb_vs_offset_limit(const struct wined3d_gl_info *gl_info) +{ + GLuint prog; + BOOL ret = FALSE; + static const char testcode[] = + "!!ARBvp1.0\n" + "PARAM C[66] = { program.env[0..65] };\n" + "ADDRESS A0;" + "PARAM zero = {0.0, 0.0, 0.0, 0.0};\n" + "ARL A0.x, zero.x;\n" + "MOV result.position, C[A0.x + 65];\n" + "END\n"; + + while (gl_info->gl_ops.gl.p_glGetError()); + GL_EXTCALL(glGenProgramsARB(1, &prog)); + if(!prog) { + ERR("Failed to create an ARB offset limit test program\n"); + } + GL_EXTCALL(glBindProgramARB(GL_VERTEX_PROGRAM_ARB, prog)); + GL_EXTCALL(glProgramStringARB(GL_VERTEX_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, + strlen(testcode), testcode)); + if (gl_info->gl_ops.gl.p_glGetError()) + { + TRACE("OpenGL implementation does not allow indirect addressing offsets > 63\n"); + TRACE("error: %s\n", debugstr_a((const char *)gl_info->gl_ops.gl.p_glGetString(GL_PROGRAM_ERROR_STRING_ARB))); + ret = TRUE; + } else TRACE("OpenGL implementation allows offsets > 63\n"); + + GL_EXTCALL(glBindProgramARB(GL_VERTEX_PROGRAM_ARB, 0)); + GL_EXTCALL(glDeleteProgramsARB(1, &prog)); + checkGLcall("ARB vp offset limit test cleanup"); + + return ret; +} + +static BOOL match_amd_r300_to_500(const struct wined3d_gl_info *gl_info, struct wined3d_caps_gl_ctx *ctx, + const char *gl_renderer, enum wined3d_gl_vendor gl_vendor, + enum wined3d_pci_vendor card_vendor, enum wined3d_pci_device device) +{ + return card_vendor == HW_VENDOR_AMD + && (device == CARD_AMD_RADEON_9500 + || device == CARD_AMD_RADEON_X700 + || device == CARD_AMD_RADEON_X1600); +} + +static BOOL match_geforce5(const struct wined3d_gl_info *gl_info, struct wined3d_caps_gl_ctx *ctx, + const char *gl_renderer, enum wined3d_gl_vendor gl_vendor, + enum wined3d_pci_vendor card_vendor, enum wined3d_pci_device device) +{ + return card_vendor == HW_VENDOR_NVIDIA + && (device == CARD_NVIDIA_GEFORCEFX_5200 + || device == CARD_NVIDIA_GEFORCEFX_5600 + || device == CARD_NVIDIA_GEFORCEFX_5800); +} + +static BOOL match_apple(const struct wined3d_gl_info *gl_info, struct wined3d_caps_gl_ctx *ctx, + const char *gl_renderer, enum wined3d_gl_vendor gl_vendor, + enum wined3d_pci_vendor card_vendor, enum wined3d_pci_device device) +{ + /* MacOS has various specialities in the extensions it advertises. Some + * have to be loaded from the OpenGL 1.2+ core, while other extensions are + * advertised, but software emulated. So try to detect the Apple OpenGL + * implementation to apply some extension fixups afterwards. + * + * Detecting this isn't really easy. The vendor string doesn't mention + * Apple. Compile-time checks aren't sufficient either because a Linux + * binary may display on a macOS X server via remote X11. So try to detect + * the OpenGL implementation by looking at certain Apple extensions. Some + * extensions like client storage might be supported on other + * implementations too, but GL_APPLE_flush_render is specific to the + * macOS X window management, and GL_APPLE_ycbcr_422 is QuickTime + * specific. So the chance that other implementations support them is + * rather small since Win32 QuickTime uses DirectDraw, not OpenGL. + * + * This test has been moved into wined3d_guess_gl_vendor(). */ + return gl_vendor == GL_VENDOR_APPLE; +} + +/* Context activation is done by the caller. */ +static void test_pbo_functionality(struct wined3d_gl_info *gl_info) +{ + /* Some OpenGL implementations, namely Apple's Geforce 8 driver, advertises PBOs, + * but glTexSubImage from a PBO fails miserably, with the first line repeated over + * all the texture. This function detects this bug by its symptom and disables PBOs + * if the test fails. + * + * The test uploads a 4x4 texture via the PBO in the "native" format GL_BGRA, + * GL_UNSIGNED_INT_8_8_8_8_REV. This format triggers the bug, and it is what we use + * for D3DFMT_A8R8G8B8. Then the texture is read back without any PBO and the data + * read back is compared to the original. If they are equal PBOs are assumed to work, + * otherwise the PBO extension is disabled. */ + GLuint texture, pbo; + static const unsigned int pattern[] = + { + 0x00000000, 0x000000ff, 0x0000ff00, 0x40ff0000, + 0x80ffffff, 0x40ffff00, 0x00ff00ff, 0x0000ffff, + 0x00ffff00, 0x00ff00ff, 0x0000ffff, 0x000000ff, + 0x80ff00ff, 0x0000ffff, 0x00ff00ff, 0x40ff00ff + }; + unsigned int check[ARRAY_SIZE(pattern)]; + + /* No PBO -> No point in testing them. */ + if (!gl_info->supported[ARB_PIXEL_BUFFER_OBJECT]) return; + + while (gl_info->gl_ops.gl.p_glGetError()); + gl_info->gl_ops.gl.p_glGenTextures(1, &texture); + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_2D, texture); + + gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); + gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 4, 4, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, 0); + checkGLcall("Specifying the PBO test texture"); + + GL_EXTCALL(glGenBuffers(1, &pbo)); + GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo)); + GL_EXTCALL(glBufferData(GL_PIXEL_UNPACK_BUFFER, sizeof(pattern), pattern, GL_STREAM_DRAW)); + checkGLcall("Specifying the PBO test pbo"); + + gl_info->gl_ops.gl.p_glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 4, 4, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL); + checkGLcall("Loading the PBO test texture"); + + GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0)); + + gl_info->gl_ops.gl.p_glFinish(); /* just to be sure */ + + memset(check, 0, sizeof(check)); + gl_info->gl_ops.gl.p_glGetTexImage(GL_TEXTURE_2D, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, check); + checkGLcall("Reading back the PBO test texture"); + + gl_info->gl_ops.gl.p_glDeleteTextures(1, &texture); + GL_EXTCALL(glDeleteBuffers(1, &pbo)); + checkGLcall("PBO test cleanup"); + + if (memcmp(check, pattern, sizeof(check))) + { + WARN_(d3d_perf)("PBO test failed, read back data doesn't match original.\n" + "Disabling PBOs. This may result in slower performance.\n"); + gl_info->supported[ARB_PIXEL_BUFFER_OBJECT] = FALSE; + } + else + { + TRACE("PBO test successful.\n"); + } +} + +static BOOL match_dx10_capable(const struct wined3d_gl_info *gl_info, struct wined3d_caps_gl_ctx *ctx, + const char *gl_renderer, enum wined3d_gl_vendor gl_vendor, + enum wined3d_pci_vendor card_vendor, enum wined3d_pci_device device) +{ + /* Direct3D 9 cards support 40 single float varyings in hardware, most + * drivers report 32. ATI misreports 44 varyings. So assume that if we + * have more than 44 varyings we have a Direct3D 10+ card. This detection + * is for the gl_ClipPos varying quirk. If a Direct3D 9 card really + * supports more than 44 varyings and we subtract one in Direct3D 9 + * shaders it's not going to hurt us because the Direct3D 9 limit is + * hardcoded. + * + * Direct3D 10 cards usually have 64 varyings. */ + return gl_info->limits.glsl_varyings > 44; +} + +static BOOL match_not_dx10_capable(const struct wined3d_gl_info *gl_info, struct wined3d_caps_gl_ctx *ctx, + const char *gl_renderer, enum wined3d_gl_vendor gl_vendor, + enum wined3d_pci_vendor card_vendor, enum wined3d_pci_device device) +{ + return !match_dx10_capable(gl_info, ctx, gl_renderer, gl_vendor, card_vendor, device); +} + +/* A GL context is provided by the caller */ +static BOOL match_allows_spec_alpha(const struct wined3d_gl_info *gl_info, struct wined3d_caps_gl_ctx *ctx, + const char *gl_renderer, enum wined3d_gl_vendor gl_vendor, + enum wined3d_pci_vendor card_vendor, enum wined3d_pci_device device) +{ + GLenum error; + DWORD data[16]; + + if (!gl_info->supported[EXT_SECONDARY_COLOR] || !gl_info->supported[WINED3D_GL_LEGACY_CONTEXT]) + return FALSE; + + while (gl_info->gl_ops.gl.p_glGetError()); + GL_EXTCALL(glSecondaryColorPointerEXT)(4, GL_UNSIGNED_BYTE, 4, data); + error = gl_info->gl_ops.gl.p_glGetError(); + + if (error == GL_NO_ERROR) + { + TRACE("GL Implementation accepts 4 component specular color pointers\n"); + return TRUE; + } + else + { + TRACE("GL implementation does not accept 4 component specular colors, error %s\n", + debug_glerror(error)); + return FALSE; + } +} + +/* A GL context is provided by the caller */ +static BOOL match_broken_nv_clip(const struct wined3d_gl_info *gl_info, struct wined3d_caps_gl_ctx *ctx, + const char *gl_renderer, enum wined3d_gl_vendor gl_vendor, + enum wined3d_pci_vendor card_vendor, enum wined3d_pci_device device) +{ + GLuint prog; + BOOL ret = FALSE; + GLint pos; + static const char testcode[] = + "!!ARBvp1.0\n" + "OPTION NV_vertex_program2;\n" + "MOV result.clip[0], 0.0;\n" + "MOV result.position, 0.0;\n" + "END\n"; + + if (!gl_info->supported[NV_VERTEX_PROGRAM2_OPTION]) return FALSE; + + while (gl_info->gl_ops.gl.p_glGetError()); + + GL_EXTCALL(glGenProgramsARB(1, &prog)); + if(!prog) + { + ERR("Failed to create the NVvp clip test program\n"); + return FALSE; + } + GL_EXTCALL(glBindProgramARB(GL_VERTEX_PROGRAM_ARB, prog)); + GL_EXTCALL(glProgramStringARB(GL_VERTEX_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, + strlen(testcode), testcode)); + gl_info->gl_ops.gl.p_glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &pos); + if(pos != -1) + { + WARN("GL_NV_vertex_program2_option result.clip[] test failed\n"); + TRACE("error: %s\n", debugstr_a((const char *)gl_info->gl_ops.gl.p_glGetString(GL_PROGRAM_ERROR_STRING_ARB))); + ret = TRUE; + while (gl_info->gl_ops.gl.p_glGetError()); + } + else TRACE("GL_NV_vertex_program2_option result.clip[] test passed\n"); + + GL_EXTCALL(glBindProgramARB(GL_VERTEX_PROGRAM_ARB, 0)); + GL_EXTCALL(glDeleteProgramsARB(1, &prog)); + checkGLcall("GL_NV_vertex_program2_option result.clip[] test cleanup"); + + return ret; +} + +/* Context activation is done by the caller. */ +static BOOL match_fbo_tex_update(const struct wined3d_gl_info *gl_info, struct wined3d_caps_gl_ctx *ctx, + const char *gl_renderer, enum wined3d_gl_vendor gl_vendor, + enum wined3d_pci_vendor card_vendor, enum wined3d_pci_device device) +{ + char data[4 * 4 * 4]; + GLuint tex, fbo; + GLenum status; + + if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return FALSE; + + memset(data, 0xcc, sizeof(data)); + + gl_info->gl_ops.gl.p_glGenTextures(1, &tex); + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_2D, tex); + gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 4, 4, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL); + checkGLcall("glTexImage2D"); + + gl_info->fbo_ops.glGenFramebuffers(1, &fbo); + gl_info->fbo_ops.glBindFramebuffer(GL_FRAMEBUFFER, fbo); + gl_info->fbo_ops.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0); + checkGLcall("glFramebufferTexture2D"); + + status = gl_info->fbo_ops.glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) ERR("FBO status %#x\n", status); + checkGLcall("glCheckFramebufferStatus"); + + memset(data, 0x11, sizeof(data)); + gl_info->gl_ops.gl.p_glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 4, 4, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, data); + checkGLcall("glTexSubImage2D"); + + gl_info->gl_ops.gl.p_glClearColor(0.996f, 0.729f, 0.745f, 0.792f); + gl_info->gl_ops.gl.p_glClear(GL_COLOR_BUFFER_BIT); + checkGLcall("glClear"); + + gl_info->gl_ops.gl.p_glGetTexImage(GL_TEXTURE_2D, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, data); + checkGLcall("glGetTexImage"); + + gl_info->fbo_ops.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); + gl_info->fbo_ops.glBindFramebuffer(GL_FRAMEBUFFER, 0); + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_2D, 0); + checkGLcall("glBindTexture"); + + gl_info->fbo_ops.glDeleteFramebuffers(1, &fbo); + gl_info->gl_ops.gl.p_glDeleteTextures(1, &tex); + checkGLcall("glDeleteTextures"); + + return *(DWORD *)data == 0x11111111; +} + +/* Context activation is done by the caller. */ +static BOOL match_broken_rgba16(const struct wined3d_gl_info *gl_info, struct wined3d_caps_gl_ctx *ctx, + const char *gl_renderer, enum wined3d_gl_vendor gl_vendor, + enum wined3d_pci_vendor card_vendor, enum wined3d_pci_device device) +{ + /* GL_RGBA16 uses GL_RGBA8 internally on Geforce 7 and older cards. + * This leads to graphical bugs in Half Life 2 and Unreal engine games. */ + GLuint tex; + GLint size; + + gl_info->gl_ops.gl.p_glGenTextures(1, &tex); + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_2D, tex); + gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16, 4, 4, 0, GL_RGBA, GL_UNSIGNED_SHORT, NULL); + checkGLcall("glTexImage2D"); + + gl_info->gl_ops.gl.p_glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_RED_SIZE, &size); + checkGLcall("glGetTexLevelParameteriv"); + TRACE("Real color depth is %d\n", size); + + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_2D, 0); + checkGLcall("glBindTexture"); + gl_info->gl_ops.gl.p_glDeleteTextures(1, &tex); + checkGLcall("glDeleteTextures"); + + return size < 16; +} + +static BOOL match_fglrx(const struct wined3d_gl_info *gl_info, struct wined3d_caps_gl_ctx *ctx, + const char *gl_renderer, enum wined3d_gl_vendor gl_vendor, + enum wined3d_pci_vendor card_vendor, enum wined3d_pci_device device) +{ + return gl_vendor == GL_VENDOR_FGLRX; +} + +static BOOL match_r200(const struct wined3d_gl_info *gl_info, struct wined3d_caps_gl_ctx *ctx, + const char *gl_renderer, enum wined3d_gl_vendor gl_vendor, + enum wined3d_pci_vendor card_vendor, enum wined3d_pci_device device) +{ + if (card_vendor != HW_VENDOR_AMD) return FALSE; + if (device == CARD_AMD_RADEON_8500) return TRUE; + return FALSE; +} + +static BOOL match_broken_arb_fog(const struct wined3d_gl_info *gl_info, struct wined3d_caps_gl_ctx *ctx, + const char *gl_renderer, enum wined3d_gl_vendor gl_vendor, + enum wined3d_pci_vendor card_vendor, enum wined3d_pci_device device) +{ + DWORD data[4]; + GLuint tex, fbo; + GLenum status; + float color[4] = {0.0f, 1.0f, 0.0f, 0.0f}; + GLuint prog; + GLint err_pos; + static const char program_code[] = + "!!ARBfp1.0\n" + "OPTION ARB_fog_linear;\n" + "MOV result.color, {1.0, 0.0, 0.0, 0.0};\n" + "END\n"; + + if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) + return FALSE; + if (!gl_info->supported[ARB_FRAGMENT_PROGRAM]) + return FALSE; + if (!gl_info->supported[WINED3D_GL_LEGACY_CONTEXT]) + return FALSE; + + gl_info->gl_ops.gl.p_glGenTextures(1, &tex); + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_2D, tex); + gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, 4, 1, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL); + checkGLcall("glTexImage2D"); + + gl_info->fbo_ops.glGenFramebuffers(1, &fbo); + gl_info->fbo_ops.glBindFramebuffer(GL_FRAMEBUFFER, fbo); + gl_info->fbo_ops.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0); + checkGLcall("glFramebufferTexture2D"); + + status = gl_info->fbo_ops.glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) ERR("FBO status %#x\n", status); + checkGLcall("glCheckFramebufferStatus"); + + gl_info->gl_ops.gl.p_glClearColor(0.0f, 0.0f, 1.0f, 0.0f); + gl_info->gl_ops.gl.p_glClear(GL_COLOR_BUFFER_BIT); + checkGLcall("glClear"); + gl_info->gl_ops.gl.p_glViewport(0, 0, 4, 1); + checkGLcall("glViewport"); + + gl_info->gl_ops.gl.p_glEnable(GL_FOG); + gl_info->gl_ops.gl.p_glFogf(GL_FOG_START, 0.5f); + gl_info->gl_ops.gl.p_glFogf(GL_FOG_END, 0.5f); + gl_info->gl_ops.gl.p_glFogi(GL_FOG_MODE, GL_LINEAR); + gl_info->gl_ops.gl.p_glHint(GL_FOG_HINT, GL_NICEST); + gl_info->gl_ops.gl.p_glFogfv(GL_FOG_COLOR, color); + checkGLcall("fog setup"); + + GL_EXTCALL(glGenProgramsARB(1, &prog)); + GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, prog)); + GL_EXTCALL(glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, + strlen(program_code), program_code)); + gl_info->gl_ops.gl.p_glEnable(GL_FRAGMENT_PROGRAM_ARB); + checkGLcall("Test fragment program setup"); + + gl_info->gl_ops.gl.p_glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &err_pos); + if (err_pos != -1) + { + const char *error_str; + error_str = (const char *)gl_info->gl_ops.gl.p_glGetString(GL_PROGRAM_ERROR_STRING_ARB); + FIXME("Fog test program error at position %d: %s\n\n", err_pos, debugstr_a(error_str)); + } + + gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP); + gl_info->gl_ops.gl.p_glVertex3f(-1.0f, -1.0f, 0.0f); + gl_info->gl_ops.gl.p_glVertex3f( 1.0f, -1.0f, 1.0f); + gl_info->gl_ops.gl.p_glVertex3f(-1.0f, 1.0f, 0.0f); + gl_info->gl_ops.gl.p_glVertex3f( 1.0f, 1.0f, 1.0f); + gl_info->gl_ops.gl.p_glEnd(); + checkGLcall("ARBfp fog test draw"); + + gl_info->gl_ops.gl.p_glGetTexImage(GL_TEXTURE_2D, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, data); + checkGLcall("glGetTexImage"); + data[0] &= 0x00ffffff; + data[1] &= 0x00ffffff; + data[2] &= 0x00ffffff; + data[3] &= 0x00ffffff; + + gl_info->fbo_ops.glBindFramebuffer(GL_FRAMEBUFFER, 0); + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_2D, 0); + + gl_info->fbo_ops.glDeleteFramebuffers(1, &fbo); + gl_info->gl_ops.gl.p_glDeleteTextures(1, &tex); + gl_info->gl_ops.gl.p_glDisable(GL_FOG); + GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, 0)); + gl_info->gl_ops.gl.p_glDisable(GL_FRAGMENT_PROGRAM_ARB); + GL_EXTCALL(glDeleteProgramsARB(1, &prog)); + checkGLcall("ARBfp fog test teardown"); + + TRACE("Fog test data: %08x %08x %08x %08x\n", data[0], data[1], data[2], data[3]); + return data[0] != 0x00ff0000 || data[3] != 0x0000ff00; +} + +static BOOL match_broken_viewport_subpixel_bits(const struct wined3d_gl_info *gl_info, + struct wined3d_caps_gl_ctx *ctx, const char *gl_renderer, enum wined3d_gl_vendor gl_vendor, + enum wined3d_pci_vendor card_vendor, enum wined3d_pci_device device) +{ + if (!gl_info->supported[ARB_VIEWPORT_ARRAY]) + return FALSE; + if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) + return FALSE; + return !wined3d_caps_gl_ctx_test_viewport_subpixel_bits(ctx); +} + +static BOOL match_no_independent_bit_depths(const struct wined3d_gl_info *gl_info, + struct wined3d_caps_gl_ctx *ctx, const char *gl_renderer, enum wined3d_gl_vendor gl_vendor, + enum wined3d_pci_vendor card_vendor, enum wined3d_pci_device device) +{ + GLuint tex[2], fbo; + GLenum status; + + /* ARB_framebuffer_object allows implementation-dependent internal format + * restrictions. The EXT extension explicitly calls out an error in the + * relevant case. */ + if (!gl_info->supported[ARB_FRAMEBUFFER_OBJECT]) + return TRUE; + if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) + return TRUE; + + gl_info->gl_ops.gl.p_glGenTextures(2, tex); + + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_2D, tex[0]); + gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 4, 1, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, NULL); + + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_2D, tex[1]); + gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB5, 4, 1, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, NULL); + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_2D, 0); + + gl_info->fbo_ops.glGenFramebuffers(1, &fbo); + gl_info->fbo_ops.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo); + gl_info->fbo_ops.glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex[0], 0); + gl_info->fbo_ops.glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, tex[1], 0); + + status = gl_info->fbo_ops.glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER); + + gl_info->fbo_ops.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + gl_info->fbo_ops.glDeleteFramebuffers(1, &fbo); + gl_info->gl_ops.gl.p_glDeleteTextures(2, tex); + checkGLcall("testing multiple framebuffer attachments with different bit depths"); + + return status != GL_FRAMEBUFFER_COMPLETE; +} + +static void quirk_apple_glsl_constants(struct wined3d_gl_info *gl_info) +{ + /* MacOS needs uniforms for relative addressing offsets. This can + * accumulate to quite a few uniforms. Beyond that the general uniform + * isn't optimal, so reserve a number of uniforms. 12 vec4's should allow + * 48 different offsets or other helper immediate values. */ + TRACE("Reserving 12 GLSL constants for compiler private use.\n"); + gl_info->reserved_glsl_constants = max(gl_info->reserved_glsl_constants, 12); +} + +static void quirk_amd_dx9(struct wined3d_gl_info *gl_info) +{ + /* MacOS advertises GL_ARB_texture_non_power_of_two on ATI r500 and + * earlier cards, although these cards only support + * GL_ARB_texture_rectangle (D3DPTEXTURECAPS_NONPOW2CONDITIONAL). + * + * If real NP2 textures are used, the driver falls back to software. We + * could just disable the extension and use GL_ARB_texture_rectangle + * instead, but texture_rectangle is inconvenient due to the + * non-normalised texture coordinates. Thus set an internal extension + * flag, GL_WINE_normalized_texrect, which signals the code that it can + * use non-power-of-two textures as per GL_ARB_texture_non_power_of_two, + * but has to stick to the texture_rectangle limits. + * + * Fglrx doesn't advertise GL_ARB_texture_non_power_of_two, but it + * advertises OpenGL 2.0, which has this extension promoted to core. The + * extension loading code sets this extension supported due to that, so + * this code works on fglrx as well. */ + if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO]) + { + TRACE("GL_ARB_texture_non_power_of_two advertised on R500 or earlier card, removing.\n"); + gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] = FALSE; + gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT] = TRUE; + } +} + +static void quirk_no_np2(struct wined3d_gl_info *gl_info) +{ + /* The NVIDIA GeForce FX series reports OpenGL 2.0 capabilities with the + * latest drivers versions, but doesn't explicitly advertise the + * ARB_tex_npot extension in the OpenGL extension string. This usually + * means that ARB_tex_npot is supported in hardware as long as the + * application is staying within the limits enforced by the + * ARB_texture_rectangle extension. This however is not true for the + * FX series, which instantly falls back to a slower software path as + * soon as ARB_tex_npot is used. We therefore completely remove + * ARB_tex_npot from the list of supported extensions. + * + * Note that WINE_normalized_texrect can't be used in this case because + * internally it uses ARB_tex_npot, triggering the software fallback. + * There is not much we can do here apart from disabling the + * software-emulated extension and re-enable ARB_tex_rect (which was + * previously disabled in wined3d_adapter_init_gl_caps). + * + * This fixup removes performance problems on both the FX 5900 and + * FX 5700 (e.g. for framebuffer post-processing effects in the game + * "Max Payne 2"). The behaviour can be verified through a simple test + * app attached in bugreport #14724. */ + TRACE("GL_ARB_texture_non_power_of_two advertised through OpenGL 2.0 on NV FX card, removing.\n"); + gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] = FALSE; + gl_info->supported[ARB_TEXTURE_RECTANGLE] = TRUE; +} + +static void quirk_clip_varying(struct wined3d_gl_info *gl_info) +{ + gl_info->quirks |= WINED3D_QUIRK_GLSL_CLIP_VARYING; +} + +static void quirk_allows_specular_alpha(struct wined3d_gl_info *gl_info) +{ + gl_info->quirks |= WINED3D_QUIRK_ALLOWS_SPECULAR_ALPHA; +} + +static void quirk_disable_nvvp_clip(struct wined3d_gl_info *gl_info) +{ + gl_info->quirks |= WINED3D_QUIRK_NV_CLIP_BROKEN; +} + +static void quirk_fbo_tex_update(struct wined3d_gl_info *gl_info) +{ + gl_info->quirks |= WINED3D_QUIRK_FBO_TEX_UPDATE; +} + +static void quirk_broken_rgba16(struct wined3d_gl_info *gl_info) +{ + gl_info->quirks |= WINED3D_QUIRK_BROKEN_RGBA16; +} + +static void quirk_infolog_spam(struct wined3d_gl_info *gl_info) +{ + gl_info->quirks |= WINED3D_QUIRK_INFO_LOG_SPAM; +} + +static void quirk_limited_tex_filtering(struct wined3d_gl_info *gl_info) +{ + /* NVIDIA GeForce 6xxx and 7xxx support accelerated VTF only on a few + * selected texture formats. They are apparently the only Direct3D 9 class + * GPUs supporting VTF. Also, Direct3D 9-era GPUs are somewhat limited + * with float texture filtering and blending. */ + gl_info->quirks |= WINED3D_QUIRK_LIMITED_TEX_FILTERING; +} + +static void quirk_r200_constants(struct wined3d_gl_info *gl_info) +{ + /* The Mesa r200 driver (and there is no other driver for this GPU Wine + * would run on) loads some fog parameters (start, end, exponent, but not + * the colour) into the program. + * + * Apparently the fog hardware is only able to handle linear fog with a + * range of 0.0;1.0, and it is the responsibility of the vertex pipeline + * to handle non-linear fog and linear fog with start and end other than + * 0.0 and 1.0. */ + TRACE("Reserving 1 ARB constant for compiler private use.\n"); + gl_info->reserved_arb_constants = max(gl_info->reserved_arb_constants, 1); +} + +static void quirk_broken_arb_fog(struct wined3d_gl_info *gl_info) +{ + gl_info->quirks |= WINED3D_QUIRK_BROKEN_ARB_FOG; +} + +static void quirk_broken_viewport_subpixel_bits(struct wined3d_gl_info *gl_info) +{ + if (gl_info->supported[ARB_CLIP_CONTROL]) + { + TRACE("Disabling ARB_clip_control.\n"); + gl_info->supported[ARB_CLIP_CONTROL] = FALSE; + } +} + +static void quirk_no_independent_bit_depths(struct wined3d_gl_info *gl_info) +{ + gl_info->quirks |= WINED3D_QUIRK_NO_INDEPENDENT_BIT_DEPTHS; +} + +static const struct wined3d_gpu_description *query_gpu_description(const struct wined3d_gl_info *gl_info, + UINT64 *vram_bytes) +{ + const struct wined3d_gpu_description *gpu_description = NULL, *gpu_description_override; + enum wined3d_pci_vendor vendor = PCI_VENDOR_NONE; + enum wined3d_pci_device device = PCI_DEVICE_NONE; + GLuint value; + + if (gl_info->supported[WGL_WINE_QUERY_RENDERER]) + { + if (GL_EXTCALL(wglQueryCurrentRendererIntegerWINE(WGL_RENDERER_VENDOR_ID_WINE, &value))) + vendor = value; + if (GL_EXTCALL(wglQueryCurrentRendererIntegerWINE(WGL_RENDERER_DEVICE_ID_WINE, &value))) + device = value; + if (GL_EXTCALL(wglQueryCurrentRendererIntegerWINE(WGL_RENDERER_VIDEO_MEMORY_WINE, &value))) + *vram_bytes = (UINT64)value * 1024 * 1024; + + TRACE("Card reports vendor PCI ID 0x%04x, device PCI ID 0x%04x, 0x%s bytes of video memory.\n", + vendor, device, wine_dbgstr_longlong(*vram_bytes)); + + gpu_description = wined3d_get_gpu_description(vendor, device); + } + + if ((gpu_description_override = wined3d_get_user_override_gpu_description(vendor, device))) + gpu_description = gpu_description_override; + + return gpu_description; +} + +/* Context activation is done by the caller. */ +static void fixup_extensions(struct wined3d_gl_info *gl_info, struct wined3d_caps_gl_ctx *ctx, + const char *gl_renderer, enum wined3d_gl_vendor gl_vendor) +{ + enum wined3d_pci_vendor card_vendor = ctx->gpu_description->vendor; + enum wined3d_pci_device device = ctx->gpu_description->device; + unsigned int i; + + static const struct driver_quirk + { + BOOL (*match)(const struct wined3d_gl_info *gl_info, struct wined3d_caps_gl_ctx *ctx, + const char *gl_renderer, enum wined3d_gl_vendor gl_vendor, + enum wined3d_pci_vendor card_vendor, enum wined3d_pci_device device); + void (*apply)(struct wined3d_gl_info *gl_info); + const char *description; + } + quirk_table[] = + { + { + match_amd_r300_to_500, + quirk_amd_dx9, + "AMD normalised texrect quirk" + }, + { + match_apple, + quirk_apple_glsl_constants, + "Apple GLSL uniform override" + }, + { + match_geforce5, + quirk_no_np2, + "Geforce 5 NP2 disable" + }, + { + match_dx10_capable, + quirk_clip_varying, + "Reserved varying for gl_ClipPos" + }, + { + /* GL_EXT_secondary_color does not allow 4 component secondary + * colours, but most OpenGL implementations accept it. Apple's + * is the only OpenGL implementation known to reject it. + * + * If we can pass 4-component specular colours, do it, because + * (a) we don't have to screw around with the data, and (b) the + * Direct3D fixed-function vertex pipeline passes specular alpha + * to the pixel shader if any is used. Otherwise the specular + * alpha is used to pass the fog coordinate, which we pass to + * OpenGL via GL_EXT_fog_coord. */ + match_allows_spec_alpha, + quirk_allows_specular_alpha, + "Allow specular alpha quirk" + }, + { + match_broken_nv_clip, + quirk_disable_nvvp_clip, + "Apple NV_vertex_program clip bug quirk" + }, + { + match_fbo_tex_update, + quirk_fbo_tex_update, + "FBO rebind for attachment updates" + }, + { + match_broken_rgba16, + quirk_broken_rgba16, + "True RGBA16 is not available" + }, + { + match_fglrx, + quirk_infolog_spam, + "Not printing GLSL infolog" + }, + { + match_not_dx10_capable, + quirk_limited_tex_filtering, + "Texture filtering, blending and VTF support is limited" + }, + { + match_r200, + quirk_r200_constants, + "r200 vertex shader constants" + }, + { + match_broken_arb_fog, + quirk_broken_arb_fog, + "ARBfp fogstart == fogend workaround" + }, + { + match_broken_viewport_subpixel_bits, + quirk_broken_viewport_subpixel_bits, + "NVIDIA viewport subpixel bits bug" + }, + { + match_no_independent_bit_depths, + quirk_no_independent_bit_depths, + "No support for MRT with independent bit depths" + }, + }; + + for (i = 0; i < ARRAY_SIZE(quirk_table); ++i) + { + if (!quirk_table[i].match(gl_info, ctx, gl_renderer, gl_vendor, card_vendor, device)) continue; + TRACE("Applying driver quirk \"%s\".\n", quirk_table[i].description); + quirk_table[i].apply(gl_info); + } + + /* Find out if PBOs work as they are supposed to. */ + test_pbo_functionality(gl_info); +} + +static DWORD wined3d_parse_gl_version(const char *gl_version) +{ + const char *ptr = gl_version; + int major, minor; + + major = atoi(ptr); + if (major <= 0) + ERR("Invalid OpenGL major version %d.\n", major); + + while (isdigit(*ptr)) ++ptr; + if (*ptr++ != '.') + ERR("Invalid OpenGL version string %s.\n", debugstr_a(gl_version)); + + minor = atoi(ptr); + + TRACE("Found OpenGL version %d.%d.\n", major, minor); + + return MAKEDWORD_VERSION(major, minor); +} + +static enum wined3d_gl_vendor wined3d_guess_gl_vendor(const struct wined3d_gl_info *gl_info, + const char *gl_vendor_string, const char *gl_renderer, const char *gl_version) +{ + /* MacOS has various specialities in the extensions it advertises. Some have to be loaded from + * the opengl 1.2+ core, while other extensions are advertised, but software emulated. So try to + * detect the Apple OpenGL implementation to apply some extension fixups afterwards. + * + * Detecting this isn't really easy. The vendor string doesn't mention Apple. Compile-time checks + * aren't sufficient either because a Linux binary may display on a macos X server via remote X11. + * So try to detect the GL implementation by looking at certain Apple extensions. Some extensions + * like client storage might be supported on other implementations too, but GL_APPLE_flush_render + * is specific to the Mac OS X window management, and GL_APPLE_ycbcr_422 is QuickTime specific. So + * the chance that other implementations support them is rather small since Win32 QuickTime uses + * DirectDraw, not OpenGL. */ + if (gl_info->supported[APPLE_FLUSH_RENDER] + && (gl_info->supported[APPLE_YCBCR_422] || gl_info->supported[APPLE_RGB_422])) + return GL_VENDOR_APPLE; + + if (strstr(gl_vendor_string, "NVIDIA")) + return GL_VENDOR_NVIDIA; + + if (strstr(gl_vendor_string, "ATI")) + return GL_VENDOR_FGLRX; + + if (strstr(gl_vendor_string, "Mesa") + || strstr(gl_vendor_string, "X.Org") + || strstr(gl_vendor_string, "Advanced Micro Devices, Inc.") + || strstr(gl_vendor_string, "DRI R300 Project") + || strstr(gl_vendor_string, "Tungsten Graphics, Inc") + || strstr(gl_vendor_string, "VMware, Inc.") + || strstr(gl_vendor_string, "Red Hat") + || strstr(gl_vendor_string, "Intel") + || strstr(gl_renderer, "Mesa") + || strstr(gl_renderer, "Gallium") + || strstr(gl_renderer, "Intel") + || strstr(gl_version, "Mesa")) + return GL_VENDOR_MESA; + + FIXME("Received unrecognized GL_VENDOR %s. Returning GL_VENDOR_UNKNOWN.\n", + debugstr_a(gl_vendor_string)); + + return GL_VENDOR_UNKNOWN; +} + +static enum wined3d_pci_vendor wined3d_guess_card_vendor(const char *gl_vendor_string, const char *gl_renderer) +{ + if (strstr(gl_vendor_string, "NVIDIA") + || strstr(gl_vendor_string, "Nouveau") + || strstr(gl_vendor_string, "nouveau")) + return HW_VENDOR_NVIDIA; + + if (strstr(gl_vendor_string, "ATI") + || strstr(gl_vendor_string, "Advanced Micro Devices, Inc.") + || strstr(gl_vendor_string, "X.Org R300 Project") + || strstr(gl_renderer, "AMD") + || strstr(gl_renderer, "FirePro") + || strstr(gl_renderer, "Radeon") + || strstr(gl_renderer, "R100") + || strstr(gl_renderer, "R200") + || strstr(gl_renderer, "R300") + || strstr(gl_renderer, "R600") + || strstr(gl_renderer, "R700")) + return HW_VENDOR_AMD; + + if (strstr(gl_vendor_string, "Intel(R)") + /* Intel switched from Intel(R) to Intel® recently, so just match Intel. */ + || strstr(gl_renderer, "Intel") + || strstr(gl_renderer, "i915") + || strstr(gl_vendor_string, "Intel Inc.")) + return HW_VENDOR_INTEL; + + if (strstr(gl_vendor_string, "Red Hat") + || strstr(gl_renderer, "virgl")) + return HW_VENDOR_REDHAT; + + if (strstr(gl_renderer, "SVGA3D")) + return HW_VENDOR_VMWARE; + + if (strstr(gl_vendor_string, "Mesa") + || strstr(gl_vendor_string, "Brian Paul") + || strstr(gl_vendor_string, "Tungsten Graphics, Inc") + || strstr(gl_vendor_string, "VMware, Inc.")) + return HW_VENDOR_SOFTWARE; + + FIXME("Received unrecognized GL_VENDOR %s. Returning HW_VENDOR_NVIDIA.\n", debugstr_a(gl_vendor_string)); + + return HW_VENDOR_NVIDIA; +} + +static enum wined3d_feature_level feature_level_from_caps(const struct wined3d_gl_info *gl_info, + const struct shader_caps *shader_caps, const struct fragment_caps *fragment_caps) +{ + unsigned int shader_model; + + shader_model = min(shader_caps->vs_version, shader_caps->ps_version); + shader_model = min(shader_model, max(shader_caps->gs_version, 3)); + shader_model = min(shader_model, max(shader_caps->hs_version, 4)); + shader_model = min(shader_model, max(shader_caps->ds_version, 4)); + + if (gl_info->supported[WINED3D_GL_VERSION_3_2] + && gl_info->supported[ARB_POLYGON_OFFSET_CLAMP] + && gl_info->supported[ARB_SAMPLER_OBJECTS]) + { + if (shader_model >= 5 + && gl_info->supported[ARB_DRAW_INDIRECT] + && gl_info->supported[ARB_TEXTURE_COMPRESSION_BPTC]) + return WINED3D_FEATURE_LEVEL_11_1; + + if (shader_model >= 4) + { + if (gl_info->supported[ARB_TEXTURE_CUBE_MAP_ARRAY] + && gl_info->supported[ARB_DRAW_BUFFERS_BLEND]) + return WINED3D_FEATURE_LEVEL_10_1; + return WINED3D_FEATURE_LEVEL_10; + } + } + + if (shader_model >= 3 && gl_info->limits.texture_size >= 4096 && gl_info->limits.buffers >= 4) + return WINED3D_FEATURE_LEVEL_9_3; + if (shader_model >= 2) + { + if (gl_info->supported[ARB_OCCLUSION_QUERY] + && gl_info->supported[ARB_TEXTURE_MIRROR_CLAMP_TO_EDGE] + && gl_info->supported[EXT_BLEND_EQUATION_SEPARATE] + && gl_info->supported[EXT_BLEND_FUNC_SEPARATE]) + return WINED3D_FEATURE_LEVEL_9_2; + + return WINED3D_FEATURE_LEVEL_9_1; + } + if (shader_model >= 1) + return WINED3D_FEATURE_LEVEL_8; + + if (fragment_caps->TextureOpCaps & WINED3DTEXOPCAPS_DOTPRODUCT3) + return WINED3D_FEATURE_LEVEL_7; + if (fragment_caps->MaxSimultaneousTextures > 1) + return WINED3D_FEATURE_LEVEL_6; + + return WINED3D_FEATURE_LEVEL_5; +} + +static const struct wined3d_renderer_table +{ + const char *renderer; + enum wined3d_pci_device id; +} +cards_nvidia_binary[] = +{ + /* Direct 3D 11 */ + {"RTX 2080 Ti", CARD_NVIDIA_GEFORCE_RTX2080TI}, /* GeForce 2000 - highend */ + {"RTX 2080", CARD_NVIDIA_GEFORCE_RTX2080}, /* GeForce 2000 - highend */ + {"RTX 2070", CARD_NVIDIA_GEFORCE_RTX2070}, /* GeForce 2000 - highend */ + {"RTX 2060", CARD_NVIDIA_GEFORCE_RTX2060}, /* GeForce 2000 - highend */ + {"GTX 1660 Ti", CARD_NVIDIA_GEFORCE_GTX1660TI}, /* GeForce 1600 - highend */ + {"GTX 1660 SUPER", CARD_NVIDIA_GEFORCE_GTX1660SUPER}, /* GeForce 1600 - highend */ + {"GTX 1650 SUPER", CARD_NVIDIA_GEFORCE_GTX1650SUPER}, /* GeForce 1600 - midend high */ + {"TITAN V", CARD_NVIDIA_TITANV}, /* GeForce 1000 - highend */ + {"TITAN X (Pascal)", CARD_NVIDIA_TITANX_PASCAL}, /* GeForce 1000 - highend */ + {"GTX 1080 Ti", CARD_NVIDIA_GEFORCE_GTX1080TI}, /* GeForce 1000 - highend */ + {"GTX 1080", CARD_NVIDIA_GEFORCE_GTX1080}, /* GeForce 1000 - highend */ + {"GTX 1070", CARD_NVIDIA_GEFORCE_GTX1070}, /* GeForce 1000 - highend */ + {"GTX 1060 3GB", CARD_NVIDIA_GEFORCE_GTX1060_3GB},/* GeForce 1000 - midend high */ + {"GTX 1060", CARD_NVIDIA_GEFORCE_GTX1060}, /* GeForce 1000 - midend high */ + {"GTX 1050 Ti", CARD_NVIDIA_GEFORCE_GTX1050TI}, /* GeForce 1000 - midend */ + {"GTX 1050", CARD_NVIDIA_GEFORCE_GTX1050}, /* GeForce 1000 - midend */ + {"GTX 980 Ti", CARD_NVIDIA_GEFORCE_GTX980TI}, /* GeForce 900 - highend */ + {"GTX 980", CARD_NVIDIA_GEFORCE_GTX980}, /* GeForce 900 - highend */ + {"GTX 970M", CARD_NVIDIA_GEFORCE_GTX970M}, /* GeForce 900 - highend mobile*/ + {"GTX 970", CARD_NVIDIA_GEFORCE_GTX970}, /* GeForce 900 - highend */ + {"GTX TITAN X", CARD_NVIDIA_GEFORCE_GTXTITANX}, /* Geforce 900 - highend */ + {"GTX 960M", CARD_NVIDIA_GEFORCE_GTX960M}, /* GeForce 900 - midend high mobile */ + {"GTX 960", CARD_NVIDIA_GEFORCE_GTX960}, /* GeForce 900 - midend high */ + {"GTX 950M", CARD_NVIDIA_GEFORCE_GTX950M}, /* GeForce 900 - midend mobile */ + {"GTX 950", CARD_NVIDIA_GEFORCE_GTX950}, /* GeForce 900 - midend */ + {"GeForce 940M", CARD_NVIDIA_GEFORCE_940M}, /* GeForce 900 - midend mobile */ + {"GTX 880M", CARD_NVIDIA_GEFORCE_GTX880M}, /* GeForce 800 - mobile */ + {"GTX 870M", CARD_NVIDIA_GEFORCE_GTX870M}, /* GeForce 800 - mobile */ + {"GTX 860M", CARD_NVIDIA_GEFORCE_GTX860M}, /* GeForce 800 - mobile */ + {"GTX 850M", CARD_NVIDIA_GEFORCE_GTX850M}, /* GeForce 800 - mobile */ + {"GeForce 845M", CARD_NVIDIA_GEFORCE_845M}, /* GeForce 800 - mobile */ + {"GeForce 840M", CARD_NVIDIA_GEFORCE_840M}, /* GeForce 800 - mobile */ + {"GeForce 830M", CARD_NVIDIA_GEFORCE_830M}, /* GeForce 800 - mobile */ + {"GeForce 820M", CARD_NVIDIA_GEFORCE_820M}, /* GeForce 800 - mobile */ + {"GTX 780 Ti", CARD_NVIDIA_GEFORCE_GTX780TI}, /* Geforce 700 - highend */ + {"GTX TITAN Black", CARD_NVIDIA_GEFORCE_GTXTITANB}, /* Geforce 700 - highend */ + {"GTX TITAN Z", CARD_NVIDIA_GEFORCE_GTXTITANZ}, /* Geforce 700 - highend */ + {"GTX TITAN", CARD_NVIDIA_GEFORCE_GTXTITAN}, /* Geforce 700 - highend */ + {"GTX 780", CARD_NVIDIA_GEFORCE_GTX780}, /* Geforce 700 - highend */ + {"GTX 770M", CARD_NVIDIA_GEFORCE_GTX770M}, /* Geforce 700 - midend high mobile */ + {"GTX 770", CARD_NVIDIA_GEFORCE_GTX770}, /* Geforce 700 - highend */ + {"GTX 765M", CARD_NVIDIA_GEFORCE_GTX765M}, /* Geforce 700 - midend high mobile */ + {"GTX 760 Ti", CARD_NVIDIA_GEFORCE_GTX760TI}, /* Geforce 700 - midend high */ + {"GTX 760", CARD_NVIDIA_GEFORCE_GTX760}, /* Geforce 700 - midend high */ + {"GTX 750 Ti", CARD_NVIDIA_GEFORCE_GTX750TI}, /* Geforce 700 - midend */ + {"GTX 750", CARD_NVIDIA_GEFORCE_GTX750}, /* Geforce 700 - midend */ + {"GT 750M", CARD_NVIDIA_GEFORCE_GT750M}, /* Geforce 700 - midend mobile */ + {"GT 740M", CARD_NVIDIA_GEFORCE_GT740M}, /* Geforce 700 - midend mobile */ + {"GT 730M", CARD_NVIDIA_GEFORCE_GT730M}, /* Geforce 700 - midend mobile */ + {"GT 730", CARD_NVIDIA_GEFORCE_GT730}, /* Geforce 700 - lowend */ + {"GTX 690", CARD_NVIDIA_GEFORCE_GTX690}, /* Geforce 600 - highend */ + {"GTX 680", CARD_NVIDIA_GEFORCE_GTX680}, /* Geforce 600 - highend */ + {"GTX 675MX", CARD_NVIDIA_GEFORCE_GTX675MX_1},/* Geforce 600 - highend */ + {"GTX 670MX", CARD_NVIDIA_GEFORCE_GTX670MX}, /* Geforce 600 - highend */ + {"GTX 670", CARD_NVIDIA_GEFORCE_GTX670}, /* Geforce 600 - midend high */ + {"GTX 660 Ti", CARD_NVIDIA_GEFORCE_GTX660TI}, /* Geforce 600 - midend high */ + {"GTX 660M", CARD_NVIDIA_GEFORCE_GTX660M}, /* Geforce 600 - midend high mobile */ + {"GTX 660", CARD_NVIDIA_GEFORCE_GTX660}, /* Geforce 600 - midend high */ + {"GTX 650 Ti", CARD_NVIDIA_GEFORCE_GTX650TI}, /* Geforce 600 - lowend */ + {"GTX 650", CARD_NVIDIA_GEFORCE_GTX650}, /* Geforce 600 - lowend */ + {"GT 650M", CARD_NVIDIA_GEFORCE_GT650M}, /* Geforce 600 - midend mobile */ + {"GT 640M", CARD_NVIDIA_GEFORCE_GT640M}, /* Geforce 600 - midend mobile */ + {"GT 630M", CARD_NVIDIA_GEFORCE_GT630M}, /* Geforce 600 - midend mobile */ + {"GT 630", CARD_NVIDIA_GEFORCE_GT630}, /* Geforce 600 - lowend */ + {"GT 610", CARD_NVIDIA_GEFORCE_GT610}, /* Geforce 600 - lowend */ + {"GTX 580", CARD_NVIDIA_GEFORCE_GTX580}, /* Geforce 500 - highend */ + {"GTX 570", CARD_NVIDIA_GEFORCE_GTX570}, /* Geforce 500 - midend high */ + {"GTX 560 Ti", CARD_NVIDIA_GEFORCE_GTX560TI}, /* Geforce 500 - midend */ + {"GTX 560M", CARD_NVIDIA_GEFORCE_GTX560M}, /* Geforce 500 - midend mobile */ + {"GTX 560", CARD_NVIDIA_GEFORCE_GTX560}, /* Geforce 500 - midend */ + {"GT 555M", CARD_NVIDIA_GEFORCE_GT555M}, /* Geforce 500 - midend mobile */ + {"GTX 550 Ti", CARD_NVIDIA_GEFORCE_GTX550}, /* Geforce 500 - midend */ + {"GT 540M", CARD_NVIDIA_GEFORCE_GT540M}, /* Geforce 500 - midend mobile */ + {"GT 525M", CARD_NVIDIA_GEFORCE_GT525M}, /* Geforce 500 - lowend mobile */ + {"GT 520", CARD_NVIDIA_GEFORCE_GT520}, /* Geforce 500 - lowend */ + {"GTX 480", CARD_NVIDIA_GEFORCE_GTX480}, /* Geforce 400 - highend */ + {"GTX 470", CARD_NVIDIA_GEFORCE_GTX470}, /* Geforce 400 - midend high */ + /* Direct 3D 10 */ + {"GTX 465", CARD_NVIDIA_GEFORCE_GTX465}, /* Geforce 400 - midend */ + {"GTX 460M", CARD_NVIDIA_GEFORCE_GTX460M}, /* Geforce 400 - highend mobile */ + {"GTX 460", CARD_NVIDIA_GEFORCE_GTX460}, /* Geforce 400 - midend */ + {"GTS 450", CARD_NVIDIA_GEFORCE_GTS450}, /* Geforce 400 - midend low */ + {"GT 440", CARD_NVIDIA_GEFORCE_GT440}, /* Geforce 400 - lowend */ + {"GT 430", CARD_NVIDIA_GEFORCE_GT430}, /* Geforce 400 - lowend */ + {"GT 425M", CARD_NVIDIA_GEFORCE_GT425M}, /* Geforce 400 - lowend mobile */ + {"GT 420", CARD_NVIDIA_GEFORCE_GT420}, /* Geforce 400 - lowend */ + {"410M", CARD_NVIDIA_GEFORCE_410M}, /* Geforce 400 - lowend mobile */ + {"GT 330", CARD_NVIDIA_GEFORCE_GT330}, /* Geforce 300 - highend */ + {"GTS 360M", CARD_NVIDIA_GEFORCE_GTS350M}, /* Geforce 300 - highend mobile */ + {"GTS 350M", CARD_NVIDIA_GEFORCE_GTS350M}, /* Geforce 300 - highend mobile */ + {"GT 330M", CARD_NVIDIA_GEFORCE_GT325M}, /* Geforce 300 - midend mobile */ + {"GT 325M", CARD_NVIDIA_GEFORCE_GT325M}, /* Geforce 300 - midend mobile */ + {"GT 320M", CARD_NVIDIA_GEFORCE_GT320M}, /* Geforce 300 - midend mobile */ + {"320M", CARD_NVIDIA_GEFORCE_320M}, /* Geforce 300 - midend mobile */ + {"315M", CARD_NVIDIA_GEFORCE_315M}, /* Geforce 300 - midend mobile */ + {"GTX 295", CARD_NVIDIA_GEFORCE_GTX280}, /* Geforce 200 - highend */ + {"GTX 285", CARD_NVIDIA_GEFORCE_GTX280}, /* Geforce 200 - highend */ + {"GTX 280", CARD_NVIDIA_GEFORCE_GTX280}, /* Geforce 200 - highend */ + {"GTX 275", CARD_NVIDIA_GEFORCE_GTX275}, /* Geforce 200 - midend high */ + {"GTX 260", CARD_NVIDIA_GEFORCE_GTX260}, /* Geforce 200 - midend */ + {"GTS 250", CARD_NVIDIA_GEFORCE_GTS250}, /* Geforce 200 - midend */ + {"GT 240", CARD_NVIDIA_GEFORCE_GT240}, /* Geforce 200 - midend */ + {"GT 220", CARD_NVIDIA_GEFORCE_GT220}, /* Geforce 200 - lowend */ + {"GeForce 310", CARD_NVIDIA_GEFORCE_210}, /* Geforce 200 - lowend */ + {"GeForce 305", CARD_NVIDIA_GEFORCE_210}, /* Geforce 200 - lowend */ + {"GeForce 210", CARD_NVIDIA_GEFORCE_210}, /* Geforce 200 - lowend */ + {"G 210", CARD_NVIDIA_GEFORCE_210}, /* Geforce 200 - lowend */ + {"GTS 150", CARD_NVIDIA_GEFORCE_9800GT}, /* Geforce 9 - highend / Geforce 200 - midend */ + {"9800", CARD_NVIDIA_GEFORCE_9800GT}, /* Geforce 9 - highend / Geforce 200 - midend */ + {"9700M GT", CARD_NVIDIA_GEFORCE_9700MGT}, /* Geforce 9 - midend */ + {"GT 140", CARD_NVIDIA_GEFORCE_9600GT}, /* Geforce 9 - midend */ + {"9600", CARD_NVIDIA_GEFORCE_9600GT}, /* Geforce 9 - midend */ + {"GT 130", CARD_NVIDIA_GEFORCE_9500GT}, /* Geforce 9 - midend low / Geforce 200 - low */ + {"GT 120", CARD_NVIDIA_GEFORCE_9500GT}, /* Geforce 9 - midend low / Geforce 200 - low */ + {"9500", CARD_NVIDIA_GEFORCE_9500GT}, /* Geforce 9 - midend low / Geforce 200 - low */ + {"9400M", CARD_NVIDIA_GEFORCE_9400M}, /* Geforce 9 - lowend */ + {"9400", CARD_NVIDIA_GEFORCE_9400GT}, /* Geforce 9 - lowend */ + {"9300", CARD_NVIDIA_GEFORCE_9300}, /* Geforce 9 - lowend low */ + {"9200", CARD_NVIDIA_GEFORCE_9200}, /* Geforce 9 - lowend low */ + {"9100", CARD_NVIDIA_GEFORCE_9200}, /* Geforce 9 - lowend low */ + {"G 100", CARD_NVIDIA_GEFORCE_9200}, /* Geforce 9 - lowend low */ + {"8800 GTX", CARD_NVIDIA_GEFORCE_8800GTX}, /* Geforce 8 - highend high */ + {"8800", CARD_NVIDIA_GEFORCE_8800GTS}, /* Geforce 8 - highend */ + {"8600M", CARD_NVIDIA_GEFORCE_8600MGT}, /* Geforce 8 - midend mobile */ + {"8600 M", CARD_NVIDIA_GEFORCE_8600MGT}, /* Geforce 8 - midend mobile */ + {"8700", CARD_NVIDIA_GEFORCE_8600GT}, /* Geforce 8 - midend */ + {"8600", CARD_NVIDIA_GEFORCE_8600GT}, /* Geforce 8 - midend */ + {"8500", CARD_NVIDIA_GEFORCE_8500GT}, /* Geforce 8 - mid-lowend */ + {"8400", CARD_NVIDIA_GEFORCE_8400GS}, /* Geforce 8 - mid-lowend */ + {"8300", CARD_NVIDIA_GEFORCE_8300GS}, /* Geforce 8 - lowend */ + {"8200", CARD_NVIDIA_GEFORCE_8200}, /* Geforce 8 - lowend */ + {"8100", CARD_NVIDIA_GEFORCE_8200}, /* Geforce 8 - lowend */ + /* Direct 3D 9 SM3 */ + {"Quadro FX 5", CARD_NVIDIA_GEFORCE_7800GT}, /* Geforce 7 - highend */ + {"Quadro FX 4", CARD_NVIDIA_GEFORCE_7800GT}, /* Geforce 7 - highend */ + {"7950", CARD_NVIDIA_GEFORCE_7800GT}, /* Geforce 7 - highend */ + {"7900", CARD_NVIDIA_GEFORCE_7800GT}, /* Geforce 7 - highend */ + {"7800", CARD_NVIDIA_GEFORCE_7800GT}, /* Geforce 7 - highend */ + {"7700", CARD_NVIDIA_GEFORCE_7600}, /* Geforce 7 - midend */ + {"7600", CARD_NVIDIA_GEFORCE_7600}, /* Geforce 7 - midend */ + {"7400", CARD_NVIDIA_GEFORCE_7400}, /* Geforce 7 - lower medium */ + {"7300", CARD_NVIDIA_GEFORCE_7300}, /* Geforce 7 - lowend */ + {"6800", CARD_NVIDIA_GEFORCE_6800}, /* Geforce 6 - highend */ + {"6700", CARD_NVIDIA_GEFORCE_6600GT}, /* Geforce 6 - midend */ + {"6610", CARD_NVIDIA_GEFORCE_6600GT}, /* Geforce 6 - midend */ + {"6600", CARD_NVIDIA_GEFORCE_6600GT}, /* Geforce 6 - midend */ + /* Direct 3D 9 SM2 */ + {"Quadro FX", CARD_NVIDIA_GEFORCEFX_5800}, /* GeforceFX - highend */ + {"5950", CARD_NVIDIA_GEFORCEFX_5800}, /* GeforceFX - highend */ + {"5900", CARD_NVIDIA_GEFORCEFX_5800}, /* GeforceFX - highend */ + {"5800", CARD_NVIDIA_GEFORCEFX_5800}, /* GeforceFX - highend */ + {"5750", CARD_NVIDIA_GEFORCEFX_5600}, /* GeforceFX - midend */ + {"5700", CARD_NVIDIA_GEFORCEFX_5600}, /* GeforceFX - midend */ + {"5650", CARD_NVIDIA_GEFORCEFX_5600}, /* GeforceFX - midend */ + {"5600", CARD_NVIDIA_GEFORCEFX_5600}, /* GeforceFX - midend */ + {"5500", CARD_NVIDIA_GEFORCEFX_5200}, /* GeforceFX - lowend */ + {"5300", CARD_NVIDIA_GEFORCEFX_5200}, /* GeforceFX - lowend */ + {"5250", CARD_NVIDIA_GEFORCEFX_5200}, /* GeforceFX - lowend */ + {"5200", CARD_NVIDIA_GEFORCEFX_5200}, /* GeforceFX - lowend */ + {"5100", CARD_NVIDIA_GEFORCEFX_5200}, /* GeforceFX - lowend */ + /* Direct 3D 8 */ + {"Quadro4", CARD_NVIDIA_GEFORCE4_TI4200}, + {"GeForce4 Ti", CARD_NVIDIA_GEFORCE4_TI4200}, /* Geforce4 Ti4200/Ti4400/Ti4600/Ti4800 */ + /* Direct 3D 7 */ + {"GeForce4 MX", CARD_NVIDIA_GEFORCE4_MX}, /* MX420/MX440/MX460/MX4000 */ + {"Quadro2 MXR", CARD_NVIDIA_GEFORCE2_MX}, + {"GeForce2 MX", CARD_NVIDIA_GEFORCE2_MX}, /* Geforce2 standard/MX100/MX200/MX400 */ + {"Quadro2", CARD_NVIDIA_GEFORCE2}, + {"GeForce2", CARD_NVIDIA_GEFORCE2}, /* Geforce2 GTS/Pro/Ti/Ultra */ + /* Direct 3D 6 */ + {"TNT2", CARD_NVIDIA_RIVA_TNT2}, /* Riva TNT2 standard/M64/Pro/Ultra */ +}, +/* See http://developer.amd.com/resources/hardware-drivers/ati-catalyst-pc-vendor-id-1002-li/ + * + * Beware: renderer string do not match exact card model, + * e.g. HD 4800 is returned for multiple cards, even for RV790 based ones. */ +cards_amd_binary[] = +{ + {"RX 480", CARD_AMD_RADEON_RX_480}, + {"RX 460", CARD_AMD_RADEON_RX_460}, + {"R9 Fury Series", CARD_AMD_RADEON_R9_FURY}, + /* Southern Islands */ + {"HD 7900", CARD_AMD_RADEON_HD7900}, + {"HD 7800", CARD_AMD_RADEON_HD7800}, + {"HD 7700", CARD_AMD_RADEON_HD7700}, + /* Northern Islands */ + {"HD 6970", CARD_AMD_RADEON_HD6900}, + {"HD 6900", CARD_AMD_RADEON_HD6900}, + {"HD 6800", CARD_AMD_RADEON_HD6800}, + {"HD 6770M", CARD_AMD_RADEON_HD6600M}, + {"HD 6750M", CARD_AMD_RADEON_HD6600M}, + {"HD 6700", CARD_AMD_RADEON_HD6700}, + {"HD 6670", CARD_AMD_RADEON_HD6600}, + {"HD 6630M", CARD_AMD_RADEON_HD6600M}, + {"HD 6600M", CARD_AMD_RADEON_HD6600M}, + {"HD 6600", CARD_AMD_RADEON_HD6600}, + {"HD 6570", CARD_AMD_RADEON_HD6600}, + {"HD 6500M", CARD_AMD_RADEON_HD6600M}, + {"HD 6500", CARD_AMD_RADEON_HD6600}, + {"HD 6480G", CARD_AMD_RADEON_HD6480G}, + {"HD 6400", CARD_AMD_RADEON_HD6400}, + {"HD 6300", CARD_AMD_RADEON_HD6300}, + {"HD 6200", CARD_AMD_RADEON_HD6300}, + /* Evergreen */ + {"HD 5870", CARD_AMD_RADEON_HD5800}, /* Radeon EG CYPRESS PRO */ + {"HD 5850", CARD_AMD_RADEON_HD5800}, /* Radeon EG CYPRESS XT */ + {"HD 5800", CARD_AMD_RADEON_HD5800}, /* Radeon EG CYPRESS HD58xx generic renderer string */ + {"HD 5770", CARD_AMD_RADEON_HD5700}, /* Radeon EG JUNIPER XT */ + {"HD 5750", CARD_AMD_RADEON_HD5700}, /* Radeon EG JUNIPER LE */ + {"HD 5700", CARD_AMD_RADEON_HD5700}, /* Radeon EG JUNIPER HD57xx generic renderer string */ + {"HD 5670", CARD_AMD_RADEON_HD5600}, /* Radeon EG REDWOOD XT */ + {"HD 5570", CARD_AMD_RADEON_HD5600}, /* Radeon EG REDWOOD PRO mapped to HD5600 series */ + {"HD 5550", CARD_AMD_RADEON_HD5600}, /* Radeon EG REDWOOD LE mapped to HD5600 series */ + {"HD 5450", CARD_AMD_RADEON_HD5400}, /* Radeon EG CEDAR PRO */ + {"HD 5000", CARD_AMD_RADEON_HD5600}, /* Defaulting to HD 5600 */ + /* R700 */ + {"HD 4890", CARD_AMD_RADEON_HD4800}, /* Radeon RV790 */ + {"HD 4870", CARD_AMD_RADEON_HD4800}, /* Radeon RV770 */ + {"HD 4850", CARD_AMD_RADEON_HD4800}, /* Radeon RV770 */ + {"HD 4830", CARD_AMD_RADEON_HD4800}, /* Radeon RV770 */ + {"HD 4800", CARD_AMD_RADEON_HD4800}, /* Radeon RV7xx HD48xx generic renderer string */ + {"HD 4770", CARD_AMD_RADEON_HD4700}, /* Radeon RV740 */ + {"HD 4700", CARD_AMD_RADEON_HD4700}, /* Radeon RV7xx HD47xx generic renderer string */ + {"HD 4670", CARD_AMD_RADEON_HD4600}, /* Radeon RV730 */ + {"HD 4650", CARD_AMD_RADEON_HD4600}, /* Radeon RV730 */ + {"HD 4600", CARD_AMD_RADEON_HD4600}, /* Radeon RV730 */ + {"HD 4550", CARD_AMD_RADEON_HD4350}, /* Radeon RV710 */ + {"HD 4350", CARD_AMD_RADEON_HD4350}, /* Radeon RV710 */ + /* R600/R700 integrated */ + {"HD 4200M", CARD_AMD_RADEON_HD4200M}, + {"HD 3300", CARD_AMD_RADEON_HD3200}, + {"HD 3200", CARD_AMD_RADEON_HD3200}, + {"HD 3100", CARD_AMD_RADEON_HD3200}, + /* R600 */ + {"HD 3870", CARD_AMD_RADEON_HD2900}, /* HD2900/HD3800 - highend */ + {"HD 3850", CARD_AMD_RADEON_HD2900}, /* HD2900/HD3800 - highend */ + {"HD 2900", CARD_AMD_RADEON_HD2900}, /* HD2900/HD3800 - highend */ + {"HD 3830", CARD_AMD_RADEON_HD2600}, /* China-only midend */ + {"HD 3690", CARD_AMD_RADEON_HD2600}, /* HD2600/HD3600 - midend */ + {"HD 3650", CARD_AMD_RADEON_HD2600}, /* HD2600/HD3600 - midend */ + {"HD 2600", CARD_AMD_RADEON_HD2600}, /* HD2600/HD3600 - midend */ + {"HD 3470", CARD_AMD_RADEON_HD2350}, /* HD2350/HD2400/HD3400 - lowend */ + {"HD 3450", CARD_AMD_RADEON_HD2350}, /* HD2350/HD2400/HD3400 - lowend */ + {"HD 3430", CARD_AMD_RADEON_HD2350}, /* HD2350/HD2400/HD3400 - lowend */ + {"HD 3400", CARD_AMD_RADEON_HD2350}, /* HD2350/HD2400/HD3400 - lowend */ + {"HD 2400", CARD_AMD_RADEON_HD2350}, /* HD2350/HD2400/HD3400 - lowend */ + {"HD 2350", CARD_AMD_RADEON_HD2350}, /* HD2350/HD2400/HD3400 - lowend */ + /* Radeon R5xx */ + {"X1950", CARD_AMD_RADEON_X1600}, + {"X1900", CARD_AMD_RADEON_X1600}, + {"X1800", CARD_AMD_RADEON_X1600}, + {"X1650", CARD_AMD_RADEON_X1600}, + {"X1600", CARD_AMD_RADEON_X1600}, + /* Radeon R4xx + X1300/X1400/X1450/X1550/X2300/X2500/HD2300 (lowend R5xx) + * Note X2300/X2500/HD2300 are R5xx GPUs with a 2xxx naming but they are still DX9-only */ + {"HD 2300", CARD_AMD_RADEON_X700}, + {"X2500", CARD_AMD_RADEON_X700}, + {"X2300", CARD_AMD_RADEON_X700}, + {"X1550", CARD_AMD_RADEON_X700}, + {"X1450", CARD_AMD_RADEON_X700}, + {"X1400", CARD_AMD_RADEON_X700}, + {"X1300", CARD_AMD_RADEON_X700}, + {"X850", CARD_AMD_RADEON_X700}, + {"X800", CARD_AMD_RADEON_X700}, + {"X700", CARD_AMD_RADEON_X700}, + /* Radeon Xpress Series - onboard, DX9b, Shader 2.0, 300-400 MHz */ + {"Radeon Xpress", CARD_AMD_RADEON_XPRESS_200M}, +}, +cards_intel[] = +{ + /* Skylake */ + {"Iris Pro Graphics P580", CARD_INTEL_IPP580_1}, + {"Skylake", CARD_INTEL_HD520_1}, + /* Broadwell */ + {"Iris Pro P6300", CARD_INTEL_IPP6300}, + {"Iris Pro 6200", CARD_INTEL_IP6200}, + {"Iris 6100", CARD_INTEL_I6100}, + {"Iris(TM) Graphics 6100", CARD_INTEL_I6100}, /* MacOS */ + /* Haswell */ + {"Iris Pro 5200", CARD_INTEL_IP5200_1}, + {"Iris 5100", CARD_INTEL_I5100_1}, + {"HD Graphics 5000", CARD_INTEL_HD5000_1}, /* MacOS */ + {"Haswell Mobile", CARD_INTEL_HWM}, + {"Iris OpenGL Engine", CARD_INTEL_HWM}, /* MacOS */ + /* Ivybridge */ + {"Ivybridge Server", CARD_INTEL_IVBS}, + {"Ivybridge Mobile", CARD_INTEL_IVBM}, + {"Ivybridge Desktop", CARD_INTEL_IVBD}, + {"HD Graphics 4000", CARD_INTEL_IVBD}, /* MacOS */ + /* Sandybridge */ + {"Sandybridge Server", CARD_INTEL_SNBS}, + {"Sandybridge Mobile", CARD_INTEL_SNBM}, + {"Sandybridge Desktop", CARD_INTEL_SNBD}, + /* Ironlake */ + {"Ironlake Mobile", CARD_INTEL_ILKM}, + {"Ironlake Desktop", CARD_INTEL_ILKD}, + /* G4x */ + {"B43", CARD_INTEL_B43}, + {"G41", CARD_INTEL_G41}, + {"G45", CARD_INTEL_G45}, + {"Q45", CARD_INTEL_Q45}, + {"Integrated Graphics Device", CARD_INTEL_IGD}, + {"GM45", CARD_INTEL_GM45}, + /* i965 */ + {"965GME", CARD_INTEL_965GME}, + {"965GM", CARD_INTEL_965GM}, + {"X3100", CARD_INTEL_965GM}, /* MacOS */ + {"946GZ", CARD_INTEL_946GZ}, + {"965G", CARD_INTEL_965G}, + {"965Q", CARD_INTEL_965Q}, + /* i945 */ + {"Pineview M", CARD_INTEL_PNVM}, + {"Pineview G", CARD_INTEL_PNVG}, + {"IGD", CARD_INTEL_PNVG}, + {"Q33", CARD_INTEL_Q33}, + {"G33", CARD_INTEL_G33}, + {"Q35", CARD_INTEL_Q35}, + {"945GME", CARD_INTEL_945GME}, + {"945GM", CARD_INTEL_945GM}, + {"GMA 950", CARD_INTEL_945GM}, /* MacOS */ + {"945G", CARD_INTEL_945G}, + /* i915 */ + {"915GM", CARD_INTEL_915GM}, + {"E7221G", CARD_INTEL_E7221G}, + {"915G", CARD_INTEL_915G}, + /* i8xx */ + {"865G", CARD_INTEL_865G}, + {"845G", CARD_INTEL_845G}, + {"855GM", CARD_INTEL_855GM}, + {"830M", CARD_INTEL_830M}, +}, +/* 20101109 - These are never returned by current Gallium radeon + * drivers: R700, RV790, R680, RV535, RV516, R410, RS485, RV360, RV351. */ +cards_amd_mesa[] = +{ + /* Navi 10/14 */ + {"NAVI10", CARD_AMD_RADEON_RX_NAVI_10}, + {"NAVI14", CARD_AMD_RADEON_RX_NAVI_14}, + /* Polaris 10/11 */ + {"POLARIS10", CARD_AMD_RADEON_RX_480}, + {"POLARIS11", CARD_AMD_RADEON_RX_460}, + /* Volcanic Islands */ + {"FIJI", CARD_AMD_RADEON_R9_FURY}, + {"TONGA", CARD_AMD_RADEON_R9_285}, + /* Sea Islands */ + {"HAWAII", CARD_AMD_RADEON_R9_290}, + {"KAVERI", CARD_AMD_RADEON_R7 }, + {"KABINI", CARD_AMD_RADEON_R3 }, + {"BONAIRE", CARD_AMD_RADEON_HD8770}, + /* Southern Islands */ + {"OLAND", CARD_AMD_RADEON_HD8670}, + {"HAINAN", CARD_AMD_RADEON_HD8600M}, + {"TAHITI", CARD_AMD_RADEON_HD7900}, + {"PITCAIRN", CARD_AMD_RADEON_HD7800}, + {"CAPE VERDE", CARD_AMD_RADEON_HD7700}, + /* Northern Islands */ + {"ARUBA", CARD_AMD_RADEON_HD7660D}, + {"CAYMAN", CARD_AMD_RADEON_HD6900}, + {"BARTS", CARD_AMD_RADEON_HD6800}, + {"TURKS", CARD_AMD_RADEON_HD6600}, + {"SUMO2", CARD_AMD_RADEON_HD6410D}, /* SUMO2 first, because we do a strstr(). */ + {"SUMO", CARD_AMD_RADEON_HD6550D}, + {"CAICOS", CARD_AMD_RADEON_HD6400}, + {"PALM", CARD_AMD_RADEON_HD6300}, + /* Evergreen */ + {"HEMLOCK", CARD_AMD_RADEON_HD5900}, + {"CYPRESS", CARD_AMD_RADEON_HD5800}, + {"JUNIPER", CARD_AMD_RADEON_HD5700}, + {"REDWOOD", CARD_AMD_RADEON_HD5600}, + {"CEDAR", CARD_AMD_RADEON_HD5400}, + /* R700 */ + {"R700", CARD_AMD_RADEON_HD4800}, + {"RV790", CARD_AMD_RADEON_HD4800}, + {"RV770", CARD_AMD_RADEON_HD4800}, + {"RV740", CARD_AMD_RADEON_HD4700}, + {"RV730", CARD_AMD_RADEON_HD4600}, + {"RV710", CARD_AMD_RADEON_HD4350}, + /* R600/R700 integrated */ + {"RS880", CARD_AMD_RADEON_HD4200M}, + {"RS780", CARD_AMD_RADEON_HD3200}, + /* R600 */ + {"R680", CARD_AMD_RADEON_HD2900}, + {"R600", CARD_AMD_RADEON_HD2900}, + {"RV670", CARD_AMD_RADEON_HD3850}, + {"RV635", CARD_AMD_RADEON_HD2600}, + {"RV630", CARD_AMD_RADEON_HD2600}, + {"RV620", CARD_AMD_RADEON_HD2350}, + {"RV610", CARD_AMD_RADEON_HD2350}, + /* R500 */ + {"R580", CARD_AMD_RADEON_X1600}, + {"R520", CARD_AMD_RADEON_X1600}, + {"RV570", CARD_AMD_RADEON_X1600}, + {"RV560", CARD_AMD_RADEON_X1600}, + {"RV535", CARD_AMD_RADEON_X1600}, + {"RV530", CARD_AMD_RADEON_X1600}, + {"RV516", CARD_AMD_RADEON_X700}, + {"RV515", CARD_AMD_RADEON_X700}, + /* R400 */ + {"R481", CARD_AMD_RADEON_X700}, + {"R480", CARD_AMD_RADEON_X700}, + {"R430", CARD_AMD_RADEON_X700}, + {"R423", CARD_AMD_RADEON_X700}, + {"R420", CARD_AMD_RADEON_X700}, + {"R410", CARD_AMD_RADEON_X700}, + {"RV410", CARD_AMD_RADEON_X700}, + /* Radeon Xpress - onboard, DX9b, Shader 2.0, 300-400 MHz */ + {"RS740", CARD_AMD_RADEON_XPRESS_200M}, + {"RS690", CARD_AMD_RADEON_XPRESS_200M}, + {"RS600", CARD_AMD_RADEON_XPRESS_200M}, + {"RS485", CARD_AMD_RADEON_XPRESS_200M}, + {"RS482", CARD_AMD_RADEON_XPRESS_200M}, + {"RS480", CARD_AMD_RADEON_XPRESS_200M}, + {"RS400", CARD_AMD_RADEON_XPRESS_200M}, + {"RC410", CARD_AMD_RADEON_XPRESS_200M}, + /* R300 */ + {"R360", CARD_AMD_RADEON_9500}, + {"R350", CARD_AMD_RADEON_9500}, + {"R300", CARD_AMD_RADEON_9500}, + {"RV380", CARD_AMD_RADEON_9500}, + {"RV370", CARD_AMD_RADEON_9500}, + {"RV360", CARD_AMD_RADEON_9500}, + {"RV351", CARD_AMD_RADEON_9500}, + {"RV350", CARD_AMD_RADEON_9500}, +}, +cards_nvidia_mesa[] = +{ + /* Maxwell */ + {"NV124", CARD_NVIDIA_GEFORCE_GTX970}, + {"NV120", CARD_NVIDIA_GEFORCE_GTX980TI}, + {"NV118", CARD_NVIDIA_GEFORCE_840M}, + {"NV117", CARD_NVIDIA_GEFORCE_GTX750}, + /* Kepler */ + {"NV108", CARD_NVIDIA_GEFORCE_GT740M}, + {"NV106", CARD_NVIDIA_GEFORCE_GT720}, + {"NVF1", CARD_NVIDIA_GEFORCE_GTX780TI}, + {"NVF0", CARD_NVIDIA_GEFORCE_GTX780}, + {"NVE6", CARD_NVIDIA_GEFORCE_GTX770M}, + {"NVE4", CARD_NVIDIA_GEFORCE_GTX680}, /* 690 / 675MX / 760TI */ + /* Fermi */ + {"NVD9", CARD_NVIDIA_GEFORCE_GT520}, + {"NVD7", CARD_NVIDIA_GEFORCE_820M}, + {"NVCF", CARD_NVIDIA_GEFORCE_GTX550}, + {"NVCE", CARD_NVIDIA_GEFORCE_GTX560}, + {"NVC8", CARD_NVIDIA_GEFORCE_GTX570}, + {"NVC4", CARD_NVIDIA_GEFORCE_GTX460}, + {"NVC3", CARD_NVIDIA_GEFORCE_GT440}, + {"NVC1", CARD_NVIDIA_GEFORCE_GT420}, + {"NVC0", CARD_NVIDIA_GEFORCE_GTX480}, + /* Tesla */ + {"NVAF", CARD_NVIDIA_GEFORCE_GT320M}, + {"NVAC", CARD_NVIDIA_GEFORCE_8200}, + {"NVAA", CARD_NVIDIA_GEFORCE_8200}, /* 8100 */ + {"NVA8", CARD_NVIDIA_GEFORCE_210}, + {"NVA5", CARD_NVIDIA_GEFORCE_GT220}, + {"NVA3", CARD_NVIDIA_GEFORCE_GT240}, + {"NVA0", CARD_NVIDIA_GEFORCE_GTX280}, + {"NV98", CARD_NVIDIA_GEFORCE_9200}, + {"NV96", CARD_NVIDIA_GEFORCE_9400GT}, + {"NV94", CARD_NVIDIA_GEFORCE_9600GT}, + {"NV92", CARD_NVIDIA_GEFORCE_9800GT}, + {"NV86", CARD_NVIDIA_GEFORCE_8500GT}, + {"NV84", CARD_NVIDIA_GEFORCE_8600GT}, + {"NV50", CARD_NVIDIA_GEFORCE_8800GTX}, + /* Curie */ + {"NV68", CARD_NVIDIA_GEFORCE_6200}, /* 7050 */ + {"NV67", CARD_NVIDIA_GEFORCE_6200}, /* 7000M */ + {"NV63", CARD_NVIDIA_GEFORCE_6200}, /* 7100 */ + {"NV4E", CARD_NVIDIA_GEFORCE_6200}, /* 6100 Go / 6150 Go */ + {"NV4C", CARD_NVIDIA_GEFORCE_6200}, /* 6150SE */ + {"NV4B", CARD_NVIDIA_GEFORCE_7600}, + {"NV4A", CARD_NVIDIA_GEFORCE_6200}, + {"NV49", CARD_NVIDIA_GEFORCE_7800GT}, /* 7900 */ + {"NV47", CARD_NVIDIA_GEFORCE_7800GT}, + {"NV46", CARD_NVIDIA_GEFORCE_7400}, + {"NV45", CARD_NVIDIA_GEFORCE_6800}, + {"NV44", CARD_NVIDIA_GEFORCE_6200}, + {"NV43", CARD_NVIDIA_GEFORCE_6600GT}, + {"NV42", CARD_NVIDIA_GEFORCE_6800}, + {"NV41", CARD_NVIDIA_GEFORCE_6800}, + {"NV40", CARD_NVIDIA_GEFORCE_6800}, + /* Rankine */ + {"NV38", CARD_NVIDIA_GEFORCEFX_5800}, /* FX 5950 Ultra */ + {"NV36", CARD_NVIDIA_GEFORCEFX_5800}, /* FX 5700/5750 */ + {"NV35", CARD_NVIDIA_GEFORCEFX_5800}, /* FX 5900 */ + {"NV34", CARD_NVIDIA_GEFORCEFX_5200}, + {"NV31", CARD_NVIDIA_GEFORCEFX_5600}, + {"NV30", CARD_NVIDIA_GEFORCEFX_5800}, + /* Kelvin */ + {"nv28", CARD_NVIDIA_GEFORCE4_TI4200}, + {"nv25", CARD_NVIDIA_GEFORCE4_TI4200}, + {"nv20", CARD_NVIDIA_GEFORCE3}, + /* Celsius */ + {"nv1F", CARD_NVIDIA_GEFORCE4_MX}, /* GF4 MX IGP */ + {"nv1A", CARD_NVIDIA_GEFORCE2}, /* GF2 IGP */ + {"nv18", CARD_NVIDIA_GEFORCE4_MX}, + {"nv17", CARD_NVIDIA_GEFORCE4_MX}, + {"nv16", CARD_NVIDIA_GEFORCE2}, + {"nv15", CARD_NVIDIA_GEFORCE2}, + {"nv11", CARD_NVIDIA_GEFORCE2_MX}, + {"nv10", CARD_NVIDIA_GEFORCE}, + /* Fahrenheit */ + {"nv05", CARD_NVIDIA_RIVA_TNT2}, + {"nv04", CARD_NVIDIA_RIVA_TNT}, + {"nv03", CARD_NVIDIA_RIVA_128}, +}, +cards_redhat[] = +{ + {"virgl", CARD_REDHAT_VIRGL}, +}, +cards_vmware[] = +{ + {"SVGA3D", CARD_VMWARE_SVGA3D}, +}; + +static const struct gl_vendor_selection +{ + enum wined3d_gl_vendor gl_vendor; + const char *description; /* Description of the card selector i.e. Apple OS/X Intel */ + const struct wined3d_renderer_table *cards; /* To be used as cards[], pointer to the first member in an array */ + size_t cards_size; /* Number of entries in the array above */ +} +amd_gl_vendor_table[] = +{ + {GL_VENDOR_APPLE, "Apple OSX AMD/ATI binary driver", cards_amd_binary, ARRAY_SIZE(cards_amd_binary)}, + {GL_VENDOR_FGLRX, "AMD/ATI binary driver", cards_amd_binary, ARRAY_SIZE(cards_amd_binary)}, + {GL_VENDOR_MESA, "Mesa AMD/ATI driver", cards_amd_mesa, ARRAY_SIZE(cards_amd_mesa)}, +}, +nvidia_gl_vendor_table[] = +{ + {GL_VENDOR_APPLE, "Apple OSX NVidia binary driver", cards_nvidia_binary, ARRAY_SIZE(cards_nvidia_binary)}, + {GL_VENDOR_MESA, "Mesa Nouveau driver", cards_nvidia_mesa, ARRAY_SIZE(cards_nvidia_mesa)}, + {GL_VENDOR_NVIDIA, "NVIDIA binary driver", cards_nvidia_binary, ARRAY_SIZE(cards_nvidia_binary)}, +}, +redhat_gl_vendor_table[] = +{ + {GL_VENDOR_MESA, "Red Hat driver", cards_redhat, ARRAY_SIZE(cards_redhat)}, +}, +vmware_gl_vendor_table[] = +{ + {GL_VENDOR_MESA, "VMware driver", cards_vmware, ARRAY_SIZE(cards_vmware)}, +}, +intel_gl_vendor_table[] = +{ + {GL_VENDOR_APPLE, "Apple OSX Intel binary driver", cards_intel, ARRAY_SIZE(cards_intel)}, + {GL_VENDOR_MESA, "Mesa Intel driver", cards_intel, ARRAY_SIZE(cards_intel)}, +}; + +static enum wined3d_pci_device select_card_handler(const struct gl_vendor_selection *table, + unsigned int table_size, enum wined3d_gl_vendor gl_vendor, const char *gl_renderer) +{ + unsigned int i, j; + + for (i = 0; i < table_size; ++i) + { + if (table[i].gl_vendor != gl_vendor) + continue; + + TRACE("Applying card selector \"%s\".\n", table[i].description); + + for (j = 0; j < table[i].cards_size; ++j) + { + if (strstr(gl_renderer, table[i].cards[j].renderer)) + return table[i].cards[j].id; + } + return PCI_DEVICE_NONE; + } + FIXME("Couldn't find a suitable card selector for GL vendor %04x (using GL_RENDERER %s)\n", + gl_vendor, debugstr_a(gl_renderer)); + + return PCI_DEVICE_NONE; +} + +static const struct +{ + enum wined3d_pci_vendor card_vendor; + const char *description; /* Description of the card selector i.e. Apple OS/X Intel */ + const struct gl_vendor_selection *gl_vendor_selection; + unsigned int gl_vendor_count; +} +card_vendor_table[] = +{ + {HW_VENDOR_AMD, "AMD", amd_gl_vendor_table, ARRAY_SIZE(amd_gl_vendor_table)}, + {HW_VENDOR_NVIDIA, "NVIDIA", nvidia_gl_vendor_table, ARRAY_SIZE(nvidia_gl_vendor_table)}, + {HW_VENDOR_REDHAT, "Red Hat",redhat_gl_vendor_table, ARRAY_SIZE(redhat_gl_vendor_table)}, + {HW_VENDOR_VMWARE, "VMware", vmware_gl_vendor_table, ARRAY_SIZE(vmware_gl_vendor_table)}, + {HW_VENDOR_INTEL, "Intel", intel_gl_vendor_table, ARRAY_SIZE(intel_gl_vendor_table)}, +}; + +static enum wined3d_pci_device wined3d_guess_card(enum wined3d_feature_level feature_level, + const char *gl_renderer, enum wined3d_gl_vendor *gl_vendor, + enum wined3d_pci_vendor *card_vendor) +{ + /* A Direct3D device object contains the PCI id (vendor + device) of the + * videocard which is used for rendering. Various applications use this + * information to get a rough estimation of the features of the card and + * some might use it for enabling 3d effects only on certain types of + * videocards. In some cases games might even use it to work around bugs + * which happen on certain videocards/driver combinations. The problem is + * that OpenGL only exposes a rendering string containing the name of the + * videocard and not the PCI id. + * + * Various games depend on the PCI id, so somehow we need to provide one. + * A simple option is to parse the renderer string and translate this to + * the right PCI id. This is a lot of work because there are more than 200 + * GPUs just for NVIDIA. Various cards share the same renderer string, so + * the amount of code might be 'small' but there are quite a number of + * exceptions which would make this a pain to maintain. Another way would + * be to query the PCI id from the operating system (assuming this is the + * videocard which is used for rendering which is not always the case). + * This would work but it is not very portable. Second it would not work + * well in, let's say, a remote X situation in which the amount of 3d + * features which can be used is limited. + * + * As said most games only use the PCI id to get an indication of the + * capabilities of the card. It doesn't really matter if the given id is + * the correct one if we return the id of a card with similar 3d features. + * + * The code below checks the OpenGL capabilities of a videocard and matches + * that to a certain level of Direct3D functionality. Once a card passes + * the Direct3D9 check, we know that the card (in case of NVIDIA) is at + * least a GeforceFX. To give a better estimate we do a basic check on the + * renderer string but if that won't pass we return a default card. This + * way is better than maintaining a full card database as even without a + * full database we can return a card with similar features. Second the + * size of the database can be made quite small because when you know what + * type of 3d functionality a card has, you know to which GPU family the + * GPU must belong. Because of this you only have to check a small part of + * the renderer string to distinguish between different models from that + * family. + * + * The code also selects a default amount of video memory which we will + * use for an estimation of the amount of free texture memory. In case of + * real D3D the amount of texture memory includes video memory and system + * memory (to be specific AGP memory or in case of PCIE TurboCache / + * HyperMemory). We don't know how much system memory can be addressed by + * the system but we can make a reasonable estimation about the amount of + * video memory. If the value is slightly wrong it doesn't matter as we + * didn't include AGP-like memory which makes the amount of addressable + * memory higher and second OpenGL isn't that critical it moves to system + * memory behind our backs if really needed. Note that the amount of video + * memory can be overruled using a registry setting. */ + + enum wined3d_pci_device device; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(card_vendor_table); ++i) + { + if (card_vendor_table[i].card_vendor != *card_vendor) + continue; + + TRACE("Applying card selector \"%s\".\n", card_vendor_table[i].description); + device = select_card_handler(card_vendor_table[i].gl_vendor_selection, + card_vendor_table[i].gl_vendor_count, *gl_vendor, gl_renderer); + if (device != PCI_DEVICE_NONE) + return device; + + TRACE("Unrecognized renderer %s, falling back to default.\n", debugstr_a(gl_renderer)); + + return wined3d_gpu_from_feature_level(card_vendor, feature_level); + } + + FIXME("No card selector available for card vendor %04x (using GL_RENDERER %s).\n", + *card_vendor, debugstr_a(gl_renderer)); + + return wined3d_gpu_from_feature_level(card_vendor, feature_level); +} + +static const struct wined3d_vertex_pipe_ops *select_vertex_implementation(const struct wined3d_gl_info *gl_info, + const struct wined3d_shader_backend_ops *shader_backend_ops) +{ + if (shader_backend_ops == &glsl_shader_backend && gl_info->supported[ARB_VERTEX_SHADER]) + return &glsl_vertex_pipe; + return &ffp_vertex_pipe; +} + +static const struct wined3d_fragment_pipe_ops *select_fragment_implementation(const struct wined3d_gl_info *gl_info, + const struct wined3d_shader_backend_ops *shader_backend_ops) +{ + if (shader_backend_ops == &glsl_shader_backend && gl_info->supported[ARB_FRAGMENT_SHADER]) + return &glsl_fragment_pipe; + if (gl_info->supported[ARB_FRAGMENT_PROGRAM]) + return &arbfp_fragment_pipeline; + if (gl_info->supported[ATI_FRAGMENT_SHADER]) + return &atifs_fragment_pipeline; + if (gl_info->supported[NV_REGISTER_COMBINERS] && gl_info->supported[NV_TEXTURE_SHADER2]) + return &nvts_fragment_pipeline; + if (gl_info->supported[NV_REGISTER_COMBINERS]) + return &nvrc_fragment_pipeline; + return &ffp_fragment_pipeline; +} + +static const struct wined3d_shader_backend_ops *select_shader_backend(const struct wined3d_gl_info *gl_info) +{ + BOOL glsl = wined3d_settings.shader_backend == WINED3D_SHADER_BACKEND_AUTO + || wined3d_settings.shader_backend == WINED3D_SHADER_BACKEND_GLSL; + BOOL arb = wined3d_settings.shader_backend == WINED3D_SHADER_BACKEND_AUTO + || wined3d_settings.shader_backend == WINED3D_SHADER_BACKEND_ARB; + + if (!gl_info->supported[WINED3D_GL_LEGACY_CONTEXT] && !glsl) + { + ERR_(winediag)("Ignoring the shader backend registry key. " + "GLSL is the only shader backend available on core profile contexts. " + "You need to explicitly set GL version to use legacy contexts.\n"); + glsl = TRUE; + } + + glsl = glsl && gl_info->glsl_version >= MAKEDWORD_VERSION(1, 20); + + if (glsl && gl_info->supported[ARB_VERTEX_SHADER] && gl_info->supported[ARB_FRAGMENT_SHADER]) + return &glsl_shader_backend; + if (arb && gl_info->supported[ARB_VERTEX_PROGRAM] && gl_info->supported[ARB_FRAGMENT_PROGRAM]) + return &arb_program_shader_backend; + if (glsl && (gl_info->supported[ARB_VERTEX_SHADER] || gl_info->supported[ARB_FRAGMENT_SHADER])) + return &glsl_shader_backend; + if (arb && (gl_info->supported[ARB_VERTEX_PROGRAM] || gl_info->supported[ARB_FRAGMENT_PROGRAM])) + return &arb_program_shader_backend; + return &none_shader_backend; +} + +static void parse_extension_string(struct wined3d_gl_info *gl_info, const char *extensions, + const struct wined3d_extension_map *map, UINT entry_count) +{ + while (*extensions) + { + const char *start; + size_t len; + UINT i; + + while (isspace(*extensions)) + ++extensions; + start = extensions; + while (!isspace(*extensions) && *extensions) + ++extensions; + + len = extensions - start; + if (!len) + continue; + + TRACE("- %s.\n", debugstr_an(start, len)); + + for (i = 0; i < entry_count; ++i) + { + if (len == strlen(map[i].extension_string) + && !memcmp(start, map[i].extension_string, len)) + { + TRACE(" FOUND: %s support.\n", map[i].extension_string); + gl_info->supported[map[i].extension] = TRUE; + break; + } + } + } +} + +static void enumerate_gl_extensions(struct wined3d_gl_info *gl_info, + const struct wined3d_extension_map *map, unsigned int map_entries_count) +{ + const char *gl_extension_name; + unsigned int i, j; + GLint extensions_count; + + gl_info->gl_ops.gl.p_glGetIntegerv(GL_NUM_EXTENSIONS, &extensions_count); + for (i = 0; i < extensions_count; ++i) + { + gl_extension_name = (const char *)GL_EXTCALL(glGetStringi(GL_EXTENSIONS, i)); + TRACE("- %s.\n", debugstr_a(gl_extension_name)); + for (j = 0; j < map_entries_count; ++j) + { + if (!strcmp(gl_extension_name, map[j].extension_string)) + { + TRACE("FOUND: %s support.\n", map[j].extension_string); + gl_info->supported[map[j].extension] = TRUE; + break; + } + } + } +} + +static void load_gl_funcs(struct wined3d_gl_info *gl_info) +{ +#define USE_GL_FUNC(pfn) gl_info->gl_ops.ext.p_##pfn = (void *)wglGetProcAddress(#pfn); + /* GL_APPLE_fence */ + USE_GL_FUNC(glDeleteFencesAPPLE) + USE_GL_FUNC(glFinishFenceAPPLE) + USE_GL_FUNC(glFinishObjectAPPLE) + USE_GL_FUNC(glGenFencesAPPLE) + USE_GL_FUNC(glIsFenceAPPLE) + USE_GL_FUNC(glSetFenceAPPLE) + USE_GL_FUNC(glTestFenceAPPLE) + USE_GL_FUNC(glTestObjectAPPLE) + /* GL_APPLE_flush_buffer_range */ + USE_GL_FUNC(glBufferParameteriAPPLE) + USE_GL_FUNC(glFlushMappedBufferRangeAPPLE) + /* GL_ARB_base_instance */ + USE_GL_FUNC(glDrawArraysInstancedBaseInstance) + USE_GL_FUNC(glDrawElementsInstancedBaseVertexBaseInstance) + /* GL_ARB_blend_func_extended */ + USE_GL_FUNC(glBindFragDataLocationIndexed) + USE_GL_FUNC(glGetFragDataIndex) + /* GL_ARB_buffer_storage */ + USE_GL_FUNC(glBufferStorage) + /* GL_ARB_clear_buffer_object */ + USE_GL_FUNC(glClearBufferData) + USE_GL_FUNC(glClearBufferSubData) + /* GL_ARB_clear_texture */ + USE_GL_FUNC(glClearTexImage) + USE_GL_FUNC(glClearTexSubImage) + /* GL_ARB_clip_control */ + USE_GL_FUNC(glClipControl) + /* GL_ARB_color_buffer_float */ + USE_GL_FUNC(glClampColorARB) + /* GL_ARB_compute_shader */ + USE_GL_FUNC(glDispatchCompute) + USE_GL_FUNC(glDispatchComputeIndirect) + /* GL_ARB_copy_buffer */ + USE_GL_FUNC(glCopyBufferSubData) + /* GL_ARB_copy_image */ + USE_GL_FUNC(glCopyImageSubData) + /* GL_ARB_debug_output */ + USE_GL_FUNC(glDebugMessageCallbackARB) + USE_GL_FUNC(glDebugMessageControlARB) + USE_GL_FUNC(glDebugMessageInsertARB) + USE_GL_FUNC(glGetDebugMessageLogARB) + /* GL_ARB_draw_buffers */ + USE_GL_FUNC(glDrawBuffersARB) + /* GL_ARB_draw_buffers_blend */ + USE_GL_FUNC(glBlendEquationiARB) + USE_GL_FUNC(glBlendEquationSeparateiARB) + USE_GL_FUNC(glBlendFunciARB) + USE_GL_FUNC(glBlendFuncSeparateiARB) + /* GL_ARB_draw_elements_base_vertex */ + USE_GL_FUNC(glDrawElementsBaseVertex) + USE_GL_FUNC(glDrawElementsInstancedBaseVertex) + USE_GL_FUNC(glDrawRangeElementsBaseVertex) + USE_GL_FUNC(glMultiDrawElementsBaseVertex) + /* GL_ARB_draw_indirect */ + USE_GL_FUNC(glDrawArraysIndirect) + USE_GL_FUNC(glDrawElementsIndirect) + /* GL_ARB_draw_instanced */ + USE_GL_FUNC(glDrawArraysInstancedARB) + USE_GL_FUNC(glDrawElementsInstancedARB) + /* GL_ARB_ES2_compatibility */ + USE_GL_FUNC(glReleaseShaderCompiler) + USE_GL_FUNC(glShaderBinary) + USE_GL_FUNC(glGetShaderPrecisionFormat) + USE_GL_FUNC(glDepthRangef) + USE_GL_FUNC(glClearDepthf) + /* GL_ARB_framebuffer_no_attachments */ + USE_GL_FUNC(glFramebufferParameteri) + /* GL_ARB_framebuffer_object */ + USE_GL_FUNC(glBindFramebuffer) + USE_GL_FUNC(glBindRenderbuffer) + USE_GL_FUNC(glBlitFramebuffer) + USE_GL_FUNC(glCheckFramebufferStatus) + USE_GL_FUNC(glDeleteFramebuffers) + USE_GL_FUNC(glDeleteRenderbuffers) + USE_GL_FUNC(glFramebufferRenderbuffer) + USE_GL_FUNC(glFramebufferTexture) + USE_GL_FUNC(glFramebufferTexture1D) + USE_GL_FUNC(glFramebufferTexture2D) + USE_GL_FUNC(glFramebufferTexture3D) + USE_GL_FUNC(glFramebufferTextureLayer) + USE_GL_FUNC(glGenFramebuffers) + USE_GL_FUNC(glGenRenderbuffers) + USE_GL_FUNC(glGenerateMipmap) + USE_GL_FUNC(glGetFramebufferAttachmentParameteriv) + USE_GL_FUNC(glGetRenderbufferParameteriv) + USE_GL_FUNC(glIsFramebuffer) + USE_GL_FUNC(glIsRenderbuffer) + USE_GL_FUNC(glRenderbufferStorage) + USE_GL_FUNC(glRenderbufferStorageMultisample) + /* GL_ARB_geometry_shader4 */ + USE_GL_FUNC(glFramebufferTextureARB) + USE_GL_FUNC(glFramebufferTextureFaceARB) + USE_GL_FUNC(glFramebufferTextureLayerARB) + USE_GL_FUNC(glProgramParameteriARB) + /* GL_ARB_instanced_arrays */ + USE_GL_FUNC(glVertexAttribDivisorARB) + /* GL_ARB_internalformat_query */ + USE_GL_FUNC(glGetInternalformativ) + /* GL_ARB_internalformat_query2 */ + USE_GL_FUNC(glGetInternalformati64v) + /* GL_ARB_map_buffer_range */ + USE_GL_FUNC(glFlushMappedBufferRange) + USE_GL_FUNC(glMapBufferRange) + /* GL_ARB_multisample */ + USE_GL_FUNC(glSampleCoverageARB) + /* GL_ARB_multitexture */ + USE_GL_FUNC(glActiveTextureARB) + USE_GL_FUNC(glClientActiveTextureARB) + USE_GL_FUNC(glMultiTexCoord1fARB) + USE_GL_FUNC(glMultiTexCoord1fvARB) + USE_GL_FUNC(glMultiTexCoord2fARB) + USE_GL_FUNC(glMultiTexCoord2fvARB) + USE_GL_FUNC(glMultiTexCoord2svARB) + USE_GL_FUNC(glMultiTexCoord3fARB) + USE_GL_FUNC(glMultiTexCoord3fvARB) + USE_GL_FUNC(glMultiTexCoord4fARB) + USE_GL_FUNC(glMultiTexCoord4fvARB) + USE_GL_FUNC(glMultiTexCoord4svARB) + /* GL_ARB_occlusion_query */ + USE_GL_FUNC(glBeginQueryARB) + USE_GL_FUNC(glDeleteQueriesARB) + USE_GL_FUNC(glEndQueryARB) + USE_GL_FUNC(glGenQueriesARB) + USE_GL_FUNC(glGetQueryivARB) + USE_GL_FUNC(glGetQueryObjectivARB) + USE_GL_FUNC(glGetQueryObjectuivARB) + USE_GL_FUNC(glIsQueryARB) + /* GL_ARB_point_parameters */ + USE_GL_FUNC(glPointParameterfARB) + USE_GL_FUNC(glPointParameterfvARB) + /* GL_ARB_polgyon_offset_clamp */ + USE_GL_FUNC(glPolygonOffsetClamp) + /* GL_ARB_provoking_vertex */ + USE_GL_FUNC(glProvokingVertex) + /* GL_ARB_sample_shading */ + USE_GL_FUNC(glMinSampleShadingARB) + /* GL_ARB_sampler_objects */ + USE_GL_FUNC(glGenSamplers) + USE_GL_FUNC(glDeleteSamplers) + USE_GL_FUNC(glIsSampler) + USE_GL_FUNC(glBindSampler) + USE_GL_FUNC(glSamplerParameteri) + USE_GL_FUNC(glSamplerParameterf) + USE_GL_FUNC(glSamplerParameteriv) + USE_GL_FUNC(glSamplerParameterfv) + USE_GL_FUNC(glSamplerParameterIiv) + USE_GL_FUNC(glSamplerParameterIuiv) + USE_GL_FUNC(glGetSamplerParameteriv) + USE_GL_FUNC(glGetSamplerParameterfv) + USE_GL_FUNC(glGetSamplerParameterIiv) + USE_GL_FUNC(glGetSamplerParameterIuiv) + /* GL_ARB_shader_atomic_counters */ + USE_GL_FUNC(glGetActiveAtomicCounterBufferiv) + /* GL_ARB_shader_image_load_store */ + USE_GL_FUNC(glBindImageTexture) + USE_GL_FUNC(glMemoryBarrier) + /* GL_ARB_shader_objects */ + USE_GL_FUNC(glAttachObjectARB) + USE_GL_FUNC(glBindAttribLocationARB) + USE_GL_FUNC(glCompileShaderARB) + USE_GL_FUNC(glCreateProgramObjectARB) + USE_GL_FUNC(glCreateShaderObjectARB) + USE_GL_FUNC(glDeleteObjectARB) + USE_GL_FUNC(glDetachObjectARB) + USE_GL_FUNC(glGetActiveUniformARB) + USE_GL_FUNC(glGetAttachedObjectsARB) + USE_GL_FUNC(glGetAttribLocationARB) + USE_GL_FUNC(glGetHandleARB) + USE_GL_FUNC(glGetInfoLogARB) + USE_GL_FUNC(glGetObjectParameterfvARB) + USE_GL_FUNC(glGetObjectParameterivARB) + USE_GL_FUNC(glGetShaderSourceARB) + USE_GL_FUNC(glGetUniformLocationARB) + USE_GL_FUNC(glGetUniformfvARB) + USE_GL_FUNC(glGetUniformivARB) + USE_GL_FUNC(glLinkProgramARB) + USE_GL_FUNC(glShaderSourceARB) + USE_GL_FUNC(glUniform1fARB) + USE_GL_FUNC(glUniform1fvARB) + USE_GL_FUNC(glUniform1iARB) + USE_GL_FUNC(glUniform1ivARB) + USE_GL_FUNC(glUniform2fARB) + USE_GL_FUNC(glUniform2fvARB) + USE_GL_FUNC(glUniform2iARB) + USE_GL_FUNC(glUniform2ivARB) + USE_GL_FUNC(glUniform3fARB) + USE_GL_FUNC(glUniform3fvARB) + USE_GL_FUNC(glUniform3iARB) + USE_GL_FUNC(glUniform3ivARB) + USE_GL_FUNC(glUniform4fARB) + USE_GL_FUNC(glUniform4fvARB) + USE_GL_FUNC(glUniform4iARB) + USE_GL_FUNC(glUniform4ivARB) + USE_GL_FUNC(glUniformMatrix2fvARB) + USE_GL_FUNC(glUniformMatrix3fvARB) + USE_GL_FUNC(glUniformMatrix4fvARB) + USE_GL_FUNC(glUseProgramObjectARB) + USE_GL_FUNC(glValidateProgramARB) + /* GL_ARB_shader_storage_buffer_object */ + USE_GL_FUNC(glShaderStorageBlockBinding) + /* GL_ARB_sync */ + USE_GL_FUNC(glClientWaitSync) + USE_GL_FUNC(glDeleteSync) + USE_GL_FUNC(glFenceSync) + USE_GL_FUNC(glGetInteger64v) + USE_GL_FUNC(glGetSynciv) + USE_GL_FUNC(glIsSync) + USE_GL_FUNC(glWaitSync) + /* GL_ARB_tessellation_shader */ + USE_GL_FUNC(glPatchParameteri) + USE_GL_FUNC(glPatchParameterfv) + /* GL_ARB_texture_buffer_object */ + USE_GL_FUNC(glTexBufferARB) + /* GL_ARB_texture_buffer_range */ + USE_GL_FUNC(glTexBufferRange) + /* GL_ARB_texture_compression */ + USE_GL_FUNC(glCompressedTexImage2DARB) + USE_GL_FUNC(glCompressedTexImage3DARB) + USE_GL_FUNC(glCompressedTexSubImage2DARB) + USE_GL_FUNC(glCompressedTexSubImage3DARB) + USE_GL_FUNC(glGetCompressedTexImageARB) + /* GL_ARB_texture_multisample */ + USE_GL_FUNC(glGetMultisamplefv); + USE_GL_FUNC(glSampleMaski); + USE_GL_FUNC(glTexImage2DMultisample); + USE_GL_FUNC(glTexImage3DMultisample); + /* GL_ARB_texture_storage */ + USE_GL_FUNC(glTexStorage1D) + USE_GL_FUNC(glTexStorage2D) + USE_GL_FUNC(glTexStorage3D) + /* GL_ARB_texture_storage_multisample */ + USE_GL_FUNC(glTexStorage2DMultisample); + USE_GL_FUNC(glTexStorage3DMultisample); + /* GL_ARB_texture_view */ + USE_GL_FUNC(glTextureView) + /* GL_ARB_timer_query */ + USE_GL_FUNC(glQueryCounter) + USE_GL_FUNC(glGetQueryObjectui64v) + /* GL_ARB_transform_feedback2 */ + USE_GL_FUNC(glBindTransformFeedback); + USE_GL_FUNC(glDeleteTransformFeedbacks); + USE_GL_FUNC(glDrawTransformFeedback); + USE_GL_FUNC(glGenTransformFeedbacks); + USE_GL_FUNC(glIsTransformFeedback); + USE_GL_FUNC(glPauseTransformFeedback); + USE_GL_FUNC(glResumeTransformFeedback); + /* GL_ARB_transform_feedback3 */ + USE_GL_FUNC(glBeginQueryIndexed); + USE_GL_FUNC(glDrawTransformFeedbackStream); + USE_GL_FUNC(glEndQueryIndexed); + USE_GL_FUNC(glGetQueryIndexediv); + /* GL_ARB_uniform_buffer_object */ + USE_GL_FUNC(glBindBufferBase) + USE_GL_FUNC(glBindBufferRange) + USE_GL_FUNC(glGetActiveUniformBlockName) + USE_GL_FUNC(glGetActiveUniformBlockiv) + USE_GL_FUNC(glGetActiveUniformName) + USE_GL_FUNC(glGetActiveUniformsiv) + USE_GL_FUNC(glGetIntegeri_v) + USE_GL_FUNC(glGetUniformBlockIndex) + USE_GL_FUNC(glGetUniformIndices) + USE_GL_FUNC(glUniformBlockBinding) + /* GL_ARB_vertex_buffer_object */ + USE_GL_FUNC(glBindBufferARB) + USE_GL_FUNC(glBufferDataARB) + USE_GL_FUNC(glBufferSubDataARB) + USE_GL_FUNC(glDeleteBuffersARB) + USE_GL_FUNC(glGenBuffersARB) + USE_GL_FUNC(glGetBufferParameterivARB) + USE_GL_FUNC(glGetBufferPointervARB) + USE_GL_FUNC(glGetBufferSubDataARB) + USE_GL_FUNC(glIsBufferARB) + USE_GL_FUNC(glMapBufferARB) + USE_GL_FUNC(glUnmapBufferARB) + /* GL_ARB_vertex_program */ + USE_GL_FUNC(glBindProgramARB) + USE_GL_FUNC(glDeleteProgramsARB) + USE_GL_FUNC(glDisableVertexAttribArrayARB) + USE_GL_FUNC(glEnableVertexAttribArrayARB) + USE_GL_FUNC(glGenProgramsARB) + USE_GL_FUNC(glGetProgramivARB) + USE_GL_FUNC(glProgramEnvParameter4fvARB) + USE_GL_FUNC(glProgramLocalParameter4fvARB) + USE_GL_FUNC(glProgramStringARB) + USE_GL_FUNC(glVertexAttrib1dARB) + USE_GL_FUNC(glVertexAttrib1dvARB) + USE_GL_FUNC(glVertexAttrib1fARB) + USE_GL_FUNC(glVertexAttrib1fvARB) + USE_GL_FUNC(glVertexAttrib1sARB) + USE_GL_FUNC(glVertexAttrib1svARB) + USE_GL_FUNC(glVertexAttrib2dARB) + USE_GL_FUNC(glVertexAttrib2dvARB) + USE_GL_FUNC(glVertexAttrib2fARB) + USE_GL_FUNC(glVertexAttrib2fvARB) + USE_GL_FUNC(glVertexAttrib2sARB) + USE_GL_FUNC(glVertexAttrib2svARB) + USE_GL_FUNC(glVertexAttrib3dARB) + USE_GL_FUNC(glVertexAttrib3dvARB) + USE_GL_FUNC(glVertexAttrib3fARB) + USE_GL_FUNC(glVertexAttrib3fvARB) + USE_GL_FUNC(glVertexAttrib3sARB) + USE_GL_FUNC(glVertexAttrib3svARB) + USE_GL_FUNC(glVertexAttrib4NbvARB) + USE_GL_FUNC(glVertexAttrib4NivARB) + USE_GL_FUNC(glVertexAttrib4NsvARB) + USE_GL_FUNC(glVertexAttrib4NubARB) + USE_GL_FUNC(glVertexAttrib4NubvARB) + USE_GL_FUNC(glVertexAttrib4NuivARB) + USE_GL_FUNC(glVertexAttrib4NusvARB) + USE_GL_FUNC(glVertexAttrib4bvARB) + USE_GL_FUNC(glVertexAttrib4dARB) + USE_GL_FUNC(glVertexAttrib4dvARB) + USE_GL_FUNC(glVertexAttrib4fARB) + USE_GL_FUNC(glVertexAttrib4fvARB) + USE_GL_FUNC(glVertexAttrib4ivARB) + USE_GL_FUNC(glVertexAttrib4sARB) + USE_GL_FUNC(glVertexAttrib4svARB) + USE_GL_FUNC(glVertexAttrib4ubvARB) + USE_GL_FUNC(glVertexAttrib4uivARB) + USE_GL_FUNC(glVertexAttrib4usvARB) + USE_GL_FUNC(glVertexAttribPointerARB) + /* GL_ARB_viewport_array */ + USE_GL_FUNC(glDepthRangeArrayv) + USE_GL_FUNC(glDepthRangeIndexed) + USE_GL_FUNC(glGetDoublei_v) + USE_GL_FUNC(glGetFloati_v) + USE_GL_FUNC(glScissorArrayv) + USE_GL_FUNC(glScissorIndexed) + USE_GL_FUNC(glScissorIndexedv) + USE_GL_FUNC(glViewportArrayv) + USE_GL_FUNC(glViewportIndexedf) + USE_GL_FUNC(glViewportIndexedfv) + /* GL_ARB_texture_barrier */ + USE_GL_FUNC(glTextureBarrier); + /* GL_ATI_fragment_shader */ + USE_GL_FUNC(glAlphaFragmentOp1ATI) + USE_GL_FUNC(glAlphaFragmentOp2ATI) + USE_GL_FUNC(glAlphaFragmentOp3ATI) + USE_GL_FUNC(glBeginFragmentShaderATI) + USE_GL_FUNC(glBindFragmentShaderATI) + USE_GL_FUNC(glColorFragmentOp1ATI) + USE_GL_FUNC(glColorFragmentOp2ATI) + USE_GL_FUNC(glColorFragmentOp3ATI) + USE_GL_FUNC(glDeleteFragmentShaderATI) + USE_GL_FUNC(glEndFragmentShaderATI) + USE_GL_FUNC(glGenFragmentShadersATI) + USE_GL_FUNC(glPassTexCoordATI) + USE_GL_FUNC(glSampleMapATI) + USE_GL_FUNC(glSetFragmentShaderConstantATI) + /* GL_ATI_separate_stencil */ + USE_GL_FUNC(glStencilOpSeparateATI) + USE_GL_FUNC(glStencilFuncSeparateATI) + /* GL_EXT_blend_color */ + USE_GL_FUNC(glBlendColorEXT) + /* GL_EXT_blend_equation_separate */ + USE_GL_FUNC(glBlendFuncSeparateEXT) + /* GL_EXT_blend_func_separate */ + USE_GL_FUNC(glBlendEquationSeparateEXT) + /* GL_EXT_blend_minmax */ + USE_GL_FUNC(glBlendEquationEXT) + /* GL_EXT_depth_bounds_test */ + USE_GL_FUNC(glDepthBoundsEXT) + /* GL_EXT_draw_buffers2 */ + USE_GL_FUNC(glColorMaskIndexedEXT) + USE_GL_FUNC(glDisableIndexedEXT) + USE_GL_FUNC(glEnableIndexedEXT) + USE_GL_FUNC(glGetBooleanIndexedvEXT) + USE_GL_FUNC(glGetIntegerIndexedvEXT) + USE_GL_FUNC(glIsEnabledIndexedEXT) + /* GL_EXT_fog_coord */ + USE_GL_FUNC(glFogCoordPointerEXT) + USE_GL_FUNC(glFogCoorddEXT) + USE_GL_FUNC(glFogCoorddvEXT) + USE_GL_FUNC(glFogCoordfEXT) + USE_GL_FUNC(glFogCoordfvEXT) + /* GL_EXT_framebuffer_blit */ + USE_GL_FUNC(glBlitFramebufferEXT) + /* GL_EXT_framebuffer_multisample */ + USE_GL_FUNC(glRenderbufferStorageMultisampleEXT) + /* GL_EXT_framebuffer_object */ + USE_GL_FUNC(glBindFramebufferEXT) + USE_GL_FUNC(glBindRenderbufferEXT) + USE_GL_FUNC(glCheckFramebufferStatusEXT) + USE_GL_FUNC(glDeleteFramebuffersEXT) + USE_GL_FUNC(glDeleteRenderbuffersEXT) + USE_GL_FUNC(glFramebufferRenderbufferEXT) + USE_GL_FUNC(glFramebufferTexture1DEXT) + USE_GL_FUNC(glFramebufferTexture2DEXT) + USE_GL_FUNC(glFramebufferTexture3DEXT) + USE_GL_FUNC(glGenFramebuffersEXT) + USE_GL_FUNC(glGenRenderbuffersEXT) + USE_GL_FUNC(glGenerateMipmapEXT) + USE_GL_FUNC(glGetFramebufferAttachmentParameterivEXT) + USE_GL_FUNC(glGetRenderbufferParameterivEXT) + USE_GL_FUNC(glIsFramebufferEXT) + USE_GL_FUNC(glIsRenderbufferEXT) + USE_GL_FUNC(glRenderbufferStorageEXT) + /* GL_EXT_gpu_program_parameters */ + USE_GL_FUNC(glProgramEnvParameters4fvEXT) + USE_GL_FUNC(glProgramLocalParameters4fvEXT) + /* GL_EXT_gpu_shader4 */ + USE_GL_FUNC(glBindFragDataLocationEXT) + USE_GL_FUNC(glGetFragDataLocationEXT) + USE_GL_FUNC(glGetUniformuivEXT) + USE_GL_FUNC(glGetVertexAttribIivEXT) + USE_GL_FUNC(glGetVertexAttribIuivEXT) + USE_GL_FUNC(glUniform1uiEXT) + USE_GL_FUNC(glUniform1uivEXT) + USE_GL_FUNC(glUniform2uiEXT) + USE_GL_FUNC(glUniform2uivEXT) + USE_GL_FUNC(glUniform3uiEXT) + USE_GL_FUNC(glUniform3uivEXT) + USE_GL_FUNC(glUniform4uiEXT) + USE_GL_FUNC(glUniform4uivEXT) + USE_GL_FUNC(glVertexAttribI1iEXT) + USE_GL_FUNC(glVertexAttribI1ivEXT) + USE_GL_FUNC(glVertexAttribI1uiEXT) + USE_GL_FUNC(glVertexAttribI1uivEXT) + USE_GL_FUNC(glVertexAttribI2iEXT) + USE_GL_FUNC(glVertexAttribI2ivEXT) + USE_GL_FUNC(glVertexAttribI2uiEXT) + USE_GL_FUNC(glVertexAttribI2uivEXT) + USE_GL_FUNC(glVertexAttribI3iEXT) + USE_GL_FUNC(glVertexAttribI3ivEXT) + USE_GL_FUNC(glVertexAttribI3uiEXT) + USE_GL_FUNC(glVertexAttribI3uivEXT) + USE_GL_FUNC(glVertexAttribI4bvEXT) + USE_GL_FUNC(glVertexAttribI4iEXT) + USE_GL_FUNC(glVertexAttribI4ivEXT) + USE_GL_FUNC(glVertexAttribI4svEXT) + USE_GL_FUNC(glVertexAttribI4ubvEXT) + USE_GL_FUNC(glVertexAttribI4uiEXT) + USE_GL_FUNC(glVertexAttribI4uivEXT) + USE_GL_FUNC(glVertexAttribI4usvEXT) + USE_GL_FUNC(glVertexAttribIPointerEXT) + /* GL_EXT_memory_object */ + USE_GL_FUNC(glGetUnsignedBytei_vEXT) + USE_GL_FUNC(glGetUnsignedBytevEXT) + /* GL_EXT_point_parameters */ + USE_GL_FUNC(glPointParameterfEXT) + USE_GL_FUNC(glPointParameterfvEXT) + /* GL_EXT_polgyon_offset_clamp */ + USE_GL_FUNC(glPolygonOffsetClampEXT) + /* GL_EXT_provoking_vertex */ + USE_GL_FUNC(glProvokingVertexEXT) + /* GL_EXT_secondary_color */ + USE_GL_FUNC(glSecondaryColor3fEXT) + USE_GL_FUNC(glSecondaryColor3fvEXT) + USE_GL_FUNC(glSecondaryColor3ubEXT) + USE_GL_FUNC(glSecondaryColor3ubvEXT) + USE_GL_FUNC(glSecondaryColorPointerEXT) + /* GL_EXT_stencil_two_side */ + USE_GL_FUNC(glActiveStencilFaceEXT) + /* GL_EXT_texture3D */ + USE_GL_FUNC(glTexImage3D) + USE_GL_FUNC(glTexImage3DEXT) + USE_GL_FUNC(glTexSubImage3D) + USE_GL_FUNC(glTexSubImage3DEXT) + /* GL_NV_fence */ + USE_GL_FUNC(glDeleteFencesNV) + USE_GL_FUNC(glFinishFenceNV) + USE_GL_FUNC(glGenFencesNV) + USE_GL_FUNC(glGetFenceivNV) + USE_GL_FUNC(glIsFenceNV) + USE_GL_FUNC(glSetFenceNV) + USE_GL_FUNC(glTestFenceNV) + /* GL_NV_half_float */ + USE_GL_FUNC(glColor3hNV) + USE_GL_FUNC(glColor3hvNV) + USE_GL_FUNC(glColor4hNV) + USE_GL_FUNC(glColor4hvNV) + USE_GL_FUNC(glFogCoordhNV) + USE_GL_FUNC(glFogCoordhvNV) + USE_GL_FUNC(glMultiTexCoord1hNV) + USE_GL_FUNC(glMultiTexCoord1hvNV) + USE_GL_FUNC(glMultiTexCoord2hNV) + USE_GL_FUNC(glMultiTexCoord2hvNV) + USE_GL_FUNC(glMultiTexCoord3hNV) + USE_GL_FUNC(glMultiTexCoord3hvNV) + USE_GL_FUNC(glMultiTexCoord4hNV) + USE_GL_FUNC(glMultiTexCoord4hvNV) + USE_GL_FUNC(glNormal3hNV) + USE_GL_FUNC(glNormal3hvNV) + USE_GL_FUNC(glSecondaryColor3hNV) + USE_GL_FUNC(glSecondaryColor3hvNV) + USE_GL_FUNC(glTexCoord1hNV) + USE_GL_FUNC(glTexCoord1hvNV) + USE_GL_FUNC(glTexCoord2hNV) + USE_GL_FUNC(glTexCoord2hvNV) + USE_GL_FUNC(glTexCoord3hNV) + USE_GL_FUNC(glTexCoord3hvNV) + USE_GL_FUNC(glTexCoord4hNV) + USE_GL_FUNC(glTexCoord4hvNV) + USE_GL_FUNC(glVertex2hNV) + USE_GL_FUNC(glVertex2hvNV) + USE_GL_FUNC(glVertex3hNV) + USE_GL_FUNC(glVertex3hvNV) + USE_GL_FUNC(glVertex4hNV) + USE_GL_FUNC(glVertex4hvNV) + USE_GL_FUNC(glVertexAttrib1hNV) + USE_GL_FUNC(glVertexAttrib1hvNV) + USE_GL_FUNC(glVertexAttrib2hNV) + USE_GL_FUNC(glVertexAttrib2hvNV) + USE_GL_FUNC(glVertexAttrib3hNV) + USE_GL_FUNC(glVertexAttrib3hvNV) + USE_GL_FUNC(glVertexAttrib4hNV) + USE_GL_FUNC(glVertexAttrib4hvNV) + USE_GL_FUNC(glVertexAttribs1hvNV) + USE_GL_FUNC(glVertexAttribs2hvNV) + USE_GL_FUNC(glVertexAttribs3hvNV) + USE_GL_FUNC(glVertexAttribs4hvNV) + USE_GL_FUNC(glVertexWeighthNV) + USE_GL_FUNC(glVertexWeighthvNV) + /* GL_NV_point_sprite */ + USE_GL_FUNC(glPointParameteriNV) + USE_GL_FUNC(glPointParameterivNV) + /* GL_NV_register_combiners */ + USE_GL_FUNC(glCombinerInputNV) + USE_GL_FUNC(glCombinerOutputNV) + USE_GL_FUNC(glCombinerParameterfNV) + USE_GL_FUNC(glCombinerParameterfvNV) + USE_GL_FUNC(glCombinerParameteriNV) + USE_GL_FUNC(glCombinerParameterivNV) + USE_GL_FUNC(glFinalCombinerInputNV) + /* GL_NV_texture_barrier */ + USE_GL_FUNC(glTextureBarrierNV); + /* WGL extensions */ + USE_GL_FUNC(wglChoosePixelFormatARB) + USE_GL_FUNC(wglGetExtensionsStringARB) + USE_GL_FUNC(wglGetPixelFormatAttribfvARB) + USE_GL_FUNC(wglGetPixelFormatAttribivARB) + USE_GL_FUNC(wglQueryCurrentRendererIntegerWINE) + USE_GL_FUNC(wglQueryCurrentRendererStringWINE) + USE_GL_FUNC(wglQueryRendererIntegerWINE) + USE_GL_FUNC(wglQueryRendererStringWINE) + USE_GL_FUNC(wglSetPixelFormatWINE) + USE_GL_FUNC(wglSwapIntervalEXT) + + /* Newer core functions */ + USE_GL_FUNC(glActiveTexture) /* OpenGL 1.3 */ + USE_GL_FUNC(glAttachShader) /* OpenGL 2.0 */ + USE_GL_FUNC(glBeginQuery) /* OpenGL 1.5 */ + USE_GL_FUNC(glBeginTransformFeedback) /* OpenGL 3.0 */ + USE_GL_FUNC(glBindAttribLocation) /* OpenGL 2.0 */ + USE_GL_FUNC(glBindBuffer) /* OpenGL 1.5 */ + USE_GL_FUNC(glBindFragDataLocation) /* OpenGL 3.0 */ + USE_GL_FUNC(glBindVertexArray) /* OpenGL 3.0 */ + USE_GL_FUNC(glBlendColor) /* OpenGL 1.4 */ + USE_GL_FUNC(glBlendEquation) /* OpenGL 1.4 */ + USE_GL_FUNC(glBlendEquationi) /* OpenGL 4.0 */ + USE_GL_FUNC(glBlendEquationSeparate) /* OpenGL 2.0 */ + USE_GL_FUNC(glBlendEquationSeparatei) /* OpenGL 4.0 */ + USE_GL_FUNC(glBlendFunci) /* OpenGL 4.0 */ + USE_GL_FUNC(glBlendFuncSeparate) /* OpenGL 1.4 */ + USE_GL_FUNC(glBlendFuncSeparatei) /* OpenGL 4.0 */ + USE_GL_FUNC(glBufferData) /* OpenGL 1.5 */ + USE_GL_FUNC(glBufferSubData) /* OpenGL 1.5 */ + USE_GL_FUNC(glColorMaski) /* OpenGL 3.0 */ + USE_GL_FUNC(glCompileShader) /* OpenGL 2.0 */ + USE_GL_FUNC(glCompressedTexImage2D) /* OpenGL 1.3 */ + USE_GL_FUNC(glCompressedTexImage3D) /* OpenGL 1.3 */ + USE_GL_FUNC(glCompressedTexSubImage2D) /* OpenGL 1.3 */ + USE_GL_FUNC(glCompressedTexSubImage3D) /* OpenGL 1.3 */ + USE_GL_FUNC(glCreateProgram) /* OpenGL 2.0 */ + USE_GL_FUNC(glCreateShader) /* OpenGL 2.0 */ + USE_GL_FUNC(glDebugMessageCallback) /* OpenGL 4.3 */ + USE_GL_FUNC(glDebugMessageControl) /* OpenGL 4.3 */ + USE_GL_FUNC(glDebugMessageInsert) /* OpenGL 4.3 */ + USE_GL_FUNC(glDeleteBuffers) /* OpenGL 1.5 */ + USE_GL_FUNC(glDeleteProgram) /* OpenGL 2.0 */ + USE_GL_FUNC(glDeleteQueries) /* OpenGL 1.5 */ + USE_GL_FUNC(glDeleteShader) /* OpenGL 2.0 */ + USE_GL_FUNC(glDeleteVertexArrays) /* OpenGL 3.0 */ + USE_GL_FUNC(glDetachShader) /* OpenGL 2.0 */ + USE_GL_FUNC(glDisablei) /* OpenGL 3.0 */ + USE_GL_FUNC(glDisableVertexAttribArray) /* OpenGL 2.0 */ + USE_GL_FUNC(glDrawArraysInstanced) /* OpenGL 3.1 */ + USE_GL_FUNC(glDrawBuffers) /* OpenGL 2.0 */ + USE_GL_FUNC(glDrawElementsInstanced) /* OpenGL 3.1 */ + USE_GL_FUNC(glEnablei) /* OpenGL 3.0 */ + USE_GL_FUNC(glEnableVertexAttribArray) /* OpenGL 2.0 */ + USE_GL_FUNC(glEndQuery) /* OpenGL 1.5 */ + USE_GL_FUNC(glEndTransformFeedback) /* OpenGL 3.0 */ + USE_GL_FUNC(glFramebufferTexture) /* OpenGL 3.2 */ + USE_GL_FUNC(glGenBuffers) /* OpenGL 1.5 */ + USE_GL_FUNC(glGenQueries) /* OpenGL 1.5 */ + USE_GL_FUNC(glGenVertexArrays) /* OpenGL 3.0 */ + USE_GL_FUNC(glGetActiveUniform) /* OpenGL 2.0 */ + USE_GL_FUNC(glGetAttachedShaders) /* OpenGL 2.0 */ + USE_GL_FUNC(glGetAttribLocation) /* OpenGL 2.0 */ + USE_GL_FUNC(glGetBooleani_v) /* OpenGL 3.0 */ + USE_GL_FUNC(glGetBufferSubData) /* OpenGL 1.5 */ + USE_GL_FUNC(glGetCompressedTexImage) /* OpenGL 1.3 */ + USE_GL_FUNC(glGetDebugMessageLog) /* OpenGL 4.3 */ + USE_GL_FUNC(glGetIntegeri_v) /* OpenGL 3.0 */ + USE_GL_FUNC(glGetProgramInfoLog) /* OpenGL 2.0 */ + USE_GL_FUNC(glGetProgramiv) /* OpenGL 2.0 */ + USE_GL_FUNC(glGetQueryiv) /* OpenGL 1.5 */ + USE_GL_FUNC(glGetQueryObjectuiv) /* OpenGL 1.5 */ + USE_GL_FUNC(glGetShaderInfoLog) /* OpenGL 2.0 */ + USE_GL_FUNC(glGetShaderiv) /* OpenGL 2.0 */ + USE_GL_FUNC(glGetShaderSource) /* OpenGL 2.0 */ + USE_GL_FUNC(glGetStringi) /* OpenGL 3.0 */ + USE_GL_FUNC(glGetTextureLevelParameteriv) /* OpenGL 4.5 */ + USE_GL_FUNC(glGetTextureParameteriv) /* OpenGL 4.5 */ + USE_GL_FUNC(glGetUniformfv) /* OpenGL 2.0 */ + USE_GL_FUNC(glGetUniformiv) /* OpenGL 2.0 */ + USE_GL_FUNC(glGetUniformLocation) /* OpenGL 2.0 */ + USE_GL_FUNC(glIsEnabledi) /* OpenGL 3.0 */ + USE_GL_FUNC(glLinkProgram) /* OpenGL 2.0 */ + USE_GL_FUNC(glMapBuffer) /* OpenGL 1.5 */ + USE_GL_FUNC(glMinSampleShading) /* OpenGL 4.0 */ + USE_GL_FUNC(glPointParameteri) /* OpenGL 1.4 */ + USE_GL_FUNC(glPointParameteriv) /* OpenGL 1.4 */ + USE_GL_FUNC(glShaderSource) /* OpenGL 2.0 */ + USE_GL_FUNC(glStencilFuncSeparate) /* OpenGL 2.0 */ + USE_GL_FUNC(glStencilOpSeparate) /* OpenGL 2.0 */ + USE_GL_FUNC(glTexBuffer) /* OpenGL 3.1 */ + USE_GL_FUNC(glTexImage3D) /* OpenGL 1.2 */ + USE_GL_FUNC(glTexSubImage3D) /* OpenGL 1.2 */ + USE_GL_FUNC(glTransformFeedbackVaryings) /* OpenGL 3.0 */ + USE_GL_FUNC(glUniform1f) /* OpenGL 2.0 */ + USE_GL_FUNC(glUniform1fv) /* OpenGL 2.0 */ + USE_GL_FUNC(glUniform1i) /* OpenGL 2.0 */ + USE_GL_FUNC(glUniform1iv) /* OpenGL 2.0 */ + USE_GL_FUNC(glUniform2f) /* OpenGL 2.0 */ + USE_GL_FUNC(glUniform2fv) /* OpenGL 2.0 */ + USE_GL_FUNC(glUniform2i) /* OpenGL 2.0 */ + USE_GL_FUNC(glUniform2iv) /* OpenGL 2.0 */ + USE_GL_FUNC(glUniform3f) /* OpenGL 2.0 */ + USE_GL_FUNC(glUniform3fv) /* OpenGL 2.0 */ + USE_GL_FUNC(glUniform3i) /* OpenGL 2.0 */ + USE_GL_FUNC(glUniform3iv) /* OpenGL 2.0 */ + USE_GL_FUNC(glUniform4f) /* OpenGL 2.0 */ + USE_GL_FUNC(glUniform4fv) /* OpenGL 2.0 */ + USE_GL_FUNC(glUniform4i) /* OpenGL 2.0 */ + USE_GL_FUNC(glUniform4iv) /* OpenGL 2.0 */ + USE_GL_FUNC(glUniformMatrix2fv) /* OpenGL 2.0 */ + USE_GL_FUNC(glUniformMatrix3fv) /* OpenGL 2.0 */ + USE_GL_FUNC(glUniformMatrix4fv) /* OpenGL 2.0 */ + USE_GL_FUNC(glUnmapBuffer) /* OpenGL 1.5 */ + USE_GL_FUNC(glUseProgram) /* OpenGL 2.0 */ + USE_GL_FUNC(glValidateProgram) /* OpenGL 2.0 */ + USE_GL_FUNC(glVertexAttrib1f) /* OpenGL 2.0 */ + USE_GL_FUNC(glVertexAttrib1fv) /* OpenGL 2.0 */ + USE_GL_FUNC(glVertexAttrib2f) /* OpenGL 2.0 */ + USE_GL_FUNC(glVertexAttrib2fv) /* OpenGL 2.0 */ + USE_GL_FUNC(glVertexAttrib3f) /* OpenGL 2.0 */ + USE_GL_FUNC(glVertexAttrib3fv) /* OpenGL 2.0 */ + USE_GL_FUNC(glVertexAttrib4f) /* OpenGL 2.0 */ + USE_GL_FUNC(glVertexAttrib4fv) /* OpenGL 2.0 */ + USE_GL_FUNC(glVertexAttrib4Nsv) /* OpenGL 2.0 */ + USE_GL_FUNC(glVertexAttrib4Nub) /* OpenGL 2.0 */ + USE_GL_FUNC(glVertexAttrib4Nubv) /* OpenGL 2.0 */ + USE_GL_FUNC(glVertexAttrib4Nusv) /* OpenGL 2.0 */ + USE_GL_FUNC(glVertexAttrib4sv) /* OpenGL 2.0 */ + USE_GL_FUNC(glVertexAttrib4ubv) /* OpenGL 2.0 */ + USE_GL_FUNC(glVertexAttribDivisor) /* OpenGL 3.3 */ + USE_GL_FUNC(glVertexAttribIPointer) /* OpenGL 3.0 */ + USE_GL_FUNC(glVertexAttribPointer) /* OpenGL 2.0 */ +#undef USE_GL_FUNC + +#ifndef USE_WIN32_OPENGL + /* hack: use the functions directly from the TEB table to bypass the thunks */ + /* note that we still need the above wglGetProcAddress calls to initialize the table */ + gl_info->gl_ops.ext = ((struct opengl_funcs *)NtCurrentTeb()->glTable)->ext; +#endif + +#define MAP_GL_FUNCTION(core_func, ext_func) \ + do \ + { \ + if (!gl_info->gl_ops.ext.p_##core_func) \ + gl_info->gl_ops.ext.p_##core_func = gl_info->gl_ops.ext.p_##ext_func; \ + } while (0) +#define MAP_GL_FUNCTION_CAST(core_func, ext_func) \ + do \ + { \ + if (!gl_info->gl_ops.ext.p_##core_func) \ + gl_info->gl_ops.ext.p_##core_func = (void *)gl_info->gl_ops.ext.p_##ext_func; \ + } while (0) + + MAP_GL_FUNCTION(glActiveTexture, glActiveTextureARB); + MAP_GL_FUNCTION(glAttachShader, glAttachObjectARB); + MAP_GL_FUNCTION(glBeginQuery, glBeginQueryARB); + MAP_GL_FUNCTION(glBindAttribLocation, glBindAttribLocationARB); + MAP_GL_FUNCTION(glBindBuffer, glBindBufferARB); + MAP_GL_FUNCTION(glBindFragDataLocation, glBindFragDataLocationEXT); + MAP_GL_FUNCTION(glBlendColor, glBlendColorEXT); + MAP_GL_FUNCTION(glBlendEquation, glBlendEquationEXT); + MAP_GL_FUNCTION(glBlendEquationi, glBlendEquationiARB); + MAP_GL_FUNCTION(glBlendEquationSeparate, glBlendEquationSeparateEXT); + MAP_GL_FUNCTION(glBlendEquationSeparatei, glBlendEquationSeparateiARB); + MAP_GL_FUNCTION(glBlendFunci, glBlendFunciARB); + MAP_GL_FUNCTION(glBlendFuncSeparate, glBlendFuncSeparateEXT); + MAP_GL_FUNCTION(glBlendFuncSeparatei, glBlendFuncSeparateiARB); + MAP_GL_FUNCTION(glBufferData, glBufferDataARB); + MAP_GL_FUNCTION(glBufferSubData, glBufferSubDataARB); + MAP_GL_FUNCTION(glColorMaski, glColorMaskIndexedEXT); + MAP_GL_FUNCTION(glCompileShader, glCompileShaderARB); + MAP_GL_FUNCTION(glCompressedTexImage2D, glCompressedTexImage2DARB); + MAP_GL_FUNCTION(glCompressedTexImage3D, glCompressedTexImage3DARB); + MAP_GL_FUNCTION(glCompressedTexSubImage2D, glCompressedTexSubImage2DARB); + MAP_GL_FUNCTION(glCompressedTexSubImage3D, glCompressedTexSubImage3DARB); + MAP_GL_FUNCTION(glCreateProgram, glCreateProgramObjectARB); + MAP_GL_FUNCTION(glCreateShader, glCreateShaderObjectARB); + MAP_GL_FUNCTION(glDebugMessageCallback, glDebugMessageCallbackARB); + MAP_GL_FUNCTION(glDebugMessageControl, glDebugMessageControlARB); + MAP_GL_FUNCTION(glDebugMessageInsert, glDebugMessageInsertARB); + MAP_GL_FUNCTION(glDeleteBuffers, glDeleteBuffersARB); + MAP_GL_FUNCTION(glDeleteProgram, glDeleteObjectARB); + MAP_GL_FUNCTION(glDeleteQueries, glDeleteQueriesARB); + MAP_GL_FUNCTION(glDeleteShader, glDeleteObjectARB); + MAP_GL_FUNCTION(glDetachShader, glDetachObjectARB); + MAP_GL_FUNCTION(glDisablei, glDisableIndexedEXT); + MAP_GL_FUNCTION(glDisableVertexAttribArray, glDisableVertexAttribArrayARB); + MAP_GL_FUNCTION(glDrawArraysInstanced, glDrawArraysInstancedARB); + MAP_GL_FUNCTION(glDrawBuffers, glDrawBuffersARB); + MAP_GL_FUNCTION(glDrawElementsInstanced, glDrawElementsInstancedARB); + MAP_GL_FUNCTION(glEnablei, glEnableIndexedEXT); + MAP_GL_FUNCTION(glEnableVertexAttribArray, glEnableVertexAttribArrayARB); + MAP_GL_FUNCTION(glEndQuery, glEndQueryARB); + MAP_GL_FUNCTION(glFramebufferTexture, glFramebufferTextureARB); + MAP_GL_FUNCTION(glGenBuffers, glGenBuffersARB); + MAP_GL_FUNCTION(glGenQueries, glGenQueriesARB); + MAP_GL_FUNCTION(glGetActiveUniform, glGetActiveUniformARB); + MAP_GL_FUNCTION(glGetAttachedShaders, glGetAttachedObjectsARB); + MAP_GL_FUNCTION(glGetAttribLocation, glGetAttribLocationARB); + MAP_GL_FUNCTION(glGetBooleani_v, glGetBooleanIndexedvEXT); + MAP_GL_FUNCTION(glGetBufferSubData, glGetBufferSubDataARB); + MAP_GL_FUNCTION(glGetCompressedTexImage, glGetCompressedTexImageARB); + MAP_GL_FUNCTION(glGetDebugMessageLog, glGetDebugMessageLogARB); + MAP_GL_FUNCTION(glGetIntegeri_v, glGetIntegerIndexedvEXT); + MAP_GL_FUNCTION(glGetProgramInfoLog, glGetInfoLogARB); + MAP_GL_FUNCTION(glGetProgramiv, glGetObjectParameterivARB); + MAP_GL_FUNCTION(glGetQueryiv, glGetQueryivARB); + MAP_GL_FUNCTION(glGetQueryObjectuiv, glGetQueryObjectuivARB); + MAP_GL_FUNCTION(glGetShaderInfoLog, glGetInfoLogARB); + MAP_GL_FUNCTION(glGetShaderiv, glGetObjectParameterivARB); + MAP_GL_FUNCTION(glGetShaderSource, glGetShaderSourceARB); + MAP_GL_FUNCTION(glGetUniformfv, glGetUniformfvARB); + MAP_GL_FUNCTION(glGetUniformiv, glGetUniformivARB); + MAP_GL_FUNCTION(glGetUniformLocation, glGetUniformLocationARB); + MAP_GL_FUNCTION(glIsEnabledi, glIsEnabledIndexedEXT); + MAP_GL_FUNCTION(glLinkProgram, glLinkProgramARB); + MAP_GL_FUNCTION(glMapBuffer, glMapBufferARB); + MAP_GL_FUNCTION(glMinSampleShading, glMinSampleShadingARB); + MAP_GL_FUNCTION(glPolygonOffsetClamp, glPolygonOffsetClampEXT); + MAP_GL_FUNCTION_CAST(glShaderSource, glShaderSourceARB); + MAP_GL_FUNCTION(glTexBuffer, glTexBufferARB); + MAP_GL_FUNCTION_CAST(glTexImage3D, glTexImage3DEXT); + MAP_GL_FUNCTION(glTexSubImage3D, glTexSubImage3DEXT); + MAP_GL_FUNCTION(glUniform1f, glUniform1fARB); + MAP_GL_FUNCTION(glUniform1fv, glUniform1fvARB); + MAP_GL_FUNCTION(glUniform1i, glUniform1iARB); + MAP_GL_FUNCTION(glUniform1iv, glUniform1ivARB); + MAP_GL_FUNCTION(glUniform2f, glUniform2fARB); + MAP_GL_FUNCTION(glUniform2fv, glUniform2fvARB); + MAP_GL_FUNCTION(glUniform2i, glUniform2iARB); + MAP_GL_FUNCTION(glUniform2iv, glUniform2ivARB); + MAP_GL_FUNCTION(glUniform3f, glUniform3fARB); + MAP_GL_FUNCTION(glUniform3fv, glUniform3fvARB); + MAP_GL_FUNCTION(glUniform3i, glUniform3iARB); + MAP_GL_FUNCTION(glUniform3iv, glUniform3ivARB); + MAP_GL_FUNCTION(glUniform4f, glUniform4fARB); + MAP_GL_FUNCTION(glUniform4fv, glUniform4fvARB); + MAP_GL_FUNCTION(glUniform4i, glUniform4iARB); + MAP_GL_FUNCTION(glUniform4iv, glUniform4ivARB); + MAP_GL_FUNCTION(glUniformMatrix2fv, glUniformMatrix2fvARB); + MAP_GL_FUNCTION(glUniformMatrix3fv, glUniformMatrix3fvARB); + MAP_GL_FUNCTION(glUniformMatrix4fv, glUniformMatrix4fvARB); + MAP_GL_FUNCTION(glUnmapBuffer, glUnmapBufferARB); + MAP_GL_FUNCTION(glUseProgram, glUseProgramObjectARB); + MAP_GL_FUNCTION(glValidateProgram, glValidateProgramARB); + MAP_GL_FUNCTION(glVertexAttrib1f, glVertexAttrib1fARB); + MAP_GL_FUNCTION(glVertexAttrib1fv, glVertexAttrib1fvARB); + MAP_GL_FUNCTION(glVertexAttrib2f, glVertexAttrib2fARB); + MAP_GL_FUNCTION(glVertexAttrib2fv, glVertexAttrib2fvARB); + MAP_GL_FUNCTION(glVertexAttrib3f, glVertexAttrib3fARB); + MAP_GL_FUNCTION(glVertexAttrib3fv, glVertexAttrib3fvARB); + MAP_GL_FUNCTION(glVertexAttrib4f, glVertexAttrib4fARB); + MAP_GL_FUNCTION(glVertexAttrib4fv, glVertexAttrib4fvARB); + MAP_GL_FUNCTION(glVertexAttrib4Nsv, glVertexAttrib4NsvARB); + MAP_GL_FUNCTION(glVertexAttrib4Nub, glVertexAttrib4NubARB); + MAP_GL_FUNCTION(glVertexAttrib4Nubv, glVertexAttrib4NubvARB); + MAP_GL_FUNCTION(glVertexAttrib4Nusv, glVertexAttrib4NusvARB); + MAP_GL_FUNCTION(glVertexAttrib4sv, glVertexAttrib4svARB); + MAP_GL_FUNCTION(glVertexAttrib4ubv, glVertexAttrib4ubvARB); + MAP_GL_FUNCTION(glVertexAttribDivisor, glVertexAttribDivisorARB); + MAP_GL_FUNCTION(glVertexAttribIPointer, glVertexAttribIPointerEXT); + MAP_GL_FUNCTION(glVertexAttribPointer, glVertexAttribPointerARB); +#undef MAP_GL_FUNCTION +#undef MAP_GL_FUNCTION_CAST +} + +static void wined3d_adapter_init_limits(struct wined3d_gl_info *gl_info) +{ + unsigned int i, sampler_count; + GLint gl_max; + + gl_info->limits.buffers = 1; + gl_info->limits.textures = 0; + gl_info->limits.texture_coords = 0; + for (i = 0; i < WINED3D_SHADER_TYPE_COUNT; ++i) + { + gl_info->limits.uniform_blocks[i] = 0; + gl_info->limits.samplers[i] = 0; + } + gl_info->limits.samplers[WINED3D_SHADER_TYPE_PIXEL] = 1; + gl_info->limits.combined_samplers = gl_info->limits.samplers[WINED3D_SHADER_TYPE_PIXEL]; + gl_info->limits.graphics_samplers = gl_info->limits.combined_samplers; + gl_info->limits.vertex_attribs = 16; + gl_info->limits.texture_buffer_offset_alignment = 1; + gl_info->limits.glsl_vs_float_constants = 0; + gl_info->limits.glsl_ps_float_constants = 0; + gl_info->limits.arb_vs_float_constants = 0; + gl_info->limits.arb_vs_native_constants = 0; + gl_info->limits.arb_vs_instructions = 0; + gl_info->limits.arb_vs_temps = 0; + gl_info->limits.arb_ps_float_constants = 0; + gl_info->limits.arb_ps_local_constants = 0; + gl_info->limits.arb_ps_instructions = 0; + gl_info->limits.arb_ps_temps = 0; + + gl_info->gl_ops.gl.p_glGetIntegerv(GL_MAX_CLIP_DISTANCES, &gl_max); + gl_info->limits.user_clip_distances = min(WINED3D_MAX_CLIP_DISTANCES, gl_max); + TRACE("Clip plane support - max planes %d.\n", gl_max); + + if (gl_info->supported[WINED3D_GL_LEGACY_CONTEXT]) + { + gl_info->gl_ops.gl.p_glGetIntegerv(GL_MAX_LIGHTS, &gl_max); + gl_info->limits.lights = gl_max; + TRACE("Light support - max lights %d.\n", gl_max); + } + + gl_info->gl_ops.gl.p_glGetIntegerv(GL_MAX_TEXTURE_SIZE, &gl_max); + gl_info->limits.texture_size = gl_max; + TRACE("Maximum texture size support - max texture size %d.\n", gl_max); + + if (gl_info->supported[ARB_MAP_BUFFER_ALIGNMENT]) + { + gl_info->gl_ops.gl.p_glGetIntegerv(GL_MIN_MAP_BUFFER_ALIGNMENT, &gl_max); + TRACE("Minimum buffer map alignment: %d.\n", gl_max); + } + else + { + WARN_(d3d_perf)("Driver doesn't guarantee a minimum buffer map alignment.\n"); + } + if (gl_info->supported[NV_REGISTER_COMBINERS]) + { + gl_info->gl_ops.gl.p_glGetIntegerv(GL_MAX_GENERAL_COMBINERS_NV, &gl_max); + gl_info->limits.general_combiners = gl_max; + TRACE("Max general combiners: %d.\n", gl_max); + } + if (gl_info->supported[ARB_DRAW_BUFFERS] && wined3d_settings.offscreen_rendering_mode == ORM_FBO) + { + gl_info->gl_ops.gl.p_glGetIntegerv(GL_MAX_DRAW_BUFFERS_ARB, &gl_max); + gl_info->limits.buffers = min(WINED3D_MAX_RENDER_TARGETS, gl_max); + TRACE("Max draw buffers: %u.\n", gl_max); + } + if (gl_info->supported[ARB_MULTITEXTURE]) + { + if (gl_info->supported[WINED3D_GL_LEGACY_CONTEXT]) + { + gl_info->gl_ops.gl.p_glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB, &gl_max); + gl_info->limits.textures = min(WINED3D_MAX_TEXTURES, gl_max); + TRACE("Max textures: %d.\n", gl_info->limits.textures); + + if (gl_info->supported[ARB_FRAGMENT_PROGRAM]) + { + gl_info->gl_ops.gl.p_glGetIntegerv(GL_MAX_TEXTURE_COORDS_ARB, &gl_max); + gl_info->limits.texture_coords = min(WINED3D_MAX_TEXTURES, gl_max); + } + else + { + gl_info->limits.texture_coords = gl_info->limits.textures; + } + TRACE("Max texture coords: %d.\n", gl_info->limits.texture_coords); + } + + if (gl_info->supported[ARB_FRAGMENT_PROGRAM] || gl_info->supported[ARB_FRAGMENT_SHADER]) + { + gl_info->gl_ops.gl.p_glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &gl_max); + gl_info->limits.samplers[WINED3D_SHADER_TYPE_PIXEL] = gl_max; + } + else + { + gl_info->limits.samplers[WINED3D_SHADER_TYPE_PIXEL] = gl_info->limits.textures; + } + TRACE("Max fragment samplers: %d.\n", gl_info->limits.samplers[WINED3D_SHADER_TYPE_PIXEL]); + + if (gl_info->supported[ARB_VERTEX_SHADER]) + { + unsigned int vertex_sampler_count; + + gl_info->gl_ops.gl.p_glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS_ARB, &gl_max); + vertex_sampler_count = gl_info->limits.samplers[WINED3D_SHADER_TYPE_VERTEX] = gl_max; + gl_info->gl_ops.gl.p_glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS_ARB, &gl_max); + gl_info->limits.combined_samplers = gl_max; + gl_info->gl_ops.gl.p_glGetIntegerv(GL_MAX_VERTEX_ATTRIBS_ARB, &gl_max); + gl_info->limits.vertex_attribs = gl_max; + + /* Loading GLSL sampler uniforms is much simpler if we can assume + * that the sampler setup is known at shader link time. In a + * vertex shader + pixel shader combination this isn't an issue + * because then the sampler setup only depends on the two shaders. + * If a pixel shader is used with fixed-function vertex processing + * we're fine too because fixed-function vertex processing doesn't + * use any samplers. If fixed-function fragment processing is used + * we have to make sure that all vertex sampler setups are valid + * together with all possible fixed-function fragment processing + * setups. This is true if vsamplers + WINED3D_MAX_TEXTURES <= max_samplers. + * This is true on all Direct3D 9 cards that support vertex + * texture fetch (GeForce 6 and GeForce 7 cards). Direct3D 9 + * Radeon cards do not support vertex texture fetch. Direct3D 10 + * cards have 128 samplers, and Direct3D 9 is limited to 8 + * fixed-function texture stages and 4 vertex samplers. + * Direct3D 10 does not have a fixed-function pipeline anymore. + * + * So this is just a check to check that our assumption holds + * true. If not, write a warning and reduce the number of vertex + * samplers or probably disable vertex texture fetch. */ + if (vertex_sampler_count && gl_info->limits.combined_samplers < 12 + && WINED3D_MAX_TEXTURES + vertex_sampler_count > gl_info->limits.combined_samplers) + { + FIXME("OpenGL implementation supports %u vertex samplers and %u total samplers.\n", + vertex_sampler_count, gl_info->limits.combined_samplers); + FIXME("Expected vertex samplers + WINED3D_MAX_TEXTURES(=8) > combined_samplers.\n"); + if (gl_info->limits.combined_samplers > WINED3D_MAX_TEXTURES) + vertex_sampler_count = gl_info->limits.combined_samplers - WINED3D_MAX_TEXTURES; + else + vertex_sampler_count = 0; + gl_info->limits.samplers[WINED3D_SHADER_TYPE_VERTEX] = vertex_sampler_count; + } + } + else + { + gl_info->limits.combined_samplers = gl_info->limits.samplers[WINED3D_SHADER_TYPE_PIXEL]; + } + TRACE("Max vertex samplers: %u.\n", gl_info->limits.samplers[WINED3D_SHADER_TYPE_VERTEX]); + TRACE("Max combined samplers: %u.\n", gl_info->limits.combined_samplers); + TRACE("Max vertex attributes: %u.\n", gl_info->limits.vertex_attribs); + } + else + { + gl_info->limits.textures = 1; + gl_info->limits.texture_coords = 1; + } + + if (gl_info->supported[EXT_TEXTURE3D]) + { + gl_info->gl_ops.gl.p_glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE_EXT, &gl_max); + gl_info->limits.texture3d_size = gl_max; + TRACE("Max texture3D size: %d.\n", gl_info->limits.texture3d_size); + } + if (gl_info->supported[ARB_TEXTURE_FILTER_ANISOTROPIC]) + { + gl_info->gl_ops.gl.p_glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY, &gl_max); + gl_info->limits.anisotropy = gl_max; + TRACE("Max anisotropy: %d.\n", gl_info->limits.anisotropy); + } + if (gl_info->supported[ARB_FRAGMENT_PROGRAM]) + { + GL_EXTCALL(glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, GL_MAX_PROGRAM_ENV_PARAMETERS_ARB, &gl_max)); + gl_info->limits.arb_ps_float_constants = gl_max; + TRACE("Max ARB_FRAGMENT_PROGRAM float constants: %d.\n", gl_info->limits.arb_ps_float_constants); + GL_EXTCALL(glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, GL_MAX_PROGRAM_NATIVE_PARAMETERS_ARB, &gl_max)); + gl_info->limits.arb_ps_native_constants = gl_max; + TRACE("Max ARB_FRAGMENT_PROGRAM native float constants: %d.\n", + gl_info->limits.arb_ps_native_constants); + GL_EXTCALL(glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, GL_MAX_PROGRAM_NATIVE_TEMPORARIES_ARB, &gl_max)); + gl_info->limits.arb_ps_temps = gl_max; + TRACE("Max ARB_FRAGMENT_PROGRAM native temporaries: %d.\n", gl_info->limits.arb_ps_temps); + GL_EXTCALL(glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, GL_MAX_PROGRAM_NATIVE_INSTRUCTIONS_ARB, &gl_max)); + gl_info->limits.arb_ps_instructions = gl_max; + TRACE("Max ARB_FRAGMENT_PROGRAM native instructions: %d.\n", gl_info->limits.arb_ps_instructions); + GL_EXTCALL(glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, GL_MAX_PROGRAM_LOCAL_PARAMETERS_ARB, &gl_max)); + gl_info->limits.arb_ps_local_constants = gl_max; + TRACE("Max ARB_FRAGMENT_PROGRAM local parameters: %d.\n", gl_info->limits.arb_ps_instructions); + } + if (gl_info->supported[ARB_VERTEX_PROGRAM]) + { + GL_EXTCALL(glGetProgramivARB(GL_VERTEX_PROGRAM_ARB, GL_MAX_PROGRAM_ENV_PARAMETERS_ARB, &gl_max)); + gl_info->limits.arb_vs_float_constants = gl_max; + TRACE("Max ARB_VERTEX_PROGRAM float constants: %d.\n", gl_info->limits.arb_vs_float_constants); + GL_EXTCALL(glGetProgramivARB(GL_VERTEX_PROGRAM_ARB, GL_MAX_PROGRAM_NATIVE_PARAMETERS_ARB, &gl_max)); + gl_info->limits.arb_vs_native_constants = gl_max; + TRACE("Max ARB_VERTEX_PROGRAM native float constants: %d.\n", + gl_info->limits.arb_vs_native_constants); + GL_EXTCALL(glGetProgramivARB(GL_VERTEX_PROGRAM_ARB, GL_MAX_PROGRAM_NATIVE_TEMPORARIES_ARB, &gl_max)); + gl_info->limits.arb_vs_temps = gl_max; + TRACE("Max ARB_VERTEX_PROGRAM native temporaries: %d.\n", gl_info->limits.arb_vs_temps); + GL_EXTCALL(glGetProgramivARB(GL_VERTEX_PROGRAM_ARB, GL_MAX_PROGRAM_NATIVE_INSTRUCTIONS_ARB, &gl_max)); + gl_info->limits.arb_vs_instructions = gl_max; + TRACE("Max ARB_VERTEX_PROGRAM native instructions: %d.\n", gl_info->limits.arb_vs_instructions); + } + if (gl_info->supported[ARB_VERTEX_SHADER]) + { + gl_info->gl_ops.gl.p_glGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB, &gl_max); + gl_info->limits.glsl_vs_float_constants = gl_max / 4; + TRACE("Max ARB_VERTEX_SHADER float constants: %u.\n", gl_info->limits.glsl_vs_float_constants); + + if (gl_info->supported[ARB_UNIFORM_BUFFER_OBJECT]) + { + gl_info->gl_ops.gl.p_glGetIntegerv(GL_MAX_VERTEX_UNIFORM_BLOCKS, &gl_max); + gl_info->limits.uniform_blocks[WINED3D_SHADER_TYPE_VERTEX] = min(gl_max, WINED3D_MAX_CBS); + TRACE("Max vertex uniform blocks: %u (%d).\n", + gl_info->limits.uniform_blocks[WINED3D_SHADER_TYPE_VERTEX], gl_max); + } + } + if (gl_info->supported[ARB_TESSELLATION_SHADER]) + { + gl_info->gl_ops.gl.p_glGetIntegerv(GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS, &gl_max); + gl_info->limits.uniform_blocks[WINED3D_SHADER_TYPE_HULL] = min(gl_max, WINED3D_MAX_CBS); + TRACE("Max hull uniform blocks: %u (%d).\n", + gl_info->limits.uniform_blocks[WINED3D_SHADER_TYPE_HULL], gl_max); + gl_info->gl_ops.gl.p_glGetIntegerv(GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS, &gl_max); + gl_info->limits.samplers[WINED3D_SHADER_TYPE_HULL] = gl_max; + TRACE("Max hull samplers: %u.\n", gl_info->limits.samplers[WINED3D_SHADER_TYPE_HULL]); + + gl_info->gl_ops.gl.p_glGetIntegerv(GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS, &gl_max); + gl_info->limits.uniform_blocks[WINED3D_SHADER_TYPE_DOMAIN] = min(gl_max, WINED3D_MAX_CBS); + TRACE("Max domain uniform blocks: %u (%d).\n", + gl_info->limits.uniform_blocks[WINED3D_SHADER_TYPE_DOMAIN], gl_max); + gl_info->gl_ops.gl.p_glGetIntegerv(GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS, &gl_max); + gl_info->limits.samplers[WINED3D_SHADER_TYPE_DOMAIN] = gl_max; + TRACE("Max domain samplers: %u.\n", gl_info->limits.samplers[WINED3D_SHADER_TYPE_DOMAIN]); + } + if (gl_info->supported[WINED3D_GL_VERSION_3_2] && gl_info->supported[ARB_UNIFORM_BUFFER_OBJECT]) + { + gl_info->gl_ops.gl.p_glGetIntegerv(GL_MAX_GEOMETRY_UNIFORM_BLOCKS, &gl_max); + gl_info->limits.uniform_blocks[WINED3D_SHADER_TYPE_GEOMETRY] = min(gl_max, WINED3D_MAX_CBS); + TRACE("Max geometry uniform blocks: %u (%d).\n", + gl_info->limits.uniform_blocks[WINED3D_SHADER_TYPE_GEOMETRY], gl_max); + gl_info->gl_ops.gl.p_glGetIntegerv(GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS, &gl_max); + gl_info->limits.samplers[WINED3D_SHADER_TYPE_GEOMETRY] = gl_max; + TRACE("Max geometry samplers: %u.\n", gl_info->limits.samplers[WINED3D_SHADER_TYPE_GEOMETRY]); + } + if (gl_info->supported[ARB_FRAGMENT_SHADER]) + { + gl_info->gl_ops.gl.p_glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_COMPONENTS_ARB, &gl_max); + gl_info->limits.glsl_ps_float_constants = gl_max / 4; + TRACE("Max ARB_FRAGMENT_SHADER float constants: %u.\n", gl_info->limits.glsl_ps_float_constants); + if (gl_info->supported[WINED3D_GL_LEGACY_CONTEXT]) + { + gl_info->gl_ops.gl.p_glGetIntegerv(GL_MAX_VARYING_FLOATS_ARB, &gl_max); + gl_info->limits.glsl_varyings = gl_max; + } + else + { + gl_info->gl_ops.gl.p_glGetIntegerv(GL_MAX_FRAGMENT_INPUT_COMPONENTS, &gl_max); + gl_info->limits.glsl_varyings = gl_max - 4; + } + TRACE("Max GLSL varyings: %u (%u 4 component varyings).\n", gl_info->limits.glsl_varyings, + gl_info->limits.glsl_varyings / 4); + + if (gl_info->supported[ARB_UNIFORM_BUFFER_OBJECT]) + { + gl_info->gl_ops.gl.p_glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_BLOCKS, &gl_max); + gl_info->limits.uniform_blocks[WINED3D_SHADER_TYPE_PIXEL] = min(gl_max, WINED3D_MAX_CBS); + TRACE("Max fragment uniform blocks: %u (%d).\n", + gl_info->limits.uniform_blocks[WINED3D_SHADER_TYPE_PIXEL], gl_max); + } + } + if (gl_info->supported[ARB_COMPUTE_SHADER]) + { + gl_info->gl_ops.gl.p_glGetIntegerv(GL_MAX_COMPUTE_UNIFORM_BLOCKS, &gl_max); + gl_info->limits.uniform_blocks[WINED3D_SHADER_TYPE_COMPUTE] = min(gl_max, WINED3D_MAX_CBS); + TRACE("Max compute uniform blocks: %u (%d).\n", + gl_info->limits.uniform_blocks[WINED3D_SHADER_TYPE_COMPUTE], gl_max); + gl_info->gl_ops.gl.p_glGetIntegerv(GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS, &gl_max); + gl_info->limits.samplers[WINED3D_SHADER_TYPE_COMPUTE] = gl_max; + TRACE("Max compute samplers: %u.\n", gl_info->limits.samplers[WINED3D_SHADER_TYPE_COMPUTE]); + } + if (gl_info->supported[ARB_UNIFORM_BUFFER_OBJECT]) + { + gl_info->gl_ops.gl.p_glGetIntegerv(GL_MAX_COMBINED_UNIFORM_BLOCKS, &gl_max); + TRACE("Max combined uniform blocks: %d.\n", gl_max); + gl_info->gl_ops.gl.p_glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &gl_max); + TRACE("Max uniform buffer bindings: %d.\n", gl_max); + } + if (gl_info->supported[ARB_TEXTURE_BUFFER_RANGE]) + { + gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT, &gl_max); + gl_info->limits.texture_buffer_offset_alignment = gl_max; + TRACE("Minimum required texture buffer offset alignment %d.\n", gl_max); + } + if (gl_info->supported[ARB_SHADER_ATOMIC_COUNTERS]) + { + GLint max_fragment_buffers, max_combined_buffers, max_bindings; + gl_info->gl_ops.gl.p_glGetIntegerv(GL_MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS, &max_fragment_buffers); + TRACE("Max fragment atomic counter buffers: %d.\n", max_fragment_buffers); + gl_info->gl_ops.gl.p_glGetIntegerv(GL_MAX_COMBINED_ATOMIC_COUNTER_BUFFERS, &max_combined_buffers); + TRACE("Max combined atomic counter buffers: %d.\n", max_combined_buffers); + gl_info->gl_ops.gl.p_glGetIntegerv(GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS, &max_bindings); + TRACE("Max atomic counter buffer bindings: %d.\n", max_bindings); + if (max_fragment_buffers < MAX_UNORDERED_ACCESS_VIEWS + || max_combined_buffers < MAX_UNORDERED_ACCESS_VIEWS + || max_bindings < MAX_UNORDERED_ACCESS_VIEWS) + { + WARN("Disabling ARB_shader_atomic_counters.\n"); + gl_info->supported[ARB_SHADER_ATOMIC_COUNTERS] = FALSE; + } + } + if (gl_info->supported[ARB_TRANSFORM_FEEDBACK3]) + { + gl_info->gl_ops.gl.p_glGetIntegerv(GL_MAX_VERTEX_STREAMS, &gl_max); + TRACE("Max vertex streams: %d.\n", gl_max); + } + + if (gl_info->supported[NV_LIGHT_MAX_EXPONENT]) + gl_info->gl_ops.gl.p_glGetFloatv(GL_MAX_SHININESS_NV, &gl_info->limits.shininess); + else + gl_info->limits.shininess = 128.0f; + + if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT] || gl_info->supported[EXT_FRAMEBUFFER_MULTISAMPLE]) + { + gl_info->gl_ops.gl.p_glGetIntegerv(GL_MAX_SAMPLES, &gl_max); + gl_info->limits.samples = gl_max; + } + + if (gl_info->supported[ARB_FRAMEBUFFER_NO_ATTACHMENTS]) + { + gl_info->gl_ops.gl.p_glGetIntegerv(GL_MAX_FRAMEBUFFER_WIDTH, &gl_max); + gl_info->limits.framebuffer_width = gl_max; + gl_info->gl_ops.gl.p_glGetIntegerv(GL_MAX_FRAMEBUFFER_HEIGHT, &gl_max); + gl_info->limits.framebuffer_height = gl_max; + } + else + { + gl_info->limits.framebuffer_width = gl_info->limits.texture_size; + gl_info->limits.framebuffer_height = gl_info->limits.texture_size; + } + + gl_info->limits.samplers[WINED3D_SHADER_TYPE_PIXEL] = + min(gl_info->limits.samplers[WINED3D_SHADER_TYPE_PIXEL], MAX_GL_FRAGMENT_SAMPLERS); + sampler_count = 0; + for (i = 0; i < WINED3D_SHADER_TYPE_GRAPHICS_COUNT; ++i) + sampler_count += gl_info->limits.samplers[i]; + if (gl_info->supported[WINED3D_GL_VERSION_3_2] && gl_info->limits.combined_samplers < sampler_count) + { + /* The minimum value for GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS in OpenGL + * 3.2 is 48 (16 per stage). When tessellation shaders are supported + * the minimum value is increased to 80. */ + WARN("Graphics pipeline sampler count %u is greater than combined sampler count %u.\n", + sampler_count, gl_info->limits.combined_samplers); + for (i = 0; i < WINED3D_SHADER_TYPE_GRAPHICS_COUNT; ++i) + gl_info->limits.samplers[i] = min(gl_info->limits.samplers[i], 16); + } + + /* A majority of OpenGL implementations allow us to statically partition + * the set of texture bindings into six separate sets. */ + gl_info->limits.graphics_samplers = gl_info->limits.combined_samplers; + sampler_count = 0; + for (i = 0; i < WINED3D_SHADER_TYPE_COUNT; ++i) + sampler_count += gl_info->limits.samplers[i]; + if (gl_info->limits.combined_samplers >= sampler_count) + gl_info->limits.graphics_samplers -= gl_info->limits.samplers[WINED3D_SHADER_TYPE_COMPUTE]; +} + +/* Context activation is done by the caller. */ +static BOOL wined3d_adapter_init_gl_caps(struct wined3d_adapter *adapter, + struct wined3d_caps_gl_ctx *caps_gl_ctx, unsigned int wined3d_creation_flags) +{ + static const struct + { + enum wined3d_gl_extension extension; + DWORD min_gl_version; + } + core_extensions[] = + { + {EXT_TEXTURE3D, MAKEDWORD_VERSION(1, 2)}, + {ARB_MULTISAMPLE, MAKEDWORD_VERSION(1, 3)}, + {ARB_MULTITEXTURE, MAKEDWORD_VERSION(1, 3)}, + {ARB_TEXTURE_BORDER_CLAMP, MAKEDWORD_VERSION(1, 3)}, + {ARB_TEXTURE_COMPRESSION, MAKEDWORD_VERSION(1, 3)}, + {ARB_TEXTURE_CUBE_MAP, MAKEDWORD_VERSION(1, 3)}, + {ARB_DEPTH_TEXTURE, MAKEDWORD_VERSION(1, 4)}, + {ARB_POINT_PARAMETERS, MAKEDWORD_VERSION(1, 4)}, + {ARB_SHADOW, MAKEDWORD_VERSION(1, 4)}, + {ARB_TEXTURE_MIRRORED_REPEAT, MAKEDWORD_VERSION(1, 4)}, + {EXT_BLEND_COLOR, MAKEDWORD_VERSION(1, 4)}, + {EXT_BLEND_FUNC_SEPARATE, MAKEDWORD_VERSION(1, 4)}, + {EXT_BLEND_MINMAX, MAKEDWORD_VERSION(1, 4)}, + {EXT_BLEND_SUBTRACT, MAKEDWORD_VERSION(1, 4)}, + {EXT_STENCIL_WRAP, MAKEDWORD_VERSION(1, 4)}, + {NV_POINT_SPRITE, MAKEDWORD_VERSION(1, 4)}, + {ARB_OCCLUSION_QUERY, MAKEDWORD_VERSION(1, 5)}, + {ARB_VERTEX_BUFFER_OBJECT, MAKEDWORD_VERSION(1, 5)}, + {ARB_DRAW_BUFFERS, MAKEDWORD_VERSION(2, 0)}, + {ARB_FRAGMENT_SHADER, MAKEDWORD_VERSION(2, 0)}, + {ARB_SHADING_LANGUAGE_100, MAKEDWORD_VERSION(2, 0)}, + {ARB_TEXTURE_NON_POWER_OF_TWO, MAKEDWORD_VERSION(2, 0)}, + {ARB_VERTEX_SHADER, MAKEDWORD_VERSION(2, 0)}, + {EXT_BLEND_EQUATION_SEPARATE, MAKEDWORD_VERSION(2, 0)}, + {ARB_PIXEL_BUFFER_OBJECT, MAKEDWORD_VERSION(2, 1)}, + {EXT_TEXTURE_SRGB, MAKEDWORD_VERSION(2, 1)}, + {ARB_COLOR_BUFFER_FLOAT, MAKEDWORD_VERSION(3, 0)}, + {ARB_DEPTH_BUFFER_FLOAT, MAKEDWORD_VERSION(3, 0)}, + {ARB_FRAMEBUFFER_OBJECT, MAKEDWORD_VERSION(3, 0)}, + {ARB_FRAMEBUFFER_SRGB, MAKEDWORD_VERSION(3, 0)}, + {ARB_HALF_FLOAT_PIXEL, MAKEDWORD_VERSION(3, 0)}, + {ARB_HALF_FLOAT_VERTEX, MAKEDWORD_VERSION(3, 0)}, + {ARB_MAP_BUFFER_RANGE, MAKEDWORD_VERSION(3, 0)}, + {ARB_TEXTURE_COMPRESSION_RGTC, MAKEDWORD_VERSION(3, 0)}, + {ARB_TEXTURE_FLOAT, MAKEDWORD_VERSION(3, 0)}, + {ARB_TEXTURE_RG, MAKEDWORD_VERSION(3, 0)}, + {EXT_DRAW_BUFFERS2, MAKEDWORD_VERSION(3, 0)}, + {EXT_PACKED_FLOAT, MAKEDWORD_VERSION(3, 0)}, + {EXT_TEXTURE_ARRAY, MAKEDWORD_VERSION(3, 0)}, + {EXT_TEXTURE_INTEGER, MAKEDWORD_VERSION(3, 0)}, + {EXT_TEXTURE_SHARED_EXPONENT, MAKEDWORD_VERSION(3, 0)}, + /* We don't want to enable EXT_GPU_SHADER4: even though similar + * functionality is available in core GL 3.0 / GLSL 1.30, it's different + * enough that reusing the same flag for the new features hurts more + * than it helps. */ + /* EXT_framebuffer_object, EXT_framebuffer_blit, + * EXT_framebuffer_multisample and EXT_packed_depth_stencil + * are integrated into ARB_framebuffer_object. */ + + {ARB_COPY_BUFFER, MAKEDWORD_VERSION(3, 1)}, + {ARB_DRAW_INSTANCED, MAKEDWORD_VERSION(3, 1)}, + {ARB_TEXTURE_BUFFER_OBJECT, MAKEDWORD_VERSION(3, 1)}, + {ARB_UNIFORM_BUFFER_OBJECT, MAKEDWORD_VERSION(3, 1)}, + {EXT_TEXTURE_SNORM, MAKEDWORD_VERSION(3, 1)}, + /* We don't need or want GL_ARB_texture_rectangle (core in 3.1). */ + + {ARB_DEPTH_CLAMP, MAKEDWORD_VERSION(3, 2)}, + {ARB_DRAW_ELEMENTS_BASE_VERTEX, MAKEDWORD_VERSION(3, 2)}, + /* ARB_geometry_shader4 exposes a somewhat different API compared to 3.2 + * core geometry shaders so it's not really correct to expose the + * extension for core-only support. */ + {ARB_FRAGMENT_COORD_CONVENTIONS, MAKEDWORD_VERSION(3, 2)}, + {ARB_PROVOKING_VERTEX, MAKEDWORD_VERSION(3, 2)}, + {ARB_SEAMLESS_CUBE_MAP, MAKEDWORD_VERSION(3, 2)}, + {ARB_SYNC, MAKEDWORD_VERSION(3, 2)}, + {ARB_TEXTURE_MULTISAMPLE, MAKEDWORD_VERSION(3, 2)}, + {ARB_VERTEX_ARRAY_BGRA, MAKEDWORD_VERSION(3, 2)}, + + {ARB_BLEND_FUNC_EXTENDED, MAKEDWORD_VERSION(3, 3)}, + {ARB_EXPLICIT_ATTRIB_LOCATION, MAKEDWORD_VERSION(3, 3)}, + {ARB_INSTANCED_ARRAYS, MAKEDWORD_VERSION(3, 3)}, + {ARB_SAMPLER_OBJECTS, MAKEDWORD_VERSION(3, 3)}, + {ARB_SHADER_BIT_ENCODING, MAKEDWORD_VERSION(3, 3)}, + {ARB_TEXTURE_RGB10_A2UI, MAKEDWORD_VERSION(3, 3)}, + {ARB_TEXTURE_SWIZZLE, MAKEDWORD_VERSION(3, 3)}, + {ARB_TIMER_QUERY, MAKEDWORD_VERSION(3, 3)}, + {ARB_VERTEX_TYPE_2_10_10_10_REV, MAKEDWORD_VERSION(3, 3)}, + + {ARB_DRAW_BUFFERS_BLEND, MAKEDWORD_VERSION(4, 0)}, + {ARB_DRAW_INDIRECT, MAKEDWORD_VERSION(4, 0)}, + {ARB_GPU_SHADER5, MAKEDWORD_VERSION(4, 0)}, + {ARB_SAMPLE_SHADING, MAKEDWORD_VERSION(4, 0)}, + {ARB_TESSELLATION_SHADER, MAKEDWORD_VERSION(4, 0)}, + {ARB_TEXTURE_CUBE_MAP_ARRAY, MAKEDWORD_VERSION(4, 0)}, + {ARB_TEXTURE_GATHER, MAKEDWORD_VERSION(4, 0)}, + {ARB_TRANSFORM_FEEDBACK2, MAKEDWORD_VERSION(4, 0)}, + {ARB_TRANSFORM_FEEDBACK3, MAKEDWORD_VERSION(4, 0)}, + + {ARB_ES2_COMPATIBILITY, MAKEDWORD_VERSION(4, 1)}, + {ARB_VIEWPORT_ARRAY, MAKEDWORD_VERSION(4, 1)}, + + {ARB_BASE_INSTANCE, MAKEDWORD_VERSION(4, 2)}, + {ARB_CONSERVATIVE_DEPTH, MAKEDWORD_VERSION(4, 2)}, + {ARB_INTERNALFORMAT_QUERY, MAKEDWORD_VERSION(4, 2)}, + {ARB_MAP_BUFFER_ALIGNMENT, MAKEDWORD_VERSION(4, 2)}, + {ARB_SHADER_ATOMIC_COUNTERS, MAKEDWORD_VERSION(4, 2)}, + {ARB_SHADER_IMAGE_LOAD_STORE, MAKEDWORD_VERSION(4, 2)}, + {ARB_SHADING_LANGUAGE_420PACK, MAKEDWORD_VERSION(4, 2)}, + {ARB_SHADING_LANGUAGE_PACKING, MAKEDWORD_VERSION(4, 2)}, + {ARB_TEXTURE_COMPRESSION_BPTC, MAKEDWORD_VERSION(4, 2)}, + {ARB_TEXTURE_STORAGE, MAKEDWORD_VERSION(4, 2)}, + + {ARB_CLEAR_BUFFER_OBJECT, MAKEDWORD_VERSION(4, 3)}, + {ARB_COMPUTE_SHADER, MAKEDWORD_VERSION(4, 3)}, + {ARB_COPY_IMAGE, MAKEDWORD_VERSION(4, 3)}, + {ARB_DEBUG_OUTPUT, MAKEDWORD_VERSION(4, 3)}, + {ARB_ES3_COMPATIBILITY, MAKEDWORD_VERSION(4, 3)}, + {ARB_FRAGMENT_LAYER_VIEWPORT, MAKEDWORD_VERSION(4, 3)}, + {ARB_FRAMEBUFFER_NO_ATTACHMENTS, MAKEDWORD_VERSION(4, 3)}, + {ARB_INTERNALFORMAT_QUERY2, MAKEDWORD_VERSION(4, 3)}, + {ARB_SHADER_IMAGE_SIZE, MAKEDWORD_VERSION(4, 3)}, + {ARB_SHADER_STORAGE_BUFFER_OBJECT, MAKEDWORD_VERSION(4, 3)}, + {ARB_STENCIL_TEXTURING, MAKEDWORD_VERSION(4, 3)}, + {ARB_TEXTURE_BUFFER_RANGE, MAKEDWORD_VERSION(4, 3)}, + {ARB_TEXTURE_QUERY_LEVELS, MAKEDWORD_VERSION(4, 3)}, + {ARB_TEXTURE_STORAGE_MULTISAMPLE, MAKEDWORD_VERSION(4, 3)}, + {ARB_TEXTURE_VIEW, MAKEDWORD_VERSION(4, 3)}, + + {ARB_BUFFER_STORAGE, MAKEDWORD_VERSION(4, 4)}, + {ARB_CLEAR_TEXTURE, MAKEDWORD_VERSION(4, 4)}, + {ARB_QUERY_BUFFER_OBJECT, MAKEDWORD_VERSION(4, 4)}, + {ARB_TEXTURE_MIRROR_CLAMP_TO_EDGE, MAKEDWORD_VERSION(4, 4)}, + + {ARB_CLIP_CONTROL, MAKEDWORD_VERSION(4, 5)}, + {ARB_CULL_DISTANCE, MAKEDWORD_VERSION(4, 5)}, + {ARB_DERIVATIVE_CONTROL, MAKEDWORD_VERSION(4, 5)}, + {ARB_SHADER_TEXTURE_IMAGE_SAMPLES, MAKEDWORD_VERSION(4, 5)}, + {ARB_TEXTURE_BARRIER, MAKEDWORD_VERSION(4, 5)}, + + {ARB_PIPELINE_STATISTICS_QUERY, MAKEDWORD_VERSION(4, 6)}, + {ARB_POLYGON_OFFSET_CLAMP, MAKEDWORD_VERSION(4, 6)}, + {ARB_TEXTURE_FILTER_ANISOTROPIC, MAKEDWORD_VERSION(4, 6)}, + }; + const char *gl_vendor_str, *gl_renderer_str, *gl_version_str; + struct wined3d_gl_info *gl_info = &adapter->gl_info; + DWORD gl_version, gl_ext_emul_mask; + const char *WGL_Extensions = NULL; + enum wined3d_gl_vendor gl_vendor; + GLint context_profile = 0; + unsigned int i, j; + HDC hdc; + + TRACE("adapter %p.\n", adapter); + + gl_renderer_str = (const char *)gl_info->gl_ops.gl.p_glGetString(GL_RENDERER); + TRACE("GL_RENDERER: %s.\n", debugstr_a(gl_renderer_str)); + if (!gl_renderer_str) + { + ERR("Received a NULL GL_RENDERER.\n"); + return FALSE; + } + + gl_vendor_str = (const char *)gl_info->gl_ops.gl.p_glGetString(GL_VENDOR); + TRACE("GL_VENDOR: %s.\n", debugstr_a(gl_vendor_str)); + if (!gl_vendor_str) + { + ERR("Received a NULL GL_VENDOR.\n"); + return FALSE; + } + + /* Parse the GL_VERSION field into major and minor information */ + gl_version_str = (const char *)gl_info->gl_ops.gl.p_glGetString(GL_VERSION); + TRACE("GL_VERSION: %s.\n", debugstr_a(gl_version_str)); + if (!gl_version_str) + { + ERR("Received a NULL GL_VERSION.\n"); + return FALSE; + } + gl_version = wined3d_parse_gl_version(gl_version_str); + + load_gl_funcs(gl_info); + + memset(gl_info->supported, 0, sizeof(gl_info->supported)); + gl_info->supported[WINED3D_GL_EXT_NONE] = TRUE; + + if (gl_version >= MAKEDWORD_VERSION(3, 2)) + { + gl_info->gl_ops.gl.p_glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &context_profile); + checkGLcall("Querying context profile"); + } + if (context_profile & GL_CONTEXT_CORE_PROFILE_BIT) + TRACE("Got a core profile context.\n"); + else + gl_info->supported[WINED3D_GL_LEGACY_CONTEXT] = TRUE; + + TRACE("GL extensions reported:\n"); + if (gl_info->supported[WINED3D_GL_LEGACY_CONTEXT]) + { + const char *gl_extensions = (const char *)gl_info->gl_ops.gl.p_glGetString(GL_EXTENSIONS); + + if (!gl_extensions) + { + ERR("Received a NULL GL_EXTENSIONS.\n"); + return FALSE; + } + parse_extension_string(gl_info, gl_extensions, gl_extension_map, ARRAY_SIZE(gl_extension_map)); + } + else + { + enumerate_gl_extensions(gl_info, gl_extension_map, ARRAY_SIZE(gl_extension_map)); + } + + hdc = wglGetCurrentDC(); + /* Not all GL drivers might offer WGL extensions e.g. VirtualBox. */ + if (GL_EXTCALL(wglGetExtensionsStringARB)) + WGL_Extensions = (const char *)GL_EXTCALL(wglGetExtensionsStringARB(hdc)); + if (!WGL_Extensions) + WARN("WGL extensions not supported.\n"); + else + parse_extension_string(gl_info, WGL_Extensions, wgl_extension_map, ARRAY_SIZE(wgl_extension_map)); + + for (i = 0; i < ARRAY_SIZE(core_extensions); ++i) + { + if (!gl_info->supported[core_extensions[i].extension] + && gl_version >= core_extensions[i].min_gl_version) + { + for (j = 0; j < ARRAY_SIZE(gl_extension_map); ++j) + if (gl_extension_map[j].extension == core_extensions[i].extension) + break; + + if (j < ARRAY_SIZE(gl_extension_map)) + { + TRACE("GL CORE: %s support.\n", gl_extension_map[j].extension_string); + gl_info->supported[core_extensions[i].extension] = TRUE; + } + else + { + FIXME("GL extension %u not in the GL extensions map.\n", core_extensions[i].extension); + } + } + } + + if (gl_info->supported[EXT_BLEND_MINMAX] || gl_info->supported[EXT_BLEND_SUBTRACT]) + gl_info->supported[WINED3D_GL_BLEND_EQUATION] = TRUE; + + if (gl_version >= MAKEDWORD_VERSION(2, 0)) + { + gl_info->supported[WINED3D_GL_VERSION_2_0] = TRUE; + /* We want to use the core APIs for two-sided stencil in GL 2.0. */ + gl_info->supported[EXT_STENCIL_TWO_SIDE] = FALSE; + } + if (gl_version >= MAKEDWORD_VERSION(3, 2)) + gl_info->supported[WINED3D_GL_VERSION_3_2] = TRUE; + + /* All the points are actually point sprites in core contexts, the APIs from + * ARB_point_sprite are not supported anymore. */ + if (!gl_info->supported[WINED3D_GL_LEGACY_CONTEXT]) + gl_info->supported[ARB_POINT_SPRITE] = FALSE; + + if (gl_info->supported[APPLE_FENCE]) + { + /* GL_NV_fence and GL_APPLE_fence provide the same functionality basically. + * The apple extension interacts with some other apple exts. Disable the NV + * extension if the apple one is support to prevent confusion in other parts + * of the code. */ + gl_info->supported[NV_FENCE] = FALSE; + } + if (gl_info->supported[APPLE_FLOAT_PIXELS]) + { + /* GL_APPLE_float_pixels == GL_ARB_texture_float + GL_ARB_half_float_pixel + * + * The enums are the same: + * GL_RGBA16F_ARB = GL_RGBA_FLOAT16_APPLE = 0x881a + * GL_RGB16F_ARB = GL_RGB_FLOAT16_APPLE = 0x881b + * GL_RGBA32F_ARB = GL_RGBA_FLOAT32_APPLE = 0x8814 + * GL_RGB32F_ARB = GL_RGB_FLOAT32_APPLE = 0x8815 + * GL_HALF_FLOAT_ARB = GL_HALF_APPLE = 0x140b + */ + if (!gl_info->supported[ARB_TEXTURE_FLOAT]) + { + TRACE(" IMPLIED: GL_ARB_texture_float support (by GL_APPLE_float_pixels).\n"); + gl_info->supported[ARB_TEXTURE_FLOAT] = TRUE; + } + if (!gl_info->supported[ARB_HALF_FLOAT_PIXEL]) + { + TRACE(" IMPLIED: GL_ARB_half_float_pixel support (by GL_APPLE_float_pixels).\n"); + gl_info->supported[ARB_HALF_FLOAT_PIXEL] = TRUE; + } + } + if (gl_info->supported[ARB_MAP_BUFFER_RANGE]) + { + /* GL_ARB_map_buffer_range and GL_APPLE_flush_buffer_range provide the same + * functionality. Prefer the ARB extension */ + gl_info->supported[APPLE_FLUSH_BUFFER_RANGE] = FALSE; + } + if (gl_info->supported[ARB_TEXTURE_CUBE_MAP]) + { + TRACE(" IMPLIED: NVIDIA (NV) Texture Gen Reflection support.\n"); + gl_info->supported[NV_TEXGEN_REFLECTION] = TRUE; + } + if (!gl_info->supported[EXT_TEXTURE_COMPRESSION_RGTC] && gl_info->supported[ARB_TEXTURE_COMPRESSION_RGTC]) + { + TRACE(" IMPLIED: EXT_texture_compression_rgtc support (by ARB_texture_compression_rgtc).\n"); + gl_info->supported[EXT_TEXTURE_COMPRESSION_RGTC] = TRUE; + } + if (!gl_info->supported[ARB_TEXTURE_COMPRESSION_RGTC] && gl_info->supported[EXT_TEXTURE_COMPRESSION_RGTC]) + { + TRACE(" IMPLIED: ARB_texture_compression_rgtc support (by EXT_texture_compression_rgtc).\n"); + gl_info->supported[ARB_TEXTURE_COMPRESSION_RGTC] = TRUE; + } + if (gl_info->supported[ARB_TEXTURE_COMPRESSION_RGTC] && !gl_info->supported[ARB_TEXTURE_RG]) + { + TRACE("ARB_texture_rg not supported, disabling ARB_texture_compression_rgtc.\n"); + gl_info->supported[ARB_TEXTURE_COMPRESSION_RGTC] = FALSE; + } + if (gl_info->supported[NV_TEXTURE_SHADER2]) + { + if (gl_info->supported[NV_REGISTER_COMBINERS]) + { + /* Also disable ATI_FRAGMENT_SHADER if register combiners and texture_shader2 + * are supported. The nv extensions provide the same functionality as the + * ATI one, and a bit more(signed pixelformats). */ + gl_info->supported[ATI_FRAGMENT_SHADER] = FALSE; + } + } + if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO]) + { + /* If we have full NP2 texture support, disable + * GL_ARB_texture_rectangle because we will never use it. + * This saves a few redundant glDisable calls. */ + gl_info->supported[ARB_TEXTURE_RECTANGLE] = FALSE; + } + if (gl_info->supported[ATI_FRAGMENT_SHADER]) + { + /* Disable NV_register_combiners and fragment shader if this is supported. + * generally the NV extensions are preferred over the ATI ones, and this + * extension is disabled if register_combiners and texture_shader2 are both + * supported. So we reach this place only if we have incomplete NV dxlevel 8 + * fragment processing support. */ + gl_info->supported[NV_REGISTER_COMBINERS] = FALSE; + gl_info->supported[NV_REGISTER_COMBINERS2] = FALSE; + gl_info->supported[NV_TEXTURE_SHADER] = FALSE; + gl_info->supported[NV_TEXTURE_SHADER2] = FALSE; + } + if (gl_info->supported[NV_HALF_FLOAT]) + { + /* GL_ARB_half_float_vertex is a subset of GL_NV_half_float. */ + gl_info->supported[ARB_HALF_FLOAT_VERTEX] = TRUE; + } + if (gl_info->supported[ARB_FRAMEBUFFER_SRGB] && !gl_info->supported[EXT_TEXTURE_SRGB_DECODE]) + { + /* Current wined3d sRGB infrastructure requires EXT_texture_sRGB_decode + * for GL_ARB_framebuffer_sRGB support (without EXT_texture_sRGB_decode + * we never render to sRGB surfaces). */ + TRACE("EXT_texture_sRGB_decode is not supported, disabling ARB_framebuffer_sRGB.\n"); + gl_info->supported[ARB_FRAMEBUFFER_SRGB] = FALSE; + } + if (gl_info->supported[ARB_OCCLUSION_QUERY]) + { + GLint counter_bits; + + GL_EXTCALL(glGetQueryiv(GL_SAMPLES_PASSED, GL_QUERY_COUNTER_BITS, &counter_bits)); + TRACE("Occlusion query counter has %d bits.\n", counter_bits); + if (!counter_bits) + gl_info->supported[ARB_OCCLUSION_QUERY] = FALSE; + } + if (gl_info->supported[ARB_TIMER_QUERY]) + { + GLint counter_bits; + + GL_EXTCALL(glGetQueryiv(GL_TIMESTAMP, GL_QUERY_COUNTER_BITS, &counter_bits)); + TRACE("Timestamp query counter has %d bits.\n", counter_bits); + if (!counter_bits) + gl_info->supported[ARB_TIMER_QUERY] = FALSE; + } + if (gl_version >= MAKEDWORD_VERSION(3, 0)) + { + GLint counter_bits; + + gl_info->supported[WINED3D_GL_PRIMITIVE_QUERY] = TRUE; + + GL_EXTCALL(glGetQueryiv(GL_PRIMITIVES_GENERATED, GL_QUERY_COUNTER_BITS, &counter_bits)); + TRACE("Primitives query counter has %d bits.\n", counter_bits); + if (!counter_bits) + gl_info->supported[WINED3D_GL_PRIMITIVE_QUERY] = FALSE; + + GL_EXTCALL(glGetQueryiv(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, GL_QUERY_COUNTER_BITS, &counter_bits)); + TRACE("Transform feedback primitives query counter has %d bits.\n", counter_bits); + if (!counter_bits) + gl_info->supported[WINED3D_GL_PRIMITIVE_QUERY] = FALSE; + } + if (gl_info->supported[ARB_VIEWPORT_ARRAY]) + { + GLint subpixel_bits; + + gl_info->gl_ops.gl.p_glGetIntegerv(GL_VIEWPORT_SUBPIXEL_BITS, &subpixel_bits); + TRACE("Viewport supports %d subpixel bits.\n", subpixel_bits); + if (subpixel_bits < 8 && gl_info->supported[ARB_CLIP_CONTROL]) + { + TRACE("Disabling ARB_clip_control because viewport subpixel bits < 8.\n"); + gl_info->supported[ARB_CLIP_CONTROL] = FALSE; + } + } + if (gl_info->supported[ARB_CLIP_CONTROL] && !gl_info->supported[ARB_VIEWPORT_ARRAY]) + { + /* When using ARB_clip_control we need the float viewport parameters + * introduced by ARB_viewport_array to take care of the shifted pixel + * coordinates. */ + TRACE("Disabling ARB_clip_control because ARB_viewport_array is not supported.\n"); + gl_info->supported[ARB_CLIP_CONTROL] = FALSE; + } + if (gl_info->supported[ARB_STENCIL_TEXTURING] && !gl_info->supported[ARB_TEXTURE_SWIZZLE]) + { + /* The stencil value needs to be placed in the green channel. */ + TRACE("Disabling ARB_stencil_texturing because ARB_texture_swizzle is not supported.\n"); + gl_info->supported[ARB_STENCIL_TEXTURING] = FALSE; + } + if (!gl_info->supported[ATI_TEXTURE_MIRROR_ONCE] && gl_info->supported[EXT_TEXTURE_MIRROR_CLAMP]) + { + TRACE(" IMPLIED: ATI_texture_mirror_once support (by EXT_texture_mirror_clamp).\n"); + gl_info->supported[ATI_TEXTURE_MIRROR_ONCE] = TRUE; + } + if (!gl_info->supported[ARB_TEXTURE_MIRROR_CLAMP_TO_EDGE] && gl_info->supported[ATI_TEXTURE_MIRROR_ONCE]) + { + TRACE(" IMPLIED: ARB_texture_mirror_clamp_to_edge support (by ATI_texture_mirror_once).\n"); + gl_info->supported[ARB_TEXTURE_MIRROR_CLAMP_TO_EDGE] = TRUE; + } + if (gl_info->supported[ARB_TEXTURE_STORAGE] && gl_info->supported[APPLE_YCBCR_422]) + { + /* AFAIK APPLE_ycbcr_422 is only available in legacy contexts so we shouldn't ever hit this. */ + ERR("Disabling APPLE_ycbcr_422 because of ARB_texture_storage.\n"); + gl_info->supported[APPLE_YCBCR_422] = FALSE; + } + if (gl_info->supported[ARB_DRAW_INDIRECT] && !gl_info->supported[ARB_BASE_INSTANCE]) + { + /* If ARB_base_instance is not supported the baseInstance field + * in indirect draw parameters must be 0 or behavior is undefined. + */ + WARN("Disabling ARB_draw_indirect because ARB_base_instance is not supported.\n"); + gl_info->supported[ARB_DRAW_INDIRECT] = FALSE; + } + if (gl_info->supported[ARB_TEXTURE_MULTISAMPLE] && !wined3d_settings.multisample_textures) + gl_info->supported[ARB_TEXTURE_MULTISAMPLE] = FALSE; + if (gl_info->supported[ARB_TEXTURE_MULTISAMPLE] && !gl_info->supported[ARB_TEXTURE_STORAGE_MULTISAMPLE]) + { + WARN("Disabling ARB_texture_multisample because immutable storage is not supported.\n"); + gl_info->supported[ARB_TEXTURE_MULTISAMPLE] = FALSE; + } + if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT] && !gl_info->supported[EXT_PACKED_DEPTH_STENCIL]) + { + TRACE(" IMPLIED: GL_EXT_packed_depth_stencil (by GL_ARB_framebuffer_object).\n"); + gl_info->supported[EXT_PACKED_DEPTH_STENCIL] = TRUE; + } + + wined3d_adapter_init_limits(gl_info); + + if (gl_info->supported[ARB_VERTEX_PROGRAM] && test_arb_vs_offset_limit(gl_info)) + gl_info->quirks |= WINED3D_QUIRK_ARB_VS_OFFSET_LIMIT; + + if (gl_info->supported[ARB_SHADING_LANGUAGE_100]) + { + const char *str = (const char *)gl_info->gl_ops.gl.p_glGetString(GL_SHADING_LANGUAGE_VERSION_ARB); + unsigned int major, minor; + + TRACE("GLSL version string: %s.\n", debugstr_a(str)); + + /* The format of the GLSL version string is "major.minor[.release] [vendor info]". */ + sscanf(str, "%u.%u", &major, &minor); + gl_info->glsl_version = MAKEDWORD_VERSION(major, minor); + if (gl_info->glsl_version > MAKEDWORD_VERSION(1, 30) && gl_version < MAKEDWORD_VERSION(3, 0)) + { + WARN("OpenGL version %u.%u too low, limiting GLSL version to 1.30.\n", + gl_version >> 16, gl_version & 0xffff); + gl_info->glsl_version = MAKEDWORD_VERSION(1, 30); + } + if (gl_info->glsl_version >= MAKEDWORD_VERSION(1, 30)) + gl_info->supported[WINED3D_GLSL_130] = TRUE; + } + + checkGLcall("extension detection"); + + adapter->shader_backend = select_shader_backend(gl_info); + adapter->vertex_pipe = select_vertex_implementation(gl_info, adapter->shader_backend); + adapter->fragment_pipe = select_fragment_implementation(gl_info, adapter->shader_backend); + + if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]) + { + gl_info->fbo_ops.glIsRenderbuffer = gl_info->gl_ops.ext.p_glIsRenderbuffer; + gl_info->fbo_ops.glBindRenderbuffer = gl_info->gl_ops.ext.p_glBindRenderbuffer; + gl_info->fbo_ops.glDeleteRenderbuffers = gl_info->gl_ops.ext.p_glDeleteRenderbuffers; + gl_info->fbo_ops.glGenRenderbuffers = gl_info->gl_ops.ext.p_glGenRenderbuffers; + gl_info->fbo_ops.glRenderbufferStorage = gl_info->gl_ops.ext.p_glRenderbufferStorage; + gl_info->fbo_ops.glRenderbufferStorageMultisample = gl_info->gl_ops.ext.p_glRenderbufferStorageMultisample; + gl_info->fbo_ops.glGetRenderbufferParameteriv = gl_info->gl_ops.ext.p_glGetRenderbufferParameteriv; + gl_info->fbo_ops.glIsFramebuffer = gl_info->gl_ops.ext.p_glIsFramebuffer; + gl_info->fbo_ops.glBindFramebuffer = gl_info->gl_ops.ext.p_glBindFramebuffer; + gl_info->fbo_ops.glDeleteFramebuffers = gl_info->gl_ops.ext.p_glDeleteFramebuffers; + gl_info->fbo_ops.glGenFramebuffers = gl_info->gl_ops.ext.p_glGenFramebuffers; + gl_info->fbo_ops.glCheckFramebufferStatus = gl_info->gl_ops.ext.p_glCheckFramebufferStatus; + gl_info->fbo_ops.glFramebufferTexture1D = gl_info->gl_ops.ext.p_glFramebufferTexture1D; + gl_info->fbo_ops.glFramebufferTexture2D = gl_info->gl_ops.ext.p_glFramebufferTexture2D; + gl_info->fbo_ops.glFramebufferTexture3D = gl_info->gl_ops.ext.p_glFramebufferTexture3D; + gl_info->fbo_ops.glFramebufferTextureLayer = gl_info->gl_ops.ext.p_glFramebufferTextureLayer; + gl_info->fbo_ops.glFramebufferRenderbuffer = gl_info->gl_ops.ext.p_glFramebufferRenderbuffer; + gl_info->fbo_ops.glGetFramebufferAttachmentParameteriv + = gl_info->gl_ops.ext.p_glGetFramebufferAttachmentParameteriv; + gl_info->fbo_ops.glBlitFramebuffer = gl_info->gl_ops.ext.p_glBlitFramebuffer; + gl_info->fbo_ops.glGenerateMipmap = gl_info->gl_ops.ext.p_glGenerateMipmap; + gl_info->fbo_ops.glFramebufferTexture = gl_info->gl_ops.ext.p_glFramebufferTexture; + } + else + { + if (gl_info->supported[EXT_FRAMEBUFFER_OBJECT]) + { + gl_info->fbo_ops.glIsRenderbuffer = gl_info->gl_ops.ext.p_glIsRenderbufferEXT; + gl_info->fbo_ops.glBindRenderbuffer = gl_info->gl_ops.ext.p_glBindRenderbufferEXT; + gl_info->fbo_ops.glDeleteRenderbuffers = gl_info->gl_ops.ext.p_glDeleteRenderbuffersEXT; + gl_info->fbo_ops.glGenRenderbuffers = gl_info->gl_ops.ext.p_glGenRenderbuffersEXT; + gl_info->fbo_ops.glRenderbufferStorage = gl_info->gl_ops.ext.p_glRenderbufferStorageEXT; + gl_info->fbo_ops.glGetRenderbufferParameteriv = gl_info->gl_ops.ext.p_glGetRenderbufferParameterivEXT; + gl_info->fbo_ops.glIsFramebuffer = gl_info->gl_ops.ext.p_glIsFramebufferEXT; + gl_info->fbo_ops.glBindFramebuffer = gl_info->gl_ops.ext.p_glBindFramebufferEXT; + gl_info->fbo_ops.glDeleteFramebuffers = gl_info->gl_ops.ext.p_glDeleteFramebuffersEXT; + gl_info->fbo_ops.glGenFramebuffers = gl_info->gl_ops.ext.p_glGenFramebuffersEXT; + gl_info->fbo_ops.glCheckFramebufferStatus = gl_info->gl_ops.ext.p_glCheckFramebufferStatusEXT; + gl_info->fbo_ops.glFramebufferTexture1D = gl_info->gl_ops.ext.p_glFramebufferTexture1DEXT; + gl_info->fbo_ops.glFramebufferTexture2D = gl_info->gl_ops.ext.p_glFramebufferTexture2DEXT; + gl_info->fbo_ops.glFramebufferTexture3D = gl_info->gl_ops.ext.p_glFramebufferTexture3DEXT; + gl_info->fbo_ops.glFramebufferRenderbuffer = gl_info->gl_ops.ext.p_glFramebufferRenderbufferEXT; + gl_info->fbo_ops.glGetFramebufferAttachmentParameteriv + = gl_info->gl_ops.ext.p_glGetFramebufferAttachmentParameterivEXT; + gl_info->fbo_ops.glGenerateMipmap = gl_info->gl_ops.ext.p_glGenerateMipmapEXT; + } + else if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) + { + WARN_(d3d_perf)("Framebuffer objects not supported, falling back to backbuffer offscreen rendering mode.\n"); + wined3d_settings.offscreen_rendering_mode = ORM_BACKBUFFER; + } + + if (gl_info->supported[ARB_GEOMETRY_SHADER4]) + { + gl_info->fbo_ops.glFramebufferTexture = gl_info->gl_ops.ext.p_glFramebufferTextureARB; + gl_info->fbo_ops.glFramebufferTextureLayer = gl_info->gl_ops.ext.p_glFramebufferTextureLayerARB; + } + if (gl_info->supported[EXT_FRAMEBUFFER_BLIT]) + { + gl_info->fbo_ops.glBlitFramebuffer = gl_info->gl_ops.ext.p_glBlitFramebufferEXT; + } + if (gl_info->supported[EXT_FRAMEBUFFER_MULTISAMPLE]) + { + gl_info->fbo_ops.glRenderbufferStorageMultisample + = gl_info->gl_ops.ext.p_glRenderbufferStorageMultisampleEXT; + } + } + + gl_info->wrap_lookup[WINED3D_TADDRESS_WRAP - WINED3D_TADDRESS_WRAP] = GL_REPEAT; + gl_info->wrap_lookup[WINED3D_TADDRESS_MIRROR - WINED3D_TADDRESS_WRAP] = + gl_info->supported[ARB_TEXTURE_MIRRORED_REPEAT] ? GL_MIRRORED_REPEAT_ARB : GL_REPEAT; + gl_info->wrap_lookup[WINED3D_TADDRESS_CLAMP - WINED3D_TADDRESS_WRAP] = GL_CLAMP_TO_EDGE; + gl_info->wrap_lookup[WINED3D_TADDRESS_BORDER - WINED3D_TADDRESS_WRAP] = + gl_info->supported[ARB_TEXTURE_BORDER_CLAMP] ? GL_CLAMP_TO_BORDER_ARB : GL_REPEAT; + gl_info->wrap_lookup[WINED3D_TADDRESS_MIRROR_ONCE - WINED3D_TADDRESS_WRAP] = + gl_info->supported[ARB_TEXTURE_MIRROR_CLAMP_TO_EDGE] ? GL_MIRROR_CLAMP_TO_EDGE : GL_REPEAT; + + if (!gl_info->supported[WINED3D_GL_LEGACY_CONTEXT]) + { + GLuint vao; + + GL_EXTCALL(glGenVertexArrays(1, &vao)); + GL_EXTCALL(glBindVertexArray(vao)); + checkGLcall("creating VAO"); + } + + gl_vendor = wined3d_guess_gl_vendor(gl_info, gl_vendor_str, gl_renderer_str, gl_version_str); + TRACE("Guessed GL vendor %#x.\n", gl_vendor); + + if (!(caps_gl_ctx->gpu_description = query_gpu_description(gl_info, &caps_gl_ctx->vram_bytes))) + { + enum wined3d_feature_level feature_level; + struct fragment_caps fragment_caps; + enum wined3d_pci_vendor vendor; + enum wined3d_pci_device device; + struct shader_caps shader_caps; + + adapter->shader_backend->shader_get_caps(adapter, &shader_caps); + adapter->fragment_pipe->get_caps(adapter, &fragment_caps); + feature_level = feature_level_from_caps(gl_info, &shader_caps, &fragment_caps); + + vendor = wined3d_guess_card_vendor(gl_vendor_str, gl_renderer_str); + TRACE("Guessed vendor PCI ID 0x%04x.\n", vendor); + + device = wined3d_guess_card(feature_level, gl_renderer_str, &gl_vendor, &vendor); + TRACE("Guessed device PCI ID 0x%04x.\n", device); + + if (!(caps_gl_ctx->gpu_description = wined3d_get_gpu_description(vendor, device))) + { + ERR("Card %04x:%04x not found in driver DB.\n", vendor, device); + return FALSE; + } + } + fixup_extensions(gl_info, caps_gl_ctx, gl_renderer_str, gl_vendor); + + adapter->vram_bytes_used = 0; + TRACE("Emulating 0x%s bytes of video ram.\n", wine_dbgstr_longlong(caps_gl_ctx->vram_bytes)); + + if (gl_info->supported[EXT_MEMORY_OBJECT]) + { + GLint device_count = 0; + + gl_info->gl_ops.gl.p_glGetIntegerv(GL_NUM_DEVICE_UUIDS_EXT, &device_count); + if (device_count > 0) + { + if (device_count > 1) + FIXME("A set of %d devices is not supported.\n", device_count); + + GL_EXTCALL(glGetUnsignedBytevEXT(GL_DRIVER_UUID_EXT, (GLubyte *)&adapter->driver_uuid)); + GL_EXTCALL(glGetUnsignedBytei_vEXT(GL_DEVICE_UUID_EXT, 0, (GLubyte *)&adapter->device_uuid)); + + TRACE("Driver UUID: %s, device UUID %s.\n", + debugstr_guid(&adapter->driver_uuid), debugstr_guid(&adapter->device_uuid)); + } + else + { + WARN("Unexpected device count %d.\n", device_count); + } + } + + gl_ext_emul_mask = adapter->vertex_pipe->vp_get_emul_mask(gl_info) + | adapter->fragment_pipe->get_emul_mask(gl_info); + if (gl_ext_emul_mask & GL_EXT_EMUL_ARB_MULTITEXTURE) + install_gl_compat_wrapper(gl_info, ARB_MULTITEXTURE); + if (gl_ext_emul_mask & GL_EXT_EMUL_EXT_FOG_COORD) + install_gl_compat_wrapper(gl_info, EXT_FOG_COORD); + + return TRUE; +} + +static void WINE_GLAPI invalid_func(const void *data) +{ + ERR("Invalid vertex attribute function called.\n"); + DebugBreak(); +} + +static void WINE_GLAPI invalid_texcoord_func(GLenum unit, const void *data) +{ + ERR("Invalid texcoord function called.\n"); + DebugBreak(); +} + +static void WINE_GLAPI invalid_generic_attrib_func(GLuint idx, const void *data) +{ + ERR("Invalid attribute function called.\n"); + DebugBreak(); +} + +/* Helper functions for providing vertex data to OpenGL. The arrays are + * initialised based on the extension detection and are used in + * draw_primitive_immediate_mode(). */ +static void WINE_GLAPI position_d3dcolor(const void *data) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl_get_current()->gl_info; + DWORD pos = *((const DWORD *)data); + + FIXME("Add a test for fixed function position from d3dcolor type.\n"); + gl_info->gl_ops.gl.p_glVertex4s(D3DCOLOR_B_R(pos), + D3DCOLOR_B_G(pos), + D3DCOLOR_B_B(pos), + D3DCOLOR_B_A(pos)); +} + +static void WINE_GLAPI position_float4(const void *data) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl_get_current()->gl_info; + const GLfloat *pos = data; + + if (pos[3] != 0.0f && pos[3] != 1.0f) + { + float w = 1.0f / pos[3]; + + gl_info->gl_ops.gl.p_glVertex4f(pos[0] * w, pos[1] * w, pos[2] * w, w); + } + else + { + gl_info->gl_ops.gl.p_glVertex3fv(pos); + } +} + +static void WINE_GLAPI diffuse_d3dcolor(const void *data) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl_get_current()->gl_info; + DWORD diffuseColor = *((const DWORD *)data); + + gl_info->gl_ops.gl.p_glColor4ub(D3DCOLOR_B_R(diffuseColor), + D3DCOLOR_B_G(diffuseColor), + D3DCOLOR_B_B(diffuseColor), + D3DCOLOR_B_A(diffuseColor)); +} + +static void WINE_GLAPI specular_d3dcolor(const void *data) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl_get_current()->gl_info; + DWORD specularColor = *((const DWORD *)data); + GLubyte d[] = + { + D3DCOLOR_B_R(specularColor), + D3DCOLOR_B_G(specularColor), + D3DCOLOR_B_B(specularColor) + }; + + gl_info->gl_ops.ext.p_glSecondaryColor3ubvEXT(d); +} + +static void WINE_GLAPI warn_no_specular_func(const void *data) +{ + WARN("GL_EXT_secondary_color not supported.\n"); +} + +static void WINE_GLAPI generic_d3dcolor(GLuint idx, const void *data) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl_get_current()->gl_info; + DWORD color = *((const DWORD *)data); + + gl_info->gl_ops.ext.p_glVertexAttrib4Nub(idx, + D3DCOLOR_B_R(color), D3DCOLOR_B_G(color), + D3DCOLOR_B_B(color), D3DCOLOR_B_A(color)); +} + +static void WINE_GLAPI generic_short2n(GLuint idx, const void *data) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl_get_current()->gl_info; + const GLshort s[] = {((const GLshort *)data)[0], ((const GLshort *)data)[1], 0, 1}; + + gl_info->gl_ops.ext.p_glVertexAttrib4Nsv(idx, s); +} + +static void WINE_GLAPI generic_ushort2n(GLuint idx, const void *data) +{ + const GLushort s[] = {((const GLushort *)data)[0], ((const GLushort *)data)[1], 0, 1}; + const struct wined3d_gl_info *gl_info = wined3d_context_gl_get_current()->gl_info; + + gl_info->gl_ops.ext.p_glVertexAttrib4Nusv(idx, s); +} + +static void WINE_GLAPI generic_float16_2(GLuint idx, const void *data) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl_get_current()->gl_info; + float x = float_16_to_32(((const unsigned short *)data) + 0); + float y = float_16_to_32(((const unsigned short *)data) + 1); + + gl_info->gl_ops.ext.p_glVertexAttrib2f(idx, x, y); +} + +static void WINE_GLAPI generic_float16_4(GLuint idx, const void *data) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl_get_current()->gl_info; + float x = float_16_to_32(((const unsigned short *)data) + 0); + float y = float_16_to_32(((const unsigned short *)data) + 1); + float z = float_16_to_32(((const unsigned short *)data) + 2); + float w = float_16_to_32(((const unsigned short *)data) + 3); + + gl_info->gl_ops.ext.p_glVertexAttrib4f(idx, x, y, z, w); +} + +static void wined3d_adapter_init_ffp_attrib_ops(struct wined3d_adapter *adapter) +{ + const struct wined3d_gl_info *gl_info = &adapter->gl_info; + struct wined3d_d3d_info *d3d_info = &adapter->d3d_info; + struct wined3d_ffp_attrib_ops *ops = &d3d_info->ffp_attrib_ops; + unsigned int i; + + for (i = 0; i < WINED3D_FFP_EMIT_COUNT; ++i) + { + ops->position[i] = invalid_func; + ops->diffuse[i] = invalid_func; + ops->specular[i] = invalid_func; + ops->normal[i] = invalid_func; + ops->texcoord[i] = invalid_texcoord_func; + ops->generic[i] = invalid_generic_attrib_func; + } + + ops->position[WINED3D_FFP_EMIT_FLOAT3] = (wined3d_ffp_attrib_func)gl_info->gl_ops.gl.p_glVertex3fv; + if (!d3d_info->xyzrhw) + ops->position[WINED3D_FFP_EMIT_FLOAT4] = position_float4; + else + ops->position[WINED3D_FFP_EMIT_FLOAT4] = (wined3d_ffp_attrib_func)gl_info->gl_ops.gl.p_glVertex4fv; + ops->position[WINED3D_FFP_EMIT_D3DCOLOR] = position_d3dcolor; + ops->position[WINED3D_FFP_EMIT_SHORT4] = (wined3d_ffp_attrib_func)gl_info->gl_ops.gl.p_glVertex2sv; + + ops->diffuse[WINED3D_FFP_EMIT_FLOAT3] = (wined3d_ffp_attrib_func)gl_info->gl_ops.gl.p_glColor3fv; + ops->diffuse[WINED3D_FFP_EMIT_FLOAT4] = (wined3d_ffp_attrib_func)gl_info->gl_ops.gl.p_glColor4fv; + ops->diffuse[WINED3D_FFP_EMIT_D3DCOLOR] = diffuse_d3dcolor; + ops->diffuse[WINED3D_FFP_EMIT_UBYTE4N] = (wined3d_ffp_attrib_func)gl_info->gl_ops.gl.p_glColor4ubv; + ops->diffuse[WINED3D_FFP_EMIT_SHORT4N] = (wined3d_ffp_attrib_func)gl_info->gl_ops.gl.p_glColor4sv; + ops->diffuse[WINED3D_FFP_EMIT_USHORT4N] = (wined3d_ffp_attrib_func)gl_info->gl_ops.gl.p_glColor4usv; + + /* No 4 component entry points here. */ + if (gl_info->supported[EXT_SECONDARY_COLOR]) + ops->specular[WINED3D_FFP_EMIT_FLOAT3] = (wined3d_ffp_attrib_func)GL_EXTCALL(glSecondaryColor3fvEXT); + else + ops->specular[WINED3D_FFP_EMIT_FLOAT3] = warn_no_specular_func; + if (gl_info->supported[EXT_SECONDARY_COLOR]) + ops->specular[WINED3D_FFP_EMIT_D3DCOLOR] = specular_d3dcolor; + else + ops->specular[WINED3D_FFP_EMIT_D3DCOLOR] = warn_no_specular_func; + + /* Only 3 component entry points here. Test how others behave. Float4 + * normals are used by one of our tests, trying to pass it to the pixel + * shader, which fails on Windows. */ + ops->normal[WINED3D_FFP_EMIT_FLOAT3] = (wined3d_ffp_attrib_func)gl_info->gl_ops.gl.p_glNormal3fv; + /* Just ignore the 4th value. */ + ops->normal[WINED3D_FFP_EMIT_FLOAT4] = (wined3d_ffp_attrib_func)gl_info->gl_ops.gl.p_glNormal3fv; + + ops->texcoord[WINED3D_FFP_EMIT_FLOAT1] = (wined3d_ffp_texcoord_func)gl_info->gl_ops.ext.p_glMultiTexCoord1fvARB; + ops->texcoord[WINED3D_FFP_EMIT_FLOAT2] = (wined3d_ffp_texcoord_func)gl_info->gl_ops.ext.p_glMultiTexCoord2fvARB; + ops->texcoord[WINED3D_FFP_EMIT_FLOAT3] = (wined3d_ffp_texcoord_func)gl_info->gl_ops.ext.p_glMultiTexCoord3fvARB; + ops->texcoord[WINED3D_FFP_EMIT_FLOAT4] = (wined3d_ffp_texcoord_func)gl_info->gl_ops.ext.p_glMultiTexCoord4fvARB; + ops->texcoord[WINED3D_FFP_EMIT_SHORT2] = (wined3d_ffp_texcoord_func)gl_info->gl_ops.ext.p_glMultiTexCoord2svARB; + ops->texcoord[WINED3D_FFP_EMIT_SHORT4] = (wined3d_ffp_texcoord_func)gl_info->gl_ops.ext.p_glMultiTexCoord4svARB; + if (gl_info->supported[NV_HALF_FLOAT]) + { + /* Not supported by ARB_HALF_FLOAT_VERTEX, so check for NV_HALF_FLOAT. */ + ops->texcoord[WINED3D_FFP_EMIT_FLOAT16_2] = + (wined3d_ffp_texcoord_func)gl_info->gl_ops.ext.p_glMultiTexCoord2hvNV; + ops->texcoord[WINED3D_FFP_EMIT_FLOAT16_4] = + (wined3d_ffp_texcoord_func)gl_info->gl_ops.ext.p_glMultiTexCoord4hvNV; + } + + ops->generic[WINED3D_FFP_EMIT_FLOAT1] = (wined3d_generic_attrib_func)gl_info->gl_ops.ext.p_glVertexAttrib1fv; + ops->generic[WINED3D_FFP_EMIT_FLOAT2] = (wined3d_generic_attrib_func)gl_info->gl_ops.ext.p_glVertexAttrib2fv; + ops->generic[WINED3D_FFP_EMIT_FLOAT3] = (wined3d_generic_attrib_func)gl_info->gl_ops.ext.p_glVertexAttrib3fv; + ops->generic[WINED3D_FFP_EMIT_FLOAT4] = (wined3d_generic_attrib_func)gl_info->gl_ops.ext.p_glVertexAttrib4fv; + if (gl_info->supported[ARB_VERTEX_ARRAY_BGRA]) + ops->generic[WINED3D_FFP_EMIT_D3DCOLOR] = generic_d3dcolor; + else + ops->generic[WINED3D_FFP_EMIT_D3DCOLOR] = + (wined3d_generic_attrib_func)gl_info->gl_ops.ext.p_glVertexAttrib4Nubv; + ops->generic[WINED3D_FFP_EMIT_UBYTE4] = (wined3d_generic_attrib_func)gl_info->gl_ops.ext.p_glVertexAttrib4ubv; + ops->generic[WINED3D_FFP_EMIT_SHORT2] = (wined3d_generic_attrib_func)gl_info->gl_ops.ext.p_glVertexAttrib2sv; + ops->generic[WINED3D_FFP_EMIT_SHORT4] = (wined3d_generic_attrib_func)gl_info->gl_ops.ext.p_glVertexAttrib4sv; + ops->generic[WINED3D_FFP_EMIT_UBYTE4N] = (wined3d_generic_attrib_func)gl_info->gl_ops.ext.p_glVertexAttrib4Nubv; + ops->generic[WINED3D_FFP_EMIT_SHORT2N] = generic_short2n; + ops->generic[WINED3D_FFP_EMIT_SHORT4N] = (wined3d_generic_attrib_func)gl_info->gl_ops.ext.p_glVertexAttrib4Nsv; + ops->generic[WINED3D_FFP_EMIT_USHORT2N] = generic_ushort2n; + ops->generic[WINED3D_FFP_EMIT_USHORT4N] = (wined3d_generic_attrib_func)gl_info->gl_ops.ext.p_glVertexAttrib4Nusv; + if (gl_info->supported[NV_HALF_FLOAT] && gl_info->supported[NV_VERTEX_PROGRAM]) + { + ops->generic[WINED3D_FFP_EMIT_FLOAT16_2] = + (wined3d_generic_attrib_func)gl_info->gl_ops.ext.p_glVertexAttrib2hvNV; + ops->generic[WINED3D_FFP_EMIT_FLOAT16_4] = + (wined3d_generic_attrib_func)gl_info->gl_ops.ext.p_glVertexAttrib4hvNV; + } + else + { + ops->generic[WINED3D_FFP_EMIT_FLOAT16_2] = generic_float16_2; + ops->generic[WINED3D_FFP_EMIT_FLOAT16_4] = generic_float16_4; + } +} + +static void wined3d_adapter_init_fb_cfgs(struct wined3d_adapter_gl *adapter_gl, HDC dc) +{ + const struct wined3d_gl_info *gl_info = &adapter_gl->a.gl_info; + int i; + + if (gl_info->supported[WGL_ARB_PIXEL_FORMAT]) + { + UINT attrib_count = 0; + GLint cfg_count; + int attribs[11]; + int values[11]; + int attribute; + + attribute = WGL_NUMBER_PIXEL_FORMATS_ARB; + GL_EXTCALL(wglGetPixelFormatAttribivARB(dc, 0, 0, 1, &attribute, &cfg_count)); + + adapter_gl->pixel_formats = heap_calloc(cfg_count, sizeof(*adapter_gl->pixel_formats)); + attribs[attrib_count++] = WGL_RED_BITS_ARB; + attribs[attrib_count++] = WGL_GREEN_BITS_ARB; + attribs[attrib_count++] = WGL_BLUE_BITS_ARB; + attribs[attrib_count++] = WGL_ALPHA_BITS_ARB; + attribs[attrib_count++] = WGL_COLOR_BITS_ARB; + attribs[attrib_count++] = WGL_DEPTH_BITS_ARB; + attribs[attrib_count++] = WGL_STENCIL_BITS_ARB; + attribs[attrib_count++] = WGL_DRAW_TO_WINDOW_ARB; + attribs[attrib_count++] = WGL_PIXEL_TYPE_ARB; + attribs[attrib_count++] = WGL_DOUBLE_BUFFER_ARB; + attribs[attrib_count++] = WGL_AUX_BUFFERS_ARB; + + for (i = 0, adapter_gl->pixel_format_count = 0; i < cfg_count; ++i) + { + struct wined3d_pixel_format *cfg = &adapter_gl->pixel_formats[adapter_gl->pixel_format_count]; + int format_id = i + 1; + + if (!GL_EXTCALL(wglGetPixelFormatAttribivARB(dc, format_id, 0, attrib_count, attribs, values))) + continue; + + cfg->iPixelFormat = format_id; + cfg->redSize = values[0]; + cfg->greenSize = values[1]; + cfg->blueSize = values[2]; + cfg->alphaSize = values[3]; + cfg->colorSize = values[4]; + cfg->depthSize = values[5]; + cfg->stencilSize = values[6]; + cfg->windowDrawable = values[7]; + cfg->iPixelType = values[8]; + cfg->doubleBuffer = values[9]; + cfg->auxBuffers = values[10]; + + cfg->numSamples = 0; + /* Check multisample support. */ + if (gl_info->supported[ARB_MULTISAMPLE]) + { + int attribs[2] = {WGL_SAMPLE_BUFFERS_ARB, WGL_SAMPLES_ARB}; + int values[2]; + + if (GL_EXTCALL(wglGetPixelFormatAttribivARB(dc, format_id, 0, 2, attribs, values))) + { + /* values[0] = WGL_SAMPLE_BUFFERS_ARB which tells whether + * multisampling is supported. values[1] = number of + * multisample buffers. */ + if (values[0]) + cfg->numSamples = values[1]; + } + } + + TRACE("iPixelFormat=%d, iPixelType=%#x, doubleBuffer=%d, RGBA=%d/%d/%d/%d, " + "depth=%d, stencil=%d, samples=%d, windowDrawable=%d\n", + cfg->iPixelFormat, cfg->iPixelType, cfg->doubleBuffer, + cfg->redSize, cfg->greenSize, cfg->blueSize, cfg->alphaSize, + cfg->depthSize, cfg->stencilSize, cfg->numSamples, cfg->windowDrawable); + + ++adapter_gl->pixel_format_count; + } + } + else + { + int cfg_count; + + cfg_count = DescribePixelFormat(dc, 0, 0, 0); + adapter_gl->pixel_formats = heap_calloc(cfg_count, sizeof(*adapter_gl->pixel_formats)); + + for (i = 0, adapter_gl->pixel_format_count = 0; i < cfg_count; ++i) + { + struct wined3d_pixel_format *cfg = &adapter_gl->pixel_formats[adapter_gl->pixel_format_count]; + PIXELFORMATDESCRIPTOR pfd; + int format_id = i + 1; + + if (!DescribePixelFormat(dc, format_id, sizeof(pfd), &pfd)) + continue; + + /* We only want HW acceleration using an OpenGL ICD driver. + * PFD_GENERIC_FORMAT = slow OpenGL 1.1 GDI software rendering. + * PFD_GENERIC_ACCELERATED = partial hw acceleration using a MCD + * driver (e.g. 3dfx minigl). */ + if (pfd.dwFlags & (PFD_GENERIC_FORMAT | PFD_GENERIC_ACCELERATED)) + { + TRACE("Skipping format %d because it isn't ICD accelerated.\n", format_id); + continue; + } + + cfg->iPixelFormat = format_id; + cfg->redSize = pfd.cRedBits; + cfg->greenSize = pfd.cGreenBits; + cfg->blueSize = pfd.cBlueBits; + cfg->alphaSize = pfd.cAlphaBits; + cfg->colorSize = pfd.cColorBits; + cfg->depthSize = pfd.cDepthBits; + cfg->stencilSize = pfd.cStencilBits; + cfg->windowDrawable = (pfd.dwFlags & PFD_DRAW_TO_WINDOW) ? 1 : 0; + cfg->iPixelType = (pfd.iPixelType == PFD_TYPE_RGBA) ? WGL_TYPE_RGBA_ARB : WGL_TYPE_COLORINDEX_ARB; + cfg->doubleBuffer = (pfd.dwFlags & PFD_DOUBLEBUFFER) ? 1 : 0; + cfg->auxBuffers = pfd.cAuxBuffers; + cfg->numSamples = 0; + + TRACE("iPixelFormat=%d, iPixelType=%#x, doubleBuffer=%d, RGBA=%d/%d/%d/%d, " + "depth=%d, stencil=%d, windowDrawable=%d\n", + cfg->iPixelFormat, cfg->iPixelType, cfg->doubleBuffer, + cfg->redSize, cfg->greenSize, cfg->blueSize, cfg->alphaSize, + cfg->depthSize, cfg->stencilSize, cfg->windowDrawable); + + ++adapter_gl->pixel_format_count; + } + } +} + +static void adapter_gl_destroy(struct wined3d_adapter *adapter) +{ + struct wined3d_adapter_gl *adapter_gl = wined3d_adapter_gl(adapter); + + heap_free(adapter_gl->pixel_formats); + wined3d_adapter_cleanup(adapter); + heap_free(adapter_gl); +} + +static HRESULT adapter_gl_create_device(struct wined3d *wined3d, const struct wined3d_adapter *adapter, + enum wined3d_device_type device_type, HWND focus_window, unsigned int flags, BYTE surface_alignment, + const enum wined3d_feature_level *levels, unsigned int level_count, + struct wined3d_device_parent *device_parent, struct wined3d_device **device) +{ + struct wined3d_device_gl *device_gl; + HRESULT hr; + + if (!(device_gl = heap_alloc_zero(sizeof(*device_gl)))) + return E_OUTOFMEMORY; + + device_gl->current_fence_id = 1; + + if (FAILED(hr = wined3d_device_init(&device_gl->d, wined3d, adapter->ordinal, device_type, focus_window, + flags, surface_alignment, levels, level_count, adapter->gl_info.supported, device_parent))) + { + WARN("Failed to initialize device, hr %#x.\n", hr); + heap_free(device_gl); + return hr; + } + + *device = &device_gl->d; + return WINED3D_OK; +} + +static void adapter_gl_destroy_device(struct wined3d_device *device) +{ + struct wined3d_device_gl *device_gl = wined3d_device_gl(device); + + wined3d_device_cleanup(&device_gl->d); + heap_free(device_gl); +} + +struct wined3d_context *adapter_gl_acquire_context(struct wined3d_device *device, + struct wined3d_texture *texture, unsigned int sub_resource_idx) +{ + return wined3d_context_gl_acquire(device, texture, sub_resource_idx); +} + +void adapter_gl_release_context(struct wined3d_context *context) +{ + return wined3d_context_gl_release(wined3d_context_gl(context)); +} + +static void adapter_gl_get_wined3d_caps(const struct wined3d_adapter *adapter, struct wined3d_caps *caps) +{ + const struct wined3d_d3d_info *d3d_info = &adapter->d3d_info; + const struct wined3d_gl_info *gl_info = &adapter->gl_info; + + caps->ddraw_caps.dds_caps |= WINEDDSCAPS_BACKBUFFER + | WINEDDSCAPS_COMPLEX + | WINEDDSCAPS_FRONTBUFFER + | WINEDDSCAPS_3DDEVICE + | WINEDDSCAPS_VIDEOMEMORY + | WINEDDSCAPS_OWNDC + | WINEDDSCAPS_LOCALVIDMEM + | WINEDDSCAPS_NONLOCALVIDMEM; + + caps->ddraw_caps.caps |= WINEDDCAPS_3D; + + if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT] || gl_info->supported[EXT_FRAMEBUFFER_OBJECT]) + caps->Caps2 |= WINED3DCAPS2_CANGENMIPMAP; + + if (gl_info->supported[WINED3D_GL_BLEND_EQUATION]) + caps->PrimitiveMiscCaps |= WINED3DPMISCCAPS_BLENDOP; + if (gl_info->supported[EXT_BLEND_EQUATION_SEPARATE] && gl_info->supported[EXT_BLEND_FUNC_SEPARATE]) + caps->PrimitiveMiscCaps |= WINED3DPMISCCAPS_SEPARATEALPHABLEND; + if (gl_info->supported[EXT_DRAW_BUFFERS2]) + caps->PrimitiveMiscCaps |= WINED3DPMISCCAPS_INDEPENDENTWRITEMASKS; + if (gl_info->supported[ARB_FRAMEBUFFER_SRGB]) + caps->PrimitiveMiscCaps |= WINED3DPMISCCAPS_POSTBLENDSRGBCONVERT; + if (~gl_info->quirks & WINED3D_QUIRK_NO_INDEPENDENT_BIT_DEPTHS) + caps->PrimitiveMiscCaps |= WINED3DPMISCCAPS_MRTINDEPENDENTBITDEPTHS; + + if (gl_info->supported[ARB_SAMPLER_OBJECTS] || gl_info->supported[EXT_TEXTURE_LOD_BIAS]) + caps->RasterCaps |= WINED3DPRASTERCAPS_MIPMAPLODBIAS; + + if (gl_info->supported[ARB_TEXTURE_FILTER_ANISOTROPIC]) + { + caps->RasterCaps |= WINED3DPRASTERCAPS_ANISOTROPY; + + caps->TextureFilterCaps |= WINED3DPTFILTERCAPS_MAGFANISOTROPIC + | WINED3DPTFILTERCAPS_MINFANISOTROPIC; + } + + if (gl_info->supported[ARB_BLEND_FUNC_EXTENDED]) + caps->DestBlendCaps |= WINED3DPBLENDCAPS_SRCALPHASAT; + + if (gl_info->supported[EXT_BLEND_COLOR]) + { + caps->SrcBlendCaps |= WINED3DPBLENDCAPS_BLENDFACTOR; + caps->DestBlendCaps |= WINED3DPBLENDCAPS_BLENDFACTOR; + } + + if (gl_info->supported[EXT_TEXTURE3D]) + { + caps->TextureCaps |= WINED3DPTEXTURECAPS_VOLUMEMAP + | WINED3DPTEXTURECAPS_MIPVOLUMEMAP; + if (!d3d_info->texture_npot) + caps->TextureCaps |= WINED3DPTEXTURECAPS_VOLUMEMAP_POW2; + + caps->VolumeTextureFilterCaps |= WINED3DPTFILTERCAPS_MAGFLINEAR + | WINED3DPTFILTERCAPS_MAGFPOINT + | WINED3DPTFILTERCAPS_MINFLINEAR + | WINED3DPTFILTERCAPS_MINFPOINT + | WINED3DPTFILTERCAPS_MIPFLINEAR + | WINED3DPTFILTERCAPS_MIPFPOINT + | WINED3DPTFILTERCAPS_LINEAR + | WINED3DPTFILTERCAPS_LINEARMIPLINEAR + | WINED3DPTFILTERCAPS_LINEARMIPNEAREST + | WINED3DPTFILTERCAPS_MIPLINEAR + | WINED3DPTFILTERCAPS_MIPNEAREST + | WINED3DPTFILTERCAPS_NEAREST; + + caps->VolumeTextureAddressCaps |= WINED3DPTADDRESSCAPS_INDEPENDENTUV + | WINED3DPTADDRESSCAPS_CLAMP + | WINED3DPTADDRESSCAPS_WRAP; + + if (gl_info->supported[ARB_TEXTURE_BORDER_CLAMP]) + { + caps->VolumeTextureAddressCaps |= WINED3DPTADDRESSCAPS_BORDER; + } + if (gl_info->supported[ARB_TEXTURE_MIRRORED_REPEAT]) + { + caps->VolumeTextureAddressCaps |= WINED3DPTADDRESSCAPS_MIRROR; + } + if (gl_info->supported[ARB_TEXTURE_MIRROR_CLAMP_TO_EDGE]) + { + caps->VolumeTextureAddressCaps |= WINED3DPTADDRESSCAPS_MIRRORONCE; + } + + caps->MaxVolumeExtent = gl_info->limits.texture3d_size; + } + + if (gl_info->supported[ARB_TEXTURE_CUBE_MAP]) + { + caps->TextureCaps |= WINED3DPTEXTURECAPS_CUBEMAP + | WINED3DPTEXTURECAPS_MIPCUBEMAP; + if (!d3d_info->texture_npot) + caps->TextureCaps |= WINED3DPTEXTURECAPS_CUBEMAP_POW2; + + caps->CubeTextureFilterCaps |= WINED3DPTFILTERCAPS_MAGFLINEAR + | WINED3DPTFILTERCAPS_MAGFPOINT + | WINED3DPTFILTERCAPS_MINFLINEAR + | WINED3DPTFILTERCAPS_MINFPOINT + | WINED3DPTFILTERCAPS_MIPFLINEAR + | WINED3DPTFILTERCAPS_MIPFPOINT + | WINED3DPTFILTERCAPS_LINEAR + | WINED3DPTFILTERCAPS_LINEARMIPLINEAR + | WINED3DPTFILTERCAPS_LINEARMIPNEAREST + | WINED3DPTFILTERCAPS_MIPLINEAR + | WINED3DPTFILTERCAPS_MIPNEAREST + | WINED3DPTFILTERCAPS_NEAREST; + + if (gl_info->supported[ARB_TEXTURE_FILTER_ANISOTROPIC]) + { + caps->CubeTextureFilterCaps |= WINED3DPTFILTERCAPS_MAGFANISOTROPIC + | WINED3DPTFILTERCAPS_MINFANISOTROPIC; + } + } + + if (gl_info->supported[ARB_TEXTURE_BORDER_CLAMP]) + { + caps->TextureAddressCaps |= WINED3DPTADDRESSCAPS_BORDER; + } + if (gl_info->supported[ARB_TEXTURE_MIRRORED_REPEAT]) + { + caps->TextureAddressCaps |= WINED3DPTADDRESSCAPS_MIRROR; + } + if (gl_info->supported[ARB_TEXTURE_MIRROR_CLAMP_TO_EDGE]) + { + caps->TextureAddressCaps |= WINED3DPTADDRESSCAPS_MIRRORONCE; + } + + if (gl_info->supported[EXT_STENCIL_WRAP]) + { + caps->StencilCaps |= WINED3DSTENCILCAPS_DECR + | WINED3DSTENCILCAPS_INCR; + } + + if (gl_info->supported[WINED3D_GL_VERSION_2_0] + || gl_info->supported[EXT_STENCIL_TWO_SIDE] + || gl_info->supported[ATI_SEPARATE_STENCIL]) + { + caps->StencilCaps |= WINED3DSTENCILCAPS_TWOSIDED; + } + + caps->MaxAnisotropy = gl_info->limits.anisotropy; + + if (caps->VertexShaderVersion >= 3) + { + caps->MaxVertexShader30InstructionSlots + = max(caps->MaxVertexShader30InstructionSlots, gl_info->limits.arb_vs_instructions); + } + if (caps->VertexShaderVersion >= 2) + { + caps->VS20Caps.temp_count = max(caps->VS20Caps.temp_count, gl_info->limits.arb_vs_temps); + + if (gl_info->supported[ARB_HALF_FLOAT_VERTEX]) + caps->DeclTypes |= WINED3DDTCAPS_FLOAT16_2 | WINED3DDTCAPS_FLOAT16_4; + } + + if (caps->PixelShaderVersion >= 3) + { + caps->MaxPixelShader30InstructionSlots + = max(caps->MaxPixelShader30InstructionSlots, gl_info->limits.arb_ps_instructions); + } + if (caps->PixelShaderVersion >= 2) + { + caps->PS20Caps.temp_count = max(caps->PS20Caps.temp_count, gl_info->limits.arb_ps_temps); + } +} + +static BOOL wined3d_check_pixel_format_color(const struct wined3d_pixel_format *cfg, + const struct wined3d_format *format) +{ + /* Float formats need FBOs. If FBOs are used this function isn't called */ + if (format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FLOAT) + return FALSE; + + /* Probably a RGBA_float or color index mode. */ + if (cfg->iPixelType != WGL_TYPE_RGBA_ARB) + return FALSE; + + if (cfg->redSize < format->red_size + || cfg->greenSize < format->green_size + || cfg->blueSize < format->blue_size + || cfg->alphaSize < format->alpha_size) + return FALSE; + + return TRUE; +} + +static BOOL wined3d_check_pixel_format_depth(const struct wined3d_pixel_format *cfg, + const struct wined3d_format *format) +{ + BOOL lockable = FALSE; + + /* Float formats need FBOs. If FBOs are used this function isn't called */ + if (format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FLOAT) + return FALSE; + + if ((format->id == WINED3DFMT_D16_LOCKABLE) || (format->id == WINED3DFMT_D32_FLOAT)) + lockable = TRUE; + + /* On some modern cards like the Geforce8/9, GLX doesn't offer some + * depth/stencil formats which D3D9 reports. We can safely report + * "compatible" formats (e.g. D24 can be used for D16) as long as we + * aren't dealing with a lockable format. This also helps D3D <= 7 as they + * expect D16 which isn't offered without this on Geforce8 cards. */ + if (!(cfg->depthSize == format->depth_size || (!lockable && cfg->depthSize > format->depth_size))) + return FALSE; + + /* Some cards like Intel i915 ones only offer D24S8 but lots of games also + * need a format without stencil. We can allow a mismatch if the format + * doesn't have any stencil bits. If it does have stencil bits the size + * must match, or stencil wrapping would break. */ + if (format->stencil_size && cfg->stencilSize != format->stencil_size) + return FALSE; + + return TRUE; +} + +static BOOL adapter_gl_check_format(const struct wined3d_adapter *adapter, + const struct wined3d_format *adapter_format, const struct wined3d_format *rt_format, + const struct wined3d_format *ds_format) +{ + const struct wined3d_adapter_gl *adapter_gl = wined3d_adapter_gl_const(adapter); + unsigned int i; + + if (wined3d_settings.offscreen_rendering_mode != ORM_BACKBUFFER) + return TRUE; + + if (adapter_format && rt_format) + { + /* In backbuffer mode the front and backbuffer share the same WGL + * pixelformat. The format must match in RGB, alpha is allowed to be + * different. (Only the backbuffer can have alpha.) */ + if (adapter_format->red_size != rt_format->red_size + || adapter_format->green_size != rt_format->green_size + || adapter_format->blue_size != rt_format->blue_size) + { + TRACE("Render target format %s doesn't match with adapter format %s.\n", + debug_d3dformat(rt_format->id), debug_d3dformat(adapter_format->id)); + return FALSE; + } + } + + for (i = 0; i < adapter_gl->pixel_format_count; ++i) + { + const struct wined3d_pixel_format *cfg = &adapter_gl->pixel_formats[i]; + + /* Check if there is a WGL pixel format matching the requirements, the format should also be window + * drawable (not offscreen; e.g. Nvidia offers R5G6B5 for pbuffers even when X is running at 24bit) */ + if (adapter_format && rt_format && !cfg->windowDrawable) + continue; + + if ((!adapter_format || wined3d_check_pixel_format_color(cfg, adapter_format)) + && (!rt_format || wined3d_check_pixel_format_color(cfg, rt_format)) + && (!ds_format || wined3d_check_pixel_format_depth(cfg, ds_format))) + { + TRACE("Pixel format %d is compatible.\n", cfg->iPixelFormat); + return TRUE; + } + } + + return FALSE; +} + +static HRESULT adapter_gl_init_3d(struct wined3d_device *device) +{ + TRACE("device %p.\n", device); + + wined3d_cs_init_object(device->cs, wined3d_device_create_primary_opengl_context_cs, device); + wined3d_cs_finish(device->cs, WINED3D_CS_QUEUE_DEFAULT); + if (!wined3d_swapchain_gl(device->swapchains[0])->context_count) + return E_FAIL; + + return WINED3D_OK; +} + +static void adapter_gl_uninit_3d(struct wined3d_device *device) +{ + TRACE("device %p.\n", device); + + wined3d_cs_destroy_object(device->cs, wined3d_device_delete_opengl_contexts_cs, device); + wined3d_cs_finish(device->cs, WINED3D_CS_QUEUE_DEFAULT); +} + +static void *adapter_gl_map_bo_address(struct wined3d_context *context, + const struct wined3d_bo_address *data, size_t size, uint32_t map_flags) +{ + return wined3d_context_gl_map_bo_address(wined3d_context_gl(context), data, size, map_flags); +} + +static void adapter_gl_unmap_bo_address(struct wined3d_context *context, + const struct wined3d_bo_address *data, unsigned int range_count, const struct wined3d_range *ranges) +{ + wined3d_context_gl_unmap_bo_address(wined3d_context_gl(context), data, range_count, ranges); +} + +static void adapter_gl_copy_bo_address(struct wined3d_context *context, + const struct wined3d_bo_address *dst, const struct wined3d_bo_address *src, size_t size) +{ + wined3d_context_gl_copy_bo_address(wined3d_context_gl(context), dst, src, size); +} + +static HRESULT adapter_gl_create_swapchain(struct wined3d_device *device, + struct wined3d_swapchain_desc *desc, struct wined3d_swapchain_state_parent *state_parent, + void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_swapchain **swapchain) +{ + struct wined3d_swapchain_gl *swapchain_gl; + HRESULT hr; + + TRACE("device %p, desc %p, state_parent %p, parent %p, parent_ops %p, swapchain %p.\n", + device, desc, state_parent, parent, parent_ops, swapchain); + + if (!(swapchain_gl = heap_alloc_zero(sizeof(*swapchain_gl)))) + return E_OUTOFMEMORY; + + if (FAILED(hr = wined3d_swapchain_gl_init(swapchain_gl, device, desc, state_parent, parent, parent_ops))) + { + WARN("Failed to initialise swapchain, hr %#x.\n", hr); + heap_free(swapchain_gl); + return hr; + } + + TRACE("Created swapchain %p.\n", swapchain_gl); + *swapchain = &swapchain_gl->s; + + return hr; +} + +static void adapter_gl_destroy_swapchain(struct wined3d_swapchain *swapchain) +{ + struct wined3d_swapchain_gl *swapchain_gl = wined3d_swapchain_gl(swapchain); + + wined3d_swapchain_gl_cleanup(swapchain_gl); + heap_free(swapchain_gl); +} + +static HRESULT adapter_gl_create_buffer(struct wined3d_device *device, + const struct wined3d_buffer_desc *desc, const struct wined3d_sub_resource_data *data, + void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_buffer **buffer) +{ + struct wined3d_buffer_gl *buffer_gl; + HRESULT hr; + + TRACE("device %p, desc %p, data %p, parent %p, parent_ops %p, buffer %p.\n", + device, desc, data, parent, parent_ops, buffer); + + if (!(buffer_gl = heap_alloc_zero(sizeof(*buffer_gl)))) + return E_OUTOFMEMORY; + + if (FAILED(hr = wined3d_buffer_gl_init(buffer_gl, device, desc, data, parent, parent_ops))) + { + WARN("Failed to initialise buffer, hr %#x.\n", hr); + heap_free(buffer_gl); + return hr; + } + + TRACE("Created buffer %p.\n", buffer_gl); + *buffer = &buffer_gl->b; + + return hr; +} + +static void adapter_gl_destroy_buffer(struct wined3d_buffer *buffer) +{ + struct wined3d_buffer_gl *buffer_gl = wined3d_buffer_gl(buffer); + struct wined3d_device *device = buffer_gl->b.resource.device; + unsigned int swapchain_count = device->swapchain_count; + + TRACE("buffer_gl %p.\n", buffer_gl); + + /* Take a reference to the device, in case releasing the buffer would + * cause the device to be destroyed. However, swapchain resources don't + * take a reference to the device, and we wouldn't want to increment the + * refcount on a device that's in the process of being destroyed. */ + if (swapchain_count) + wined3d_device_incref(device); + wined3d_buffer_cleanup(&buffer_gl->b); + wined3d_cs_destroy_object(device->cs, heap_free, buffer_gl); + if (swapchain_count) + wined3d_device_decref(device); +} + +static HRESULT adapter_gl_create_texture(struct wined3d_device *device, + const struct wined3d_resource_desc *desc, unsigned int layer_count, unsigned int level_count, + uint32_t flags, void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_texture **texture) +{ + struct wined3d_texture_gl *texture_gl; + HRESULT hr; + + TRACE("device %p, desc %p, layer_count %u, level_count %u, flags %#x, parent %p, parent_ops %p, texture %p.\n", + device, desc, layer_count, level_count, flags, parent, parent_ops, texture); + + if (!(texture_gl = wined3d_texture_allocate_object_memory(sizeof(*texture_gl), level_count, layer_count))) + return E_OUTOFMEMORY; + + if (FAILED(hr = wined3d_texture_gl_init(texture_gl, device, desc, + layer_count, level_count, flags, parent, parent_ops))) + { + WARN("Failed to initialise texture, hr %#x.\n", hr); + heap_free(texture_gl); + return hr; + } + + TRACE("Created texture %p.\n", texture_gl); + *texture = &texture_gl->t; + + return hr; +} + +static void adapter_gl_destroy_texture(struct wined3d_texture *texture) +{ + struct wined3d_texture_gl *texture_gl = wined3d_texture_gl(texture); + struct wined3d_device *device = texture_gl->t.resource.device; + unsigned int swapchain_count = device->swapchain_count; + + TRACE("texture_gl %p.\n", texture_gl); + + /* Take a reference to the device, in case releasing the texture would + * cause the device to be destroyed. However, swapchain resources don't + * take a reference to the device, and we wouldn't want to increment the + * refcount on a device that's in the process of being destroyed. */ + if (swapchain_count) + wined3d_device_incref(device); + + wined3d_texture_sub_resources_destroyed(texture); + texture->resource.parent_ops->wined3d_object_destroyed(texture->resource.parent); + + wined3d_texture_cleanup(&texture_gl->t); + wined3d_cs_destroy_object(device->cs, heap_free, texture_gl); + + if (swapchain_count) + wined3d_device_decref(device); +} + +static HRESULT adapter_gl_create_rendertarget_view(const struct wined3d_view_desc *desc, + struct wined3d_resource *resource, void *parent, const struct wined3d_parent_ops *parent_ops, + struct wined3d_rendertarget_view **view) +{ + struct wined3d_rendertarget_view_gl *view_gl; + HRESULT hr; + + TRACE("desc %s, resource %p, parent %p, parent_ops %p, view %p.\n", + wined3d_debug_view_desc(desc, resource), resource, parent, parent_ops, view); + + if (!(view_gl = heap_alloc_zero(sizeof(*view_gl)))) + return E_OUTOFMEMORY; + + if (FAILED(hr = wined3d_rendertarget_view_gl_init(view_gl, desc, resource, parent, parent_ops))) + { + WARN("Failed to initialise view, hr %#x.\n", hr); + heap_free(view_gl); + return hr; + } + + TRACE("Created render target view %p.\n", view_gl); + *view = &view_gl->v; + + return hr; +} + +struct wined3d_view_gl_destroy_ctx +{ + struct wined3d_device *device; + const struct wined3d_gl_view *gl_view; + struct wined3d_bo_gl *counter_bo; + void *object; + struct wined3d_view_gl_destroy_ctx *free; +}; + +static void wined3d_view_gl_destroy_object(void *object) +{ + struct wined3d_view_gl_destroy_ctx *ctx = object; + const struct wined3d_gl_info *gl_info; + struct wined3d_context *context; + struct wined3d_device *device; + GLuint counter_id; + + TRACE("ctx %p.\n", ctx); + + device = ctx->device; + + counter_id = ctx->counter_bo ? ctx->counter_bo->id : 0; + if (ctx->gl_view->name || counter_id) + { + context = context_acquire(device, NULL, 0); + gl_info = wined3d_context_gl(context)->gl_info; + if (ctx->gl_view->name) + { + context_gl_resource_released(device, ctx->gl_view->name, FALSE); + gl_info->gl_ops.gl.p_glDeleteTextures(1, &ctx->gl_view->name); + } + if (counter_id) + wined3d_context_gl_destroy_bo(wined3d_context_gl(context), ctx->counter_bo); + checkGLcall("delete resources"); + context_release(context); + } + + heap_free(ctx->object); + heap_free(ctx->free); +} + +static void wined3d_view_gl_destroy(struct wined3d_device *device, + const struct wined3d_gl_view *gl_view, struct wined3d_bo_gl *counter_bo, void *object) +{ + struct wined3d_view_gl_destroy_ctx *ctx, c; + + if (!(ctx = heap_alloc(sizeof(*ctx)))) + ctx = &c; + ctx->device = device; + ctx->gl_view = gl_view; + ctx->counter_bo = counter_bo; + ctx->object = object; + ctx->free = ctx != &c ? ctx : NULL; + + wined3d_cs_destroy_object(device->cs, wined3d_view_gl_destroy_object, ctx); + if (ctx == &c) + device->cs->ops->finish(device->cs, WINED3D_CS_QUEUE_DEFAULT); +} + +static void adapter_gl_destroy_rendertarget_view(struct wined3d_rendertarget_view *view) +{ + struct wined3d_rendertarget_view_gl *view_gl = wined3d_rendertarget_view_gl(view); + struct wined3d_device *device = view_gl->v.resource->device; + unsigned int swapchain_count = device->swapchain_count; + + TRACE("view_gl %p.\n", view_gl); + + /* Take a reference to the device, in case releasing the view's resource + * would cause the device to be destroyed. However, swapchain resources + * don't take a reference to the device, and we wouldn't want to increment + * the refcount on a device that's in the process of being destroyed. */ + if (swapchain_count) + wined3d_device_incref(device); + wined3d_rendertarget_view_cleanup(&view_gl->v); + wined3d_view_gl_destroy(device, &view_gl->gl_view, NULL, view_gl); + if (swapchain_count) + wined3d_device_decref(device); +} + +static HRESULT adapter_gl_create_shader_resource_view(const struct wined3d_view_desc *desc, + struct wined3d_resource *resource, void *parent, const struct wined3d_parent_ops *parent_ops, + struct wined3d_shader_resource_view **view) +{ + struct wined3d_shader_resource_view_gl *view_gl; + HRESULT hr; + + TRACE("desc %s, resource %p, parent %p, parent_ops %p, view %p.\n", + wined3d_debug_view_desc(desc, resource), resource, parent, parent_ops, view); + + if (!(view_gl = heap_alloc_zero(sizeof(*view_gl)))) + return E_OUTOFMEMORY; + + if (FAILED(hr = wined3d_shader_resource_view_gl_init(view_gl, desc, resource, parent, parent_ops))) + { + WARN("Failed to initialise view, hr %#x.\n", hr); + heap_free(view_gl); + return hr; + } + + TRACE("Created shader resource view %p.\n", view_gl); + *view = &view_gl->v; + + return hr; +} + +static void adapter_gl_destroy_shader_resource_view(struct wined3d_shader_resource_view *view) +{ + struct wined3d_shader_resource_view_gl *view_gl = wined3d_shader_resource_view_gl(view); + struct wined3d_device *device = view_gl->v.resource->device; + unsigned int swapchain_count = device->swapchain_count; + + TRACE("view_gl %p.\n", view_gl); + + /* Take a reference to the device, in case releasing the view's resource + * would cause the device to be destroyed. However, swapchain resources + * don't take a reference to the device, and we wouldn't want to increment + * the refcount on a device that's in the process of being destroyed. */ + if (swapchain_count) + wined3d_device_incref(device); + list_remove(&view_gl->bo_user.entry); + wined3d_shader_resource_view_cleanup(&view_gl->v); + wined3d_view_gl_destroy(device, &view_gl->gl_view, NULL, view_gl); + if (swapchain_count) + wined3d_device_decref(device); +} + +static HRESULT adapter_gl_create_unordered_access_view(const struct wined3d_view_desc *desc, + struct wined3d_resource *resource, void *parent, const struct wined3d_parent_ops *parent_ops, + struct wined3d_unordered_access_view **view) +{ + struct wined3d_unordered_access_view_gl *view_gl; + HRESULT hr; + + TRACE("desc %s, resource %p, parent %p, parent_ops %p, view %p.\n", + wined3d_debug_view_desc(desc, resource), resource, parent, parent_ops, view); + + if (!(view_gl = heap_alloc_zero(sizeof(*view_gl)))) + return E_OUTOFMEMORY; + + if (FAILED(hr = wined3d_unordered_access_view_gl_init(view_gl, desc, resource, parent, parent_ops))) + { + WARN("Failed to initialise view, hr %#x.\n", hr); + heap_free(view_gl); + return hr; + } + + TRACE("Created unordered access view %p.\n", view_gl); + *view = &view_gl->v; + + return hr; +} + +static void adapter_gl_destroy_unordered_access_view(struct wined3d_unordered_access_view *view) +{ + struct wined3d_unordered_access_view_gl *view_gl = wined3d_unordered_access_view_gl(view); + struct wined3d_device *device = view_gl->v.resource->device; + unsigned int swapchain_count = device->swapchain_count; + + TRACE("view_gl %p.\n", view_gl); + + /* Take a reference to the device, in case releasing the view's resource + * would cause the device to be destroyed. However, swapchain resources + * don't take a reference to the device, and we wouldn't want to increment + * the refcount on a device that's in the process of being destroyed. */ + if (swapchain_count) + wined3d_device_incref(device); + list_remove(&view_gl->bo_user.entry); + wined3d_unordered_access_view_cleanup(&view_gl->v); + wined3d_view_gl_destroy(device, &view_gl->gl_view, &view_gl->counter_bo, view_gl); + if (swapchain_count) + wined3d_device_decref(device); +} + +static HRESULT adapter_gl_create_sampler(struct wined3d_device *device, const struct wined3d_sampler_desc *desc, + void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_sampler **sampler) +{ + struct wined3d_sampler_gl *sampler_gl; + + TRACE("device %p, desc %p, parent %p, parent_ops %p, sampler %p.\n", + device, desc, parent, parent_ops, sampler); + + if (!(sampler_gl = heap_alloc_zero(sizeof(*sampler_gl)))) + return E_OUTOFMEMORY; + + wined3d_sampler_gl_init(sampler_gl, device, desc, parent, parent_ops); + + TRACE("Created sampler %p.\n", sampler_gl); + *sampler = &sampler_gl->s; + + return WINED3D_OK; +} + +static void wined3d_sampler_gl_destroy_object(void *object) +{ + struct wined3d_sampler_gl *sampler_gl = object; + const struct wined3d_gl_info *gl_info; + struct wined3d_context *context; + + TRACE("sampler_gl %p.\n", sampler_gl); + + if (sampler_gl->name) + { + context = context_acquire(sampler_gl->s.device, NULL, 0); + gl_info = wined3d_context_gl(context)->gl_info; + GL_EXTCALL(glDeleteSamplers(1, &sampler_gl->name)); + context_release(context); + } + + heap_free(sampler_gl); +} + +static void adapter_gl_destroy_sampler(struct wined3d_sampler *sampler) +{ + struct wined3d_sampler_gl *sampler_gl = wined3d_sampler_gl(sampler); + + TRACE("sampler_gl %p.\n", sampler_gl); + + wined3d_cs_destroy_object(sampler->device->cs, wined3d_sampler_gl_destroy_object, sampler_gl); +} + +static HRESULT adapter_gl_create_query(struct wined3d_device *device, enum wined3d_query_type type, + void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_query **query) +{ + TRACE("device %p, type %#x, parent %p, parent_ops %p, query %p.\n", + device, type, parent, parent_ops, query); + + return wined3d_query_gl_create(device, type, parent, parent_ops, query); +} + +static void wined3d_query_gl_destroy_object(void *object) +{ + struct wined3d_query *query = object; + + TRACE("query %p.\n", query); + + if (query->buffer_object) + { + struct wined3d_context *context; + + context = context_acquire(query->device, NULL, 0); + wined3d_query_gl_destroy_buffer_object(wined3d_context_gl(context), query); + context_release(context); + } + + /* Queries are specific to the GL context that created them. Not + * deleting the query will obviously leak it, but that's still better + * than potentially deleting a different query with the same id in this + * context, and (still) leaking the actual query. */ + query->query_ops->query_destroy(query); +} + +static void adapter_gl_destroy_query(struct wined3d_query *query) +{ + TRACE("query %p.\n", query); + + wined3d_cs_destroy_object(query->device->cs, wined3d_query_gl_destroy_object, query); +} + +static void adapter_gl_flush_context(struct wined3d_context *context) +{ + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + + TRACE("context_gl %p.\n", context_gl); + + if (context_gl->valid) + context_gl->gl_info->gl_ops.gl.p_glFlush(); +} + +static void adapter_gl_clear_uav(struct wined3d_context *context, + struct wined3d_unordered_access_view *view, const struct wined3d_uvec4 *clear_value) +{ + TRACE("context %p, view %p, clear_value %s.\n", context, view, debug_uvec4(clear_value)); + + wined3d_unordered_access_view_gl_clear_uint(wined3d_unordered_access_view_gl(view), + clear_value, wined3d_context_gl(context)); +} + +static const struct wined3d_adapter_ops wined3d_adapter_gl_ops = +{ + .adapter_destroy = adapter_gl_destroy, + .adapter_create_device = adapter_gl_create_device, + .adapter_destroy_device = adapter_gl_destroy_device, + .adapter_acquire_context = adapter_gl_acquire_context, + .adapter_release_context = adapter_gl_release_context, + .adapter_get_wined3d_caps = adapter_gl_get_wined3d_caps, + .adapter_check_format = adapter_gl_check_format, + .adapter_init_3d = adapter_gl_init_3d, + .adapter_uninit_3d = adapter_gl_uninit_3d, + .adapter_map_bo_address = adapter_gl_map_bo_address, + .adapter_unmap_bo_address = adapter_gl_unmap_bo_address, + .adapter_copy_bo_address = adapter_gl_copy_bo_address, + .adapter_create_swapchain = adapter_gl_create_swapchain, + .adapter_destroy_swapchain = adapter_gl_destroy_swapchain, + .adapter_create_buffer = adapter_gl_create_buffer, + .adapter_destroy_buffer = adapter_gl_destroy_buffer, + .adapter_create_texture = adapter_gl_create_texture, + .adapter_destroy_texture = adapter_gl_destroy_texture, + .adapter_create_rendertarget_view = adapter_gl_create_rendertarget_view, + .adapter_destroy_rendertarget_view = adapter_gl_destroy_rendertarget_view, + .adapter_create_shader_resource_view = adapter_gl_create_shader_resource_view, + .adapter_destroy_shader_resource_view = adapter_gl_destroy_shader_resource_view, + .adapter_create_unordered_access_view = adapter_gl_create_unordered_access_view, + .adapter_destroy_unordered_access_view = adapter_gl_destroy_unordered_access_view, + .adapter_create_sampler = adapter_gl_create_sampler, + .adapter_destroy_sampler = adapter_gl_destroy_sampler, + .adapter_create_query = adapter_gl_create_query, + .adapter_destroy_query = adapter_gl_destroy_query, + .adapter_flush_context = adapter_gl_flush_context, + .adapter_draw_primitive = draw_primitive, + .adapter_dispatch_compute = dispatch_compute, + .adapter_clear_uav = adapter_gl_clear_uav, +}; + +static void wined3d_adapter_gl_init_d3d_info(struct wined3d_adapter_gl *adapter_gl, uint32_t wined3d_creation_flags) +{ + const struct wined3d_gl_info *gl_info = &adapter_gl->a.gl_info; + struct wined3d_d3d_info *d3d_info = &adapter_gl->a.d3d_info; + struct wined3d_vertex_caps vertex_caps; + struct fragment_caps fragment_caps; + struct shader_caps shader_caps; + GLfloat f[2]; + + adapter_gl->a.shader_backend->shader_get_caps(&adapter_gl->a, &shader_caps); + adapter_gl->a.vertex_pipe->vp_get_caps(&adapter_gl->a, &vertex_caps); + adapter_gl->a.misc_state_template = misc_state_template_gl; + adapter_gl->a.fragment_pipe->get_caps(&adapter_gl->a, &fragment_caps); + + d3d_info->limits.vs_version = shader_caps.vs_version; + d3d_info->limits.hs_version = shader_caps.hs_version; + d3d_info->limits.ds_version = shader_caps.ds_version; + d3d_info->limits.gs_version = shader_caps.gs_version; + d3d_info->limits.ps_version = shader_caps.ps_version; + d3d_info->limits.cs_version = shader_caps.cs_version; + d3d_info->limits.vs_uniform_count = shader_caps.vs_uniform_count; + d3d_info->limits.ps_uniform_count = shader_caps.ps_uniform_count; + d3d_info->limits.varying_count = shader_caps.varying_count; + d3d_info->limits.ffp_textures = fragment_caps.MaxSimultaneousTextures; + d3d_info->limits.ffp_blend_stages = fragment_caps.MaxTextureBlendStages; + TRACE("Max texture stages: %u.\n", d3d_info->limits.ffp_blend_stages); + d3d_info->limits.ffp_vertex_blend_matrices = vertex_caps.max_vertex_blend_matrices; + d3d_info->limits.active_light_count = vertex_caps.max_active_lights; + + d3d_info->limits.max_rt_count = gl_info->limits.buffers; + d3d_info->limits.max_clip_distances = gl_info->limits.user_clip_distances; + d3d_info->limits.texture_size = gl_info->limits.texture_size; + + gl_info->gl_ops.gl.p_glGetFloatv(gl_info->supported[WINED3D_GL_LEGACY_CONTEXT] + ? GL_ALIASED_POINT_SIZE_RANGE : GL_POINT_SIZE_RANGE, f); + d3d_info->limits.pointsize_max = f[1]; + TRACE("Maximum point size support - max point size %.8e.\n", f[1]); + + d3d_info->wined3d_creation_flags = wined3d_creation_flags; + d3d_info->xyzrhw = vertex_caps.xyzrhw; + d3d_info->emulated_flatshading = vertex_caps.emulated_flatshading; + d3d_info->ffp_generic_attributes = vertex_caps.ffp_generic_attributes; + d3d_info->ffp_alpha_test = !!gl_info->supported[WINED3D_GL_LEGACY_CONTEXT]; + d3d_info->vs_clipping = shader_caps.wined3d_caps & WINED3D_SHADER_CAP_VS_CLIPPING; + d3d_info->shader_color_key = !!(fragment_caps.wined3d_caps & WINED3D_FRAGMENT_CAP_COLOR_KEY); + d3d_info->shader_double_precision = !!(shader_caps.wined3d_caps & WINED3D_SHADER_CAP_DOUBLE_PRECISION); + d3d_info->shader_output_interpolation = !!(shader_caps.wined3d_caps & WINED3D_SHADER_CAP_OUTPUT_INTERPOLATION); + d3d_info->viewport_array_index_any_shader = !!gl_info->supported[ARB_SHADER_VIEWPORT_LAYER_ARRAY]; + d3d_info->texture_npot = !!gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO]; + d3d_info->texture_npot_conditional = gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT] + || gl_info->supported[ARB_TEXTURE_RECTANGLE]; + d3d_info->draw_base_vertex_offset = !!gl_info->supported[ARB_DRAW_ELEMENTS_BASE_VERTEX]; + d3d_info->vertex_bgra = !!gl_info->supported[ARB_VERTEX_ARRAY_BGRA]; + d3d_info->texture_swizzle = !!gl_info->supported[ARB_TEXTURE_SWIZZLE]; + d3d_info->srgb_read_control = !!gl_info->supported[EXT_TEXTURE_SRGB_DECODE]; + d3d_info->srgb_write_control = !!gl_info->supported[ARB_FRAMEBUFFER_SRGB]; + d3d_info->clip_control = !!gl_info->supported[ARB_CLIP_CONTROL]; + d3d_info->full_ffp_varyings = !!(shader_caps.wined3d_caps & WINED3D_SHADER_CAP_FULL_FFP_VARYINGS); + d3d_info->scaled_resolve = !!gl_info->supported[EXT_FRAMEBUFFER_MULTISAMPLE_BLIT_SCALED]; + d3d_info->feature_level = feature_level_from_caps(gl_info, &shader_caps, &fragment_caps); + + if (gl_info->supported[ARB_TEXTURE_MULTISAMPLE]) + d3d_info->multisample_draw_location = WINED3D_LOCATION_TEXTURE_RGB; + else + d3d_info->multisample_draw_location = WINED3D_LOCATION_RB_MULTISAMPLE; +} + +static BOOL wined3d_adapter_gl_init(struct wined3d_adapter_gl *adapter_gl, + unsigned int ordinal, unsigned int wined3d_creation_flags) +{ + static const DWORD supported_gl_versions[] = + { + MAKEDWORD_VERSION(4, 4), + MAKEDWORD_VERSION(3, 2), + MAKEDWORD_VERSION(1, 0), + }; + struct wined3d_driver_info *driver_info = &adapter_gl->a.driver_info; + struct wined3d_gl_info *gl_info = &adapter_gl->a.gl_info; + struct wined3d_caps_gl_ctx caps_gl_ctx = {0}; + LUID primary_luid, *luid = NULL; + unsigned int i; + + TRACE("adapter_gl %p, ordinal %u, wined3d_creation_flags %#x.\n", + adapter_gl, ordinal, wined3d_creation_flags); + + if (ordinal == 0 && wined3d_get_primary_adapter_luid(&primary_luid)) + luid = &primary_luid; + + if (!wined3d_adapter_init(&adapter_gl->a, ordinal, luid, &wined3d_adapter_gl_ops)) + return FALSE; + + /* Dynamically load all GL core functions */ +#ifdef USE_WIN32_OPENGL + { + HMODULE mod_gl = GetModuleHandleA("opengl32.dll"); +#define USE_GL_FUNC(f) gl_info->gl_ops.gl.p_##f = (void *)GetProcAddress(mod_gl, #f); + ALL_WGL_FUNCS +#undef USE_GL_FUNC + gl_info->gl_ops.wgl.p_wglSwapBuffers = (void *)GetProcAddress(mod_gl, "wglSwapBuffers"); + gl_info->gl_ops.wgl.p_wglGetPixelFormat = (void *)GetProcAddress(mod_gl, "wglGetPixelFormat"); + } +#else + /* To bypass the opengl32 thunks retrieve functions from the WGL driver instead of opengl32 */ + { + HDC hdc = GetDC( 0 ); + const struct opengl_funcs *wgl_driver = __wine_get_wgl_driver( hdc, WINE_WGL_DRIVER_VERSION ); + ReleaseDC( 0, hdc ); + if (!wgl_driver || wgl_driver == (void *)-1) return FALSE; + gl_info->gl_ops.wgl = wgl_driver->wgl; + gl_info->gl_ops.gl = wgl_driver->gl; + } +#endif + + gl_info->p_glEnableWINE = gl_info->gl_ops.gl.p_glEnable; + gl_info->p_glDisableWINE = gl_info->gl_ops.gl.p_glDisable; + + if (!wined3d_caps_gl_ctx_create(&adapter_gl->a, &caps_gl_ctx)) + { + ERR("Failed to get a GL context for adapter %p.\n", adapter_gl); + return FALSE; + } + + for (i = 0; i < ARRAY_SIZE(supported_gl_versions); ++i) + { + if (supported_gl_versions[i] <= wined3d_settings.max_gl_version) + break; + } + if (i == ARRAY_SIZE(supported_gl_versions)) + { + ERR_(winediag)("Requested invalid GL version %u.%u.\n", + wined3d_settings.max_gl_version >> 16, wined3d_settings.max_gl_version & 0xffff); + i = ARRAY_SIZE(supported_gl_versions) - 1; + } + + for (; i < ARRAY_SIZE(supported_gl_versions); ++i) + { + gl_info->selected_gl_version = supported_gl_versions[i]; + + if (wined3d_caps_gl_ctx_create_attribs(&caps_gl_ctx, gl_info)) + break; + + WARN("Couldn't create an OpenGL %u.%u context, trying fallback to a lower version.\n", + supported_gl_versions[i] >> 16, supported_gl_versions[i] & 0xffff); + } + + if (!wined3d_adapter_init_gl_caps(&adapter_gl->a, &caps_gl_ctx, wined3d_creation_flags)) + { + ERR("Failed to initialize GL caps for adapter %p.\n", adapter_gl); + wined3d_caps_gl_ctx_destroy(&caps_gl_ctx); + return FALSE; + } + + wined3d_adapter_gl_init_d3d_info(adapter_gl, wined3d_creation_flags); + + if (!adapter_gl->a.d3d_info.shader_color_key) + { + /* We do not want to deal with re-creating immutable texture storage + * for colour-keying emulation. */ + WARN("Disabling ARB_texture_storage because fragment pipe doesn't support colour-keying.\n"); + gl_info->supported[ARB_TEXTURE_STORAGE] = FALSE; + } + + if (!wined3d_driver_info_init(driver_info, caps_gl_ctx.gpu_description, adapter_gl->a.d3d_info.feature_level, + caps_gl_ctx.vram_bytes, 0)) + { + wined3d_caps_gl_ctx_destroy(&caps_gl_ctx); + return FALSE; + } + TRACE("Reporting (fake) driver version 0x%08x-0x%08x.\n", + driver_info->version_high, driver_info->version_low); + + if (wined3d_settings.offscreen_rendering_mode == ORM_BACKBUFFER) + ERR_(winediag)("You are using the backbuffer for offscreen rendering. " + "This is unsupported, and will be removed in a future version.\n"); + + wined3d_adapter_init_fb_cfgs(adapter_gl, caps_gl_ctx.dc); + /* We haven't found any suitable formats. This should only happen in + * case of GDI software rendering, which is pretty useless anyway. */ + if (!adapter_gl->pixel_format_count) + { + WARN("No suitable pixel formats found.\n"); + wined3d_caps_gl_ctx_destroy(&caps_gl_ctx); + heap_free(adapter_gl->pixel_formats); + return FALSE; + } + + if (!wined3d_adapter_gl_init_format_info(&adapter_gl->a, &caps_gl_ctx)) + { + ERR("Failed to initialize GL format info.\n"); + wined3d_caps_gl_ctx_destroy(&caps_gl_ctx); + heap_free(adapter_gl->pixel_formats); + return FALSE; + } + + wined3d_caps_gl_ctx_destroy(&caps_gl_ctx); + + wined3d_adapter_init_ffp_attrib_ops(&adapter_gl->a); + + return TRUE; +} + +struct wined3d_adapter *wined3d_adapter_gl_create(unsigned int ordinal, unsigned int wined3d_creation_flags) +{ + struct wined3d_adapter_gl *adapter; + + if (!(adapter = heap_alloc_zero(sizeof(*adapter)))) + return NULL; + + if (!wined3d_adapter_gl_init(adapter, ordinal, wined3d_creation_flags)) + { + heap_free(adapter); + return NULL; + } + + TRACE("Created adapter %p.\n", adapter); + + return &adapter->a; +} diff --git a/wrappers/directx/d3dwine_wrapper/adapter_vk.c b/wrappers/directx/d3dwine_wrapper/adapter_vk.c new file mode 100644 index 00000000000..8c79cc05058 --- /dev/null +++ b/wrappers/directx/d3dwine_wrapper/adapter_vk.c @@ -0,0 +1,2383 @@ +/* + * Copyright 2018 Henri Verbeet for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "config.h" +#include "wine/port.h" +#include "wined3d_private.h" + +#include "wine/vulkan_driver.h" + +WINE_DEFAULT_DEBUG_CHANNEL(d3d); + +static const struct wined3d_state_entry_template misc_state_template_vk[] = +{ + {STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_VERTEX), {STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_VERTEX), state_nop}}, + {STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_HULL), {STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_HULL), state_nop}}, + {STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_DOMAIN), {STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_DOMAIN), state_nop}}, + {STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_GEOMETRY), {STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_GEOMETRY), state_nop}}, + {STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_PIXEL), {STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_PIXEL), state_nop}}, + {STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_COMPUTE), {STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_COMPUTE), state_nop}}, + {STATE_GRAPHICS_SHADER_RESOURCE_BINDING, {STATE_GRAPHICS_SHADER_RESOURCE_BINDING, state_nop}}, + {STATE_GRAPHICS_UNORDERED_ACCESS_VIEW_BINDING, {STATE_GRAPHICS_UNORDERED_ACCESS_VIEW_BINDING, state_nop}}, + {STATE_COMPUTE_SHADER_RESOURCE_BINDING, {STATE_COMPUTE_SHADER_RESOURCE_BINDING, state_nop}}, + {STATE_COMPUTE_UNORDERED_ACCESS_VIEW_BINDING, {STATE_COMPUTE_UNORDERED_ACCESS_VIEW_BINDING, state_nop}}, + {STATE_STREAM_OUTPUT, {STATE_STREAM_OUTPUT, state_nop}}, + {STATE_BLEND, {STATE_BLEND, state_nop}}, + {STATE_BLEND_FACTOR, {STATE_BLEND_FACTOR, state_nop}}, + {STATE_SAMPLE_MASK, {STATE_SAMPLE_MASK, state_nop}}, + {STATE_STREAMSRC, {STATE_STREAMSRC, state_nop}}, + {STATE_VDECL, {STATE_VDECL, state_nop}}, + {STATE_DEPTH_STENCIL, {STATE_DEPTH_STENCIL, state_nop}}, + {STATE_STENCIL_REF, {STATE_STENCIL_REF, state_nop}}, + {STATE_RASTERIZER, {STATE_RASTERIZER, state_nop}}, + {STATE_SCISSORRECT, {STATE_SCISSORRECT, state_nop}}, + {STATE_POINTSPRITECOORDORIGIN, {STATE_POINTSPRITECOORDORIGIN, state_nop}}, + + {STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_MAT00), {STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_MAT00), state_nop}}, + {STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_MAT01), {STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_MAT00)}}, + {STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_MAT10), {STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_MAT00)}}, + {STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_MAT11), {STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_MAT00)}}, + {STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_MAT00), {STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_MAT00), state_nop}}, + {STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_MAT01), {STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_MAT00)}}, + {STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_MAT10), {STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_MAT00)}}, + {STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_MAT11), {STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_MAT00)}}, + {STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_MAT00), {STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_MAT00), state_nop}}, + {STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_MAT01), {STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_MAT00)}}, + {STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_MAT10), {STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_MAT00)}}, + {STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_MAT11), {STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_MAT00)}}, + {STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_MAT00), {STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_MAT00), state_nop}}, + {STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_MAT01), {STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_MAT00)}}, + {STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_MAT10), {STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_MAT00)}}, + {STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_MAT11), {STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_MAT00)}}, + {STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_MAT00), {STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_MAT00), state_nop}}, + {STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_MAT01), {STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_MAT00)}}, + {STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_MAT10), {STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_MAT00)}}, + {STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_MAT11), {STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_MAT00)}}, + {STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_MAT00), {STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_MAT00), state_nop}}, + {STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_MAT01), {STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_MAT00)}}, + {STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_MAT10), {STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_MAT00)}}, + {STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_MAT11), {STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_MAT00)}}, + {STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_MAT00), {STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_MAT00), state_nop}}, + {STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_MAT01), {STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_MAT00)}}, + {STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_MAT10), {STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_MAT00)}}, + {STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_MAT11), {STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_MAT00)}}, + {STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_MAT00), {STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_MAT00), state_nop}}, + {STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_MAT01), {STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_MAT00)}}, + {STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_MAT10), {STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_MAT00)}}, + {STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_MAT11), {STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_MAT00)}}, + {STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_LSCALE), {STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_LSCALE), state_nop}}, + {STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_LOFFSET), {STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_LSCALE)}}, + {STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_LSCALE), {STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_LSCALE), state_nop}}, + {STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_LOFFSET), {STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_LSCALE)}}, + {STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_LSCALE), {STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_LSCALE), state_nop}}, + {STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_LOFFSET), {STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_LSCALE)}}, + {STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_LSCALE), {STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_LSCALE), state_nop}}, + {STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_LOFFSET), {STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_LSCALE)}}, + {STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_LSCALE), {STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_LSCALE), state_nop}}, + {STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_LOFFSET), {STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_LSCALE)}}, + {STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_LSCALE), {STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_LSCALE), state_nop}}, + {STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_LOFFSET), {STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_LSCALE)}}, + {STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_LSCALE), {STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_LSCALE), state_nop}}, + {STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_LOFFSET), {STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_LSCALE)}}, + {STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_LSCALE), {STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_LSCALE), state_nop}}, + {STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_LOFFSET), {STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_LSCALE)}}, + + {STATE_VIEWPORT, {STATE_VIEWPORT, state_nop}}, + {STATE_INDEXBUFFER, {STATE_INDEXBUFFER, state_nop}}, + {STATE_RENDER(WINED3D_RS_ANTIALIAS), {STATE_RENDER(WINED3D_RS_ANTIALIAS), state_nop}}, + {STATE_RENDER(WINED3D_RS_TEXTUREPERSPECTIVE), {STATE_RENDER(WINED3D_RS_TEXTUREPERSPECTIVE), state_nop}}, + {STATE_RENDER(WINED3D_RS_WRAPU), {STATE_RENDER(WINED3D_RS_WRAPU), state_nop}}, + {STATE_RENDER(WINED3D_RS_WRAPV), {STATE_RENDER(WINED3D_RS_WRAPV), state_nop}}, + {STATE_RENDER(WINED3D_RS_LINEPATTERN), {STATE_RENDER(WINED3D_RS_LINEPATTERN), state_nop}}, + {STATE_RENDER(WINED3D_RS_MONOENABLE), {STATE_RENDER(WINED3D_RS_MONOENABLE), state_nop}}, + {STATE_RENDER(WINED3D_RS_ROP2), {STATE_RENDER(WINED3D_RS_ROP2), state_nop}}, + {STATE_RENDER(WINED3D_RS_PLANEMASK), {STATE_RENDER(WINED3D_RS_PLANEMASK), state_nop}}, + {STATE_RENDER(WINED3D_RS_LASTPIXEL), {STATE_RENDER(WINED3D_RS_LASTPIXEL), state_nop}}, + {STATE_RENDER(WINED3D_RS_DITHERENABLE), {STATE_RENDER(WINED3D_RS_DITHERENABLE), state_nop}}, + {STATE_RENDER(WINED3D_RS_SUBPIXEL), {STATE_RENDER(WINED3D_RS_SUBPIXEL), state_nop}}, + {STATE_RENDER(WINED3D_RS_SUBPIXELX), {STATE_RENDER(WINED3D_RS_SUBPIXELX), state_nop}}, + {STATE_RENDER(WINED3D_RS_STIPPLEDALPHA), {STATE_RENDER(WINED3D_RS_STIPPLEDALPHA), state_nop}}, + {STATE_RENDER(WINED3D_RS_STIPPLEENABLE), {STATE_RENDER(WINED3D_RS_STIPPLEENABLE), state_nop}}, + {STATE_RENDER(WINED3D_RS_MIPMAPLODBIAS), {STATE_RENDER(WINED3D_RS_MIPMAPLODBIAS), state_nop}}, + {STATE_RENDER(WINED3D_RS_ANISOTROPY), {STATE_RENDER(WINED3D_RS_ANISOTROPY), state_nop}}, + {STATE_RENDER(WINED3D_RS_FLUSHBATCH), {STATE_RENDER(WINED3D_RS_FLUSHBATCH), state_nop}}, + {STATE_RENDER(WINED3D_RS_TRANSLUCENTSORTINDEPENDENT), {STATE_RENDER(WINED3D_RS_TRANSLUCENTSORTINDEPENDENT), state_nop}}, + {STATE_RENDER(WINED3D_RS_WRAP0), {STATE_RENDER(WINED3D_RS_WRAP0), state_nop}}, + {STATE_RENDER(WINED3D_RS_WRAP1), {STATE_RENDER(WINED3D_RS_WRAP0)}}, + {STATE_RENDER(WINED3D_RS_WRAP2), {STATE_RENDER(WINED3D_RS_WRAP0)}}, + {STATE_RENDER(WINED3D_RS_WRAP3), {STATE_RENDER(WINED3D_RS_WRAP0)}}, + {STATE_RENDER(WINED3D_RS_WRAP4), {STATE_RENDER(WINED3D_RS_WRAP0)}}, + {STATE_RENDER(WINED3D_RS_WRAP5), {STATE_RENDER(WINED3D_RS_WRAP0)}}, + {STATE_RENDER(WINED3D_RS_WRAP6), {STATE_RENDER(WINED3D_RS_WRAP0)}}, + {STATE_RENDER(WINED3D_RS_WRAP7), {STATE_RENDER(WINED3D_RS_WRAP0)}}, + {STATE_RENDER(WINED3D_RS_WRAP8), {STATE_RENDER(WINED3D_RS_WRAP0)}}, + {STATE_RENDER(WINED3D_RS_WRAP9), {STATE_RENDER(WINED3D_RS_WRAP0)}}, + {STATE_RENDER(WINED3D_RS_WRAP10), {STATE_RENDER(WINED3D_RS_WRAP0)}}, + {STATE_RENDER(WINED3D_RS_WRAP11), {STATE_RENDER(WINED3D_RS_WRAP0)}}, + {STATE_RENDER(WINED3D_RS_WRAP12), {STATE_RENDER(WINED3D_RS_WRAP0)}}, + {STATE_RENDER(WINED3D_RS_WRAP13), {STATE_RENDER(WINED3D_RS_WRAP0)}}, + {STATE_RENDER(WINED3D_RS_WRAP14), {STATE_RENDER(WINED3D_RS_WRAP0)}}, + {STATE_RENDER(WINED3D_RS_WRAP15), {STATE_RENDER(WINED3D_RS_WRAP0)}}, + {STATE_RENDER(WINED3D_RS_EXTENTS), {STATE_RENDER(WINED3D_RS_EXTENTS), state_nop}}, + {STATE_RENDER(WINED3D_RS_COLORKEYBLENDENABLE), {STATE_RENDER(WINED3D_RS_COLORKEYBLENDENABLE), state_nop}}, + {STATE_RENDER(WINED3D_RS_SOFTWAREVERTEXPROCESSING), {STATE_RENDER(WINED3D_RS_SOFTWAREVERTEXPROCESSING), state_nop}}, + {STATE_RENDER(WINED3D_RS_PATCHEDGESTYLE), {STATE_RENDER(WINED3D_RS_PATCHEDGESTYLE), state_nop}}, + {STATE_RENDER(WINED3D_RS_PATCHSEGMENTS), {STATE_RENDER(WINED3D_RS_PATCHSEGMENTS), state_nop}}, + {STATE_RENDER(WINED3D_RS_POSITIONDEGREE), {STATE_RENDER(WINED3D_RS_POSITIONDEGREE), state_nop}}, + {STATE_RENDER(WINED3D_RS_NORMALDEGREE), {STATE_RENDER(WINED3D_RS_NORMALDEGREE), state_nop}}, + {STATE_RENDER(WINED3D_RS_MINTESSELLATIONLEVEL), {STATE_RENDER(WINED3D_RS_ENABLEADAPTIVETESSELLATION)}}, + {STATE_RENDER(WINED3D_RS_MAXTESSELLATIONLEVEL), {STATE_RENDER(WINED3D_RS_ENABLEADAPTIVETESSELLATION)}}, + {STATE_RENDER(WINED3D_RS_ADAPTIVETESS_X), {STATE_RENDER(WINED3D_RS_ENABLEADAPTIVETESSELLATION)}}, + {STATE_RENDER(WINED3D_RS_ADAPTIVETESS_Y), {STATE_RENDER(WINED3D_RS_ENABLEADAPTIVETESSELLATION)}}, + {STATE_RENDER(WINED3D_RS_ADAPTIVETESS_Z), {STATE_RENDER(WINED3D_RS_ENABLEADAPTIVETESSELLATION)}}, + {STATE_RENDER(WINED3D_RS_ADAPTIVETESS_W), {STATE_RENDER(WINED3D_RS_ENABLEADAPTIVETESSELLATION)}}, + {STATE_RENDER(WINED3D_RS_ENABLEADAPTIVETESSELLATION), {STATE_RENDER(WINED3D_RS_ENABLEADAPTIVETESSELLATION), state_nop}}, + {STATE_RENDER(WINED3D_RS_MULTISAMPLEANTIALIAS), {STATE_RENDER(WINED3D_RS_MULTISAMPLEANTIALIAS), state_nop}}, + {STATE_RENDER(WINED3D_RS_DEBUGMONITORTOKEN), {STATE_RENDER(WINED3D_RS_DEBUGMONITORTOKEN), state_nop}}, + {STATE_RENDER(WINED3D_RS_ZVISIBLE), {STATE_RENDER(WINED3D_RS_ZVISIBLE), state_nop}}, + /* Samplers */ + {STATE_SAMPLER(0), {STATE_SAMPLER(0), state_nop}}, + {STATE_SAMPLER(1), {STATE_SAMPLER(1), state_nop}}, + {STATE_SAMPLER(2), {STATE_SAMPLER(2), state_nop}}, + {STATE_SAMPLER(3), {STATE_SAMPLER(3), state_nop}}, + {STATE_SAMPLER(4), {STATE_SAMPLER(4), state_nop}}, + {STATE_SAMPLER(5), {STATE_SAMPLER(5), state_nop}}, + {STATE_SAMPLER(6), {STATE_SAMPLER(6), state_nop}}, + {STATE_SAMPLER(7), {STATE_SAMPLER(7), state_nop}}, + {STATE_SAMPLER(8), {STATE_SAMPLER(8), state_nop}}, + {STATE_SAMPLER(9), {STATE_SAMPLER(9), state_nop}}, + {STATE_SAMPLER(10), {STATE_SAMPLER(10), state_nop}}, + {STATE_SAMPLER(11), {STATE_SAMPLER(11), state_nop}}, + {STATE_SAMPLER(12), {STATE_SAMPLER(12), state_nop}}, + {STATE_SAMPLER(13), {STATE_SAMPLER(13), state_nop}}, + {STATE_SAMPLER(14), {STATE_SAMPLER(14), state_nop}}, + {STATE_SAMPLER(15), {STATE_SAMPLER(15), state_nop}}, + {STATE_SAMPLER(16), /* Vertex sampler 0 */ {STATE_SAMPLER(16), state_nop}}, + {STATE_SAMPLER(17), /* Vertex sampler 1 */ {STATE_SAMPLER(17), state_nop}}, + {STATE_SAMPLER(18), /* Vertex sampler 2 */ {STATE_SAMPLER(18), state_nop}}, + {STATE_SAMPLER(19), /* Vertex sampler 3 */ {STATE_SAMPLER(19), state_nop}}, + {STATE_BASEVERTEXINDEX, {STATE_STREAMSRC}}, + {STATE_FRAMEBUFFER, {STATE_FRAMEBUFFER, state_nop}}, + {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), state_nop}}, + {STATE_SHADER(WINED3D_SHADER_TYPE_HULL), {STATE_SHADER(WINED3D_SHADER_TYPE_HULL), state_nop}}, + {STATE_SHADER(WINED3D_SHADER_TYPE_DOMAIN), {STATE_SHADER(WINED3D_SHADER_TYPE_DOMAIN), state_nop}}, + {STATE_SHADER(WINED3D_SHADER_TYPE_GEOMETRY), {STATE_SHADER(WINED3D_SHADER_TYPE_GEOMETRY), state_nop}}, + {STATE_SHADER(WINED3D_SHADER_TYPE_COMPUTE), {STATE_SHADER(WINED3D_SHADER_TYPE_COMPUTE), state_nop}}, + {0}, /* Terminate */ +}; + +static inline const struct wined3d_adapter_vk *wined3d_adapter_vk_const(const struct wined3d_adapter *adapter) +{ + return CONTAINING_RECORD(adapter, struct wined3d_adapter_vk, a); +} + +static const char *debug_vk_version(uint32_t version) +{ + return wine_dbg_sprintf("%u.%u.%u", + VK_VERSION_MAJOR(version), VK_VERSION_MINOR(version), VK_VERSION_PATCH(version)); +} + +static HRESULT hresult_from_vk_result(VkResult vr) +{ + switch (vr) + { + case VK_SUCCESS: + return S_OK; + case VK_ERROR_OUT_OF_HOST_MEMORY: + WARN("Out of host memory.\n"); + return E_OUTOFMEMORY; + case VK_ERROR_OUT_OF_DEVICE_MEMORY: + WARN("Out of device memory.\n"); + return E_OUTOFMEMORY; + case VK_ERROR_DEVICE_LOST: + WARN("Device lost.\n"); + return E_FAIL; + case VK_ERROR_EXTENSION_NOT_PRESENT: + WARN("Extension not present.\n"); + return E_FAIL; + default: + FIXME("Unhandled VkResult %d.\n", vr); + return E_FAIL; + } +} + +#ifdef USE_WIN32_VULKAN +static BOOL wined3d_load_vulkan(struct wined3d_vk_info *vk_info) +{ + struct vulkan_ops *vk_ops = &vk_info->vk_ops; + + if (!(vk_info->vulkan_lib = LoadLibraryA("vulkan-1.dll"))) + { + WARN("Failed to load vulkan-1.dll.\n"); + return FALSE; + } + + vk_ops->vkGetInstanceProcAddr = (void *)GetProcAddress(vk_info->vulkan_lib, "vkGetInstanceProcAddr"); + if (!vk_ops->vkGetInstanceProcAddr) + { + FreeLibrary(vk_info->vulkan_lib); + return FALSE; + } + + return TRUE; +} + +static void wined3d_unload_vulkan(struct wined3d_vk_info *vk_info) +{ + if (vk_info->vulkan_lib) + { + FreeLibrary(vk_info->vulkan_lib); + vk_info->vulkan_lib = NULL; + } +} +#else +static BOOL wined3d_load_vulkan(struct wined3d_vk_info *vk_info) +{ + struct vulkan_ops *vk_ops = &vk_info->vk_ops; + const struct vulkan_funcs *vk_funcs; + HDC dc; + + dc = GetDC(0); + vk_funcs = __wine_get_vulkan_driver(dc, WINE_VULKAN_DRIVER_VERSION); + ReleaseDC(0, dc); + + if (!vk_funcs) + return FALSE; + + vk_ops->vkGetInstanceProcAddr = (void *)vk_funcs->p_vkGetInstanceProcAddr; + return TRUE; +} + +static void wined3d_unload_vulkan(struct wined3d_vk_info *vk_info) {} +#endif + +static void adapter_vk_destroy(struct wined3d_adapter *adapter) +{ + struct wined3d_adapter_vk *adapter_vk = wined3d_adapter_vk(adapter); + struct wined3d_vk_info *vk_info = &adapter_vk->vk_info; + + VK_CALL(vkDestroyInstance(vk_info->instance, NULL)); + wined3d_unload_vulkan(vk_info); + wined3d_adapter_cleanup(&adapter_vk->a); + heap_free(adapter_vk->device_extensions); + heap_free(adapter_vk); +} + +static HRESULT wined3d_select_vulkan_queue_family(const struct wined3d_adapter_vk *adapter_vk, + uint32_t *queue_family_index, uint32_t *timestamp_bits) +{ + VkPhysicalDevice physical_device = adapter_vk->physical_device; + const struct wined3d_vk_info *vk_info = &adapter_vk->vk_info; + VkQueueFamilyProperties *queue_properties; + uint32_t count, i; + + VK_CALL(vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &count, NULL)); + + if (!(queue_properties = heap_calloc(count, sizeof(*queue_properties)))) + return E_OUTOFMEMORY; + + VK_CALL(vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &count, queue_properties)); + + for (i = 0; i < count; ++i) + { + if (queue_properties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) + { + *queue_family_index = i; + *timestamp_bits = queue_properties[i].timestampValidBits; + heap_free(queue_properties); + return WINED3D_OK; + } + } + heap_free(queue_properties); + + WARN("Failed to find graphics queue.\n"); + return E_FAIL; +} + +struct wined3d_physical_device_info +{ + VkPhysicalDeviceTransformFeedbackFeaturesEXT xfb_features; + VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT vertex_divisor_features; + + VkPhysicalDeviceFeatures2 features2; +}; + +static void wined3d_disable_vulkan_features(struct wined3d_physical_device_info *info) +{ + VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT *vertex_divisor_features = &info->vertex_divisor_features; + VkPhysicalDeviceFeatures *features = &info->features2.features; + + vertex_divisor_features->vertexAttributeInstanceRateZeroDivisor = VK_FALSE; + + features->depthBounds = VK_FALSE; + features->alphaToOne = VK_FALSE; + features->textureCompressionETC2 = VK_FALSE; + features->textureCompressionASTC_LDR = VK_FALSE; + features->shaderStorageImageMultisample = VK_FALSE; + features->shaderUniformBufferArrayDynamicIndexing = VK_FALSE; + features->shaderSampledImageArrayDynamicIndexing = VK_FALSE; + features->shaderStorageBufferArrayDynamicIndexing = VK_FALSE; + features->shaderStorageImageArrayDynamicIndexing = VK_FALSE; + features->shaderInt16 = VK_FALSE; + features->shaderResourceResidency = VK_FALSE; + features->shaderResourceMinLod = VK_FALSE; + features->sparseBinding = VK_FALSE; + features->sparseResidencyBuffer = VK_FALSE; + features->sparseResidencyImage2D = VK_FALSE; + features->sparseResidencyImage3D = VK_FALSE; + features->sparseResidency2Samples = VK_FALSE; + features->sparseResidency4Samples = VK_FALSE; + features->sparseResidency8Samples = VK_FALSE; + features->sparseResidency16Samples = VK_FALSE; + features->sparseResidencyAliased = VK_FALSE; + features->inheritedQueries = VK_FALSE; +} + +static struct wined3d_allocator_chunk *wined3d_allocator_vk_create_chunk(struct wined3d_allocator *allocator, + struct wined3d_context *context, unsigned int memory_type, size_t chunk_size) +{ + struct wined3d_context_vk *context_vk = wined3d_context_vk(context); + struct wined3d_allocator_chunk_vk *chunk_vk; + + if (!(chunk_vk = heap_alloc(sizeof(*chunk_vk)))) + return NULL; + + if (!wined3d_allocator_chunk_init(&chunk_vk->c, allocator)) + { + heap_free(chunk_vk); + return NULL; + } + + if (!(chunk_vk->vk_memory = wined3d_context_vk_allocate_vram_chunk_memory(context_vk, memory_type, chunk_size))) + { + wined3d_allocator_chunk_cleanup(&chunk_vk->c); + heap_free(chunk_vk); + return NULL; + } + list_add_head(&allocator->pools[memory_type].chunks, &chunk_vk->c.entry); + + return &chunk_vk->c; +} + +static void wined3d_allocator_vk_destroy_chunk(struct wined3d_allocator_chunk *chunk) +{ + struct wined3d_allocator_chunk_vk *chunk_vk = wined3d_allocator_chunk_vk(chunk); + const struct wined3d_vk_info *vk_info; + struct wined3d_device_vk *device_vk; + + TRACE("chunk %p.\n", chunk); + + device_vk = CONTAINING_RECORD(chunk_vk->c.allocator, struct wined3d_device_vk, allocator); + vk_info = &device_vk->vk_info; + + if (chunk_vk->c.map_ptr) + VK_CALL(vkUnmapMemory(device_vk->vk_device, chunk_vk->vk_memory)); + VK_CALL(vkFreeMemory(device_vk->vk_device, chunk_vk->vk_memory, NULL)); + TRACE("Freed memory 0x%s.\n", wine_dbgstr_longlong(chunk_vk->vk_memory)); + wined3d_allocator_chunk_cleanup(&chunk_vk->c); + heap_free(chunk_vk); +} + +static const struct wined3d_allocator_ops wined3d_allocator_vk_ops = +{ + .allocator_create_chunk = wined3d_allocator_vk_create_chunk, + .allocator_destroy_chunk = wined3d_allocator_vk_destroy_chunk, +}; + +static HRESULT adapter_vk_create_device(struct wined3d *wined3d, const struct wined3d_adapter *adapter, + enum wined3d_device_type device_type, HWND focus_window, unsigned int flags, BYTE surface_alignment, + const enum wined3d_feature_level *levels, unsigned int level_count, + struct wined3d_device_parent *device_parent, struct wined3d_device **device) +{ + const struct wined3d_adapter_vk *adapter_vk = wined3d_adapter_vk_const(adapter); + VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT *vertex_divisor_features; + const struct wined3d_vk_info *vk_info = &adapter_vk->vk_info; + VkPhysicalDeviceTransformFeedbackFeaturesEXT *xfb_features; + struct wined3d_physical_device_info physical_device_info; + static const float priorities[] = {1.0f}; + VkPhysicalDeviceFeatures2 *features2; + struct wined3d_device_vk *device_vk; + VkDevice vk_device = VK_NULL_HANDLE; + VkDeviceQueueCreateInfo queue_info; + VkPhysicalDevice physical_device; + VkDeviceCreateInfo device_info; + uint32_t queue_family_index; + uint32_t timestamp_bits; + VkResult vr; + HRESULT hr; + + if (!(device_vk = heap_alloc_zero(sizeof(*device_vk)))) + return E_OUTOFMEMORY; + + if (FAILED(hr = wined3d_select_vulkan_queue_family(adapter_vk, &queue_family_index, ×tamp_bits))) + goto fail; + + physical_device = adapter_vk->physical_device; + + memset(&physical_device_info, 0, sizeof(physical_device_info)); + + xfb_features = &physical_device_info.xfb_features; + xfb_features->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_FEATURES_EXT; + + vertex_divisor_features = &physical_device_info.vertex_divisor_features; + vertex_divisor_features->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_FEATURES_EXT; + vertex_divisor_features->pNext = xfb_features; + + features2 = &physical_device_info.features2; + features2->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; + features2->pNext = vertex_divisor_features; + + if (vk_info->vk_ops.vkGetPhysicalDeviceFeatures2) + VK_CALL(vkGetPhysicalDeviceFeatures2(physical_device, features2)); + else + VK_CALL(vkGetPhysicalDeviceFeatures(physical_device, &features2->features)); + + if (!vertex_divisor_features->vertexAttributeInstanceRateDivisor) + { + WARN("Vertex attribute divisors not supported.\n"); + hr = E_FAIL; + goto fail; + } + + wined3d_disable_vulkan_features(&physical_device_info); + + queue_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queue_info.pNext = NULL; + queue_info.flags = 0; + queue_info.queueFamilyIndex = queue_family_index; + queue_info.queueCount = ARRAY_SIZE(priorities); + queue_info.pQueuePriorities = priorities; + + device_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + device_info.pNext = features2->pNext; + device_info.flags = 0; + device_info.queueCreateInfoCount = 1; + device_info.pQueueCreateInfos = &queue_info; + device_info.enabledLayerCount = 0; + device_info.ppEnabledLayerNames = NULL; + device_info.enabledExtensionCount = adapter_vk->device_extension_count; + device_info.ppEnabledExtensionNames = adapter_vk->device_extensions; + device_info.pEnabledFeatures = &features2->features; + + if ((vr = VK_CALL(vkCreateDevice(physical_device, &device_info, NULL, &vk_device))) < 0) + { + WARN("Failed to create Vulkan device, vr %s.\n", wined3d_debug_vkresult(vr)); + vk_device = VK_NULL_HANDLE; + hr = hresult_from_vk_result(vr); + goto fail; + } + + device_vk->vk_device = vk_device; + VK_CALL(vkGetDeviceQueue(vk_device, queue_family_index, 0, &device_vk->vk_queue)); + device_vk->vk_queue_family_index = queue_family_index; + device_vk->timestamp_bits = timestamp_bits; + + device_vk->vk_info = *vk_info; +#define VK_DEVICE_PFN(name) \ + if (!(device_vk->vk_info.vk_ops.name = (void *)VK_CALL(vkGetDeviceProcAddr(vk_device, #name)))) \ + { \ + WARN("Could not get device proc addr for '" #name "'.\n"); \ + hr = E_FAIL; \ + goto fail; \ + } +#define VK_DEVICE_EXT_PFN(name) \ + device_vk->vk_info.vk_ops.name = (void *)VK_CALL(vkGetDeviceProcAddr(vk_device, #name)); + VK_DEVICE_FUNCS() +#undef VK_DEVICE_EXT_PFN +#undef VK_DEVICE_PFN + + if (!wined3d_allocator_init(&device_vk->allocator, + adapter_vk->memory_properties.memoryTypeCount, &wined3d_allocator_vk_ops)) + { + WARN("Failed to initialise allocator.\n"); + hr = E_FAIL; + goto fail; + } + + if (FAILED(hr = wined3d_device_init(&device_vk->d, wined3d, adapter->ordinal, device_type, focus_window, + flags, surface_alignment, levels, level_count, vk_info->supported, device_parent))) + { + WARN("Failed to initialize device, hr %#x.\n", hr); + wined3d_allocator_cleanup(&device_vk->allocator); + goto fail; + } + + *device = &device_vk->d; + + return WINED3D_OK; + +fail: + VK_CALL(vkDestroyDevice(vk_device, NULL)); + heap_free(device_vk); + return hr; +} + +static void adapter_vk_destroy_device(struct wined3d_device *device) +{ + struct wined3d_device_vk *device_vk = wined3d_device_vk(device); + const struct wined3d_vk_info *vk_info = &device_vk->vk_info; + + wined3d_device_cleanup(&device_vk->d); + wined3d_allocator_cleanup(&device_vk->allocator); + VK_CALL(vkDestroyDevice(device_vk->vk_device, NULL)); + heap_free(device_vk); +} + +struct wined3d_context *adapter_vk_acquire_context(struct wined3d_device *device, + struct wined3d_texture *texture, unsigned int sub_resource_idx) +{ + TRACE("device %p, texture %p, sub_resource_idx %u.\n", device, texture, sub_resource_idx); + + wined3d_from_cs(device->cs); + + if (!device->context_count) + return NULL; + + return &wined3d_device_vk(device)->context_vk.c; +} + +void adapter_vk_release_context(struct wined3d_context *context) +{ + TRACE("context %p.\n", context); +} + +static void adapter_vk_get_wined3d_caps(const struct wined3d_adapter *adapter, struct wined3d_caps *caps) +{ + const struct wined3d_adapter_vk *adapter_vk = wined3d_adapter_vk_const(adapter); + const VkPhysicalDeviceLimits *limits = &adapter_vk->device_limits; + bool sampler_anisotropy = limits->maxSamplerAnisotropy > 1.0f; + const struct wined3d_vk_info *vk_info = &adapter_vk->vk_info; + + caps->ddraw_caps.dds_caps |= WINEDDSCAPS_BACKBUFFER + | WINEDDSCAPS_COMPLEX + | WINEDDSCAPS_FRONTBUFFER + | WINEDDSCAPS_3DDEVICE + | WINEDDSCAPS_VIDEOMEMORY + | WINEDDSCAPS_OWNDC + | WINEDDSCAPS_LOCALVIDMEM + | WINEDDSCAPS_NONLOCALVIDMEM; + caps->ddraw_caps.caps |= WINEDDCAPS_3D; + + caps->Caps2 |= WINED3DCAPS2_CANGENMIPMAP; + + caps->PrimitiveMiscCaps |= WINED3DPMISCCAPS_BLENDOP + | WINED3DPMISCCAPS_INDEPENDENTWRITEMASKS + | WINED3DPMISCCAPS_MRTINDEPENDENTBITDEPTHS + | WINED3DPMISCCAPS_POSTBLENDSRGBCONVERT + | WINED3DPMISCCAPS_SEPARATEALPHABLEND; + + caps->RasterCaps |= WINED3DPRASTERCAPS_MIPMAPLODBIAS; + + if (sampler_anisotropy) + { + caps->RasterCaps |= WINED3DPRASTERCAPS_ANISOTROPY; + + caps->TextureFilterCaps |= WINED3DPTFILTERCAPS_MAGFANISOTROPIC + | WINED3DPTFILTERCAPS_MINFANISOTROPIC; + + caps->MaxAnisotropy = limits->maxSamplerAnisotropy; + } + + caps->SrcBlendCaps |= WINED3DPBLENDCAPS_BLENDFACTOR; + caps->DestBlendCaps |= WINED3DPBLENDCAPS_BLENDFACTOR + | WINED3DPBLENDCAPS_SRCALPHASAT; + + caps->TextureCaps |= WINED3DPTEXTURECAPS_VOLUMEMAP + | WINED3DPTEXTURECAPS_MIPVOLUMEMAP + | WINED3DPTEXTURECAPS_VOLUMEMAP_POW2; + caps->VolumeTextureFilterCaps |= WINED3DPTFILTERCAPS_MAGFLINEAR + | WINED3DPTFILTERCAPS_MAGFPOINT + | WINED3DPTFILTERCAPS_MINFLINEAR + | WINED3DPTFILTERCAPS_MINFPOINT + | WINED3DPTFILTERCAPS_MIPFLINEAR + | WINED3DPTFILTERCAPS_MIPFPOINT + | WINED3DPTFILTERCAPS_LINEAR + | WINED3DPTFILTERCAPS_LINEARMIPLINEAR + | WINED3DPTFILTERCAPS_LINEARMIPNEAREST + | WINED3DPTFILTERCAPS_MIPLINEAR + | WINED3DPTFILTERCAPS_MIPNEAREST + | WINED3DPTFILTERCAPS_NEAREST; + caps->VolumeTextureAddressCaps |= WINED3DPTADDRESSCAPS_INDEPENDENTUV + | WINED3DPTADDRESSCAPS_CLAMP + | WINED3DPTADDRESSCAPS_WRAP; + caps->VolumeTextureAddressCaps |= WINED3DPTADDRESSCAPS_BORDER + | WINED3DPTADDRESSCAPS_MIRROR; + if (vk_info->supported[WINED3D_VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE]) + caps->VolumeTextureAddressCaps |= WINED3DPTADDRESSCAPS_MIRRORONCE; + + caps->MaxVolumeExtent = limits->maxImageDimension3D; + + caps->TextureCaps |= WINED3DPTEXTURECAPS_CUBEMAP + | WINED3DPTEXTURECAPS_MIPCUBEMAP + | WINED3DPTEXTURECAPS_CUBEMAP_POW2; + caps->CubeTextureFilterCaps |= WINED3DPTFILTERCAPS_MAGFLINEAR + | WINED3DPTFILTERCAPS_MAGFPOINT + | WINED3DPTFILTERCAPS_MINFLINEAR + | WINED3DPTFILTERCAPS_MINFPOINT + | WINED3DPTFILTERCAPS_MIPFLINEAR + | WINED3DPTFILTERCAPS_MIPFPOINT + | WINED3DPTFILTERCAPS_LINEAR + | WINED3DPTFILTERCAPS_LINEARMIPLINEAR + | WINED3DPTFILTERCAPS_LINEARMIPNEAREST + | WINED3DPTFILTERCAPS_MIPLINEAR + | WINED3DPTFILTERCAPS_MIPNEAREST + | WINED3DPTFILTERCAPS_NEAREST; + + if (sampler_anisotropy) + { + caps->CubeTextureFilterCaps |= WINED3DPTFILTERCAPS_MAGFANISOTROPIC + | WINED3DPTFILTERCAPS_MINFANISOTROPIC; + } + + caps->TextureAddressCaps |= WINED3DPTADDRESSCAPS_BORDER + | WINED3DPTADDRESSCAPS_MIRROR; + if (vk_info->supported[WINED3D_VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE]) + caps->TextureAddressCaps |= WINED3DPTADDRESSCAPS_MIRRORONCE; + + caps->StencilCaps |= WINED3DSTENCILCAPS_DECR + | WINED3DSTENCILCAPS_INCR + | WINED3DSTENCILCAPS_TWOSIDED; + + caps->DeclTypes |= WINED3DDTCAPS_FLOAT16_2 | WINED3DDTCAPS_FLOAT16_4; + + caps->MaxPixelShader30InstructionSlots = WINED3DMAX30SHADERINSTRUCTIONS; + caps->MaxVertexShader30InstructionSlots = WINED3DMAX30SHADERINSTRUCTIONS; + caps->PS20Caps.temp_count = WINED3DPS20_MAX_NUMTEMPS; + caps->VS20Caps.temp_count = WINED3DVS20_MAX_NUMTEMPS; +} + +static BOOL adapter_vk_check_format(const struct wined3d_adapter *adapter, + const struct wined3d_format *adapter_format, const struct wined3d_format *rt_format, + const struct wined3d_format *ds_format) +{ + return TRUE; +} + +static HRESULT adapter_vk_init_3d(struct wined3d_device *device) +{ + struct wined3d_context_vk *context_vk; + struct wined3d_device_vk *device_vk; + HRESULT hr; + + TRACE("device %p.\n", device); + + device_vk = wined3d_device_vk(device); + context_vk = &device_vk->context_vk; + if (FAILED(hr = wined3d_context_vk_init(context_vk, device->swapchains[0]))) + { + WARN("Failed to initialise context.\n"); + return hr; + } + + if (FAILED(hr = device->shader_backend->shader_alloc_private(device, + device->adapter->vertex_pipe, device->adapter->fragment_pipe))) + { + ERR("Failed to allocate shader private data, hr %#x.\n", hr); + wined3d_context_vk_cleanup(context_vk); + return hr; + } + + if (!device_context_add(device, &context_vk->c)) + { + ERR("Failed to add the newly created context to the context list.\n"); + device->shader_backend->shader_free_private(device, NULL); + wined3d_context_vk_cleanup(context_vk); + return E_FAIL; + } + + TRACE("Initialised context %p.\n", context_vk); + + if (!(device_vk->d.blitter = wined3d_cpu_blitter_create())) + { + ERR("Failed to create CPU blitter.\n"); + device_context_remove(device, &context_vk->c); + device->shader_backend->shader_free_private(device, NULL); + wined3d_context_vk_cleanup(context_vk); + return E_FAIL; + } + wined3d_vk_blitter_create(&device_vk->d.blitter); + + wined3d_device_create_default_samplers(device, &context_vk->c); + wined3d_device_vk_create_null_resources(device_vk, context_vk); + wined3d_device_vk_create_null_views(device_vk, context_vk); + + return WINED3D_OK; +} + +static void adapter_vk_uninit_3d_cs(void *object) +{ + struct wined3d_device_vk *device_vk = object; + struct wined3d_context_vk *context_vk; + struct wined3d_device *device; + struct wined3d_shader *shader; + + TRACE("device_vk %p.\n", device_vk); + + context_vk = &device_vk->context_vk; + device = &device_vk->d; + + LIST_FOR_EACH_ENTRY(shader, &device->shaders, struct wined3d_shader, shader_list_entry) + { + device->shader_backend->shader_destroy(shader); + } + + device->blitter->ops->blitter_destroy(device->blitter, NULL); + device->shader_backend->shader_free_private(device, &context_vk->c); + wined3d_device_vk_destroy_null_views(device_vk, context_vk); + wined3d_device_vk_destroy_null_resources(device_vk, context_vk); + wined3d_device_destroy_default_samplers(device, &context_vk->c); +} + +static void adapter_vk_uninit_3d(struct wined3d_device *device) +{ + struct wined3d_context_vk *context_vk; + struct wined3d_device_vk *device_vk; + + TRACE("device %p.\n", device); + + device_vk = wined3d_device_vk(device); + context_vk = &device_vk->context_vk; + + wined3d_cs_destroy_object(device->cs, adapter_vk_uninit_3d_cs, device_vk); + wined3d_cs_finish(device->cs, WINED3D_CS_QUEUE_DEFAULT); + + device_context_remove(device, &context_vk->c); + wined3d_context_vk_cleanup(context_vk); +} + +static void *wined3d_bo_vk_map(struct wined3d_bo_vk *bo, struct wined3d_context_vk *context_vk) +{ + const struct wined3d_vk_info *vk_info; + struct wined3d_device_vk *device_vk; + struct wined3d_bo_slab_vk *slab; + VkResult vr; + + if (bo->map_ptr) + return bo->map_ptr; + + vk_info = context_vk->vk_info; + device_vk = wined3d_device_vk(context_vk->c.device); + + if ((slab = bo->slab)) + { + if (!(bo->map_ptr = wined3d_bo_slab_vk_map(slab, context_vk))) + { + ERR("Failed to map slab.\n"); + return NULL; + } + } + else if (bo->memory) + { + struct wined3d_allocator_chunk_vk *chunk_vk = wined3d_allocator_chunk_vk(bo->memory->chunk); + + if (!(bo->map_ptr = wined3d_allocator_chunk_vk_map(chunk_vk, context_vk))) + { + ERR("Failed to map chunk.\n"); + return NULL; + } + } + else if ((vr = VK_CALL(vkMapMemory(device_vk->vk_device, bo->vk_memory, 0, VK_WHOLE_SIZE, 0, &bo->map_ptr))) < 0) + { + ERR("Failed to map memory, vr %s.\n", wined3d_debug_vkresult(vr)); + return NULL; + } + + return bo->map_ptr; +} + +static void wined3d_bo_vk_unmap(struct wined3d_bo_vk *bo, struct wined3d_context_vk *context_vk) +{ + const struct wined3d_vk_info *vk_info; + struct wined3d_device_vk *device_vk; + struct wined3d_bo_slab_vk *slab; + + if (wined3d_map_persistent()) + return; + + bo->map_ptr = NULL; + + if ((slab = bo->slab)) + { + wined3d_bo_slab_vk_unmap(slab, context_vk); + return; + } + + if (bo->memory) + { + wined3d_allocator_chunk_vk_unmap(wined3d_allocator_chunk_vk(bo->memory->chunk), context_vk); + return; + } + + vk_info = context_vk->vk_info; + device_vk = wined3d_device_vk(context_vk->c.device); + VK_CALL(vkUnmapMemory(device_vk->vk_device, bo->vk_memory)); +} + +void *wined3d_bo_slab_vk_map(struct wined3d_bo_slab_vk *slab_vk, struct wined3d_context_vk *context_vk) +{ + TRACE("slab_vk %p, context_vk %p.\n", slab_vk, context_vk); + + if (!slab_vk->map_ptr && !(slab_vk->map_ptr = wined3d_bo_vk_map(&slab_vk->bo, context_vk))) + { + ERR("Failed to map slab.\n"); + return NULL; + } + + ++slab_vk->map_count; + + return slab_vk->map_ptr; +} + +void wined3d_bo_slab_vk_unmap(struct wined3d_bo_slab_vk *slab_vk, struct wined3d_context_vk *context_vk) +{ + if (--slab_vk->map_count) + return; + + wined3d_bo_vk_unmap(&slab_vk->bo, context_vk); + slab_vk->map_ptr = NULL; +} + +static VkAccessFlags vk_access_mask_from_buffer_usage(VkBufferUsageFlags usage) +{ + VkAccessFlags flags = 0; + + if (usage & VK_BUFFER_USAGE_VERTEX_BUFFER_BIT) + flags |= VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT; + if (usage & VK_BUFFER_USAGE_INDEX_BUFFER_BIT) + flags |= VK_ACCESS_INDEX_READ_BIT; + if (usage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) + flags |= VK_ACCESS_UNIFORM_READ_BIT; + if (usage & VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT) + flags |= VK_ACCESS_SHADER_READ_BIT; + if (usage & VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT) + flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; + if (usage & VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT) + flags |= VK_ACCESS_INDIRECT_COMMAND_READ_BIT; + if (usage & VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT) + flags |= VK_ACCESS_TRANSFORM_FEEDBACK_WRITE_BIT_EXT; + if (usage & VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_COUNTER_BUFFER_BIT_EXT) + flags |= VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_READ_BIT_EXT + | VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT; + + return flags; +} + +static void *adapter_vk_map_bo_address(struct wined3d_context *context, + const struct wined3d_bo_address *data, size_t size, uint32_t map_flags) +{ + struct wined3d_context_vk *context_vk = wined3d_context_vk(context); + const struct wined3d_vk_info *vk_info; + struct wined3d_device_vk *device_vk; + VkCommandBuffer vk_command_buffer; + VkBufferMemoryBarrier vk_barrier; + struct wined3d_bo_user *bo_user; + struct wined3d_bo_vk *bo, tmp; + VkMappedMemoryRange range; + void *map_ptr; + + if (!(bo = (struct wined3d_bo_vk *)data->buffer_object)) + return data->addr; + + vk_info = context_vk->vk_info; + device_vk = wined3d_device_vk(context->device); + + if (map_flags & WINED3D_MAP_NOOVERWRITE) + goto map; + + if ((map_flags & WINED3D_MAP_DISCARD) && bo->command_buffer_id > context_vk->completed_command_buffer_id) + { + if (wined3d_context_vk_create_bo(context_vk, bo->size, bo->usage, bo->memory_type, &tmp)) + { + list_move_head(&tmp.users, &bo->users); + wined3d_context_vk_destroy_bo(context_vk, bo); + *bo = tmp; + list_init(&bo->users); + list_move_head(&bo->users, &tmp.users); + LIST_FOR_EACH_ENTRY(bo_user, &bo->users, struct wined3d_bo_user, entry) + { + bo_user->valid = false; + } + + goto map; + } + + ERR("Failed to create new buffer object.\n"); + } + + if (map_flags & WINED3D_MAP_READ) + { + if (!(vk_command_buffer = wined3d_context_vk_get_command_buffer(context_vk))) + { + ERR("Failed to get command buffer.\n"); + return NULL; + } + + wined3d_context_vk_end_current_render_pass(context_vk); + + vk_barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; + vk_barrier.pNext = NULL; + vk_barrier.srcAccessMask = vk_access_mask_from_buffer_usage(bo->usage); + vk_barrier.dstAccessMask = VK_ACCESS_HOST_READ_BIT; + vk_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + vk_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + vk_barrier.buffer = bo->vk_buffer; + vk_barrier.offset = bo->buffer_offset + (uintptr_t)data->addr; + vk_barrier.size = size; + VK_CALL(vkCmdPipelineBarrier(vk_command_buffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_HOST_BIT, 0, 0, NULL, 1, &vk_barrier, 0, NULL)); + + if (!(bo->memory_type & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) + { + range.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; + range.pNext = NULL; + range.memory = bo->vk_memory; + range.offset = bo->memory_offset + (uintptr_t)data->addr; + range.size = size; + VK_CALL(vkInvalidateMappedMemoryRanges(device_vk->vk_device, 1, &range)); + } + + wined3d_context_vk_reference_bo(context_vk, bo); + } + + if (bo->command_buffer_id == context_vk->current_command_buffer.id) + wined3d_context_vk_submit_command_buffer(context_vk, 0, NULL, NULL, 0, NULL); + wined3d_context_vk_wait_command_buffer(context_vk, bo->command_buffer_id); + +map: + if (!(map_ptr = wined3d_bo_vk_map(bo, context_vk))) + { + ERR("Failed to map bo.\n"); + return NULL; + } + + return (uint8_t *)map_ptr + bo->memory_offset + (uintptr_t)data->addr; +} + +static void adapter_vk_unmap_bo_address(struct wined3d_context *context, + const struct wined3d_bo_address *data, unsigned int range_count, const struct wined3d_range *ranges) +{ + struct wined3d_context_vk *context_vk = wined3d_context_vk(context); + const struct wined3d_vk_info *vk_info; + struct wined3d_device_vk *device_vk; + VkMappedMemoryRange range; + struct wined3d_bo_vk *bo; + unsigned int i; + + if (!(bo = (struct wined3d_bo_vk *)data->buffer_object)) + return; + + vk_info = context_vk->vk_info; + device_vk = wined3d_device_vk(context->device); + + if (!(bo->memory_type & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) + { + range.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; + range.pNext = NULL; + range.memory = bo->vk_memory; + + for (i = 0; i < range_count; ++i) + { + range.offset = bo->memory_offset + ranges[i].offset; + range.size = ranges[i].size; + VK_CALL(vkFlushMappedMemoryRanges(device_vk->vk_device, 1, &range)); + } + } + + wined3d_bo_vk_unmap(bo, context_vk); +} + +static void adapter_vk_copy_bo_address(struct wined3d_context *context, + const struct wined3d_bo_address *dst, const struct wined3d_bo_address *src, size_t size) +{ + struct wined3d_context_vk *context_vk = wined3d_context_vk(context); + const struct wined3d_vk_info *vk_info = context_vk->vk_info; + struct wined3d_bo_vk staging_bo, *src_bo, *dst_bo; + VkAccessFlags src_access_mask, dst_access_mask; + VkBufferMemoryBarrier vk_barrier[2]; + struct wined3d_bo_address staging; + VkCommandBuffer vk_command_buffer; + struct wined3d_range range; + void *dst_ptr, *src_ptr; + VkBufferCopy region; + + src_bo = (struct wined3d_bo_vk *)src->buffer_object; + dst_bo = (struct wined3d_bo_vk *)dst->buffer_object; + + if (src_bo && dst_bo) + { + if (!(vk_command_buffer = wined3d_context_vk_get_command_buffer(context_vk))) + { + ERR("Failed to get command buffer.\n"); + return; + } + + wined3d_context_vk_end_current_render_pass(context_vk); + + src_access_mask = vk_access_mask_from_buffer_usage(src_bo->usage); + dst_access_mask = vk_access_mask_from_buffer_usage(dst_bo->usage); + + region.srcOffset = src_bo->buffer_offset + (uintptr_t)src->addr; + region.dstOffset = dst_bo->buffer_offset + (uintptr_t)dst->addr; + region.size = size; + + vk_barrier[0].sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; + vk_barrier[0].pNext = NULL; + vk_barrier[0].srcAccessMask = src_access_mask; + vk_barrier[0].dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + vk_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + vk_barrier[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + vk_barrier[0].buffer = src_bo->vk_buffer; + vk_barrier[0].offset = region.srcOffset; + vk_barrier[0].size = region.size; + + vk_barrier[1].sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; + vk_barrier[1].pNext = NULL; + vk_barrier[1].srcAccessMask = dst_access_mask; + vk_barrier[1].dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + vk_barrier[1].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + vk_barrier[1].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + vk_barrier[1].buffer = dst_bo->vk_buffer; + vk_barrier[1].offset = region.dstOffset; + vk_barrier[1].size = region.size; + + VK_CALL(vkCmdPipelineBarrier(vk_command_buffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, NULL, 2, vk_barrier, 0, NULL)); + + VK_CALL(vkCmdCopyBuffer(vk_command_buffer, src_bo->vk_buffer, dst_bo->vk_buffer, 1, ®ion)); + + vk_barrier[0].srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + vk_barrier[0].dstAccessMask = src_access_mask; + + vk_barrier[1].srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + vk_barrier[1].dstAccessMask = dst_access_mask; + + VK_CALL(vkCmdPipelineBarrier(vk_command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, NULL, 2, vk_barrier, 0, NULL)); + + wined3d_context_vk_reference_bo(context_vk, src_bo); + wined3d_context_vk_reference_bo(context_vk, dst_bo); + + return; + } + + if (src_bo && !(src_bo->memory_type & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)) + { + if (!(wined3d_context_vk_create_bo(context_vk, size, VK_BUFFER_USAGE_TRANSFER_DST_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &staging_bo))) + { + ERR("Failed to create staging bo.\n"); + return; + } + + staging.buffer_object = (uintptr_t)&staging_bo; + staging.addr = NULL; + adapter_vk_copy_bo_address(context, &staging, src, size); + adapter_vk_copy_bo_address(context, dst, &staging, size); + + wined3d_context_vk_destroy_bo(context_vk, &staging_bo); + + return; + } + + if (dst_bo && (dst_bo->command_buffer_id > context_vk->completed_command_buffer_id + || !(dst_bo->memory_type & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT))) + { + if (!(wined3d_context_vk_create_bo(context_vk, size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &staging_bo))) + { + ERR("Failed to create staging bo.\n"); + return; + } + + staging.buffer_object = (uintptr_t)&staging_bo; + staging.addr = NULL; + adapter_vk_copy_bo_address(context, &staging, src, size); + adapter_vk_copy_bo_address(context, dst, &staging, size); + + wined3d_context_vk_destroy_bo(context_vk, &staging_bo); + + return; + } + + src_ptr = adapter_vk_map_bo_address(context, src, size, WINED3D_MAP_READ); + dst_ptr = adapter_vk_map_bo_address(context, dst, size, WINED3D_MAP_WRITE); + + memcpy(dst_ptr, src_ptr, size); + + range.offset = 0; + range.size = size; + adapter_vk_unmap_bo_address(context, dst, 1, &range); + adapter_vk_unmap_bo_address(context, src, 0, NULL); +} + +static HRESULT adapter_vk_create_swapchain(struct wined3d_device *device, + struct wined3d_swapchain_desc *desc, struct wined3d_swapchain_state_parent *state_parent, + void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_swapchain **swapchain) +{ + struct wined3d_swapchain_vk *swapchain_vk; + HRESULT hr; + + TRACE("device %p, desc %p, state_parent %p, parent %p, parent_ops %p, swapchain %p.\n", + device, desc, state_parent, parent, parent_ops, swapchain); + + if (!(swapchain_vk = heap_alloc_zero(sizeof(*swapchain_vk)))) + return E_OUTOFMEMORY; + + if (FAILED(hr = wined3d_swapchain_vk_init(swapchain_vk, device, desc, state_parent, parent, + parent_ops))) + { + WARN("Failed to initialise swapchain, hr %#x.\n", hr); + heap_free(swapchain_vk); + return hr; + } + + TRACE("Created swapchain %p.\n", swapchain_vk); + *swapchain = &swapchain_vk->s; + + return hr; +} + +static void adapter_vk_destroy_swapchain(struct wined3d_swapchain *swapchain) +{ + struct wined3d_swapchain_vk *swapchain_vk = wined3d_swapchain_vk(swapchain); + + wined3d_swapchain_vk_cleanup(swapchain_vk); + heap_free(swapchain_vk); +} + +unsigned int wined3d_adapter_vk_get_memory_type_index(const struct wined3d_adapter_vk *adapter_vk, + uint32_t memory_type_mask, VkMemoryPropertyFlags flags) +{ + const VkPhysicalDeviceMemoryProperties *memory_info = &adapter_vk->memory_properties; + unsigned int i; + + for (i = 0; i < memory_info->memoryTypeCount; ++i) + { + if (!(memory_type_mask & (1u << i))) + continue; + if ((memory_info->memoryTypes[i].propertyFlags & flags) == flags) + return i; + } + + return ~0u; +} + +static HRESULT adapter_vk_create_buffer(struct wined3d_device *device, + const struct wined3d_buffer_desc *desc, const struct wined3d_sub_resource_data *data, + void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_buffer **buffer) +{ + struct wined3d_buffer_vk *buffer_vk; + HRESULT hr; + + TRACE("device %p, desc %p, data %p, parent %p, parent_ops %p, buffer %p.\n", + device, desc, data, parent, parent_ops, buffer); + + if (!(buffer_vk = heap_alloc_zero(sizeof(*buffer_vk)))) + return E_OUTOFMEMORY; + + if (FAILED(hr = wined3d_buffer_vk_init(buffer_vk, device, desc, data, parent, parent_ops))) + { + WARN("Failed to initialise buffer, hr %#x.\n", hr); + heap_free(buffer_vk); + return hr; + } + + TRACE("Created buffer %p.\n", buffer_vk); + *buffer = &buffer_vk->b; + + return hr; +} + +static void adapter_vk_destroy_buffer(struct wined3d_buffer *buffer) +{ + struct wined3d_buffer_vk *buffer_vk = wined3d_buffer_vk(buffer); + struct wined3d_device *device = buffer_vk->b.resource.device; + unsigned int swapchain_count = device->swapchain_count; + + TRACE("buffer_vk %p.\n", buffer_vk); + + /* Take a reference to the device, in case releasing the buffer would + * cause the device to be destroyed. However, swapchain resources don't + * take a reference to the device, and we wouldn't want to increment the + * refcount on a device that's in the process of being destroyed. */ + if (swapchain_count) + wined3d_device_incref(device); + wined3d_buffer_cleanup(&buffer_vk->b); + wined3d_cs_destroy_object(device->cs, heap_free, buffer_vk); + if (swapchain_count) + wined3d_device_decref(device); +} + +static HRESULT adapter_vk_create_texture(struct wined3d_device *device, + const struct wined3d_resource_desc *desc, unsigned int layer_count, unsigned int level_count, + uint32_t flags, void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_texture **texture) +{ + struct wined3d_texture_vk *texture_vk; + HRESULT hr; + + TRACE("device %p, desc %p, layer_count %u, level_count %u, flags %#x, parent %p, parent_ops %p, texture %p.\n", + device, desc, layer_count, level_count, flags, parent, parent_ops, texture); + + if (!(texture_vk = wined3d_texture_allocate_object_memory(sizeof(*texture_vk), level_count, layer_count))) + return E_OUTOFMEMORY; + + if (FAILED(hr = wined3d_texture_vk_init(texture_vk, device, desc, + layer_count, level_count, flags, parent, parent_ops))) + { + WARN("Failed to initialise texture, hr %#x.\n", hr); + heap_free(texture_vk); + return hr; + } + + TRACE("Created texture %p.\n", texture_vk); + *texture = &texture_vk->t; + + return hr; +} + +static void adapter_vk_destroy_texture(struct wined3d_texture *texture) +{ + struct wined3d_texture_vk *texture_vk = wined3d_texture_vk(texture); + struct wined3d_device *device = texture_vk->t.resource.device; + unsigned int swapchain_count = device->swapchain_count; + + TRACE("texture_vk %p.\n", texture_vk); + + /* Take a reference to the device, in case releasing the texture would + * cause the device to be destroyed. However, swapchain resources don't + * take a reference to the device, and we wouldn't want to increment the + * refcount on a device that's in the process of being destroyed. */ + if (swapchain_count) + wined3d_device_incref(device); + + wined3d_texture_sub_resources_destroyed(texture); + texture->resource.parent_ops->wined3d_object_destroyed(texture->resource.parent); + + wined3d_texture_cleanup(&texture_vk->t); + wined3d_cs_destroy_object(device->cs, heap_free, texture_vk); + + if (swapchain_count) + wined3d_device_decref(device); +} + +static HRESULT adapter_vk_create_rendertarget_view(const struct wined3d_view_desc *desc, + struct wined3d_resource *resource, void *parent, const struct wined3d_parent_ops *parent_ops, + struct wined3d_rendertarget_view **view) +{ + struct wined3d_rendertarget_view_vk *view_vk; + HRESULT hr; + + TRACE("desc %s, resource %p, parent %p, parent_ops %p, view %p.\n", + wined3d_debug_view_desc(desc, resource), resource, parent, parent_ops, view); + + if (!(view_vk = heap_alloc_zero(sizeof(*view_vk)))) + return E_OUTOFMEMORY; + + if (FAILED(hr = wined3d_rendertarget_view_vk_init(view_vk, desc, resource, parent, parent_ops))) + { + WARN("Failed to initialise view, hr %#x.\n", hr); + heap_free(view_vk); + return hr; + } + + TRACE("Created render target view %p.\n", view_vk); + *view = &view_vk->v; + + return hr; +} + +struct wined3d_view_vk_destroy_ctx +{ + struct wined3d_device_vk *device_vk; + VkBufferView *vk_buffer_view; + VkImageView *vk_image_view; + struct wined3d_bo_vk *vk_counter_bo; + VkBufferView *vk_counter_view; + uint64_t *command_buffer_id; + void *object; + struct wined3d_view_vk_destroy_ctx *free; +}; + +static void wined3d_view_vk_destroy_object(void *object) +{ + struct wined3d_view_vk_destroy_ctx *ctx = object; + const struct wined3d_vk_info *vk_info; + struct wined3d_device_vk *device_vk; + struct wined3d_context *context; + + TRACE("ctx %p.\n", ctx); + + device_vk = ctx->device_vk; + vk_info = &wined3d_adapter_vk(device_vk->d.adapter)->vk_info; + context = context_acquire(&device_vk->d, NULL, 0); + + if (ctx->vk_buffer_view) + { + if (context) + { + wined3d_context_vk_destroy_buffer_view(wined3d_context_vk(context), + *ctx->vk_buffer_view, *ctx->command_buffer_id); + } + else + { + VK_CALL(vkDestroyBufferView(device_vk->vk_device, *ctx->vk_buffer_view, NULL)); + TRACE("Destroyed buffer view 0x%s.\n", wine_dbgstr_longlong(*ctx->vk_buffer_view)); + } + } + if (ctx->vk_image_view) + { + if (context) + { + wined3d_context_vk_destroy_image_view(wined3d_context_vk(context), + *ctx->vk_image_view, *ctx->command_buffer_id); + } + else + { + VK_CALL(vkDestroyImageView(device_vk->vk_device, *ctx->vk_image_view, NULL)); + TRACE("Destroyed image view 0x%s.\n", wine_dbgstr_longlong(*ctx->vk_image_view)); + } + } + if (ctx->vk_counter_bo && ctx->vk_counter_bo->vk_buffer) + wined3d_context_vk_destroy_bo(wined3d_context_vk(context), ctx->vk_counter_bo); + if (ctx->vk_counter_view) + { + if (context) + { + wined3d_context_vk_destroy_buffer_view(wined3d_context_vk(context), + *ctx->vk_counter_view, *ctx->command_buffer_id); + } + else + { + VK_CALL(vkDestroyBufferView(device_vk->vk_device, *ctx->vk_counter_view, NULL)); + TRACE("Destroyed counter buffer view 0x%s.\n", wine_dbgstr_longlong(*ctx->vk_counter_view)); + } + } + + if (context) + context_release(context); + + heap_free(ctx->object); + heap_free(ctx->free); +} + +static void wined3d_view_vk_destroy(struct wined3d_device *device, VkBufferView *vk_buffer_view, + VkImageView *vk_image_view, struct wined3d_bo_vk *vk_counter_bo, + VkBufferView *vk_counter_view, uint64_t *command_buffer_id, void *view_vk) +{ + struct wined3d_view_vk_destroy_ctx *ctx, c; + + if (!(ctx = heap_alloc(sizeof(*ctx)))) + ctx = &c; + ctx->device_vk = wined3d_device_vk(device); + ctx->vk_buffer_view = vk_buffer_view; + ctx->vk_image_view = vk_image_view; + ctx->vk_counter_bo = vk_counter_bo; + ctx->vk_counter_view = vk_counter_view; + ctx->command_buffer_id = command_buffer_id; + ctx->object = view_vk; + ctx->free = ctx != &c ? ctx : NULL; + + wined3d_cs_destroy_object(device->cs, wined3d_view_vk_destroy_object, ctx); + if (ctx == &c) + device->cs->ops->finish(device->cs, WINED3D_CS_QUEUE_DEFAULT); +} + +static void adapter_vk_destroy_rendertarget_view(struct wined3d_rendertarget_view *view) +{ + struct wined3d_rendertarget_view_vk *view_vk = wined3d_rendertarget_view_vk(view); + struct wined3d_device *device = view_vk->v.resource->device; + unsigned int swapchain_count = device->swapchain_count; + + TRACE("view_vk %p.\n", view_vk); + + /* Take a reference to the device, in case releasing the view's resource + * would cause the device to be destroyed. However, swapchain resources + * don't take a reference to the device, and we wouldn't want to increment + * the refcount on a device that's in the process of being destroyed. */ + if (swapchain_count) + wined3d_device_incref(device); + wined3d_rendertarget_view_cleanup(&view_vk->v); + wined3d_view_vk_destroy(device, NULL, &view_vk->vk_image_view, + NULL, NULL, &view_vk->command_buffer_id, view_vk); + if (swapchain_count) + wined3d_device_decref(device); +} + +static HRESULT adapter_vk_create_shader_resource_view(const struct wined3d_view_desc *desc, + struct wined3d_resource *resource, void *parent, const struct wined3d_parent_ops *parent_ops, + struct wined3d_shader_resource_view **view) +{ + struct wined3d_shader_resource_view_vk *view_vk; + HRESULT hr; + + TRACE("desc %s, resource %p, parent %p, parent_ops %p, view %p.\n", + wined3d_debug_view_desc(desc, resource), resource, parent, parent_ops, view); + + if (!(view_vk = heap_alloc_zero(sizeof(*view_vk)))) + return E_OUTOFMEMORY; + + if (FAILED(hr = wined3d_shader_resource_view_vk_init(view_vk, desc, resource, parent, parent_ops))) + { + WARN("Failed to initialise view, hr %#x.\n", hr); + heap_free(view_vk); + return hr; + } + + TRACE("Created shader resource view %p.\n", view_vk); + *view = &view_vk->v; + + return hr; +} + +static void adapter_vk_destroy_shader_resource_view(struct wined3d_shader_resource_view *view) +{ + struct wined3d_shader_resource_view_vk *srv_vk = wined3d_shader_resource_view_vk(view); + struct wined3d_device *device = srv_vk->v.resource->device; + unsigned int swapchain_count = device->swapchain_count; + struct wined3d_view_vk *view_vk = &srv_vk->view_vk; + VkBufferView *vk_buffer_view = NULL; + VkImageView *vk_image_view = NULL; + + TRACE("srv_vk %p.\n", srv_vk); + + /* Take a reference to the device, in case releasing the view's resource + * would cause the device to be destroyed. However, swapchain resources + * don't take a reference to the device, and we wouldn't want to increment + * the refcount on a device that's in the process of being destroyed. */ + if (swapchain_count) + wined3d_device_incref(device); + if (srv_vk->v.resource->type == WINED3D_RTYPE_BUFFER) + vk_buffer_view = &view_vk->u.vk_buffer_view; + else + vk_image_view = &view_vk->u.vk_image_info.imageView; + list_remove(&view_vk->bo_user.entry); + wined3d_shader_resource_view_cleanup(&srv_vk->v); + wined3d_view_vk_destroy(device, vk_buffer_view, vk_image_view, + NULL, NULL, &view_vk->command_buffer_id, srv_vk); + if (swapchain_count) + wined3d_device_decref(device); +} + +static HRESULT adapter_vk_create_unordered_access_view(const struct wined3d_view_desc *desc, + struct wined3d_resource *resource, void *parent, const struct wined3d_parent_ops *parent_ops, + struct wined3d_unordered_access_view **view) +{ + struct wined3d_unordered_access_view_vk *view_vk; + HRESULT hr; + + TRACE("desc %s, resource %p, parent %p, parent_ops %p, view %p.\n", + wined3d_debug_view_desc(desc, resource), resource, parent, parent_ops, view); + + if (!(view_vk = heap_alloc_zero(sizeof(*view_vk)))) + return E_OUTOFMEMORY; + + if (FAILED(hr = wined3d_unordered_access_view_vk_init(view_vk, desc, resource, parent, parent_ops))) + { + WARN("Failed to initialise view, hr %#x.\n", hr); + heap_free(view_vk); + return hr; + } + + TRACE("Created unordered access view %p.\n", view_vk); + *view = &view_vk->v; + + return hr; +} + +static void adapter_vk_destroy_unordered_access_view(struct wined3d_unordered_access_view *view) +{ + struct wined3d_unordered_access_view_vk *uav_vk = wined3d_unordered_access_view_vk(view); + struct wined3d_device *device = uav_vk->v.resource->device; + unsigned int swapchain_count = device->swapchain_count; + struct wined3d_view_vk *view_vk = &uav_vk->view_vk; + VkBufferView *vk_buffer_view = NULL; + VkImageView *vk_image_view = NULL; + + TRACE("uav_vk %p.\n", uav_vk); + + /* Take a reference to the device, in case releasing the view's resource + * would cause the device to be destroyed. However, swapchain resources + * don't take a reference to the device, and we wouldn't want to increment + * the refcount on a device that's in the process of being destroyed. */ + if (swapchain_count) + wined3d_device_incref(device); + if (uav_vk->v.resource->type == WINED3D_RTYPE_BUFFER) + vk_buffer_view = &view_vk->u.vk_buffer_view; + else + vk_image_view = &view_vk->u.vk_image_info.imageView; + list_remove(&view_vk->bo_user.entry); + wined3d_unordered_access_view_cleanup(&uav_vk->v); + wined3d_view_vk_destroy(device, vk_buffer_view, vk_image_view, &uav_vk->counter_bo, + &uav_vk->vk_counter_view, &view_vk->command_buffer_id, uav_vk); + if (swapchain_count) + wined3d_device_decref(device); +} + +static HRESULT adapter_vk_create_sampler(struct wined3d_device *device, const struct wined3d_sampler_desc *desc, + void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_sampler **sampler) +{ + struct wined3d_sampler_vk *sampler_vk; + + TRACE("device %p, desc %p, parent %p, parent_ops %p, sampler %p.\n", + device, desc, parent, parent_ops, sampler); + + if (!(sampler_vk = heap_alloc_zero(sizeof(*sampler_vk)))) + return E_OUTOFMEMORY; + + wined3d_sampler_vk_init(sampler_vk, device, desc, parent, parent_ops); + + TRACE("Created sampler %p.\n", sampler_vk); + *sampler = &sampler_vk->s; + + return WINED3D_OK; +} + +static void wined3d_sampler_vk_destroy_object(void *object) +{ + struct wined3d_sampler_vk *sampler_vk = object; + struct wined3d_context_vk *context_vk; + + TRACE("sampler_vk %p.\n", sampler_vk); + + context_vk = wined3d_context_vk(context_acquire(sampler_vk->s.device, NULL, 0)); + + wined3d_context_vk_destroy_sampler(context_vk, sampler_vk->vk_image_info.sampler, sampler_vk->command_buffer_id); + heap_free(sampler_vk); + + context_release(&context_vk->c); +} + +static void adapter_vk_destroy_sampler(struct wined3d_sampler *sampler) +{ + struct wined3d_sampler_vk *sampler_vk = wined3d_sampler_vk(sampler); + + TRACE("sampler_vk %p.\n", sampler_vk); + + wined3d_cs_destroy_object(sampler->device->cs, wined3d_sampler_vk_destroy_object, sampler_vk); +} + +static HRESULT adapter_vk_create_query(struct wined3d_device *device, enum wined3d_query_type type, + void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_query **query) +{ + TRACE("device %p, type %#x, parent %p, parent_ops %p, query %p.\n", + device, type, parent, parent_ops, query); + + return wined3d_query_vk_create(device, type, parent, parent_ops, query); +} + +static void wined3d_query_vk_destroy_object(void *object) +{ + struct wined3d_query_vk *query_vk = object; + + TRACE("query_vk %p.\n", query_vk); + + query_vk->q.query_ops->query_destroy(&query_vk->q); +} + +static void adapter_vk_destroy_query(struct wined3d_query *query) +{ + struct wined3d_query_vk *query_vk = wined3d_query_vk(query); + + TRACE("query_vk %p.\n", query_vk); + + wined3d_cs_destroy_object(query->device->cs, wined3d_query_vk_destroy_object, query_vk); +} + +static void adapter_vk_flush_context(struct wined3d_context *context) +{ + struct wined3d_context_vk *context_vk = wined3d_context_vk(context); + + TRACE("context_vk %p.\n", context_vk); + + wined3d_context_vk_submit_command_buffer(context_vk, 0, NULL, NULL, 0, NULL); +} + +static void adapter_vk_draw_primitive(struct wined3d_device *device, + const struct wined3d_state *state, const struct wined3d_draw_parameters *parameters) +{ + struct wined3d_buffer_vk *indirect_vk = NULL; + const struct wined3d_vk_info *vk_info; + struct wined3d_context_vk *context_vk; + VkCommandBuffer vk_command_buffer; + uint32_t instance_count; + unsigned int i; + + TRACE("device %p, state %p, parameters %p.\n", device, state, parameters); + + context_vk = wined3d_context_vk(context_acquire(device, NULL, 0)); + vk_info = context_vk->vk_info; + + if (parameters->indirect) + indirect_vk = wined3d_buffer_vk(parameters->u.indirect.buffer); + + if (!(vk_command_buffer = wined3d_context_vk_apply_draw_state(context_vk, + state, indirect_vk, parameters->indexed))) + { + ERR("Failed to apply draw state.\n"); + context_release(&context_vk->c); + return; + } + + if (context_vk->c.transform_feedback_active) + { + if (!context_vk->vk_so_counter_bo.vk_buffer) + { + struct wined3d_bo_vk *bo = &context_vk->vk_so_counter_bo; + + if (!wined3d_context_vk_create_bo(context_vk, ARRAY_SIZE(context_vk->vk_so_counters) * sizeof(uint32_t) * 2, + VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_COUNTER_BUFFER_BIT_EXT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, bo)) + ERR("Failed to create counter BO.\n"); + for (i = 0; i < ARRAY_SIZE(context_vk->vk_so_counters); ++i) + { + context_vk->vk_so_counters[i] = bo->vk_buffer; + context_vk->vk_so_offsets[i] = bo->buffer_offset + i * sizeof(uint32_t) * 2; + } + } + + wined3d_context_vk_reference_bo(context_vk, &context_vk->vk_so_counter_bo); + if (context_vk->c.transform_feedback_paused) + VK_CALL(vkCmdBeginTransformFeedbackEXT(vk_command_buffer, 0, ARRAY_SIZE(context_vk->vk_so_counters), + context_vk->vk_so_counters, context_vk->vk_so_offsets)); + else + VK_CALL(vkCmdBeginTransformFeedbackEXT(vk_command_buffer, 0, 0, NULL, NULL)); + } + + if (parameters->indirect) + { + struct wined3d_bo_vk *bo = &indirect_vk->bo; + uint32_t stride, size; + + wined3d_context_vk_reference_bo(context_vk, bo); + size = indirect_vk->b.resource.size - parameters->u.indirect.offset; + + if (parameters->indexed) + { + stride = sizeof(VkDrawIndexedIndirectCommand); + VK_CALL(vkCmdDrawIndexedIndirect(vk_command_buffer, bo->vk_buffer, + bo->buffer_offset + parameters->u.indirect.offset, size / stride, stride)); + } + else + { + stride = sizeof(VkDrawIndirectCommand); + VK_CALL(vkCmdDrawIndirect(vk_command_buffer, bo->vk_buffer, + bo->buffer_offset + parameters->u.indirect.offset, size / stride, stride)); + } + } + else + { + instance_count = parameters->u.direct.instance_count; + if (context_vk->c.instance_count) + instance_count = context_vk->c.instance_count; + if (!instance_count) + instance_count = 1; + + if (parameters->indexed) + VK_CALL(vkCmdDrawIndexed(vk_command_buffer, parameters->u.direct.index_count, + instance_count, parameters->u.direct.start_idx, parameters->u.direct.base_vertex_idx, + parameters->u.direct.start_instance)); + else + VK_CALL(vkCmdDraw(vk_command_buffer, parameters->u.direct.index_count, instance_count, + parameters->u.direct.start_idx, parameters->u.direct.start_instance)); + } + + if (context_vk->c.transform_feedback_active) + { + VK_CALL(vkCmdEndTransformFeedbackEXT(vk_command_buffer, 0, ARRAY_SIZE(context_vk->vk_so_counters), + context_vk->vk_so_counters, context_vk->vk_so_offsets)); + context_vk->c.transform_feedback_paused = 1; + context_vk->c.transform_feedback_active = 0; + } + + context_release(&context_vk->c); +} + +static void adapter_vk_dispatch_compute(struct wined3d_device *device, + const struct wined3d_state *state, const struct wined3d_dispatch_parameters *parameters) +{ + struct wined3d_buffer_vk *indirect_vk = NULL; + const struct wined3d_vk_info *vk_info; + struct wined3d_context_vk *context_vk; + VkCommandBuffer vk_command_buffer; + + TRACE("device %p, state %p, parameters %p.\n", device, state, parameters); + + context_vk = wined3d_context_vk(context_acquire(device, NULL, 0)); + vk_info = context_vk->vk_info; + + if (parameters->indirect) + indirect_vk = wined3d_buffer_vk(parameters->u.indirect.buffer); + + if (!(vk_command_buffer = wined3d_context_vk_apply_compute_state(context_vk, state, indirect_vk))) + { + ERR("Failed to apply compute state.\n"); + context_release(&context_vk->c); + return; + } + + if (parameters->indirect) + { + struct wined3d_bo_vk *bo = &indirect_vk->bo; + + wined3d_context_vk_reference_bo(context_vk, bo); + VK_CALL(vkCmdDispatchIndirect(vk_command_buffer, bo->vk_buffer, + bo->buffer_offset + parameters->u.indirect.offset)); + } + else + { + const struct wined3d_direct_dispatch_parameters *direct = ¶meters->u.direct; + + VK_CALL(vkCmdDispatch(vk_command_buffer, direct->group_count_x, direct->group_count_y, direct->group_count_z)); + } + + VK_CALL(vkCmdPipelineBarrier(vk_command_buffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, 0, 0, NULL, 0, NULL, 0, NULL)); + + context_release(&context_vk->c); +} + +static void adapter_vk_clear_uav(struct wined3d_context *context, + struct wined3d_unordered_access_view *view, const struct wined3d_uvec4 *clear_value) +{ + TRACE("context %p, view %p, clear_value %s.\n", context, view, debug_uvec4(clear_value)); + + wined3d_unordered_access_view_vk_clear_uint(wined3d_unordered_access_view_vk(view), + clear_value, wined3d_context_vk(context)); +} + +static const struct wined3d_adapter_ops wined3d_adapter_vk_ops = +{ + .adapter_destroy = adapter_vk_destroy, + .adapter_create_device = adapter_vk_create_device, + .adapter_destroy_device = adapter_vk_destroy_device, + .adapter_acquire_context = adapter_vk_acquire_context, + .adapter_release_context = adapter_vk_release_context, + .adapter_get_wined3d_caps = adapter_vk_get_wined3d_caps, + .adapter_check_format = adapter_vk_check_format, + .adapter_init_3d = adapter_vk_init_3d, + .adapter_uninit_3d = adapter_vk_uninit_3d, + .adapter_map_bo_address = adapter_vk_map_bo_address, + .adapter_unmap_bo_address = adapter_vk_unmap_bo_address, + .adapter_copy_bo_address = adapter_vk_copy_bo_address, + .adapter_create_swapchain = adapter_vk_create_swapchain, + .adapter_destroy_swapchain = adapter_vk_destroy_swapchain, + .adapter_create_buffer = adapter_vk_create_buffer, + .adapter_destroy_buffer = adapter_vk_destroy_buffer, + .adapter_create_texture = adapter_vk_create_texture, + .adapter_destroy_texture = adapter_vk_destroy_texture, + .adapter_create_rendertarget_view = adapter_vk_create_rendertarget_view, + .adapter_destroy_rendertarget_view = adapter_vk_destroy_rendertarget_view, + .adapter_create_shader_resource_view = adapter_vk_create_shader_resource_view, + .adapter_destroy_shader_resource_view = adapter_vk_destroy_shader_resource_view, + .adapter_create_unordered_access_view = adapter_vk_create_unordered_access_view, + .adapter_destroy_unordered_access_view = adapter_vk_destroy_unordered_access_view, + .adapter_create_sampler = adapter_vk_create_sampler, + .adapter_destroy_sampler = adapter_vk_destroy_sampler, + .adapter_create_query = adapter_vk_create_query, + .adapter_destroy_query = adapter_vk_destroy_query, + .adapter_flush_context = adapter_vk_flush_context, + .adapter_draw_primitive = adapter_vk_draw_primitive, + .adapter_dispatch_compute = adapter_vk_dispatch_compute, + .adapter_clear_uav = adapter_vk_clear_uav, +}; + +static unsigned int wined3d_get_wine_vk_version(void) +{ + const char *ptr = PACKAGE_VERSION; + int major, minor; + + major = atoi(ptr); + + while (isdigit(*ptr)) + ++ptr; + if (*ptr == '.') + ++ptr; + + minor = atoi(ptr); + + return VK_MAKE_VERSION(major, minor, 0); +} + +static const struct +{ + const char *name; + unsigned int core_since_version; + BOOL required; +} +vulkan_instance_extensions[] = +{ + {VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, VK_API_VERSION_1_1, FALSE}, + {VK_KHR_SURFACE_EXTENSION_NAME, ~0u, TRUE}, + {VK_KHR_WIN32_SURFACE_EXTENSION_NAME, ~0u, TRUE}, +}; + +static BOOL enable_vulkan_instance_extensions(uint32_t *extension_count, + const char *enabled_extensions[], const struct wined3d_vk_info *vk_info) +{ + PFN_vkEnumerateInstanceExtensionProperties pfn_vkEnumerateInstanceExtensionProperties; + VkExtensionProperties *extensions = NULL; + BOOL success = FALSE, found; + unsigned int i, j, count; + VkResult vr; + + *extension_count = 0; + + if (!(pfn_vkEnumerateInstanceExtensionProperties + = (void *)VK_CALL(vkGetInstanceProcAddr(NULL, "vkEnumerateInstanceExtensionProperties")))) + { + WARN("Failed to get 'vkEnumerateInstanceExtensionProperties'.\n"); + goto done; + } + + if ((vr = pfn_vkEnumerateInstanceExtensionProperties(NULL, &count, NULL)) < 0) + { + WARN("Failed to count instance extensions, vr %s.\n", wined3d_debug_vkresult(vr)); + goto done; + } + if (!(extensions = heap_calloc(count, sizeof(*extensions)))) + { + WARN("Out of memory.\n"); + goto done; + } + if ((vr = pfn_vkEnumerateInstanceExtensionProperties(NULL, &count, extensions)) < 0) + { + WARN("Failed to enumerate extensions, vr %s.\n", wined3d_debug_vkresult(vr)); + goto done; + } + + TRACE("Vulkan instance extensions reported:\n"); + for (i = 0; i < count; ++i) + { + TRACE(" - %s.\n", debugstr_a(extensions[i].extensionName)); + } + + for (i = 0; i < ARRAY_SIZE(vulkan_instance_extensions); ++i) + { + if (vulkan_instance_extensions[i].core_since_version <= vk_info->api_version) + continue; + + for (j = 0, found = FALSE; j < count; ++j) + { + if (!strcmp(extensions[j].extensionName, vulkan_instance_extensions[i].name)) + { + found = TRUE; + break; + } + } + if (found) + { + TRACE("Enabling instance extension '%s'.\n", vulkan_instance_extensions[i].name); + enabled_extensions[(*extension_count)++] = vulkan_instance_extensions[i].name; + } + else if (!found && vulkan_instance_extensions[i].required) + { + WARN("Required extension '%s' is not available.\n", vulkan_instance_extensions[i].name); + goto done; + } + } + success = TRUE; + +done: + heap_free(extensions); + return success; +} + +static BOOL wined3d_init_vulkan(struct wined3d_vk_info *vk_info) +{ + const char *enabled_instance_extensions[ARRAY_SIZE(vulkan_instance_extensions)]; + PFN_vkEnumerateInstanceVersion pfn_vkEnumerateInstanceVersion; + struct vulkan_ops *vk_ops = &vk_info->vk_ops; + VkInstance instance = VK_NULL_HANDLE; + VkInstanceCreateInfo instance_info; + VkApplicationInfo app_info; + uint32_t api_version = 0; + char app_name[MAX_PATH]; + VkResult vr; + + if (!wined3d_load_vulkan(vk_info)) + return FALSE; + + if (!(vk_ops->vkCreateInstance = (void *)VK_CALL(vkGetInstanceProcAddr(NULL, "vkCreateInstance")))) + { + ERR("Failed to get 'vkCreateInstance'.\n"); + goto fail; + } + + vk_info->api_version = VK_API_VERSION_1_0; + if ((pfn_vkEnumerateInstanceVersion = (void *)VK_CALL(vkGetInstanceProcAddr(NULL, "vkEnumerateInstanceVersion"))) + && pfn_vkEnumerateInstanceVersion(&api_version) == VK_SUCCESS) + { + TRACE("Vulkan instance API version %s.\n", debug_vk_version(api_version)); + + if (api_version >= VK_API_VERSION_1_1) + vk_info->api_version = VK_API_VERSION_1_1; + } + + memset(&app_info, 0, sizeof(app_info)); + app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + if (wined3d_get_app_name(app_name, ARRAY_SIZE(app_name))) + app_info.pApplicationName = app_name; + app_info.pEngineName = "Damavand"; + app_info.engineVersion = wined3d_get_wine_vk_version(); + app_info.apiVersion = vk_info->api_version; + + memset(&instance_info, 0, sizeof(instance_info)); + instance_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + instance_info.pApplicationInfo = &app_info; + instance_info.ppEnabledExtensionNames = enabled_instance_extensions; + if (!enable_vulkan_instance_extensions(&instance_info.enabledExtensionCount, enabled_instance_extensions, vk_info)) + goto fail; + + memset(vk_info->supported, 0, sizeof(vk_info->supported)); + vk_info->supported[WINED3D_VK_EXT_NONE] = TRUE; + + if ((vr = VK_CALL(vkCreateInstance(&instance_info, NULL, &instance))) < 0) + { + WARN("Failed to create Vulkan instance, vr %s.\n", wined3d_debug_vkresult(vr)); + goto fail; + } + + TRACE("Created Vulkan instance %p.\n", instance); + +#define LOAD_INSTANCE_PFN(name) \ + if (!(vk_ops->name = (void *)VK_CALL(vkGetInstanceProcAddr(instance, #name)))) \ + { \ + WARN("Could not get instance proc addr for '" #name "'.\n"); \ + goto fail; \ + } +#define LOAD_INSTANCE_OPT_PFN(name) \ + vk_ops->name = (void *)VK_CALL(vkGetInstanceProcAddr(instance, #name)); +#define VK_INSTANCE_PFN LOAD_INSTANCE_PFN +#define VK_INSTANCE_EXT_PFN LOAD_INSTANCE_OPT_PFN +#define VK_DEVICE_PFN LOAD_INSTANCE_PFN +#define VK_DEVICE_EXT_PFN LOAD_INSTANCE_OPT_PFN + VK_INSTANCE_FUNCS() + VK_DEVICE_FUNCS() +#undef VK_INSTANCE_PFN +#undef VK_INSTANCE_EXT_PFN +#undef VK_DEVICE_PFN +#undef VK_DEVICE_EXT_PFN + +#define MAP_INSTANCE_FUNCTION(core_pfn, ext_pfn) \ + if (!vk_ops->core_pfn) \ + vk_ops->core_pfn = (void *)VK_CALL(vkGetInstanceProcAddr(instance, #ext_pfn)); + MAP_INSTANCE_FUNCTION(vkGetPhysicalDeviceProperties2, vkGetPhysicalDeviceProperties2KHR) + MAP_INSTANCE_FUNCTION(vkGetPhysicalDeviceFeatures2, vkGetPhysicalDeviceFeatures2KHR) +#undef MAP_INSTANCE_FUNCTION + + vk_info->instance = instance; + + return TRUE; + +fail: + if (vk_ops->vkDestroyInstance) + VK_CALL(vkDestroyInstance(instance, NULL)); + wined3d_unload_vulkan(vk_info); + return FALSE; +} + +static VkPhysicalDevice get_vulkan_physical_device(struct wined3d_vk_info *vk_info) +{ + VkPhysicalDevice physical_devices[1]; + uint32_t count; + VkResult vr; + + if ((vr = VK_CALL(vkEnumeratePhysicalDevices(vk_info->instance, &count, NULL))) < 0) + { + WARN("Failed to enumerate physical devices, vr %s.\n", wined3d_debug_vkresult(vr)); + return VK_NULL_HANDLE; + } + if (!count) + { + WARN("No physical device.\n"); + return VK_NULL_HANDLE; + } + if (count > 1) + { + /* TODO: Create wined3d_adapter for each device. */ + FIXME("Multiple physical devices available.\n"); + count = 1; + } + + if ((vr = VK_CALL(vkEnumeratePhysicalDevices(vk_info->instance, &count, physical_devices))) < 0) + { + WARN("Failed to get physical devices, vr %s.\n", wined3d_debug_vkresult(vr)); + return VK_NULL_HANDLE; + } + + return physical_devices[0]; +} + +static enum wined3d_display_driver guess_display_driver(enum wined3d_pci_vendor vendor) +{ + switch (vendor) + { + case HW_VENDOR_AMD: return DRIVER_AMD_RX; + case HW_VENDOR_INTEL: return DRIVER_INTEL_HD4000; + case HW_VENDOR_NVIDIA: return DRIVER_NVIDIA_GEFORCE8; + default: return DRIVER_WINE; + } +} + +static bool adapter_vk_init_driver_info(struct wined3d_adapter_vk *adapter_vk, + const VkPhysicalDeviceProperties *properties) +{ + const VkPhysicalDeviceMemoryProperties *memory_properties = &adapter_vk->memory_properties; + const struct wined3d_gpu_description *gpu_description; + struct wined3d_gpu_description description; + UINT64 vram_bytes, sysmem_bytes; + const VkMemoryType *type; + const VkMemoryHeap *heap; + unsigned int i; + + TRACE("Device name: %s.\n", debugstr_a(properties->deviceName)); + TRACE("Vendor ID: 0x%04x, Device ID: 0x%04x.\n", properties->vendorID, properties->deviceID); + TRACE("Driver version: %#x.\n", properties->driverVersion); + TRACE("API version: %s.\n", debug_vk_version(properties->apiVersion)); + + for (i = 0, vram_bytes = 0, sysmem_bytes = 0; i < memory_properties->memoryHeapCount; ++i) + { + heap = &memory_properties->memoryHeaps[i]; + TRACE("Memory heap [%u]: flags %#x, size 0x%s.\n", + i, heap->flags, wine_dbgstr_longlong(heap->size)); + if (heap->flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) + vram_bytes += heap->size; + else + sysmem_bytes += heap->size; + } + TRACE("Total device memory: 0x%s.\n", wine_dbgstr_longlong(vram_bytes)); + TRACE("Total shared system memory: 0x%s.\n", wine_dbgstr_longlong(sysmem_bytes)); + + for (i = 0; i < memory_properties->memoryTypeCount; ++i) + { + type = &memory_properties->memoryTypes[i]; + TRACE("Memory type [%u]: flags %#x, heap %u.\n", i, type->propertyFlags, type->heapIndex); + } + + if (!(gpu_description = wined3d_get_user_override_gpu_description(properties->vendorID, properties->deviceID))) + gpu_description = wined3d_get_gpu_description(properties->vendorID, properties->deviceID); + + if (!gpu_description) + { + FIXME("Failed to retrieve GPU description for device %s %04x:%04x.\n", + debugstr_a(properties->deviceName), properties->vendorID, properties->deviceID); + + description.vendor = properties->vendorID; + description.device = properties->deviceID; + description.description = properties->deviceName; + description.driver = guess_display_driver(properties->vendorID); + description.vidmem = vram_bytes; + + gpu_description = &description; + } + + return wined3d_driver_info_init(&adapter_vk->a.driver_info, gpu_description, + adapter_vk->a.d3d_info.feature_level, vram_bytes, sysmem_bytes); +} + +static enum wined3d_feature_level feature_level_from_caps(const struct shader_caps *shader_caps) +{ + unsigned int shader_model; + + shader_model = min(shader_caps->vs_version, shader_caps->ps_version); + shader_model = min(shader_model, max(shader_caps->gs_version, 3)); + shader_model = min(shader_model, max(shader_caps->hs_version, 4)); + shader_model = min(shader_model, max(shader_caps->ds_version, 4)); + + if (shader_model >= 5) + return WINED3D_FEATURE_LEVEL_11_1; + + if (shader_model >= 4) + return WINED3D_FEATURE_LEVEL_10_1; + + return WINED3D_FEATURE_LEVEL_NONE; +} + +static void wined3d_adapter_vk_init_d3d_info(struct wined3d_adapter_vk *adapter_vk, uint32_t wined3d_creation_flags) +{ + struct wined3d_d3d_info *d3d_info = &adapter_vk->a.d3d_info; + struct wined3d_vertex_caps vertex_caps; + struct fragment_caps fragment_caps; + struct shader_caps shader_caps; + + adapter_vk->a.shader_backend->shader_get_caps(&adapter_vk->a, &shader_caps); + adapter_vk->a.vertex_pipe->vp_get_caps(&adapter_vk->a, &vertex_caps); + adapter_vk->a.fragment_pipe->get_caps(&adapter_vk->a, &fragment_caps); + + d3d_info->limits.vs_version = shader_caps.vs_version; + d3d_info->limits.hs_version = shader_caps.hs_version; + d3d_info->limits.ds_version = shader_caps.ds_version; + d3d_info->limits.gs_version = shader_caps.gs_version; + d3d_info->limits.ps_version = shader_caps.ps_version; + d3d_info->limits.cs_version = shader_caps.cs_version; + d3d_info->limits.vs_uniform_count = shader_caps.vs_uniform_count; + d3d_info->limits.ps_uniform_count = shader_caps.ps_uniform_count; + d3d_info->limits.varying_count = shader_caps.varying_count; + d3d_info->limits.ffp_textures = fragment_caps.MaxSimultaneousTextures; + d3d_info->limits.ffp_blend_stages = fragment_caps.MaxTextureBlendStages; + d3d_info->limits.ffp_vertex_blend_matrices = vertex_caps.max_vertex_blend_matrices; + d3d_info->limits.active_light_count = vertex_caps.max_active_lights; + + d3d_info->limits.max_rt_count = WINED3D_MAX_RENDER_TARGETS; + d3d_info->limits.max_clip_distances = WINED3D_MAX_CLIP_DISTANCES; + d3d_info->limits.texture_size = adapter_vk->device_limits.maxImageDimension2D; + d3d_info->limits.pointsize_max = adapter_vk->device_limits.pointSizeRange[1]; + + d3d_info->wined3d_creation_flags = wined3d_creation_flags; + + d3d_info->xyzrhw = vertex_caps.xyzrhw; + d3d_info->emulated_flatshading = vertex_caps.emulated_flatshading; + d3d_info->ffp_generic_attributes = vertex_caps.ffp_generic_attributes; + d3d_info->ffp_alpha_test = false; + d3d_info->vs_clipping = !!(shader_caps.wined3d_caps & WINED3D_SHADER_CAP_VS_CLIPPING); + d3d_info->shader_color_key = !!(fragment_caps.wined3d_caps & WINED3D_FRAGMENT_CAP_COLOR_KEY); + d3d_info->shader_double_precision = !!(shader_caps.wined3d_caps & WINED3D_SHADER_CAP_DOUBLE_PRECISION); + d3d_info->shader_output_interpolation = !!(shader_caps.wined3d_caps & WINED3D_SHADER_CAP_OUTPUT_INTERPOLATION); + d3d_info->viewport_array_index_any_shader = false; /* VK_EXT_shader_viewport_index_layer */ + d3d_info->texture_npot = true; + d3d_info->texture_npot_conditional = true; + d3d_info->draw_base_vertex_offset = true; + d3d_info->vertex_bgra = true; + d3d_info->texture_swizzle = true; + d3d_info->srgb_read_control = false; + d3d_info->srgb_write_control = false; + d3d_info->clip_control = true; + d3d_info->full_ffp_varyings = !!(shader_caps.wined3d_caps & WINED3D_SHADER_CAP_FULL_FFP_VARYINGS); + d3d_info->scaled_resolve = false; + d3d_info->feature_level = feature_level_from_caps(&shader_caps); + + d3d_info->multisample_draw_location = WINED3D_LOCATION_TEXTURE_RGB; +} + +static bool wined3d_adapter_vk_init_device_extensions(struct wined3d_adapter_vk *adapter_vk) +{ + VkPhysicalDevice physical_device = adapter_vk->physical_device; + struct wined3d_vk_info *vk_info = &adapter_vk->vk_info; + unsigned int count, enable_count, i, j; + const char **enabled_extensions = NULL; + VkExtensionProperties *extensions; + bool found, success = false; + SIZE_T enable_size = 0; + VkResult vr; + + static const struct + { + const char *name; + unsigned int core_since_version; + bool required; + } + info[] = + { + {VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME, ~0u}, + {VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME, ~0u, true}, + {VK_KHR_MAINTENANCE1_EXTENSION_NAME, VK_API_VERSION_1_1, true}, + {VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME,VK_API_VERSION_1_2}, + {VK_KHR_SHADER_DRAW_PARAMETERS_EXTENSION_NAME, VK_API_VERSION_1_1, true}, + {VK_KHR_SWAPCHAIN_EXTENSION_NAME, ~0u, true}, + }; + + static const struct + { + const char *name; + enum wined3d_vk_extension extension; + } + map[] = + { + {VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME, WINED3D_VK_EXT_TRANSFORM_FEEDBACK}, + {VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME, WINED3D_VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE}, + }; + + if ((vr = VK_CALL(vkEnumerateDeviceExtensionProperties(physical_device, NULL, &count, NULL))) < 0) + { + ERR("Failed to enumerate device extensions, vr %s.\n", wined3d_debug_vkresult(vr)); + return false; + } + + if (!(extensions = heap_calloc(count, sizeof(*extensions)))) + { + ERR("Failed to allocate extension properties array.\n"); + return false; + } + + if ((vr = VK_CALL(vkEnumerateDeviceExtensionProperties(physical_device, NULL, &count, extensions))) < 0) + { + ERR("Failed to enumerate device extensions, vr %s.\n", wined3d_debug_vkresult(vr)); + goto done; + } + + TRACE("Vulkan device extensions reported:\n"); + for (i = 0; i < count; ++i) + { + TRACE(" - %s.\n", debugstr_a(extensions[i].extensionName)); + } + + for (i = 0, enable_count = 0; i < ARRAY_SIZE(info); ++i) + { + if (info[i].core_since_version <= vk_info->api_version) + continue; + + for (j = 0, found = false; j < count; ++j) + { + if (!strcmp(extensions[j].extensionName, info[i].name)) + { + found = true; + break; + } + } + + if (!found) + { + if (!info[i].required) + continue; + WARN("Required extension '%s' is not available.\n", info[i].name); + goto done; + } + + TRACE("Enabling device extension '%s'.\n", info[i].name); + if (!wined3d_array_reserve((void **)&enabled_extensions, &enable_size, + enable_count + 1, sizeof(*enabled_extensions))) + { + ERR("Failed to allocate enabled extensions array.\n"); + goto done; + } + enabled_extensions[enable_count++] = info[i].name; + } + success = true; + + for (i = 0; i < ARRAY_SIZE(map); ++i) + { + for (j = 0; j < enable_count; ++j) + { + if (!strcmp(enabled_extensions[j], map[i].name)) + { + vk_info->supported[map[i].extension] = TRUE; + break; + } + } + } + +done: + if (success) + { + adapter_vk->device_extension_count = enable_count; + adapter_vk->device_extensions = enabled_extensions; + } + else + { + heap_free(enabled_extensions); + } + heap_free(extensions); + return success; +} + +static BOOL wined3d_adapter_vk_init(struct wined3d_adapter_vk *adapter_vk, + unsigned int ordinal, unsigned int wined3d_creation_flags) +{ + struct wined3d_vk_info *vk_info = &adapter_vk->vk_info; + struct wined3d_adapter *adapter = &adapter_vk->a; + VkPhysicalDeviceIDProperties id_properties; + VkPhysicalDeviceProperties2 properties2; + LUID primary_luid, *luid = NULL; + + TRACE("adapter_vk %p, ordinal %u, wined3d_creation_flags %#x.\n", + adapter_vk, ordinal, wined3d_creation_flags); + + if (!wined3d_init_vulkan(vk_info)) + { + WARN("Failed to initialize Vulkan.\n"); + return FALSE; + } + + if (!(adapter_vk->physical_device = get_vulkan_physical_device(vk_info))) + goto fail_vulkan; + + if (!wined3d_adapter_vk_init_device_extensions(adapter_vk)) + goto fail_vulkan; + + memset(&id_properties, 0, sizeof(id_properties)); + id_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES; + properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; + properties2.pNext = &id_properties; + + if (vk_info->vk_ops.vkGetPhysicalDeviceProperties2) + VK_CALL(vkGetPhysicalDeviceProperties2(adapter_vk->physical_device, &properties2)); + else + VK_CALL(vkGetPhysicalDeviceProperties(adapter_vk->physical_device, &properties2.properties)); + adapter_vk->device_limits = properties2.properties.limits; + + VK_CALL(vkGetPhysicalDeviceMemoryProperties(adapter_vk->physical_device, &adapter_vk->memory_properties)); + + if (id_properties.deviceLUIDValid) + luid = (LUID *)id_properties.deviceLUID; + else if (ordinal == 0 && wined3d_get_primary_adapter_luid(&primary_luid)) + luid = &primary_luid; + + if (!wined3d_adapter_init(adapter, ordinal, luid, &wined3d_adapter_vk_ops)) + { + heap_free(adapter_vk->device_extensions); + goto fail_vulkan; + } + + adapter->vertex_pipe = wined3d_spirv_vertex_pipe_init_vk(); + adapter->fragment_pipe = wined3d_spirv_fragment_pipe_init_vk(); + adapter->misc_state_template = misc_state_template_vk; + adapter->shader_backend = wined3d_spirv_shader_backend_init_vk(); + + wined3d_adapter_vk_init_d3d_info(adapter_vk, wined3d_creation_flags); + + if (!adapter_vk_init_driver_info(adapter_vk, &properties2.properties)) + goto fail; + TRACE("Reporting (fake) driver version 0x%08x-0x%08x.\n", + adapter_vk->a.driver_info.version_high, adapter_vk->a.driver_info.version_low); + adapter->vram_bytes_used = 0; + TRACE("Emulating 0x%s bytes of video ram.\n", wine_dbgstr_longlong(adapter->driver_info.vram_bytes)); + + memcpy(&adapter->driver_uuid, id_properties.driverUUID, sizeof(adapter->driver_uuid)); + memcpy(&adapter->device_uuid, id_properties.deviceUUID, sizeof(adapter->device_uuid)); + + if (!wined3d_adapter_vk_init_format_info(adapter_vk, vk_info)) + goto fail; + + return TRUE; + +fail: + wined3d_adapter_cleanup(adapter); + heap_free(adapter_vk->device_extensions); +fail_vulkan: + VK_CALL(vkDestroyInstance(vk_info->instance, NULL)); + wined3d_unload_vulkan(vk_info); + return FALSE; +} + +struct wined3d_adapter *wined3d_adapter_vk_create(unsigned int ordinal, + unsigned int wined3d_creation_flags) +{ + struct wined3d_adapter_vk *adapter_vk; + + if (!(adapter_vk = heap_alloc_zero(sizeof(*adapter_vk)))) + return NULL; + + if (!wined3d_adapter_vk_init(adapter_vk, ordinal, wined3d_creation_flags)) + { + heap_free(adapter_vk); + return NULL; + } + + TRACE("Created adapter %p.\n", adapter_vk); + + return &adapter_vk->a; +} diff --git a/wrappers/directx/d3dwine_wrapper/arb_program_shader.c b/wrappers/directx/d3dwine_wrapper/arb_program_shader.c new file mode 100644 index 00000000000..af8f2a508fa --- /dev/null +++ b/wrappers/directx/d3dwine_wrapper/arb_program_shader.c @@ -0,0 +1,7991 @@ +/* + * Pixel and vertex shaders implementation using ARB_vertex_program + * and ARB_fragment_program GL extensions. + * + * Copyright 2002-2003 Jason Edmeades + * Copyright 2002-2003 Raphael Junqueira + * Copyright 2004 Christian Costa + * Copyright 2005 Oliver Stieber + * Copyright 2006 Ivan Gyurdiev + * Copyright 2006 Jason Green + * Copyright 2006 Henri Verbeet + * Copyright 2007-2011, 2013-2014 Stefan Dösinger for CodeWeavers + * Copyright 2009 Henri Verbeet for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "config.h" +#include "wine/port.h" + +#include + +#include "wined3d_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(d3d_shader); +WINE_DECLARE_DEBUG_CHANNEL(d3d_constants); +WINE_DECLARE_DEBUG_CHANNEL(d3d); +WINE_DECLARE_DEBUG_CHANNEL(d3d_perf); + +static BOOL shader_is_pshader_version(enum wined3d_shader_type type) +{ + return type == WINED3D_SHADER_TYPE_PIXEL; +} + +static BOOL shader_is_vshader_version(enum wined3d_shader_type type) +{ + return type == WINED3D_SHADER_TYPE_VERTEX; +} + +static const char *get_line(const char **ptr) +{ + const char *p, *q; + + p = *ptr; + if (!(q = strstr(p, "\n"))) + { + if (!*p) return NULL; + *ptr += strlen(p); + return p; + } + *ptr = q + 1; + + return p; +} + +enum arb_helper_value +{ + ARB_ZERO, + ARB_ONE, + ARB_TWO, + ARB_0001, + ARB_EPS, + + ARB_VS_REL_OFFSET +}; + +static const char *arb_get_helper_value(enum wined3d_shader_type shader, enum arb_helper_value value) +{ + if (shader != WINED3D_SHADER_TYPE_VERTEX && shader != WINED3D_SHADER_TYPE_PIXEL) + { + ERR("Unsupported shader type '%s'.\n", debug_shader_type(shader)); + return "bad"; + } + + if (shader == WINED3D_SHADER_TYPE_PIXEL) + { + switch (value) + { + case ARB_ZERO: return "ps_helper_const.x"; + case ARB_ONE: return "ps_helper_const.y"; + case ARB_TWO: return "coefmul.x"; + case ARB_0001: return "ps_helper_const.xxxy"; + case ARB_EPS: return "ps_helper_const.z"; + default: break; + } + } + else + { + switch (value) + { + case ARB_ZERO: return "helper_const.x"; + case ARB_ONE: return "helper_const.y"; + case ARB_TWO: return "helper_const.z"; + case ARB_EPS: return "helper_const.w"; + case ARB_0001: return "helper_const.xxxy"; + case ARB_VS_REL_OFFSET: return "rel_addr_const.y"; + } + } + FIXME("Unmanaged %s shader helper constant requested: %u.\n", + shader == WINED3D_SHADER_TYPE_PIXEL ? "pixel" : "vertex", value); + switch (value) + { + case ARB_ZERO: return "0.0"; + case ARB_ONE: return "1.0"; + case ARB_TWO: return "2.0"; + case ARB_0001: return "{0.0, 0.0, 0.0, 1.0}"; + case ARB_EPS: return "1e-8"; + default: return "bad"; + } +} + +static inline BOOL ffp_clip_emul(const struct wined3d_context *context) +{ + return context->lowest_disabled_stage < 7; +} + +/* ARB_program_shader private data */ + +struct control_frame +{ + struct list entry; + enum + { + IF, + IFC, + LOOP, + REP + } type; + BOOL muting; + BOOL outer_loop; + union + { + unsigned int loop; + unsigned int ifc; + } no; + struct wined3d_shader_loop_control loop_control; + BOOL had_else; +}; + +struct arb_ps_np2fixup_info +{ + struct ps_np2fixup_info super; + /* For ARB we need an offset value: + * With both GLSL and ARB mode the NP2 fixup information (the texture dimensions) are stored in a + * consecutive way (GLSL uses a uniform array). Since ARB doesn't know the notion of a "standalone" + * array we need an offset to the index inside the program local parameter array. */ + UINT offset; +}; + +struct arb_ps_compile_args +{ + struct ps_compile_args super; + WORD bools; + WORD clip; /* only a boolean, use a WORD for alignment */ + unsigned char loop_ctrl[WINED3D_MAX_CONSTS_I][3]; +}; + +struct stb_const_desc +{ + unsigned char texunit; + UINT const_num; +}; + +struct arb_ps_compiled_shader +{ + struct arb_ps_compile_args args; + struct arb_ps_np2fixup_info np2fixup_info; + struct stb_const_desc bumpenvmatconst[WINED3D_MAX_TEXTURES]; + struct stb_const_desc luminanceconst[WINED3D_MAX_TEXTURES]; + UINT int_consts[WINED3D_MAX_CONSTS_I]; + GLuint prgId; + UINT ycorrection; + unsigned char numbumpenvmatconsts; + char num_int_consts; +}; + +struct arb_vs_compile_args +{ + struct vs_compile_args super; + union + { + struct + { + WORD bools; + unsigned char clip_texcoord; + unsigned char clipplane_mask; + } boolclip; + DWORD boolclip_compare; + } clip; + DWORD ps_signature; + union + { + unsigned char samplers[4]; + DWORD samplers_compare; + } vertex; + unsigned char loop_ctrl[WINED3D_MAX_CONSTS_I][3]; +}; + +struct arb_vs_compiled_shader +{ + struct arb_vs_compile_args args; + GLuint prgId; + UINT int_consts[WINED3D_MAX_CONSTS_I]; + char num_int_consts; + char need_color_unclamp; + UINT pos_fixup; +}; + +struct recorded_instruction +{ + struct wined3d_shader_instruction ins; + struct list entry; +}; + +struct shader_arb_ctx_priv +{ + char addr_reg[50]; + enum + { + /* plain GL_ARB_vertex_program or GL_ARB_fragment_program */ + ARB, + /* GL_NV_vertex_program2_option or GL_NV_fragment_program_option */ + NV2, + /* GL_NV_vertex_program3 or GL_NV_fragment_program2 */ + NV3 + } target_version; + + const struct wined3d_gl_info *gl_info; + const struct arb_vs_compile_args *cur_vs_args; + const struct arb_ps_compile_args *cur_ps_args; + const struct arb_ps_compiled_shader *compiled_fprog; + const struct arb_vs_compiled_shader *compiled_vprog; + struct arb_ps_np2fixup_info *cur_np2fixup_info; + struct list control_frames; + struct list record; + BOOL recording; + BOOL muted; + unsigned int num_loops, loop_depth, num_ifcs; + int aL; + BOOL ps_post_process; + + unsigned int vs_clipplanes; + BOOL footer_written; + BOOL in_main_func; + + /* For 3.0 vertex shaders */ + const char *vs_output[MAX_REG_OUTPUT]; + /* For 2.x and earlier vertex shaders */ + const char *texcrd_output[8], *color_output[2], *fog_output; + + /* 3.0 pshader input for compatibility with fixed function */ + const char *ps_input[MAX_REG_INPUT]; +}; + +struct ps_signature +{ + struct wined3d_shader_signature sig; + DWORD idx; + struct wine_rb_entry entry; +}; + +struct arb_pshader_private { + struct arb_ps_compiled_shader *gl_shaders; + UINT num_gl_shaders, shader_array_size; + DWORD input_signature_idx; + DWORD clipplane_emulation; + BOOL clamp_consts; +}; + +struct arb_vshader_private { + struct arb_vs_compiled_shader *gl_shaders; + UINT num_gl_shaders, shader_array_size; + UINT rel_offset; +}; + +struct shader_arb_priv +{ + GLuint current_vprogram_id; + GLuint current_fprogram_id; + const struct arb_ps_compiled_shader *compiled_fprog; + const struct arb_vs_compiled_shader *compiled_vprog; + BOOL use_arbfp_fixed_func; + struct wine_rb_tree fragment_shaders; + BOOL last_ps_const_clamped; + BOOL last_vs_color_unclamp; + + struct wine_rb_tree signature_tree; + DWORD ps_sig_number; + + unsigned int highest_dirty_ps_const, highest_dirty_vs_const; + char vshader_const_dirty[WINED3D_MAX_VS_CONSTS_F]; + char pshader_const_dirty[WINED3D_MAX_PS_CONSTS_F]; + const struct wined3d_context *last_context; + + const struct wined3d_vertex_pipe_ops *vertex_pipe; + const struct wined3d_fragment_pipe_ops *fragment_pipe; + BOOL ffp_proj_control; +}; + +/* Context activation for state handlers is done by the caller. */ + +static BOOL need_rel_addr_const(const struct arb_vshader_private *shader_data, + const struct wined3d_shader_reg_maps *reg_maps, const struct wined3d_gl_info *gl_info) +{ + if (shader_data->rel_offset) return TRUE; + if (!reg_maps->usesmova) return FALSE; + return !gl_info->supported[NV_VERTEX_PROGRAM2_OPTION]; +} + +/* Returns TRUE if result.clip from GL_NV_vertex_program2 should be used and FALSE otherwise */ +static inline BOOL use_nv_clip(const struct wined3d_gl_info *gl_info) +{ + return gl_info->supported[NV_VERTEX_PROGRAM2_OPTION] + && !(gl_info->quirks & WINED3D_QUIRK_NV_CLIP_BROKEN); +} + +static BOOL need_helper_const(const struct arb_vshader_private *shader_data, + const struct wined3d_shader_reg_maps *reg_maps, const struct wined3d_gl_info *gl_info) +{ + if (need_rel_addr_const(shader_data, reg_maps, gl_info)) return TRUE; + if (!gl_info->supported[NV_VERTEX_PROGRAM]) return TRUE; /* Need to init colors. */ + if (gl_info->quirks & WINED3D_QUIRK_ARB_VS_OFFSET_LIMIT) return TRUE; /* Load the immval offset. */ + if (!use_nv_clip(gl_info)) return TRUE; /* Init the clip texcoord */ + if (reg_maps->usesnrm) return TRUE; /* 0.0 */ + if (reg_maps->usespow) return TRUE; /* EPS, 0.0 and 1.0 */ + if (reg_maps->fog) return TRUE; /* Clamping fog coord, 0.0 and 1.0 */ + return FALSE; +} + +static unsigned int reserved_vs_const(const struct arb_vshader_private *shader_data, + const struct wined3d_shader_reg_maps *reg_maps, const struct wined3d_gl_info *gl_info) +{ + unsigned int ret = 1; + /* We use one PARAM for the pos fixup, and in some cases one to load + * some immediate values into the shader. */ + if (need_helper_const(shader_data, reg_maps, gl_info)) ++ret; + if (need_rel_addr_const(shader_data, reg_maps, gl_info)) ++ret; + return ret; +} + +/* Loads floating point constants into the currently set ARB_vertex/fragment_program. + * When constant_list == NULL, it will load all the constants. + * + * @target_type should be either GL_VERTEX_PROGRAM_ARB (for vertex shaders) + * or GL_FRAGMENT_PROGRAM_ARB (for pixel shaders) + */ +/* Context activation is done by the caller. */ +static unsigned int shader_arb_load_constants_f(const struct wined3d_shader *shader, + const struct wined3d_gl_info *gl_info, GLuint target_type, unsigned int max_constants, + const struct wined3d_vec4 *constants, char *dirty_consts) +{ + struct wined3d_shader_lconst *lconst; + unsigned int ret, i, j; + + if (TRACE_ON(d3d_constants)) + { + for (i = 0; i < max_constants; ++i) + { + if (!dirty_consts[i]) + continue; + TRACE_(d3d_constants)("Loading constant %u: %s.\n", i, debug_vec4(&constants[i])); + } + } + + i = 0; + + /* In 1.X pixel shaders constants are implicitly clamped in the range [-1;1] */ + if (target_type == GL_FRAGMENT_PROGRAM_ARB && shader->reg_maps.shader_version.major == 1) + { + float lcl_const[4]; + /* ps 1.x supports only 8 constants, clamp only those. When switching between 1.x and higher + * shaders, the first 8 constants are marked dirty for reload + */ + for (; i < min(8, max_constants); ++i) + { + if (!dirty_consts[i]) + continue; + dirty_consts[i] = 0; + + if (constants[i].x > 1.0f) + lcl_const[0] = 1.0f; + else if (constants[i].x < -1.0f) + lcl_const[0] = -1.0f; + else + lcl_const[0] = constants[i].x; + + if (constants[i].y > 1.0f) + lcl_const[1] = 1.0f; + else if (constants[i].y < -1.0f) + lcl_const[1] = -1.0f; + else + lcl_const[1] = constants[i].y; + + if (constants[i].z > 1.0f) + lcl_const[2] = 1.0f; + else if (constants[i].z < -1.0f) + lcl_const[2] = -1.0f; + else + lcl_const[2] = constants[i].z; + + if (constants[i].w > 1.0f) + lcl_const[3] = 1.0f; + else if (constants[i].w < -1.0f) + lcl_const[3] = -1.0f; + else + lcl_const[3] = constants[i].w; + + GL_EXTCALL(glProgramEnvParameter4fvARB(target_type, i, lcl_const)); + } + + /* If further constants are dirty, reload them without clamping. + * + * The alternative is not to touch them, but then we cannot reset the dirty constant count + * to zero. That's bad for apps that only use PS 1.x shaders, because in that case the code + * above would always re-check the first 8 constants since max_constant remains at the init + * value + */ + } + + if (gl_info->supported[EXT_GPU_PROGRAM_PARAMETERS]) + { + /* TODO: Benchmark if we're better of with finding the dirty constants ourselves, + * or just reloading *all* constants at once + * + GL_EXTCALL(glProgramEnvParameters4fvEXT(target_type, i, max_constants, constants + (i * 4))); + */ + for (; i < max_constants; ++i) + { + if (!dirty_consts[i]) + continue; + + /* Find the next block of dirty constants */ + dirty_consts[i] = 0; + j = i; + for (++i; (i < max_constants) && dirty_consts[i]; ++i) + { + dirty_consts[i] = 0; + } + + GL_EXTCALL(glProgramEnvParameters4fvEXT(target_type, j, i - j, &constants[j].x)); + } + } + else + { + for (; i < max_constants; ++i) + { + if (dirty_consts[i]) + { + dirty_consts[i] = 0; + GL_EXTCALL(glProgramEnvParameter4fvARB(target_type, i, &constants[i].x)); + } + } + } + checkGLcall("glProgramEnvParameter4fvARB()"); + + /* Load immediate constants */ + if (shader->load_local_constsF) + { + if (TRACE_ON(d3d_shader)) + { + LIST_FOR_EACH_ENTRY(lconst, &shader->constantsF, struct wined3d_shader_lconst, entry) + { + GLfloat* values = (GLfloat*)lconst->value; + TRACE_(d3d_constants)("Loading local constants %i: %f, %f, %f, %f\n", lconst->idx, + values[0], values[1], values[2], values[3]); + } + } + /* Immediate constants are clamped for 1.X shaders at loading times */ + ret = 0; + LIST_FOR_EACH_ENTRY(lconst, &shader->constantsF, struct wined3d_shader_lconst, entry) + { + dirty_consts[lconst->idx] = 1; /* Dirtify so the non-immediate constant overwrites it next time */ + ret = max(ret, lconst->idx + 1); + GL_EXTCALL(glProgramEnvParameter4fvARB(target_type, lconst->idx, (GLfloat*)lconst->value)); + } + checkGLcall("glProgramEnvParameter4fvARB()"); + return ret; /* The loaded immediate constants need reloading for the next shader */ + } else { + return 0; /* No constants are dirty now */ + } +} + +/* Loads the texture dimensions for NP2 fixup into the currently set + * ARB_[vertex/fragment]_programs. */ +static void shader_arb_load_np2fixup_constants(const struct arb_ps_np2fixup_info *fixup, + const struct wined3d_gl_info *gl_info, const struct wined3d_state *state) +{ + GLfloat np2fixup_constants[4 * WINED3D_MAX_FRAGMENT_SAMPLERS]; + WORD active = fixup->super.active; + UINT i; + + if (!active) + return; + + for (i = 0; active; active >>= 1, ++i) + { + const struct wined3d_texture *tex = state->textures[i]; + unsigned char idx = fixup->super.idx[i]; + GLfloat *tex_dim = &np2fixup_constants[(idx >> 1) * 4]; + + if (!(active & 1)) + continue; + + if (!tex) + { + ERR("Nonexistent texture is flagged for NP2 texcoord fixup.\n"); + continue; + } + + if (idx % 2) + { + tex_dim[2] = tex->pow2_matrix[0]; + tex_dim[3] = tex->pow2_matrix[5]; + } + else + { + tex_dim[0] = tex->pow2_matrix[0]; + tex_dim[1] = tex->pow2_matrix[5]; + } + } + + for (i = 0; i < fixup->super.num_consts; ++i) + { + GL_EXTCALL(glProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, + fixup->offset + i, &np2fixup_constants[i * 4])); + } +} + +/* Context activation is done by the caller. */ +static void shader_arb_ps_local_constants(const struct arb_ps_compiled_shader *gl_shader, + const struct wined3d_context_gl *context_gl, const struct wined3d_state *state, unsigned int rt_height) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + unsigned char i; + + for(i = 0; i < gl_shader->numbumpenvmatconsts; i++) + { + int texunit = gl_shader->bumpenvmatconst[i].texunit; + + /* The state manager takes care that this function is always called if the bump env matrix changes */ + const float *data = (const float *)&state->texture_states[texunit][WINED3D_TSS_BUMPENV_MAT00]; + GL_EXTCALL(glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, + gl_shader->bumpenvmatconst[i].const_num, data)); + + if (gl_shader->luminanceconst[i].const_num != WINED3D_CONST_NUM_UNUSED) + { + /* WINED3D_TSS_BUMPENVLSCALE and WINED3D_TSS_BUMPENVLOFFSET are next to each other. + * point gl to the scale, and load 4 floats. x = scale, y = offset, z and w are junk, we + * don't care about them. The pointers are valid for sure because the stateblock is bigger. + * (they're WINED3D_TSS_TEXTURETRANSFORMFLAGS and WINED3D_TSS_ADDRESSW, so most likely 0 or NaN + */ + const float *scale = (const float *)&state->texture_states[texunit][WINED3D_TSS_BUMPENV_LSCALE]; + GL_EXTCALL(glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, + gl_shader->luminanceconst[i].const_num, scale)); + } + } + checkGLcall("Load bumpmap consts"); + + if(gl_shader->ycorrection != WINED3D_CONST_NUM_UNUSED) + { + /* ycorrection.x: Backbuffer height(onscreen) or 0(offscreen). + * ycorrection.y: -1.0(onscreen), 1.0(offscreen) + * ycorrection.z: 1.0 + * ycorrection.w: 0.0 + */ + float val[4]; + val[0] = context_gl->c.render_offscreen ? 0.0f : (float)rt_height; + val[1] = context_gl->c.render_offscreen ? 1.0f : -1.0f; + val[2] = 1.0f; + val[3] = 0.0f; + GL_EXTCALL(glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, gl_shader->ycorrection, val)); + checkGLcall("y correction loading"); + } + + if (!gl_shader->num_int_consts) return; + + for (i = 0; i < WINED3D_MAX_CONSTS_I; ++i) + { + if(gl_shader->int_consts[i] != WINED3D_CONST_NUM_UNUSED) + { + float val[4]; + val[0] = (float)state->ps_consts_i[i].x; + val[1] = (float)state->ps_consts_i[i].y; + val[2] = (float)state->ps_consts_i[i].z; + val[3] = -1.0f; + + GL_EXTCALL(glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, gl_shader->int_consts[i], val)); + } + } + checkGLcall("Load ps int consts"); +} + +/* Context activation is done by the caller. */ +static void shader_arb_vs_local_constants(const struct arb_vs_compiled_shader *gl_shader, + const struct wined3d_context_gl *context_gl, const struct wined3d_state *state) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + float position_fixup[4]; + unsigned char i; + + /* Upload the position fixup */ + shader_get_position_fixup(&context_gl->c, state, 1, position_fixup); + GL_EXTCALL(glProgramLocalParameter4fvARB(GL_VERTEX_PROGRAM_ARB, gl_shader->pos_fixup, position_fixup)); + + if (!gl_shader->num_int_consts) return; + + for (i = 0; i < WINED3D_MAX_CONSTS_I; ++i) + { + if(gl_shader->int_consts[i] != WINED3D_CONST_NUM_UNUSED) + { + float val[4]; + val[0] = (float)state->vs_consts_i[i].x; + val[1] = (float)state->vs_consts_i[i].y; + val[2] = (float)state->vs_consts_i[i].z; + val[3] = -1.0f; + + GL_EXTCALL(glProgramLocalParameter4fvARB(GL_VERTEX_PROGRAM_ARB, gl_shader->int_consts[i], val)); + } + } + checkGLcall("Load vs int consts"); +} + +static void shader_arb_select(void *shader_priv, struct wined3d_context *context, + const struct wined3d_state *state); + +/** + * Loads the app-supplied constants into the currently set ARB_[vertex/fragment]_programs. + * + * We only support float constants in ARB at the moment, so don't + * worry about the Integers or Booleans + */ +/* Context activation is done by the caller (state handler). */ +static void shader_arb_load_constants_internal(struct shader_arb_priv *priv, struct wined3d_context_gl *context_gl, + const struct wined3d_state *state, BOOL use_ps, BOOL use_vs, BOOL from_shader_select) +{ + const struct wined3d_d3d_info *d3d_info = context_gl->c.d3d_info; + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + + if (!from_shader_select) + { + const struct wined3d_shader *vshader = state->shader[WINED3D_SHADER_TYPE_VERTEX]; + const struct wined3d_shader *pshader = state->shader[WINED3D_SHADER_TYPE_PIXEL]; + + if (vshader + && (vshader->reg_maps.boolean_constants + || (!gl_info->supported[NV_VERTEX_PROGRAM2_OPTION] + && (vshader->reg_maps.integer_constants & ~vshader->reg_maps.local_int_consts)))) + { + TRACE("bool/integer vertex shader constants potentially modified, forcing shader reselection.\n"); + shader_arb_select(priv, &context_gl->c, state); + } + else if (pshader + && (pshader->reg_maps.boolean_constants + || (!gl_info->supported[NV_FRAGMENT_PROGRAM_OPTION] + && (pshader->reg_maps.integer_constants & ~pshader->reg_maps.local_int_consts)))) + { + TRACE("bool/integer pixel shader constants potentially modified, forcing shader reselection.\n"); + shader_arb_select(priv, &context_gl->c, state); + } + } + + if (&context_gl->c != priv->last_context) + { + memset(priv->vshader_const_dirty, 1, + sizeof(*priv->vshader_const_dirty) * d3d_info->limits.vs_uniform_count); + priv->highest_dirty_vs_const = d3d_info->limits.vs_uniform_count; + + memset(priv->pshader_const_dirty, 1, + sizeof(*priv->pshader_const_dirty) * d3d_info->limits.ps_uniform_count); + priv->highest_dirty_ps_const = d3d_info->limits.ps_uniform_count; + + priv->last_context = &context_gl->c; + } + + if (use_vs) + { + const struct wined3d_shader *vshader = state->shader[WINED3D_SHADER_TYPE_VERTEX]; + const struct arb_vs_compiled_shader *gl_shader = priv->compiled_vprog; + + /* Load DirectX 9 float constants for vertex shader */ + priv->highest_dirty_vs_const = shader_arb_load_constants_f(vshader, gl_info, GL_VERTEX_PROGRAM_ARB, + priv->highest_dirty_vs_const, state->vs_consts_f, priv->vshader_const_dirty); + shader_arb_vs_local_constants(gl_shader, context_gl, state); + } + + if (use_ps) + { + const struct wined3d_shader *pshader = state->shader[WINED3D_SHADER_TYPE_PIXEL]; + const struct arb_ps_compiled_shader *gl_shader = priv->compiled_fprog; + UINT rt_height = state->fb.render_targets[0]->height; + + /* Load DirectX 9 float constants for pixel shader */ + priv->highest_dirty_ps_const = shader_arb_load_constants_f(pshader, gl_info, GL_FRAGMENT_PROGRAM_ARB, + priv->highest_dirty_ps_const, state->ps_consts_f, priv->pshader_const_dirty); + shader_arb_ps_local_constants(gl_shader, context_gl, state, rt_height); + + if (context_gl->c.constant_update_mask & WINED3D_SHADER_CONST_PS_NP2_FIXUP) + shader_arb_load_np2fixup_constants(&gl_shader->np2fixup_info, gl_info, state); + } +} + +static void shader_arb_load_constants(void *shader_priv, struct wined3d_context *context, + const struct wined3d_state *state) +{ + shader_arb_load_constants_internal(shader_priv, wined3d_context_gl(context), + state, use_ps(state), use_vs(state), FALSE); +} + +static void shader_arb_update_float_vertex_constants(struct wined3d_device *device, UINT start, UINT count) +{ + struct wined3d_context_gl *context_gl = wined3d_context_gl_get_current(); + struct shader_arb_priv *priv = device->shader_priv; + + /* We don't want shader constant dirtification to be an O(contexts), so just dirtify the active + * context. On a context switch the old context will be fully dirtified */ + if (!context_gl || context_gl->c.device != device) + return; + + memset(priv->vshader_const_dirty + start, 1, sizeof(*priv->vshader_const_dirty) * count); + priv->highest_dirty_vs_const = max(priv->highest_dirty_vs_const, start + count); +} + +static void shader_arb_update_float_pixel_constants(struct wined3d_device *device, UINT start, UINT count) +{ + struct wined3d_context_gl *context_gl = wined3d_context_gl_get_current(); + struct shader_arb_priv *priv = device->shader_priv; + + /* We don't want shader constant dirtification to be an O(contexts), so just dirtify the active + * context. On a context switch the old context will be fully dirtified */ + if (!context_gl || context_gl->c.device != device) + return; + + memset(priv->pshader_const_dirty + start, 1, sizeof(*priv->pshader_const_dirty) * count); + priv->highest_dirty_ps_const = max(priv->highest_dirty_ps_const, start + count); +} + +static void shader_arb_append_imm_vec4(struct wined3d_string_buffer *buffer, const float *values) +{ + char str[4][17]; + + wined3d_ftoa(values[0], str[0]); + wined3d_ftoa(values[1], str[1]); + wined3d_ftoa(values[2], str[2]); + wined3d_ftoa(values[3], str[3]); + shader_addline(buffer, "{%s, %s, %s, %s}", str[0], str[1], str[2], str[3]); +} + +/* Generate the variable & register declarations for the ARB_vertex_program output target */ +static void shader_generate_arb_declarations(const struct wined3d_shader *shader, + const struct wined3d_shader_reg_maps *reg_maps, struct wined3d_string_buffer *buffer, + const struct wined3d_gl_info *gl_info, DWORD *num_clipplanes, + const struct shader_arb_ctx_priv *ctx) +{ + DWORD i; + char pshader = shader_is_pshader_version(reg_maps->shader_version.type); + const struct wined3d_shader_lconst *lconst; + unsigned max_constantsF; + DWORD map; + + /* In pixel shaders, all private constants are program local, we don't need anything + * from program.env. Thus we can advertise the full set of constants in pixel shaders. + * If we need a private constant the GL implementation will squeeze it in somewhere + * + * With vertex shaders we need the posFixup and on some GL implementations 4 helper + * immediate values. The posFixup is loaded using program.env for now, so always + * subtract one from the number of constants. If the shader uses indirect addressing, + * account for the helper const too because we have to declare all available d3d constants + * and don't know which are actually used. + */ + if (pshader) + { + max_constantsF = gl_info->limits.arb_ps_native_constants; + /* 24 is the minimum MAX_PROGRAM_ENV_PARAMETERS_ARB value. */ + if (max_constantsF < 24) + max_constantsF = gl_info->limits.arb_ps_float_constants; + } + else + { + const struct arb_vshader_private *shader_data = shader->backend_data; + max_constantsF = gl_info->limits.arb_vs_native_constants; + /* 96 is the minimum MAX_PROGRAM_ENV_PARAMETERS_ARB value. + * Also prevents max_constantsF from becoming less than 0 and + * wrapping . */ + if (max_constantsF < 96) + max_constantsF = gl_info->limits.arb_vs_float_constants; + + if (reg_maps->usesrelconstF) + { + DWORD highest_constf = 0, clip_limit; + + max_constantsF -= reserved_vs_const(shader_data, reg_maps, gl_info); + max_constantsF -= wined3d_popcount(reg_maps->integer_constants); + max_constantsF -= gl_info->reserved_arb_constants; + + for (i = 0; i < shader->limits->constant_float; ++i) + { + if (wined3d_bitmap_is_set(reg_maps->constf, i)) + highest_constf = i; + } + + if(use_nv_clip(gl_info) && ctx->target_version >= NV2) + { + if(ctx->cur_vs_args->super.clip_enabled) + clip_limit = gl_info->limits.user_clip_distances; + else + clip_limit = 0; + } + else + { + unsigned int mask = ctx->cur_vs_args->clip.boolclip.clipplane_mask; + clip_limit = min(wined3d_popcount(mask), 4); + } + *num_clipplanes = min(clip_limit, max_constantsF - highest_constf - 1); + max_constantsF -= *num_clipplanes; + if(*num_clipplanes < clip_limit) + { + WARN("Only %u clip planes out of %u enabled.\n", *num_clipplanes, + gl_info->limits.user_clip_distances); + } + } + else + { + if (ctx->target_version >= NV2) + *num_clipplanes = gl_info->limits.user_clip_distances; + else + *num_clipplanes = min(gl_info->limits.user_clip_distances, 4); + } + } + + for (i = 0, map = reg_maps->temporary; map; map >>= 1, ++i) + { + if (map & 1) shader_addline(buffer, "TEMP R%u;\n", i); + } + + for (i = 0, map = reg_maps->address; map; map >>= 1, ++i) + { + if (map & 1) shader_addline(buffer, "ADDRESS A%u;\n", i); + } + + if (pshader && reg_maps->shader_version.major == 1 && reg_maps->shader_version.minor <= 3) + { + for (i = 0, map = reg_maps->texcoord; map; map >>= 1, ++i) + { + if (map & 1) shader_addline(buffer, "TEMP T%u;\n", i); + } + } + + if (!shader->load_local_constsF) + { + LIST_FOR_EACH_ENTRY(lconst, &shader->constantsF, struct wined3d_shader_lconst, entry) + { + const float *value; + value = (const float *)lconst->value; + shader_addline(buffer, "PARAM C%u = ", lconst->idx); + shader_arb_append_imm_vec4(buffer, value); + shader_addline(buffer, ";\n"); + } + } + + /* After subtracting privately used constants from the hardware limit(they are loaded as + * local constants), make sure the shader doesn't violate the env constant limit + */ + if (pshader) + { + max_constantsF = min(max_constantsF, gl_info->limits.arb_ps_float_constants); + } + else + { + max_constantsF = min(max_constantsF, gl_info->limits.arb_vs_float_constants); + } + + /* Avoid declaring more constants than needed */ + max_constantsF = min(max_constantsF, shader->limits->constant_float); + + /* we use the array-based constants array if the local constants are marked for loading, + * because then we use indirect addressing, or when the local constant list is empty, + * because then we don't know if we're using indirect addressing or not. If we're hardcoding + * local constants do not declare the loaded constants as an array because ARB compilers usually + * do not optimize unused constants away + */ + if (reg_maps->usesrelconstF) + { + /* Need to PARAM the environment parameters (constants) so we can use relative addressing */ + shader_addline(buffer, "PARAM C[%d] = { program.env[0..%d] };\n", + max_constantsF, max_constantsF - 1); + } + else + { + for (i = 0; i < max_constantsF; ++i) + { + if (!shader_constant_is_local(shader, i) && wined3d_extract_bits(reg_maps->constf, i, 1)) + { + shader_addline(buffer, "PARAM C%d = program.env[%d];\n",i, i); + } + } + } +} + +static const char * const shift_tab[] = { + "dummy", /* 0 (none) */ + "coefmul.x", /* 1 (x2) */ + "coefmul.y", /* 2 (x4) */ + "coefmul.z", /* 3 (x8) */ + "coefmul.w", /* 4 (x16) */ + "dummy", /* 5 (x32) */ + "dummy", /* 6 (x64) */ + "dummy", /* 7 (x128) */ + "dummy", /* 8 (d256) */ + "dummy", /* 9 (d128) */ + "dummy", /* 10 (d64) */ + "dummy", /* 11 (d32) */ + "coefdiv.w", /* 12 (d16) */ + "coefdiv.z", /* 13 (d8) */ + "coefdiv.y", /* 14 (d4) */ + "coefdiv.x" /* 15 (d2) */ +}; + +static void shader_arb_get_write_mask(const struct wined3d_shader_instruction *ins, + const struct wined3d_shader_dst_param *dst, char *write_mask) +{ + char *ptr = write_mask; + + if (dst->write_mask != WINED3DSP_WRITEMASK_ALL) + { + *ptr++ = '.'; + if (dst->write_mask & WINED3DSP_WRITEMASK_0) *ptr++ = 'x'; + if (dst->write_mask & WINED3DSP_WRITEMASK_1) *ptr++ = 'y'; + if (dst->write_mask & WINED3DSP_WRITEMASK_2) *ptr++ = 'z'; + if (dst->write_mask & WINED3DSP_WRITEMASK_3) *ptr++ = 'w'; + } + + *ptr = '\0'; +} + +static void shader_arb_get_swizzle(const struct wined3d_shader_src_param *param, BOOL fixup, char *swizzle_str) +{ + /* For registers of type WINED3DDECLTYPE_D3DCOLOR, data is stored as "bgra", + * but addressed as "rgba". To fix this we need to swap the register's x + * and z components. */ + const char *swizzle_chars = fixup ? "zyxw" : "xyzw"; + char *ptr = swizzle_str; + + /* swizzle bits fields: wwzzyyxx */ + DWORD swizzle = param->swizzle; + DWORD swizzle_x = swizzle & 0x03; + DWORD swizzle_y = (swizzle >> 2) & 0x03; + DWORD swizzle_z = (swizzle >> 4) & 0x03; + DWORD swizzle_w = (swizzle >> 6) & 0x03; + + /* If the swizzle is the default swizzle (ie, "xyzw"), we don't need to + * generate a swizzle string. Unless we need to our own swizzling. */ + if (swizzle != WINED3DSP_NOSWIZZLE || fixup) + { + *ptr++ = '.'; + if (swizzle_x == swizzle_y && swizzle_x == swizzle_z && swizzle_x == swizzle_w) { + *ptr++ = swizzle_chars[swizzle_x]; + } else { + *ptr++ = swizzle_chars[swizzle_x]; + *ptr++ = swizzle_chars[swizzle_y]; + *ptr++ = swizzle_chars[swizzle_z]; + *ptr++ = swizzle_chars[swizzle_w]; + } + } + + *ptr = '\0'; +} + +static void shader_arb_request_a0(const struct wined3d_shader_instruction *ins, const char *src) +{ + struct shader_arb_ctx_priv *priv = ins->ctx->backend_data; + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + + if (!strcmp(priv->addr_reg, src)) + return; + + snprintf(priv->addr_reg, sizeof(priv->addr_reg), "%s", src); + shader_addline(buffer, "ARL A0.x, %s;\n", src); +} + +static void shader_arb_get_src_param(const struct wined3d_shader_instruction *ins, + const struct wined3d_shader_src_param *src, unsigned int tmpreg, char *outregstr); + +static void shader_arb_get_register_name(const struct wined3d_shader_instruction *ins, + const struct wined3d_shader_register *reg, char *register_name, BOOL *is_color) +{ + /* oPos, oFog and oPts in D3D */ + static const char * const rastout_reg_names[] = {"TMP_OUT", "TMP_FOGCOORD", "result.pointsize"}; + const struct wined3d_shader *shader = ins->ctx->shader; + const struct wined3d_shader_reg_maps *reg_maps = ins->ctx->reg_maps; + BOOL pshader = shader_is_pshader_version(reg_maps->shader_version.type); + struct shader_arb_ctx_priv *ctx = ins->ctx->backend_data; + + *is_color = FALSE; + + switch (reg->type) + { + case WINED3DSPR_TEMP: + sprintf(register_name, "R%u", reg->idx[0].offset); + break; + + case WINED3DSPR_INPUT: + if (pshader) + { + if (reg_maps->shader_version.major < 3) + { + if (!reg->idx[0].offset) + strcpy(register_name, "fragment.color.primary"); + else + strcpy(register_name, "fragment.color.secondary"); + } + else + { + if (reg->idx[0].rel_addr) + { + char rel_reg[50]; + shader_arb_get_src_param(ins, reg->idx[0].rel_addr, 0, rel_reg); + + if (!strcmp(rel_reg, "**aL_emul**")) + { + DWORD idx = ctx->aL + reg->idx[0].offset; + if(idx < MAX_REG_INPUT) + { + strcpy(register_name, ctx->ps_input[idx]); + } + else + { + ERR("Pixel shader input register out of bounds: %u\n", idx); + sprintf(register_name, "out_of_bounds_%u", idx); + } + } + else if (reg_maps->input_registers & 0x0300) + { + /* There are two ways basically: + * + * 1) Use the unrolling code that is used for loop emulation and unroll the loop. + * That means trouble if the loop also contains a breakc or if the control values + * aren't local constants. + * 2) Generate an if block that checks if aL.y < 8, == 8 or == 9 and selects the + * source dynamically. The trouble is that we cannot simply read aL.y because it + * is an ADDRESS register. We could however push it, load .zw with a value and use + * ADAC to load the condition code register and pop it again afterwards + */ + FIXME("Relative input register addressing with more than 8 registers\n"); + + /* This is better than nothing for now */ + sprintf(register_name, "fragment.texcoord[%s + %u]", rel_reg, reg->idx[0].offset); + } + else if(ctx->cur_ps_args->super.vp_mode != WINED3D_VP_MODE_SHADER) + { + /* This is problematic because we'd have to consult the ctx->ps_input strings + * for where to find the varying. Some may be "0.0", others can be texcoords or + * colors. This needs either a pipeline replacement to make the vertex shader feed + * proper varyings, or loop unrolling + * + * For now use the texcoords and hope for the best + */ + FIXME("Non-vertex shader varying input with indirect addressing\n"); + sprintf(register_name, "fragment.texcoord[%s + %u]", rel_reg, reg->idx[0].offset); + } + else + { + /* D3D supports indirect addressing only with aL in loop registers. The loop instruction + * pulls GL_NV_fragment_program2 in + */ + sprintf(register_name, "fragment.texcoord[%s + %u]", rel_reg, reg->idx[0].offset); + } + } + else + { + if (reg->idx[0].offset < MAX_REG_INPUT) + { + strcpy(register_name, ctx->ps_input[reg->idx[0].offset]); + } + else + { + ERR("Pixel shader input register out of bounds: %u\n", reg->idx[0].offset); + sprintf(register_name, "out_of_bounds_%u", reg->idx[0].offset); + } + } + } + } + else + { + if (ctx->cur_vs_args->super.swizzle_map & (1u << reg->idx[0].offset)) + *is_color = TRUE; + sprintf(register_name, "vertex.attrib[%u]", reg->idx[0].offset); + } + break; + + case WINED3DSPR_CONST: + if (!pshader && reg->idx[0].rel_addr) + { + const struct arb_vshader_private *shader_data = shader->backend_data; + UINT rel_offset = ctx->target_version == ARB ? shader_data->rel_offset : 0; + BOOL aL = FALSE; + char rel_reg[50]; + if (reg_maps->shader_version.major < 2) + { + sprintf(rel_reg, "A0.x"); + } + else + { + shader_arb_get_src_param(ins, reg->idx[0].rel_addr, 0, rel_reg); + if (ctx->target_version == ARB) + { + if (!strcmp(rel_reg, "**aL_emul**")) + { + aL = TRUE; + } else { + shader_arb_request_a0(ins, rel_reg); + sprintf(rel_reg, "A0.x"); + } + } + } + if (aL) + sprintf(register_name, "C[%u]", ctx->aL + reg->idx[0].offset); + else if (reg->idx[0].offset >= rel_offset) + sprintf(register_name, "C[%s + %u]", rel_reg, reg->idx[0].offset - rel_offset); + else + sprintf(register_name, "C[%s - %u]", rel_reg, rel_offset - reg->idx[0].offset); + } + else + { + if (reg_maps->usesrelconstF) + sprintf(register_name, "C[%u]", reg->idx[0].offset); + else + sprintf(register_name, "C%u", reg->idx[0].offset); + } + break; + + case WINED3DSPR_TEXTURE: /* case WINED3DSPR_ADDR: */ + if (pshader) + { + if (reg_maps->shader_version.major == 1 + && reg_maps->shader_version.minor <= 3) + /* In ps <= 1.3, Tx is a temporary register as destination + * to all instructions, and as source to most instructions. + * For some instructions it is the texcoord input. Those + * instructions know about the special use. */ + sprintf(register_name, "T%u", reg->idx[0].offset); + else + /* In ps 1.4 and 2.x Tx is always a (read-only) varying. */ + sprintf(register_name, "fragment.texcoord[%u]", reg->idx[0].offset); + } + else + { + if (reg_maps->shader_version.major == 1 || ctx->target_version >= NV2) + sprintf(register_name, "A%u", reg->idx[0].offset); + else + sprintf(register_name, "A%u_SHADOW", reg->idx[0].offset); + } + break; + + case WINED3DSPR_COLOROUT: + if (ctx->ps_post_process && !reg->idx[0].offset) + { + strcpy(register_name, "TMP_COLOR"); + } + else + { + if (ctx->cur_ps_args->super.srgb_correction) + FIXME("sRGB correction on higher render targets.\n"); + if (reg_maps->rt_mask > 1) + sprintf(register_name, "result.color[%u]", reg->idx[0].offset); + else + strcpy(register_name, "result.color"); + } + break; + + case WINED3DSPR_RASTOUT: + if (reg->idx[0].offset == 1) + sprintf(register_name, "%s", ctx->fog_output); + else + sprintf(register_name, "%s", rastout_reg_names[reg->idx[0].offset]); + break; + + case WINED3DSPR_DEPTHOUT: + strcpy(register_name, "result.depth"); + break; + + case WINED3DSPR_ATTROUT: + /* case WINED3DSPR_OUTPUT: */ + if (pshader) + sprintf(register_name, "oD[%u]", reg->idx[0].offset); + else + strcpy(register_name, ctx->color_output[reg->idx[0].offset]); + break; + + case WINED3DSPR_TEXCRDOUT: + if (pshader) + sprintf(register_name, "oT[%u]", reg->idx[0].offset); + else if (reg_maps->shader_version.major < 3) + strcpy(register_name, ctx->texcrd_output[reg->idx[0].offset]); + else + strcpy(register_name, ctx->vs_output[reg->idx[0].offset]); + break; + + case WINED3DSPR_LOOP: + if(ctx->target_version >= NV2) + { + /* Pshader has an implicitly declared loop index counter A0.x that cannot be renamed */ + if(pshader) sprintf(register_name, "A0.x"); + else sprintf(register_name, "aL.y"); + } + else + { + /* Unfortunately this code cannot return the value of ctx->aL here. An immediate value + * would be valid, but if aL is used for indexing(its only use), there's likely an offset, + * thus the result would be something like C[15 + 30], which is not valid in the ARB program + * grammar. So return a marker for the emulated aL and intercept it in constant and varying + * indexing + */ + sprintf(register_name, "**aL_emul**"); + } + + break; + + case WINED3DSPR_CONSTINT: + sprintf(register_name, "I%u", reg->idx[0].offset); + break; + + case WINED3DSPR_MISCTYPE: + if (!reg->idx[0].offset) + sprintf(register_name, "vpos"); + else if (reg->idx[0].offset == 1) + sprintf(register_name, "fragment.facing.x"); + else + FIXME("Unknown MISCTYPE register index %u.\n", reg->idx[0].offset); + break; + + default: + FIXME("Unhandled register type %#x[%u].\n", reg->type, reg->idx[0].offset); + sprintf(register_name, "unrecognized_register[%u]", reg->idx[0].offset); + break; + } +} + +static void shader_arb_get_dst_param(const struct wined3d_shader_instruction *ins, + const struct wined3d_shader_dst_param *wined3d_dst, char *str) +{ + char register_name[255]; + char write_mask[6]; + BOOL is_color; + + shader_arb_get_register_name(ins, &wined3d_dst->reg, register_name, &is_color); + strcpy(str, register_name); + + shader_arb_get_write_mask(ins, wined3d_dst, write_mask); + strcat(str, write_mask); +} + +static const char *shader_arb_get_fixup_swizzle(enum fixup_channel_source channel_source) +{ + switch(channel_source) + { + case CHANNEL_SOURCE_ZERO: return "0"; + case CHANNEL_SOURCE_ONE: return "1"; + case CHANNEL_SOURCE_X: return "x"; + case CHANNEL_SOURCE_Y: return "y"; + case CHANNEL_SOURCE_Z: return "z"; + case CHANNEL_SOURCE_W: return "w"; + default: + FIXME("Unhandled channel source %#x\n", channel_source); + return "undefined"; + } +} + +struct color_fixup_masks +{ + DWORD source; + DWORD sign; +}; + +static struct color_fixup_masks calc_color_correction(struct color_fixup_desc fixup, DWORD dst_mask) +{ + struct color_fixup_masks masks = {0, 0}; + + if (is_complex_fixup(fixup)) + { + enum complex_fixup complex_fixup = get_complex_fixup(fixup); + FIXME("Complex fixup (%#x) not supported\n", complex_fixup); + return masks; + } + + if (fixup.x_source != CHANNEL_SOURCE_X) + masks.source |= WINED3DSP_WRITEMASK_0; + if (fixup.y_source != CHANNEL_SOURCE_Y) + masks.source |= WINED3DSP_WRITEMASK_1; + if (fixup.z_source != CHANNEL_SOURCE_Z) + masks.source |= WINED3DSP_WRITEMASK_2; + if (fixup.w_source != CHANNEL_SOURCE_W) + masks.source |= WINED3DSP_WRITEMASK_3; + masks.source &= dst_mask; + + if (fixup.x_sign_fixup) + masks.sign |= WINED3DSP_WRITEMASK_0; + if (fixup.y_sign_fixup) + masks.sign |= WINED3DSP_WRITEMASK_1; + if (fixup.z_sign_fixup) + masks.sign |= WINED3DSP_WRITEMASK_2; + if (fixup.w_sign_fixup) + masks.sign |= WINED3DSP_WRITEMASK_3; + masks.sign &= dst_mask; + + return masks; +} + +static void gen_color_correction(struct wined3d_string_buffer *buffer, const char *dst, + const char *src, const char *one, const char *two, + struct color_fixup_desc fixup, struct color_fixup_masks masks) +{ + const char *sign_fixup_src = dst; + + if (masks.source) + { + if (masks.sign) + sign_fixup_src = "TA"; + + shader_addline(buffer, "SWZ %s, %s, %s, %s, %s, %s;\n", sign_fixup_src, src, + shader_arb_get_fixup_swizzle(fixup.x_source), shader_arb_get_fixup_swizzle(fixup.y_source), + shader_arb_get_fixup_swizzle(fixup.z_source), shader_arb_get_fixup_swizzle(fixup.w_source)); + } + else if (masks.sign) + { + sign_fixup_src = src; + } + + if (masks.sign) + { + char reg_mask[6]; + char *ptr = reg_mask; + + if (masks.sign != WINED3DSP_WRITEMASK_ALL) + { + *ptr++ = '.'; + if (masks.sign & WINED3DSP_WRITEMASK_0) + *ptr++ = 'x'; + if (masks.sign & WINED3DSP_WRITEMASK_1) + *ptr++ = 'y'; + if (masks.sign & WINED3DSP_WRITEMASK_2) + *ptr++ = 'z'; + if (masks.sign & WINED3DSP_WRITEMASK_3) + *ptr++ = 'w'; + } + *ptr = '\0'; + + shader_addline(buffer, "MAD %s%s, %s, %s, -%s;\n", dst, reg_mask, sign_fixup_src, two, one); + } +} + +static const char *shader_arb_get_modifier(const struct wined3d_shader_instruction *ins) +{ + DWORD mod; + struct shader_arb_ctx_priv *priv = ins->ctx->backend_data; + if (!ins->dst_count) return ""; + + mod = ins->dst[0].modifiers; + + /* Silently ignore PARTIALPRECISION if it's not supported */ + if(priv->target_version == ARB) mod &= ~WINED3DSPDM_PARTIALPRECISION; + + if(mod & WINED3DSPDM_MSAMPCENTROID) + { + FIXME("Unhandled modifier WINED3DSPDM_MSAMPCENTROID\n"); + mod &= ~WINED3DSPDM_MSAMPCENTROID; + } + + switch(mod) + { + case WINED3DSPDM_SATURATE | WINED3DSPDM_PARTIALPRECISION: + return "H_SAT"; + + case WINED3DSPDM_SATURATE: + return "_SAT"; + + case WINED3DSPDM_PARTIALPRECISION: + return "H"; + + case 0: + return ""; + + default: + FIXME("Unknown modifiers 0x%08x\n", mod); + return ""; + } +} + +#define TEX_PROJ 0x1 +#define TEX_BIAS 0x2 +#define TEX_LOD 0x4 +#define TEX_DERIV 0x10 + +static void shader_hw_sample(const struct wined3d_shader_instruction *ins, DWORD sampler_idx, + const char *dst_str, const char *coord_reg, WORD flags, const char *dsx, const char *dsy) +{ + BOOL pshader = shader_is_pshader_version(ins->ctx->reg_maps->shader_version.type); + struct shader_arb_ctx_priv *priv = ins->ctx->backend_data; + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + enum wined3d_shader_resource_type resource_type; + struct color_fixup_masks masks; + const char *tex_dst = dst_str; + BOOL np2_fixup = FALSE; + const char *tex_type; + const char *mod; + + if (pshader) + { + resource_type = pixelshader_get_resource_type(ins->ctx->reg_maps, sampler_idx, + priv->cur_ps_args->super.tex_types); + } + else + { + resource_type = ins->ctx->reg_maps->resource_info[sampler_idx].type; + + /* D3D vertex shader sampler IDs are vertex samplers(0-3), not global d3d samplers */ + sampler_idx += WINED3D_MAX_FRAGMENT_SAMPLERS; + } + + switch (resource_type) + { + case WINED3D_SHADER_RESOURCE_TEXTURE_1D: + tex_type = "1D"; + break; + + case WINED3D_SHADER_RESOURCE_TEXTURE_2D: + if (pshader && priv->cur_ps_args->super.np2_fixup & (1u << sampler_idx) + && priv->gl_info->supported[ARB_TEXTURE_RECTANGLE]) + tex_type = "RECT"; + else + tex_type = "2D"; + + if (pshader) + { + if (priv->cur_np2fixup_info->super.active & (1u << sampler_idx)) + { + if (flags) FIXME("Only ordinary sampling from NP2 textures is supported.\n"); + else np2_fixup = TRUE; + } + } + break; + + case WINED3D_SHADER_RESOURCE_TEXTURE_3D: + tex_type = "3D"; + break; + + case WINED3D_SHADER_RESOURCE_TEXTURE_CUBE: + tex_type = "CUBE"; + break; + + default: + ERR("Unexpected resource type %#x.\n", resource_type); + tex_type = ""; + } + + /* TEX, TXL, TXD and TXP do not support the "H" modifier, + * so don't use shader_arb_get_modifier + */ + if(ins->dst[0].modifiers & WINED3DSPDM_SATURATE) mod = "_SAT"; + else mod = ""; + + /* Fragment samplers always have identity mapping */ + if(sampler_idx >= WINED3D_MAX_FRAGMENT_SAMPLERS) + { + sampler_idx = priv->cur_vs_args->vertex.samplers[sampler_idx - WINED3D_MAX_FRAGMENT_SAMPLERS]; + } + + if (pshader) + { + masks = calc_color_correction(priv->cur_ps_args->super.color_fixup[sampler_idx], + ins->dst[0].write_mask); + + if (masks.source || masks.sign) + tex_dst = "TA"; + } + + if (flags & TEX_DERIV) + { + if(flags & TEX_PROJ) FIXME("Projected texture sampling with custom derivatives\n"); + if(flags & TEX_BIAS) FIXME("Biased texture sampling with custom derivatives\n"); + shader_addline(buffer, "TXD%s %s, %s, %s, %s, texture[%u], %s;\n", mod, tex_dst, coord_reg, + dsx, dsy, sampler_idx, tex_type); + } + else if(flags & TEX_LOD) + { + if(flags & TEX_PROJ) FIXME("Projected texture sampling with explicit lod\n"); + if(flags & TEX_BIAS) FIXME("Biased texture sampling with explicit lod\n"); + shader_addline(buffer, "TXL%s %s, %s, texture[%u], %s;\n", mod, tex_dst, coord_reg, + sampler_idx, tex_type); + } + else if (flags & TEX_BIAS) + { + /* Shouldn't be possible, but let's check for it */ + if(flags & TEX_PROJ) FIXME("Biased and Projected texture sampling\n"); + /* TXB takes the 4th component of the source vector automatically, as d3d. Nothing more to do */ + shader_addline(buffer, "TXB%s %s, %s, texture[%u], %s;\n", mod, tex_dst, coord_reg, sampler_idx, tex_type); + } + else if (flags & TEX_PROJ) + { + shader_addline(buffer, "TXP%s %s, %s, texture[%u], %s;\n", mod, tex_dst, coord_reg, sampler_idx, tex_type); + } + else + { + if (np2_fixup) + { + const unsigned char idx = priv->cur_np2fixup_info->super.idx[sampler_idx]; + shader_addline(buffer, "MUL TA, np2fixup[%u].%s, %s;\n", idx >> 1, + (idx % 2) ? "zwxy" : "xyzw", coord_reg); + + shader_addline(buffer, "TEX%s %s, TA, texture[%u], %s;\n", mod, tex_dst, sampler_idx, tex_type); + } + else + shader_addline(buffer, "TEX%s %s, %s, texture[%u], %s;\n", mod, tex_dst, coord_reg, sampler_idx, tex_type); + } + + if (pshader) + { + gen_color_correction(buffer, dst_str, tex_dst, + arb_get_helper_value(WINED3D_SHADER_TYPE_PIXEL, ARB_ONE), + arb_get_helper_value(WINED3D_SHADER_TYPE_PIXEL, ARB_TWO), + priv->cur_ps_args->super.color_fixup[sampler_idx], masks); + } +} + +static void shader_arb_get_src_param(const struct wined3d_shader_instruction *ins, + const struct wined3d_shader_src_param *src, unsigned int tmpreg, char *outregstr) +{ + /* Generate a line that does the input modifier computation and return the input register to use */ + BOOL is_color = FALSE, insert_line; + char regstr[256]; + char swzstr[20]; + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + struct shader_arb_ctx_priv *ctx = ins->ctx->backend_data; + const char *one = arb_get_helper_value(ins->ctx->reg_maps->shader_version.type, ARB_ONE); + const char *two = arb_get_helper_value(ins->ctx->reg_maps->shader_version.type, ARB_TWO); + + /* Assume a new line will be added */ + insert_line = TRUE; + + /* Get register name */ + shader_arb_get_register_name(ins, &src->reg, regstr, &is_color); + shader_arb_get_swizzle(src, is_color, swzstr); + + switch (src->modifiers) + { + case WINED3DSPSM_NONE: + sprintf(outregstr, "%s%s", regstr, swzstr); + insert_line = FALSE; + break; + case WINED3DSPSM_NEG: + sprintf(outregstr, "-%s%s", regstr, swzstr); + insert_line = FALSE; + break; + case WINED3DSPSM_BIAS: + shader_addline(buffer, "ADD T%c, %s, -coefdiv.x;\n", 'A' + tmpreg, regstr); + break; + case WINED3DSPSM_BIASNEG: + shader_addline(buffer, "ADD T%c, -%s, coefdiv.x;\n", 'A' + tmpreg, regstr); + break; + case WINED3DSPSM_SIGN: + shader_addline(buffer, "MAD T%c, %s, %s, -%s;\n", 'A' + tmpreg, regstr, two, one); + break; + case WINED3DSPSM_SIGNNEG: + shader_addline(buffer, "MAD T%c, %s, -%s, %s;\n", 'A' + tmpreg, regstr, two, one); + break; + case WINED3DSPSM_COMP: + shader_addline(buffer, "SUB T%c, %s, %s;\n", 'A' + tmpreg, one, regstr); + break; + case WINED3DSPSM_X2: + shader_addline(buffer, "ADD T%c, %s, %s;\n", 'A' + tmpreg, regstr, regstr); + break; + case WINED3DSPSM_X2NEG: + shader_addline(buffer, "ADD T%c, -%s, -%s;\n", 'A' + tmpreg, regstr, regstr); + break; + case WINED3DSPSM_DZ: + shader_addline(buffer, "RCP T%c, %s.z;\n", 'A' + tmpreg, regstr); + shader_addline(buffer, "MUL T%c, %s, T%c;\n", 'A' + tmpreg, regstr, 'A' + tmpreg); + break; + case WINED3DSPSM_DW: + shader_addline(buffer, "RCP T%c, %s.w;\n", 'A' + tmpreg, regstr); + shader_addline(buffer, "MUL T%c, %s, T%c;\n", 'A' + tmpreg, regstr, 'A' + tmpreg); + break; + case WINED3DSPSM_ABS: + if(ctx->target_version >= NV2) { + sprintf(outregstr, "|%s%s|", regstr, swzstr); + insert_line = FALSE; + } else { + shader_addline(buffer, "ABS T%c, %s;\n", 'A' + tmpreg, regstr); + } + break; + case WINED3DSPSM_ABSNEG: + if(ctx->target_version >= NV2) { + sprintf(outregstr, "-|%s%s|", regstr, swzstr); + } else { + shader_addline(buffer, "ABS T%c, %s;\n", 'A' + tmpreg, regstr); + sprintf(outregstr, "-T%c%s", 'A' + tmpreg, swzstr); + } + insert_line = FALSE; + break; + default: + sprintf(outregstr, "%s%s", regstr, swzstr); + insert_line = FALSE; + } + + /* Return modified or original register, with swizzle */ + if (insert_line) + sprintf(outregstr, "T%c%s", 'A' + tmpreg, swzstr); +} + +static void pshader_hw_bem(const struct wined3d_shader_instruction *ins) +{ + const struct wined3d_shader_dst_param *dst = &ins->dst[0]; + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + DWORD sampler_code = dst->reg.idx[0].offset; + char dst_name[50]; + char src_name[2][50]; + + shader_arb_get_dst_param(ins, dst, dst_name); + + /* Sampling the perturbation map in Tsrc was done already, including the signedness correction if needed + * + * Keep in mind that src_name[1] can be "TB" and src_name[0] can be "TA" because modifiers like _x2 are valid + * with bem. So delay loading the first parameter until after the perturbation calculation which needs two + * temps is done. + */ + shader_arb_get_src_param(ins, &ins->src[1], 1, src_name[1]); + shader_addline(buffer, "SWZ TA, bumpenvmat%d, x, z, 0, 0;\n", sampler_code); + shader_addline(buffer, "DP3 TC.r, TA, %s;\n", src_name[1]); + shader_addline(buffer, "SWZ TA, bumpenvmat%d, y, w, 0, 0;\n", sampler_code); + shader_addline(buffer, "DP3 TC.g, TA, %s;\n", src_name[1]); + + shader_arb_get_src_param(ins, &ins->src[0], 0, src_name[0]); + shader_addline(buffer, "ADD %s, %s, TC;\n", dst_name, src_name[0]); +} + +static DWORD negate_modifiers(DWORD mod, char *extra_char) +{ + *extra_char = ' '; + switch(mod) + { + case WINED3DSPSM_NONE: return WINED3DSPSM_NEG; + case WINED3DSPSM_NEG: return WINED3DSPSM_NONE; + case WINED3DSPSM_BIAS: return WINED3DSPSM_BIASNEG; + case WINED3DSPSM_BIASNEG: return WINED3DSPSM_BIAS; + case WINED3DSPSM_SIGN: return WINED3DSPSM_SIGNNEG; + case WINED3DSPSM_SIGNNEG: return WINED3DSPSM_SIGN; + case WINED3DSPSM_COMP: *extra_char = '-'; return WINED3DSPSM_COMP; + case WINED3DSPSM_X2: return WINED3DSPSM_X2NEG; + case WINED3DSPSM_X2NEG: return WINED3DSPSM_X2; + case WINED3DSPSM_DZ: *extra_char = '-'; return WINED3DSPSM_DZ; + case WINED3DSPSM_DW: *extra_char = '-'; return WINED3DSPSM_DW; + case WINED3DSPSM_ABS: return WINED3DSPSM_ABSNEG; + case WINED3DSPSM_ABSNEG: return WINED3DSPSM_ABS; + } + FIXME("Unknown modifier %u\n", mod); + return mod; +} + +static void pshader_hw_cnd(const struct wined3d_shader_instruction *ins) +{ + const struct wined3d_shader_dst_param *dst = &ins->dst[0]; + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + char dst_name[50]; + char src_name[3][50]; + DWORD shader_version = WINED3D_SHADER_VERSION(ins->ctx->reg_maps->shader_version.major, + ins->ctx->reg_maps->shader_version.minor); + + shader_arb_get_dst_param(ins, dst, dst_name); + shader_arb_get_src_param(ins, &ins->src[1], 1, src_name[1]); + + if (shader_version <= WINED3D_SHADER_VERSION(1, 3) && ins->coissue + && ins->dst->write_mask != WINED3DSP_WRITEMASK_3) + { + shader_addline(buffer, "MOV%s %s, %s;\n", shader_arb_get_modifier(ins), dst_name, src_name[1]); + } + else + { + struct wined3d_shader_src_param src0_copy = ins->src[0]; + char extra_neg; + + /* src0 may have a negate srcmod set, so we can't blindly add "-" to the name */ + src0_copy.modifiers = negate_modifiers(src0_copy.modifiers, &extra_neg); + + shader_arb_get_src_param(ins, &src0_copy, 0, src_name[0]); + shader_arb_get_src_param(ins, &ins->src[2], 2, src_name[2]); + shader_addline(buffer, "ADD TA, %c%s, coefdiv.x;\n", extra_neg, src_name[0]); + shader_addline(buffer, "CMP%s %s, TA, %s, %s;\n", shader_arb_get_modifier(ins), + dst_name, src_name[1], src_name[2]); + } +} + +static void pshader_hw_cmp(const struct wined3d_shader_instruction *ins) +{ + const struct wined3d_shader_dst_param *dst = &ins->dst[0]; + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + char dst_name[50]; + char src_name[3][50]; + + shader_arb_get_dst_param(ins, dst, dst_name); + + /* Generate input register names (with modifiers) */ + shader_arb_get_src_param(ins, &ins->src[0], 0, src_name[0]); + shader_arb_get_src_param(ins, &ins->src[1], 1, src_name[1]); + shader_arb_get_src_param(ins, &ins->src[2], 2, src_name[2]); + + shader_addline(buffer, "CMP%s %s, %s, %s, %s;\n", shader_arb_get_modifier(ins), + dst_name, src_name[0], src_name[2], src_name[1]); +} + +/** Process the WINED3DSIO_DP2ADD instruction in ARB. + * dst = dot2(src0, src1) + src2 */ +static void pshader_hw_dp2add(const struct wined3d_shader_instruction *ins) +{ + const struct wined3d_shader_dst_param *dst = &ins->dst[0]; + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + char dst_name[50]; + char src_name[3][50]; + struct shader_arb_ctx_priv *ctx = ins->ctx->backend_data; + + shader_arb_get_dst_param(ins, dst, dst_name); + shader_arb_get_src_param(ins, &ins->src[0], 0, src_name[0]); + shader_arb_get_src_param(ins, &ins->src[2], 2, src_name[2]); + + if(ctx->target_version >= NV3) + { + /* GL_NV_fragment_program2 has a 1:1 matching instruction */ + shader_arb_get_src_param(ins, &ins->src[1], 1, src_name[1]); + shader_addline(buffer, "DP2A%s %s, %s, %s, %s;\n", shader_arb_get_modifier(ins), + dst_name, src_name[0], src_name[1], src_name[2]); + } + else if(ctx->target_version >= NV2) + { + /* dst.x = src2.?, src0.x, src1.x + src0.y * src1.y + * dst.y = src2.?, src0.x, src1.z + src0.y * src1.w + * dst.z = src2.?, src0.x, src1.x + src0.y * src1.y + * dst.z = src2.?, src0.x, src1.z + src0.y * src1.w + * + * Make sure that src1.zw = src1.xy, then we get a classic dp2add + * + * .xyxy and other swizzles that we could get with this are not valid in + * plain ARBfp, but luckily the NV extension grammar lifts this limitation. + */ + struct wined3d_shader_src_param tmp_param = ins->src[1]; + DWORD swizzle = tmp_param.swizzle & 0xf; /* Selects .xy */ + tmp_param.swizzle = swizzle | (swizzle << 4); /* Creates .xyxy */ + + shader_arb_get_src_param(ins, &tmp_param, 1, src_name[1]); + + shader_addline(buffer, "X2D%s %s, %s, %s, %s;\n", shader_arb_get_modifier(ins), + dst_name, src_name[2], src_name[0], src_name[1]); + } + else + { + shader_arb_get_src_param(ins, &ins->src[1], 1, src_name[1]); + /* Emulate a DP2 with a DP3 and 0.0. Don't use the dest as temp register, it could be src[1] or src[2] + * src_name[0] can be TA, but TA is a private temp for modifiers, so it is save to overwrite + */ + shader_addline(buffer, "MOV TA, %s;\n", src_name[0]); + shader_addline(buffer, "MOV TA.z, 0.0;\n"); + shader_addline(buffer, "DP3 TA, TA, %s;\n", src_name[1]); + shader_addline(buffer, "ADD%s %s, TA, %s;\n", shader_arb_get_modifier(ins), dst_name, src_name[2]); + } +} + +/* Map the opcode 1-to-1 to the GL code */ +static void shader_hw_map2gl(const struct wined3d_shader_instruction *ins) +{ + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + const char *instruction; + char arguments[256], dst_str[50]; + unsigned int i; + const struct wined3d_shader_dst_param *dst = &ins->dst[0]; + + switch (ins->handler_idx) + { + case WINED3DSIH_ABS: instruction = "ABS"; break; + case WINED3DSIH_ADD: instruction = "ADD"; break; + case WINED3DSIH_CRS: instruction = "XPD"; break; + case WINED3DSIH_DP3: instruction = "DP3"; break; + case WINED3DSIH_DP4: instruction = "DP4"; break; + case WINED3DSIH_DST: instruction = "DST"; break; + case WINED3DSIH_FRC: instruction = "FRC"; break; + case WINED3DSIH_LIT: instruction = "LIT"; break; + case WINED3DSIH_LRP: instruction = "LRP"; break; + case WINED3DSIH_MAD: instruction = "MAD"; break; + case WINED3DSIH_MAX: instruction = "MAX"; break; + case WINED3DSIH_MIN: instruction = "MIN"; break; + case WINED3DSIH_MOV: instruction = "MOV"; break; + case WINED3DSIH_MUL: instruction = "MUL"; break; + case WINED3DSIH_SGE: instruction = "SGE"; break; + case WINED3DSIH_SLT: instruction = "SLT"; break; + case WINED3DSIH_SUB: instruction = "SUB"; break; + case WINED3DSIH_MOVA:instruction = "ARR"; break; + case WINED3DSIH_DSX: instruction = "DDX"; break; + default: instruction = ""; + FIXME("Unhandled opcode %s.\n", debug_d3dshaderinstructionhandler(ins->handler_idx)); + break; + } + + /* Note that shader_arb_add_dst_param() adds spaces. */ + arguments[0] = '\0'; + shader_arb_get_dst_param(ins, dst, dst_str); + for (i = 0; i < ins->src_count; ++i) + { + char operand[100]; + strcat(arguments, ", "); + shader_arb_get_src_param(ins, &ins->src[i], i, operand); + strcat(arguments, operand); + } + shader_addline(buffer, "%s%s %s%s;\n", instruction, shader_arb_get_modifier(ins), dst_str, arguments); +} + +static void shader_hw_nop(const struct wined3d_shader_instruction *ins) {} + +static DWORD shader_arb_select_component(DWORD swizzle, DWORD component) +{ + return ((swizzle >> 2 * component) & 0x3) * 0x55; +} + +static void shader_hw_mov(const struct wined3d_shader_instruction *ins) +{ + const struct wined3d_shader *shader = ins->ctx->shader; + const struct wined3d_shader_reg_maps *reg_maps = ins->ctx->reg_maps; + BOOL pshader = shader_is_pshader_version(reg_maps->shader_version.type); + struct shader_arb_ctx_priv *ctx = ins->ctx->backend_data; + const char *zero = arb_get_helper_value(reg_maps->shader_version.type, ARB_ZERO); + const char *one = arb_get_helper_value(reg_maps->shader_version.type, ARB_ONE); + const char *two = arb_get_helper_value(reg_maps->shader_version.type, ARB_TWO); + + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + char src0_param[256]; + + if (ins->handler_idx == WINED3DSIH_MOVA) + { + const struct arb_vshader_private *shader_data = shader->backend_data; + char write_mask[6]; + const char *offset = arb_get_helper_value(WINED3D_SHADER_TYPE_VERTEX, ARB_VS_REL_OFFSET); + + if(ctx->target_version >= NV2) { + shader_hw_map2gl(ins); + return; + } + shader_arb_get_src_param(ins, &ins->src[0], 0, src0_param); + shader_arb_get_write_mask(ins, &ins->dst[0], write_mask); + + /* This implements the mova formula used in GLSL. The first two instructions + * prepare the sign() part. Note that it is fine to have my_sign(0.0) = 1.0 + * in this case: + * mova A0.x, 0.0 + * + * A0.x = arl(floor(abs(0.0) + 0.5) * 1.0) = floor(0.5) = 0.0 since arl does a floor + * + * The ARL is performed when A0 is used - the requested component is read from A0_SHADOW into + * A0.x. We can use the overwritten component of A0_shadow as temporary storage for the sign. + */ + shader_addline(buffer, "SGE A0_SHADOW%s, %s, %s;\n", write_mask, src0_param, zero); + shader_addline(buffer, "MAD A0_SHADOW%s, A0_SHADOW, %s, -%s;\n", write_mask, two, one); + + shader_addline(buffer, "ABS TA%s, %s;\n", write_mask, src0_param); + shader_addline(buffer, "ADD TA%s, TA, rel_addr_const.x;\n", write_mask); + shader_addline(buffer, "FLR TA%s, TA;\n", write_mask); + if (shader_data->rel_offset) + { + shader_addline(buffer, "ADD TA%s, TA, %s;\n", write_mask, offset); + } + shader_addline(buffer, "MUL A0_SHADOW%s, TA, A0_SHADOW;\n", write_mask); + + ((struct shader_arb_ctx_priv *)ins->ctx->backend_data)->addr_reg[0] = '\0'; + } + else if (reg_maps->shader_version.major == 1 + && !shader_is_pshader_version(reg_maps->shader_version.type) + && ins->dst[0].reg.type == WINED3DSPR_ADDR) + { + const struct arb_vshader_private *shader_data = shader->backend_data; + src0_param[0] = '\0'; + + if (shader_data->rel_offset && ctx->target_version == ARB) + { + const char *offset = arb_get_helper_value(WINED3D_SHADER_TYPE_VERTEX, ARB_VS_REL_OFFSET); + shader_arb_get_src_param(ins, &ins->src[0], 0, src0_param); + shader_addline(buffer, "ADD TA.x, %s, %s;\n", src0_param, offset); + shader_addline(buffer, "ARL A0.x, TA.x;\n"); + } + else + { + /* Apple's ARB_vertex_program implementation does not accept an ARL source argument + * with more than one component. Thus replicate the first source argument over all + * 4 components. For example, .xyzw -> .x (or better: .xxxx), .zwxy -> .z, etc) */ + struct wined3d_shader_src_param tmp_src = ins->src[0]; + tmp_src.swizzle = shader_arb_select_component(tmp_src.swizzle, 0); + shader_arb_get_src_param(ins, &tmp_src, 0, src0_param); + shader_addline(buffer, "ARL A0.x, %s;\n", src0_param); + } + } + else if (ins->dst[0].reg.type == WINED3DSPR_COLOROUT && !ins->dst[0].reg.idx[0].offset && pshader) + { + if (ctx->ps_post_process && shader->u.ps.color0_mov) + { + shader_addline(buffer, "#mov handled in srgb write or fog code\n"); + return; + } + shader_hw_map2gl(ins); + } + else + { + shader_hw_map2gl(ins); + } +} + +static void pshader_hw_texkill(const struct wined3d_shader_instruction *ins) +{ + const struct wined3d_shader_dst_param *dst = &ins->dst[0]; + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + char reg_dest[40]; + + /* No swizzles are allowed in d3d's texkill. PS 1.x ignores the 4th component as documented, + * but >= 2.0 honors it (undocumented, but tested by the d3d9 testsuite) + */ + shader_arb_get_dst_param(ins, dst, reg_dest); + + if (ins->ctx->reg_maps->shader_version.major >= 2) + { + const char *kilsrc = "TA"; + BOOL is_color; + + shader_arb_get_register_name(ins, &dst->reg, reg_dest, &is_color); + if(dst->write_mask == WINED3DSP_WRITEMASK_ALL) + { + kilsrc = reg_dest; + } + else + { + /* Sigh. KIL doesn't support swizzles/writemasks. KIL passes a writemask, but ".xy" for example + * is not valid as a swizzle in ARB (needs ".xyyy"). Use SWZ to load the register properly, and set + * masked out components to 0(won't kill) + */ + char x = '0', y = '0', z = '0', w = '0'; + if(dst->write_mask & WINED3DSP_WRITEMASK_0) x = 'x'; + if(dst->write_mask & WINED3DSP_WRITEMASK_1) y = 'y'; + if(dst->write_mask & WINED3DSP_WRITEMASK_2) z = 'z'; + if(dst->write_mask & WINED3DSP_WRITEMASK_3) w = 'w'; + shader_addline(buffer, "SWZ TA, %s, %c, %c, %c, %c;\n", reg_dest, x, y, z, w); + } + shader_addline(buffer, "KIL %s;\n", kilsrc); + } + else + { + /* ARB fp doesn't like swizzles on the parameter of the KIL instruction. To mask the 4th component, + * copy the register into our general purpose TMP variable, overwrite .w and pass TMP to KIL + * + * ps_1_3 shaders use the texcoord incarnation of the Tx register. ps_1_4 shaders can use the same, + * or pass in any temporary register(in shader phase 2) + */ + if (ins->ctx->reg_maps->shader_version.minor <= 3) + sprintf(reg_dest, "fragment.texcoord[%u]", dst->reg.idx[0].offset); + else + shader_arb_get_dst_param(ins, dst, reg_dest); + shader_addline(buffer, "SWZ TA, %s, x, y, z, 1;\n", reg_dest); + shader_addline(buffer, "KIL TA;\n"); + } +} + +static void pshader_hw_tex(const struct wined3d_shader_instruction *ins) +{ + struct shader_arb_ctx_priv *priv = ins->ctx->backend_data; + const struct wined3d_shader_dst_param *dst = &ins->dst[0]; + DWORD shader_version = WINED3D_SHADER_VERSION(ins->ctx->reg_maps->shader_version.major, + ins->ctx->reg_maps->shader_version.minor); + struct wined3d_shader_src_param src; + + char reg_dest[40]; + char reg_coord[40]; + DWORD reg_sampler_code; + WORD myflags = 0; + BOOL swizzle_coord = FALSE; + + /* All versions have a destination register */ + shader_arb_get_dst_param(ins, dst, reg_dest); + + /* 1.0-1.4: Use destination register number as texture code. + 2.0+: Use provided sampler number as texture code. */ + if (shader_version < WINED3D_SHADER_VERSION(2,0)) + reg_sampler_code = dst->reg.idx[0].offset; + else + reg_sampler_code = ins->src[1].reg.idx[0].offset; + + /* 1.0-1.3: Use the texcoord varying. + 1.4+: Use provided coordinate source register. */ + if (shader_version < WINED3D_SHADER_VERSION(1,4)) + sprintf(reg_coord, "fragment.texcoord[%u]", reg_sampler_code); + else { + /* TEX is the only instruction that can handle DW and DZ natively */ + src = ins->src[0]; + if(src.modifiers == WINED3DSPSM_DW) src.modifiers = WINED3DSPSM_NONE; + if(src.modifiers == WINED3DSPSM_DZ) src.modifiers = WINED3DSPSM_NONE; + shader_arb_get_src_param(ins, &src, 0, reg_coord); + } + + /* projection flag: + * 1.1, 1.2, 1.3: Use WINED3D_TSS_TEXTURETRANSFORMFLAGS + * 1.4: Use WINED3DSPSM_DZ or WINED3DSPSM_DW on src[0] + * 2.0+: Use WINED3DSI_TEXLD_PROJECT on the opcode + */ + if (shader_version < WINED3D_SHADER_VERSION(1,4)) + { + DWORD flags = 0; + if (reg_sampler_code < WINED3D_MAX_TEXTURES) + flags = priv->cur_ps_args->super.tex_transform >> reg_sampler_code * WINED3D_PSARGS_TEXTRANSFORM_SHIFT; + if (flags & WINED3D_PSARGS_PROJECTED) + { + myflags |= TEX_PROJ; + if ((flags & ~WINED3D_PSARGS_PROJECTED) == WINED3D_TTFF_COUNT3) + swizzle_coord = TRUE; + } + } + else if (shader_version < WINED3D_SHADER_VERSION(2,0)) + { + enum wined3d_shader_src_modifier src_mod = ins->src[0].modifiers; + if (src_mod == WINED3DSPSM_DZ) + { + swizzle_coord = TRUE; + myflags |= TEX_PROJ; + } else if(src_mod == WINED3DSPSM_DW) { + myflags |= TEX_PROJ; + } + } else { + if (ins->flags & WINED3DSI_TEXLD_PROJECT) myflags |= TEX_PROJ; + if (ins->flags & WINED3DSI_TEXLD_BIAS) myflags |= TEX_BIAS; + } + + if (swizzle_coord) + { + /* TXP cannot handle DZ natively, so move the z coordinate to .w. + * reg_coord is a read-only varying register, so we need a temp reg */ + shader_addline(ins->ctx->buffer, "SWZ TA, %s, x, y, z, z;\n", reg_coord); + strcpy(reg_coord, "TA"); + } + + shader_hw_sample(ins, reg_sampler_code, reg_dest, reg_coord, myflags, NULL, NULL); +} + +static void pshader_hw_texcoord(const struct wined3d_shader_instruction *ins) +{ + const struct wined3d_shader_dst_param *dst = &ins->dst[0]; + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + DWORD shader_version = WINED3D_SHADER_VERSION(ins->ctx->reg_maps->shader_version.major, + ins->ctx->reg_maps->shader_version.minor); + char dst_str[50]; + + if (shader_version < WINED3D_SHADER_VERSION(1,4)) + { + DWORD reg = dst->reg.idx[0].offset; + + shader_arb_get_dst_param(ins, &ins->dst[0], dst_str); + shader_addline(buffer, "MOV_SAT %s, fragment.texcoord[%u];\n", dst_str, reg); + } else { + char reg_src[40]; + + shader_arb_get_src_param(ins, &ins->src[0], 0, reg_src); + shader_arb_get_dst_param(ins, &ins->dst[0], dst_str); + shader_addline(buffer, "MOV %s, %s;\n", dst_str, reg_src); + } +} + +static void pshader_hw_texreg2ar(const struct wined3d_shader_instruction *ins) +{ + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + DWORD flags = 0; + + DWORD reg1 = ins->dst[0].reg.idx[0].offset; + char dst_str[50]; + char src_str[50]; + + /* Note that texreg2ar treats Tx as a temporary register, not as a varying */ + shader_arb_get_dst_param(ins, &ins->dst[0], dst_str); + shader_arb_get_src_param(ins, &ins->src[0], 0, src_str); + /* Move .x first in case src_str is "TA" */ + shader_addline(buffer, "MOV TA.y, %s.x;\n", src_str); + shader_addline(buffer, "MOV TA.x, %s.w;\n", src_str); + if (reg1 < WINED3D_MAX_TEXTURES) + { + struct shader_arb_ctx_priv *priv = ins->ctx->backend_data; + flags = priv->cur_ps_args->super.tex_transform >> reg1 * WINED3D_PSARGS_TEXTRANSFORM_SHIFT; + } + shader_hw_sample(ins, reg1, dst_str, "TA", flags & WINED3D_PSARGS_PROJECTED ? TEX_PROJ : 0, NULL, NULL); +} + +static void pshader_hw_texreg2gb(const struct wined3d_shader_instruction *ins) +{ + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + + DWORD reg1 = ins->dst[0].reg.idx[0].offset; + char dst_str[50]; + char src_str[50]; + + /* Note that texreg2gb treats Tx as a temporary register, not as a varying */ + shader_arb_get_dst_param(ins, &ins->dst[0], dst_str); + shader_arb_get_src_param(ins, &ins->src[0], 0, src_str); + shader_addline(buffer, "MOV TA.x, %s.y;\n", src_str); + shader_addline(buffer, "MOV TA.y, %s.z;\n", src_str); + shader_hw_sample(ins, reg1, dst_str, "TA", 0, NULL, NULL); +} + +static void pshader_hw_texreg2rgb(const struct wined3d_shader_instruction *ins) +{ + DWORD reg1 = ins->dst[0].reg.idx[0].offset; + char dst_str[50]; + char src_str[50]; + + /* Note that texreg2rg treats Tx as a temporary register, not as a varying */ + shader_arb_get_dst_param(ins, &ins->dst[0], dst_str); + shader_arb_get_src_param(ins, &ins->src[0], 0, src_str); + shader_hw_sample(ins, reg1, dst_str, src_str, 0, NULL, NULL); +} + +static void pshader_hw_texbem(const struct wined3d_shader_instruction *ins) +{ + struct shader_arb_ctx_priv *priv = ins->ctx->backend_data; + const struct wined3d_shader_dst_param *dst = &ins->dst[0]; + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + char reg_coord[40], dst_reg[50], src_reg[50]; + DWORD reg_dest_code; + + /* All versions have a destination register. The Tx where the texture coordinates come + * from is the varying incarnation of the texture register + */ + reg_dest_code = dst->reg.idx[0].offset; + shader_arb_get_dst_param(ins, &ins->dst[0], dst_reg); + shader_arb_get_src_param(ins, &ins->src[0], 0, src_reg); + sprintf(reg_coord, "fragment.texcoord[%u]", reg_dest_code); + + /* Sampling the perturbation map in Tsrc was done already, including the signedness correction if needed + * The Tx in which the perturbation map is stored is the tempreg incarnation of the texture register + * + * GL_NV_fragment_program_option could handle this in one instruction via X2D: + * X2D TA.xy, fragment.texcoord, T%u, bumpenvmat%u.xzyw + * + * However, the NV extensions are never enabled for <= 2.0 shaders because of the performance penalty that + * comes with it, and texbem is an 1.x only instruction. No 1.x instruction forces us to enable the NV + * extension. + */ + shader_addline(buffer, "SWZ TB, bumpenvmat%d, x, z, 0, 0;\n", reg_dest_code); + shader_addline(buffer, "DP3 TA.x, TB, %s;\n", src_reg); + shader_addline(buffer, "SWZ TB, bumpenvmat%d, y, w, 0, 0;\n", reg_dest_code); + shader_addline(buffer, "DP3 TA.y, TB, %s;\n", src_reg); + + /* with projective textures, texbem only divides the static texture coord, not the displacement, + * so we can't let the GL handle this. + */ + if ((priv->cur_ps_args->super.tex_transform >> reg_dest_code * WINED3D_PSARGS_TEXTRANSFORM_SHIFT) + & WINED3D_PSARGS_PROJECTED) + { + shader_addline(buffer, "RCP TB.w, %s.w;\n", reg_coord); + shader_addline(buffer, "MUL TB.xy, %s, TB.w;\n", reg_coord); + shader_addline(buffer, "ADD TA.xy, TA, TB;\n"); + } else { + shader_addline(buffer, "ADD TA.xy, TA, %s;\n", reg_coord); + } + + shader_hw_sample(ins, reg_dest_code, dst_reg, "TA", 0, NULL, NULL); + + if (ins->handler_idx == WINED3DSIH_TEXBEML) + { + /* No src swizzles are allowed, so this is ok */ + shader_addline(buffer, "MAD TA, %s.z, luminance%d.x, luminance%d.y;\n", + src_reg, reg_dest_code, reg_dest_code); + shader_addline(buffer, "MUL %s, %s, TA;\n", dst_reg, dst_reg); + } +} + +static void pshader_hw_texm3x2pad(const struct wined3d_shader_instruction *ins) +{ + DWORD reg = ins->dst[0].reg.idx[0].offset; + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + char src0_name[50], dst_name[50]; + BOOL is_color; + struct wined3d_shader_register tmp_reg = ins->dst[0].reg; + + shader_arb_get_src_param(ins, &ins->src[0], 0, src0_name); + /* The next instruction will be a texm3x2tex or texm3x2depth that writes to the uninitialized + * T register. Use this register to store the calculated vector + */ + tmp_reg.idx[0].offset = reg + 1; + shader_arb_get_register_name(ins, &tmp_reg, dst_name, &is_color); + shader_addline(buffer, "DP3 %s.x, fragment.texcoord[%u], %s;\n", dst_name, reg, src0_name); +} + +static void pshader_hw_texm3x2tex(const struct wined3d_shader_instruction *ins) +{ + struct shader_arb_ctx_priv *priv = ins->ctx->backend_data; + DWORD flags; + DWORD reg = ins->dst[0].reg.idx[0].offset; + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + char dst_str[50]; + char src0_name[50]; + char dst_reg[50]; + BOOL is_color; + + /* We know that we're writing to the uninitialized T register, so use it for temporary storage */ + shader_arb_get_register_name(ins, &ins->dst[0].reg, dst_reg, &is_color); + + shader_arb_get_dst_param(ins, &ins->dst[0], dst_str); + shader_arb_get_src_param(ins, &ins->src[0], 0, src0_name); + shader_addline(buffer, "DP3 %s.y, fragment.texcoord[%u], %s;\n", dst_reg, reg, src0_name); + flags = reg < WINED3D_MAX_TEXTURES ? priv->cur_ps_args->super.tex_transform >> reg * WINED3D_PSARGS_TEXTRANSFORM_SHIFT : 0; + shader_hw_sample(ins, reg, dst_str, dst_reg, flags & WINED3D_PSARGS_PROJECTED ? TEX_PROJ : 0, NULL, NULL); +} + +static void pshader_hw_texm3x3pad(const struct wined3d_shader_instruction *ins) +{ + struct wined3d_shader_tex_mx *tex_mx = ins->ctx->tex_mx; + DWORD reg = ins->dst[0].reg.idx[0].offset; + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + char src0_name[50], dst_name[50]; + struct wined3d_shader_register tmp_reg = ins->dst[0].reg; + BOOL is_color; + + /* There are always 2 texm3x3pad instructions followed by one texm3x3[tex,vspec, ...] instruction, with + * incrementing ins->dst[0].register_idx numbers. So the pad instruction already knows the final destination + * register, and this register is uninitialized(otherwise the assembler complains that it is 'redeclared') + */ + tmp_reg.idx[0].offset = reg + 2 - tex_mx->current_row; + shader_arb_get_register_name(ins, &tmp_reg, dst_name, &is_color); + + shader_arb_get_src_param(ins, &ins->src[0], 0, src0_name); + shader_addline(buffer, "DP3 %s.%c, fragment.texcoord[%u], %s;\n", + dst_name, 'x' + tex_mx->current_row, reg, src0_name); + tex_mx->texcoord_w[tex_mx->current_row++] = reg; +} + +static void pshader_hw_texm3x3tex(const struct wined3d_shader_instruction *ins) +{ + struct shader_arb_ctx_priv *priv = ins->ctx->backend_data; + struct wined3d_shader_tex_mx *tex_mx = ins->ctx->tex_mx; + DWORD flags; + DWORD reg = ins->dst[0].reg.idx[0].offset; + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + char dst_str[50]; + char src0_name[50], dst_name[50]; + BOOL is_color; + + shader_arb_get_register_name(ins, &ins->dst[0].reg, dst_name, &is_color); + shader_arb_get_src_param(ins, &ins->src[0], 0, src0_name); + shader_addline(buffer, "DP3 %s.z, fragment.texcoord[%u], %s;\n", dst_name, reg, src0_name); + + /* Sample the texture using the calculated coordinates */ + shader_arb_get_dst_param(ins, &ins->dst[0], dst_str); + flags = reg < WINED3D_MAX_TEXTURES ? priv->cur_ps_args->super.tex_transform >> reg * WINED3D_PSARGS_TEXTRANSFORM_SHIFT : 0; + shader_hw_sample(ins, reg, dst_str, dst_name, flags & WINED3D_PSARGS_PROJECTED ? TEX_PROJ : 0, NULL, NULL); + tex_mx->current_row = 0; +} + +static void pshader_hw_texm3x3vspec(const struct wined3d_shader_instruction *ins) +{ + struct shader_arb_ctx_priv *priv = ins->ctx->backend_data; + struct wined3d_shader_tex_mx *tex_mx = ins->ctx->tex_mx; + DWORD flags; + DWORD reg = ins->dst[0].reg.idx[0].offset; + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + char dst_str[50]; + char src0_name[50]; + char dst_reg[50]; + BOOL is_color; + + /* Get the dst reg without writemask strings. We know this register is uninitialized, so we can use all + * components for temporary data storage + */ + shader_arb_get_register_name(ins, &ins->dst[0].reg, dst_reg, &is_color); + shader_arb_get_src_param(ins, &ins->src[0], 0, src0_name); + shader_addline(buffer, "DP3 %s.z, fragment.texcoord[%u], %s;\n", dst_reg, reg, src0_name); + + /* Construct the eye-ray vector from w coordinates */ + shader_addline(buffer, "MOV TB.x, fragment.texcoord[%u].w;\n", tex_mx->texcoord_w[0]); + shader_addline(buffer, "MOV TB.y, fragment.texcoord[%u].w;\n", tex_mx->texcoord_w[1]); + shader_addline(buffer, "MOV TB.z, fragment.texcoord[%u].w;\n", reg); + + /* Calculate reflection vector + */ + shader_addline(buffer, "DP3 %s.w, %s, TB;\n", dst_reg, dst_reg); + /* The .w is ignored when sampling, so I can use TB.w to calculate dot(N, N) */ + shader_addline(buffer, "DP3 TB.w, %s, %s;\n", dst_reg, dst_reg); + shader_addline(buffer, "RCP TB.w, TB.w;\n"); + shader_addline(buffer, "MUL %s.w, %s.w, TB.w;\n", dst_reg, dst_reg); + shader_addline(buffer, "MUL %s, %s.w, %s;\n", dst_reg, dst_reg, dst_reg); + shader_addline(buffer, "MAD %s, coefmul.x, %s, -TB;\n", dst_reg, dst_reg); + + /* Sample the texture using the calculated coordinates */ + shader_arb_get_dst_param(ins, &ins->dst[0], dst_str); + flags = reg < WINED3D_MAX_TEXTURES ? priv->cur_ps_args->super.tex_transform >> reg * WINED3D_PSARGS_TEXTRANSFORM_SHIFT : 0; + shader_hw_sample(ins, reg, dst_str, dst_reg, flags & WINED3D_PSARGS_PROJECTED ? TEX_PROJ : 0, NULL, NULL); + tex_mx->current_row = 0; +} + +static void pshader_hw_texm3x3spec(const struct wined3d_shader_instruction *ins) +{ + struct shader_arb_ctx_priv *priv = ins->ctx->backend_data; + struct wined3d_shader_tex_mx *tex_mx = ins->ctx->tex_mx; + DWORD flags; + DWORD reg = ins->dst[0].reg.idx[0].offset; + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + char dst_str[50]; + char src0_name[50]; + char src1_name[50]; + char dst_reg[50]; + BOOL is_color; + + shader_arb_get_src_param(ins, &ins->src[0], 0, src0_name); + shader_arb_get_src_param(ins, &ins->src[0], 1, src1_name); + shader_arb_get_register_name(ins, &ins->dst[0].reg, dst_reg, &is_color); + /* Note: dst_reg.xy is input here, generated by two texm3x3pad instructions */ + shader_addline(buffer, "DP3 %s.z, fragment.texcoord[%u], %s;\n", dst_reg, reg, src0_name); + + /* Calculate reflection vector. + * + * dot(N, E) + * dst_reg.xyz = 2 * --------- * N - E + * dot(N, N) + * + * Which normalizes the normal vector + */ + shader_addline(buffer, "DP3 %s.w, %s, %s;\n", dst_reg, dst_reg, src1_name); + shader_addline(buffer, "DP3 TC.w, %s, %s;\n", dst_reg, dst_reg); + shader_addline(buffer, "RCP TC.w, TC.w;\n"); + shader_addline(buffer, "MUL %s.w, %s.w, TC.w;\n", dst_reg, dst_reg); + shader_addline(buffer, "MUL %s, %s.w, %s;\n", dst_reg, dst_reg, dst_reg); + shader_addline(buffer, "MAD %s, coefmul.x, %s, -%s;\n", dst_reg, dst_reg, src1_name); + + /* Sample the texture using the calculated coordinates */ + shader_arb_get_dst_param(ins, &ins->dst[0], dst_str); + flags = reg < WINED3D_MAX_TEXTURES ? priv->cur_ps_args->super.tex_transform >> reg * WINED3D_PSARGS_TEXTRANSFORM_SHIFT : 0; + shader_hw_sample(ins, reg, dst_str, dst_reg, flags & WINED3D_PSARGS_PROJECTED ? TEX_PROJ : 0, NULL, NULL); + tex_mx->current_row = 0; +} + +static void pshader_hw_texdepth(const struct wined3d_shader_instruction *ins) +{ + const struct wined3d_shader_dst_param *dst = &ins->dst[0]; + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + char dst_name[50]; + const char *zero = arb_get_helper_value(ins->ctx->reg_maps->shader_version.type, ARB_ZERO); + const char *one = arb_get_helper_value(ins->ctx->reg_maps->shader_version.type, ARB_ONE); + + /* texdepth has an implicit destination, the fragment depth value. It's only parameter, + * which is essentially an input, is the destination register because it is the first + * parameter. According to the msdn, this must be register r5, but let's keep it more flexible + * here(writemasks/swizzles are not valid on texdepth) + */ + shader_arb_get_dst_param(ins, dst, dst_name); + + /* According to the msdn, the source register(must be r5) is unusable after + * the texdepth instruction, so we're free to modify it + */ + shader_addline(buffer, "MIN %s.y, %s.y, %s;\n", dst_name, dst_name, one); + + /* How to deal with the special case dst_name.g == 0? if r != 0, then + * the r * (1 / 0) will give infinity, which is clamped to 1.0, the correct + * result. But if r = 0.0, then 0 * inf = 0, which is incorrect. + */ + shader_addline(buffer, "RCP %s.y, %s.y;\n", dst_name, dst_name); + shader_addline(buffer, "MUL TA.x, %s.x, %s.y;\n", dst_name, dst_name); + shader_addline(buffer, "MIN TA.x, TA.x, %s;\n", one); + shader_addline(buffer, "MAX result.depth, TA.x, %s;\n", zero); +} + +/** Process the WINED3DSIO_TEXDP3TEX instruction in ARB: + * Take a 3-component dot product of the TexCoord[dstreg] and src, + * then perform a 1D texture lookup from stage dstregnum, place into dst. */ +static void pshader_hw_texdp3tex(const struct wined3d_shader_instruction *ins) +{ + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + DWORD sampler_idx = ins->dst[0].reg.idx[0].offset; + char src0[50]; + char dst_str[50]; + + shader_arb_get_src_param(ins, &ins->src[0], 0, src0); + shader_addline(buffer, "MOV TB, 0.0;\n"); + shader_addline(buffer, "DP3 TB.x, fragment.texcoord[%u], %s;\n", sampler_idx, src0); + + shader_arb_get_dst_param(ins, &ins->dst[0], dst_str); + shader_hw_sample(ins, sampler_idx, dst_str, "TB", 0 /* Only one coord, can't be projected */, NULL, NULL); +} + +/** Process the WINED3DSIO_TEXDP3 instruction in ARB: + * Take a 3-component dot product of the TexCoord[dstreg] and src. */ +static void pshader_hw_texdp3(const struct wined3d_shader_instruction *ins) +{ + const struct wined3d_shader_dst_param *dst = &ins->dst[0]; + char src0[50]; + char dst_str[50]; + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + + /* Handle output register */ + shader_arb_get_dst_param(ins, dst, dst_str); + shader_arb_get_src_param(ins, &ins->src[0], 0, src0); + shader_addline(buffer, "DP3 %s, fragment.texcoord[%u], %s;\n", dst_str, dst->reg.idx[0].offset, src0); +} + +/** Process the WINED3DSIO_TEXM3X3 instruction in ARB + * Perform the 3rd row of a 3x3 matrix multiply */ +static void pshader_hw_texm3x3(const struct wined3d_shader_instruction *ins) +{ + const struct wined3d_shader_dst_param *dst = &ins->dst[0]; + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + char dst_str[50], dst_name[50]; + char src0[50]; + BOOL is_color; + + shader_arb_get_dst_param(ins, dst, dst_str); + shader_arb_get_src_param(ins, &ins->src[0], 0, src0); + shader_arb_get_register_name(ins, &ins->dst[0].reg, dst_name, &is_color); + shader_addline(buffer, "DP3 %s.z, fragment.texcoord[%u], %s;\n", dst_name, dst->reg.idx[0].offset, src0); + shader_addline(buffer, "MOV %s, %s;\n", dst_str, dst_name); +} + +/** Process the WINED3DSIO_TEXM3X2DEPTH instruction in ARB: + * Last row of a 3x2 matrix multiply, use the result to calculate the depth: + * Calculate tmp0.y = TexCoord[dstreg] . src.xyz; (tmp0.x has already been calculated) + * depth = (tmp0.y == 0.0) ? 1.0 : tmp0.x / tmp0.y + */ +static void pshader_hw_texm3x2depth(const struct wined3d_shader_instruction *ins) +{ + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + const struct wined3d_shader_dst_param *dst = &ins->dst[0]; + char src0[50], dst_name[50]; + BOOL is_color; + const char *zero = arb_get_helper_value(ins->ctx->reg_maps->shader_version.type, ARB_ZERO); + const char *one = arb_get_helper_value(ins->ctx->reg_maps->shader_version.type, ARB_ONE); + + shader_arb_get_src_param(ins, &ins->src[0], 0, src0); + shader_arb_get_register_name(ins, &ins->dst[0].reg, dst_name, &is_color); + shader_addline(buffer, "DP3 %s.y, fragment.texcoord[%u], %s;\n", dst_name, dst->reg.idx[0].offset, src0); + + /* How to deal with the special case dst_name.g == 0? if r != 0, then + * the r * (1 / 0) will give infinity, which is clamped to 1.0, the correct + * result. But if r = 0.0, then 0 * inf = 0, which is incorrect. + */ + shader_addline(buffer, "RCP %s.y, %s.y;\n", dst_name, dst_name); + shader_addline(buffer, "MUL %s.x, %s.x, %s.y;\n", dst_name, dst_name, dst_name); + shader_addline(buffer, "MIN %s.x, %s.x, %s;\n", dst_name, dst_name, one); + shader_addline(buffer, "MAX result.depth, %s.x, %s;\n", dst_name, zero); +} + +/** Handles transforming all WINED3DSIO_M?x? opcodes for + Vertex/Pixel shaders to ARB_vertex_program codes */ +static void shader_hw_mnxn(const struct wined3d_shader_instruction *ins) +{ + int i; + int nComponents = 0; + struct wined3d_shader_dst_param tmp_dst = {{0}}; + struct wined3d_shader_src_param tmp_src[2] = {{{0}}}; + struct wined3d_shader_instruction tmp_ins; + + memset(&tmp_ins, 0, sizeof(tmp_ins)); + + /* Set constants for the temporary argument */ + tmp_ins.ctx = ins->ctx; + tmp_ins.dst_count = 1; + tmp_ins.dst = &tmp_dst; + tmp_ins.src_count = 2; + tmp_ins.src = tmp_src; + + switch(ins->handler_idx) + { + case WINED3DSIH_M4x4: + nComponents = 4; + tmp_ins.handler_idx = WINED3DSIH_DP4; + break; + case WINED3DSIH_M4x3: + nComponents = 3; + tmp_ins.handler_idx = WINED3DSIH_DP4; + break; + case WINED3DSIH_M3x4: + nComponents = 4; + tmp_ins.handler_idx = WINED3DSIH_DP3; + break; + case WINED3DSIH_M3x3: + nComponents = 3; + tmp_ins.handler_idx = WINED3DSIH_DP3; + break; + case WINED3DSIH_M3x2: + nComponents = 2; + tmp_ins.handler_idx = WINED3DSIH_DP3; + break; + default: + FIXME("Unhandled opcode %s.\n", debug_d3dshaderinstructionhandler(ins->handler_idx)); + break; + } + + tmp_dst = ins->dst[0]; + tmp_src[0] = ins->src[0]; + tmp_src[1] = ins->src[1]; + for (i = 0; i < nComponents; ++i) + { + tmp_dst.write_mask = WINED3DSP_WRITEMASK_0 << i; + shader_hw_map2gl(&tmp_ins); + ++tmp_src[1].reg.idx[0].offset; + } +} + +static DWORD abs_modifier(DWORD mod, BOOL *need_abs) +{ + *need_abs = FALSE; + + switch(mod) + { + case WINED3DSPSM_NONE: return WINED3DSPSM_ABS; + case WINED3DSPSM_NEG: return WINED3DSPSM_ABS; + case WINED3DSPSM_BIAS: *need_abs = TRUE; return WINED3DSPSM_BIAS; + case WINED3DSPSM_BIASNEG: *need_abs = TRUE; return WINED3DSPSM_BIASNEG; + case WINED3DSPSM_SIGN: *need_abs = TRUE; return WINED3DSPSM_SIGN; + case WINED3DSPSM_SIGNNEG: *need_abs = TRUE; return WINED3DSPSM_SIGNNEG; + case WINED3DSPSM_COMP: *need_abs = TRUE; return WINED3DSPSM_COMP; + case WINED3DSPSM_X2: *need_abs = TRUE; return WINED3DSPSM_X2; + case WINED3DSPSM_X2NEG: *need_abs = TRUE; return WINED3DSPSM_X2NEG; + case WINED3DSPSM_DZ: *need_abs = TRUE; return WINED3DSPSM_DZ; + case WINED3DSPSM_DW: *need_abs = TRUE; return WINED3DSPSM_DW; + case WINED3DSPSM_ABS: return WINED3DSPSM_ABS; + case WINED3DSPSM_ABSNEG: return WINED3DSPSM_ABS; + } + FIXME("Unknown modifier %u\n", mod); + return mod; +} + +static void shader_hw_scalar_op(const struct wined3d_shader_instruction *ins) +{ + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + const char *instruction; + struct wined3d_shader_src_param src0_copy = ins->src[0]; + BOOL need_abs = FALSE; + + char dst[50]; + char src[50]; + + switch(ins->handler_idx) + { + case WINED3DSIH_RSQ: instruction = "RSQ"; break; + case WINED3DSIH_RCP: instruction = "RCP"; break; + case WINED3DSIH_EXPP: + if (ins->ctx->reg_maps->shader_version.major < 2) + { + instruction = "EXP"; + break; + } + /* Drop through. */ + case WINED3DSIH_EXP: + instruction = "EX2"; + break; + case WINED3DSIH_LOG: + case WINED3DSIH_LOGP: + /* The precision requirements suggest that LOGP matches ARBvp's LOG + * instruction, but notice that the output of those instructions is + * different. */ + src0_copy.modifiers = abs_modifier(src0_copy.modifiers, &need_abs); + instruction = "LG2"; + break; + default: instruction = ""; + FIXME("Unhandled opcode %s.\n", debug_d3dshaderinstructionhandler(ins->handler_idx)); + break; + } + + /* Dx sdk says .x is used if no swizzle is given, but our test shows that + * .w is used. */ + src0_copy.swizzle = shader_arb_select_component(src0_copy.swizzle, 3); + + shader_arb_get_dst_param(ins, &ins->dst[0], dst); /* Destination */ + shader_arb_get_src_param(ins, &src0_copy, 0, src); + + if(need_abs) + { + shader_addline(buffer, "ABS TA.w, %s;\n", src); + shader_addline(buffer, "%s%s %s, TA.w;\n", instruction, shader_arb_get_modifier(ins), dst); + } + else + { + shader_addline(buffer, "%s%s %s, %s;\n", instruction, shader_arb_get_modifier(ins), dst, src); + } + +} + +static void shader_hw_nrm(const struct wined3d_shader_instruction *ins) +{ + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + char dst_name[50]; + char src_name[50]; + struct shader_arb_ctx_priv *priv = ins->ctx->backend_data; + BOOL pshader = shader_is_pshader_version(ins->ctx->reg_maps->shader_version.type); + const char *zero = arb_get_helper_value(ins->ctx->reg_maps->shader_version.type, ARB_ZERO); + + shader_arb_get_dst_param(ins, &ins->dst[0], dst_name); + shader_arb_get_src_param(ins, &ins->src[0], 1 /* Use TB */, src_name); + + /* In D3D, NRM of a vector with length zero returns zero. Catch this situation, as + * otherwise NRM or RSQ would return NaN */ + if(pshader && priv->target_version >= NV3) + { + /* GL_NV_fragment_program2's NRM needs protection against length zero vectors too + * + * TODO: Find out if DP3+NRM+MOV is really faster than DP3+RSQ+MUL + */ + shader_addline(buffer, "DP3C TA, %s, %s;\n", src_name, src_name); + shader_addline(buffer, "NRM%s %s, %s;\n", shader_arb_get_modifier(ins), dst_name, src_name); + shader_addline(buffer, "MOV %s (EQ), %s;\n", dst_name, zero); + } + else if(priv->target_version >= NV2) + { + shader_addline(buffer, "DP3C TA.x, %s, %s;\n", src_name, src_name); + shader_addline(buffer, "RSQ TA.x (NE), TA.x;\n"); + shader_addline(buffer, "MUL%s %s, %s, TA.x;\n", shader_arb_get_modifier(ins), dst_name, + src_name); + } + else + { + const char *one = arb_get_helper_value(ins->ctx->reg_maps->shader_version.type, ARB_ONE); + + shader_addline(buffer, "DP3 TA.x, %s, %s;\n", src_name, src_name); + /* Pass any non-zero value to RSQ if the input vector has a length of zero. The + * RSQ result doesn't matter, as long as multiplying it by 0 returns 0. + */ + shader_addline(buffer, "SGE TA.y, -TA.x, %s;\n", zero); + shader_addline(buffer, "MAD TA.x, %s, TA.y, TA.x;\n", one); + + shader_addline(buffer, "RSQ TA.x, TA.x;\n"); + /* dst.w = src[0].w * 1 / (src.x^2 + src.y^2 + src.z^2)^(1/2) according to msdn*/ + shader_addline(buffer, "MUL%s %s, %s, TA.x;\n", shader_arb_get_modifier(ins), dst_name, + src_name); + } +} + +static void shader_hw_lrp(const struct wined3d_shader_instruction *ins) +{ + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + char dst_name[50]; + char src_name[3][50]; + + /* ARB_fragment_program has a convenient LRP instruction */ + if(shader_is_pshader_version(ins->ctx->reg_maps->shader_version.type)) { + shader_hw_map2gl(ins); + return; + } + + shader_arb_get_dst_param(ins, &ins->dst[0], dst_name); + shader_arb_get_src_param(ins, &ins->src[0], 0, src_name[0]); + shader_arb_get_src_param(ins, &ins->src[1], 1, src_name[1]); + shader_arb_get_src_param(ins, &ins->src[2], 2, src_name[2]); + + shader_addline(buffer, "SUB TA, %s, %s;\n", src_name[1], src_name[2]); + shader_addline(buffer, "MAD%s %s, %s, TA, %s;\n", shader_arb_get_modifier(ins), + dst_name, src_name[0], src_name[2]); +} + +static void shader_hw_sincos(const struct wined3d_shader_instruction *ins) +{ + /* This instruction exists in ARB, but the d3d instruction takes two extra parameters which + * must contain fixed constants. So we need a separate function to filter those constants and + * can't use map2gl + */ + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + struct shader_arb_ctx_priv *priv = ins->ctx->backend_data; + const struct wined3d_shader_dst_param *dst = &ins->dst[0]; + char dst_name[50]; + char src_name0[50], src_name1[50], src_name2[50]; + BOOL is_color; + + shader_arb_get_src_param(ins, &ins->src[0], 0, src_name0); + if(shader_is_pshader_version(ins->ctx->reg_maps->shader_version.type)) { + shader_arb_get_dst_param(ins, &ins->dst[0], dst_name); + /* No modifiers are supported on SCS */ + shader_addline(buffer, "SCS %s, %s;\n", dst_name, src_name0); + + if(ins->dst[0].modifiers & WINED3DSPDM_SATURATE) + { + shader_arb_get_register_name(ins, &dst->reg, src_name0, &is_color); + shader_addline(buffer, "MOV_SAT %s, %s;\n", dst_name, src_name0); + } + } else if(priv->target_version >= NV2) { + shader_arb_get_register_name(ins, &dst->reg, dst_name, &is_color); + + /* Sincos writemask must be .x, .y or .xy */ + if(dst->write_mask & WINED3DSP_WRITEMASK_0) + shader_addline(buffer, "COS%s %s.x, %s;\n", shader_arb_get_modifier(ins), dst_name, src_name0); + if(dst->write_mask & WINED3DSP_WRITEMASK_1) + shader_addline(buffer, "SIN%s %s.y, %s;\n", shader_arb_get_modifier(ins), dst_name, src_name0); + } else { + /* Approximate sine and cosine with a taylor series, as per math textbook. The application passes 8 + * helper constants(D3DSINCOSCONST1 and D3DSINCOSCONST2) in src1 and src2. + * + * sin(x) = x - x^3/3! + x^5/5! - x^7/7! + ... + * cos(x) = 1 - x^2/2! + x^4/4! - x^6/6! + ... + * + * The constants we get are: + * + * +1 +1, -1 -1 +1 +1 -1 -1 + * ---- , ---- , ---- , ----- , ----- , ----- , ------ + * 1!*2 2!*4 3!*8 4!*16 5!*32 6!*64 7!*128 + * + * If used with x^2, x^3, x^4 etc they calculate sin(x/2) and cos(x/2): + * + * (x/2)^2 = x^2 / 4 + * (x/2)^3 = x^3 / 8 + * (x/2)^4 = x^4 / 16 + * (x/2)^5 = x^5 / 32 + * etc + * + * To get the final result: + * sin(x) = 2 * sin(x/2) * cos(x/2) + * cos(x) = cos(x/2)^2 - sin(x/2)^2 + * (from sin(x+y) and cos(x+y) rules) + * + * As per MSDN, dst.z is undefined after the operation, and so is + * dst.x and dst.y if they're masked out by the writemask. Ie + * sincos dst.y, src1, c0, c1 + * returns the sine in dst.y. dst.x and dst.z are undefined, dst.w is not touched. The assembler + * vsa.exe also stops with an error if the dest register is the same register as the source + * register. This means we can use dest.xyz as temporary storage. The assembler vsa.exe output also + * indicates that sincos consumes 8 instruction slots in vs_2_0(and, strangely, in vs_3_0). + */ + shader_arb_get_src_param(ins, &ins->src[1], 1, src_name1); + shader_arb_get_src_param(ins, &ins->src[2], 2, src_name2); + shader_arb_get_register_name(ins, &dst->reg, dst_name, &is_color); + + shader_addline(buffer, "MUL %s.x, %s, %s;\n", dst_name, src_name0, src_name0); /* x ^ 2 */ + shader_addline(buffer, "MUL TA.y, %s.x, %s;\n", dst_name, src_name0); /* x ^ 3 */ + shader_addline(buffer, "MUL %s.y, TA.y, %s;\n", dst_name, src_name0); /* x ^ 4 */ + shader_addline(buffer, "MUL TA.z, %s.y, %s;\n", dst_name, src_name0); /* x ^ 5 */ + shader_addline(buffer, "MUL %s.z, TA.z, %s;\n", dst_name, src_name0); /* x ^ 6 */ + shader_addline(buffer, "MUL TA.w, %s.z, %s;\n", dst_name, src_name0); /* x ^ 7 */ + + /* sin(x/2) + * + * Unfortunately we don't get the constants in a DP4-capable form. Is there a way to + * properly merge that with MULs in the code above? + * The swizzles .yz and xw however fit into the .yzxw swizzle added to ps_2_0. Maybe + * we can merge the sine and cosine MAD rows to calculate them together. + */ + shader_addline(buffer, "MUL TA.x, %s, %s.w;\n", src_name0, src_name2); /* x^1, +1/(1!*2) */ + shader_addline(buffer, "MAD TA.x, TA.y, %s.x, TA.x;\n", src_name2); /* -1/(3!*8) */ + shader_addline(buffer, "MAD TA.x, TA.z, %s.w, TA.x;\n", src_name1); /* +1/(5!*32) */ + shader_addline(buffer, "MAD TA.x, TA.w, %s.x, TA.x;\n", src_name1); /* -1/(7!*128) */ + + /* cos(x/2) */ + shader_addline(buffer, "MAD TA.y, %s.x, %s.y, %s.z;\n", dst_name, src_name2, src_name2); /* -1/(2!*4), +1.0 */ + shader_addline(buffer, "MAD TA.y, %s.y, %s.z, TA.y;\n", dst_name, src_name1); /* +1/(4!*16) */ + shader_addline(buffer, "MAD TA.y, %s.z, %s.y, TA.y;\n", dst_name, src_name1); /* -1/(6!*64) */ + + if(dst->write_mask & WINED3DSP_WRITEMASK_0) { + /* cos x */ + shader_addline(buffer, "MUL TA.z, TA.y, TA.y;\n"); + shader_addline(buffer, "MAD %s.x, -TA.x, TA.x, TA.z;\n", dst_name); + } + if(dst->write_mask & WINED3DSP_WRITEMASK_1) { + /* sin x */ + shader_addline(buffer, "MUL %s.y, TA.x, TA.y;\n", dst_name); + shader_addline(buffer, "ADD %s.y, %s.y, %s.y;\n", dst_name, dst_name, dst_name); + } + } +} + +static void shader_hw_sgn(const struct wined3d_shader_instruction *ins) +{ + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + char dst_name[50]; + char src_name[50]; + struct shader_arb_ctx_priv *ctx = ins->ctx->backend_data; + + shader_arb_get_dst_param(ins, &ins->dst[0], dst_name); + shader_arb_get_src_param(ins, &ins->src[0], 0, src_name); + + /* SGN is only valid in vertex shaders */ + if(ctx->target_version >= NV2) { + shader_addline(buffer, "SSG%s %s, %s;\n", shader_arb_get_modifier(ins), dst_name, src_name); + return; + } + + /* If SRC > 0.0, -SRC < SRC = TRUE, otherwise false. + * if SRC < 0.0, SRC < -SRC = TRUE. If neither is true, src = 0.0 + */ + if(ins->dst[0].modifiers & WINED3DSPDM_SATURATE) { + shader_addline(buffer, "SLT %s, -%s, %s;\n", dst_name, src_name, src_name); + } else { + /* src contains TA? Write to the dest first. This won't overwrite our destination. + * Then use TA, and calculate the final result + * + * Not reading from TA? Store the first result in TA to avoid overwriting the + * destination if src reg = dst reg + */ + if(strstr(src_name, "TA")) + { + shader_addline(buffer, "SLT %s, %s, -%s;\n", dst_name, src_name, src_name); + shader_addline(buffer, "SLT TA, -%s, %s;\n", src_name, src_name); + shader_addline(buffer, "ADD %s, %s, -TA;\n", dst_name, dst_name); + } + else + { + shader_addline(buffer, "SLT TA, -%s, %s;\n", src_name, src_name); + shader_addline(buffer, "SLT %s, %s, -%s;\n", dst_name, src_name, src_name); + shader_addline(buffer, "ADD %s, TA, -%s;\n", dst_name, dst_name); + } + } +} + +static void shader_hw_dsy(const struct wined3d_shader_instruction *ins) +{ + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + char src[50]; + char dst[50]; + char dst_name[50]; + BOOL is_color; + + shader_arb_get_dst_param(ins, &ins->dst[0], dst); + shader_arb_get_src_param(ins, &ins->src[0], 0, src); + shader_arb_get_register_name(ins, &ins->dst[0].reg, dst_name, &is_color); + + shader_addline(buffer, "DDY %s, %s;\n", dst, src); + shader_addline(buffer, "MUL%s %s, %s, ycorrection.y;\n", shader_arb_get_modifier(ins), dst, dst_name); +} + +static void shader_hw_pow(const struct wined3d_shader_instruction *ins) +{ + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + char src0[50], src1[50], dst[50]; + struct wined3d_shader_src_param src0_copy = ins->src[0]; + BOOL need_abs = FALSE; + struct shader_arb_ctx_priv *priv = ins->ctx->backend_data; + const char *one = arb_get_helper_value(ins->ctx->reg_maps->shader_version.type, ARB_ONE); + + /* POW operates on the absolute value of the input */ + src0_copy.modifiers = abs_modifier(src0_copy.modifiers, &need_abs); + + shader_arb_get_dst_param(ins, &ins->dst[0], dst); + shader_arb_get_src_param(ins, &src0_copy, 0, src0); + shader_arb_get_src_param(ins, &ins->src[1], 1, src1); + + if (need_abs) + shader_addline(buffer, "ABS TA.x, %s;\n", src0); + else + shader_addline(buffer, "MOV TA.x, %s;\n", src0); + + if (priv->target_version >= NV2) + { + shader_addline(buffer, "MOVC TA.y, %s;\n", src1); + shader_addline(buffer, "POW%s %s, TA.x, TA.y;\n", shader_arb_get_modifier(ins), dst); + shader_addline(buffer, "MOV %s (EQ.y), %s;\n", dst, one); + } + else + { + const char *zero = arb_get_helper_value(ins->ctx->reg_maps->shader_version.type, ARB_ZERO); + const char *flt_eps = arb_get_helper_value(ins->ctx->reg_maps->shader_version.type, ARB_EPS); + + shader_addline(buffer, "ABS TA.y, %s;\n", src1); + shader_addline(buffer, "SGE TA.y, -TA.y, %s;\n", zero); + /* Possibly add flt_eps to avoid getting float special values */ + shader_addline(buffer, "MAD TA.z, TA.y, %s, %s;\n", flt_eps, src1); + shader_addline(buffer, "POW%s TA.x, TA.x, TA.z;\n", shader_arb_get_modifier(ins)); + shader_addline(buffer, "MAD TA.x, -TA.x, TA.y, TA.x;\n"); + shader_addline(buffer, "MAD %s, TA.y, %s, TA.x;\n", dst, one); + } +} + +static void shader_hw_loop(const struct wined3d_shader_instruction *ins) +{ + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + char src_name[50]; + BOOL vshader = shader_is_vshader_version(ins->ctx->reg_maps->shader_version.type); + + /* src0 is aL */ + shader_arb_get_src_param(ins, &ins->src[1], 0, src_name); + + if(vshader) + { + struct shader_arb_ctx_priv *priv = ins->ctx->backend_data; + struct list *e = list_head(&priv->control_frames); + struct control_frame *control_frame = LIST_ENTRY(e, struct control_frame, entry); + + if(priv->loop_depth > 1) shader_addline(buffer, "PUSHA aL;\n"); + /* The constant loader makes sure to load -1 into iX.w */ + shader_addline(buffer, "ARLC aL, %s.xywz;\n", src_name); + shader_addline(buffer, "BRA loop_%u_end (LE.x);\n", control_frame->no.loop); + shader_addline(buffer, "loop_%u_start:\n", control_frame->no.loop); + } + else + { + shader_addline(buffer, "LOOP %s;\n", src_name); + } +} + +static void shader_hw_rep(const struct wined3d_shader_instruction *ins) +{ + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + char src_name[50]; + BOOL vshader = shader_is_vshader_version(ins->ctx->reg_maps->shader_version.type); + + shader_arb_get_src_param(ins, &ins->src[0], 0, src_name); + + /* The constant loader makes sure to load -1 into iX.w */ + if(vshader) + { + struct shader_arb_ctx_priv *priv = ins->ctx->backend_data; + struct list *e = list_head(&priv->control_frames); + struct control_frame *control_frame = LIST_ENTRY(e, struct control_frame, entry); + + if(priv->loop_depth > 1) shader_addline(buffer, "PUSHA aL;\n"); + + shader_addline(buffer, "ARLC aL, %s.xywz;\n", src_name); + shader_addline(buffer, "BRA loop_%u_end (LE.x);\n", control_frame->no.loop); + shader_addline(buffer, "loop_%u_start:\n", control_frame->no.loop); + } + else + { + shader_addline(buffer, "REP %s;\n", src_name); + } +} + +static void shader_hw_endloop(const struct wined3d_shader_instruction *ins) +{ + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + BOOL vshader = shader_is_vshader_version(ins->ctx->reg_maps->shader_version.type); + + if(vshader) + { + struct shader_arb_ctx_priv *priv = ins->ctx->backend_data; + struct list *e = list_head(&priv->control_frames); + struct control_frame *control_frame = LIST_ENTRY(e, struct control_frame, entry); + + shader_addline(buffer, "ARAC aL.xy, aL;\n"); + shader_addline(buffer, "BRA loop_%u_start (GT.x);\n", control_frame->no.loop); + shader_addline(buffer, "loop_%u_end:\n", control_frame->no.loop); + + if(priv->loop_depth > 1) shader_addline(buffer, "POPA aL;\n"); + } + else + { + shader_addline(buffer, "ENDLOOP;\n"); + } +} + +static void shader_hw_endrep(const struct wined3d_shader_instruction *ins) +{ + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + BOOL vshader = shader_is_vshader_version(ins->ctx->reg_maps->shader_version.type); + + if(vshader) + { + struct shader_arb_ctx_priv *priv = ins->ctx->backend_data; + struct list *e = list_head(&priv->control_frames); + struct control_frame *control_frame = LIST_ENTRY(e, struct control_frame, entry); + + shader_addline(buffer, "ARAC aL.xy, aL;\n"); + shader_addline(buffer, "BRA loop_%u_start (GT.x);\n", control_frame->no.loop); + shader_addline(buffer, "loop_%u_end:\n", control_frame->no.loop); + + if(priv->loop_depth > 1) shader_addline(buffer, "POPA aL;\n"); + } + else + { + shader_addline(buffer, "ENDREP;\n"); + } +} + +static const struct control_frame *find_last_loop(const struct shader_arb_ctx_priv *priv) +{ + struct control_frame *control_frame; + + LIST_FOR_EACH_ENTRY(control_frame, &priv->control_frames, struct control_frame, entry) + { + if(control_frame->type == LOOP || control_frame->type == REP) return control_frame; + } + ERR("Could not find loop for break\n"); + return NULL; +} + +static void shader_hw_break(const struct wined3d_shader_instruction *ins) +{ + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + const struct control_frame *control_frame = find_last_loop(ins->ctx->backend_data); + BOOL vshader = shader_is_vshader_version(ins->ctx->reg_maps->shader_version.type); + + if(vshader) + { + shader_addline(buffer, "BRA loop_%u_end;\n", control_frame->no.loop); + } + else + { + shader_addline(buffer, "BRK;\n"); + } +} + +static const char *get_compare(enum wined3d_shader_rel_op op) +{ + switch (op) + { + case WINED3D_SHADER_REL_OP_GT: return "GT"; + case WINED3D_SHADER_REL_OP_EQ: return "EQ"; + case WINED3D_SHADER_REL_OP_GE: return "GE"; + case WINED3D_SHADER_REL_OP_LT: return "LT"; + case WINED3D_SHADER_REL_OP_NE: return "NE"; + case WINED3D_SHADER_REL_OP_LE: return "LE"; + default: + FIXME("Unrecognized operator %#x.\n", op); + return "(\?\?)"; + } +} + +static enum wined3d_shader_rel_op invert_compare(enum wined3d_shader_rel_op op) +{ + switch (op) + { + case WINED3D_SHADER_REL_OP_GT: return WINED3D_SHADER_REL_OP_LE; + case WINED3D_SHADER_REL_OP_EQ: return WINED3D_SHADER_REL_OP_NE; + case WINED3D_SHADER_REL_OP_GE: return WINED3D_SHADER_REL_OP_LT; + case WINED3D_SHADER_REL_OP_LT: return WINED3D_SHADER_REL_OP_GE; + case WINED3D_SHADER_REL_OP_NE: return WINED3D_SHADER_REL_OP_EQ; + case WINED3D_SHADER_REL_OP_LE: return WINED3D_SHADER_REL_OP_GT; + default: + FIXME("Unrecognized operator %#x.\n", op); + return -1; + } +} + +static void shader_hw_breakc(const struct wined3d_shader_instruction *ins) +{ + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + BOOL vshader = shader_is_vshader_version(ins->ctx->reg_maps->shader_version.type); + const struct control_frame *control_frame = find_last_loop(ins->ctx->backend_data); + char src_name0[50]; + char src_name1[50]; + const char *comp = get_compare(ins->flags); + + shader_arb_get_src_param(ins, &ins->src[0], 0, src_name0); + shader_arb_get_src_param(ins, &ins->src[1], 1, src_name1); + + if(vshader) + { + /* SUBC CC, src0, src1" works only in pixel shaders, so use TA to throw + * away the subtraction result + */ + shader_addline(buffer, "SUBC TA, %s, %s;\n", src_name0, src_name1); + shader_addline(buffer, "BRA loop_%u_end (%s.x);\n", control_frame->no.loop, comp); + } + else + { + shader_addline(buffer, "SUBC TA, %s, %s;\n", src_name0, src_name1); + shader_addline(buffer, "BRK (%s.x);\n", comp); + } +} + +static void shader_hw_ifc(const struct wined3d_shader_instruction *ins) +{ + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + struct shader_arb_ctx_priv *priv = ins->ctx->backend_data; + struct list *e = list_head(&priv->control_frames); + struct control_frame *control_frame = LIST_ENTRY(e, struct control_frame, entry); + const char *comp; + char src_name0[50]; + char src_name1[50]; + BOOL vshader = shader_is_vshader_version(ins->ctx->reg_maps->shader_version.type); + + shader_arb_get_src_param(ins, &ins->src[0], 0, src_name0); + shader_arb_get_src_param(ins, &ins->src[1], 1, src_name1); + + if(vshader) + { + /* Invert the flag. We jump to the else label if the condition is NOT true */ + comp = get_compare(invert_compare(ins->flags)); + shader_addline(buffer, "SUBC TA, %s, %s;\n", src_name0, src_name1); + shader_addline(buffer, "BRA ifc_%u_else (%s.x);\n", control_frame->no.ifc, comp); + } + else + { + comp = get_compare(ins->flags); + shader_addline(buffer, "SUBC TA, %s, %s;\n", src_name0, src_name1); + shader_addline(buffer, "IF %s.x;\n", comp); + } +} + +static void shader_hw_else(const struct wined3d_shader_instruction *ins) +{ + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + struct shader_arb_ctx_priv *priv = ins->ctx->backend_data; + struct list *e = list_head(&priv->control_frames); + struct control_frame *control_frame = LIST_ENTRY(e, struct control_frame, entry); + BOOL vshader = shader_is_vshader_version(ins->ctx->reg_maps->shader_version.type); + + if(vshader) + { + shader_addline(buffer, "BRA ifc_%u_endif;\n", control_frame->no.ifc); + shader_addline(buffer, "ifc_%u_else:\n", control_frame->no.ifc); + control_frame->had_else = TRUE; + } + else + { + shader_addline(buffer, "ELSE;\n"); + } +} + +static void shader_hw_endif(const struct wined3d_shader_instruction *ins) +{ + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + struct shader_arb_ctx_priv *priv = ins->ctx->backend_data; + struct list *e = list_head(&priv->control_frames); + struct control_frame *control_frame = LIST_ENTRY(e, struct control_frame, entry); + BOOL vshader = shader_is_vshader_version(ins->ctx->reg_maps->shader_version.type); + + if(vshader) + { + if(control_frame->had_else) + { + shader_addline(buffer, "ifc_%u_endif:\n", control_frame->no.ifc); + } + else + { + shader_addline(buffer, "#No else branch. else is endif\n"); + shader_addline(buffer, "ifc_%u_else:\n", control_frame->no.ifc); + } + } + else + { + shader_addline(buffer, "ENDIF;\n"); + } +} + +static void shader_hw_texldd(const struct wined3d_shader_instruction *ins) +{ + DWORD sampler_idx = ins->src[1].reg.idx[0].offset; + char reg_dest[40]; + char reg_src[3][40]; + WORD flags = TEX_DERIV; + + shader_arb_get_dst_param(ins, &ins->dst[0], reg_dest); + shader_arb_get_src_param(ins, &ins->src[0], 0, reg_src[0]); + shader_arb_get_src_param(ins, &ins->src[2], 1, reg_src[1]); + shader_arb_get_src_param(ins, &ins->src[3], 2, reg_src[2]); + + if (ins->flags & WINED3DSI_TEXLD_PROJECT) flags |= TEX_PROJ; + if (ins->flags & WINED3DSI_TEXLD_BIAS) flags |= TEX_BIAS; + + shader_hw_sample(ins, sampler_idx, reg_dest, reg_src[0], flags, reg_src[1], reg_src[2]); +} + +static void shader_hw_texldl(const struct wined3d_shader_instruction *ins) +{ + DWORD sampler_idx = ins->src[1].reg.idx[0].offset; + char reg_dest[40]; + char reg_coord[40]; + WORD flags = TEX_LOD; + + shader_arb_get_dst_param(ins, &ins->dst[0], reg_dest); + shader_arb_get_src_param(ins, &ins->src[0], 0, reg_coord); + + if (ins->flags & WINED3DSI_TEXLD_PROJECT) flags |= TEX_PROJ; + if (ins->flags & WINED3DSI_TEXLD_BIAS) flags |= TEX_BIAS; + + shader_hw_sample(ins, sampler_idx, reg_dest, reg_coord, flags, NULL, NULL); +} + +static void shader_hw_label(const struct wined3d_shader_instruction *ins) +{ + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + struct shader_arb_ctx_priv *priv = ins->ctx->backend_data; + + priv->in_main_func = FALSE; + /* Call instructions activate the NV extensions, not labels and rets. If there is an uncalled + * subroutine, don't generate a label that will make GL complain + */ + if(priv->target_version == ARB) return; + + shader_addline(buffer, "l%u:\n", ins->src[0].reg.idx[0].offset); +} + +static void vshader_add_footer(struct shader_arb_ctx_priv *priv_ctx, + const struct arb_vshader_private *shader_data, const struct arb_vs_compile_args *args, + const struct wined3d_shader_reg_maps *reg_maps, const struct wined3d_gl_info *gl_info, + struct wined3d_string_buffer *buffer) +{ + unsigned int i; + + /* The D3DRS_FOGTABLEMODE render state defines if the shader-generated fog coord is used + * or if the fragment depth is used. If the fragment depth is used(FOGTABLEMODE != NONE), + * the fog frag coord is thrown away. If the fog frag coord is used, but not written by + * the shader, it is set to 0.0(fully fogged, since start = 1.0, end = 0.0) + */ + if (args->super.fog_src == VS_FOG_Z) + { + shader_addline(buffer, "MOV result.fogcoord, TMP_OUT.z;\n"); + } + else + { + if (!reg_maps->fog) + { + /* posFixup.x is always 1.0, so we can safely use it */ + shader_addline(buffer, "ADD result.fogcoord, posFixup.x, -posFixup.x;\n"); + } + else + { + /* Clamp fogcoord */ + const char *zero = arb_get_helper_value(reg_maps->shader_version.type, ARB_ZERO); + const char *one = arb_get_helper_value(reg_maps->shader_version.type, ARB_ONE); + + shader_addline(buffer, "MIN TMP_FOGCOORD.x, TMP_FOGCOORD.x, %s;\n", one); + shader_addline(buffer, "MAX result.fogcoord.x, TMP_FOGCOORD.x, %s;\n", zero); + } + } + + /* Clipplanes are always stored without y inversion */ + if (use_nv_clip(gl_info) && priv_ctx->target_version >= NV2) + { + if (args->super.clip_enabled) + { + for (i = 0; i < priv_ctx->vs_clipplanes; i++) + { + shader_addline(buffer, "DP4 result.clip[%u].x, TMP_OUT, state.clip[%u].plane;\n", i, i); + } + } + } + else if (args->clip.boolclip.clip_texcoord) + { + static const char component[4] = {'x', 'y', 'z', 'w'}; + unsigned int cur_clip = 0; + const char *zero = arb_get_helper_value(WINED3D_SHADER_TYPE_VERTEX, ARB_ZERO); + + for (i = 0; i < gl_info->limits.user_clip_distances; ++i) + { + if (args->clip.boolclip.clipplane_mask & (1u << i)) + { + shader_addline(buffer, "DP4 TA.%c, TMP_OUT, state.clip[%u].plane;\n", + component[cur_clip++], i); + } + } + switch (cur_clip) + { + case 0: + shader_addline(buffer, "MOV TA, %s;\n", zero); + break; + case 1: + shader_addline(buffer, "MOV TA.yzw, %s;\n", zero); + break; + case 2: + shader_addline(buffer, "MOV TA.zw, %s;\n", zero); + break; + case 3: + shader_addline(buffer, "MOV TA.w, %s;\n", zero); + break; + } + shader_addline(buffer, "MOV result.texcoord[%u], TA;\n", + args->clip.boolclip.clip_texcoord - 1); + } + + /* Write the final position. + * + * OpenGL coordinates specify the center of the pixel while d3d coords specify + * the corner. The offsets are stored in z and w in posFixup. posFixup.y contains + * 1.0 or -1.0 to turn the rendering upside down for offscreen rendering. PosFixup.x + * contains 1.0 to allow a mad, but arb vs swizzles are too restricted for that. + */ + if (!gl_info->supported[ARB_CLIP_CONTROL]) + { + shader_addline(buffer, "MUL TA, posFixup, TMP_OUT.w;\n"); + shader_addline(buffer, "ADD TMP_OUT.x, TMP_OUT.x, TA.z;\n"); + shader_addline(buffer, "MAD TMP_OUT.y, TMP_OUT.y, posFixup.y, TA.w;\n"); + + /* Z coord [0;1]->[-1;1] mapping, see comment in + * get_projection_matrix() in utils.c. */ + if (need_helper_const(shader_data, reg_maps, gl_info)) + { + const char *two = arb_get_helper_value(WINED3D_SHADER_TYPE_VERTEX, ARB_TWO); + shader_addline(buffer, "MAD TMP_OUT.z, TMP_OUT.z, %s, -TMP_OUT.w;\n", two); + } + else + { + shader_addline(buffer, "ADD TMP_OUT.z, TMP_OUT.z, TMP_OUT.z;\n"); + shader_addline(buffer, "ADD TMP_OUT.z, TMP_OUT.z, -TMP_OUT.w;\n"); + } + } + + shader_addline(buffer, "MOV result.position, TMP_OUT;\n"); + + priv_ctx->footer_written = TRUE; +} + +static void shader_hw_ret(const struct wined3d_shader_instruction *ins) +{ + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + struct shader_arb_ctx_priv *priv = ins->ctx->backend_data; + const struct wined3d_shader *shader = ins->ctx->shader; + BOOL vshader = shader_is_vshader_version(ins->ctx->reg_maps->shader_version.type); + + if(priv->target_version == ARB) return; + + if(vshader) + { + if (priv->in_main_func) vshader_add_footer(priv, shader->backend_data, + priv->cur_vs_args, ins->ctx->reg_maps, priv->gl_info, buffer); + } + + shader_addline(buffer, "RET;\n"); +} + +static void shader_hw_call(const struct wined3d_shader_instruction *ins) +{ + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + shader_addline(buffer, "CAL l%u;\n", ins->src[0].reg.idx[0].offset); +} + +static BOOL shader_arb_compile(const struct wined3d_gl_info *gl_info, GLenum target, const char *src) +{ + const char *ptr, *line; + GLint native, pos; + + if (TRACE_ON(d3d_shader)) + { + ptr = src; + while ((line = get_line(&ptr))) TRACE_(d3d_shader)(" %.*s", (int)(ptr - line), line); + } + + GL_EXTCALL(glProgramStringARB(target, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(src), src)); + checkGLcall("glProgramStringARB()"); + + if (FIXME_ON(d3d_shader)) + { + gl_info->gl_ops.gl.p_glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &pos); + if (pos != -1) + { + FIXME_(d3d_shader)("Program error at position %d: %s\n\n", pos, + debugstr_a((const char *)gl_info->gl_ops.gl.p_glGetString(GL_PROGRAM_ERROR_STRING_ARB))); + ptr = src; + while ((line = get_line(&ptr))) FIXME_(d3d_shader)(" %.*s", (int)(ptr - line), line); + FIXME_(d3d_shader)("\n"); + + return FALSE; + } + } + + if (WARN_ON(d3d_perf)) + { + GL_EXTCALL(glGetProgramivARB(target, GL_PROGRAM_UNDER_NATIVE_LIMITS_ARB, &native)); + checkGLcall("glGetProgramivARB()"); + if (!native) + WARN_(d3d_perf)("Program exceeds native resource limits.\n"); + } + + return TRUE; +} + +static void arbfp_add_sRGB_correction(struct wined3d_string_buffer *buffer, const char *fragcolor, + const char *tmp1, const char *tmp2, const char *tmp3, const char *tmp4, BOOL condcode) +{ + /* Perform sRGB write correction. See GLX_EXT_framebuffer_sRGB */ + + if(condcode) + { + /* Sigh. MOVC CC doesn't work, so use one of the temps as dummy dest */ + shader_addline(buffer, "SUBC %s, %s.x, srgb_consts1.x;\n", tmp1, fragcolor); + /* Calculate the > 0.0031308 case */ + shader_addline(buffer, "POW %s.x (GE), %s.x, srgb_consts0.x;\n", fragcolor, fragcolor); + shader_addline(buffer, "POW %s.y (GE), %s.y, srgb_consts0.x;\n", fragcolor, fragcolor); + shader_addline(buffer, "POW %s.z (GE), %s.z, srgb_consts0.x;\n", fragcolor, fragcolor); + shader_addline(buffer, "MUL %s.xyz (GE), %s, srgb_consts0.y;\n", fragcolor, fragcolor); + shader_addline(buffer, "SUB %s.xyz (GE), %s, srgb_consts0.z;\n", fragcolor, fragcolor); + /* Calculate the < case */ + shader_addline(buffer, "MUL %s.xyz (LT), srgb_consts0.w, %s;\n", fragcolor, fragcolor); + } + else + { + /* Calculate the > 0.0031308 case */ + shader_addline(buffer, "POW %s.x, %s.x, srgb_consts0.x;\n", tmp1, fragcolor); + shader_addline(buffer, "POW %s.y, %s.y, srgb_consts0.x;\n", tmp1, fragcolor); + shader_addline(buffer, "POW %s.z, %s.z, srgb_consts0.x;\n", tmp1, fragcolor); + shader_addline(buffer, "MUL %s, %s, srgb_consts0.y;\n", tmp1, tmp1); + shader_addline(buffer, "SUB %s, %s, srgb_consts0.z;\n", tmp1, tmp1); + /* Calculate the < case */ + shader_addline(buffer, "MUL %s, srgb_consts0.w, %s;\n", tmp2, fragcolor); + /* Get 1.0 / 0.0 masks for > 0.0031308 and < 0.0031308 */ + shader_addline(buffer, "SLT %s, srgb_consts1.x, %s;\n", tmp3, fragcolor); + shader_addline(buffer, "SGE %s, srgb_consts1.x, %s;\n", tmp4, fragcolor); + /* Store the components > 0.0031308 in the destination */ + shader_addline(buffer, "MUL %s.xyz, %s, %s;\n", fragcolor, tmp1, tmp3); + /* Add the components that are < 0.0031308 */ + shader_addline(buffer, "MAD %s.xyz, %s, %s, %s;\n", fragcolor, tmp2, tmp4, fragcolor); + /* Move everything into result.color at once. Nvidia hardware cannot handle partial + * result.color writes(.rgb first, then .a), or handle overwriting already written + * components. The assembler uses a temporary register in this case, which is usually + * not allocated from one of our registers that were used earlier. + */ + } + /* [0.0;1.0] clamping. Not needed, this is done implicitly */ +} + +static const DWORD *find_loop_control_values(const struct wined3d_shader *shader, DWORD idx) +{ + const struct wined3d_shader_lconst *constant; + + LIST_FOR_EACH_ENTRY(constant, &shader->constantsI, struct wined3d_shader_lconst, entry) + { + if (constant->idx == idx) + { + return constant->value; + } + } + return NULL; +} + +static void init_ps_input(const struct wined3d_shader *shader, + const struct arb_ps_compile_args *args, struct shader_arb_ctx_priv *priv) +{ + static const char * const texcoords[8] = + { + "fragment.texcoord[0]", "fragment.texcoord[1]", "fragment.texcoord[2]", "fragment.texcoord[3]", + "fragment.texcoord[4]", "fragment.texcoord[5]", "fragment.texcoord[6]", "fragment.texcoord[7]" + }; + unsigned int i; + const struct wined3d_shader_signature_element *input; + const char *semantic_name; + DWORD semantic_idx; + + if (args->super.vp_mode == WINED3D_VP_MODE_SHADER) + { + /* That one is easy. The vertex shaders provide v0-v7 in + * fragment.texcoord and v8 and v9 in fragment.color. */ + for (i = 0; i < 8; ++i) + { + priv->ps_input[i] = texcoords[i]; + } + priv->ps_input[8] = "fragment.color.primary"; + priv->ps_input[9] = "fragment.color.secondary"; + return; + } + + /* The fragment shader has to collect the varyings on its own. In any case + * properly load color0 and color1. In the case of pre-transformed + * vertices also load texture coordinates. Set other attributes to 0.0. + * + * For fixed-function this behavior is correct, according to the tests. + * For pre-transformed we'd either need a replacement shader that can load + * other attributes like BINORMAL, or load the texture coordinate + * attribute pointers to match the fragment shader signature. */ + for (i = 0; i < shader->input_signature.element_count; ++i) + { + input = &shader->input_signature.elements[i]; + if (!(semantic_name = input->semantic_name)) + continue; + semantic_idx = input->semantic_idx; + + if (shader_match_semantic(semantic_name, WINED3D_DECL_USAGE_COLOR)) + { + if (!semantic_idx) + priv->ps_input[input->register_idx] = "fragment.color.primary"; + else if (semantic_idx == 1) + priv->ps_input[input->register_idx] = "fragment.color.secondary"; + else + priv->ps_input[input->register_idx] = "0.0"; + } + else if (args->super.vp_mode == WINED3D_VP_MODE_FF) + { + priv->ps_input[input->register_idx] = "0.0"; + } + else if (shader_match_semantic(semantic_name, WINED3D_DECL_USAGE_TEXCOORD)) + { + if (semantic_idx < 8) + priv->ps_input[input->register_idx] = texcoords[semantic_idx]; + else + priv->ps_input[input->register_idx] = "0.0"; + } + else if (shader_match_semantic(semantic_name, WINED3D_DECL_USAGE_FOG)) + { + if (!semantic_idx) + priv->ps_input[input->register_idx] = "fragment.fogcoord"; + else + priv->ps_input[input->register_idx] = "0.0"; + } + else + { + priv->ps_input[input->register_idx] = "0.0"; + } + + TRACE("v%u, semantic %s%u is %s\n", input->register_idx, + semantic_name, semantic_idx, priv->ps_input[input->register_idx]); + } +} + +static void arbfp_add_linear_fog(struct wined3d_string_buffer *buffer, + const char *fragcolor, const char *tmp) +{ + shader_addline(buffer, "SUB %s.x, state.fog.params.z, fragment.fogcoord.x;\n", tmp); + shader_addline(buffer, "MUL_SAT %s.x, %s.x, state.fog.params.w;\n", tmp, tmp); + shader_addline(buffer, "LRP %s.rgb, %s.x, %s, state.fog.color;\n", fragcolor, tmp, fragcolor); +} + +/* Context activation is done by the caller. */ +static GLuint shader_arb_generate_pshader(const struct wined3d_shader *shader, + const struct wined3d_gl_info *gl_info, struct wined3d_string_buffer *buffer, + const struct arb_ps_compile_args *args, struct arb_ps_compiled_shader *compiled) +{ + const struct wined3d_shader_reg_maps *reg_maps = &shader->reg_maps; + GLuint retval; + char fragcolor[16]; + DWORD next_local = 0; + struct shader_arb_ctx_priv priv_ctx; + BOOL dcl_td = FALSE; + BOOL want_nv_prog = FALSE; + struct arb_pshader_private *shader_priv = shader->backend_data; + DWORD map; + BOOL custom_linear_fog = FALSE; + + char srgbtmp[4][4]; + char ftoa_tmp[17]; + unsigned int i, found = 0; + + for (i = 0, map = reg_maps->temporary; map; map >>= 1, ++i) + { + if (!(map & 1) + || (shader->u.ps.color0_mov && i == shader->u.ps.color0_reg) + || (reg_maps->shader_version.major < 2 && !i)) + continue; + + sprintf(srgbtmp[found], "R%u", i); + ++found; + if (found == 4) break; + } + + switch(found) { + case 0: + sprintf(srgbtmp[0], "TA"); + sprintf(srgbtmp[1], "TB"); + sprintf(srgbtmp[2], "TC"); + sprintf(srgbtmp[3], "TD"); + dcl_td = TRUE; + break; + case 1: + sprintf(srgbtmp[1], "TA"); + sprintf(srgbtmp[2], "TB"); + sprintf(srgbtmp[3], "TC"); + break; + case 2: + sprintf(srgbtmp[2], "TA"); + sprintf(srgbtmp[3], "TB"); + break; + case 3: + sprintf(srgbtmp[3], "TA"); + break; + case 4: + break; + } + + /* Create the hw ARB shader */ + memset(&priv_ctx, 0, sizeof(priv_ctx)); + priv_ctx.gl_info = gl_info; + priv_ctx.cur_ps_args = args; + priv_ctx.compiled_fprog = compiled; + priv_ctx.cur_np2fixup_info = &compiled->np2fixup_info; + init_ps_input(shader, args, &priv_ctx); + list_init(&priv_ctx.control_frames); + priv_ctx.ps_post_process = args->super.srgb_correction; + + /* Avoid enabling NV_fragment_program* if we do not need it. + * + * Enabling GL_NV_fragment_program_option causes the driver to occupy a temporary register, + * and it slows down the shader execution noticeably(about 5%). Usually our instruction emulation + * is faster than what we gain from using higher native instructions. There are some things though + * that cannot be emulated. In that case enable the extensions. + * If the extension is enabled, instruction handlers that support both ways will use it. + * + * Testing shows no performance difference between OPTION NV_fragment_program2 and NV_fragment_program. + * So enable the best we can get. + */ + if(reg_maps->usesdsx || reg_maps->usesdsy || reg_maps->loop_depth > 0 || reg_maps->usestexldd || + reg_maps->usestexldl || reg_maps->usesfacing || reg_maps->usesifc || reg_maps->usescall) + { + want_nv_prog = TRUE; + } + + shader_addline(buffer, "!!ARBfp1.0\n"); + if (want_nv_prog && gl_info->supported[NV_FRAGMENT_PROGRAM2]) + { + shader_addline(buffer, "OPTION NV_fragment_program2;\n"); + priv_ctx.target_version = NV3; + } + else if (want_nv_prog && gl_info->supported[NV_FRAGMENT_PROGRAM_OPTION]) + { + shader_addline(buffer, "OPTION NV_fragment_program;\n"); + priv_ctx.target_version = NV2; + } else { + if(want_nv_prog) + { + /* This is an error - either we're advertising the wrong shader version, or aren't enforcing some + * limits properly + */ + ERR("The shader requires instructions that are not available in plain GL_ARB_fragment_program\n"); + ERR("Try GLSL\n"); + } + priv_ctx.target_version = ARB; + } + + if (reg_maps->rt_mask > 1) + { + shader_addline(buffer, "OPTION ARB_draw_buffers;\n"); + } + + if (reg_maps->shader_version.major < 3) + { + switch (args->super.fog) + { + case WINED3D_FFP_PS_FOG_OFF: + break; + case WINED3D_FFP_PS_FOG_LINEAR: + if (gl_info->quirks & WINED3D_QUIRK_BROKEN_ARB_FOG) + { + custom_linear_fog = TRUE; + priv_ctx.ps_post_process = TRUE; + break; + } + shader_addline(buffer, "OPTION ARB_fog_linear;\n"); + break; + case WINED3D_FFP_PS_FOG_EXP: + shader_addline(buffer, "OPTION ARB_fog_exp;\n"); + break; + case WINED3D_FFP_PS_FOG_EXP2: + shader_addline(buffer, "OPTION ARB_fog_exp2;\n"); + break; + } + } + + /* For now always declare the temps. At least the Nvidia assembler optimizes completely + * unused temps away(but occupies them for the whole shader if they're used once). Always + * declaring them avoids tricky bookkeeping work + */ + shader_addline(buffer, "TEMP TA;\n"); /* Used for modifiers */ + shader_addline(buffer, "TEMP TB;\n"); /* Used for modifiers */ + shader_addline(buffer, "TEMP TC;\n"); /* Used for modifiers */ + if(dcl_td) shader_addline(buffer, "TEMP TD;\n"); /* Used for sRGB writing */ + shader_addline(buffer, "PARAM coefdiv = { 0.5, 0.25, 0.125, 0.0625 };\n"); + shader_addline(buffer, "PARAM coefmul = { 2, 4, 8, 16 };\n"); + wined3d_ftoa(eps, ftoa_tmp); + shader_addline(buffer, "PARAM ps_helper_const = { 0.0, 1.0, %s, 0.0 };\n", ftoa_tmp); + + if (reg_maps->shader_version.major < 2) + { + strcpy(fragcolor, "R0"); + } + else + { + if (priv_ctx.ps_post_process) + { + if (shader->u.ps.color0_mov) + { + sprintf(fragcolor, "R%u", shader->u.ps.color0_reg); + } + else + { + shader_addline(buffer, "TEMP TMP_COLOR;\n"); + strcpy(fragcolor, "TMP_COLOR"); + } + } else { + strcpy(fragcolor, "result.color"); + } + } + + if (args->super.srgb_correction) + { + shader_addline(buffer, "PARAM srgb_consts0 = "); + shader_arb_append_imm_vec4(buffer, &wined3d_srgb_const[0].x); + shader_addline(buffer, ";\n"); + shader_addline(buffer, "PARAM srgb_consts1 = "); + shader_arb_append_imm_vec4(buffer, &wined3d_srgb_const[1].x); + shader_addline(buffer, ";\n"); + } + + /* Base Declarations */ + shader_generate_arb_declarations(shader, reg_maps, buffer, gl_info, NULL, &priv_ctx); + + for (i = 0, map = reg_maps->bumpmat; map; map >>= 1, ++i) + { + unsigned char bump_const; + + if (!(map & 1)) continue; + + bump_const = compiled->numbumpenvmatconsts; + compiled->bumpenvmatconst[bump_const].const_num = WINED3D_CONST_NUM_UNUSED; + compiled->bumpenvmatconst[bump_const].texunit = i; + compiled->luminanceconst[bump_const].const_num = WINED3D_CONST_NUM_UNUSED; + compiled->luminanceconst[bump_const].texunit = i; + + /* We can fit the constants into the constant limit for sure because texbem, texbeml, bem and beml are only supported + * in 1.x shaders, and GL_ARB_fragment_program has a constant limit of 24 constants. So in the worst case we're loading + * 8 shader constants, 8 bump matrices and 8 luminance parameters and are perfectly fine. (No NP2 fixup on bumpmapped + * textures due to conditional NP2 restrictions) + * + * Use local constants to load the bump env parameters, not program.env. This avoids collisions with d3d constants of + * shaders in newer shader models. Since the bump env parameters have to share their space with NP2 fixup constants, + * their location is shader dependent anyway and they cannot be loaded globally. + */ + compiled->bumpenvmatconst[bump_const].const_num = next_local++; + shader_addline(buffer, "PARAM bumpenvmat%d = program.local[%d];\n", + i, compiled->bumpenvmatconst[bump_const].const_num); + compiled->numbumpenvmatconsts = bump_const + 1; + + if (!(reg_maps->luminanceparams & (1u << i))) + continue; + + compiled->luminanceconst[bump_const].const_num = next_local++; + shader_addline(buffer, "PARAM luminance%d = program.local[%d];\n", + i, compiled->luminanceconst[bump_const].const_num); + } + + for (i = 0; i < WINED3D_MAX_CONSTS_I; ++i) + { + compiled->int_consts[i] = WINED3D_CONST_NUM_UNUSED; + if (reg_maps->integer_constants & (1u << i) && priv_ctx.target_version >= NV2) + { + const DWORD *control_values = find_loop_control_values(shader, i); + + if(control_values) + { + shader_addline(buffer, "PARAM I%u = {%u, %u, %u, -1};\n", i, + control_values[0], control_values[1], control_values[2]); + } + else + { + compiled->int_consts[i] = next_local; + compiled->num_int_consts++; + shader_addline(buffer, "PARAM I%u = program.local[%u];\n", i, next_local++); + } + } + } + + if(reg_maps->vpos || reg_maps->usesdsy) + { + compiled->ycorrection = next_local; + shader_addline(buffer, "PARAM ycorrection = program.local[%u];\n", next_local++); + + if(reg_maps->vpos) + { + shader_addline(buffer, "TEMP vpos;\n"); + /* ycorrection.x: Backbuffer height(onscreen) or 0(offscreen). + * ycorrection.y: -1.0(onscreen), 1.0(offscreen) + * ycorrection.z: 1.0 + * ycorrection.w: 0.0 + */ + shader_addline(buffer, "MAD vpos, fragment.position, ycorrection.zyww, ycorrection.wxww;\n"); + shader_addline(buffer, "FLR vpos.xy, vpos;\n"); + } + } + else + { + compiled->ycorrection = WINED3D_CONST_NUM_UNUSED; + } + + /* Load constants to fixup NP2 texcoords if there are still free constants left: + * Constants (texture dimensions) for the NP2 fixup are loaded as local program parameters. This will consume + * at most 8 (WINED3D_MAX_FRAGMENT_SAMPLERS / 2) parameters, which is highly unlikely, since the application had to + * use 16 NP2 textures at the same time. In case that we run out of constants the fixup is simply not + * applied / activated. This will probably result in wrong rendering of the texture, but will save us from + * shader compilation errors and the subsequent errors when drawing with this shader. */ + if (priv_ctx.cur_ps_args->super.np2_fixup) { + unsigned char cur_fixup_sampler = 0; + + struct arb_ps_np2fixup_info* const fixup = priv_ctx.cur_np2fixup_info; + const WORD map = priv_ctx.cur_ps_args->super.np2_fixup; + const UINT max_lconsts = gl_info->limits.arb_ps_local_constants; + + fixup->offset = next_local; + fixup->super.active = 0; + + for (i = 0; i < WINED3D_MAX_FRAGMENT_SAMPLERS; ++i) + { + if (!(map & (1u << i))) + continue; + + if (fixup->offset + (cur_fixup_sampler >> 1) < max_lconsts) + { + fixup->super.active |= (1u << i); + fixup->super.idx[i] = cur_fixup_sampler++; + } + else + { + FIXME("No free constant found to load NP2 fixup data into shader. " + "Sampling from this texture will probably look wrong.\n"); + break; + } + } + + fixup->super.num_consts = (cur_fixup_sampler + 1) >> 1; + if (fixup->super.num_consts) { + shader_addline(buffer, "PARAM np2fixup[%u] = { program.env[%u..%u] };\n", + fixup->super.num_consts, fixup->offset, fixup->super.num_consts + fixup->offset - 1); + } + } + + if (shader_priv->clipplane_emulation != ~0U && args->clip) + { + shader_addline(buffer, "KIL fragment.texcoord[%u];\n", shader_priv->clipplane_emulation); + } + + /* Base Shader Body */ + if (FAILED(shader_generate_code(shader, buffer, reg_maps, &priv_ctx, NULL, NULL))) + return 0; + + if(args->super.srgb_correction) { + arbfp_add_sRGB_correction(buffer, fragcolor, srgbtmp[0], srgbtmp[1], srgbtmp[2], srgbtmp[3], + priv_ctx.target_version >= NV2); + } + + if (custom_linear_fog) + arbfp_add_linear_fog(buffer, fragcolor, "TA"); + + if(strcmp(fragcolor, "result.color")) { + shader_addline(buffer, "MOV result.color, %s;\n", fragcolor); + } + shader_addline(buffer, "END\n"); + + /* TODO: change to resource.glObjectHandle or something like that */ + GL_EXTCALL(glGenProgramsARB(1, &retval)); + + TRACE("Creating a hw pixel shader, prg=%d\n", retval); + GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, retval)); + + TRACE("Created hw pixel shader, prg=%d\n", retval); + if (!shader_arb_compile(gl_info, GL_FRAGMENT_PROGRAM_ARB, buffer->buffer)) + return 0; + + return retval; +} + +static int compare_sig(const struct wined3d_shader_signature *sig1, const struct wined3d_shader_signature *sig2) +{ + unsigned int i; + int ret; + + if (sig1->element_count != sig2->element_count) + return sig1->element_count < sig2->element_count ? -1 : 1; + + for (i = 0; i < sig1->element_count; ++i) + { + const struct wined3d_shader_signature_element *e1, *e2; + + e1 = &sig1->elements[i]; + e2 = &sig2->elements[i]; + + if (!e1->semantic_name || !e2->semantic_name) + { + /* Compare pointers, not contents. One string is NULL (element + * does not exist), the other one is not NULL. */ + if (e1->semantic_name != e2->semantic_name) + return e1->semantic_name < e2->semantic_name ? -1 : 1; + continue; + } + + if ((ret = strcmp(e1->semantic_name, e2->semantic_name))) + return ret; + if (e1->semantic_idx != e2->semantic_idx) + return e1->semantic_idx < e2->semantic_idx ? -1 : 1; + if (e1->sysval_semantic != e2->sysval_semantic) + return e1->sysval_semantic < e2->sysval_semantic ? -1 : 1; + if (e1->component_type != e2->component_type) + return e1->component_type < e2->component_type ? -1 : 1; + if (e1->register_idx != e2->register_idx) + return e1->register_idx < e2->register_idx ? -1 : 1; + if (e1->mask != e2->mask) + return e1->mask < e2->mask ? -1 : 1; + } + return 0; +} + +static void clone_sig(struct wined3d_shader_signature *new, const struct wined3d_shader_signature *sig) +{ + unsigned int i; + char *name; + + new->element_count = sig->element_count; + new->elements = heap_calloc(new->element_count, sizeof(*new->elements)); + for (i = 0; i < sig->element_count; ++i) + { + new->elements[i] = sig->elements[i]; + + if (!new->elements[i].semantic_name) + continue; + + /* Clone the semantic string */ + name = heap_alloc(strlen(sig->elements[i].semantic_name) + 1); + strcpy(name, sig->elements[i].semantic_name); + new->elements[i].semantic_name = name; + } +} + +static DWORD find_input_signature(struct shader_arb_priv *priv, const struct wined3d_shader_signature *sig) +{ + struct wine_rb_entry *entry = wine_rb_get(&priv->signature_tree, sig); + struct ps_signature *found_sig; + + if (entry) + { + found_sig = WINE_RB_ENTRY_VALUE(entry, struct ps_signature, entry); + TRACE("Found existing signature %u\n", found_sig->idx); + return found_sig->idx; + } + found_sig = heap_alloc_zero(sizeof(*found_sig)); + clone_sig(&found_sig->sig, sig); + found_sig->idx = priv->ps_sig_number++; + TRACE("New signature stored and assigned number %u\n", found_sig->idx); + if(wine_rb_put(&priv->signature_tree, sig, &found_sig->entry) == -1) + { + ERR("Failed to insert program entry.\n"); + } + return found_sig->idx; +} + +static void init_output_registers(const struct wined3d_shader *shader, + const struct wined3d_shader_signature *ps_input_sig, + struct shader_arb_ctx_priv *priv_ctx, struct arb_vs_compiled_shader *compiled) +{ + unsigned int i, j; + static const char * const texcoords[8] = + { + "result.texcoord[0]", "result.texcoord[1]", "result.texcoord[2]", "result.texcoord[3]", + "result.texcoord[4]", "result.texcoord[5]", "result.texcoord[6]", "result.texcoord[7]" + }; + /* Write generic input varyings 0 to 7 to result.texcoord[], varying 8 to result.color.primary + * and varying 9 to result.color.secondary + */ + static const char * const decl_idx_to_string[MAX_REG_INPUT] = + { + "result.texcoord[0]", "result.texcoord[1]", "result.texcoord[2]", "result.texcoord[3]", + "result.texcoord[4]", "result.texcoord[5]", "result.texcoord[6]", "result.texcoord[7]", + "result.color.primary", "result.color.secondary" + }; + + if (!ps_input_sig) + { + TRACE("Pixel shader uses builtin varyings\n"); + /* Map builtins to builtins */ + for(i = 0; i < 8; i++) + { + priv_ctx->texcrd_output[i] = texcoords[i]; + } + priv_ctx->color_output[0] = "result.color.primary"; + priv_ctx->color_output[1] = "result.color.secondary"; + priv_ctx->fog_output = "TMP_FOGCOORD"; + + /* Map declared regs to builtins. Use "TA" to /dev/null unread output */ + for (i = 0; i < shader->output_signature.element_count; ++i) + { + const struct wined3d_shader_signature_element *output = &shader->output_signature.elements[i]; + + if (!output->semantic_name) + continue; + + if (shader_match_semantic(output->semantic_name, WINED3D_DECL_USAGE_POSITION)) + { + TRACE("o%u is TMP_OUT\n", output->register_idx); + if (!output->semantic_idx) + priv_ctx->vs_output[output->register_idx] = "TMP_OUT"; + else + priv_ctx->vs_output[output->register_idx] = "TA"; + } + else if (shader_match_semantic(output->semantic_name, WINED3D_DECL_USAGE_PSIZE)) + { + TRACE("o%u is result.pointsize\n", output->register_idx); + if (!output->semantic_idx) + priv_ctx->vs_output[output->register_idx] = "result.pointsize"; + else + priv_ctx->vs_output[output->register_idx] = "TA"; + } + else if (shader_match_semantic(output->semantic_name, WINED3D_DECL_USAGE_COLOR)) + { + TRACE("o%u is result.color.?, idx %u\n", output->register_idx, output->semantic_idx); + if (!output->semantic_idx) + priv_ctx->vs_output[output->register_idx] = "result.color.primary"; + else if (output->semantic_idx == 1) + priv_ctx->vs_output[output->register_idx] = "result.color.secondary"; + else priv_ctx->vs_output[output->register_idx] = "TA"; + } + else if (shader_match_semantic(output->semantic_name, WINED3D_DECL_USAGE_TEXCOORD)) + { + TRACE("o%u is result.texcoord[%u]\n", output->register_idx, output->semantic_idx); + if (output->semantic_idx >= 8) + priv_ctx->vs_output[output->register_idx] = "TA"; + else + priv_ctx->vs_output[output->register_idx] = texcoords[output->semantic_idx]; + } + else if (shader_match_semantic(output->semantic_name, WINED3D_DECL_USAGE_FOG)) + { + TRACE("o%u is result.fogcoord\n", output->register_idx); + if (output->semantic_idx > 0) + priv_ctx->vs_output[output->register_idx] = "TA"; + else + priv_ctx->vs_output[output->register_idx] = "result.fogcoord"; + } + else + { + priv_ctx->vs_output[output->register_idx] = "TA"; + } + } + return; + } + + TRACE("Pixel shader uses declared varyings\n"); + + /* Map builtin to declared. /dev/null the results by default to the TA temp reg */ + for(i = 0; i < 8; i++) + { + priv_ctx->texcrd_output[i] = "TA"; + } + priv_ctx->color_output[0] = "TA"; + priv_ctx->color_output[1] = "TA"; + priv_ctx->fog_output = "TA"; + + for (i = 0; i < ps_input_sig->element_count; ++i) + { + const struct wined3d_shader_signature_element *input = &ps_input_sig->elements[i]; + + if (!input->semantic_name) + continue; + + /* If a declared input register is not written by builtin arguments, don't write to it. + * GL_NV_vertex_program makes sure the input defaults to 0.0, which is correct with D3D + * + * Don't care about POSITION and PSIZE here - this is a builtin vertex shader, position goes + * to TMP_OUT in any case + */ + if (shader_match_semantic(input->semantic_name, WINED3D_DECL_USAGE_TEXCOORD)) + { + if (input->semantic_idx < 8) + priv_ctx->texcrd_output[input->semantic_idx] = decl_idx_to_string[input->register_idx]; + } + else if (shader_match_semantic(input->semantic_name, WINED3D_DECL_USAGE_COLOR)) + { + if (input->semantic_idx < 2) + priv_ctx->color_output[input->semantic_idx] = decl_idx_to_string[input->register_idx]; + } + else if (shader_match_semantic(input->semantic_name, WINED3D_DECL_USAGE_FOG)) + { + if (!input->semantic_idx) + priv_ctx->fog_output = decl_idx_to_string[input->register_idx]; + } + else + { + continue; + } + + if (!strcmp(decl_idx_to_string[input->register_idx], "result.color.primary") + || !strcmp(decl_idx_to_string[input->register_idx], "result.color.secondary")) + { + compiled->need_color_unclamp = TRUE; + } + } + + /* Map declared to declared */ + for (i = 0; i < shader->output_signature.element_count; ++i) + { + const struct wined3d_shader_signature_element *output = &shader->output_signature.elements[i]; + + /* Write unread output to TA to throw them away */ + priv_ctx->vs_output[output->register_idx] = "TA"; + + if (!output->semantic_name) + continue; + + if (shader_match_semantic(output->semantic_name, WINED3D_DECL_USAGE_POSITION) && !output->semantic_idx) + { + priv_ctx->vs_output[output->register_idx] = "TMP_OUT"; + continue; + } + else if (shader_match_semantic(output->semantic_name, WINED3D_DECL_USAGE_PSIZE) && !output->semantic_idx) + { + priv_ctx->vs_output[output->register_idx] = "result.pointsize"; + continue; + } + + for (j = 0; j < ps_input_sig->element_count; ++j) + { + const struct wined3d_shader_signature_element *input = &ps_input_sig->elements[j]; + + if (!input->semantic_name) + continue; + + if (!strcmp(input->semantic_name, output->semantic_name) + && input->semantic_idx == output->semantic_idx) + { + priv_ctx->vs_output[output->register_idx] = decl_idx_to_string[input->register_idx]; + + if (!strcmp(priv_ctx->vs_output[output->register_idx], "result.color.primary") + || !strcmp(priv_ctx->vs_output[output->register_idx], "result.color.secondary")) + { + compiled->need_color_unclamp = TRUE; + } + } + } + } +} + +/* Context activation is done by the caller. */ +static GLuint shader_arb_generate_vshader(const struct wined3d_shader *shader, + const struct wined3d_gl_info *gl_info, struct wined3d_string_buffer *buffer, + const struct arb_vs_compile_args *args, struct arb_vs_compiled_shader *compiled, + const struct wined3d_shader_signature *ps_input_sig) +{ + const struct arb_vshader_private *shader_data = shader->backend_data; + const struct wined3d_shader_reg_maps *reg_maps = &shader->reg_maps; + GLuint ret; + DWORD next_local = 0; + struct shader_arb_ctx_priv priv_ctx; + unsigned int i; + + memset(&priv_ctx, 0, sizeof(priv_ctx)); + priv_ctx.gl_info = gl_info; + priv_ctx.cur_vs_args = args; + list_init(&priv_ctx.control_frames); + init_output_registers(shader, ps_input_sig, &priv_ctx, compiled); + + /* Create the hw ARB shader */ + shader_addline(buffer, "!!ARBvp1.0\n"); + + /* Always enable the NV extension if available. Unlike fragment shaders, there is no + * mesurable performance penalty, and we can always make use of it for clipplanes. + */ + if (gl_info->supported[NV_VERTEX_PROGRAM3]) + { + shader_addline(buffer, "OPTION NV_vertex_program3;\n"); + priv_ctx.target_version = NV3; + shader_addline(buffer, "ADDRESS aL;\n"); + } + else if (gl_info->supported[NV_VERTEX_PROGRAM2_OPTION]) + { + shader_addline(buffer, "OPTION NV_vertex_program2;\n"); + priv_ctx.target_version = NV2; + shader_addline(buffer, "ADDRESS aL;\n"); + } else { + priv_ctx.target_version = ARB; + } + + shader_addline(buffer, "TEMP TMP_OUT;\n"); + if (reg_maps->fog) + shader_addline(buffer, "TEMP TMP_FOGCOORD;\n"); + if (need_helper_const(shader_data, reg_maps, gl_info)) + { + char ftoa_tmp[17]; + wined3d_ftoa(eps, ftoa_tmp); + shader_addline(buffer, "PARAM helper_const = { 0.0, 1.0, 2.0, %s};\n", ftoa_tmp); + } + if (need_rel_addr_const(shader_data, reg_maps, gl_info)) + { + shader_addline(buffer, "PARAM rel_addr_const = { 0.5, %d.0, 0.0, 0.0 };\n", shader_data->rel_offset); + shader_addline(buffer, "TEMP A0_SHADOW;\n"); + } + + shader_addline(buffer, "TEMP TA;\n"); + shader_addline(buffer, "TEMP TB;\n"); + + /* Base Declarations */ + shader_generate_arb_declarations(shader, reg_maps, buffer, gl_info, + &priv_ctx.vs_clipplanes, &priv_ctx); + + for (i = 0; i < WINED3D_MAX_CONSTS_I; ++i) + { + compiled->int_consts[i] = WINED3D_CONST_NUM_UNUSED; + if (reg_maps->integer_constants & (1u << i) && priv_ctx.target_version >= NV2) + { + const DWORD *control_values = find_loop_control_values(shader, i); + + if(control_values) + { + shader_addline(buffer, "PARAM I%u = {%u, %u, %u, -1};\n", i, + control_values[0], control_values[1], control_values[2]); + } + else + { + compiled->int_consts[i] = next_local; + compiled->num_int_consts++; + shader_addline(buffer, "PARAM I%u = program.local[%u];\n", i, next_local++); + } + } + } + + /* We need a constant to fixup the final position */ + shader_addline(buffer, "PARAM posFixup = program.local[%u];\n", next_local); + compiled->pos_fixup = next_local++; + + /* Initialize output parameters. GL_ARB_vertex_program does not require special initialization values + * for output parameters. D3D in theory does not do that either, but some applications depend on a + * proper initialization of the secondary color, and programs using the fixed function pipeline without + * a replacement shader depend on the texcoord.w being set properly. + * + * GL_NV_vertex_program defines that all output values are initialized to {0.0, 0.0, 0.0, 1.0}. This + * assertion is in effect even when using GL_ARB_vertex_program without any NV specific additions. So + * skip this if NV_vertex_program is supported. Otherwise, initialize the secondary color. For the tex- + * coords, we have a flag in the opengl caps. Many cards do not require the texcoord being set, and + * this can eat a number of instructions, so skip it unless this cap is set as well + */ + if (!gl_info->supported[NV_VERTEX_PROGRAM]) + { + const char *color_init = arb_get_helper_value(WINED3D_SHADER_TYPE_VERTEX, ARB_0001); + shader_addline(buffer, "MOV result.color.secondary, %s;\n", color_init); + } + + /* The shader starts with the main function */ + priv_ctx.in_main_func = TRUE; + /* Base Shader Body */ + if (FAILED(shader_generate_code(shader, buffer, reg_maps, &priv_ctx, NULL, NULL))) + return -1; + + if (!priv_ctx.footer_written) vshader_add_footer(&priv_ctx, + shader_data, args, reg_maps, gl_info, buffer); + + shader_addline(buffer, "END\n"); + + /* TODO: change to resource.glObjectHandle or something like that */ + GL_EXTCALL(glGenProgramsARB(1, &ret)); + + TRACE("Creating a hw vertex shader, prg=%d\n", ret); + GL_EXTCALL(glBindProgramARB(GL_VERTEX_PROGRAM_ARB, ret)); + + TRACE("Created hw vertex shader, prg=%d\n", ret); + if (!shader_arb_compile(gl_info, GL_VERTEX_PROGRAM_ARB, buffer->buffer)) + return -1; + + return ret; +} + +/* Context activation is done by the caller. */ +static struct arb_ps_compiled_shader *find_arb_pshader(struct wined3d_context_gl *context_gl, + struct wined3d_shader *shader, const struct arb_ps_compile_args *args) +{ + const struct wined3d_d3d_info *d3d_info = context_gl->c.d3d_info; + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + struct wined3d_device *device = shader->device; + UINT i; + DWORD new_size; + struct arb_ps_compiled_shader *new_array; + struct wined3d_string_buffer buffer; + struct arb_pshader_private *shader_data; + GLuint ret; + + if (!shader->backend_data) + { + struct shader_arb_priv *priv = device->shader_priv; + + shader->backend_data = heap_alloc_zero(sizeof(*shader_data)); + shader_data = shader->backend_data; + shader_data->clamp_consts = shader->reg_maps.shader_version.major == 1; + + if (shader->reg_maps.shader_version.major < 3) + shader_data->input_signature_idx = ~0U; + else + shader_data->input_signature_idx = find_input_signature(priv, &shader->input_signature); + + TRACE("Shader got assigned input signature index %u\n", shader_data->input_signature_idx); + + if (!d3d_info->vs_clipping) + shader_data->clipplane_emulation = shader_find_free_input_register(&shader->reg_maps, + d3d_info->limits.ffp_blend_stages - 1); + else + shader_data->clipplane_emulation = ~0U; + } + shader_data = shader->backend_data; + + /* Usually we have very few GL shaders for each d3d shader(just 1 or maybe 2), + * so a linear search is more performant than a hashmap or a binary search + * (cache coherency etc) + */ + for (i = 0; i < shader_data->num_gl_shaders; ++i) + { + if (!memcmp(&shader_data->gl_shaders[i].args, args, sizeof(*args))) + return &shader_data->gl_shaders[i]; + } + + TRACE("No matching GL shader found, compiling a new shader\n"); + if(shader_data->shader_array_size == shader_data->num_gl_shaders) { + if (shader_data->num_gl_shaders) + { + new_size = shader_data->shader_array_size + max(1, shader_data->shader_array_size / 2); + new_array = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, shader_data->gl_shaders, + new_size * sizeof(*shader_data->gl_shaders)); + } + else + { + new_array = heap_alloc_zero(sizeof(*shader_data->gl_shaders)); + new_size = 1; + } + + if(!new_array) { + ERR("Out of memory\n"); + return 0; + } + shader_data->gl_shaders = new_array; + shader_data->shader_array_size = new_size; + } + + shader_data->gl_shaders[shader_data->num_gl_shaders].args = *args; + + if (!string_buffer_init(&buffer)) + { + ERR("Failed to initialize shader buffer.\n"); + return 0; + } + + ret = shader_arb_generate_pshader(shader, gl_info, &buffer, args, + &shader_data->gl_shaders[shader_data->num_gl_shaders]); + string_buffer_free(&buffer); + shader_data->gl_shaders[shader_data->num_gl_shaders].prgId = ret; + + return &shader_data->gl_shaders[shader_data->num_gl_shaders++]; +} + +static inline BOOL vs_args_equal(const struct arb_vs_compile_args *stored, const struct arb_vs_compile_args *new, + const DWORD use_map, BOOL skip_int) { + if((stored->super.swizzle_map & use_map) != new->super.swizzle_map) return FALSE; + if(stored->super.clip_enabled != new->super.clip_enabled) return FALSE; + if(stored->super.fog_src != new->super.fog_src) return FALSE; + if(stored->clip.boolclip_compare != new->clip.boolclip_compare) return FALSE; + if(stored->ps_signature != new->ps_signature) return FALSE; + if(stored->vertex.samplers_compare != new->vertex.samplers_compare) return FALSE; + if(skip_int) return TRUE; + + return !memcmp(stored->loop_ctrl, new->loop_ctrl, sizeof(stored->loop_ctrl)); +} + +static struct arb_vs_compiled_shader *find_arb_vshader(struct wined3d_shader *shader, + const struct wined3d_gl_info *gl_info, DWORD use_map, const struct arb_vs_compile_args *args, + const struct wined3d_shader_signature *ps_input_sig) +{ + UINT i; + DWORD new_size; + struct arb_vs_compiled_shader *new_array; + struct wined3d_string_buffer buffer; + struct arb_vshader_private *shader_data; + GLuint ret; + + if (!shader->backend_data) + { + const struct wined3d_shader_reg_maps *reg_maps = &shader->reg_maps; + + shader->backend_data = heap_alloc_zero(sizeof(*shader_data)); + shader_data = shader->backend_data; + + if ((gl_info->quirks & WINED3D_QUIRK_ARB_VS_OFFSET_LIMIT) + && reg_maps->min_rel_offset <= reg_maps->max_rel_offset) + { + if (reg_maps->max_rel_offset - reg_maps->min_rel_offset > 127) + { + FIXME("The difference between the minimum and maximum relative offset is > 127.\n"); + FIXME("Which this OpenGL implementation does not support. Try using GLSL.\n"); + FIXME("Min: %u, Max: %u.\n", reg_maps->min_rel_offset, reg_maps->max_rel_offset); + } + else if (reg_maps->max_rel_offset - reg_maps->min_rel_offset > 63) + shader_data->rel_offset = reg_maps->min_rel_offset + 63; + else if (reg_maps->max_rel_offset > 63) + shader_data->rel_offset = reg_maps->min_rel_offset; + } + } + shader_data = shader->backend_data; + + /* Usually we have very few GL shaders for each d3d shader(just 1 or maybe 2), + * so a linear search is more performant than a hashmap or a binary search + * (cache coherency etc) + */ + for(i = 0; i < shader_data->num_gl_shaders; i++) { + if (vs_args_equal(&shader_data->gl_shaders[i].args, args, + use_map, gl_info->supported[NV_VERTEX_PROGRAM2_OPTION])) + { + return &shader_data->gl_shaders[i]; + } + } + + TRACE("No matching GL shader found, compiling a new shader\n"); + + if(shader_data->shader_array_size == shader_data->num_gl_shaders) { + if (shader_data->num_gl_shaders) + { + new_size = shader_data->shader_array_size + max(1, shader_data->shader_array_size / 2); + new_array = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, shader_data->gl_shaders, + new_size * sizeof(*shader_data->gl_shaders)); + } + else + { + new_array = heap_alloc_zero(sizeof(*shader_data->gl_shaders)); + new_size = 1; + } + + if(!new_array) { + ERR("Out of memory\n"); + return 0; + } + shader_data->gl_shaders = new_array; + shader_data->shader_array_size = new_size; + } + + shader_data->gl_shaders[shader_data->num_gl_shaders].args = *args; + + if (!string_buffer_init(&buffer)) + { + ERR("Failed to initialize shader buffer.\n"); + return 0; + } + + ret = shader_arb_generate_vshader(shader, gl_info, &buffer, args, + &shader_data->gl_shaders[shader_data->num_gl_shaders], + ps_input_sig); + string_buffer_free(&buffer); + shader_data->gl_shaders[shader_data->num_gl_shaders].prgId = ret; + + return &shader_data->gl_shaders[shader_data->num_gl_shaders++]; +} + +static void find_arb_ps_compile_args(const struct wined3d_state *state, + const struct wined3d_context_gl *context_gl, const struct wined3d_shader *shader, + struct arb_ps_compile_args *args) +{ + const struct wined3d_d3d_info *d3d_info = context_gl->c.d3d_info; + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + int i; + WORD int_skip; + + find_ps_compile_args(state, shader, context_gl->c.stream_info.position_transformed, &args->super, &context_gl->c); + + /* This forces all local boolean constants to 1 to make them stateblock independent */ + args->bools = shader->reg_maps.local_bool_consts; + + for (i = 0; i < WINED3D_MAX_CONSTS_B; ++i) + { + if (state->ps_consts_b[i]) + args->bools |= ( 1u << i); + } + + /* Only enable the clip plane emulation KIL if at least one clipplane is enabled. The KIL instruction + * is quite expensive because it forces the driver to disable early Z discards. It is cheaper to + * duplicate the shader than have a no-op KIL instruction in every shader + */ + if (!d3d_info->vs_clipping && use_vs(state) + && state->render_states[WINED3D_RS_CLIPPING] + && state->render_states[WINED3D_RS_CLIPPLANEENABLE]) + args->clip = 1; + else + args->clip = 0; + + /* Skip if unused or local, or supported natively */ + int_skip = ~shader->reg_maps.integer_constants | shader->reg_maps.local_int_consts; + if (int_skip == 0xffff || gl_info->supported[NV_FRAGMENT_PROGRAM_OPTION]) + { + memset(args->loop_ctrl, 0, sizeof(args->loop_ctrl)); + return; + } + + for (i = 0; i < WINED3D_MAX_CONSTS_I; ++i) + { + if (int_skip & (1u << i)) + { + args->loop_ctrl[i][0] = 0; + args->loop_ctrl[i][1] = 0; + args->loop_ctrl[i][2] = 0; + } + else + { + args->loop_ctrl[i][0] = state->ps_consts_i[i].x; + args->loop_ctrl[i][1] = state->ps_consts_i[i].y; + args->loop_ctrl[i][2] = state->ps_consts_i[i].z; + } + } +} + +static void find_arb_vs_compile_args(const struct wined3d_state *state, + const struct wined3d_context_gl *context_gl, const struct wined3d_shader *shader, + struct arb_vs_compile_args *args) +{ + const struct wined3d_d3d_info *d3d_info = context_gl->c.d3d_info; + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + const struct wined3d_device *device = shader->device; + const struct wined3d_adapter *adapter = device->adapter; + int i; + WORD int_skip; + + find_vs_compile_args(state, shader, context_gl->c.stream_info.swizzle_map, &args->super, &context_gl->c); + + args->clip.boolclip_compare = 0; + if (use_ps(state)) + { + const struct wined3d_shader *ps = state->shader[WINED3D_SHADER_TYPE_PIXEL]; + const struct arb_pshader_private *shader_priv = ps->backend_data; + args->ps_signature = shader_priv->input_signature_idx; + + args->clip.boolclip.clip_texcoord = shader_priv->clipplane_emulation + 1; + } + else + { + args->ps_signature = ~0; + if (!d3d_info->vs_clipping && adapter->fragment_pipe == &arbfp_fragment_pipeline) + args->clip.boolclip.clip_texcoord = ffp_clip_emul(&context_gl->c) ? d3d_info->limits.ffp_blend_stages : 0; + /* Otherwise: Setting boolclip_compare set clip_texcoord to 0 */ + } + + if (args->clip.boolclip.clip_texcoord) + { + if (state->render_states[WINED3D_RS_CLIPPING]) + args->clip.boolclip.clipplane_mask = (unsigned char)state->render_states[WINED3D_RS_CLIPPLANEENABLE]; + /* clipplane_mask was set to 0 by setting boolclip_compare to 0 */ + } + + /* This forces all local boolean constants to 1 to make them stateblock independent */ + args->clip.boolclip.bools = shader->reg_maps.local_bool_consts; + /* TODO: Figure out if it would be better to store bool constants as bitmasks in the stateblock */ + for (i = 0; i < WINED3D_MAX_CONSTS_B; ++i) + { + if (state->vs_consts_b[i]) + args->clip.boolclip.bools |= (1u << i); + } + + args->vertex.samplers[0] = context_gl->tex_unit_map[WINED3D_MAX_FRAGMENT_SAMPLERS + 0]; + args->vertex.samplers[1] = context_gl->tex_unit_map[WINED3D_MAX_FRAGMENT_SAMPLERS + 1]; + args->vertex.samplers[2] = context_gl->tex_unit_map[WINED3D_MAX_FRAGMENT_SAMPLERS + 2]; + args->vertex.samplers[3] = 0; + + /* Skip if unused or local */ + int_skip = ~shader->reg_maps.integer_constants | shader->reg_maps.local_int_consts; + /* This is about flow control, not clipping. */ + if (int_skip == 0xffff || gl_info->supported[NV_VERTEX_PROGRAM2_OPTION]) + { + memset(args->loop_ctrl, 0, sizeof(args->loop_ctrl)); + return; + } + + for (i = 0; i < WINED3D_MAX_CONSTS_I; ++i) + { + if (int_skip & (1u << i)) + { + args->loop_ctrl[i][0] = 0; + args->loop_ctrl[i][1] = 0; + args->loop_ctrl[i][2] = 0; + } + else + { + args->loop_ctrl[i][0] = state->vs_consts_i[i].x; + args->loop_ctrl[i][1] = state->vs_consts_i[i].y; + args->loop_ctrl[i][2] = state->vs_consts_i[i].z; + } + } +} + +/* Context activation is done by the caller. */ +static void shader_arb_select(void *shader_priv, struct wined3d_context *context, + const struct wined3d_state *state) +{ + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + struct shader_arb_priv *priv = shader_priv; + int i; + + /* Deal with pixel shaders first so the vertex shader arg function has the input signature ready */ + if (use_ps(state)) + { + struct wined3d_shader *ps = state->shader[WINED3D_SHADER_TYPE_PIXEL]; + struct arb_ps_compile_args compile_args; + struct arb_ps_compiled_shader *compiled; + + TRACE("Using pixel shader %p.\n", ps); + find_arb_ps_compile_args(state, context_gl, ps, &compile_args); + compiled = find_arb_pshader(context_gl, ps, &compile_args); + priv->current_fprogram_id = compiled->prgId; + priv->compiled_fprog = compiled; + + /* Bind the fragment program */ + GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, priv->current_fprogram_id)); + checkGLcall("glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, priv->current_fprogram_id);"); + + if (!priv->use_arbfp_fixed_func) + priv->fragment_pipe->fp_enable(context, FALSE); + + /* Enable OpenGL fragment programs. */ + gl_info->gl_ops.gl.p_glEnable(GL_FRAGMENT_PROGRAM_ARB); + checkGLcall("glEnable(GL_FRAGMENT_PROGRAM_ARB);"); + + TRACE("Bound fragment program %u and enabled GL_FRAGMENT_PROGRAM_ARB\n", priv->current_fprogram_id); + + /* Pixel Shader 1.x constants are clamped to [-1;1], Pixel Shader 2.0 constants are not. If switching between + * a 1.x and newer shader, reload the first 8 constants + */ + if (priv->last_ps_const_clamped != ((struct arb_pshader_private *)ps->backend_data)->clamp_consts) + { + priv->last_ps_const_clamped = ((struct arb_pshader_private *)ps->backend_data)->clamp_consts; + priv->highest_dirty_ps_const = max(priv->highest_dirty_ps_const, 8); + for(i = 0; i < 8; i++) + { + priv->pshader_const_dirty[i] = 1; + } + /* Also takes care of loading local constants */ + shader_arb_load_constants_internal(shader_priv, context_gl, state, TRUE, FALSE, TRUE); + } + else + { + UINT rt_height = state->fb.render_targets[0]->height; + shader_arb_ps_local_constants(compiled, context_gl, state, rt_height); + } + + /* Force constant reloading for the NP2 fixup (see comment in shader_glsl_select for more info) */ + if (compiled->np2fixup_info.super.active) + context->constant_update_mask |= WINED3D_SHADER_CONST_PS_NP2_FIXUP; + + if (ps->load_local_constsF) + context->constant_update_mask |= WINED3D_SHADER_CONST_PS_F; + } + else + { + if (gl_info->supported[ARB_FRAGMENT_PROGRAM] && !priv->use_arbfp_fixed_func) + { + /* Disable only if we're not using arbfp fixed function fragment + * processing. If this is used, keep GL_FRAGMENT_PROGRAM_ARB + * enabled, and the fixed function pipeline will bind the fixed + * function replacement shader. */ + gl_info->gl_ops.gl.p_glDisable(GL_FRAGMENT_PROGRAM_ARB); + checkGLcall("glDisable(GL_FRAGMENT_PROGRAM_ARB)"); + priv->current_fprogram_id = 0; + } + priv->fragment_pipe->fp_enable(context, TRUE); + } + + if (use_vs(state)) + { + struct wined3d_shader *vs = state->shader[WINED3D_SHADER_TYPE_VERTEX]; + struct arb_vs_compile_args compile_args; + struct arb_vs_compiled_shader *compiled; + const struct wined3d_shader_signature *ps_input_sig; + + TRACE("Using vertex shader %p\n", vs); + find_arb_vs_compile_args(state, context_gl, vs, &compile_args); + + /* Instead of searching for the signature in the signature list, read the one from the + * current pixel shader. It's maybe not the shader where the signature came from, but it + * is the same signature and faster to find. */ + if (compile_args.ps_signature == ~0U) + ps_input_sig = NULL; + else + ps_input_sig = &state->shader[WINED3D_SHADER_TYPE_PIXEL]->input_signature; + + compiled = find_arb_vshader(vs, gl_info, context->stream_info.use_map, + &compile_args, ps_input_sig); + priv->current_vprogram_id = compiled->prgId; + priv->compiled_vprog = compiled; + + /* Bind the vertex program */ + GL_EXTCALL(glBindProgramARB(GL_VERTEX_PROGRAM_ARB, priv->current_vprogram_id)); + checkGLcall("glBindProgramARB(GL_VERTEX_PROGRAM_ARB, priv->current_vprogram_id);"); + + priv->vertex_pipe->vp_enable(context, FALSE); + + /* Enable OpenGL vertex programs */ + gl_info->gl_ops.gl.p_glEnable(GL_VERTEX_PROGRAM_ARB); + checkGLcall("glEnable(GL_VERTEX_PROGRAM_ARB);"); + TRACE("Bound vertex program %u and enabled GL_VERTEX_PROGRAM_ARB\n", priv->current_vprogram_id); + shader_arb_vs_local_constants(compiled, context_gl, state); + + if(priv->last_vs_color_unclamp != compiled->need_color_unclamp) { + priv->last_vs_color_unclamp = compiled->need_color_unclamp; + + if (gl_info->supported[ARB_COLOR_BUFFER_FLOAT]) + { + GL_EXTCALL(glClampColorARB(GL_CLAMP_VERTEX_COLOR_ARB, !compiled->need_color_unclamp)); + checkGLcall("glClampColorARB"); + } else { + FIXME("vertex color clamp needs to be changed, but extension not supported.\n"); + } + } + + if (vs->load_local_constsF) + context->constant_update_mask |= WINED3D_SHADER_CONST_VS_F; + } + else + { + if (gl_info->supported[ARB_VERTEX_PROGRAM]) + { + priv->current_vprogram_id = 0; + gl_info->gl_ops.gl.p_glDisable(GL_VERTEX_PROGRAM_ARB); + checkGLcall("glDisable(GL_VERTEX_PROGRAM_ARB)"); + } + priv->vertex_pipe->vp_enable(context, TRUE); + } +} + +static void shader_arb_select_compute(void *shader_priv, struct wined3d_context *context, + const struct wined3d_state *state) +{ + ERR("Compute pipeline not supported by the ARB shader backend.\n"); +} + +/* Context activation is done by the caller. */ +static void shader_arb_disable(void *shader_priv, struct wined3d_context *context) +{ + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + struct shader_arb_priv *priv = shader_priv; + + if (gl_info->supported[ARB_FRAGMENT_PROGRAM]) + { + gl_info->gl_ops.gl.p_glDisable(GL_FRAGMENT_PROGRAM_ARB); + checkGLcall("glDisable(GL_FRAGMENT_PROGRAM_ARB)"); + priv->current_fprogram_id = 0; + } + priv->fragment_pipe->fp_enable(context, FALSE); + + if (gl_info->supported[ARB_VERTEX_PROGRAM]) + { + priv->current_vprogram_id = 0; + gl_info->gl_ops.gl.p_glDisable(GL_VERTEX_PROGRAM_ARB); + checkGLcall("glDisable(GL_VERTEX_PROGRAM_ARB)"); + } + priv->vertex_pipe->vp_enable(context, FALSE); + + if (gl_info->supported[ARB_COLOR_BUFFER_FLOAT] && priv->last_vs_color_unclamp) + { + GL_EXTCALL(glClampColorARB(GL_CLAMP_VERTEX_COLOR_ARB, GL_FIXED_ONLY_ARB)); + checkGLcall("glClampColorARB"); + priv->last_vs_color_unclamp = FALSE; + } + + context->shader_update_mask = (1u << WINED3D_SHADER_TYPE_PIXEL) + | (1u << WINED3D_SHADER_TYPE_VERTEX) + | (1u << WINED3D_SHADER_TYPE_GEOMETRY) + | (1u << WINED3D_SHADER_TYPE_HULL) + | (1u << WINED3D_SHADER_TYPE_DOMAIN) + | (1u << WINED3D_SHADER_TYPE_COMPUTE); +} + +static void shader_arb_destroy(struct wined3d_shader *shader) +{ + struct wined3d_device *device = shader->device; + const struct wined3d_gl_info *gl_info; + struct wined3d_context *context; + unsigned int i; + + /* This can happen if a shader was never compiled */ + if (!shader->backend_data) + return; + + context = context_acquire(device, NULL, 0); + gl_info = wined3d_context_gl(context)->gl_info; + + if (shader_is_pshader_version(shader->reg_maps.shader_version.type)) + { + struct arb_pshader_private *shader_data = shader->backend_data; + + for (i = 0; i < shader_data->num_gl_shaders; ++i) + GL_EXTCALL(glDeleteProgramsARB(1, &shader_data->gl_shaders[i].prgId)); + + heap_free(shader_data->gl_shaders); + } + else + { + struct arb_vshader_private *shader_data = shader->backend_data; + + for (i = 0; i < shader_data->num_gl_shaders; ++i) + GL_EXTCALL(glDeleteProgramsARB(1, &shader_data->gl_shaders[i].prgId)); + + heap_free(shader_data->gl_shaders); + } + + checkGLcall("delete programs"); + + context_release(context); + + heap_free(shader->backend_data); + shader->backend_data = NULL; +} + +static int sig_tree_compare(const void *key, const struct wine_rb_entry *entry) +{ + struct ps_signature *e = WINE_RB_ENTRY_VALUE(entry, struct ps_signature, entry); + return compare_sig(key, &e->sig); +} + +static HRESULT shader_arb_alloc(struct wined3d_device *device, const struct wined3d_vertex_pipe_ops *vertex_pipe, + const struct wined3d_fragment_pipe_ops *fragment_pipe) +{ + const struct wined3d_d3d_info *d3d_info = &device->adapter->d3d_info; + struct fragment_caps fragment_caps; + void *vertex_priv, *fragment_priv; + struct shader_arb_priv *priv; + + if (!(priv = heap_alloc_zero(sizeof(*priv)))) + return E_OUTOFMEMORY; + + if (!(vertex_priv = vertex_pipe->vp_alloc(&arb_program_shader_backend, priv))) + { + ERR("Failed to initialize vertex pipe.\n"); + heap_free(priv); + return E_FAIL; + } + + if (!(fragment_priv = fragment_pipe->alloc_private(&arb_program_shader_backend, priv))) + { + ERR("Failed to initialize fragment pipe.\n"); + vertex_pipe->vp_free(device, NULL); + heap_free(priv); + return E_FAIL; + } + + memset(priv->vshader_const_dirty, 1, + sizeof(*priv->vshader_const_dirty) * d3d_info->limits.vs_uniform_count); + memset(priv->pshader_const_dirty, 1, + sizeof(*priv->pshader_const_dirty) * d3d_info->limits.ps_uniform_count); + + wine_rb_init(&priv->signature_tree, sig_tree_compare); + + priv->vertex_pipe = vertex_pipe; + priv->fragment_pipe = fragment_pipe; + fragment_pipe->get_caps(device->adapter, &fragment_caps); + priv->ffp_proj_control = fragment_caps.wined3d_caps & WINED3D_FRAGMENT_CAP_PROJ_CONTROL; + + device->vertex_priv = vertex_priv; + device->fragment_priv = fragment_priv; + device->shader_priv = priv; + + return WINED3D_OK; +} + +static void release_signature(struct wine_rb_entry *entry, void *context) +{ + struct ps_signature *sig = WINE_RB_ENTRY_VALUE(entry, struct ps_signature, entry); + unsigned int i; + + for (i = 0; i < sig->sig.element_count; ++i) + { + heap_free((char *)sig->sig.elements[i].semantic_name); + } + heap_free(sig->sig.elements); + heap_free(sig); +} + +/* Context activation is done by the caller. */ +static void shader_arb_free(struct wined3d_device *device, struct wined3d_context *context) +{ + struct shader_arb_priv *priv = device->shader_priv; + + wine_rb_destroy(&priv->signature_tree, release_signature, NULL); + priv->fragment_pipe->free_private(device, context); + priv->vertex_pipe->vp_free(device, context); + heap_free(device->shader_priv); +} + +static BOOL shader_arb_allocate_context_data(struct wined3d_context *context) +{ + return TRUE; +} + +static void shader_arb_free_context_data(struct wined3d_context *context) +{ + struct shader_arb_priv *priv; + + priv = context->device->shader_priv; + if (priv->last_context == context) + priv->last_context = NULL; +} + +static void shader_arb_init_context_state(struct wined3d_context *context) {} + +static void shader_arb_get_caps(const struct wined3d_adapter *adapter, struct shader_caps *caps) +{ + const struct wined3d_gl_info *gl_info = &adapter->gl_info; + + if (gl_info->supported[ARB_VERTEX_PROGRAM]) + { + DWORD vs_consts; + UINT vs_version; + + /* 96 is the minimum allowed value of MAX_PROGRAM_ENV_PARAMETERS_ARB + * for vertex programs. If the native limit is less than that it's + * not very useful, and e.g. Mesa swrast returns 0, probably to + * indicate it's a software implementation. */ + if (gl_info->limits.arb_vs_native_constants < 96) + vs_consts = gl_info->limits.arb_vs_float_constants; + else + vs_consts = min(gl_info->limits.arb_vs_float_constants, gl_info->limits.arb_vs_native_constants); + + if (gl_info->supported[NV_VERTEX_PROGRAM3]) + { + vs_version = 3; + TRACE("Hardware vertex shader version 3.0 enabled (NV_VERTEX_PROGRAM3)\n"); + } + else if (vs_consts >= 256) + { + /* Shader Model 2.0 requires at least 256 vertex shader constants */ + vs_version = 2; + TRACE("Hardware vertex shader version 2.0 enabled (ARB_PROGRAM)\n"); + } + else + { + vs_version = 1; + TRACE("Hardware vertex shader version 1.1 enabled (ARB_PROGRAM)\n"); + } + caps->vs_version = min(wined3d_settings.max_sm_vs, vs_version); + caps->vs_uniform_count = min(WINED3D_MAX_VS_CONSTS_F, vs_consts); + } + else + { + caps->vs_version = 0; + caps->vs_uniform_count = 0; + } + + caps->hs_version = 0; + caps->ds_version = 0; + caps->gs_version = 0; + caps->cs_version = 0; + + if (gl_info->supported[ARB_FRAGMENT_PROGRAM]) + { + DWORD ps_consts; + UINT ps_version; + + /* Similar as above for vertex programs, but the minimum for fragment + * programs is 24. */ + if (gl_info->limits.arb_ps_native_constants < 24) + ps_consts = gl_info->limits.arb_ps_float_constants; + else + ps_consts = min(gl_info->limits.arb_ps_float_constants, gl_info->limits.arb_ps_native_constants); + + if (gl_info->supported[NV_FRAGMENT_PROGRAM2]) + { + ps_version = 3; + TRACE("Hardware pixel shader version 3.0 enabled (NV_FRAGMENT_PROGRAM2)\n"); + } + else if (ps_consts >= 32) + { + /* Shader Model 2.0 requires at least 32 pixel shader constants */ + ps_version = 2; + TRACE("Hardware pixel shader version 2.0 enabled (ARB_PROGRAM)\n"); + } + else + { + ps_version = 1; + TRACE("Hardware pixel shader version 1.4 enabled (ARB_PROGRAM)\n"); + } + caps->ps_version = min(wined3d_settings.max_sm_ps, ps_version); + caps->ps_uniform_count = min(WINED3D_MAX_PS_CONSTS_F, ps_consts); + caps->ps_1x_max_value = 8.0f; + } + else + { + caps->ps_version = 0; + caps->ps_uniform_count = 0; + caps->ps_1x_max_value = 0.0f; + } + + caps->varying_count = 0; + caps->wined3d_caps = WINED3D_SHADER_CAP_SRGB_WRITE; + if (use_nv_clip(gl_info)) + caps->wined3d_caps |= WINED3D_SHADER_CAP_VS_CLIPPING; +} + +static BOOL shader_arb_color_fixup_supported(struct color_fixup_desc fixup) +{ + /* We support everything except complex conversions. */ + return !is_complex_fixup(fixup); +} + +static void shader_arb_add_instruction_modifiers(const struct wined3d_shader_instruction *ins) { + DWORD shift; + char write_mask[20], regstr[50]; + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + BOOL is_color = FALSE; + const struct wined3d_shader_dst_param *dst; + + if (!ins->dst_count) return; + + dst = &ins->dst[0]; + shift = dst->shift; + if (!shift) return; /* Saturate alone is handled by the instructions */ + + shader_arb_get_write_mask(ins, dst, write_mask); + shader_arb_get_register_name(ins, &dst->reg, regstr, &is_color); + + /* Generate a line that does the output modifier computation + * FIXME: _SAT vs shift? _SAT alone is already handled in the instructions, if this + * maps problems in e.g. _d4_sat modify shader_arb_get_modifier + */ + shader_addline(buffer, "MUL%s %s%s, %s, %s;\n", shader_arb_get_modifier(ins), + regstr, write_mask, regstr, shift_tab[shift]); +} + +static const SHADER_HANDLER shader_arb_instruction_handler_table[WINED3DSIH_TABLE_SIZE] = +{ + /* WINED3DSIH_ABS */ shader_hw_map2gl, + /* WINED3DSIH_ADD */ shader_hw_map2gl, + /* WINED3DSIH_AND */ NULL, + /* WINED3DSIH_ATOMIC_AND */ NULL, + /* WINED3DSIH_ATOMIC_CMP_STORE */ NULL, + /* WINED3DSIH_ATOMIC_IADD */ NULL, + /* WINED3DSIH_ATOMIC_IMAX */ NULL, + /* WINED3DSIH_ATOMIC_IMIN */ NULL, + /* WINED3DSIH_ATOMIC_OR */ NULL, + /* WINED3DSIH_ATOMIC_UMAX */ NULL, + /* WINED3DSIH_ATOMIC_UMIN */ NULL, + /* WINED3DSIH_ATOMIC_XOR */ NULL, + /* WINED3DSIH_BEM */ pshader_hw_bem, + /* WINED3DSIH_BFI */ NULL, + /* WINED3DSIH_BFREV */ NULL, + /* WINED3DSIH_BREAK */ shader_hw_break, + /* WINED3DSIH_BREAKC */ shader_hw_breakc, + /* WINED3DSIH_BREAKP */ NULL, + /* WINED3DSIH_BUFINFO */ NULL, + /* WINED3DSIH_CALL */ shader_hw_call, + /* WINED3DSIH_CALLNZ */ NULL, + /* WINED3DSIH_CASE */ NULL, + /* WINED3DSIH_CMP */ pshader_hw_cmp, + /* WINED3DSIH_CND */ pshader_hw_cnd, + /* WINED3DSIH_CONTINUE */ NULL, + /* WINED3DSIH_CONTINUEP */ NULL, + /* WINED3DSIH_COUNTBITS */ NULL, + /* WINED3DSIH_CRS */ shader_hw_map2gl, + /* WINED3DSIH_CUT */ NULL, + /* WINED3DSIH_CUT_STREAM */ NULL, + /* WINED3DSIH_DCL */ shader_hw_nop, + /* WINED3DSIH_DCL_CONSTANT_BUFFER */ shader_hw_nop, + /* WINED3DSIH_DCL_FUNCTION_BODY */ NULL, + /* WINED3DSIH_DCL_FUNCTION_TABLE */ NULL, + /* WINED3DSIH_DCL_GLOBAL_FLAGS */ NULL, + /* WINED3DSIH_DCL_GS_INSTANCES */ NULL, + /* WINED3DSIH_DCL_HS_FORK_PHASE_INSTANCE_COUNT */ NULL, + /* WINED3DSIH_DCL_HS_JOIN_PHASE_INSTANCE_COUNT */ NULL, + /* WINED3DSIH_DCL_HS_MAX_TESSFACTOR */ NULL, + /* WINED3DSIH_DCL_IMMEDIATE_CONSTANT_BUFFER */ NULL, + /* WINED3DSIH_DCL_INDEX_RANGE */ NULL, + /* WINED3DSIH_DCL_INDEXABLE_TEMP */ NULL, + /* WINED3DSIH_DCL_INPUT */ NULL, + /* WINED3DSIH_DCL_INPUT_CONTROL_POINT_COUNT */ NULL, + /* WINED3DSIH_DCL_INPUT_PRIMITIVE */ shader_hw_nop, + /* WINED3DSIH_DCL_INPUT_PS */ NULL, + /* WINED3DSIH_DCL_INPUT_PS_SGV */ NULL, + /* WINED3DSIH_DCL_INPUT_PS_SIV */ NULL, + /* WINED3DSIH_DCL_INPUT_SGV */ NULL, + /* WINED3DSIH_DCL_INPUT_SIV */ NULL, + /* WINED3DSIH_DCL_INTERFACE */ NULL, + /* WINED3DSIH_DCL_OUTPUT */ NULL, + /* WINED3DSIH_DCL_OUTPUT_CONTROL_POINT_COUNT */ NULL, + /* WINED3DSIH_DCL_OUTPUT_SIV */ NULL, + /* WINED3DSIH_DCL_OUTPUT_TOPOLOGY */ shader_hw_nop, + /* WINED3DSIH_DCL_RESOURCE_RAW */ NULL, + /* WINED3DSIH_DCL_RESOURCE_STRUCTURED */ NULL, + /* WINED3DSIH_DCL_SAMPLER */ NULL, + /* WINED3DSIH_DCL_STREAM */ NULL, + /* WINED3DSIH_DCL_TEMPS */ NULL, + /* WINED3DSIH_DCL_TESSELLATOR_DOMAIN */ NULL, + /* WINED3DSIH_DCL_TESSELLATOR_OUTPUT_PRIMITIVE */ NULL, + /* WINED3DSIH_DCL_TESSELLATOR_PARTITIONING */ NULL, + /* WINED3DSIH_DCL_TGSM_RAW */ NULL, + /* WINED3DSIH_DCL_TGSM_STRUCTURED */ NULL, + /* WINED3DSIH_DCL_THREAD_GROUP */ NULL, + /* WINED3DSIH_DCL_UAV_RAW */ NULL, + /* WINED3DSIH_DCL_UAV_STRUCTURED */ NULL, + /* WINED3DSIH_DCL_UAV_TYPED */ NULL, + /* WINED3DSIH_DCL_VERTICES_OUT */ shader_hw_nop, + /* WINED3DSIH_DEF */ shader_hw_nop, + /* WINED3DSIH_DEFAULT */ NULL, + /* WINED3DSIH_DEFB */ shader_hw_nop, + /* WINED3DSIH_DEFI */ shader_hw_nop, + /* WINED3DSIH_DIV */ NULL, + /* WINED3DSIH_DP2 */ NULL, + /* WINED3DSIH_DP2ADD */ pshader_hw_dp2add, + /* WINED3DSIH_DP3 */ shader_hw_map2gl, + /* WINED3DSIH_DP4 */ shader_hw_map2gl, + /* WINED3DSIH_DST */ shader_hw_map2gl, + /* WINED3DSIH_DSX */ shader_hw_map2gl, + /* WINED3DSIH_DSX_COARSE */ NULL, + /* WINED3DSIH_DSX_FINE */ NULL, + /* WINED3DSIH_DSY */ shader_hw_dsy, + /* WINED3DSIH_DSY_COARSE */ NULL, + /* WINED3DSIH_DSY_FINE */ NULL, + /* WINED3DSIH_ELSE */ shader_hw_else, + /* WINED3DSIH_EMIT */ NULL, + /* WINED3DSIH_EMIT_STREAM */ NULL, + /* WINED3DSIH_ENDIF */ shader_hw_endif, + /* WINED3DSIH_ENDLOOP */ shader_hw_endloop, + /* WINED3DSIH_ENDREP */ shader_hw_endrep, + /* WINED3DSIH_ENDSWITCH */ NULL, + /* WINED3DSIH_EQ */ NULL, + /* WINED3DSIH_EVAL_SAMPLE_INDEX */ NULL, + /* WINED3DSIH_EXP */ shader_hw_scalar_op, + /* WINED3DSIH_EXPP */ shader_hw_scalar_op, + /* WINED3DSIH_F16TOF32 */ NULL, + /* WINED3DSIH_F32TOF16 */ NULL, + /* WINED3DSIH_FCALL */ NULL, + /* WINED3DSIH_FIRSTBIT_HI */ NULL, + /* WINED3DSIH_FIRSTBIT_LO */ NULL, + /* WINED3DSIH_FIRSTBIT_SHI */ NULL, + /* WINED3DSIH_FRC */ shader_hw_map2gl, + /* WINED3DSIH_FTOI */ NULL, + /* WINED3DSIH_FTOU */ NULL, + /* WINED3DSIH_GATHER4 */ NULL, + /* WINED3DSIH_GATHER4_C */ NULL, + /* WINED3DSIH_GATHER4_PO */ NULL, + /* WINED3DSIH_GATHER4_PO_C */ NULL, + /* WINED3DSIH_GE */ NULL, + /* WINED3DSIH_HS_CONTROL_POINT_PHASE */ NULL, + /* WINED3DSIH_HS_DECLS */ NULL, + /* WINED3DSIH_HS_FORK_PHASE */ NULL, + /* WINED3DSIH_HS_JOIN_PHASE */ NULL, + /* WINED3DSIH_IADD */ NULL, + /* WINED3DSIH_IBFE */ NULL, + /* WINED3DSIH_IEQ */ NULL, + /* WINED3DSIH_IF */ NULL /* Hardcoded into the shader */, + /* WINED3DSIH_IFC */ shader_hw_ifc, + /* WINED3DSIH_IGE */ NULL, + /* WINED3DSIH_ILT */ NULL, + /* WINED3DSIH_IMAD */ NULL, + /* WINED3DSIH_IMAX */ NULL, + /* WINED3DSIH_IMIN */ NULL, + /* WINED3DSIH_IMM_ATOMIC_ALLOC */ NULL, + /* WINED3DSIH_IMM_ATOMIC_AND */ NULL, + /* WINED3DSIH_IMM_ATOMIC_CMP_EXCH */ NULL, + /* WINED3DSIH_IMM_ATOMIC_CONSUME */ NULL, + /* WINED3DSIH_IMM_ATOMIC_EXCH */ NULL, + /* WINED3DSIH_IMM_ATOMIC_IADD */ NULL, + /* WINED3DSIH_IMM_ATOMIC_IMAX */ NULL, + /* WINED3DSIH_IMM_ATOMIC_IMIN */ NULL, + /* WINED3DSIH_IMM_ATOMIC_OR */ NULL, + /* WINED3DSIH_IMM_ATOMIC_UMAX */ NULL, + /* WINED3DSIH_IMM_ATOMIC_UMIN */ NULL, + /* WINED3DSIH_IMM_ATOMIC_XOR */ NULL, + /* WINED3DSIH_IMUL */ NULL, + /* WINED3DSIH_INE */ NULL, + /* WINED3DSIH_INEG */ NULL, + /* WINED3DSIH_ISHL */ NULL, + /* WINED3DSIH_ISHR */ NULL, + /* WINED3DSIH_ITOF */ NULL, + /* WINED3DSIH_LABEL */ shader_hw_label, + /* WINED3DSIH_LD */ NULL, + /* WINED3DSIH_LD2DMS */ NULL, + /* WINED3DSIH_LD_RAW */ NULL, + /* WINED3DSIH_LD_STRUCTURED */ NULL, + /* WINED3DSIH_LD_UAV_TYPED */ NULL, + /* WINED3DSIH_LIT */ shader_hw_map2gl, + /* WINED3DSIH_LOD */ NULL, + /* WINED3DSIH_LOG */ shader_hw_scalar_op, + /* WINED3DSIH_LOGP */ shader_hw_scalar_op, + /* WINED3DSIH_LOOP */ shader_hw_loop, + /* WINED3DSIH_LRP */ shader_hw_lrp, + /* WINED3DSIH_LT */ NULL, + /* WINED3DSIH_M3x2 */ shader_hw_mnxn, + /* WINED3DSIH_M3x3 */ shader_hw_mnxn, + /* WINED3DSIH_M3x4 */ shader_hw_mnxn, + /* WINED3DSIH_M4x3 */ shader_hw_mnxn, + /* WINED3DSIH_M4x4 */ shader_hw_mnxn, + /* WINED3DSIH_MAD */ shader_hw_map2gl, + /* WINED3DSIH_MAX */ shader_hw_map2gl, + /* WINED3DSIH_MIN */ shader_hw_map2gl, + /* WINED3DSIH_MOV */ shader_hw_mov, + /* WINED3DSIH_MOVA */ shader_hw_mov, + /* WINED3DSIH_MOVC */ NULL, + /* WINED3DSIH_MUL */ shader_hw_map2gl, + /* WINED3DSIH_NE */ NULL, + /* WINED3DSIH_NOP */ shader_hw_nop, + /* WINED3DSIH_NOT */ NULL, + /* WINED3DSIH_NRM */ shader_hw_nrm, + /* WINED3DSIH_OR */ NULL, + /* WINED3DSIH_PHASE */ shader_hw_nop, + /* WINED3DSIH_POW */ shader_hw_pow, + /* WINED3DSIH_RCP */ shader_hw_scalar_op, + /* WINED3DSIH_REP */ shader_hw_rep, + /* WINED3DSIH_RESINFO */ NULL, + /* WINED3DSIH_RET */ shader_hw_ret, + /* WINED3DSIH_RETP */ NULL, + /* WINED3DSIH_ROUND_NE */ NULL, + /* WINED3DSIH_ROUND_NI */ NULL, + /* WINED3DSIH_ROUND_PI */ NULL, + /* WINED3DSIH_ROUND_Z */ NULL, + /* WINED3DSIH_RSQ */ shader_hw_scalar_op, + /* WINED3DSIH_SAMPLE */ NULL, + /* WINED3DSIH_SAMPLE_B */ NULL, + /* WINED3DSIH_SAMPLE_C */ NULL, + /* WINED3DSIH_SAMPLE_C_LZ */ NULL, + /* WINED3DSIH_SAMPLE_GRAD */ NULL, + /* WINED3DSIH_SAMPLE_INFO */ NULL, + /* WINED3DSIH_SAMPLE_LOD */ NULL, + /* WINED3DSIH_SAMPLE_POS */ NULL, + /* WINED3DSIH_SETP */ NULL, + /* WINED3DSIH_SGE */ shader_hw_map2gl, + /* WINED3DSIH_SGN */ shader_hw_sgn, + /* WINED3DSIH_SINCOS */ shader_hw_sincos, + /* WINED3DSIH_SLT */ shader_hw_map2gl, + /* WINED3DSIH_SQRT */ NULL, + /* WINED3DSIH_STORE_RAW */ NULL, + /* WINED3DSIH_STORE_STRUCTURED */ NULL, + /* WINED3DSIH_STORE_UAV_TYPED */ NULL, + /* WINED3DSIH_SUB */ shader_hw_map2gl, + /* WINED3DSIH_SWAPC */ NULL, + /* WINED3DSIH_SWITCH */ NULL, + /* WINED3DSIH_SYNC */ NULL, + /* WINED3DSIH_TEX */ pshader_hw_tex, + /* WINED3DSIH_TEXBEM */ pshader_hw_texbem, + /* WINED3DSIH_TEXBEML */ pshader_hw_texbem, + /* WINED3DSIH_TEXCOORD */ pshader_hw_texcoord, + /* WINED3DSIH_TEXDEPTH */ pshader_hw_texdepth, + /* WINED3DSIH_TEXDP3 */ pshader_hw_texdp3, + /* WINED3DSIH_TEXDP3TEX */ pshader_hw_texdp3tex, + /* WINED3DSIH_TEXKILL */ pshader_hw_texkill, + /* WINED3DSIH_TEXLDD */ shader_hw_texldd, + /* WINED3DSIH_TEXLDL */ shader_hw_texldl, + /* WINED3DSIH_TEXM3x2DEPTH */ pshader_hw_texm3x2depth, + /* WINED3DSIH_TEXM3x2PAD */ pshader_hw_texm3x2pad, + /* WINED3DSIH_TEXM3x2TEX */ pshader_hw_texm3x2tex, + /* WINED3DSIH_TEXM3x3 */ pshader_hw_texm3x3, + /* WINED3DSIH_TEXM3x3DIFF */ NULL, + /* WINED3DSIH_TEXM3x3PAD */ pshader_hw_texm3x3pad, + /* WINED3DSIH_TEXM3x3SPEC */ pshader_hw_texm3x3spec, + /* WINED3DSIH_TEXM3x3TEX */ pshader_hw_texm3x3tex, + /* WINED3DSIH_TEXM3x3VSPEC */ pshader_hw_texm3x3vspec, + /* WINED3DSIH_TEXREG2AR */ pshader_hw_texreg2ar, + /* WINED3DSIH_TEXREG2GB */ pshader_hw_texreg2gb, + /* WINED3DSIH_TEXREG2RGB */ pshader_hw_texreg2rgb, + /* WINED3DSIH_UBFE */ NULL, + /* WINED3DSIH_UDIV */ NULL, + /* WINED3DSIH_UGE */ NULL, + /* WINED3DSIH_ULT */ NULL, + /* WINED3DSIH_UMAX */ NULL, + /* WINED3DSIH_UMIN */ NULL, + /* WINED3DSIH_UMUL */ NULL, + /* WINED3DSIH_USHR */ NULL, + /* WINED3DSIH_UTOF */ NULL, + /* WINED3DSIH_XOR */ NULL, +}; + +static BOOL get_bool_const(const struct wined3d_shader_instruction *ins, + const struct wined3d_shader *shader, DWORD idx) +{ + const struct wined3d_shader_reg_maps *reg_maps = ins->ctx->reg_maps; + BOOL vshader = shader_is_vshader_version(reg_maps->shader_version.type); + const struct wined3d_shader_lconst *constant; + WORD bools = 0; + WORD flag = (1u << idx); + struct shader_arb_ctx_priv *priv = ins->ctx->backend_data; + + if (reg_maps->local_bool_consts & flag) + { + /* What good is an if(bool) with a hardcoded local constant? I don't know, but handle it */ + LIST_FOR_EACH_ENTRY(constant, &shader->constantsB, struct wined3d_shader_lconst, entry) + { + if (constant->idx == idx) + { + return constant->value[0]; + } + } + ERR("Local constant not found\n"); + return FALSE; + } + else + { + if(vshader) bools = priv->cur_vs_args->clip.boolclip.bools; + else bools = priv->cur_ps_args->bools; + return bools & flag; + } +} + +static void get_loop_control_const(const struct wined3d_shader_instruction *ins, + const struct wined3d_shader *shader, UINT idx, struct wined3d_shader_loop_control *loop_control) +{ + const struct wined3d_shader_reg_maps *reg_maps = ins->ctx->reg_maps; + struct shader_arb_ctx_priv *priv = ins->ctx->backend_data; + + /* Integer constants can either be a local constant, or they can be stored in the shader + * type specific compile args. */ + if (reg_maps->local_int_consts & (1u << idx)) + { + const struct wined3d_shader_lconst *constant; + + LIST_FOR_EACH_ENTRY(constant, &shader->constantsI, struct wined3d_shader_lconst, entry) + { + if (constant->idx == idx) + { + loop_control->count = constant->value[0]; + loop_control->start = constant->value[1]; + /* Step is signed. */ + loop_control->step = (int)constant->value[2]; + return; + } + } + /* If this happens the flag was set incorrectly */ + ERR("Local constant not found\n"); + loop_control->count = 0; + loop_control->start = 0; + loop_control->step = 0; + return; + } + + switch (reg_maps->shader_version.type) + { + case WINED3D_SHADER_TYPE_VERTEX: + /* Count and aL start value are unsigned */ + loop_control->count = priv->cur_vs_args->loop_ctrl[idx][0]; + loop_control->start = priv->cur_vs_args->loop_ctrl[idx][1]; + /* Step is signed. */ + loop_control->step = ((char)priv->cur_vs_args->loop_ctrl[idx][2]); + break; + + case WINED3D_SHADER_TYPE_PIXEL: + loop_control->count = priv->cur_ps_args->loop_ctrl[idx][0]; + loop_control->start = priv->cur_ps_args->loop_ctrl[idx][1]; + loop_control->step = ((char)priv->cur_ps_args->loop_ctrl[idx][2]); + break; + + default: + FIXME("Unhandled shader type %#x.\n", reg_maps->shader_version.type); + break; + } +} + +static void record_instruction(struct list *list, const struct wined3d_shader_instruction *ins) +{ + struct wined3d_shader_src_param *src_param = NULL, *rel_addr; + struct wined3d_shader_dst_param *dst_param; + struct recorded_instruction *rec; + unsigned int i; + + if (!(rec = heap_alloc_zero(sizeof(*rec)))) + { + ERR("Out of memory\n"); + return; + } + + rec->ins = *ins; + if (!(dst_param = heap_alloc(sizeof(*dst_param)))) + goto free; + *dst_param = *ins->dst; + if (ins->dst->reg.idx[0].rel_addr) + { + if (!(rel_addr = heap_alloc(sizeof(*rel_addr)))) + goto free; + *rel_addr = *ins->dst->reg.idx[0].rel_addr; + dst_param->reg.idx[0].rel_addr = rel_addr; + } + rec->ins.dst = dst_param; + + if (!(src_param = heap_calloc(ins->src_count, sizeof(*src_param)))) + goto free; + for (i = 0; i < ins->src_count; ++i) + { + src_param[i] = ins->src[i]; + if (ins->src[i].reg.idx[0].rel_addr) + { + if (!(rel_addr = heap_alloc(sizeof(*rel_addr)))) + goto free; + *rel_addr = *ins->src[i].reg.idx[0].rel_addr; + src_param[i].reg.idx[0].rel_addr = rel_addr; + } + } + rec->ins.src = src_param; + list_add_tail(list, &rec->entry); + return; + +free: + ERR("Out of memory\n"); + if (dst_param) + { + heap_free((void *)dst_param->reg.idx[0].rel_addr); + heap_free(dst_param); + } + if (src_param) + { + for (i = 0; i < ins->src_count; ++i) + { + heap_free((void *)src_param[i].reg.idx[0].rel_addr); + } + heap_free(src_param); + } + heap_free(rec); +} + +static void free_recorded_instruction(struct list *list) +{ + struct recorded_instruction *rec_ins, *entry2; + unsigned int i; + + LIST_FOR_EACH_ENTRY_SAFE(rec_ins, entry2, list, struct recorded_instruction, entry) + { + list_remove(&rec_ins->entry); + if (rec_ins->ins.dst) + { + heap_free((void *)rec_ins->ins.dst->reg.idx[0].rel_addr); + heap_free((void *)rec_ins->ins.dst); + } + if (rec_ins->ins.src) + { + for (i = 0; i < rec_ins->ins.src_count; ++i) + { + heap_free((void *)rec_ins->ins.src[i].reg.idx[0].rel_addr); + } + heap_free((void *)rec_ins->ins.src); + } + heap_free(rec_ins); + } +} + +static void pop_control_frame(const struct wined3d_shader_instruction *ins) +{ + struct shader_arb_ctx_priv *priv = ins->ctx->backend_data; + struct control_frame *control_frame; + + if (ins->handler_idx == WINED3DSIH_ENDLOOP || ins->handler_idx == WINED3DSIH_ENDREP) + { + struct list *e = list_head(&priv->control_frames); + control_frame = LIST_ENTRY(e, struct control_frame, entry); + list_remove(&control_frame->entry); + heap_free(control_frame); + priv->loop_depth--; + } + else if (ins->handler_idx == WINED3DSIH_ENDIF) + { + /* Non-ifc ENDIFs were already handled previously. */ + struct list *e = list_head(&priv->control_frames); + control_frame = LIST_ENTRY(e, struct control_frame, entry); + list_remove(&control_frame->entry); + heap_free(control_frame); + } +} + +static void shader_arb_handle_instruction(const struct wined3d_shader_instruction *ins) { + SHADER_HANDLER hw_fct; + struct shader_arb_ctx_priv *priv = ins->ctx->backend_data; + const struct wined3d_shader *shader = ins->ctx->shader; + struct control_frame *control_frame; + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + BOOL bool_const; + + if(ins->handler_idx == WINED3DSIH_LOOP || ins->handler_idx == WINED3DSIH_REP) + { + control_frame = heap_alloc_zero(sizeof(*control_frame)); + list_add_head(&priv->control_frames, &control_frame->entry); + + if(ins->handler_idx == WINED3DSIH_LOOP) control_frame->type = LOOP; + if(ins->handler_idx == WINED3DSIH_REP) control_frame->type = REP; + + if(priv->target_version >= NV2) + { + control_frame->no.loop = priv->num_loops++; + priv->loop_depth++; + } + else + { + /* Don't bother recording when we're in a not used if branch */ + if(priv->muted) + { + return; + } + + if(!priv->recording) + { + list_init(&priv->record); + priv->recording = TRUE; + control_frame->outer_loop = TRUE; + get_loop_control_const(ins, shader, ins->src[0].reg.idx[0].offset, &control_frame->loop_control); + return; /* Instruction is handled */ + } + /* Record this loop in the outer loop's recording */ + } + } + else if(ins->handler_idx == WINED3DSIH_ENDLOOP || ins->handler_idx == WINED3DSIH_ENDREP) + { + if(priv->target_version >= NV2) + { + /* Nothing to do. The control frame is popped after the HW instr handler */ + } + else + { + struct list *e = list_head(&priv->control_frames); + control_frame = LIST_ENTRY(e, struct control_frame, entry); + list_remove(&control_frame->entry); + + if(control_frame->outer_loop) + { + unsigned int iteration; + int aL = 0; + struct list copy; + + /* Turn off recording before playback */ + priv->recording = FALSE; + + /* Move the recorded instructions to a separate list and get them out of the private data + * structure. If there are nested loops, the shader_arb_handle_instruction below will + * be recorded again, thus priv->record might be overwritten + */ + list_init(©); + list_move_tail(©, &priv->record); + list_init(&priv->record); + + if(ins->handler_idx == WINED3DSIH_ENDLOOP) + { + shader_addline(buffer, "#unrolling loop: %u iterations, aL=%u, inc %d\n", + control_frame->loop_control.count, control_frame->loop_control.start, + control_frame->loop_control.step); + aL = control_frame->loop_control.start; + } + else + { + shader_addline(buffer, "#unrolling rep: %u iterations\n", control_frame->loop_control.count); + } + + for (iteration = 0; iteration < control_frame->loop_control.count; ++iteration) + { + struct recorded_instruction *rec_ins; + if(ins->handler_idx == WINED3DSIH_ENDLOOP) + { + priv->aL = aL; + shader_addline(buffer, "#Iteration %u, aL=%d\n", iteration, aL); + } + else + { + shader_addline(buffer, "#Iteration %u\n", iteration); + } + + LIST_FOR_EACH_ENTRY(rec_ins, ©, struct recorded_instruction, entry) + { + shader_arb_handle_instruction(&rec_ins->ins); + } + + if(ins->handler_idx == WINED3DSIH_ENDLOOP) + { + aL += control_frame->loop_control.step; + } + } + shader_addline(buffer, "#end loop/rep\n"); + + free_recorded_instruction(©); + heap_free(control_frame); + return; /* Instruction is handled */ + } + else + { + /* This is a nested loop. Proceed to the normal recording function */ + heap_free(control_frame); + } + } + } + + if(priv->recording) + { + record_instruction(&priv->record, ins); + return; + } + + /* boolean if */ + if(ins->handler_idx == WINED3DSIH_IF) + { + control_frame = heap_alloc_zero(sizeof(*control_frame)); + list_add_head(&priv->control_frames, &control_frame->entry); + control_frame->type = IF; + + bool_const = get_bool_const(ins, shader, ins->src[0].reg.idx[0].offset); + if (ins->src[0].modifiers == WINED3DSPSM_NOT) + bool_const = !bool_const; + if (!priv->muted && !bool_const) + { + shader_addline(buffer, "#if(FALSE){\n"); + priv->muted = TRUE; + control_frame->muting = TRUE; + } + else shader_addline(buffer, "#if(TRUE) {\n"); + + return; /* Instruction is handled */ + } + else if(ins->handler_idx == WINED3DSIH_IFC) + { + /* IF(bool) and if_cond(a, b) use the same ELSE and ENDIF tokens */ + control_frame = heap_alloc_zero(sizeof(*control_frame)); + control_frame->type = IFC; + control_frame->no.ifc = priv->num_ifcs++; + list_add_head(&priv->control_frames, &control_frame->entry); + } + else if(ins->handler_idx == WINED3DSIH_ELSE) + { + struct list *e = list_head(&priv->control_frames); + control_frame = LIST_ENTRY(e, struct control_frame, entry); + + if(control_frame->type == IF) + { + shader_addline(buffer, "#} else {\n"); + if(!priv->muted && !control_frame->muting) + { + priv->muted = TRUE; + control_frame->muting = TRUE; + } + else if(control_frame->muting) priv->muted = FALSE; + return; /* Instruction is handled. */ + } + /* In case of an ifc, generate a HW shader instruction */ + if (control_frame->type != IFC) + ERR("Control frame does not match.\n"); + } + else if(ins->handler_idx == WINED3DSIH_ENDIF) + { + struct list *e = list_head(&priv->control_frames); + control_frame = LIST_ENTRY(e, struct control_frame, entry); + + if(control_frame->type == IF) + { + shader_addline(buffer, "#} endif\n"); + if(control_frame->muting) priv->muted = FALSE; + list_remove(&control_frame->entry); + heap_free(control_frame); + return; /* Instruction is handled */ + } + /* In case of an ifc, generate a HW shader instruction */ + if (control_frame->type != IFC) + ERR("Control frame does not match.\n"); + } + + if(priv->muted) + { + pop_control_frame(ins); + return; + } + + /* Select handler */ + hw_fct = shader_arb_instruction_handler_table[ins->handler_idx]; + + /* Unhandled opcode */ + if (!hw_fct) + { + FIXME("Backend can't handle opcode %s.\n", debug_d3dshaderinstructionhandler(ins->handler_idx)); + return; + } + hw_fct(ins); + + pop_control_frame(ins); + + shader_arb_add_instruction_modifiers(ins); +} + +static BOOL shader_arb_has_ffp_proj_control(void *shader_priv) +{ + struct shader_arb_priv *priv = shader_priv; + + return priv->ffp_proj_control; +} + +static void shader_arb_precompile(void *shader_priv, struct wined3d_shader *shader) {} + +const struct wined3d_shader_backend_ops arb_program_shader_backend = +{ + shader_arb_handle_instruction, + shader_arb_precompile, + shader_arb_select, + shader_arb_select_compute, + shader_arb_disable, + shader_arb_update_float_vertex_constants, + shader_arb_update_float_pixel_constants, + shader_arb_load_constants, + shader_arb_destroy, + shader_arb_alloc, + shader_arb_free, + shader_arb_allocate_context_data, + shader_arb_free_context_data, + shader_arb_init_context_state, + shader_arb_get_caps, + shader_arb_color_fixup_supported, + shader_arb_has_ffp_proj_control, +}; + +/* ARB_fragment_program fixed function pipeline replacement definitions */ +#define ARB_FFP_CONST_TFACTOR 0 +#define ARB_FFP_CONST_COLOR_KEY_LOW ((ARB_FFP_CONST_TFACTOR) + 1) +#define ARB_FFP_CONST_COLOR_KEY_HIGH ((ARB_FFP_CONST_COLOR_KEY_LOW) + 1) +#define ARB_FFP_CONST_SPECULAR_ENABLE ((ARB_FFP_CONST_COLOR_KEY_HIGH) + 1) +#define ARB_FFP_CONST_CONSTANT(i) ((ARB_FFP_CONST_SPECULAR_ENABLE) + 1 + i) +#define ARB_FFP_CONST_BUMPMAT(i) ((ARB_FFP_CONST_CONSTANT(7)) + 1 + i) +#define ARB_FFP_CONST_LUMINANCE(i) ((ARB_FFP_CONST_BUMPMAT(7)) + 1 + i) + +struct arbfp_ffp_desc +{ + struct ffp_frag_desc parent; + GLuint shader; +}; + +/* Context activation is done by the caller. */ +static void arbfp_enable(const struct wined3d_context *context, BOOL enable) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl_const(context)->gl_info; + + if (enable) + { + gl_info->gl_ops.gl.p_glEnable(GL_FRAGMENT_PROGRAM_ARB); + checkGLcall("glEnable(GL_FRAGMENT_PROGRAM_ARB)"); + } + else + { + gl_info->gl_ops.gl.p_glDisable(GL_FRAGMENT_PROGRAM_ARB); + checkGLcall("glDisable(GL_FRAGMENT_PROGRAM_ARB)"); + } +} + +static void *arbfp_alloc(const struct wined3d_shader_backend_ops *shader_backend, void *shader_priv) +{ + struct shader_arb_priv *priv; + + /* Share private data between the shader backend and the pipeline + * replacement, if both are the arb implementation. This is needed to + * figure out whether ARBfp should be disabled if no pixel shader is bound + * or not. */ + if (shader_backend == &arb_program_shader_backend) + priv = shader_priv; + else if (!(priv = heap_alloc_zero(sizeof(*priv)))) + return NULL; + + wine_rb_init(&priv->fragment_shaders, wined3d_ffp_frag_program_key_compare); + priv->use_arbfp_fixed_func = TRUE; + + return priv; +} + +/* Context activation is done by the caller. */ +static void arbfp_free_ffpshader(struct wine_rb_entry *entry, void *param) +{ + struct arbfp_ffp_desc *entry_arb = WINE_RB_ENTRY_VALUE(entry, struct arbfp_ffp_desc, parent.entry); + struct wined3d_context_gl *context_gl = param; + const struct wined3d_gl_info *gl_info; + + gl_info = context_gl->gl_info; + GL_EXTCALL(glDeleteProgramsARB(1, &entry_arb->shader)); + checkGLcall("delete ffp program"); + heap_free(entry_arb); +} + +/* Context activation is done by the caller. */ +static void arbfp_free(struct wined3d_device *device, struct wined3d_context *context) +{ + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + struct shader_arb_priv *priv = device->fragment_priv; + + wine_rb_destroy(&priv->fragment_shaders, arbfp_free_ffpshader, context_gl); + priv->use_arbfp_fixed_func = FALSE; + + if (device->shader_backend != &arb_program_shader_backend) + heap_free(device->fragment_priv); +} + +static void arbfp_get_caps(const struct wined3d_adapter *adapter, struct fragment_caps *caps) +{ + const struct wined3d_gl_info *gl_info = &adapter->gl_info; + + caps->wined3d_caps = WINED3D_FRAGMENT_CAP_PROJ_CONTROL + | WINED3D_FRAGMENT_CAP_SRGB_WRITE + | WINED3D_FRAGMENT_CAP_COLOR_KEY; + caps->PrimitiveMiscCaps = WINED3DPMISCCAPS_TSSARGTEMP; + caps->TextureOpCaps = WINED3DTEXOPCAPS_DISABLE | + WINED3DTEXOPCAPS_SELECTARG1 | + WINED3DTEXOPCAPS_SELECTARG2 | + WINED3DTEXOPCAPS_MODULATE4X | + WINED3DTEXOPCAPS_MODULATE2X | + WINED3DTEXOPCAPS_MODULATE | + WINED3DTEXOPCAPS_ADDSIGNED2X | + WINED3DTEXOPCAPS_ADDSIGNED | + WINED3DTEXOPCAPS_ADD | + WINED3DTEXOPCAPS_SUBTRACT | + WINED3DTEXOPCAPS_ADDSMOOTH | + WINED3DTEXOPCAPS_BLENDCURRENTALPHA | + WINED3DTEXOPCAPS_BLENDFACTORALPHA | + WINED3DTEXOPCAPS_BLENDTEXTUREALPHA | + WINED3DTEXOPCAPS_BLENDDIFFUSEALPHA | + WINED3DTEXOPCAPS_BLENDTEXTUREALPHAPM | + WINED3DTEXOPCAPS_MODULATEALPHA_ADDCOLOR | + WINED3DTEXOPCAPS_MODULATECOLOR_ADDALPHA | + WINED3DTEXOPCAPS_MODULATEINVCOLOR_ADDALPHA | + WINED3DTEXOPCAPS_MODULATEINVALPHA_ADDCOLOR | + WINED3DTEXOPCAPS_DOTPRODUCT3 | + WINED3DTEXOPCAPS_MULTIPLYADD | + WINED3DTEXOPCAPS_LERP | + WINED3DTEXOPCAPS_BUMPENVMAP | + WINED3DTEXOPCAPS_BUMPENVMAPLUMINANCE; + + /* TODO: Implement WINED3DTEXOPCAPS_PREMODULATE */ + + caps->MaxTextureBlendStages = WINED3D_MAX_TEXTURES; + caps->MaxSimultaneousTextures = min(gl_info->limits.samplers[WINED3D_SHADER_TYPE_PIXEL], WINED3D_MAX_TEXTURES); +} + +static DWORD arbfp_get_emul_mask(const struct wined3d_gl_info *gl_info) +{ + return GL_EXT_EMUL_ARB_MULTITEXTURE | GL_EXT_EMUL_EXT_FOG_COORD; +} + +static void state_texfactor_arbfp(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) +{ + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + struct wined3d_device *device = context->device; + struct wined3d_color color; + + if (device->shader_backend == &arb_program_shader_backend) + { + struct shader_arb_priv *priv; + + /* Don't load the parameter if we're using an arbfp pixel shader, + * otherwise we'll overwrite application provided constants. */ + if (use_ps(state)) + return; + + priv = device->shader_priv; + priv->pshader_const_dirty[ARB_FFP_CONST_TFACTOR] = 1; + priv->highest_dirty_ps_const = max(priv->highest_dirty_ps_const, ARB_FFP_CONST_TFACTOR + 1); + } + + wined3d_color_from_d3dcolor(&color, state->render_states[WINED3D_RS_TEXTUREFACTOR]); + GL_EXTCALL(glProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, ARB_FFP_CONST_TFACTOR, &color.r)); + checkGLcall("glProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, ARB_FFP_CONST_TFACTOR, &color.r)"); +} + +static void state_tss_constant_arbfp(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) +{ + DWORD stage = (state_id - STATE_TEXTURESTAGE(0, 0)) / (WINED3D_HIGHEST_TEXTURE_STATE + 1); + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + struct wined3d_device *device = context->device; + struct wined3d_color color; + + if (device->shader_backend == &arb_program_shader_backend) + { + struct shader_arb_priv *priv; + + /* Don't load the parameter if we're using an arbfp pixel shader, otherwise we'll overwrite + * application provided constants. + */ + if (use_ps(state)) + return; + + priv = device->shader_priv; + priv->pshader_const_dirty[ARB_FFP_CONST_CONSTANT(stage)] = 1; + priv->highest_dirty_ps_const = max(priv->highest_dirty_ps_const, ARB_FFP_CONST_CONSTANT(stage) + 1); + } + + wined3d_color_from_d3dcolor(&color, state->texture_states[stage][WINED3D_TSS_CONSTANT]); + GL_EXTCALL(glProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, ARB_FFP_CONST_CONSTANT(stage), &color.r)); + checkGLcall("glProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, ARB_FFP_CONST_CONSTANT(stage), &color.r)"); +} + +static void state_arb_specularenable(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) +{ + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + struct wined3d_device *device = context->device; + float col[4]; + + if (device->shader_backend == &arb_program_shader_backend) + { + struct shader_arb_priv *priv; + + /* Don't load the parameter if we're using an arbfp pixel shader, otherwise we'll overwrite + * application provided constants. + */ + if (use_ps(state)) + return; + + priv = device->shader_priv; + priv->pshader_const_dirty[ARB_FFP_CONST_SPECULAR_ENABLE] = 1; + priv->highest_dirty_ps_const = max(priv->highest_dirty_ps_const, ARB_FFP_CONST_SPECULAR_ENABLE + 1); + } + + if (state->render_states[WINED3D_RS_SPECULARENABLE]) + { + /* The specular color has no alpha */ + col[0] = 1.0f; col[1] = 1.0f; + col[2] = 1.0f; col[3] = 0.0f; + } else { + col[0] = 0.0f; col[1] = 0.0f; + col[2] = 0.0f; col[3] = 0.0f; + } + GL_EXTCALL(glProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, ARB_FFP_CONST_SPECULAR_ENABLE, col)); + checkGLcall("glProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, ARB_FFP_CONST_SPECULAR_ENABLE, col)"); +} + +static void set_bumpmat_arbfp(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + DWORD stage = (state_id - STATE_TEXTURESTAGE(0, 0)) / (WINED3D_HIGHEST_TEXTURE_STATE + 1); + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + struct wined3d_device *device = context->device; + float mat[2][2]; + + context->constant_update_mask |= WINED3D_SHADER_CONST_PS_BUMP_ENV; + + if (device->shader_backend == &arb_program_shader_backend) + { + struct shader_arb_priv *priv = device->shader_priv; + + /* Exit now, don't set the bumpmat below, otherwise we may overwrite pixel shader constants. */ + if (use_ps(state)) + return; + + priv->pshader_const_dirty[ARB_FFP_CONST_BUMPMAT(stage)] = 1; + priv->highest_dirty_ps_const = max(priv->highest_dirty_ps_const, ARB_FFP_CONST_BUMPMAT(stage) + 1); + } + + mat[0][0] = *((float *)&state->texture_states[stage][WINED3D_TSS_BUMPENV_MAT00]); + mat[0][1] = *((float *)&state->texture_states[stage][WINED3D_TSS_BUMPENV_MAT01]); + mat[1][0] = *((float *)&state->texture_states[stage][WINED3D_TSS_BUMPENV_MAT10]); + mat[1][1] = *((float *)&state->texture_states[stage][WINED3D_TSS_BUMPENV_MAT11]); + + GL_EXTCALL(glProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, ARB_FFP_CONST_BUMPMAT(stage), &mat[0][0])); + checkGLcall("glProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, ARB_FFP_CONST_BUMPMAT(stage), &mat[0][0])"); +} + +static void tex_bumpenvlum_arbfp(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) +{ + DWORD stage = (state_id - STATE_TEXTURESTAGE(0, 0)) / (WINED3D_HIGHEST_TEXTURE_STATE + 1); + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + struct wined3d_device *device = context->device; + float param[4]; + + context->constant_update_mask |= WINED3D_SHADER_CONST_PS_BUMP_ENV; + + if (device->shader_backend == &arb_program_shader_backend) + { + struct shader_arb_priv *priv = device->shader_priv; + + /* Exit now, don't set the luminance below, otherwise we may overwrite pixel shader constants. */ + if (use_ps(state)) + return; + + priv->pshader_const_dirty[ARB_FFP_CONST_LUMINANCE(stage)] = 1; + priv->highest_dirty_ps_const = max(priv->highest_dirty_ps_const, ARB_FFP_CONST_LUMINANCE(stage) + 1); + } + + param[0] = *((float *)&state->texture_states[stage][WINED3D_TSS_BUMPENV_LSCALE]); + param[1] = *((float *)&state->texture_states[stage][WINED3D_TSS_BUMPENV_LOFFSET]); + param[2] = 0.0f; + param[3] = 0.0f; + + GL_EXTCALL(glProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, ARB_FFP_CONST_LUMINANCE(stage), param)); + checkGLcall("glProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, ARB_FFP_CONST_LUMINANCE(stage), param)"); +} + +static void alpha_test_arbfp(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + int glParm; + float ref; + + TRACE("context %p, state %p, state_id %#x.\n", context, state, state_id); + + if (state->render_states[WINED3D_RS_ALPHATESTENABLE]) + { + gl_info->gl_ops.gl.p_glEnable(GL_ALPHA_TEST); + checkGLcall("glEnable GL_ALPHA_TEST"); + } + else + { + gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST); + checkGLcall("glDisable GL_ALPHA_TEST"); + return; + } + + ref = wined3d_alpha_ref(state); + glParm = wined3d_gl_compare_func(state->render_states[WINED3D_RS_ALPHAFUNC]); + + if (glParm) + { + gl_info->gl_ops.gl.p_glAlphaFunc(glParm, ref); + checkGLcall("glAlphaFunc"); + } +} + +static void color_key_arbfp(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + const struct wined3d_texture *texture = state->textures[0]; + struct wined3d_device *device = context->device; + struct wined3d_color float_key[2]; + + if (!texture) + return; + + if (device->shader_backend == &arb_program_shader_backend) + { + struct shader_arb_priv *priv; + + /* Don't load the parameter if we're using an arbfp pixel shader, + * otherwise we'll overwrite application provided constants. */ + if (use_ps(state)) + return; + + priv = device->shader_priv; + priv->pshader_const_dirty[ARB_FFP_CONST_COLOR_KEY_LOW] = 1; + priv->pshader_const_dirty[ARB_FFP_CONST_COLOR_KEY_HIGH] = 1; + priv->highest_dirty_ps_const = max(priv->highest_dirty_ps_const, ARB_FFP_CONST_COLOR_KEY_HIGH + 1); + } + + wined3d_format_get_float_color_key(texture->resource.format, &texture->async.src_blt_color_key, float_key); + + GL_EXTCALL(glProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, ARB_FFP_CONST_COLOR_KEY_LOW, &float_key[0].r)); + checkGLcall("glProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, ARB_FFP_CONST_COLOR_KEY_LOW, &float_key[0].r)"); + GL_EXTCALL(glProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, ARB_FFP_CONST_COLOR_KEY_HIGH, &float_key[1].r)); + checkGLcall("glProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, ARB_FFP_CONST_COLOR_KEY_HIGH, &float_key[1].r)"); +} + +static const char *get_argreg(struct wined3d_string_buffer *buffer, DWORD argnum, unsigned int stage, DWORD arg) +{ + const char *ret; + + if(arg == ARG_UNUSED) return "unused"; /* This is the marker for unused registers */ + + switch(arg & WINED3DTA_SELECTMASK) { + case WINED3DTA_DIFFUSE: + ret = "fragment.color.primary"; break; + + case WINED3DTA_CURRENT: + ret = "ret"; + break; + + case WINED3DTA_TEXTURE: + switch(stage) { + case 0: ret = "tex0"; break; + case 1: ret = "tex1"; break; + case 2: ret = "tex2"; break; + case 3: ret = "tex3"; break; + case 4: ret = "tex4"; break; + case 5: ret = "tex5"; break; + case 6: ret = "tex6"; break; + case 7: ret = "tex7"; break; + default: ret = "unknown texture"; + } + break; + + case WINED3DTA_TFACTOR: + ret = "tfactor"; break; + + case WINED3DTA_SPECULAR: + ret = "fragment.color.secondary"; break; + + case WINED3DTA_TEMP: + ret = "tempreg"; break; + + case WINED3DTA_CONSTANT: + switch(stage) { + case 0: ret = "const0"; break; + case 1: ret = "const1"; break; + case 2: ret = "const2"; break; + case 3: ret = "const3"; break; + case 4: ret = "const4"; break; + case 5: ret = "const5"; break; + case 6: ret = "const6"; break; + case 7: ret = "const7"; break; + default: ret = "unknown constant"; + } + break; + + default: + return "unknown"; + } + + if(arg & WINED3DTA_COMPLEMENT) { + shader_addline(buffer, "SUB arg%u, const.x, %s;\n", argnum, ret); + if(argnum == 0) ret = "arg0"; + if(argnum == 1) ret = "arg1"; + if(argnum == 2) ret = "arg2"; + } + if(arg & WINED3DTA_ALPHAREPLICATE) { + shader_addline(buffer, "MOV arg%u, %s.w;\n", argnum, ret); + if(argnum == 0) ret = "arg0"; + if(argnum == 1) ret = "arg1"; + if(argnum == 2) ret = "arg2"; + } + return ret; +} + +static void gen_ffp_instr(struct wined3d_string_buffer *buffer, unsigned int stage, BOOL color, + BOOL alpha, BOOL tmp_dst, DWORD op, DWORD dw_arg0, DWORD dw_arg1, DWORD dw_arg2) +{ + const char *dstmask, *dstreg, *arg0, *arg1, *arg2; + unsigned int mul = 1; + + if (color && alpha) + dstmask = ""; + else if (color) + dstmask = ".xyz"; + else + dstmask = ".w"; + + dstreg = tmp_dst ? "tempreg" : "ret"; + + arg0 = get_argreg(buffer, 0, stage, dw_arg0); + arg1 = get_argreg(buffer, 1, stage, dw_arg1); + arg2 = get_argreg(buffer, 2, stage, dw_arg2); + + switch (op) + { + case WINED3D_TOP_DISABLE: + break; + + case WINED3D_TOP_SELECT_ARG2: + arg1 = arg2; + /* FALLTHROUGH */ + case WINED3D_TOP_SELECT_ARG1: + shader_addline(buffer, "MOV %s%s, %s;\n", dstreg, dstmask, arg1); + break; + + case WINED3D_TOP_MODULATE_4X: + mul = 2; + /* FALLTHROUGH */ + case WINED3D_TOP_MODULATE_2X: + mul *= 2; + /* FALLTHROUGH */ + case WINED3D_TOP_MODULATE: + shader_addline(buffer, "MUL %s%s, %s, %s;\n", dstreg, dstmask, arg1, arg2); + break; + + case WINED3D_TOP_ADD_SIGNED_2X: + mul = 2; + /* FALLTHROUGH */ + case WINED3D_TOP_ADD_SIGNED: + shader_addline(buffer, "SUB arg2, %s, const.w;\n", arg2); + arg2 = "arg2"; + /* FALLTHROUGH */ + case WINED3D_TOP_ADD: + shader_addline(buffer, "ADD_SAT %s%s, %s, %s;\n", dstreg, dstmask, arg1, arg2); + break; + + case WINED3D_TOP_SUBTRACT: + shader_addline(buffer, "SUB_SAT %s%s, %s, %s;\n", dstreg, dstmask, arg1, arg2); + break; + + case WINED3D_TOP_ADD_SMOOTH: + shader_addline(buffer, "SUB arg1, const.x, %s;\n", arg1); + shader_addline(buffer, "MAD_SAT %s%s, arg1, %s, %s;\n", dstreg, dstmask, arg2, arg1); + break; + + case WINED3D_TOP_BLEND_CURRENT_ALPHA: + arg0 = get_argreg(buffer, 0, stage, WINED3DTA_CURRENT); + shader_addline(buffer, "LRP %s%s, %s.w, %s, %s;\n", dstreg, dstmask, arg0, arg1, arg2); + break; + case WINED3D_TOP_BLEND_FACTOR_ALPHA: + arg0 = get_argreg(buffer, 0, stage, WINED3DTA_TFACTOR); + shader_addline(buffer, "LRP %s%s, %s.w, %s, %s;\n", dstreg, dstmask, arg0, arg1, arg2); + break; + case WINED3D_TOP_BLEND_TEXTURE_ALPHA: + arg0 = get_argreg(buffer, 0, stage, WINED3DTA_TEXTURE); + shader_addline(buffer, "LRP %s%s, %s.w, %s, %s;\n", dstreg, dstmask, arg0, arg1, arg2); + break; + case WINED3D_TOP_BLEND_DIFFUSE_ALPHA: + arg0 = get_argreg(buffer, 0, stage, WINED3DTA_DIFFUSE); + shader_addline(buffer, "LRP %s%s, %s.w, %s, %s;\n", dstreg, dstmask, arg0, arg1, arg2); + break; + + case WINED3D_TOP_BLEND_TEXTURE_ALPHA_PM: + arg0 = get_argreg(buffer, 0, stage, WINED3DTA_TEXTURE); + shader_addline(buffer, "SUB arg0.w, const.x, %s.w;\n", arg0); + shader_addline(buffer, "MAD_SAT %s%s, %s, arg0.w, %s;\n", dstreg, dstmask, arg2, arg1); + break; + + /* D3DTOP_PREMODULATE ???? */ + + case WINED3D_TOP_MODULATE_INVALPHA_ADD_COLOR: + shader_addline(buffer, "SUB arg0.w, const.x, %s;\n", arg1); + shader_addline(buffer, "MAD_SAT %s%s, arg0.w, %s, %s;\n", dstreg, dstmask, arg2, arg1); + break; + case WINED3D_TOP_MODULATE_ALPHA_ADD_COLOR: + shader_addline(buffer, "MAD_SAT %s%s, %s.w, %s, %s;\n", dstreg, dstmask, arg1, arg2, arg1); + break; + case WINED3D_TOP_MODULATE_INVCOLOR_ADD_ALPHA: + shader_addline(buffer, "SUB arg0, const.x, %s;\n", arg1); + shader_addline(buffer, "MAD_SAT %s%s, arg0, %s, %s.w;\n", dstreg, dstmask, arg2, arg1); + break; + case WINED3D_TOP_MODULATE_COLOR_ADD_ALPHA: + shader_addline(buffer, "MAD_SAT %s%s, %s, %s, %s.w;\n", dstreg, dstmask, arg1, arg2, arg1); + break; + + case WINED3D_TOP_DOTPRODUCT3: + mul = 4; + shader_addline(buffer, "SUB arg1, %s, const.w;\n", arg1); + shader_addline(buffer, "SUB arg2, %s, const.w;\n", arg2); + shader_addline(buffer, "DP3_SAT %s%s, arg1, arg2;\n", dstreg, dstmask); + break; + + case WINED3D_TOP_MULTIPLY_ADD: + shader_addline(buffer, "MAD_SAT %s%s, %s, %s, %s;\n", dstreg, dstmask, arg1, arg2, arg0); + break; + + case WINED3D_TOP_LERP: + /* The msdn is not quite right here */ + shader_addline(buffer, "LRP %s%s, %s, %s, %s;\n", dstreg, dstmask, arg0, arg1, arg2); + break; + + case WINED3D_TOP_BUMPENVMAP: + case WINED3D_TOP_BUMPENVMAP_LUMINANCE: + /* Those are handled in the first pass of the shader(generation pass 1 and 2) already */ + break; + + default: + FIXME("Unhandled texture op %08x\n", op); + } + + if (mul == 2) + shader_addline(buffer, "MUL_SAT %s%s, %s, const.y;\n", dstreg, dstmask, dstreg); + else if (mul == 4) + shader_addline(buffer, "MUL_SAT %s%s, %s, const.z;\n", dstreg, dstmask, dstreg); +} + +static const char *arbfp_texture_target(enum wined3d_gl_resource_type type) +{ + switch(type) + { + case WINED3D_GL_RES_TYPE_TEX_1D: + return "1D"; + case WINED3D_GL_RES_TYPE_TEX_2D: + return "2D"; + case WINED3D_GL_RES_TYPE_TEX_3D: + return "3D"; + case WINED3D_GL_RES_TYPE_TEX_CUBE: + return "CUBE"; + case WINED3D_GL_RES_TYPE_TEX_RECT: + return "RECT"; + default: + return "unexpected_resource_type"; + } +} + +static GLuint gen_arbfp_ffp_shader(const struct ffp_frag_settings *settings, const struct wined3d_gl_info *gl_info) +{ + BYTE tex_read = 0, bump_used = 0, luminance_used = 0, constant_used = 0; + BOOL tempreg_used = FALSE, tfactor_used = FALSE; + unsigned int stage, lowest_disabled_stage; + struct wined3d_string_buffer buffer; + struct color_fixup_masks masks; + BOOL custom_linear_fog = FALSE; + const char *textype, *instr; + DWORD arg0, arg1, arg2; + char colorcor_dst[8]; + BOOL op_equal; + GLuint ret; + + if (!string_buffer_init(&buffer)) + { + ERR("Failed to initialize shader buffer.\n"); + return 0; + } + + shader_addline(&buffer, "!!ARBfp1.0\n"); + + if (settings->color_key_enabled) + { + shader_addline(&buffer, "PARAM color_key_low = program.env[%u];\n", ARB_FFP_CONST_COLOR_KEY_LOW); + shader_addline(&buffer, "PARAM color_key_high = program.env[%u];\n", ARB_FFP_CONST_COLOR_KEY_HIGH); + tex_read |= 1; + } + + /* Find out which textures are read */ + for (stage = 0; stage < WINED3D_MAX_TEXTURES; ++stage) + { + if (settings->op[stage].cop == WINED3D_TOP_DISABLE) + break; + + arg0 = settings->op[stage].carg0 & WINED3DTA_SELECTMASK; + arg1 = settings->op[stage].carg1 & WINED3DTA_SELECTMASK; + arg2 = settings->op[stage].carg2 & WINED3DTA_SELECTMASK; + + if (arg0 == WINED3DTA_TEXTURE || arg1 == WINED3DTA_TEXTURE || arg2 == WINED3DTA_TEXTURE) + tex_read |= 1u << stage; + if (settings->op[stage].tmp_dst) + tempreg_used = TRUE; + if (arg0 == WINED3DTA_TEMP || arg1 == WINED3DTA_TEMP || arg2 == WINED3DTA_TEMP) + tempreg_used = TRUE; + if (arg0 == WINED3DTA_TFACTOR || arg1 == WINED3DTA_TFACTOR || arg2 == WINED3DTA_TFACTOR) + tfactor_used = TRUE; + if (arg0 == WINED3DTA_CONSTANT || arg1 == WINED3DTA_CONSTANT || arg2 == WINED3DTA_CONSTANT) + constant_used |= 1u << stage; + + switch (settings->op[stage].cop) + { + case WINED3D_TOP_BUMPENVMAP_LUMINANCE: + luminance_used |= 1u << stage; + /* fall through */ + case WINED3D_TOP_BUMPENVMAP: + bump_used |= 1u << stage; + /* fall through */ + case WINED3D_TOP_BLEND_TEXTURE_ALPHA: + case WINED3D_TOP_BLEND_TEXTURE_ALPHA_PM: + tex_read |= 1u << stage; + break; + + case WINED3D_TOP_BLEND_FACTOR_ALPHA: + tfactor_used = TRUE; + break; + + default: + break; + } + + if (settings->op[stage].aop == WINED3D_TOP_DISABLE) + continue; + + arg0 = settings->op[stage].aarg0 & WINED3DTA_SELECTMASK; + arg1 = settings->op[stage].aarg1 & WINED3DTA_SELECTMASK; + arg2 = settings->op[stage].aarg2 & WINED3DTA_SELECTMASK; + + if (arg0 == WINED3DTA_TEXTURE || arg1 == WINED3DTA_TEXTURE || arg2 == WINED3DTA_TEXTURE) + tex_read |= 1u << stage; + if (arg0 == WINED3DTA_TEMP || arg1 == WINED3DTA_TEMP || arg2 == WINED3DTA_TEMP) + tempreg_used = TRUE; + if (arg0 == WINED3DTA_TFACTOR || arg1 == WINED3DTA_TFACTOR || arg2 == WINED3DTA_TFACTOR) + tfactor_used = TRUE; + if (arg0 == WINED3DTA_CONSTANT || arg1 == WINED3DTA_CONSTANT || arg2 == WINED3DTA_CONSTANT) + constant_used |= 1u << stage; + } + lowest_disabled_stage = stage; + + switch (settings->fog) + { + case WINED3D_FFP_PS_FOG_OFF: break; + case WINED3D_FFP_PS_FOG_LINEAR: + if (gl_info->quirks & WINED3D_QUIRK_BROKEN_ARB_FOG) + { + custom_linear_fog = TRUE; + break; + } + shader_addline(&buffer, "OPTION ARB_fog_linear;\n"); + break; + + case WINED3D_FFP_PS_FOG_EXP: shader_addline(&buffer, "OPTION ARB_fog_exp;\n"); break; + case WINED3D_FFP_PS_FOG_EXP2: shader_addline(&buffer, "OPTION ARB_fog_exp2;\n"); break; + default: FIXME("Unexpected fog setting %d\n", settings->fog); + } + + shader_addline(&buffer, "PARAM const = {1, 2, 4, 0.5};\n"); + shader_addline(&buffer, "TEMP TMP;\n"); + shader_addline(&buffer, "TEMP ret;\n"); + if (tempreg_used || settings->sRGB_write) shader_addline(&buffer, "TEMP tempreg;\n"); + shader_addline(&buffer, "TEMP arg0;\n"); + shader_addline(&buffer, "TEMP arg1;\n"); + shader_addline(&buffer, "TEMP arg2;\n"); + for (stage = 0; stage < WINED3D_MAX_TEXTURES; ++stage) + { + if (constant_used & (1u << stage)) + shader_addline(&buffer, "PARAM const%u = program.env[%u];\n", stage, ARB_FFP_CONST_CONSTANT(stage)); + + if (!(tex_read & (1u << stage))) + continue; + + shader_addline(&buffer, "TEMP tex%u;\n", stage); + + if (!(bump_used & (1u << stage))) + continue; + shader_addline(&buffer, "PARAM bumpmat%u = program.env[%u];\n", stage, ARB_FFP_CONST_BUMPMAT(stage)); + + if (!(luminance_used & (1u << stage))) + continue; + shader_addline(&buffer, "PARAM luminance%u = program.env[%u];\n", stage, ARB_FFP_CONST_LUMINANCE(stage)); + } + if (tfactor_used) + shader_addline(&buffer, "PARAM tfactor = program.env[%u];\n", ARB_FFP_CONST_TFACTOR); + shader_addline(&buffer, "PARAM specular_enable = program.env[%u];\n", ARB_FFP_CONST_SPECULAR_ENABLE); + + if (settings->sRGB_write) + { + shader_addline(&buffer, "PARAM srgb_consts0 = "); + shader_arb_append_imm_vec4(&buffer, &wined3d_srgb_const[0].x); + shader_addline(&buffer, ";\n"); + shader_addline(&buffer, "PARAM srgb_consts1 = "); + shader_arb_append_imm_vec4(&buffer, &wined3d_srgb_const[1].x); + shader_addline(&buffer, ";\n"); + } + + if (lowest_disabled_stage < 7 && settings->emul_clipplanes) + shader_addline(&buffer, "KIL fragment.texcoord[7];\n"); + + if (tempreg_used || settings->sRGB_write) + shader_addline(&buffer, "MOV tempreg, 0.0;\n"); + + /* Generate texture sampling instructions */ + for (stage = 0; stage < WINED3D_MAX_TEXTURES && settings->op[stage].cop != WINED3D_TOP_DISABLE; ++stage) + { + if (!(tex_read & (1u << stage))) + continue; + + textype = arbfp_texture_target(settings->op[stage].tex_type); + + if (settings->op[stage].projected == WINED3D_PROJECTION_NONE) + { + instr = "TEX"; + } + else if (settings->op[stage].projected == WINED3D_PROJECTION_COUNT4 + || settings->op[stage].projected == WINED3D_PROJECTION_COUNT3) + { + instr = "TXP"; + } + else + { + FIXME("Unexpected projection mode %d\n", settings->op[stage].projected); + instr = "TXP"; + } + + if (stage > 0 + && (settings->op[stage - 1].cop == WINED3D_TOP_BUMPENVMAP + || settings->op[stage - 1].cop == WINED3D_TOP_BUMPENVMAP_LUMINANCE)) + { + shader_addline(&buffer, "SWZ arg1, bumpmat%u, x, z, 0, 0;\n", stage - 1); + shader_addline(&buffer, "DP3 ret.x, arg1, tex%u;\n", stage - 1); + shader_addline(&buffer, "SWZ arg1, bumpmat%u, y, w, 0, 0;\n", stage - 1); + shader_addline(&buffer, "DP3 ret.y, arg1, tex%u;\n", stage - 1); + + /* With projective textures, texbem only divides the static + * texture coordinate, not the displacement, so multiply the + * displacement with the dividing parameter before passing it to + * TXP. */ + if (settings->op[stage].projected != WINED3D_PROJECTION_NONE) + { + if (settings->op[stage].projected == WINED3D_PROJECTION_COUNT4) + { + shader_addline(&buffer, "MOV ret.w, fragment.texcoord[%u].w;\n", stage); + shader_addline(&buffer, "MUL ret.xyz, ret, fragment.texcoord[%u].w, fragment.texcoord[%u];\n", + stage, stage); + } + else + { + shader_addline(&buffer, "MOV ret.w, fragment.texcoord[%u].z;\n", stage); + shader_addline(&buffer, "MAD ret.xyz, ret, fragment.texcoord[%u].z, fragment.texcoord[%u];\n", + stage, stage); + } + } + else + { + shader_addline(&buffer, "ADD ret, ret, fragment.texcoord[%u];\n", stage); + } + + shader_addline(&buffer, "%s tex%u, ret, texture[%u], %s;\n", + instr, stage, stage, textype); + if (settings->op[stage - 1].cop == WINED3D_TOP_BUMPENVMAP_LUMINANCE) + { + shader_addline(&buffer, "MAD_SAT ret.x, tex%u.z, luminance%u.x, luminance%u.y;\n", + stage - 1, stage - 1, stage - 1); + shader_addline(&buffer, "MUL tex%u, tex%u, ret.x;\n", stage, stage); + } + } + else if (settings->op[stage].projected == WINED3D_PROJECTION_COUNT3) + { + shader_addline(&buffer, "MOV ret, fragment.texcoord[%u];\n", stage); + shader_addline(&buffer, "MOV ret.w, ret.z;\n"); + shader_addline(&buffer, "%s tex%u, ret, texture[%u], %s;\n", + instr, stage, stage, textype); + } + else + { + shader_addline(&buffer, "%s tex%u, fragment.texcoord[%u], texture[%u], %s;\n", + instr, stage, stage, stage, textype); + } + + sprintf(colorcor_dst, "tex%u", stage); + masks = calc_color_correction(settings->op[stage].color_fixup, WINED3DSP_WRITEMASK_ALL); + gen_color_correction(&buffer, colorcor_dst, colorcor_dst, "const.x", "const.y", + settings->op[stage].color_fixup, masks); + } + + if (settings->color_key_enabled) + { + shader_addline(&buffer, "SLT TMP, tex0, color_key_low;\n"); /* below low key */ + shader_addline(&buffer, "SGE ret, tex0, color_key_high;\n"); /* above high key */ + shader_addline(&buffer, "ADD TMP, TMP, ret;\n"); /* or */ + shader_addline(&buffer, "DP4 TMP.b, TMP, TMP;\n"); /* on any channel */ + shader_addline(&buffer, "SGE TMP, -TMP.b, 0.0;\n"); /* logical not */ + shader_addline(&buffer, "KIL -TMP;\n"); /* discard if true */ + } + + shader_addline(&buffer, "MOV ret, fragment.color.primary;\n"); + + /* Generate the main shader */ + for (stage = 0; stage < WINED3D_MAX_TEXTURES; ++stage) + { + if (settings->op[stage].cop == WINED3D_TOP_DISABLE) + break; + + if (settings->op[stage].cop == WINED3D_TOP_SELECT_ARG1 + && settings->op[stage].aop == WINED3D_TOP_SELECT_ARG1) + op_equal = settings->op[stage].carg1 == settings->op[stage].aarg1; + else if (settings->op[stage].cop == WINED3D_TOP_SELECT_ARG1 + && settings->op[stage].aop == WINED3D_TOP_SELECT_ARG2) + op_equal = settings->op[stage].carg1 == settings->op[stage].aarg2; + else if (settings->op[stage].cop == WINED3D_TOP_SELECT_ARG2 + && settings->op[stage].aop == WINED3D_TOP_SELECT_ARG1) + op_equal = settings->op[stage].carg2 == settings->op[stage].aarg1; + else if (settings->op[stage].cop == WINED3D_TOP_SELECT_ARG2 + && settings->op[stage].aop == WINED3D_TOP_SELECT_ARG2) + op_equal = settings->op[stage].carg2 == settings->op[stage].aarg2; + else + op_equal = settings->op[stage].aop == settings->op[stage].cop + && settings->op[stage].carg0 == settings->op[stage].aarg0 + && settings->op[stage].carg1 == settings->op[stage].aarg1 + && settings->op[stage].carg2 == settings->op[stage].aarg2; + + if (settings->op[stage].aop == WINED3D_TOP_DISABLE) + { + gen_ffp_instr(&buffer, stage, TRUE, FALSE, settings->op[stage].tmp_dst, + settings->op[stage].cop, settings->op[stage].carg0, + settings->op[stage].carg1, settings->op[stage].carg2); + } + else if (op_equal) + { + gen_ffp_instr(&buffer, stage, TRUE, TRUE, settings->op[stage].tmp_dst, + settings->op[stage].cop, settings->op[stage].carg0, + settings->op[stage].carg1, settings->op[stage].carg2); + } + else if (settings->op[stage].cop != WINED3D_TOP_BUMPENVMAP + && settings->op[stage].cop != WINED3D_TOP_BUMPENVMAP_LUMINANCE) + { + gen_ffp_instr(&buffer, stage, TRUE, FALSE, settings->op[stage].tmp_dst, + settings->op[stage].cop, settings->op[stage].carg0, + settings->op[stage].carg1, settings->op[stage].carg2); + gen_ffp_instr(&buffer, stage, FALSE, TRUE, settings->op[stage].tmp_dst, + settings->op[stage].aop, settings->op[stage].aarg0, + settings->op[stage].aarg1, settings->op[stage].aarg2); + } + } + + if (settings->sRGB_write || custom_linear_fog) + { + shader_addline(&buffer, "MAD ret, fragment.color.secondary, specular_enable, ret;\n"); + if (settings->sRGB_write) + arbfp_add_sRGB_correction(&buffer, "ret", "arg0", "arg1", "arg2", "tempreg", FALSE); + if (custom_linear_fog) + arbfp_add_linear_fog(&buffer, "ret", "arg0"); + shader_addline(&buffer, "MOV result.color, ret;\n"); + } + else + { + shader_addline(&buffer, "MAD result.color, fragment.color.secondary, specular_enable, ret;\n"); + } + + /* Footer */ + shader_addline(&buffer, "END\n"); + + /* Generate the shader */ + GL_EXTCALL(glGenProgramsARB(1, &ret)); + GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, ret)); + shader_arb_compile(gl_info, GL_FRAGMENT_PROGRAM_ARB, buffer.buffer); + + string_buffer_free(&buffer); + return ret; +} + +static void fragment_prog_arbfp(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + const struct wined3d_device *device = context->device; + struct shader_arb_priv *priv = device->fragment_priv; + BOOL use_pshader = use_ps(state); + struct ffp_frag_settings settings; + const struct arbfp_ffp_desc *desc; + unsigned int i; + + TRACE("context %p, state %p, state_id %#x.\n", context, state, state_id); + + if (isStateDirty(context, STATE_RENDER(WINED3D_RS_FOGENABLE))) + { + if (!use_pshader && device->shader_backend == &arb_program_shader_backend && context->last_was_pshader) + { + /* Reload fixed function constants since they collide with the + * pixel shader constants. */ + for (i = 0; i < WINED3D_MAX_TEXTURES; ++i) + { + set_bumpmat_arbfp(context, state, STATE_TEXTURESTAGE(i, WINED3D_TSS_BUMPENV_MAT00)); + state_tss_constant_arbfp(context, state, STATE_TEXTURESTAGE(i, WINED3D_TSS_CONSTANT)); + } + state_texfactor_arbfp(context, state, STATE_RENDER(WINED3D_RS_TEXTUREFACTOR)); + state_arb_specularenable(context, state, STATE_RENDER(WINED3D_RS_SPECULARENABLE)); + color_key_arbfp(context, state, STATE_COLOR_KEY); + } + else if (use_pshader) + { + context->shader_update_mask |= 1u << WINED3D_SHADER_TYPE_PIXEL; + } + return; + } + + if (!use_pshader) + { + /* Find or create a shader implementing the fixed function pipeline + * settings, then activate it. */ + gen_ffp_frag_op(context, state, &settings, FALSE); + desc = (const struct arbfp_ffp_desc *)find_ffp_frag_shader(&priv->fragment_shaders, &settings); + if (!desc) + { + struct arbfp_ffp_desc *new_desc; + + if (!(new_desc = heap_alloc(sizeof(*new_desc)))) + { + ERR("Out of memory\n"); + return; + } + + new_desc->parent.settings = settings; + new_desc->shader = gen_arbfp_ffp_shader(&settings, gl_info); + add_ffp_frag_shader(&priv->fragment_shaders, &new_desc->parent); + TRACE("Allocated fixed function replacement shader descriptor %p\n", new_desc); + desc = new_desc; + } + + /* Now activate the replacement program. GL_FRAGMENT_PROGRAM_ARB is already active (however, note the + * comment above the shader_select call below). If e.g. GLSL is active, the shader_select call will + * deactivate it. + */ + GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, desc->shader)); + checkGLcall("glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, desc->shader)"); + priv->current_fprogram_id = desc->shader; + + if (device->shader_backend == &arb_program_shader_backend && context->last_was_pshader) + { + /* Reload fixed function constants since they collide with the + * pixel shader constants. */ + for (i = 0; i < WINED3D_MAX_TEXTURES; ++i) + { + set_bumpmat_arbfp(context, state, STATE_TEXTURESTAGE(i, WINED3D_TSS_BUMPENV_MAT00)); + state_tss_constant_arbfp(context, state, STATE_TEXTURESTAGE(i, WINED3D_TSS_CONSTANT)); + } + state_texfactor_arbfp(context, state, STATE_RENDER(WINED3D_RS_TEXTUREFACTOR)); + state_arb_specularenable(context, state, STATE_RENDER(WINED3D_RS_SPECULARENABLE)); + color_key_arbfp(context, state, STATE_COLOR_KEY); + } + context->last_was_pshader = FALSE; + } + else if (!context->last_was_pshader) + { + if (device->shader_backend == &arb_program_shader_backend) + context->constant_update_mask |= WINED3D_SHADER_CONST_PS_F; + context->last_was_pshader = TRUE; + } + + context->shader_update_mask |= 1u << WINED3D_SHADER_TYPE_PIXEL; +} + +/* We can't link the fog states to the fragment state directly since the + * vertex pipeline links them to FOGENABLE. A different linking in different + * pipeline parts can't be expressed in the combined state table, so we need + * to handle that with a forwarding function. The other invisible side effect + * is that changing the fog start and fog end (which links to FOGENABLE in + * vertex) results in the fragment_prog_arbfp function being called because + * FOGENABLE is dirty, which calls this function here. */ +static void state_arbfp_fog(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + enum fogsource new_source; + DWORD fogstart = state->render_states[WINED3D_RS_FOGSTART]; + DWORD fogend = state->render_states[WINED3D_RS_FOGEND]; + + TRACE("context %p, state %p, state_id %#x.\n", context, state, state_id); + + if (!isStateDirty(context, STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL))) + fragment_prog_arbfp(context, state, state_id); + + if (!state->render_states[WINED3D_RS_FOGENABLE]) + return; + + if (state->render_states[WINED3D_RS_FOGTABLEMODE] == WINED3D_FOG_NONE) + { + if (use_vs(state)) + { + new_source = FOGSOURCE_VS; + } + else + { + if (state->render_states[WINED3D_RS_FOGVERTEXMODE] == WINED3D_FOG_NONE || context->last_was_rhw) + new_source = FOGSOURCE_COORD; + else + new_source = FOGSOURCE_FFP; + } + } + else + { + new_source = FOGSOURCE_FFP; + } + + if (new_source != context->fog_source || fogstart == fogend) + { + context->fog_source = new_source; + state_fogstartend(context, state, STATE_RENDER(WINED3D_RS_FOGSTART)); + } +} + +static void textransform(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + if (!isStateDirty(context, STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL))) + fragment_prog_arbfp(context, state, state_id); +} + +static const struct wined3d_state_entry_template arbfp_fragmentstate_template[] = +{ + {STATE_RENDER(WINED3D_RS_TEXTUREFACTOR), { STATE_RENDER(WINED3D_RS_TEXTUREFACTOR), state_texfactor_arbfp }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(0, WINED3D_TSS_COLOR_OP), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(0, WINED3D_TSS_COLOR_ARG1), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(0, WINED3D_TSS_COLOR_ARG2), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(0, WINED3D_TSS_COLOR_ARG0), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(0, WINED3D_TSS_ALPHA_OP), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(0, WINED3D_TSS_ALPHA_ARG1), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(0, WINED3D_TSS_ALPHA_ARG2), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(0, WINED3D_TSS_ALPHA_ARG0), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(0, WINED3D_TSS_RESULT_ARG), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_MAT00), { STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_MAT00), set_bumpmat_arbfp }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_MAT01), { STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_MAT10), { STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_MAT11), { STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_LSCALE), { STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_LSCALE), tex_bumpenvlum_arbfp }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_LOFFSET), { STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_LSCALE), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(1, WINED3D_TSS_COLOR_OP), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(1, WINED3D_TSS_COLOR_ARG1), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(1, WINED3D_TSS_COLOR_ARG2), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(1, WINED3D_TSS_COLOR_ARG0), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(1, WINED3D_TSS_ALPHA_OP), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(1, WINED3D_TSS_ALPHA_ARG1), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(1, WINED3D_TSS_ALPHA_ARG2), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(1, WINED3D_TSS_ALPHA_ARG0), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(1, WINED3D_TSS_RESULT_ARG), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_MAT00), { STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_MAT00), set_bumpmat_arbfp }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_MAT01), { STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_MAT10), { STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_MAT11), { STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_LSCALE), { STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_LSCALE), tex_bumpenvlum_arbfp }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_LOFFSET), { STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_LSCALE), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(2, WINED3D_TSS_COLOR_OP), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(2, WINED3D_TSS_COLOR_ARG1), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(2, WINED3D_TSS_COLOR_ARG2), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(2, WINED3D_TSS_COLOR_ARG0), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(2, WINED3D_TSS_ALPHA_OP), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(2, WINED3D_TSS_ALPHA_ARG1), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(2, WINED3D_TSS_ALPHA_ARG2), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(2, WINED3D_TSS_ALPHA_ARG0), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(2, WINED3D_TSS_RESULT_ARG), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_MAT00), { STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_MAT00), set_bumpmat_arbfp }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_MAT01), { STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_MAT10), { STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_MAT11), { STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_LSCALE), { STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_LSCALE), tex_bumpenvlum_arbfp }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_LOFFSET), { STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_LSCALE), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(3, WINED3D_TSS_COLOR_OP), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(3, WINED3D_TSS_COLOR_ARG1), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(3, WINED3D_TSS_COLOR_ARG2), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(3, WINED3D_TSS_COLOR_ARG0), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(3, WINED3D_TSS_ALPHA_OP), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(3, WINED3D_TSS_ALPHA_ARG1), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(3, WINED3D_TSS_ALPHA_ARG2), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(3, WINED3D_TSS_ALPHA_ARG0), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(3, WINED3D_TSS_RESULT_ARG), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_MAT00), { STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_MAT00), set_bumpmat_arbfp }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_MAT01), { STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_MAT10), { STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_MAT11), { STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_LSCALE), { STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_LSCALE), tex_bumpenvlum_arbfp }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_LOFFSET), { STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_LSCALE), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(4, WINED3D_TSS_COLOR_OP), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(4, WINED3D_TSS_COLOR_ARG1), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(4, WINED3D_TSS_COLOR_ARG2), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(4, WINED3D_TSS_COLOR_ARG0), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(4, WINED3D_TSS_ALPHA_OP), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(4, WINED3D_TSS_ALPHA_ARG1), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(4, WINED3D_TSS_ALPHA_ARG2), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(4, WINED3D_TSS_ALPHA_ARG0), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(4, WINED3D_TSS_RESULT_ARG), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_MAT00), { STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_MAT00), set_bumpmat_arbfp }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_MAT01), { STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_MAT10), { STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_MAT11), { STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_LSCALE), { STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_LSCALE), tex_bumpenvlum_arbfp }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_LOFFSET), { STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_LSCALE), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(5, WINED3D_TSS_COLOR_OP), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(5, WINED3D_TSS_COLOR_ARG1), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(5, WINED3D_TSS_COLOR_ARG2), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(5, WINED3D_TSS_COLOR_ARG0), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(5, WINED3D_TSS_ALPHA_OP), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(5, WINED3D_TSS_ALPHA_ARG1), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(5, WINED3D_TSS_ALPHA_ARG2), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(5, WINED3D_TSS_ALPHA_ARG0), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(5, WINED3D_TSS_RESULT_ARG), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_MAT00), { STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_MAT00), set_bumpmat_arbfp }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_MAT01), { STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_MAT10), { STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_MAT11), { STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_LSCALE), { STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_LSCALE), tex_bumpenvlum_arbfp }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_LOFFSET), { STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_LSCALE), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(6, WINED3D_TSS_COLOR_OP), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(6, WINED3D_TSS_COLOR_ARG1), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(6, WINED3D_TSS_COLOR_ARG2), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(6, WINED3D_TSS_COLOR_ARG0), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(6, WINED3D_TSS_ALPHA_OP), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(6, WINED3D_TSS_ALPHA_ARG1), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(6, WINED3D_TSS_ALPHA_ARG2), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(6, WINED3D_TSS_ALPHA_ARG0), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(6, WINED3D_TSS_RESULT_ARG), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_MAT00), { STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_MAT00), set_bumpmat_arbfp }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_MAT01), { STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_MAT10), { STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_MAT11), { STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_LSCALE), { STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_LSCALE), tex_bumpenvlum_arbfp }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_LOFFSET), { STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_LSCALE), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(7, WINED3D_TSS_COLOR_OP), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(7, WINED3D_TSS_COLOR_ARG1), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(7, WINED3D_TSS_COLOR_ARG2), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(7, WINED3D_TSS_COLOR_ARG0), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(7, WINED3D_TSS_ALPHA_OP), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(7, WINED3D_TSS_ALPHA_ARG1), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(7, WINED3D_TSS_ALPHA_ARG2), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(7, WINED3D_TSS_ALPHA_ARG0), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(7, WINED3D_TSS_RESULT_ARG), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_MAT00), { STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_MAT00), set_bumpmat_arbfp }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_MAT01), { STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_MAT10), { STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_MAT11), { STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_LSCALE), { STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_LSCALE), tex_bumpenvlum_arbfp }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_LOFFSET), { STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_LSCALE), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), fragment_prog_arbfp }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_ALPHAFUNC), { STATE_RENDER(WINED3D_RS_ALPHATESTENABLE), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_ALPHAREF), { STATE_RENDER(WINED3D_RS_ALPHATESTENABLE), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_ALPHATESTENABLE), { STATE_RENDER(WINED3D_RS_ALPHATESTENABLE), alpha_test_arbfp }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_COLORKEYENABLE), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_COLOR_KEY, { STATE_COLOR_KEY, color_key_arbfp }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_FOGENABLE), { STATE_RENDER(WINED3D_RS_FOGENABLE), state_arbfp_fog }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_FOGTABLEMODE), { STATE_RENDER(WINED3D_RS_FOGENABLE), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_FOGVERTEXMODE), { STATE_RENDER(WINED3D_RS_FOGENABLE), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_FOGSTART), { STATE_RENDER(WINED3D_RS_FOGSTART), state_fogstartend }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_FOGEND), { STATE_RENDER(WINED3D_RS_FOGSTART), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_SRGBWRITEENABLE), { STATE_RENDER(WINED3D_RS_SRGBWRITEENABLE), state_srgbwrite }, ARB_FRAMEBUFFER_SRGB }, + {STATE_RENDER(WINED3D_RS_SRGBWRITEENABLE), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_FOGCOLOR), { STATE_RENDER(WINED3D_RS_FOGCOLOR), state_fogcolor }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_FOGDENSITY), { STATE_RENDER(WINED3D_RS_FOGDENSITY), state_fogdensity }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(0,WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), {STATE_TEXTURESTAGE(0,WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), textransform}, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(1,WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), {STATE_TEXTURESTAGE(1,WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), textransform}, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(2,WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), {STATE_TEXTURESTAGE(2,WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), textransform}, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(3,WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), {STATE_TEXTURESTAGE(3,WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), textransform}, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(4,WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), {STATE_TEXTURESTAGE(4,WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), textransform}, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(5,WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), {STATE_TEXTURESTAGE(5,WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), textransform}, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(6,WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), {STATE_TEXTURESTAGE(6,WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), textransform}, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(7,WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), {STATE_TEXTURESTAGE(7,WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), textransform}, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(0, WINED3D_TSS_CONSTANT), { STATE_TEXTURESTAGE(0, WINED3D_TSS_CONSTANT), state_tss_constant_arbfp}, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(1, WINED3D_TSS_CONSTANT), { STATE_TEXTURESTAGE(1, WINED3D_TSS_CONSTANT), state_tss_constant_arbfp}, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(2, WINED3D_TSS_CONSTANT), { STATE_TEXTURESTAGE(2, WINED3D_TSS_CONSTANT), state_tss_constant_arbfp}, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(3, WINED3D_TSS_CONSTANT), { STATE_TEXTURESTAGE(3, WINED3D_TSS_CONSTANT), state_tss_constant_arbfp}, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(4, WINED3D_TSS_CONSTANT), { STATE_TEXTURESTAGE(4, WINED3D_TSS_CONSTANT), state_tss_constant_arbfp}, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(5, WINED3D_TSS_CONSTANT), { STATE_TEXTURESTAGE(5, WINED3D_TSS_CONSTANT), state_tss_constant_arbfp}, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(6, WINED3D_TSS_CONSTANT), { STATE_TEXTURESTAGE(6, WINED3D_TSS_CONSTANT), state_tss_constant_arbfp}, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(7, WINED3D_TSS_CONSTANT), { STATE_TEXTURESTAGE(7, WINED3D_TSS_CONSTANT), state_tss_constant_arbfp}, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_SPECULARENABLE), { STATE_RENDER(WINED3D_RS_SPECULARENABLE), state_arb_specularenable}, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_SHADEMODE), { STATE_RENDER(WINED3D_RS_SHADEMODE), state_shademode }, WINED3D_GL_EXT_NONE }, + {0 /* Terminate */, { 0, 0 }, WINED3D_GL_EXT_NONE }, +}; + +static BOOL arbfp_alloc_context_data(struct wined3d_context *context) +{ + return TRUE; +} + +static void arbfp_free_context_data(struct wined3d_context *context) +{ +} + +const struct wined3d_fragment_pipe_ops arbfp_fragment_pipeline = +{ + arbfp_enable, + arbfp_get_caps, + arbfp_get_emul_mask, + arbfp_alloc, + arbfp_free, + arbfp_alloc_context_data, + arbfp_free_context_data, + shader_arb_color_fixup_supported, + arbfp_fragmentstate_template, +}; + +struct arbfp_blit_type +{ + enum complex_fixup fixup : 4; + enum wined3d_gl_resource_type res_type : 3; + DWORD use_color_key : 1; + DWORD padding : 24; +}; + +struct arbfp_blit_desc +{ + GLuint shader; + struct arbfp_blit_type type; + struct wine_rb_entry entry; +}; + +#define ARBFP_BLIT_PARAM_SIZE 0 +#define ARBFP_BLIT_PARAM_COLOR_KEY_LOW 1 +#define ARBFP_BLIT_PARAM_COLOR_KEY_HIGH 2 + +struct wined3d_arbfp_blitter +{ + struct wined3d_blitter blitter; + struct wine_rb_tree shaders; + GLuint palette_texture; +}; + +static int arbfp_blit_type_compare(const void *key, const struct wine_rb_entry *entry) +{ + const struct arbfp_blit_type *ka = key; + const struct arbfp_blit_type *kb = &WINE_RB_ENTRY_VALUE(entry, const struct arbfp_blit_desc, entry)->type; + + return memcmp(ka, kb, sizeof(*ka)); +} + +/* Context activation is done by the caller. */ +static void arbfp_free_blit_shader(struct wine_rb_entry *entry, void *ctx) +{ + struct arbfp_blit_desc *entry_arb = WINE_RB_ENTRY_VALUE(entry, struct arbfp_blit_desc, entry); + const struct wined3d_gl_info *gl_info; + struct wined3d_context_gl *context_gl; + + context_gl = ctx; + gl_info = context_gl->gl_info; + + GL_EXTCALL(glDeleteProgramsARB(1, &entry_arb->shader)); + checkGLcall("glDeleteProgramsARB(1, &entry_arb->shader)"); + heap_free(entry_arb); +} + +/* Context activation is done by the caller. */ +static void arbfp_blitter_destroy(struct wined3d_blitter *blitter, struct wined3d_context *context) +{ + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + struct wined3d_arbfp_blitter *arbfp_blitter; + struct wined3d_blitter *next; + + if ((next = blitter->next)) + next->ops->blitter_destroy(next, &context_gl->c); + + arbfp_blitter = CONTAINING_RECORD(blitter, struct wined3d_arbfp_blitter, blitter); + + wine_rb_destroy(&arbfp_blitter->shaders, arbfp_free_blit_shader, context_gl); + checkGLcall("Delete blit programs"); + + if (arbfp_blitter->palette_texture) + gl_info->gl_ops.gl.p_glDeleteTextures(1, &arbfp_blitter->palette_texture); + + heap_free(arbfp_blitter); +} + +static void gen_packed_yuv_read(struct wined3d_string_buffer *buffer, + const struct arbfp_blit_type *type, char *luminance) +{ + char chroma; + const char *tex, *texinstr = "TXP"; + + if (type->fixup == COMPLEX_FIXUP_UYVY) + { + chroma = 'x'; + *luminance = 'w'; + } + else + { + chroma = 'w'; + *luminance = 'x'; + } + + tex = arbfp_texture_target(type->res_type); + if (type->res_type == WINED3D_GL_RES_TYPE_TEX_RECT) + texinstr = "TEX"; + + /* First we have to read the chroma values. This means we need at least two pixels(no filtering), + * or 4 pixels(with filtering). To get the unmodified chromas, we have to rid ourselves of the + * filtering when we sample the texture. + * + * These are the rules for reading the chroma: + * + * Even pixel: Cr + * Even pixel: U + * Odd pixel: V + * + * So we have to get the sampling x position in non-normalized coordinates in integers + */ + if (type->res_type != WINED3D_GL_RES_TYPE_TEX_RECT) + { + shader_addline(buffer, "MUL texcrd.xy, fragment.texcoord[0], size.x;\n"); + shader_addline(buffer, "MOV texcrd.w, size.x;\n"); + } + else + { + shader_addline(buffer, "MOV texcrd, fragment.texcoord[0];\n"); + } + /* We must not allow filtering between pixel x and x+1, this would mix U and V + * Vertical filtering is ok. However, bear in mind that the pixel center is at + * 0.5, so add 0.5. + */ + shader_addline(buffer, "FLR texcrd.x, texcrd.x;\n"); + shader_addline(buffer, "ADD texcrd.x, texcrd.x, coef.y;\n"); + + /* Multiply the x coordinate by 0.5 and get the fraction. This gives 0.25 + * and 0.75 for the even and odd pixels respectively. */ + shader_addline(buffer, "MUL texcrd2, texcrd, coef.y;\n"); + shader_addline(buffer, "FRC texcrd2, texcrd2;\n"); + + /* Sample Pixel 1. */ + shader_addline(buffer, "%s luminance, texcrd, texture[0], %s;\n", texinstr, tex); + + /* Put the value into either of the chroma values */ + shader_addline(buffer, "SGE temp.x, texcrd2.x, coef.y;\n"); + shader_addline(buffer, "MUL chroma.x, luminance.%c, temp.x;\n", chroma); + shader_addline(buffer, "SLT temp.x, texcrd2.x, coef.y;\n"); + shader_addline(buffer, "MUL chroma.y, luminance.%c, temp.x;\n", chroma); + + /* Sample pixel 2. If we read an even pixel(SLT above returned 1), sample + * the pixel right to the current one. Otherwise, sample the left pixel. + * Bias and scale the SLT result to -1;1 and add it to the texcrd.x. + */ + shader_addline(buffer, "MAD temp.x, temp.x, coef.z, -coef.x;\n"); + shader_addline(buffer, "ADD texcrd.x, texcrd, temp.x;\n"); + shader_addline(buffer, "%s luminance, texcrd, texture[0], %s;\n", texinstr, tex); + + /* Put the value into the other chroma */ + shader_addline(buffer, "SGE temp.x, texcrd2.x, coef.y;\n"); + shader_addline(buffer, "MAD chroma.y, luminance.%c, temp.x, chroma.y;\n", chroma); + shader_addline(buffer, "SLT temp.x, texcrd2.x, coef.y;\n"); + shader_addline(buffer, "MAD chroma.x, luminance.%c, temp.x, chroma.x;\n", chroma); + + /* TODO: If filtering is enabled, sample a 2nd pair of pixels left or right of + * the current one and lerp the two U and V values + */ + + /* This gives the correctly filtered luminance value */ + shader_addline(buffer, "TEX luminance, fragment.texcoord[0], texture[0], %s;\n", tex); +} + +static void gen_yv12_read(struct wined3d_string_buffer *buffer, + const struct arbfp_blit_type *type, char *luminance) +{ + const char *tex; + static const float yv12_coef[] + = {2.0f / 3.0f, 1.0f / 6.0f, (2.0f / 3.0f) + (1.0f / 6.0f), 1.0f / 3.0f}; + + tex = arbfp_texture_target(type->res_type); + + /* YV12 surfaces contain a WxH sized luminance plane, followed by a (W/2)x(H/2) + * V and a (W/2)x(H/2) U plane, each with 8 bit per pixel. So the effective + * bitdepth is 12 bits per pixel. Since the U and V planes have only half the + * pitch of the luminance plane, the packing into the gl texture is a bit + * unfortunate. If the whole texture is interpreted as luminance data it looks + * approximately like this: + * + * +----------------------------------+---- + * | | + * | | + * | | + * | | + * | | 2 + * | LUMINANCE | - + * | | 3 + * | | + * | | + * | | + * | | + * +----------------+-----------------+---- + * | | | + * | V even rows | V odd rows | + * | | | 1 + * +----------------+------------------ - + * | | | 3 + * | U even rows | U odd rows | + * | | | + * +----------------+-----------------+---- + * | | | + * | 0.5 | 0.5 | + * + * So it appears as if there are 4 chroma images, but in fact the odd rows + * in the chroma images are in the same row as the even ones. So it is + * kinda tricky to read + * + * When reading from rectangle textures, keep in mind that the input y coordinates + * go from 0 to d3d_height, whereas the opengl texture height is 1.5 * d3d_height + */ + shader_addline(buffer, "PARAM yv12_coef = "); + shader_arb_append_imm_vec4(buffer, yv12_coef); + shader_addline(buffer, ";\n"); + + shader_addline(buffer, "MOV texcrd, fragment.texcoord[0];\n"); + /* the chroma planes have only half the width */ + shader_addline(buffer, "MUL texcrd.x, texcrd.x, coef.y;\n"); + + /* The first value is between 2/3 and 5/6th of the texture's height, so scale+bias + * the coordinate. Also read the right side of the image when reading odd lines + * + * Don't forget to clamp the y values in into the range, otherwise we'll get filtering + * bleeding + */ + if (type->res_type == WINED3D_GL_RES_TYPE_TEX_2D) + { + shader_addline(buffer, "RCP chroma.w, size.y;\n"); + + shader_addline(buffer, "MUL texcrd2.y, texcrd.y, size.y;\n"); + + shader_addline(buffer, "FLR texcrd2.y, texcrd2.y;\n"); + shader_addline(buffer, "MAD texcrd.y, texcrd.y, yv12_coef.y, yv12_coef.x;\n"); + + /* Read odd lines from the right side (add size * 0.5 to the x coordinate). */ + shader_addline(buffer, "ADD texcrd2.x, texcrd2.y, yv12_coef.y;\n"); /* To avoid 0.5 == 0.5 comparisons */ + shader_addline(buffer, "FRC texcrd2.x, texcrd2.x;\n"); + shader_addline(buffer, "SGE texcrd2.x, texcrd2.x, coef.y;\n"); + shader_addline(buffer, "MAD texcrd.x, texcrd2.x, coef.y, texcrd.x;\n"); + + /* clamp, keep the half pixel origin in mind */ + shader_addline(buffer, "MAD temp.y, coef.y, chroma.w, yv12_coef.x;\n"); + shader_addline(buffer, "MAX texcrd.y, temp.y, texcrd.y;\n"); + shader_addline(buffer, "MAD temp.y, -coef.y, chroma.w, yv12_coef.z;\n"); + shader_addline(buffer, "MIN texcrd.y, temp.y, texcrd.y;\n"); + } + else + { + /* The y coordinate for V is in the range [size, size + size / 4). */ + shader_addline(buffer, "FLR texcrd.y, texcrd.y;\n"); + shader_addline(buffer, "MAD texcrd.y, texcrd.y, coef.w, size.y;\n"); + + /* Read odd lines from the right side (add size * 0.5 to the x coordinate). */ + shader_addline(buffer, "ADD texcrd2.x, texcrd.y, yv12_coef.y;\n"); /* To avoid 0.5 == 0.5 comparisons */ + shader_addline(buffer, "FRC texcrd2.x, texcrd2.x;\n"); + shader_addline(buffer, "SGE texcrd2.x, texcrd2.x, coef.y;\n"); + shader_addline(buffer, "MUL texcrd2.x, texcrd2.x, size.x;\n"); + shader_addline(buffer, "MAD texcrd.x, texcrd2.x, coef.y, texcrd.x;\n"); + + /* Make sure to read exactly from the pixel center */ + shader_addline(buffer, "FLR texcrd.y, texcrd.y;\n"); + shader_addline(buffer, "ADD texcrd.y, texcrd.y, coef.y;\n"); + + /* Clamp */ + shader_addline(buffer, "MAD temp.y, size.y, coef.w, size.y;\n"); + shader_addline(buffer, "ADD temp.y, temp.y, -coef.y;\n"); + shader_addline(buffer, "MIN texcrd.y, temp.y, texcrd.y;\n"); + shader_addline(buffer, "ADD temp.y, size.y, coef.y;\n"); + shader_addline(buffer, "MAX texcrd.y, temp.y, texcrd.y;\n"); + } + /* Read the texture, put the result into the output register */ + shader_addline(buffer, "TEX temp, texcrd, texture[0], %s;\n", tex); + shader_addline(buffer, "MOV chroma.x, temp.w;\n"); + + /* The other chroma value is 1/6th of the texture lower, from 5/6th to 6/6th + * No need to clamp because we're just reusing the already clamped value from above + */ + if (type->res_type == WINED3D_GL_RES_TYPE_TEX_2D) + shader_addline(buffer, "ADD texcrd.y, texcrd.y, yv12_coef.y;\n"); + else + shader_addline(buffer, "MAD texcrd.y, size.y, coef.w, texcrd.y;\n"); + shader_addline(buffer, "TEX temp, texcrd, texture[0], %s;\n", tex); + shader_addline(buffer, "MOV chroma.y, temp.w;\n"); + + /* Sample the luminance value. It is in the top 2/3rd of the texture, so scale the y coordinate. + * Clamp the y coordinate to prevent the chroma values from bleeding into the sampled luminance + * values due to filtering + */ + shader_addline(buffer, "MOV texcrd, fragment.texcoord[0];\n"); + if (type->res_type == WINED3D_GL_RES_TYPE_TEX_2D) + { + /* Multiply the y coordinate by 2/3 and clamp it */ + shader_addline(buffer, "MUL texcrd.y, texcrd.y, yv12_coef.x;\n"); + shader_addline(buffer, "MAD temp.y, -coef.y, chroma.w, yv12_coef.x;\n"); + shader_addline(buffer, "MIN texcrd.y, temp.y, texcrd.y;\n"); + shader_addline(buffer, "TEX luminance, texcrd, texture[0], %s;\n", tex); + } + else + { + /* Reading from texture_rectangles is pretty straightforward, just use the unmodified + * texture coordinate. It is still a good idea to clamp it though, since the opengl texture + * is bigger + */ + shader_addline(buffer, "ADD temp.x, size.y, -coef.y;\n"); + shader_addline(buffer, "MIN texcrd.y, texcrd.y, size.x;\n"); + shader_addline(buffer, "TEX luminance, texcrd, texture[0], %s;\n", tex); + } + *luminance = 'a'; +} + +static void gen_nv12_read(struct wined3d_string_buffer *buffer, + const struct arbfp_blit_type *type, char *luminance) +{ + const char *tex; + static const float nv12_coef[] + = {2.0f / 3.0f, 1.0f / 3.0f, 1.0f, 1.0f}; + + tex = arbfp_texture_target(type->res_type); + + /* NV12 surfaces contain a WxH sized luminance plane, followed by a (W/2)x(H/2) + * sized plane where each component is an UV pair. So the effective + * bitdepth is 12 bits per pixel If the whole texture is interpreted as luminance + * data it looks approximately like this: + * + * +----------------------------------+---- + * | | + * | | + * | | + * | | + * | | 2 + * | LUMINANCE | - + * | | 3 + * | | + * | | + * | | + * | | + * +----------------------------------+---- + * |UVUVUVUVUVUVUVUVUVUVUVUVUVUVUVUVUV| + * |UVUVUVUVUVUVUVUVUVUVUVUVUVUVUVUVUV| + * | | 1 + * | | - + * | | 3 + * | | + * | | + * +----------------------------------+---- + * + * When reading from rectangle textures, keep in mind that the input y coordinates + * go from 0 to d3d_height, whereas the opengl texture height is 1.5 * d3d_height. */ + + shader_addline(buffer, "PARAM nv12_coef = "); + shader_arb_append_imm_vec4(buffer, nv12_coef); + shader_addline(buffer, ";\n"); + + shader_addline(buffer, "MOV texcrd, fragment.texcoord[0];\n"); + /* We only have half the number of chroma pixels. */ + shader_addline(buffer, "MUL texcrd.x, texcrd.x, coef.y;\n"); + + if (type->res_type == WINED3D_GL_RES_TYPE_TEX_2D) + { + shader_addline(buffer, "RCP chroma.w, size.x;\n"); + shader_addline(buffer, "RCP chroma.z, size.y;\n"); + + shader_addline(buffer, "MAD texcrd.y, texcrd.y, nv12_coef.y, nv12_coef.x;\n"); + + /* We must not allow filtering horizontally, this would mix U and V. + * Vertical filtering is ok. However, bear in mind that the pixel center is at + * 0.5, so add 0.5. */ + + /* Convert to non-normalized coordinates so we can find the + * individual pixel. */ + shader_addline(buffer, "MUL texcrd.x, texcrd.x, size.x;\n"); + shader_addline(buffer, "FLR texcrd.x, texcrd.x;\n"); + /* Multiply by 2 since chroma components are stored in UV pixel pairs, + * add 0.5 to hit the center of the pixel. */ + shader_addline(buffer, "MAD texcrd.x, texcrd.x, coef.z, coef.y;\n"); + + /* Convert back to normalized coordinates. */ + shader_addline(buffer, "MUL texcrd.x, texcrd.x, chroma.w;\n"); + + /* Clamp, keep the half pixel origin in mind. */ + shader_addline(buffer, "MAD temp.y, coef.y, chroma.z, nv12_coef.x;\n"); + shader_addline(buffer, "MAX texcrd.y, temp.y, texcrd.y;\n"); + shader_addline(buffer, "MAD temp.y, -coef.y, chroma.z, nv12_coef.z;\n"); + shader_addline(buffer, "MIN texcrd.y, temp.y, texcrd.y;\n"); + } + else + { + /* The y coordinate for chroma is in the range [size, size + size / 2). */ + shader_addline(buffer, "MAD texcrd.y, texcrd.y, coef.y, size.y;\n"); + + shader_addline(buffer, "FLR texcrd.x, texcrd.x;\n"); + /* Multiply by 2 since chroma components are stored in UV pixel pairs, + * add 0.5 to hit the center of the pixel. */ + shader_addline(buffer, "MAD texcrd.x, texcrd.x, coef.z, coef.y;\n"); + + /* Clamp */ + shader_addline(buffer, "MAD temp.y, size.y, coef.y, size.y;\n"); + shader_addline(buffer, "ADD temp.y, temp.y, -coef.y;\n"); + shader_addline(buffer, "MIN texcrd.y, temp.y, texcrd.y;\n"); + shader_addline(buffer, "ADD temp.y, size.y, coef.y;\n"); + shader_addline(buffer, "MAX texcrd.y, temp.y, texcrd.y;\n"); + } + /* Read the texture, put the result into the output register. */ + shader_addline(buffer, "TEX temp, texcrd, texture[0], %s;\n", tex); + shader_addline(buffer, "MOV chroma.y, temp.w;\n"); + + if (type->res_type == WINED3D_GL_RES_TYPE_TEX_2D) + { + /* Add 1/size.x */ + shader_addline(buffer, "ADD texcrd.x, texcrd.x, chroma.w;\n"); + } + else + { + /* Add 1 */ + shader_addline(buffer, "ADD texcrd.x, texcrd.x, coef.x;\n"); + } + shader_addline(buffer, "TEX temp, texcrd, texture[0], %s;\n", tex); + shader_addline(buffer, "MOV chroma.x, temp.w;\n"); + + /* Sample the luminance value. It is in the top 2/3rd of the texture, so scale the y coordinate. + * Clamp the y coordinate to prevent the chroma values from bleeding into the sampled luminance + * values due to filtering. */ + shader_addline(buffer, "MOV texcrd, fragment.texcoord[0];\n"); + if (type->res_type == WINED3D_GL_RES_TYPE_TEX_2D) + { + /* Multiply the y coordinate by 2/3 and clamp it */ + shader_addline(buffer, "MUL texcrd.y, texcrd.y, nv12_coef.x;\n"); + shader_addline(buffer, "MAD temp.y, -coef.y, chroma.w, nv12_coef.x;\n"); + shader_addline(buffer, "MIN texcrd.y, temp.y, texcrd.y;\n"); + shader_addline(buffer, "TEX luminance, texcrd, texture[0], %s;\n", tex); + } + else + { + /* Reading from texture_rectangles is pretty straightforward, just use the unmodified + * texture coordinate. It is still a good idea to clamp it though, since the opengl texture + * is bigger + */ + shader_addline(buffer, "ADD temp.x, size.y, -coef.y;\n"); + shader_addline(buffer, "MIN texcrd.y, texcrd.y, size.x;\n"); + shader_addline(buffer, "TEX luminance, texcrd, texture[0], %s;\n", tex); + } + *luminance = 'a'; +} + +/* Context activation is done by the caller. */ +static GLuint gen_p8_shader(const struct wined3d_gl_info *gl_info, const struct arbfp_blit_type *type) +{ + GLuint shader; + struct wined3d_string_buffer buffer; + const char *tex_target = arbfp_texture_target(type->res_type); + + /* This should not happen because we only use this conversion for + * present blits which don't use color keying. */ + if (type->use_color_key) + FIXME("Implement P8 color keying.\n"); + + /* Shader header */ + if (!string_buffer_init(&buffer)) + { + ERR("Failed to initialize shader buffer.\n"); + return 0; + } + + GL_EXTCALL(glGenProgramsARB(1, &shader)); + GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, shader)); + if (!shader) + { + string_buffer_free(&buffer); + return 0; + } + + shader_addline(&buffer, "!!ARBfp1.0\n"); + shader_addline(&buffer, "TEMP index;\n"); + + /* { 255/256, 0.5/255*255/256, 0, 0 } */ + shader_addline(&buffer, "PARAM constants = { 0.996, 0.00195, 0, 0 };\n"); + + /* The alpha-component contains the palette index */ + shader_addline(&buffer, "TEX index, fragment.texcoord[0], texture[0], %s;\n", tex_target); + + /* Scale the index by 255/256 and add a bias of '0.5' in order to sample in the middle */ + shader_addline(&buffer, "MAD index.a, index.a, constants.x, constants.y;\n"); + + /* Use the alpha-component as an index in the palette to get the final color */ + shader_addline(&buffer, "TEX result.color, index.a, texture[1], 1D;\n"); + shader_addline(&buffer, "END\n"); + + shader_arb_compile(gl_info, GL_FRAGMENT_PROGRAM_ARB, buffer.buffer); + + string_buffer_free(&buffer); + + return shader; +} + +/* Context activation is done by the caller. */ +static void arbfp_blitter_upload_palette(struct wined3d_arbfp_blitter *blitter, + const struct wined3d_texture_gl *texture_gl, struct wined3d_context_gl *context_gl) +{ + const struct wined3d_palette *palette = texture_gl->t.swapchain ? texture_gl->t.swapchain->palette : NULL; + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + + if (!blitter->palette_texture) + gl_info->gl_ops.gl.p_glGenTextures(1, &blitter->palette_texture); + + GL_EXTCALL(glActiveTexture(GL_TEXTURE1)); + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_1D, blitter->palette_texture); + + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + + gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + /* Make sure we have discrete color levels. */ + gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + /* TODO: avoid unneeded uploads in the future by adding some SFLAG_PALETTE_DIRTY mechanism */ + if (palette) + { + gl_info->gl_ops.gl.p_glTexImage1D(GL_TEXTURE_1D, 0, GL_RGB, 256, 0, GL_BGRA, + GL_UNSIGNED_INT_8_8_8_8_REV, palette->colors); + } + else + { + static const DWORD black; + FIXME("P8 surface loaded without a palette.\n"); + gl_info->gl_ops.gl.p_glTexImage1D(GL_TEXTURE_1D, 0, GL_RGB, 1, 0, GL_BGRA, + GL_UNSIGNED_INT_8_8_8_8_REV, &black); + } + + /* Switch back to unit 0 in which the 2D texture will be stored. */ + wined3d_context_gl_active_texture(context_gl, gl_info, 0); +} + +/* Context activation is done by the caller. */ +static GLuint gen_yuv_shader(const struct wined3d_gl_info *gl_info, const struct arbfp_blit_type *type) +{ + GLuint shader; + struct wined3d_string_buffer buffer; + char luminance_component; + + if (type->use_color_key) + FIXME("Implement YUV color keying.\n"); + + /* Shader header */ + if (!string_buffer_init(&buffer)) + { + ERR("Failed to initialize shader buffer.\n"); + return 0; + } + + GL_EXTCALL(glGenProgramsARB(1, &shader)); + checkGLcall("GL_EXTCALL(glGenProgramsARB(1, &shader))"); + GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, shader)); + checkGLcall("glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, shader)"); + if (!shader) + { + string_buffer_free(&buffer); + return 0; + } + + /* The YUY2 and UYVY formats contain two pixels packed into a 32 bit macropixel, + * giving effectively 16 bit per pixel. The color consists of a luminance(Y) and + * two chroma(U and V) values. Each macropixel has two luminance values, one for + * each single pixel it contains, and one U and one V value shared between both + * pixels. + * + * The data is loaded into an A8L8 texture. With YUY2, the luminance component + * contains the luminance and alpha the chroma. With UYVY it is vice versa. Thus + * take the format into account when generating the read swizzles + * + * Reading the Y value is straightforward - just sample the texture. The hardware + * takes care of filtering in the horizontal and vertical direction. + * + * Reading the U and V values is harder. We have to avoid filtering horizontally, + * because that would mix the U and V values of one pixel or two adjacent pixels. + * Thus floor the texture coordinate and add 0.5 to get an unfiltered read, + * regardless of the filtering setting. Vertical filtering works automatically + * though - the U and V values of two rows are mixed nicely. + * + * Apart of avoiding filtering issues, the code has to know which value it just + * read, and where it can find the other one. To determine this, it checks if + * it sampled an even or odd pixel, and shifts the 2nd read accordingly. + * + * Handling horizontal filtering of U and V values requires reading a 2nd pair + * of pixels, extracting U and V and mixing them. This is not implemented yet. + * + * An alternative implementation idea is to load the texture as A8R8G8B8 texture, + * with width / 2. This way one read gives all 3 values, finding U and V is easy + * in an unfiltered situation. Finding the luminance on the other hand requires + * finding out if it is an odd or even pixel. The real drawback of this approach + * is filtering. This would have to be emulated completely in the shader, reading + * up two 2 packed pixels in up to 2 rows and interpolating both horizontally and + * vertically. Beyond that it would require adjustments to the texture handling + * code to deal with the width scaling + */ + shader_addline(&buffer, "!!ARBfp1.0\n"); + shader_addline(&buffer, "TEMP luminance;\n"); + shader_addline(&buffer, "TEMP temp;\n"); + shader_addline(&buffer, "TEMP chroma;\n"); + shader_addline(&buffer, "TEMP texcrd;\n"); + shader_addline(&buffer, "TEMP texcrd2;\n"); + shader_addline(&buffer, "PARAM coef = {1.0, 0.5, 2.0, 0.25};\n"); + shader_addline(&buffer, "PARAM yuv_coef = {1.403, 0.344, 0.714, 1.770};\n"); + shader_addline(&buffer, "PARAM size = program.local[%u];\n", ARBFP_BLIT_PARAM_SIZE); + + switch (type->fixup) + { + case COMPLEX_FIXUP_UYVY: + case COMPLEX_FIXUP_YUY2: + gen_packed_yuv_read(&buffer, type, &luminance_component); + break; + + case COMPLEX_FIXUP_YV12: + gen_yv12_read(&buffer, type, &luminance_component); + break; + + case COMPLEX_FIXUP_NV12: + gen_nv12_read(&buffer, type, &luminance_component); + break; + + default: + FIXME("Unsupported YUV fixup %#x\n", type->fixup); + string_buffer_free(&buffer); + return 0; + } + + /* Calculate the final result. Formula is taken from + * http://www.fourcc.org/fccyvrgb.php. Note that the chroma + * ranges from -0.5 to 0.5 + */ + shader_addline(&buffer, "SUB chroma.xy, chroma, coef.y;\n"); + + shader_addline(&buffer, "MAD result.color.x, chroma.x, yuv_coef.x, luminance.%c;\n", luminance_component); + shader_addline(&buffer, "MAD temp.x, -chroma.y, yuv_coef.y, luminance.%c;\n", luminance_component); + shader_addline(&buffer, "MAD result.color.y, -chroma.x, yuv_coef.z, temp.x;\n"); + shader_addline(&buffer, "MAD result.color.z, chroma.y, yuv_coef.w, luminance.%c;\n", luminance_component); + shader_addline(&buffer, "END\n"); + + shader_arb_compile(gl_info, GL_FRAGMENT_PROGRAM_ARB, buffer.buffer); + + string_buffer_free(&buffer); + + return shader; +} + +/* Context activation is done by the caller. */ +static GLuint arbfp_gen_plain_shader(const struct wined3d_gl_info *gl_info, const struct arbfp_blit_type *type) +{ + GLuint shader; + struct wined3d_string_buffer buffer; + const char *tex_target = arbfp_texture_target(type->res_type); + + /* Shader header */ + if (!string_buffer_init(&buffer)) + { + ERR("Failed to initialize shader buffer.\n"); + return 0; + } + + GL_EXTCALL(glGenProgramsARB(1, &shader)); + if (!shader) + { + string_buffer_free(&buffer); + return 0; + } + GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, shader)); + + shader_addline(&buffer, "!!ARBfp1.0\n"); + + if (type->use_color_key) + { + shader_addline(&buffer, "TEMP color;\n"); + shader_addline(&buffer, "TEMP less, greater;\n"); + shader_addline(&buffer, "PARAM color_key_low = program.local[%u];\n", ARBFP_BLIT_PARAM_COLOR_KEY_LOW); + shader_addline(&buffer, "PARAM color_key_high = program.local[%u];\n", ARBFP_BLIT_PARAM_COLOR_KEY_HIGH); + shader_addline(&buffer, "TEX color, fragment.texcoord[0], texture[0], %s;\n", tex_target); + shader_addline(&buffer, "SLT less, color, color_key_low;\n"); /* below low key */ + shader_addline(&buffer, "SGE greater, color, color_key_high;\n"); /* above high key */ + shader_addline(&buffer, "ADD less, less, greater;\n"); /* or */ + shader_addline(&buffer, "DP4 less.b, less, less;\n"); /* on any channel */ + shader_addline(&buffer, "SGE less, -less.b, 0.0;\n"); /* logical not */ + shader_addline(&buffer, "KIL -less;\n"); /* discard if true */ + shader_addline(&buffer, "MOV result.color, color;\n"); + } + else + { + shader_addline(&buffer, "TEX result.color, fragment.texcoord[0], texture[0], %s;\n", tex_target); + } + + shader_addline(&buffer, "END\n"); + + shader_arb_compile(gl_info, GL_FRAGMENT_PROGRAM_ARB, buffer.buffer); + + string_buffer_free(&buffer); + + return shader; +} + +/* Context activation is done by the caller. */ +static HRESULT arbfp_blit_set(struct wined3d_arbfp_blitter *blitter, struct wined3d_context_gl *context_gl, + const struct wined3d_texture_gl *texture_gl, unsigned int sub_resource_idx, + const struct wined3d_color_key *color_key) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + enum complex_fixup fixup; + struct wine_rb_entry *entry; + struct arbfp_blit_type type; + struct arbfp_blit_desc *desc; + struct wined3d_color float_color_key[2]; + struct wined3d_vec4 size; + unsigned int level; + GLuint shader; + + level = sub_resource_idx % texture_gl->t.level_count; + size.x = wined3d_texture_get_level_pow2_width(&texture_gl->t, level); + size.y = wined3d_texture_get_level_pow2_height(&texture_gl->t, level); + size.z = 1.0f; + size.w = 1.0f; + + if (is_complex_fixup(texture_gl->t.resource.format->color_fixup)) + fixup = get_complex_fixup(texture_gl->t.resource.format->color_fixup); + else + fixup = COMPLEX_FIXUP_NONE; + + switch (texture_gl->target) + { + case GL_TEXTURE_1D: + type.res_type = WINED3D_GL_RES_TYPE_TEX_1D; + break; + + case GL_TEXTURE_2D: + type.res_type = WINED3D_GL_RES_TYPE_TEX_2D; + break; + + case GL_TEXTURE_3D: + type.res_type = WINED3D_GL_RES_TYPE_TEX_3D; + break; + + case GL_TEXTURE_CUBE_MAP_ARB: + type.res_type = WINED3D_GL_RES_TYPE_TEX_CUBE; + break; + + case GL_TEXTURE_RECTANGLE_ARB: + type.res_type = WINED3D_GL_RES_TYPE_TEX_RECT; + break; + + default: + ERR("Unexpected GL texture type %#x.\n", texture_gl->target); + type.res_type = WINED3D_GL_RES_TYPE_TEX_2D; + } + type.fixup = fixup; + type.use_color_key = !!color_key; + type.padding = 0; + + if ((entry = wine_rb_get(&blitter->shaders, &type))) + { + desc = WINE_RB_ENTRY_VALUE(entry, struct arbfp_blit_desc, entry); + shader = desc->shader; + } + else + { + switch (fixup) + { + case COMPLEX_FIXUP_NONE: + if (!is_identity_fixup(texture_gl->t.resource.format->color_fixup)) + FIXME("Implement support for sign or swizzle fixups.\n"); + shader = arbfp_gen_plain_shader(gl_info, &type); + break; + + case COMPLEX_FIXUP_P8: + shader = gen_p8_shader(gl_info, &type); + break; + + case COMPLEX_FIXUP_YUY2: + case COMPLEX_FIXUP_UYVY: + case COMPLEX_FIXUP_YV12: + case COMPLEX_FIXUP_NV12: + shader = gen_yuv_shader(gl_info, &type); + break; + + default: + FIXME("Unsupported fixup %#x.\n", fixup); + return E_NOTIMPL; + } + + if (!shader) + { + ERR("Failed to get shader for fixup %#x.\n", fixup); + return E_NOTIMPL; + } + + if (!(desc = heap_alloc(sizeof(*desc)))) + goto err_out; + + desc->type = type; + desc->shader = shader; + if (wine_rb_put(&blitter->shaders, &desc->type, &desc->entry) == -1) + { +err_out: + ERR("Out of memory\n"); + GL_EXTCALL(glDeleteProgramsARB(1, &shader)); + checkGLcall("GL_EXTCALL(glDeleteProgramsARB(1, &shader))"); + GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, 0)); + checkGLcall("glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, 0)"); + heap_free(desc); + return E_OUTOFMEMORY; + } + } + + if (fixup == COMPLEX_FIXUP_P8) + arbfp_blitter_upload_palette(blitter, texture_gl, context_gl); + + gl_info->gl_ops.gl.p_glEnable(GL_FRAGMENT_PROGRAM_ARB); + checkGLcall("glEnable(GL_FRAGMENT_PROGRAM_ARB)"); + GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, shader)); + checkGLcall("glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, shader)"); + GL_EXTCALL(glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, ARBFP_BLIT_PARAM_SIZE, &size.x)); + checkGLcall("glProgramLocalParameter4fvARB"); + if (type.use_color_key) + { + wined3d_format_get_float_color_key(texture_gl->t.resource.format, color_key, float_color_key); + GL_EXTCALL(glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, + ARBFP_BLIT_PARAM_COLOR_KEY_LOW, &float_color_key[0].r)); + GL_EXTCALL(glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, + ARBFP_BLIT_PARAM_COLOR_KEY_HIGH, &float_color_key[1].r)); + checkGLcall("glProgramLocalParameter4fvARB"); + } + + return WINED3D_OK; +} + +/* Context activation is done by the caller. */ +static void arbfp_blit_unset(const struct wined3d_gl_info *gl_info) +{ + gl_info->gl_ops.gl.p_glDisable(GL_FRAGMENT_PROGRAM_ARB); + checkGLcall("glDisable(GL_FRAGMENT_PROGRAM_ARB)"); +} + +static BOOL arbfp_blit_supported(enum wined3d_blit_op blit_op, const struct wined3d_context *context, + const struct wined3d_resource *src_resource, DWORD src_location, + const struct wined3d_resource *dst_resource, DWORD dst_location) +{ + const struct wined3d_format *src_format = src_resource->format; + const struct wined3d_format *dst_format = dst_resource->format; + enum complex_fixup src_fixup; + BOOL decompress; + + if (src_resource->type != WINED3D_RTYPE_TEXTURE_2D) + return FALSE; + + if (blit_op == WINED3D_BLIT_OP_RAW_BLIT && dst_format->id == src_format->id) + { + if (dst_format->depth_size || dst_format->stencil_size) + blit_op = WINED3D_BLIT_OP_DEPTH_BLIT; + else + blit_op = WINED3D_BLIT_OP_COLOR_BLIT; + } + + switch (blit_op) + { + case WINED3D_BLIT_OP_COLOR_BLIT_CKEY: + if (!context->d3d_info->shader_color_key) + { + /* The conversion modifies the alpha channel so the color key might no longer match. */ + TRACE("Color keying not supported with converted textures.\n"); + return FALSE; + } + case WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST: + case WINED3D_BLIT_OP_COLOR_BLIT: + break; + + default: + TRACE("Unsupported blit_op=%d\n", blit_op); + return FALSE; + } + + decompress = (src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED) + && !(dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED); + if (!decompress && !(src_resource->access & dst_resource->access & WINED3D_RESOURCE_ACCESS_GPU)) + return FALSE; + + src_fixup = get_complex_fixup(src_format->color_fixup); + if (TRACE_ON(d3d_shader) && TRACE_ON(d3d)) + { + TRACE("Checking support for fixup:\n"); + dump_color_fixup_desc(src_format->color_fixup); + } + + if (!is_identity_fixup(dst_format->color_fixup) + && (dst_format->id != src_format->id || dst_location != WINED3D_LOCATION_DRAWABLE)) + { + TRACE("Destination fixups are not supported\n"); + return FALSE; + } + + if (is_identity_fixup(src_format->color_fixup)) + { + TRACE("[OK]\n"); + return TRUE; + } + + /* We only support YUV conversions. */ + if (!is_complex_fixup(src_format->color_fixup)) + { + if (wined3d_settings.offscreen_rendering_mode == ORM_BACKBUFFER) + { + WARN("Claiming fixup support because of ORM_BACKBUFFER.\n"); + return TRUE; + } + + TRACE("[FAILED]\n"); + return FALSE; + } + + switch(src_fixup) + { + case COMPLEX_FIXUP_YUY2: + case COMPLEX_FIXUP_UYVY: + case COMPLEX_FIXUP_YV12: + case COMPLEX_FIXUP_NV12: + case COMPLEX_FIXUP_P8: + TRACE("[OK]\n"); + return TRUE; + + default: + FIXME("Unsupported YUV fixup %#x\n", src_fixup); + TRACE("[FAILED]\n"); + return FALSE; + } +} + +static DWORD arbfp_blitter_blit(struct wined3d_blitter *blitter, enum wined3d_blit_op op, + struct wined3d_context *context, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx, + DWORD src_location, const RECT *src_rect, struct wined3d_texture *dst_texture, + unsigned int dst_sub_resource_idx, DWORD dst_location, const RECT *dst_rect, + const struct wined3d_color_key *color_key, enum wined3d_texture_filter_type filter) +{ + struct wined3d_texture_gl *src_texture_gl = wined3d_texture_gl(src_texture); + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + struct wined3d_device *device = dst_texture->resource.device; + struct wined3d_texture *staging_texture = NULL; + struct wined3d_arbfp_blitter *arbfp_blitter; + struct wined3d_color_key alpha_test_key; + struct wined3d_blitter *next; + unsigned int src_level; + RECT s, d; + + TRACE("blitter %p, op %#x, context %p, src_texture %p, src_sub_resource_idx %u, src_location %s, src_rect %s, " + "dst_texture %p, dst_sub_resource_idx %u, dst_location %s, dst_rect %s, colour_key %p, filter %s.\n", + blitter, op, context, src_texture, src_sub_resource_idx, wined3d_debug_location(src_location), + wine_dbgstr_rect(src_rect), dst_texture, dst_sub_resource_idx, wined3d_debug_location(dst_location), + wine_dbgstr_rect(dst_rect), color_key, debug_d3dtexturefiltertype(filter)); + + if (!arbfp_blit_supported(op, context, &src_texture->resource, src_location, + &dst_texture->resource, dst_location)) + { + if (!(next = blitter->next)) + { + ERR("No blitter to handle blit op %#x.\n", op); + return dst_location; + } + + TRACE("Forwarding to blitter %p.\n", next); + return next->ops->blitter_blit(next, op, context, src_texture, src_sub_resource_idx, src_location, + src_rect, dst_texture, dst_sub_resource_idx, dst_location, dst_rect, color_key, filter); + } + + arbfp_blitter = CONTAINING_RECORD(blitter, struct wined3d_arbfp_blitter, blitter); + + if (!(src_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU)) + { + struct wined3d_resource_desc desc; + struct wined3d_box upload_box; + HRESULT hr; + + TRACE("Source texture is not GPU accessible, creating a staging texture.\n"); + + src_level = src_sub_resource_idx % src_texture->level_count; + desc.resource_type = WINED3D_RTYPE_TEXTURE_2D; + desc.format = src_texture->resource.format->id; + desc.multisample_type = src_texture->resource.multisample_type; + desc.multisample_quality = src_texture->resource.multisample_quality; + desc.usage = WINED3DUSAGE_PRIVATE; + desc.bind_flags = 0; + desc.access = WINED3D_RESOURCE_ACCESS_GPU; + desc.width = wined3d_texture_get_level_width(src_texture, src_level); + desc.height = wined3d_texture_get_level_height(src_texture, src_level); + desc.depth = 1; + desc.size = 0; + + if (FAILED(hr = wined3d_texture_create(device, &desc, 1, 1, 0, + NULL, NULL, &wined3d_null_parent_ops, &staging_texture))) + { + ERR("Failed to create staging texture, hr %#x.\n", hr); + return dst_location; + } + + wined3d_box_set(&upload_box, 0, 0, desc.width, desc.height, 0, desc.depth); + wined3d_texture_upload_from_texture(staging_texture, 0, 0, 0, 0, + src_texture, src_sub_resource_idx, &upload_box); + + src_texture = staging_texture; + src_texture_gl = wined3d_texture_gl(src_texture); + src_sub_resource_idx = 0; + } + else if (wined3d_settings.offscreen_rendering_mode != ORM_FBO + && (src_texture->sub_resources[src_sub_resource_idx].locations + & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_DRAWABLE)) == WINED3D_LOCATION_DRAWABLE + && !wined3d_resource_is_offscreen(&src_texture->resource)) + { + + /* Without FBO blits transferring from the drawable to the texture is + * expensive, because we have to flip the data in sysmem. Since we can + * flip in the blitter, we don't actually need that flip anyway. So we + * use the surface's texture as scratch texture, and flip the source + * rectangle instead. */ + texture2d_load_fb_texture(src_texture_gl, src_sub_resource_idx, FALSE, context); + + s = *src_rect; + src_level = src_sub_resource_idx % src_texture->level_count; + s.top = wined3d_texture_get_level_height(src_texture, src_level) - s.top; + s.bottom = wined3d_texture_get_level_height(src_texture, src_level) - s.bottom; + src_rect = &s; + } + else + { + wined3d_texture_load(src_texture, context, FALSE); + } + + wined3d_context_gl_apply_ffp_blit_state(context_gl, device); + + if (dst_location == WINED3D_LOCATION_DRAWABLE) + { + d = *dst_rect; + wined3d_texture_translate_drawable_coords(dst_texture, context_gl->window, &d); + dst_rect = &d; + } + + if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) + { + GLenum buffer; + + if (dst_location == WINED3D_LOCATION_DRAWABLE) + { + TRACE("Destination texture %p is onscreen.\n", dst_texture); + buffer = wined3d_texture_get_gl_buffer(dst_texture); + } + else + { + TRACE("Destination texture %p is offscreen.\n", dst_texture); + buffer = GL_COLOR_ATTACHMENT0; + } + wined3d_context_gl_apply_fbo_state_blit(context_gl, GL_DRAW_FRAMEBUFFER, + &dst_texture->resource, dst_sub_resource_idx, NULL, 0, dst_location); + wined3d_context_gl_set_draw_buffer(context_gl, buffer); + wined3d_context_gl_check_fbo_status(context_gl, GL_DRAW_FRAMEBUFFER); + context_invalidate_state(context, STATE_FRAMEBUFFER); + } + + if (op == WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST) + { + const struct wined3d_format *fmt = src_texture->resource.format; + alpha_test_key.color_space_low_value = 0; + alpha_test_key.color_space_high_value = ~(((1u << fmt->alpha_size) - 1) << fmt->alpha_offset); + color_key = &alpha_test_key; + } + + arbfp_blit_set(arbfp_blitter, context_gl, src_texture_gl, src_sub_resource_idx, color_key); + + /* Draw a textured quad */ + wined3d_context_gl_draw_textured_quad(context_gl, src_texture_gl, + src_sub_resource_idx, src_rect, dst_rect, filter); + + /* Leave the opengl state valid for blitting */ + arbfp_blit_unset(context_gl->gl_info); + + if (dst_texture->swapchain && (dst_texture->swapchain->front_buffer == dst_texture)) + context_gl->gl_info->gl_ops.gl.p_glFlush(); + + if (staging_texture) + wined3d_texture_decref(staging_texture); + + return dst_location; +} + +static void arbfp_blitter_clear(struct wined3d_blitter *blitter, struct wined3d_device *device, + unsigned int rt_count, const struct wined3d_fb_state *fb, unsigned int rect_count, const RECT *clear_rects, + const RECT *draw_rect, DWORD flags, const struct wined3d_color *colour, float depth, DWORD stencil) +{ + struct wined3d_blitter *next; + + if ((next = blitter->next)) + next->ops->blitter_clear(next, device, rt_count, fb, rect_count, + clear_rects, draw_rect, flags, colour, depth, stencil); +} + +static const struct wined3d_blitter_ops arbfp_blitter_ops = +{ + arbfp_blitter_destroy, + arbfp_blitter_clear, + arbfp_blitter_blit, +}; + +void wined3d_arbfp_blitter_create(struct wined3d_blitter **next, const struct wined3d_device *device) +{ + const struct wined3d_gl_info *gl_info = &device->adapter->gl_info; + struct wined3d_arbfp_blitter *blitter; + + if (device->shader_backend != &arb_program_shader_backend + && device->shader_backend != &glsl_shader_backend) + return; + + if (!gl_info->supported[ARB_FRAGMENT_PROGRAM]) + return; + + if (!gl_info->supported[WINED3D_GL_LEGACY_CONTEXT]) + return; + + if (!(blitter = heap_alloc(sizeof(*blitter)))) + { + ERR("Failed to allocate blitter.\n"); + return; + } + + TRACE("Created blitter %p.\n", blitter); + + blitter->blitter.ops = &arbfp_blitter_ops; + blitter->blitter.next = *next; + wine_rb_init(&blitter->shaders, arbfp_blit_type_compare); + blitter->palette_texture = 0; + *next = &blitter->blitter; +} diff --git a/wrappers/directx/d3dwine_wrapper/ati_fragment_shader.c b/wrappers/directx/d3dwine_wrapper/ati_fragment_shader.c new file mode 100644 index 00000000000..ae2843db403 --- /dev/null +++ b/wrappers/directx/d3dwine_wrapper/ati_fragment_shader.c @@ -0,0 +1,1395 @@ +/* + * Fixed function pipeline replacement using GL_ATI_fragment_shader + * + * Copyright 2008 Stefan Dösinger(for CodeWeavers) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "config.h" +#include "wine/port.h" + +#include + +#include "wined3d_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(d3d_shader); +WINE_DECLARE_DEBUG_CHANNEL(d3d); + +/* Context activation for state handlers is done by the caller. */ + +/* Some private defines, Constant associations, etc. + * Env bump matrix and per stage constant should be independent, + * a stage that bump maps can't read the per state constant + */ +#define ATIFS_CONST_BUMPMAT(i) (GL_CON_0_ATI + i) +#define ATIFS_CONST_STAGE(i) (GL_CON_0_ATI + i) +#define ATIFS_CONST_TFACTOR GL_CON_6_ATI + +enum atifs_constant_value +{ + ATIFS_CONSTANT_UNUSED = 0, + ATIFS_CONSTANT_BUMP, + ATIFS_CONSTANT_TFACTOR, + ATIFS_CONSTANT_STAGE, +}; + +/* GL_ATI_fragment_shader specific fixed function pipeline description. "Inherits" from the common one */ +struct atifs_ffp_desc +{ + struct ffp_frag_desc parent; + GLuint shader; + unsigned int num_textures_used; + enum atifs_constant_value constants[WINED3D_MAX_TEXTURES]; +}; + +struct atifs_private_data +{ + struct wine_rb_tree fragment_shaders; /* A rb-tree to track fragment pipeline replacement shaders */ +}; + +struct atifs_context_private_data +{ + const struct atifs_ffp_desc *last_shader; +}; + +static const char *debug_dstmod(GLuint mod) { + switch(mod) { + case GL_NONE: return "GL_NONE"; + case GL_2X_BIT_ATI: return "GL_2X_BIT_ATI"; + case GL_4X_BIT_ATI: return "GL_4X_BIT_ATI"; + case GL_8X_BIT_ATI: return "GL_8X_BIT_ATI"; + case GL_HALF_BIT_ATI: return "GL_HALF_BIT_ATI"; + case GL_QUARTER_BIT_ATI: return "GL_QUARTER_BIT_ATI"; + case GL_EIGHTH_BIT_ATI: return "GL_EIGHTH_BIT_ATI"; + case GL_SATURATE_BIT_ATI: return "GL_SATURATE_BIT_ATI"; + default: return "Unexpected modifier\n"; + } +} + +static const char *debug_argmod(GLuint mod) { + switch(mod) { + case GL_NONE: + return "GL_NONE"; + + case GL_2X_BIT_ATI: + return "GL_2X_BIT_ATI"; + case GL_COMP_BIT_ATI: + return "GL_COMP_BIT_ATI"; + case GL_NEGATE_BIT_ATI: + return "GL_NEGATE_BIT_ATI"; + case GL_BIAS_BIT_ATI: + return "GL_BIAS_BIT_ATI"; + + case GL_2X_BIT_ATI | GL_COMP_BIT_ATI: + return "GL_2X_BIT_ATI | GL_COMP_BIT_ATI"; + case GL_2X_BIT_ATI | GL_NEGATE_BIT_ATI: + return "GL_2X_BIT_ATI | GL_NEGATE_BIT_ATI"; + case GL_2X_BIT_ATI | GL_BIAS_BIT_ATI: + return "GL_2X_BIT_ATI | GL_BIAS_BIT_ATI"; + case GL_COMP_BIT_ATI | GL_NEGATE_BIT_ATI: + return "GL_COMP_BIT_ATI | GL_NEGATE_BIT_ATI"; + case GL_COMP_BIT_ATI | GL_BIAS_BIT_ATI: + return "GL_COMP_BIT_ATI | GL_BIAS_BIT_ATI"; + case GL_NEGATE_BIT_ATI | GL_BIAS_BIT_ATI: + return "GL_NEGATE_BIT_ATI | GL_BIAS_BIT_ATI"; + + case GL_COMP_BIT_ATI | GL_NEGATE_BIT_ATI | GL_BIAS_BIT_ATI: + return "GL_COMP_BIT_ATI | GL_NEGATE_BIT_ATI | GL_BIAS_BIT_ATI"; + case GL_2X_BIT_ATI | GL_NEGATE_BIT_ATI | GL_BIAS_BIT_ATI: + return "GL_2X_BIT_ATI | GL_NEGATE_BIT_ATI | GL_BIAS_BIT_ATI"; + case GL_2X_BIT_ATI | GL_COMP_BIT_ATI | GL_BIAS_BIT_ATI: + return "GL_2X_BIT_ATI | GL_COMP_BIT_ATI | GL_BIAS_BIT_ATI"; + case GL_2X_BIT_ATI | GL_COMP_BIT_ATI | GL_NEGATE_BIT_ATI: + return "GL_2X_BIT_ATI | GL_COMP_BIT_ATI | GL_NEGATE_BIT_ATI"; + + case GL_2X_BIT_ATI | GL_COMP_BIT_ATI | GL_NEGATE_BIT_ATI | GL_BIAS_BIT_ATI: + return "GL_2X_BIT_ATI | GL_COMP_BIT_ATI | GL_NEGATE_BIT_ATI | GL_BIAS_BIT_ATI"; + + default: + return "Unexpected argmod combination\n"; + } +} +static const char *debug_register(GLuint reg) { + switch(reg) { + case GL_REG_0_ATI: return "GL_REG_0_ATI"; + case GL_REG_1_ATI: return "GL_REG_1_ATI"; + case GL_REG_2_ATI: return "GL_REG_2_ATI"; + case GL_REG_3_ATI: return "GL_REG_3_ATI"; + case GL_REG_4_ATI: return "GL_REG_4_ATI"; + case GL_REG_5_ATI: return "GL_REG_5_ATI"; + + case GL_CON_0_ATI: return "GL_CON_0_ATI"; + case GL_CON_1_ATI: return "GL_CON_1_ATI"; + case GL_CON_2_ATI: return "GL_CON_2_ATI"; + case GL_CON_3_ATI: return "GL_CON_3_ATI"; + case GL_CON_4_ATI: return "GL_CON_4_ATI"; + case GL_CON_5_ATI: return "GL_CON_5_ATI"; + case GL_CON_6_ATI: return "GL_CON_6_ATI"; + case GL_CON_7_ATI: return "GL_CON_7_ATI"; + + case GL_ZERO: return "GL_ZERO"; + case GL_ONE: return "GL_ONE"; + case GL_PRIMARY_COLOR: return "GL_PRIMARY_COLOR"; + case GL_SECONDARY_INTERPOLATOR_ATI: return "GL_SECONDARY_INTERPOLATOR_ATI"; + + default: return "Unknown register\n"; + } +} + +static const char *debug_swizzle(GLuint swizzle) { + switch(swizzle) { + case GL_SWIZZLE_STR_ATI: return "GL_SWIZZLE_STR_ATI"; + case GL_SWIZZLE_STQ_ATI: return "GL_SWIZZLE_STQ_ATI"; + case GL_SWIZZLE_STR_DR_ATI: return "GL_SWIZZLE_STR_DR_ATI"; + case GL_SWIZZLE_STQ_DQ_ATI: return "GL_SWIZZLE_STQ_DQ_ATI"; + default: return "unknown swizzle"; + } +} + +static const char *debug_rep(GLuint rep) { + switch(rep) { + case GL_NONE: return "GL_NONE"; + case GL_RED: return "GL_RED"; + case GL_GREEN: return "GL_GREEN"; + case GL_BLUE: return "GL_BLUE"; + case GL_ALPHA: return "GL_ALPHA"; + default: return "unknown argrep"; + } +} + +static const char *debug_op(GLuint op) { + switch(op) { + case GL_MOV_ATI: return "GL_MOV_ATI"; + case GL_ADD_ATI: return "GL_ADD_ATI"; + case GL_MUL_ATI: return "GL_MUL_ATI"; + case GL_SUB_ATI: return "GL_SUB_ATI"; + case GL_DOT3_ATI: return "GL_DOT3_ATI"; + case GL_DOT4_ATI: return "GL_DOT4_ATI"; + case GL_MAD_ATI: return "GL_MAD_ATI"; + case GL_LERP_ATI: return "GL_LERP_ATI"; + case GL_CND_ATI: return "GL_CND_ATI"; + case GL_CND0_ATI: return "GL_CND0_ATI"; + case GL_DOT2_ADD_ATI: return "GL_DOT2_ADD_ATI"; + default: return "unexpected op"; + } +} + +static const char *debug_mask(GLuint mask) { + switch(mask) { + case GL_NONE: return "GL_NONE"; + case GL_RED_BIT_ATI: return "GL_RED_BIT_ATI"; + case GL_GREEN_BIT_ATI: return "GL_GREEN_BIT_ATI"; + case GL_BLUE_BIT_ATI: return "GL_BLUE_BIT_ATI"; + case GL_RED_BIT_ATI | GL_GREEN_BIT_ATI: return "GL_RED_BIT_ATI | GL_GREEN_BIT_ATI"; + case GL_RED_BIT_ATI | GL_BLUE_BIT_ATI: return "GL_RED_BIT_ATI | GL_BLUE_BIT_ATI"; + case GL_GREEN_BIT_ATI | GL_BLUE_BIT_ATI:return "GL_GREEN_BIT_ATI | GL_BLUE_BIT_ATI"; + case GL_RED_BIT_ATI | GL_GREEN_BIT_ATI | GL_BLUE_BIT_ATI:return "GL_RED_BIT_ATI | GL_GREEN_BIT_ATI | GL_BLUE_BIT_ATI"; + default: return "Unexpected writemask"; + } +} + +static void wrap_op1(const struct wined3d_gl_info *gl_info, GLuint op, GLuint dst, GLuint dstMask, GLuint dstMod, + GLuint arg1, GLuint arg1Rep, GLuint arg1Mod) +{ + if(dstMask == GL_ALPHA) { + TRACE("glAlphaFragmentOp1ATI(%s, %s, %s, %s, %s, %s)\n", debug_op(op), debug_register(dst), debug_dstmod(dstMod), + debug_register(arg1), debug_rep(arg1Rep), debug_argmod(arg1Mod)); + GL_EXTCALL(glAlphaFragmentOp1ATI(op, dst, dstMod, arg1, arg1Rep, arg1Mod)); + } else { + TRACE("glColorFragmentOp1ATI(%s, %s, %s, %s, %s, %s, %s)\n", debug_op(op), debug_register(dst), + debug_mask(dstMask), debug_dstmod(dstMod), + debug_register(arg1), debug_rep(arg1Rep), debug_argmod(arg1Mod)); + GL_EXTCALL(glColorFragmentOp1ATI(op, dst, dstMask, dstMod, arg1, arg1Rep, arg1Mod)); + } +} + +static void wrap_op2(const struct wined3d_gl_info *gl_info, GLuint op, GLuint dst, GLuint dstMask, GLuint dstMod, + GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod) +{ + if(dstMask == GL_ALPHA) { + TRACE("glAlphaFragmentOp2ATI(%s, %s, %s, %s, %s, %s, %s, %s, %s)\n", debug_op(op), debug_register(dst), debug_dstmod(dstMod), + debug_register(arg1), debug_rep(arg1Rep), debug_argmod(arg1Mod), + debug_register(arg2), debug_rep(arg2Rep), debug_argmod(arg2Mod)); + GL_EXTCALL(glAlphaFragmentOp2ATI(op, dst, dstMod, arg1, arg1Rep, arg1Mod, arg2, arg2Rep, arg2Mod)); + } else { + TRACE("glColorFragmentOp2ATI(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)\n", debug_op(op), debug_register(dst), + debug_mask(dstMask), debug_dstmod(dstMod), + debug_register(arg1), debug_rep(arg1Rep), debug_argmod(arg1Mod), + debug_register(arg2), debug_rep(arg2Rep), debug_argmod(arg2Mod)); + GL_EXTCALL(glColorFragmentOp2ATI(op, dst, dstMask, dstMod, arg1, arg1Rep, arg1Mod, arg2, arg2Rep, arg2Mod)); + } +} + +static void wrap_op3(const struct wined3d_gl_info *gl_info, GLuint op, GLuint dst, GLuint dstMask, GLuint dstMod, + GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod, + GLuint arg3, GLuint arg3Rep, GLuint arg3Mod) +{ + if(dstMask == GL_ALPHA) { + /* Leave some free space to fit "GL_NONE, " in to align most alpha and color op lines */ + TRACE("glAlphaFragmentOp3ATI(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)\n", debug_op(op), debug_register(dst), debug_dstmod(dstMod), + debug_register(arg1), debug_rep(arg1Rep), debug_argmod(arg1Mod), + debug_register(arg2), debug_rep(arg2Rep), debug_argmod(arg2Mod), + debug_register(arg3), debug_rep(arg3Rep), debug_argmod(arg3Mod)); + GL_EXTCALL(glAlphaFragmentOp3ATI(op, dst, dstMod, + arg1, arg1Rep, arg1Mod, + arg2, arg2Rep, arg2Mod, + arg3, arg3Rep, arg3Mod)); + } else { + TRACE("glColorFragmentOp3ATI(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)\n", debug_op(op), debug_register(dst), + debug_mask(dstMask), debug_dstmod(dstMod), + debug_register(arg1), debug_rep(arg1Rep), debug_argmod(arg1Mod), + debug_register(arg2), debug_rep(arg2Rep), debug_argmod(arg2Mod), + debug_register(arg3), debug_rep(arg3Rep), debug_argmod(arg3Mod)); + GL_EXTCALL(glColorFragmentOp3ATI(op, dst, dstMask, dstMod, + arg1, arg1Rep, arg1Mod, + arg2, arg2Rep, arg2Mod, + arg3, arg3Rep, arg3Mod)); + } +} + +static GLuint register_for_arg(DWORD arg, const struct wined3d_gl_info *gl_info, + unsigned int stage, GLuint *mod, GLuint *rep, GLuint tmparg) +{ + GLenum ret; + + if(mod) *mod = GL_NONE; + if(arg == ARG_UNUSED) + { + if (rep) *rep = GL_NONE; + return -1; /* This is the marker for unused registers */ + } + + switch(arg & WINED3DTA_SELECTMASK) { + case WINED3DTA_DIFFUSE: + ret = GL_PRIMARY_COLOR; + break; + + case WINED3DTA_CURRENT: + /* Note that using GL_REG_0_ATI for the passed on register is safe because + * texture0 is read at stage0, so in the worst case it is read in the + * instruction writing to reg0. Afterwards texture0 is not used any longer. + * If we're reading from current + */ + ret = stage ? GL_REG_0_ATI : GL_PRIMARY_COLOR; + break; + + case WINED3DTA_TEXTURE: + ret = GL_REG_0_ATI + stage; + break; + + case WINED3DTA_TFACTOR: + ret = ATIFS_CONST_TFACTOR; + break; + + case WINED3DTA_SPECULAR: + ret = GL_SECONDARY_INTERPOLATOR_ATI; + break; + + case WINED3DTA_TEMP: + ret = tmparg; + break; + + case WINED3DTA_CONSTANT: + ret = ATIFS_CONST_STAGE(stage); + break; + + default: + FIXME("Unknown source argument %d\n", arg); + ret = GL_ZERO; + } + + if(arg & WINED3DTA_COMPLEMENT) { + if(mod) *mod |= GL_COMP_BIT_ATI; + } + if(arg & WINED3DTA_ALPHAREPLICATE) { + if(rep) *rep = GL_ALPHA; + } else { + if(rep) *rep = GL_NONE; + } + return ret; +} + +static GLuint find_tmpreg(const struct texture_stage_op op[WINED3D_MAX_TEXTURES]) +{ + int lowest_read = -1; + int lowest_write = -1; + int i; + BOOL tex_used[WINED3D_MAX_TEXTURES]; + + memset(tex_used, 0, sizeof(tex_used)); + for (i = 0; i < WINED3D_MAX_TEXTURES; ++i) + { + if (op[i].cop == WINED3D_TOP_DISABLE) + break; + + if(lowest_read == -1 && + (op[i].carg1 == WINED3DTA_TEMP || op[i].carg2 == WINED3DTA_TEMP || op[i].carg0 == WINED3DTA_TEMP || + op[i].aarg1 == WINED3DTA_TEMP || op[i].aarg2 == WINED3DTA_TEMP || op[i].aarg0 == WINED3DTA_TEMP)) { + lowest_read = i; + } + + if (lowest_write == -1 && op[i].tmp_dst) + lowest_write = i; + + if(op[i].carg1 == WINED3DTA_TEXTURE || op[i].carg2 == WINED3DTA_TEXTURE || op[i].carg0 == WINED3DTA_TEXTURE || + op[i].aarg1 == WINED3DTA_TEXTURE || op[i].aarg2 == WINED3DTA_TEXTURE || op[i].aarg0 == WINED3DTA_TEXTURE) { + tex_used[i] = TRUE; + } + } + + /* Temp reg not read? We don't need it, return GL_NONE */ + if(lowest_read == -1) return GL_NONE; + + if(lowest_write >= lowest_read) { + FIXME("Temp register read before being written\n"); + } + + if(lowest_write == -1) { + /* This needs a test. Maybe we are supposed to return 0.0/0.0/0.0/0.0, or fail drawprim, or whatever */ + FIXME("Temp register read without being written\n"); + return GL_REG_1_ATI; + } else if(lowest_write >= 1) { + /* If we're writing to the temp reg at earliest in stage 1, we can use register 1 for the temp result. + * there may be texture data stored in reg 1, but we do not need it any longer since stage 1 already + * read it + */ + return GL_REG_1_ATI; + } else { + /* Search for a free texture register. We have 6 registers available. GL_REG_0_ATI is already used + * for the regular result + */ + for(i = 1; i < 6; i++) { + if(!tex_used[i]) { + return GL_REG_0_ATI + i; + } + } + /* What to do here? Report it in ValidateDevice? */ + FIXME("Could not find a register for the temporary register\n"); + return 0; + } +} + +static const struct color_fixup_desc color_fixup_rg = +{ + 1, CHANNEL_SOURCE_X, + 1, CHANNEL_SOURCE_Y, + 0, CHANNEL_SOURCE_ONE, + 0, CHANNEL_SOURCE_ONE +}; +static const struct color_fixup_desc color_fixup_rgl = +{ + 1, CHANNEL_SOURCE_X, + 1, CHANNEL_SOURCE_Y, + 0, CHANNEL_SOURCE_Z, + 0, CHANNEL_SOURCE_W +}; +static const struct color_fixup_desc color_fixup_rgba = +{ + 1, CHANNEL_SOURCE_X, + 1, CHANNEL_SOURCE_Y, + 1, CHANNEL_SOURCE_Z, + 1, CHANNEL_SOURCE_W +}; + +static BOOL op_reads_texture(const struct texture_stage_op *op) +{ + return (op->carg0 & WINED3DTA_SELECTMASK) == WINED3DTA_TEXTURE + || (op->carg1 & WINED3DTA_SELECTMASK) == WINED3DTA_TEXTURE + || (op->carg2 & WINED3DTA_SELECTMASK) == WINED3DTA_TEXTURE + || (op->aarg0 & WINED3DTA_SELECTMASK) == WINED3DTA_TEXTURE + || (op->aarg1 & WINED3DTA_SELECTMASK) == WINED3DTA_TEXTURE + || (op->aarg2 & WINED3DTA_SELECTMASK) == WINED3DTA_TEXTURE + || op->cop == WINED3D_TOP_BLEND_TEXTURE_ALPHA; +} + +static void atifs_color_fixup(const struct wined3d_gl_info *gl_info, struct color_fixup_desc fixup, GLuint reg) +{ + if(is_same_fixup(fixup, color_fixup_rg)) + { + wrap_op1(gl_info, GL_MOV_ATI, reg, GL_RED_BIT_ATI | GL_GREEN_BIT_ATI, GL_NONE, + reg, GL_NONE, GL_2X_BIT_ATI | GL_BIAS_BIT_ATI); + wrap_op1(gl_info, GL_MOV_ATI, reg, GL_BLUE_BIT_ATI, GL_NONE, + GL_ONE, GL_NONE, GL_NONE); + wrap_op1(gl_info, GL_MOV_ATI, reg, GL_ALPHA, GL_NONE, + GL_ONE, GL_NONE, GL_NONE); + } + else if(is_same_fixup(fixup, color_fixup_rgl)) + { + wrap_op1(gl_info, GL_MOV_ATI, reg, GL_RED_BIT_ATI | GL_GREEN_BIT_ATI, GL_NONE, + reg, GL_NONE, GL_2X_BIT_ATI | GL_BIAS_BIT_ATI); + } + else if (is_same_fixup(fixup, color_fixup_rgba)) + { + wrap_op1(gl_info, GL_MOV_ATI, reg, GL_NONE, GL_NONE, + reg, GL_NONE, GL_2X_BIT_ATI | GL_BIAS_BIT_ATI); + wrap_op1(gl_info, GL_MOV_ATI, reg, GL_ALPHA, GL_NONE, + reg, GL_NONE, GL_2X_BIT_ATI | GL_BIAS_BIT_ATI); + } + else + { + /* Should not happen - atifs_color_fixup_supported refuses other fixups. */ + ERR("Unsupported color fixup.\n"); + } +} + +static BOOL op_reads_tfactor(const struct texture_stage_op *op) +{ + return (op->carg0 & WINED3DTA_SELECTMASK) == WINED3DTA_TFACTOR + || (op->carg1 & WINED3DTA_SELECTMASK) == WINED3DTA_TFACTOR + || (op->carg2 & WINED3DTA_SELECTMASK) == WINED3DTA_TFACTOR + || (op->aarg0 & WINED3DTA_SELECTMASK) == WINED3DTA_TFACTOR + || (op->aarg1 & WINED3DTA_SELECTMASK) == WINED3DTA_TFACTOR + || (op->aarg2 & WINED3DTA_SELECTMASK) == WINED3DTA_TFACTOR + || op->cop == WINED3D_TOP_BLEND_FACTOR_ALPHA + || op->aop == WINED3D_TOP_BLEND_FACTOR_ALPHA; +} + +static BOOL op_reads_constant(const struct texture_stage_op *op) +{ + return (op->carg0 & WINED3DTA_SELECTMASK) == WINED3DTA_CONSTANT + || (op->carg1 & WINED3DTA_SELECTMASK) == WINED3DTA_CONSTANT + || (op->carg2 & WINED3DTA_SELECTMASK) == WINED3DTA_CONSTANT + || (op->aarg0 & WINED3DTA_SELECTMASK) == WINED3DTA_CONSTANT + || (op->aarg1 & WINED3DTA_SELECTMASK) == WINED3DTA_CONSTANT + || (op->aarg2 & WINED3DTA_SELECTMASK) == WINED3DTA_CONSTANT; +} + +static GLuint gen_ati_shader(const struct texture_stage_op op[WINED3D_MAX_TEXTURES], + const struct wined3d_gl_info *gl_info, enum atifs_constant_value *constants) +{ + GLuint ret = GL_EXTCALL(glGenFragmentShadersATI(1)); + unsigned int stage; + GLuint arg0, arg1, arg2, extrarg; + GLuint dstmod, argmod0, argmod1, argmod2, argmodextra; + GLuint rep0, rep1, rep2; + GLuint swizzle; + GLuint tmparg = find_tmpreg(op); + GLuint dstreg; + BOOL tfactor_used = FALSE; + + if(!ret) { + ERR("Failed to generate a GL_ATI_fragment_shader shader id\n"); + return 0; + } + GL_EXTCALL(glBindFragmentShaderATI(ret)); + checkGLcall("GL_EXTCALL(glBindFragmentShaderATI(ret))"); + + TRACE("glBeginFragmentShaderATI()\n"); + GL_EXTCALL(glBeginFragmentShaderATI()); + checkGLcall("GL_EXTCALL(glBeginFragmentShaderATI())"); + + /* Pass 1: Generate sampling instructions for perturbation maps */ + for (stage = 0; stage < gl_info->limits.textures; ++stage) + { + if (op[stage].cop == WINED3D_TOP_DISABLE) + break; + if (op[stage].cop != WINED3D_TOP_BUMPENVMAP + && op[stage].cop != WINED3D_TOP_BUMPENVMAP_LUMINANCE) + continue; + + constants[stage] = ATIFS_CONSTANT_BUMP; + + TRACE("glSampleMapATI(GL_REG_%d_ATI, GL_TEXTURE_%d_ARB, GL_SWIZZLE_STR_ATI)\n", + stage, stage); + GL_EXTCALL(glSampleMapATI(GL_REG_0_ATI + stage, GL_TEXTURE0_ARB + stage, GL_SWIZZLE_STR_ATI)); + if (op[stage + 1].projected == WINED3D_PROJECTION_NONE) + swizzle = GL_SWIZZLE_STR_ATI; + else if (op[stage + 1].projected == WINED3D_PROJECTION_COUNT4) + swizzle = GL_SWIZZLE_STQ_DQ_ATI; + else + swizzle = GL_SWIZZLE_STR_DR_ATI; + TRACE("glPassTexCoordATI(GL_REG_%d_ATI, GL_TEXTURE_%d_ARB, %s)\n", + stage + 1, stage + 1, debug_swizzle(swizzle)); + GL_EXTCALL(glPassTexCoordATI(GL_REG_0_ATI + stage + 1, + GL_TEXTURE0_ARB + stage + 1, + swizzle)); + } + + /* Pass 2: Generate perturbation calculations */ + for (stage = 0; stage < gl_info->limits.textures; ++stage) + { + GLuint argmodextra_x, argmodextra_y; + struct color_fixup_desc fixup; + + if (op[stage].cop == WINED3D_TOP_DISABLE) + break; + if (op[stage].cop != WINED3D_TOP_BUMPENVMAP + && op[stage].cop != WINED3D_TOP_BUMPENVMAP_LUMINANCE) + continue; + + fixup = op[stage].color_fixup; + if (fixup.x_source != CHANNEL_SOURCE_X || fixup.y_source != CHANNEL_SOURCE_Y) + { + FIXME("Swizzles not implemented\n"); + argmodextra_x = GL_NONE; + argmodextra_y = GL_NONE; + } + else + { + /* Nice thing, we get the color correction for free :-) */ + argmodextra_x = fixup.x_sign_fixup ? GL_2X_BIT_ATI | GL_BIAS_BIT_ATI : GL_NONE; + argmodextra_y = fixup.y_sign_fixup ? GL_2X_BIT_ATI | GL_BIAS_BIT_ATI : GL_NONE; + } + + wrap_op3(gl_info, GL_DOT2_ADD_ATI, GL_REG_0_ATI + stage + 1, GL_RED_BIT_ATI, GL_NONE, + GL_REG_0_ATI + stage, GL_NONE, argmodextra_x, + ATIFS_CONST_BUMPMAT(stage), GL_NONE, GL_2X_BIT_ATI | GL_BIAS_BIT_ATI, + GL_REG_0_ATI + stage + 1, GL_RED, GL_NONE); + + /* Don't use GL_DOT2_ADD_ATI here because we cannot configure it to read the blue and alpha + * component of the bump matrix. Instead do this with two MADs: + * + * coord.a = tex.r * bump.b + coord.g + * coord.g = tex.g * bump.a + coord.a + * + * The first instruction writes to alpha so it can be coissued with the above DOT2_ADD. + * coord.a is unused. If the perturbed texture is projected, this was already handled + * in the glPassTexCoordATI above. + */ + wrap_op3(gl_info, GL_MAD_ATI, GL_REG_0_ATI + stage + 1, GL_ALPHA, GL_NONE, + GL_REG_0_ATI + stage, GL_RED, argmodextra_y, + ATIFS_CONST_BUMPMAT(stage), GL_BLUE, GL_2X_BIT_ATI | GL_BIAS_BIT_ATI, + GL_REG_0_ATI + stage + 1, GL_GREEN, GL_NONE); + wrap_op3(gl_info, GL_MAD_ATI, GL_REG_0_ATI + stage + 1, GL_GREEN_BIT_ATI, GL_NONE, + GL_REG_0_ATI + stage, GL_GREEN, argmodextra_y, + ATIFS_CONST_BUMPMAT(stage), GL_ALPHA, GL_2X_BIT_ATI | GL_BIAS_BIT_ATI, + GL_REG_0_ATI + stage + 1, GL_ALPHA, GL_NONE); + } + + /* Pass 3: Generate sampling instructions for regular textures */ + for (stage = 0; stage < gl_info->limits.textures; ++stage) + { + if (op[stage].cop == WINED3D_TOP_DISABLE) + break; + + if (op[stage].projected == WINED3D_PROJECTION_NONE) + swizzle = GL_SWIZZLE_STR_ATI; + else if (op[stage].projected == WINED3D_PROJECTION_COUNT3) + swizzle = GL_SWIZZLE_STR_DR_ATI; + else + swizzle = GL_SWIZZLE_STQ_DQ_ATI; + + if (op_reads_texture(&op[stage])) + { + if (stage > 0 + && (op[stage - 1].cop == WINED3D_TOP_BUMPENVMAP + || op[stage - 1].cop == WINED3D_TOP_BUMPENVMAP_LUMINANCE)) + { + TRACE("glSampleMapATI(GL_REG_%d_ATI, GL_REG_%d_ATI, GL_SWIZZLE_STR_ATI)\n", + stage, stage); + GL_EXTCALL(glSampleMapATI(GL_REG_0_ATI + stage, + GL_REG_0_ATI + stage, + GL_SWIZZLE_STR_ATI)); + } else { + TRACE("glSampleMapATI(GL_REG_%d_ATI, GL_TEXTURE_%d_ARB, %s)\n", + stage, stage, debug_swizzle(swizzle)); + GL_EXTCALL(glSampleMapATI(GL_REG_0_ATI + stage, + GL_TEXTURE0_ARB + stage, + swizzle)); + } + } + } + + /* Pass 4: Generate the arithmetic instructions */ + for (stage = 0; stage < WINED3D_MAX_TEXTURES; ++stage) + { + if (op[stage].cop == WINED3D_TOP_DISABLE) + { + if (!stage) + { + /* Handle complete texture disabling gracefully */ + wrap_op1(gl_info, GL_MOV_ATI, GL_REG_0_ATI, GL_NONE, GL_NONE, + GL_PRIMARY_COLOR, GL_NONE, GL_NONE); + wrap_op1(gl_info, GL_MOV_ATI, GL_REG_0_ATI, GL_ALPHA, GL_NONE, + GL_PRIMARY_COLOR, GL_NONE, GL_NONE); + } + break; + } + + if (op[stage].tmp_dst) + { + /* If we're writing to D3DTA_TEMP, but never reading from it we + * don't have to write there in the first place. Skip the entire + * stage, this saves some GPU time. */ + if (tmparg == GL_NONE) + continue; + + dstreg = tmparg; + } + else + { + dstreg = GL_REG_0_ATI; + } + + if (op[stage].cop == WINED3D_TOP_BUMPENVMAP || op[stage].cop == WINED3D_TOP_BUMPENVMAP_LUMINANCE) + { + /* Those are handled in the first pass of the shader (generation pass 1 and 2) already */ + continue; + } + + arg0 = register_for_arg(op[stage].carg0, gl_info, stage, &argmod0, &rep0, tmparg); + arg1 = register_for_arg(op[stage].carg1, gl_info, stage, &argmod1, &rep1, tmparg); + arg2 = register_for_arg(op[stage].carg2, gl_info, stage, &argmod2, &rep2, tmparg); + dstmod = GL_NONE; + argmodextra = GL_NONE; + extrarg = GL_NONE; + + if (op_reads_tfactor(&op[stage])) + tfactor_used = TRUE; + + if (op_reads_constant(&op[stage])) + { + if (constants[stage] != ATIFS_CONSTANT_UNUSED) + FIXME("Constant %u already used.\n", stage); + constants[stage] = ATIFS_CONSTANT_STAGE; + } + + if (op_reads_texture(&op[stage]) && !is_identity_fixup(op[stage].color_fixup)) + atifs_color_fixup(gl_info, op[stage].color_fixup, GL_REG_0_ATI + stage); + + switch (op[stage].cop) + { + case WINED3D_TOP_SELECT_ARG2: + arg1 = arg2; + argmod1 = argmod2; + rep1 = rep2; + /* fall through */ + case WINED3D_TOP_SELECT_ARG1: + wrap_op1(gl_info, GL_MOV_ATI, dstreg, GL_NONE, GL_NONE, + arg1, rep1, argmod1); + break; + + case WINED3D_TOP_MODULATE_4X: + if(dstmod == GL_NONE) dstmod = GL_4X_BIT_ATI; + /* fall through */ + case WINED3D_TOP_MODULATE_2X: + if(dstmod == GL_NONE) dstmod = GL_2X_BIT_ATI; + dstmod |= GL_SATURATE_BIT_ATI; + /* fall through */ + case WINED3D_TOP_MODULATE: + wrap_op2(gl_info, GL_MUL_ATI, dstreg, GL_NONE, dstmod, + arg1, rep1, argmod1, + arg2, rep2, argmod2); + break; + + case WINED3D_TOP_ADD_SIGNED_2X: + dstmod = GL_2X_BIT_ATI; + /* fall through */ + case WINED3D_TOP_ADD_SIGNED: + argmodextra = GL_BIAS_BIT_ATI; + /* fall through */ + case WINED3D_TOP_ADD: + dstmod |= GL_SATURATE_BIT_ATI; + wrap_op2(gl_info, GL_ADD_ATI, GL_REG_0_ATI, GL_NONE, dstmod, + arg1, rep1, argmod1, + arg2, rep2, argmodextra | argmod2); + break; + + case WINED3D_TOP_SUBTRACT: + dstmod |= GL_SATURATE_BIT_ATI; + wrap_op2(gl_info, GL_SUB_ATI, dstreg, GL_NONE, dstmod, + arg1, rep1, argmod1, + arg2, rep2, argmod2); + break; + + case WINED3D_TOP_ADD_SMOOTH: + argmodextra = argmod1 & GL_COMP_BIT_ATI ? argmod1 & ~GL_COMP_BIT_ATI : argmod1 | GL_COMP_BIT_ATI; + /* Dst = arg1 + * arg2(1 -arg 1) + * = arg2 * (1 - arg1) + arg1 + */ + wrap_op3(gl_info, GL_MAD_ATI, dstreg, GL_NONE, GL_SATURATE_BIT_ATI, + arg2, rep2, argmod2, + arg1, rep1, argmodextra, + arg1, rep1, argmod1); + break; + + case WINED3D_TOP_BLEND_CURRENT_ALPHA: + if (extrarg == GL_NONE) + extrarg = register_for_arg(WINED3DTA_CURRENT, gl_info, stage, NULL, NULL, -1); + /* fall through */ + case WINED3D_TOP_BLEND_FACTOR_ALPHA: + if (extrarg == GL_NONE) + extrarg = register_for_arg(WINED3DTA_TFACTOR, gl_info, stage, NULL, NULL, -1); + /* fall through */ + case WINED3D_TOP_BLEND_TEXTURE_ALPHA: + if (extrarg == GL_NONE) + extrarg = register_for_arg(WINED3DTA_TEXTURE, gl_info, stage, NULL, NULL, -1); + /* fall through */ + case WINED3D_TOP_BLEND_DIFFUSE_ALPHA: + if (extrarg == GL_NONE) + extrarg = register_for_arg(WINED3DTA_DIFFUSE, gl_info, stage, NULL, NULL, -1); + wrap_op3(gl_info, GL_LERP_ATI, dstreg, GL_NONE, GL_NONE, + extrarg, GL_ALPHA, GL_NONE, + arg1, rep1, argmod1, + arg2, rep2, argmod2); + break; + + case WINED3D_TOP_BLEND_TEXTURE_ALPHA_PM: + arg0 = register_for_arg(WINED3DTA_TEXTURE, gl_info, stage, NULL, NULL, -1); + wrap_op3(gl_info, GL_MAD_ATI, dstreg, GL_NONE, GL_NONE, + arg2, rep2, argmod2, + arg0, GL_ALPHA, GL_COMP_BIT_ATI, + arg1, rep1, argmod1); + break; + + /* D3DTOP_PREMODULATE ???? */ + + case WINED3D_TOP_MODULATE_INVALPHA_ADD_COLOR: + argmodextra = argmod1 & GL_COMP_BIT_ATI ? argmod1 & ~GL_COMP_BIT_ATI : argmod1 | GL_COMP_BIT_ATI; + /* fall through */ + case WINED3D_TOP_MODULATE_ALPHA_ADD_COLOR: + if (!argmodextra) + argmodextra = argmod1; + wrap_op3(gl_info, GL_MAD_ATI, dstreg, GL_NONE, GL_SATURATE_BIT_ATI, + arg2, rep2, argmod2, + arg1, GL_ALPHA, argmodextra, + arg1, rep1, argmod1); + break; + + case WINED3D_TOP_MODULATE_INVCOLOR_ADD_ALPHA: + argmodextra = argmod1 & GL_COMP_BIT_ATI ? argmod1 & ~GL_COMP_BIT_ATI : argmod1 | GL_COMP_BIT_ATI; + /* fall through */ + case WINED3D_TOP_MODULATE_COLOR_ADD_ALPHA: + if (!argmodextra) + argmodextra = argmod1; + wrap_op3(gl_info, GL_MAD_ATI, dstreg, GL_NONE, GL_SATURATE_BIT_ATI, + arg2, rep2, argmod2, + arg1, rep1, argmodextra, + arg1, GL_ALPHA, argmod1); + break; + + case WINED3D_TOP_DOTPRODUCT3: + wrap_op2(gl_info, GL_DOT3_ATI, dstreg, GL_NONE, GL_4X_BIT_ATI | GL_SATURATE_BIT_ATI, + arg1, rep1, argmod1 | GL_BIAS_BIT_ATI, + arg2, rep2, argmod2 | GL_BIAS_BIT_ATI); + break; + + case WINED3D_TOP_MULTIPLY_ADD: + wrap_op3(gl_info, GL_MAD_ATI, dstreg, GL_NONE, GL_SATURATE_BIT_ATI, + arg1, rep1, argmod1, + arg2, rep2, argmod2, + arg0, rep0, argmod0); + break; + + case WINED3D_TOP_LERP: + wrap_op3(gl_info, GL_LERP_ATI, dstreg, GL_NONE, GL_NONE, + arg0, rep0, argmod0, + arg1, rep1, argmod1, + arg2, rep2, argmod2); + break; + + default: FIXME("Unhandled color operation %d on stage %d\n", op[stage].cop, stage); + } + + arg0 = register_for_arg(op[stage].aarg0, gl_info, stage, &argmod0, NULL, tmparg); + arg1 = register_for_arg(op[stage].aarg1, gl_info, stage, &argmod1, NULL, tmparg); + arg2 = register_for_arg(op[stage].aarg2, gl_info, stage, &argmod2, NULL, tmparg); + dstmod = GL_NONE; + argmodextra = GL_NONE; + extrarg = GL_NONE; + + switch (op[stage].aop) + { + case WINED3D_TOP_DISABLE: + /* Get the primary color to the output if on stage 0, otherwise leave register 0 untouched */ + if (!stage) + { + wrap_op1(gl_info, GL_MOV_ATI, GL_REG_0_ATI, GL_ALPHA, GL_NONE, + GL_PRIMARY_COLOR, GL_NONE, GL_NONE); + } + break; + + case WINED3D_TOP_SELECT_ARG2: + arg1 = arg2; + argmod1 = argmod2; + /* fall through */ + case WINED3D_TOP_SELECT_ARG1: + wrap_op1(gl_info, GL_MOV_ATI, dstreg, GL_ALPHA, GL_NONE, + arg1, GL_NONE, argmod1); + break; + + case WINED3D_TOP_MODULATE_4X: + if (dstmod == GL_NONE) + dstmod = GL_4X_BIT_ATI; + /* fall through */ + case WINED3D_TOP_MODULATE_2X: + if (dstmod == GL_NONE) + dstmod = GL_2X_BIT_ATI; + dstmod |= GL_SATURATE_BIT_ATI; + /* fall through */ + case WINED3D_TOP_MODULATE: + wrap_op2(gl_info, GL_MUL_ATI, dstreg, GL_ALPHA, dstmod, + arg1, GL_NONE, argmod1, + arg2, GL_NONE, argmod2); + break; + + case WINED3D_TOP_ADD_SIGNED_2X: + dstmod = GL_2X_BIT_ATI; + /* fall through */ + case WINED3D_TOP_ADD_SIGNED: + argmodextra = GL_BIAS_BIT_ATI; + /* fall through */ + case WINED3D_TOP_ADD: + dstmod |= GL_SATURATE_BIT_ATI; + wrap_op2(gl_info, GL_ADD_ATI, dstreg, GL_ALPHA, dstmod, + arg1, GL_NONE, argmod1, + arg2, GL_NONE, argmodextra | argmod2); + break; + + case WINED3D_TOP_SUBTRACT: + dstmod |= GL_SATURATE_BIT_ATI; + wrap_op2(gl_info, GL_SUB_ATI, dstreg, GL_ALPHA, dstmod, + arg1, GL_NONE, argmod1, + arg2, GL_NONE, argmod2); + break; + + case WINED3D_TOP_ADD_SMOOTH: + argmodextra = argmod1 & GL_COMP_BIT_ATI ? argmod1 & ~GL_COMP_BIT_ATI : argmod1 | GL_COMP_BIT_ATI; + /* Dst = arg1 + * arg2(1 -arg 1) + * = arg2 * (1 - arg1) + arg1 + */ + wrap_op3(gl_info, GL_MAD_ATI, dstreg, GL_ALPHA, GL_SATURATE_BIT_ATI, + arg2, GL_NONE, argmod2, + arg1, GL_NONE, argmodextra, + arg1, GL_NONE, argmod1); + break; + + case WINED3D_TOP_BLEND_CURRENT_ALPHA: + if (extrarg == GL_NONE) + extrarg = register_for_arg(WINED3DTA_CURRENT, gl_info, stage, NULL, NULL, -1); + /* fall through */ + case WINED3D_TOP_BLEND_FACTOR_ALPHA: + if (extrarg == GL_NONE) + extrarg = register_for_arg(WINED3DTA_TFACTOR, gl_info, stage, NULL, NULL, -1); + /* fall through */ + case WINED3D_TOP_BLEND_TEXTURE_ALPHA: + if (extrarg == GL_NONE) + extrarg = register_for_arg(WINED3DTA_TEXTURE, gl_info, stage, NULL, NULL, -1); + /* fall through */ + case WINED3D_TOP_BLEND_DIFFUSE_ALPHA: + if (extrarg == GL_NONE) + extrarg = register_for_arg(WINED3DTA_DIFFUSE, gl_info, stage, NULL, NULL, -1); + wrap_op3(gl_info, GL_LERP_ATI, dstreg, GL_ALPHA, GL_NONE, + extrarg, GL_ALPHA, GL_NONE, + arg1, GL_NONE, argmod1, + arg2, GL_NONE, argmod2); + break; + + case WINED3D_TOP_BLEND_TEXTURE_ALPHA_PM: + arg0 = register_for_arg(WINED3DTA_TEXTURE, gl_info, stage, NULL, NULL, -1); + wrap_op3(gl_info, GL_MAD_ATI, dstreg, GL_ALPHA, GL_NONE, + arg2, GL_NONE, argmod2, + arg0, GL_ALPHA, GL_COMP_BIT_ATI, + arg1, GL_NONE, argmod1); + break; + + /* D3DTOP_PREMODULATE ???? */ + + case WINED3D_TOP_DOTPRODUCT3: + wrap_op2(gl_info, GL_DOT3_ATI, dstreg, GL_ALPHA, GL_4X_BIT_ATI | GL_SATURATE_BIT_ATI, + arg1, GL_NONE, argmod1 | GL_BIAS_BIT_ATI, + arg2, GL_NONE, argmod2 | GL_BIAS_BIT_ATI); + break; + + case WINED3D_TOP_MULTIPLY_ADD: + wrap_op3(gl_info, GL_MAD_ATI, dstreg, GL_ALPHA, GL_SATURATE_BIT_ATI, + arg1, GL_NONE, argmod1, + arg2, GL_NONE, argmod2, + arg0, GL_NONE, argmod0); + break; + + case WINED3D_TOP_LERP: + wrap_op3(gl_info, GL_LERP_ATI, dstreg, GL_ALPHA, GL_SATURATE_BIT_ATI, + arg1, GL_NONE, argmod1, + arg2, GL_NONE, argmod2, + arg0, GL_NONE, argmod0); + break; + + case WINED3D_TOP_MODULATE_INVALPHA_ADD_COLOR: + case WINED3D_TOP_MODULATE_ALPHA_ADD_COLOR: + case WINED3D_TOP_MODULATE_COLOR_ADD_ALPHA: + case WINED3D_TOP_MODULATE_INVCOLOR_ADD_ALPHA: + case WINED3D_TOP_BUMPENVMAP: + case WINED3D_TOP_BUMPENVMAP_LUMINANCE: + ERR("Application uses an invalid alpha operation\n"); + break; + + default: FIXME("Unhandled alpha operation %d on stage %d\n", op[stage].aop, stage); + } + } + + if (tfactor_used && constants[ATIFS_CONST_TFACTOR - GL_CON_0_ATI] != ATIFS_CONSTANT_UNUSED) + FIXME("Texture factor constant already used.\n"); + constants[ATIFS_CONST_TFACTOR - GL_CON_0_ATI] = ATIFS_CONSTANT_TFACTOR; + + /* Assign unused constants to avoid reloading due to unused <-> bump matrix switches. */ + for (stage = 0; stage < WINED3D_MAX_TEXTURES; ++stage) + { + if (constants[stage] == ATIFS_CONSTANT_UNUSED) + constants[stage] = ATIFS_CONSTANT_BUMP; + } + + TRACE("glEndFragmentShaderATI()\n"); + GL_EXTCALL(glEndFragmentShaderATI()); + checkGLcall("GL_EXTCALL(glEndFragmentShaderATI())"); + return ret; +} + +static void atifs_tfactor(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + struct atifs_context_private_data *ctx_priv = context->fragment_pipe_data; + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + struct wined3d_color color; + + if (!ctx_priv->last_shader + || ctx_priv->last_shader->constants[ATIFS_CONST_TFACTOR - GL_CON_0_ATI] != ATIFS_CONSTANT_TFACTOR) + return; + + wined3d_color_from_d3dcolor(&color, state->render_states[WINED3D_RS_TEXTUREFACTOR]); + GL_EXTCALL(glSetFragmentShaderConstantATI(ATIFS_CONST_TFACTOR, &color.r)); + checkGLcall("glSetFragmentShaderConstantATI(ATIFS_CONST_TFACTOR, &color.r)"); +} + +static void set_bumpmat(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + DWORD stage = (state_id - STATE_TEXTURESTAGE(0, 0)) / (WINED3D_HIGHEST_TEXTURE_STATE + 1); + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + float mat[2][2]; + struct atifs_context_private_data *ctx_priv = context->fragment_pipe_data; + + if (!ctx_priv->last_shader + || ctx_priv->last_shader->constants[stage] != ATIFS_CONSTANT_BUMP) + return; + + mat[0][0] = *((float *)&state->texture_states[stage][WINED3D_TSS_BUMPENV_MAT00]); + mat[1][0] = *((float *)&state->texture_states[stage][WINED3D_TSS_BUMPENV_MAT01]); + mat[0][1] = *((float *)&state->texture_states[stage][WINED3D_TSS_BUMPENV_MAT10]); + mat[1][1] = *((float *)&state->texture_states[stage][WINED3D_TSS_BUMPENV_MAT11]); + /* GL_ATI_fragment_shader allows only constants from 0.0 to 1.0, but the bumpmat + * constants can be in any range. While they should stay between [-1.0 and 1.0] because + * Shader Model 1.x pixel shaders are clamped to that range negative values are used occasionally, + * for example by our d3d9 test. So to get negative values scale -1;1 to 0;1 and undo that in the + * shader(it is free). This might potentially reduce precision. However, if the hardware does + * support proper floats it shouldn't, and if it doesn't we can't get anything better anyway. */ + mat[0][0] = (mat[0][0] + 1.0f) * 0.5f; + mat[1][0] = (mat[1][0] + 1.0f) * 0.5f; + mat[0][1] = (mat[0][1] + 1.0f) * 0.5f; + mat[1][1] = (mat[1][1] + 1.0f) * 0.5f; + GL_EXTCALL(glSetFragmentShaderConstantATI(ATIFS_CONST_BUMPMAT(stage), (float *) mat)); + checkGLcall("glSetFragmentShaderConstantATI(ATIFS_CONST_BUMPMAT(stage), mat)"); +} + +static void atifs_stage_constant(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + DWORD stage = (state_id - STATE_TEXTURESTAGE(0, 0)) / (WINED3D_HIGHEST_TEXTURE_STATE + 1); + struct atifs_context_private_data *ctx_priv = context->fragment_pipe_data; + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + struct wined3d_color color; + + if (!ctx_priv->last_shader + || ctx_priv->last_shader->constants[stage] != ATIFS_CONSTANT_STAGE) + return; + + wined3d_color_from_d3dcolor(&color, state->texture_states[stage][WINED3D_TSS_CONSTANT]); + GL_EXTCALL(glSetFragmentShaderConstantATI(ATIFS_CONST_STAGE(stage), &color.r)); + checkGLcall("glSetFragmentShaderConstantATI(ATIFS_CONST_STAGE(stage), &color.r)"); +} + +static void set_tex_op_atifs(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + struct atifs_context_private_data *ctx_priv = context->fragment_pipe_data; + const struct atifs_ffp_desc *desc, *last_shader = ctx_priv->last_shader; + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + const struct wined3d_d3d_info *d3d_info = context->d3d_info; + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + const struct wined3d_device *device = context->device; + struct atifs_private_data *priv = device->fragment_priv; + struct ffp_frag_settings settings; + DWORD mapped_stage; + unsigned int i; + + gen_ffp_frag_op(context, state, &settings, TRUE); + desc = (const struct atifs_ffp_desc *)find_ffp_frag_shader(&priv->fragment_shaders, &settings); + if (!desc) + { + struct atifs_ffp_desc *new_desc; + + if (!(new_desc = heap_alloc_zero(sizeof(*new_desc)))) + { + ERR("Out of memory\n"); + return; + } + new_desc->num_textures_used = 0; + for (i = 0; i < d3d_info->limits.ffp_blend_stages; ++i) + { + if (settings.op[i].cop == WINED3D_TOP_DISABLE) + break; + ++new_desc->num_textures_used; + } + + new_desc->parent.settings = settings; + new_desc->shader = gen_ati_shader(settings.op, gl_info, new_desc->constants); + add_ffp_frag_shader(&priv->fragment_shaders, &new_desc->parent); + TRACE("Allocated fixed function replacement shader descriptor %p.\n", new_desc); + desc = new_desc; + } + + /* GL_ATI_fragment_shader depends on the GL_TEXTURE_xD enable settings. Update the texture stages + * used by this shader + */ + for (i = 0; i < desc->num_textures_used; ++i) + { + mapped_stage = context_gl->tex_unit_map[i]; + if (mapped_stage != WINED3D_UNMAPPED_STAGE) + { + wined3d_context_gl_active_texture(context_gl, gl_info, mapped_stage); + texture_activate_dimensions(state->textures[i], gl_info); + } + } + + GL_EXTCALL(glBindFragmentShaderATI(desc->shader)); + ctx_priv->last_shader = desc; + + for (i = 0; i < WINED3D_MAX_TEXTURES; i++) + { + if (last_shader && last_shader->constants[i] == desc->constants[i]) + continue; + + switch (desc->constants[i]) + { + case ATIFS_CONSTANT_BUMP: + set_bumpmat(context, state, STATE_TEXTURESTAGE(i, WINED3D_TSS_BUMPENV_MAT00)); + break; + + case ATIFS_CONSTANT_TFACTOR: + atifs_tfactor(context, state, STATE_RENDER(WINED3D_RS_TEXTUREFACTOR)); + break; + + case ATIFS_CONSTANT_STAGE: + atifs_stage_constant(context, state, STATE_TEXTURESTAGE(i, WINED3D_TSS_CONSTANT)); + break; + + default: + ERR("Unexpected constant type %u.\n", desc->constants[i]); + } + } +} + +static void textransform(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + if (!isStateDirty(context, STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL))) + set_tex_op_atifs(context, state, state_id); +} + +static void atifs_srgbwriteenable(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + if (state->render_states[WINED3D_RS_SRGBWRITEENABLE]) + WARN("sRGB writes are not supported by this fragment pipe.\n"); +} + +static const struct wined3d_state_entry_template atifs_fragmentstate_template[] = +{ + {STATE_RENDER(WINED3D_RS_TEXTUREFACTOR), { STATE_RENDER(WINED3D_RS_TEXTUREFACTOR), atifs_tfactor }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_ALPHAFUNC), { STATE_RENDER(WINED3D_RS_ALPHATESTENABLE), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_ALPHAREF), { STATE_RENDER(WINED3D_RS_ALPHATESTENABLE), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_ALPHATESTENABLE), { STATE_RENDER(WINED3D_RS_ALPHATESTENABLE), state_alpha_test }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_COLORKEYENABLE), { STATE_RENDER(WINED3D_RS_ALPHATESTENABLE), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_FOGCOLOR), { STATE_RENDER(WINED3D_RS_FOGCOLOR), state_fogcolor }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_FOGDENSITY), { STATE_RENDER(WINED3D_RS_FOGDENSITY), state_fogdensity }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_FOGENABLE), { STATE_RENDER(WINED3D_RS_FOGENABLE), state_fog_fragpart }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_FOGTABLEMODE), { STATE_RENDER(WINED3D_RS_FOGENABLE), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_FOGVERTEXMODE), { STATE_RENDER(WINED3D_RS_FOGENABLE), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_FOGSTART), { STATE_RENDER(WINED3D_RS_FOGSTART), state_fogstartend }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_FOGEND), { STATE_RENDER(WINED3D_RS_FOGSTART), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_SRGBWRITEENABLE), { STATE_RENDER(WINED3D_RS_SRGBWRITEENABLE), atifs_srgbwriteenable }, WINED3D_GL_EXT_NONE }, + {STATE_COLOR_KEY, { STATE_COLOR_KEY, state_nop }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_SHADEMODE), { STATE_RENDER(WINED3D_RS_SHADEMODE), state_shademode }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(0, WINED3D_TSS_COLOR_OP), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(0, WINED3D_TSS_COLOR_ARG1), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(0, WINED3D_TSS_COLOR_ARG2), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(0, WINED3D_TSS_COLOR_ARG0), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(0, WINED3D_TSS_ALPHA_OP), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(0, WINED3D_TSS_ALPHA_ARG1), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(0, WINED3D_TSS_ALPHA_ARG2), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(0, WINED3D_TSS_ALPHA_ARG0), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(0, WINED3D_TSS_RESULT_ARG), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_MAT00), { STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_MAT00), set_bumpmat }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_MAT01), { STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_MAT10), { STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_MAT11), { STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(0, WINED3D_TSS_CONSTANT), { STATE_TEXTURESTAGE(0, WINED3D_TSS_CONSTANT), atifs_stage_constant }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(1, WINED3D_TSS_COLOR_OP), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(1, WINED3D_TSS_COLOR_ARG1), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(1, WINED3D_TSS_COLOR_ARG2), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(1, WINED3D_TSS_COLOR_ARG0), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(1, WINED3D_TSS_ALPHA_OP), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(1, WINED3D_TSS_ALPHA_ARG1), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(1, WINED3D_TSS_ALPHA_ARG2), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(1, WINED3D_TSS_ALPHA_ARG0), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(1, WINED3D_TSS_RESULT_ARG), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_MAT00), { STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_MAT00), set_bumpmat }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_MAT01), { STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_MAT10), { STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_MAT11), { STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(1, WINED3D_TSS_CONSTANT), { STATE_TEXTURESTAGE(1, WINED3D_TSS_CONSTANT), atifs_stage_constant }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(2, WINED3D_TSS_COLOR_OP), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(2, WINED3D_TSS_COLOR_ARG1), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(2, WINED3D_TSS_COLOR_ARG2), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(2, WINED3D_TSS_COLOR_ARG0), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(2, WINED3D_TSS_ALPHA_OP), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(2, WINED3D_TSS_ALPHA_ARG1), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(2, WINED3D_TSS_ALPHA_ARG2), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(2, WINED3D_TSS_ALPHA_ARG0), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(2, WINED3D_TSS_RESULT_ARG), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_MAT00), { STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_MAT00), set_bumpmat }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_MAT01), { STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_MAT10), { STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_MAT11), { STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(2, WINED3D_TSS_CONSTANT), { STATE_TEXTURESTAGE(2, WINED3D_TSS_CONSTANT), atifs_stage_constant }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(3, WINED3D_TSS_COLOR_OP), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(3, WINED3D_TSS_COLOR_ARG1), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(3, WINED3D_TSS_COLOR_ARG2), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(3, WINED3D_TSS_COLOR_ARG0), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(3, WINED3D_TSS_ALPHA_OP), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(3, WINED3D_TSS_ALPHA_ARG1), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(3, WINED3D_TSS_ALPHA_ARG2), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(3, WINED3D_TSS_ALPHA_ARG0), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(3, WINED3D_TSS_RESULT_ARG), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_MAT00), { STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_MAT00), set_bumpmat }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_MAT01), { STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_MAT10), { STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_MAT11), { STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(3, WINED3D_TSS_CONSTANT), { STATE_TEXTURESTAGE(3, WINED3D_TSS_CONSTANT), atifs_stage_constant }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(4, WINED3D_TSS_COLOR_OP), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(4, WINED3D_TSS_COLOR_ARG1), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(4, WINED3D_TSS_COLOR_ARG2), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(4, WINED3D_TSS_COLOR_ARG0), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(4, WINED3D_TSS_ALPHA_OP), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(4, WINED3D_TSS_ALPHA_ARG1), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(4, WINED3D_TSS_ALPHA_ARG2), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(4, WINED3D_TSS_ALPHA_ARG0), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(4, WINED3D_TSS_RESULT_ARG), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_MAT00), { STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_MAT00), set_bumpmat }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_MAT01), { STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_MAT10), { STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_MAT11), { STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(4, WINED3D_TSS_CONSTANT), { STATE_TEXTURESTAGE(4, WINED3D_TSS_CONSTANT), atifs_stage_constant }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(5, WINED3D_TSS_COLOR_OP), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(5, WINED3D_TSS_COLOR_ARG1), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(5, WINED3D_TSS_COLOR_ARG2), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(5, WINED3D_TSS_COLOR_ARG0), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(5, WINED3D_TSS_ALPHA_OP), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(5, WINED3D_TSS_ALPHA_ARG1), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(5, WINED3D_TSS_ALPHA_ARG2), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(5, WINED3D_TSS_ALPHA_ARG0), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(5, WINED3D_TSS_RESULT_ARG), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_MAT00), { STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_MAT00), set_bumpmat }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_MAT01), { STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_MAT10), { STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_MAT11), { STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(5, WINED3D_TSS_CONSTANT), { STATE_TEXTURESTAGE(5, WINED3D_TSS_CONSTANT), atifs_stage_constant }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(6, WINED3D_TSS_COLOR_OP), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(6, WINED3D_TSS_COLOR_ARG1), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(6, WINED3D_TSS_COLOR_ARG2), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(6, WINED3D_TSS_COLOR_ARG0), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(6, WINED3D_TSS_ALPHA_OP), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(6, WINED3D_TSS_ALPHA_ARG1), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(6, WINED3D_TSS_ALPHA_ARG2), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(6, WINED3D_TSS_ALPHA_ARG0), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(6, WINED3D_TSS_RESULT_ARG), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_MAT00), { STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_MAT00), set_bumpmat }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_MAT01), { STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_MAT10), { STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_MAT11), { STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(6, WINED3D_TSS_CONSTANT), { STATE_TEXTURESTAGE(6, WINED3D_TSS_CONSTANT), atifs_stage_constant }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(7, WINED3D_TSS_COLOR_OP), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(7, WINED3D_TSS_COLOR_ARG1), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(7, WINED3D_TSS_COLOR_ARG2), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(7, WINED3D_TSS_COLOR_ARG0), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(7, WINED3D_TSS_ALPHA_OP), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(7, WINED3D_TSS_ALPHA_ARG1), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(7, WINED3D_TSS_ALPHA_ARG2), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(7, WINED3D_TSS_ALPHA_ARG0), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(7, WINED3D_TSS_RESULT_ARG), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_MAT00), { STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_MAT00), set_bumpmat }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_MAT01), { STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_MAT10), { STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_MAT11), { STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(7, WINED3D_TSS_CONSTANT), { STATE_TEXTURESTAGE(7, WINED3D_TSS_CONSTANT), atifs_stage_constant }, WINED3D_GL_EXT_NONE }, + { STATE_SAMPLER(0), { STATE_SAMPLER(0), sampler_texdim }, WINED3D_GL_EXT_NONE }, + { STATE_SAMPLER(1), { STATE_SAMPLER(1), sampler_texdim }, WINED3D_GL_EXT_NONE }, + { STATE_SAMPLER(2), { STATE_SAMPLER(2), sampler_texdim }, WINED3D_GL_EXT_NONE }, + { STATE_SAMPLER(3), { STATE_SAMPLER(3), sampler_texdim }, WINED3D_GL_EXT_NONE }, + { STATE_SAMPLER(4), { STATE_SAMPLER(4), sampler_texdim }, WINED3D_GL_EXT_NONE }, + { STATE_SAMPLER(5), { STATE_SAMPLER(5), sampler_texdim }, WINED3D_GL_EXT_NONE }, + { STATE_SAMPLER(6), { STATE_SAMPLER(6), sampler_texdim }, WINED3D_GL_EXT_NONE }, + { STATE_SAMPLER(7), { STATE_SAMPLER(7), sampler_texdim }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(0,WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), {STATE_TEXTURESTAGE(0, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), textransform }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(1,WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), {STATE_TEXTURESTAGE(1, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), textransform }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(2,WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), {STATE_TEXTURESTAGE(2, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), textransform }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(3,WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), {STATE_TEXTURESTAGE(3, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), textransform }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(4,WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), {STATE_TEXTURESTAGE(4, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), textransform }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(5,WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), {STATE_TEXTURESTAGE(5, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), textransform }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(6,WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), {STATE_TEXTURESTAGE(6, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), textransform }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(7,WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), {STATE_TEXTURESTAGE(7, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), textransform }, WINED3D_GL_EXT_NONE }, + {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), set_tex_op_atifs }, WINED3D_GL_EXT_NONE }, + {0 /* Terminate */, { 0, 0 }, WINED3D_GL_EXT_NONE }, +}; + +/* Context activation is done by the caller. */ +static void atifs_enable(const struct wined3d_context *context, BOOL enable) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl_const(context)->gl_info; + + if (enable) + { + gl_info->gl_ops.gl.p_glEnable(GL_FRAGMENT_SHADER_ATI); + checkGLcall("glEnable(GL_FRAGMENT_SHADER_ATI)"); + } + else + { + gl_info->gl_ops.gl.p_glDisable(GL_FRAGMENT_SHADER_ATI); + checkGLcall("glDisable(GL_FRAGMENT_SHADER_ATI)"); + } +} + +static void atifs_get_caps(const struct wined3d_adapter *adapter, struct fragment_caps *caps) +{ + caps->wined3d_caps = WINED3D_FRAGMENT_CAP_PROJ_CONTROL; + caps->PrimitiveMiscCaps = WINED3DPMISCCAPS_TSSARGTEMP | + WINED3DPMISCCAPS_PERSTAGECONSTANT; + caps->TextureOpCaps = WINED3DTEXOPCAPS_DISABLE | + WINED3DTEXOPCAPS_SELECTARG1 | + WINED3DTEXOPCAPS_SELECTARG2 | + WINED3DTEXOPCAPS_MODULATE4X | + WINED3DTEXOPCAPS_MODULATE2X | + WINED3DTEXOPCAPS_MODULATE | + WINED3DTEXOPCAPS_ADDSIGNED2X | + WINED3DTEXOPCAPS_ADDSIGNED | + WINED3DTEXOPCAPS_ADD | + WINED3DTEXOPCAPS_SUBTRACT | + WINED3DTEXOPCAPS_ADDSMOOTH | + WINED3DTEXOPCAPS_BLENDCURRENTALPHA | + WINED3DTEXOPCAPS_BLENDFACTORALPHA | + WINED3DTEXOPCAPS_BLENDTEXTUREALPHA | + WINED3DTEXOPCAPS_BLENDDIFFUSEALPHA | + WINED3DTEXOPCAPS_BLENDTEXTUREALPHAPM | + WINED3DTEXOPCAPS_MODULATEALPHA_ADDCOLOR | + WINED3DTEXOPCAPS_MODULATECOLOR_ADDALPHA | + WINED3DTEXOPCAPS_MODULATEINVCOLOR_ADDALPHA | + WINED3DTEXOPCAPS_MODULATEINVALPHA_ADDCOLOR | + WINED3DTEXOPCAPS_DOTPRODUCT3 | + WINED3DTEXOPCAPS_MULTIPLYADD | + WINED3DTEXOPCAPS_LERP | + WINED3DTEXOPCAPS_BUMPENVMAP; + + /* TODO: Implement WINED3DTEXOPCAPS_BUMPENVMAPLUMINANCE + and WINED3DTEXOPCAPS_PREMODULATE */ + + /* GL_ATI_fragment_shader always supports 6 textures, which was the limit on r200 cards + * which this extension is exclusively focused on(later cards have GL_ARB_fragment_program). + * If the current card has more than 8 fixed function textures in OpenGL's regular fixed + * function pipeline then the ATI_fragment_shader backend imposes a stricter limit. This + * shouldn't be too hard since Nvidia cards have a limit of 4 textures with the default ffp + * pipeline, and almost all games are happy with that. We can however support up to 8 + * texture stages because we have a 2nd pass limit of 8 instructions, and per stage we use + * only 1 instruction. + * + * The proper fix for this is not to use GL_ATI_fragment_shader on cards newer than the + * r200 series and use an ARB or GLSL shader instead + */ + caps->MaxTextureBlendStages = WINED3D_MAX_TEXTURES; + caps->MaxSimultaneousTextures = 6; +} + +static DWORD atifs_get_emul_mask(const struct wined3d_gl_info *gl_info) +{ + return GL_EXT_EMUL_ARB_MULTITEXTURE | GL_EXT_EMUL_EXT_FOG_COORD; +} + +static void *atifs_alloc(const struct wined3d_shader_backend_ops *shader_backend, void *shader_priv) +{ + struct atifs_private_data *priv; + + if (!(priv = heap_alloc_zero(sizeof(*priv)))) + return NULL; + + wine_rb_init(&priv->fragment_shaders, wined3d_ffp_frag_program_key_compare); + return priv; +} + +/* Context activation is done by the caller. */ +static void atifs_free_ffpshader(struct wine_rb_entry *entry, void *param) +{ + struct atifs_ffp_desc *entry_ati = WINE_RB_ENTRY_VALUE(entry, struct atifs_ffp_desc, parent.entry); + struct wined3d_context_gl *context_gl = param; + const struct wined3d_gl_info *gl_info; + + gl_info = context_gl->gl_info; + GL_EXTCALL(glDeleteFragmentShaderATI(entry_ati->shader)); + checkGLcall("glDeleteFragmentShaderATI(entry->shader)"); + heap_free(entry_ati); +} + +/* Context activation is done by the caller. */ +static void atifs_free(struct wined3d_device *device, struct wined3d_context *context) +{ + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + struct atifs_private_data *priv = device->fragment_priv; + + wine_rb_destroy(&priv->fragment_shaders, atifs_free_ffpshader, context_gl); + + heap_free(priv); + device->fragment_priv = NULL; +} + +static BOOL atifs_color_fixup_supported(struct color_fixup_desc fixup) +{ + /* We only support sign fixup of the first two channels. */ + return is_identity_fixup(fixup) || is_same_fixup(fixup, color_fixup_rg) + || is_same_fixup(fixup, color_fixup_rgl) || is_same_fixup(fixup, color_fixup_rgba); +} + +static BOOL atifs_alloc_context_data(struct wined3d_context *context) +{ + struct atifs_context_private_data *priv; + + if (!(priv = heap_alloc_zero(sizeof(*priv)))) + return FALSE; + context->fragment_pipe_data = priv; + return TRUE; +} + +static void atifs_free_context_data(struct wined3d_context *context) +{ + heap_free(context->fragment_pipe_data); +} + +const struct wined3d_fragment_pipe_ops atifs_fragment_pipeline = +{ + atifs_enable, + atifs_get_caps, + atifs_get_emul_mask, + atifs_alloc, + atifs_free, + atifs_alloc_context_data, + atifs_free_context_data, + atifs_color_fixup_supported, + atifs_fragmentstate_template, +}; diff --git a/wrappers/directx/d3dwine_wrapper/buffer.c b/wrappers/directx/d3dwine_wrapper/buffer.c new file mode 100644 index 00000000000..9ea62a81d0b --- /dev/null +++ b/wrappers/directx/d3dwine_wrapper/buffer.c @@ -0,0 +1,1607 @@ +/* + * Copyright 2002-2005 Jason Edmeades + * Copyright 2002-2005 Raphael Junqueira + * Copyright 2004 Christian Costa + * Copyright 2005 Oliver Stieber + * Copyright 2007-2011, 2013-2014 Stefan Dösinger for CodeWeavers + * Copyright 2009-2010 Henri Verbeet for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + */ + +#include "config.h" +#include "wine/port.h" + +#include "wined3d_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(d3d); + +#define WINED3D_BUFFER_HASDESC 0x01 /* A vertex description has been found. */ +#define WINED3D_BUFFER_USE_BO 0x02 /* Use a buffer object for this buffer. */ +#define WINED3D_BUFFER_PIN_SYSMEM 0x04 /* Keep a system memory copy for this buffer. */ + +#define VB_MAXDECLCHANGES 100 /* After that number of decl changes we stop converting */ +#define VB_RESETDECLCHANGE 1000 /* Reset the decl changecount after that number of draws */ +#define VB_MAXFULLCONVERSIONS 5 /* Number of full conversions before we stop converting */ +#define VB_RESETFULLCONVS 20 /* Reset full conversion counts after that number of draws */ + +static void wined3d_buffer_evict_sysmem(struct wined3d_buffer *buffer) +{ + if (buffer->flags & WINED3D_BUFFER_PIN_SYSMEM) + { + TRACE("Not evicting system memory for buffer %p.\n", buffer); + return; + } + + TRACE("Evicting system memory for buffer %p.\n", buffer); + wined3d_buffer_invalidate_location(buffer, WINED3D_LOCATION_SYSMEM); + wined3d_resource_free_sysmem(&buffer->resource); +} + +static void buffer_invalidate_bo_range(struct wined3d_buffer *buffer, unsigned int offset, unsigned int size) +{ + if (!offset && (!size || size == buffer->resource.size)) + goto invalidate_all; + + if (offset > buffer->resource.size || size > buffer->resource.size - offset) + { + WARN("Invalid range specified, invalidating entire buffer.\n"); + goto invalidate_all; + } + + if (!wined3d_array_reserve((void **)&buffer->maps, &buffer->maps_size, + buffer->modified_areas + 1, sizeof(*buffer->maps))) + { + ERR("Failed to allocate maps array, invalidating entire buffer.\n"); + goto invalidate_all; + } + + buffer->maps[buffer->modified_areas].offset = offset; + buffer->maps[buffer->modified_areas].size = size; + ++buffer->modified_areas; + return; + +invalidate_all: + buffer->modified_areas = 1; + buffer->maps[0].offset = 0; + buffer->maps[0].size = buffer->resource.size; +} + +static inline void buffer_clear_dirty_areas(struct wined3d_buffer *This) +{ + This->modified_areas = 0; +} + +static BOOL buffer_is_dirty(const struct wined3d_buffer *buffer) +{ + return !!buffer->modified_areas; +} + +static BOOL buffer_is_fully_dirty(const struct wined3d_buffer *buffer) +{ + return buffer->modified_areas == 1 + && !buffer->maps->offset && buffer->maps->size == buffer->resource.size; +} + +static void wined3d_buffer_validate_location(struct wined3d_buffer *buffer, DWORD location) +{ + TRACE("buffer %p, location %s.\n", buffer, wined3d_debug_location(location)); + + if (location & WINED3D_LOCATION_BUFFER) + buffer_clear_dirty_areas(buffer); + + buffer->locations |= location; + + TRACE("New locations flags are %s.\n", wined3d_debug_location(buffer->locations)); +} + +static void wined3d_buffer_invalidate_range(struct wined3d_buffer *buffer, DWORD location, + unsigned int offset, unsigned int size) +{ + TRACE("buffer %p, location %s, offset %u, size %u.\n", + buffer, wined3d_debug_location(location), offset, size); + + if (location & WINED3D_LOCATION_BUFFER) + buffer_invalidate_bo_range(buffer, offset, size); + + buffer->locations &= ~location; + + TRACE("New locations flags are %s.\n", wined3d_debug_location(buffer->locations)); + + if (!buffer->locations) + ERR("Buffer %p does not have any up to date location.\n", buffer); +} + +void wined3d_buffer_invalidate_location(struct wined3d_buffer *buffer, DWORD location) +{ + wined3d_buffer_invalidate_range(buffer, location, 0, 0); +} + +/* Context activation is done by the caller. */ +static void wined3d_buffer_gl_bind(struct wined3d_buffer_gl *buffer_gl, struct wined3d_context_gl *context_gl) +{ + wined3d_context_gl_bind_bo(context_gl, buffer_gl->bo.binding, buffer_gl->bo.id); +} + +/* Context activation is done by the caller. */ +static void wined3d_buffer_gl_destroy_buffer_object(struct wined3d_buffer_gl *buffer_gl, + struct wined3d_context_gl *context_gl) +{ + struct wined3d_resource *resource = &buffer_gl->b.resource; + + if (!buffer_gl->b.buffer_object) + return; + + if (context_gl->c.transform_feedback_active && resource->bind_count + && resource->bind_flags & WINED3D_BIND_STREAM_OUTPUT) + { + /* We have to make sure that transform feedback is not active + * when deleting a potentially bound transform feedback buffer. + * This may happen when the device is being destroyed. */ + WARN("Deleting buffer object for buffer %p, disabling transform feedback.\n", buffer_gl); + wined3d_context_gl_end_transform_feedback(context_gl); + } + + buffer_gl->bo_user.valid = false; + list_remove(&buffer_gl->bo_user.entry); + wined3d_context_gl_destroy_bo(context_gl, &buffer_gl->bo); + buffer_gl->b.buffer_object = 0; +} + +/* Context activation is done by the caller. */ +static BOOL wined3d_buffer_gl_create_buffer_object(struct wined3d_buffer_gl *buffer_gl, + struct wined3d_context_gl *context_gl) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + GLenum usage = GL_STATIC_DRAW; + GLbitfield gl_storage_flags; + struct wined3d_bo_gl *bo; + bool coherent = true; + GLsizeiptr size; + GLenum binding; + + TRACE("Creating an OpenGL buffer object for wined3d buffer %p with usage %s.\n", + buffer_gl, debug_d3dusage(buffer_gl->b.resource.usage)); + + size = buffer_gl->b.resource.size; + binding = wined3d_buffer_gl_binding_from_bind_flags(gl_info, buffer_gl->b.resource.bind_flags); + if (buffer_gl->b.resource.usage & WINED3DUSAGE_DYNAMIC) + { + usage = GL_STREAM_DRAW_ARB; + coherent = false; + } + gl_storage_flags = wined3d_resource_gl_storage_flags(&buffer_gl->b.resource); + bo = &buffer_gl->bo; + if (!wined3d_context_gl_create_bo(context_gl, size, binding, usage, coherent, gl_storage_flags, bo)) + { + ERR("Failed to create OpenGL buffer object.\n"); + buffer_gl->b.flags &= ~WINED3D_BUFFER_USE_BO; + buffer_clear_dirty_areas(&buffer_gl->b); + return FALSE; + } + + list_init(&buffer_gl->bo_user.entry); + list_add_head(&buffer_gl->bo.users, &buffer_gl->bo_user.entry); + buffer_gl->b.buffer_object = (uintptr_t)bo; + buffer_invalidate_bo_range(&buffer_gl->b, 0, 0); + + return TRUE; +} + +static BOOL buffer_process_converted_attribute(struct wined3d_buffer *buffer, + const enum wined3d_buffer_conversion_type conversion_type, + const struct wined3d_stream_info_element *attrib, DWORD *stride_this_run) +{ + const struct wined3d_format *format = attrib->format; + BOOL ret = FALSE; + unsigned int i; + DWORD_PTR data; + + /* Check for some valid situations which cause us pain. One is if the buffer is used for + * constant attributes(stride = 0), the other one is if the buffer is used on two streams + * with different strides. In the 2nd case we might have to drop conversion entirely, + * it is possible that the same bytes are once read as FLOAT2 and once as UBYTE4N. + */ + if (!attrib->stride) + { + FIXME("%s used with stride 0, let's hope we get the vertex stride from somewhere else.\n", + debug_d3dformat(format->id)); + } + else if (attrib->stride != *stride_this_run && *stride_this_run) + { + FIXME("Got two concurrent strides, %d and %d.\n", attrib->stride, *stride_this_run); + } + else + { + *stride_this_run = attrib->stride; + if (buffer->stride != *stride_this_run) + { + /* We rely that this happens only on the first converted attribute that is found, + * if at all. See above check + */ + TRACE("Reconverting because converted attributes occur, and the stride changed.\n"); + buffer->stride = *stride_this_run; + heap_free(buffer->conversion_map); + buffer->conversion_map = heap_calloc(buffer->stride, sizeof(*buffer->conversion_map)); + ret = TRUE; + } + } + + data = ((DWORD_PTR)attrib->data.addr) % buffer->stride; + for (i = 0; i < format->byte_count; ++i) + { + DWORD_PTR idx = (data + i) % buffer->stride; + if (buffer->conversion_map[idx] != conversion_type) + { + TRACE("Byte %lu in vertex changed:\n", idx); + TRACE(" It was type %#x, is %#x now.\n", buffer->conversion_map[idx], conversion_type); + ret = TRUE; + buffer->conversion_map[idx] = conversion_type; + } + } + + return ret; +} + +#define WINED3D_BUFFER_FIXUP_D3DCOLOR 0x01 +#define WINED3D_BUFFER_FIXUP_XYZRHW 0x02 + +static BOOL buffer_check_attribute(struct wined3d_buffer *This, const struct wined3d_stream_info *si, + const struct wined3d_state *state, UINT attrib_idx, DWORD fixup_flags, DWORD *stride_this_run) +{ + const struct wined3d_stream_info_element *attrib = &si->elements[attrib_idx]; + enum wined3d_format_id format; + BOOL ret = FALSE; + + /* Ignore attributes that do not have our vbo. After that check we can be sure that the attribute is + * there, on nonexistent attribs the vbo is 0. + */ + if (!(si->use_map & (1u << attrib_idx)) + || state->streams[attrib->stream_idx].buffer != This) + return FALSE; + + format = attrib->format->id; + /* Look for newly appeared conversion */ + if (fixup_flags & WINED3D_BUFFER_FIXUP_D3DCOLOR && format == WINED3DFMT_B8G8R8A8_UNORM) + { + ret = buffer_process_converted_attribute(This, CONV_D3DCOLOR, attrib, stride_this_run); + } + else if (fixup_flags & WINED3D_BUFFER_FIXUP_XYZRHW && si->position_transformed) + { + if (format != WINED3DFMT_R32G32B32A32_FLOAT) + { + FIXME("Unexpected format %s for transformed position.\n", debug_d3dformat(format)); + return FALSE; + } + + ret = buffer_process_converted_attribute(This, CONV_POSITIONT, attrib, stride_this_run); + } + else if (This->conversion_map) + { + ret = buffer_process_converted_attribute(This, CONV_NONE, attrib, stride_this_run); + } + + return ret; +} + +static BOOL buffer_find_decl(struct wined3d_buffer *This, const struct wined3d_stream_info *si, + const struct wined3d_state *state, DWORD fixup_flags) +{ + UINT stride_this_run = 0; + BOOL ret = FALSE; + + /* In d3d7 the vertex buffer declaration NEVER changes because it is stored in the d3d7 vertex buffer. + * Once we have our declaration there is no need to look it up again. Index buffers also never need + * conversion, so once the (empty) conversion structure is created don't bother checking again + */ + if (This->flags & WINED3D_BUFFER_HASDESC) + { + if(This->resource.usage & WINED3DUSAGE_STATICDECL) return FALSE; + } + + if (!fixup_flags) + { + TRACE("No fixup required.\n"); + if(This->conversion_map) + { + heap_free(This->conversion_map); + This->conversion_map = NULL; + This->stride = 0; + return TRUE; + } + + return FALSE; + } + + TRACE("Finding vertex buffer conversion information\n"); + /* Certain declaration types need some fixups before we can pass them to + * opengl. This means D3DCOLOR attributes with fixed function vertex + * processing, FLOAT4 POSITIONT with fixed function, and FLOAT16 if + * GL_ARB_half_float_vertex is not supported. + * + * Note for d3d8 and d3d9: + * The vertex buffer FVF doesn't help with finding them, we have to use + * the decoded vertex declaration and pick the things that concern the + * current buffer. A problem with this is that this can change between + * draws, so we have to validate the information and reprocess the buffer + * if it changes, and avoid false positives for performance reasons. + * WineD3D doesn't even know the vertex buffer any more, it is managed + * by the client libraries and passed to SetStreamSource and ProcessVertices + * as needed. + * + * We have to distinguish between vertex shaders and fixed function to + * pick the way we access the strided vertex information. + * + * This code sets up a per-byte array with the size of the detected + * stride of the arrays in the buffer. For each byte we have a field + * that marks the conversion needed on this byte. For example, the + * following declaration with fixed function vertex processing: + * + * POSITIONT, FLOAT4 + * NORMAL, FLOAT3 + * DIFFUSE, FLOAT16_4 + * SPECULAR, D3DCOLOR + * + * Will result in + * { POSITIONT }{ NORMAL }{ DIFFUSE }{SPECULAR } + * [P][P][P][P][P][P][P][P][P][P][P][P][P][P][P][P][0][0][0][0][0][0][0][0][0][0][0][0][F][F][F][F][F][F][F][F][C][C][C][C] + * + * Where in this example map P means 4 component position conversion, 0 + * means no conversion, F means FLOAT16_2 conversion and C means D3DCOLOR + * conversion (red / blue swizzle). + * + * If we're doing conversion and the stride changes we have to reconvert + * the whole buffer. Note that we do not mind if the semantic changes, + * we only care for the conversion type. So if the NORMAL is replaced + * with a TEXCOORD, nothing has to be done, or if the DIFFUSE is replaced + * with a D3DCOLOR BLENDWEIGHT we can happily dismiss the change. Some + * conversion types depend on the semantic as well, for example a FLOAT4 + * texcoord needs no conversion while a FLOAT4 positiont needs one + */ + + ret = buffer_check_attribute(This, si, state, WINED3D_FFP_POSITION, + fixup_flags, &stride_this_run) || ret; + fixup_flags &= ~WINED3D_BUFFER_FIXUP_XYZRHW; + + ret = buffer_check_attribute(This, si, state, WINED3D_FFP_BLENDWEIGHT, + fixup_flags, &stride_this_run) || ret; + ret = buffer_check_attribute(This, si, state, WINED3D_FFP_BLENDINDICES, + fixup_flags, &stride_this_run) || ret; + ret = buffer_check_attribute(This, si, state, WINED3D_FFP_NORMAL, + fixup_flags, &stride_this_run) || ret; + ret = buffer_check_attribute(This, si, state, WINED3D_FFP_DIFFUSE, + fixup_flags, &stride_this_run) || ret; + ret = buffer_check_attribute(This, si, state, WINED3D_FFP_SPECULAR, + fixup_flags, &stride_this_run) || ret; + ret = buffer_check_attribute(This, si, state, WINED3D_FFP_TEXCOORD0, + fixup_flags, &stride_this_run) || ret; + ret = buffer_check_attribute(This, si, state, WINED3D_FFP_TEXCOORD1, + fixup_flags, &stride_this_run) || ret; + ret = buffer_check_attribute(This, si, state, WINED3D_FFP_TEXCOORD2, + fixup_flags, &stride_this_run) || ret; + ret = buffer_check_attribute(This, si, state, WINED3D_FFP_TEXCOORD3, + fixup_flags, &stride_this_run) || ret; + ret = buffer_check_attribute(This, si, state, WINED3D_FFP_TEXCOORD4, + fixup_flags, &stride_this_run) || ret; + ret = buffer_check_attribute(This, si, state, WINED3D_FFP_TEXCOORD5, + fixup_flags, &stride_this_run) || ret; + ret = buffer_check_attribute(This, si, state, WINED3D_FFP_TEXCOORD6, + fixup_flags, &stride_this_run) || ret; + ret = buffer_check_attribute(This, si, state, WINED3D_FFP_TEXCOORD7, + fixup_flags, &stride_this_run) || ret; + + if (!stride_this_run && This->conversion_map) + { + /* Sanity test */ + if (!ret) + ERR("no converted attributes found, old conversion map exists, and no declaration change?\n"); + heap_free(This->conversion_map); + This->conversion_map = NULL; + This->stride = 0; + } + + if (ret) TRACE("Conversion information changed\n"); + + return ret; +} + +static inline unsigned int fixup_d3dcolor(DWORD *dst_color) +{ + DWORD src_color = *dst_color; + + /* Color conversion like in draw_primitive_immediate_mode(). Watch out for + * endianness. If we want this to work on big-endian machines as well we + * have to consider more things. + * + * 0xff000000: Alpha mask + * 0x00ff0000: Blue mask + * 0x0000ff00: Green mask + * 0x000000ff: Red mask + */ + *dst_color = 0; + *dst_color |= (src_color & 0xff00ff00u); /* Alpha Green */ + *dst_color |= (src_color & 0x00ff0000u) >> 16; /* Red */ + *dst_color |= (src_color & 0x000000ffu) << 16; /* Blue */ + + return sizeof(*dst_color); +} + +static inline unsigned int fixup_transformed_pos(struct wined3d_vec4 *p) +{ + /* rhw conversion like in position_float4(). */ + if (p->w != 1.0f && p->w != 0.0f) + { + float w = 1.0f / p->w; + p->x *= w; + p->y *= w; + p->z *= w; + p->w = w; + } + + return sizeof(*p); +} + +ULONG CDECL wined3d_buffer_incref(struct wined3d_buffer *buffer) +{ + ULONG refcount = InterlockedIncrement(&buffer->resource.ref); + + TRACE("%p increasing refcount to %u.\n", buffer, refcount); + + return refcount; +} + +static void buffer_conversion_upload(struct wined3d_buffer *buffer, struct wined3d_context *context) +{ + unsigned int i, j, range_idx, start, end, vertex_count; + BYTE *data; + + if (!wined3d_buffer_load_location(buffer, context, WINED3D_LOCATION_SYSMEM)) + { + ERR("Failed to load system memory.\n"); + return; + } + buffer->flags |= WINED3D_BUFFER_PIN_SYSMEM; + + /* Now for each vertex in the buffer that needs conversion. */ + vertex_count = buffer->resource.size / buffer->stride; + + if (!(data = heap_alloc(buffer->resource.size))) + { + ERR("Out of memory.\n"); + return; + } + + for (range_idx = 0; range_idx < buffer->modified_areas; ++range_idx) + { + start = buffer->maps[range_idx].offset; + end = start + buffer->maps[range_idx].size; + + memcpy(data + start, (BYTE *)buffer->resource.heap_memory + start, end - start); + for (i = start / buffer->stride; i < min((end / buffer->stride) + 1, vertex_count); ++i) + { + for (j = 0; j < buffer->stride;) + { + switch (buffer->conversion_map[j]) + { + case CONV_NONE: + /* Done already */ + j += sizeof(DWORD); + break; + case CONV_D3DCOLOR: + j += fixup_d3dcolor((DWORD *) (data + i * buffer->stride + j)); + break; + case CONV_POSITIONT: + j += fixup_transformed_pos((struct wined3d_vec4 *) (data + i * buffer->stride + j)); + break; + default: + FIXME("Unimplemented conversion %d in shifted conversion.\n", buffer->conversion_map[j]); + ++j; + } + } + } + } + + buffer->buffer_ops->buffer_upload_ranges(buffer, context, + data, 0, buffer->modified_areas, buffer->maps); + + heap_free(data); +} + +BOOL wined3d_buffer_prepare_location(struct wined3d_buffer *buffer, + struct wined3d_context *context, unsigned int location) +{ + return buffer->buffer_ops->buffer_prepare_location(buffer, context, location); +} + +static void wined3d_buffer_unload_location(struct wined3d_buffer *buffer, + struct wined3d_context *context, unsigned int location) +{ + buffer->buffer_ops->buffer_unload_location(buffer, context, location); +} + +BOOL wined3d_buffer_load_location(struct wined3d_buffer *buffer, + struct wined3d_context *context, DWORD location) +{ + struct wined3d_range range; + + TRACE("buffer %p, context %p, location %s.\n", + buffer, context, wined3d_debug_location(location)); + + if (buffer->locations & location) + { + TRACE("Location (%#x) is already up to date.\n", location); + return TRUE; + } + + if (!buffer->locations) + { + ERR("Buffer %p does not have any up to date location.\n", buffer); + wined3d_buffer_validate_location(buffer, WINED3D_LOCATION_DISCARDED); + return wined3d_buffer_load_location(buffer, context, location); + } + + TRACE("Current buffer location %s.\n", wined3d_debug_location(buffer->locations)); + + if (!wined3d_buffer_prepare_location(buffer, context, location)) + return FALSE; + + if (buffer->locations & WINED3D_LOCATION_DISCARDED) + { + TRACE("Buffer previously discarded, nothing to do.\n"); + wined3d_buffer_validate_location(buffer, location); + wined3d_buffer_invalidate_location(buffer, WINED3D_LOCATION_DISCARDED); + return TRUE; + } + + switch (location) + { + case WINED3D_LOCATION_SYSMEM: + range.offset = 0; + range.size = buffer->resource.size; + buffer->buffer_ops->buffer_download_ranges(buffer, context, + buffer->resource.heap_memory, 0, 1, &range); + break; + + case WINED3D_LOCATION_BUFFER: + if (!buffer->conversion_map) + buffer->buffer_ops->buffer_upload_ranges(buffer, context, + buffer->resource.heap_memory, 0, buffer->modified_areas, buffer->maps); + else + buffer_conversion_upload(buffer, context); + break; + + default: + ERR("Invalid location %s.\n", wined3d_debug_location(location)); + return FALSE; + } + + wined3d_buffer_validate_location(buffer, location); + if (buffer->resource.heap_memory && location == WINED3D_LOCATION_BUFFER + && !(buffer->resource.usage & WINED3DUSAGE_DYNAMIC)) + wined3d_buffer_evict_sysmem(buffer); + + return TRUE; +} + +/* Context activation is done by the caller. */ +BYTE *wined3d_buffer_load_sysmem(struct wined3d_buffer *buffer, struct wined3d_context *context) +{ + if (wined3d_buffer_load_location(buffer, context, WINED3D_LOCATION_SYSMEM)) + buffer->flags |= WINED3D_BUFFER_PIN_SYSMEM; + return buffer->resource.heap_memory; +} + +DWORD wined3d_buffer_get_memory(struct wined3d_buffer *buffer, + struct wined3d_bo_address *data, DWORD locations) +{ + TRACE("buffer %p, data %p, locations %s.\n", + buffer, data, wined3d_debug_location(locations)); + + if (locations & WINED3D_LOCATION_BUFFER) + { + data->buffer_object = buffer->buffer_object; + data->addr = NULL; + return WINED3D_LOCATION_BUFFER; + } + if (locations & WINED3D_LOCATION_SYSMEM) + { + data->buffer_object = 0; + data->addr = buffer->resource.heap_memory; + return WINED3D_LOCATION_SYSMEM; + } + + ERR("Unexpected locations %s.\n", wined3d_debug_location(locations)); + data->buffer_object = 0; + data->addr = NULL; + return 0; +} + +static void buffer_resource_unload(struct wined3d_resource *resource) +{ + struct wined3d_buffer *buffer = buffer_from_resource(resource); + + TRACE("buffer %p.\n", buffer); + + if (buffer->buffer_object) + { + struct wined3d_context *context; + + context = context_acquire(resource->device, NULL, 0); + + wined3d_buffer_load_location(buffer, context, WINED3D_LOCATION_SYSMEM); + wined3d_buffer_invalidate_location(buffer, WINED3D_LOCATION_BUFFER); + wined3d_buffer_unload_location(buffer, context, WINED3D_LOCATION_BUFFER); + buffer_clear_dirty_areas(buffer); + + context_release(context); + + heap_free(buffer->conversion_map); + buffer->conversion_map = NULL; + buffer->stride = 0; + buffer->conversion_stride = 0; + buffer->flags &= ~WINED3D_BUFFER_HASDESC; + } + + resource_unload(resource); +} + +static void wined3d_buffer_drop_bo(struct wined3d_buffer *buffer) +{ + buffer->flags &= ~WINED3D_BUFFER_USE_BO; + buffer_resource_unload(&buffer->resource); +} + +static void wined3d_buffer_destroy_object(void *object) +{ + struct wined3d_buffer *buffer = object; + struct wined3d_context *context; + + TRACE("buffer %p.\n", buffer); + + if (buffer->buffer_object) + { + context = context_acquire(buffer->resource.device, NULL, 0); + wined3d_buffer_unload_location(buffer, context, WINED3D_LOCATION_BUFFER); + context_release(context); + } + heap_free(buffer->conversion_map); + heap_free(buffer->maps); +} + +void wined3d_buffer_cleanup(struct wined3d_buffer *buffer) +{ + wined3d_cs_destroy_object(buffer->resource.device->cs, wined3d_buffer_destroy_object, buffer); + resource_cleanup(&buffer->resource); +} + +ULONG CDECL wined3d_buffer_decref(struct wined3d_buffer *buffer) +{ + ULONG refcount = InterlockedDecrement(&buffer->resource.ref); + + TRACE("%p decreasing refcount to %u.\n", buffer, refcount); + + if (!refcount) + { + buffer->resource.parent_ops->wined3d_object_destroyed(buffer->resource.parent); + buffer->resource.device->adapter->adapter_ops->adapter_destroy_buffer(buffer); + } + + return refcount; +} + +void * CDECL wined3d_buffer_get_parent(const struct wined3d_buffer *buffer) +{ + TRACE("buffer %p.\n", buffer); + + return buffer->resource.parent; +} + +/* Context activation is done by the caller. */ +void wined3d_buffer_load(struct wined3d_buffer *buffer, struct wined3d_context *context, + const struct wined3d_state *state) +{ + const struct wined3d_d3d_info *d3d_info = context->d3d_info; + BOOL decl_changed = FALSE; + + TRACE("buffer %p.\n", buffer); + + if (buffer->resource.map_count && buffer->map_ptr) + { + FIXME("Buffer is mapped through buffer object, not loading.\n"); + return; + } + else if (buffer->resource.map_count) + { + WARN("Loading mapped buffer.\n"); + } + + /* TODO: Make converting independent from VBOs */ + if (!(buffer->flags & WINED3D_BUFFER_USE_BO)) + { + /* Not doing any conversion */ + return; + } + + if (!wined3d_buffer_prepare_location(buffer, context, WINED3D_LOCATION_BUFFER)) + { + ERR("Failed to prepare buffer location.\n"); + return; + } + + /* Reading the declaration makes only sense if we have valid state information + * (i.e., if this function is called during draws). */ + if (state) + { + DWORD fixup_flags = 0; + + if (!use_vs(state)) + { + if (!d3d_info->vertex_bgra && !d3d_info->ffp_generic_attributes) + fixup_flags |= WINED3D_BUFFER_FIXUP_D3DCOLOR; + if (!d3d_info->xyzrhw) + fixup_flags |= WINED3D_BUFFER_FIXUP_XYZRHW; + } + + decl_changed = buffer_find_decl(buffer, &context->stream_info, state, fixup_flags); + buffer->flags |= WINED3D_BUFFER_HASDESC; + } + + if (!decl_changed && !(buffer->flags & WINED3D_BUFFER_HASDESC && buffer_is_dirty(buffer))) + { + ++buffer->draw_count; + if (buffer->draw_count > VB_RESETDECLCHANGE) + buffer->decl_change_count = 0; + if (buffer->draw_count > VB_RESETFULLCONVS) + buffer->full_conversion_count = 0; + return; + } + + /* If applications change the declaration over and over, reconverting all the time is a huge + * performance hit. So count the declaration changes and release the VBO if there are too many + * of them (and thus stop converting) + */ + if (decl_changed) + { + ++buffer->decl_change_count; + buffer->draw_count = 0; + + if (buffer->decl_change_count > VB_MAXDECLCHANGES + || (buffer->conversion_map && (buffer->resource.usage & WINED3DUSAGE_DYNAMIC))) + { + FIXME("Too many declaration changes or converting dynamic buffer, stopping converting.\n"); + wined3d_buffer_drop_bo(buffer); + return; + } + + /* The declaration changed, reload the whole buffer. */ + WARN("Reloading buffer because of a vertex declaration change.\n"); + buffer_invalidate_bo_range(buffer, 0, 0); + } + else + { + /* However, it is perfectly fine to change the declaration every now and then. We don't want a game that + * changes it every minute drop the VBO after VB_MAX_DECL_CHANGES minutes. So count draws without + * decl changes and reset the decl change count after a specific number of them + */ + if (buffer->conversion_map && buffer_is_fully_dirty(buffer)) + { + ++buffer->full_conversion_count; + if (buffer->full_conversion_count > VB_MAXFULLCONVERSIONS) + { + FIXME("Too many full buffer conversions, stopping converting.\n"); + wined3d_buffer_drop_bo(buffer); + return; + } + } + else + { + ++buffer->draw_count; + if (buffer->draw_count > VB_RESETDECLCHANGE) + buffer->decl_change_count = 0; + if (buffer->draw_count > VB_RESETFULLCONVS) + buffer->full_conversion_count = 0; + } + } + + if (!wined3d_buffer_load_location(buffer, context, WINED3D_LOCATION_BUFFER)) + ERR("Failed to load buffer location.\n"); +} + +struct wined3d_resource * CDECL wined3d_buffer_get_resource(struct wined3d_buffer *buffer) +{ + TRACE("buffer %p.\n", buffer); + + return &buffer->resource; +} + +static HRESULT buffer_resource_sub_resource_map(struct wined3d_resource *resource, unsigned int sub_resource_idx, + struct wined3d_map_desc *map_desc, const struct wined3d_box *box, uint32_t flags) +{ + struct wined3d_buffer *buffer = buffer_from_resource(resource); + struct wined3d_device *device = resource->device; + struct wined3d_context *context; + unsigned int offset, size; + uint8_t *base; + LONG count; + + TRACE("resource %p, sub_resource_idx %u, map_desc %p, box %s, flags %#x.\n", + resource, sub_resource_idx, map_desc, debug_box(box), flags); + + if (sub_resource_idx) + { + WARN("Invalid sub_resource_idx %u.\n", sub_resource_idx); + return E_INVALIDARG; + } + + if (box) + { + offset = box->left; + size = box->right - box->left; + } + else + { + offset = size = 0; + } + + map_desc->row_pitch = map_desc->slice_pitch = resource->size; + + count = ++resource->map_count; + + if (buffer->buffer_object) + { + unsigned int dirty_offset = offset, dirty_size = size; + struct wined3d_bo_address addr; + + /* DISCARD invalidates the entire buffer, regardless of the specified + * offset and size. Some applications also depend on the entire buffer + * being uploaded in that case. Two such applications are Port Royale + * and Darkstar One. */ + if (flags & WINED3D_MAP_DISCARD) + { + dirty_offset = 0; + dirty_size = 0; + } + + if (((flags & WINED3D_MAP_WRITE) && !(flags & (WINED3D_MAP_NOOVERWRITE | WINED3D_MAP_DISCARD))) + || (!(flags & WINED3D_MAP_WRITE) && (buffer->locations & WINED3D_LOCATION_SYSMEM)) + || buffer->flags & WINED3D_BUFFER_PIN_SYSMEM) + { + if (!(buffer->locations & WINED3D_LOCATION_SYSMEM)) + { + context = context_acquire(device, NULL, 0); + wined3d_buffer_load_location(buffer, context, WINED3D_LOCATION_SYSMEM); + context_release(context); + } + + if (flags & WINED3D_MAP_WRITE) + wined3d_buffer_invalidate_range(buffer, WINED3D_LOCATION_BUFFER, dirty_offset, dirty_size); + } + else + { + context = context_acquire(device, NULL, 0); + + if (flags & WINED3D_MAP_DISCARD) + wined3d_buffer_validate_location(buffer, WINED3D_LOCATION_BUFFER); + else + wined3d_buffer_load_location(buffer, context, WINED3D_LOCATION_BUFFER); + + if (flags & WINED3D_MAP_WRITE) + { + wined3d_buffer_invalidate_location(buffer, WINED3D_LOCATION_SYSMEM); + buffer_invalidate_bo_range(buffer, dirty_offset, dirty_size); + } + + if ((flags & WINED3D_MAP_DISCARD) && resource->heap_memory) + wined3d_buffer_evict_sysmem(buffer); + + if (count == 1) + { + addr.buffer_object = buffer->buffer_object; + addr.addr = 0; + buffer->map_ptr = wined3d_context_map_bo_address(context, &addr, resource->size, flags); + + if (((DWORD_PTR)buffer->map_ptr) & (RESOURCE_ALIGNMENT - 1)) + { + WARN("Pointer %p is not %u byte aligned.\n", buffer->map_ptr, RESOURCE_ALIGNMENT); + + wined3d_context_unmap_bo_address(context, &addr, 0, NULL); + buffer->map_ptr = NULL; + + if (resource->usage & WINED3DUSAGE_DYNAMIC) + { + /* The extra copy is more expensive than not using VBOs + * at all on the NVIDIA Linux driver, which is the + * only driver that returns unaligned pointers. */ + TRACE("Dynamic buffer, dropping VBO.\n"); + wined3d_buffer_drop_bo(buffer); + } + else + { + TRACE("Falling back to doublebuffered operation.\n"); + wined3d_buffer_load_location(buffer, context, WINED3D_LOCATION_SYSMEM); + buffer->flags |= WINED3D_BUFFER_PIN_SYSMEM; + } + TRACE("New pointer is %p.\n", resource->heap_memory); + } + } + + context_release(context); + } + } + + base = buffer->map_ptr ? buffer->map_ptr : resource->heap_memory; + map_desc->data = base + offset; + + TRACE("Returning memory at %p (base %p, offset %u).\n", map_desc->data, base, offset); + + return WINED3D_OK; +} + +static HRESULT buffer_resource_sub_resource_unmap(struct wined3d_resource *resource, unsigned int sub_resource_idx) +{ + struct wined3d_buffer *buffer = buffer_from_resource(resource); + unsigned int range_count = buffer->modified_areas; + struct wined3d_device *device = resource->device; + struct wined3d_context *context; + struct wined3d_bo_address addr; + + TRACE("resource %p, sub_resource_idx %u.\n", resource, sub_resource_idx); + + if (sub_resource_idx) + { + WARN("Invalid sub_resource_idx %u.\n", sub_resource_idx); + return E_INVALIDARG; + } + + if (!resource->map_count) + { + WARN("Unmap called without a previous map call.\n"); + return WINED3D_OK; + } + + if (--resource->map_count) + { + /* Delay loading the buffer until everything is unmapped. */ + TRACE("Ignoring unmap.\n"); + return WINED3D_OK; + } + + if (!buffer->map_ptr) + return WINED3D_OK; + + context = context_acquire(device, NULL, 0); + + addr.buffer_object = buffer->buffer_object; + addr.addr = 0; + wined3d_context_unmap_bo_address(context, &addr, range_count, buffer->maps); + + context_release(context); + + buffer_clear_dirty_areas(buffer); + buffer->map_ptr = NULL; + + return WINED3D_OK; +} + +void wined3d_buffer_copy(struct wined3d_buffer *dst_buffer, unsigned int dst_offset, + struct wined3d_buffer *src_buffer, unsigned int src_offset, unsigned int size) +{ + struct wined3d_bo_address dst, src; + struct wined3d_context *context; + DWORD dst_location; + + TRACE("dst_buffer %p, dst_offset %u, src_buffer %p, src_offset %u, size %u.\n", + dst_buffer, dst_offset, src_buffer, src_offset, size); + + dst_location = wined3d_buffer_get_memory(dst_buffer, &dst, dst_buffer->locations); + dst.addr += dst_offset; + + wined3d_buffer_get_memory(src_buffer, &src, src_buffer->locations); + src.addr += src_offset; + + context = context_acquire(dst_buffer->resource.device, NULL, 0); + wined3d_context_copy_bo_address(context, &dst, &src, size); + context_release(context); + + wined3d_buffer_invalidate_range(dst_buffer, ~dst_location, dst_offset, size); +} + +void wined3d_buffer_upload_data(struct wined3d_buffer *buffer, struct wined3d_context *context, + const struct wined3d_box *box, const void *data) +{ + struct wined3d_range range; + + if (box) + { + range.offset = box->left; + range.size = box->right - box->left; + } + else + { + range.offset = 0; + range.size = buffer->resource.size; + } + + buffer->buffer_ops->buffer_upload_ranges(buffer, context, data, range.offset, 1, &range); +} + +static void wined3d_buffer_init_data(struct wined3d_buffer *buffer, + struct wined3d_device *device, const struct wined3d_sub_resource_data *data) +{ + struct wined3d_resource *resource = &buffer->resource; + struct wined3d_bo_address bo; + struct wined3d_box box; + + if (buffer->flags & WINED3D_BUFFER_USE_BO) + { + wined3d_box_set(&box, 0, 0, resource->size, 1, 0, 1); + wined3d_cs_emit_update_sub_resource(device->cs, resource, + 0, &box, data->data, data->row_pitch, data->slice_pitch); + } + else + { + wined3d_buffer_get_memory(buffer, &bo, WINED3D_LOCATION_SYSMEM); + memcpy(bo.addr, data->data, resource->size); + wined3d_buffer_validate_location(buffer, WINED3D_LOCATION_SYSMEM); + wined3d_buffer_invalidate_location(buffer, ~WINED3D_LOCATION_SYSMEM); + } +} + +static ULONG buffer_resource_incref(struct wined3d_resource *resource) +{ + return wined3d_buffer_incref(buffer_from_resource(resource)); +} + +static ULONG buffer_resource_decref(struct wined3d_resource *resource) +{ + return wined3d_buffer_decref(buffer_from_resource(resource)); +} + +static void buffer_resource_preload(struct wined3d_resource *resource) +{ + struct wined3d_context *context; + + context = context_acquire(resource->device, NULL, 0); + wined3d_buffer_load(buffer_from_resource(resource), context, NULL); + context_release(context); +} + +static const struct wined3d_resource_ops buffer_resource_ops = +{ + buffer_resource_incref, + buffer_resource_decref, + buffer_resource_preload, + buffer_resource_unload, + buffer_resource_sub_resource_map, + buffer_resource_sub_resource_unmap, +}; + +GLenum wined3d_buffer_gl_binding_from_bind_flags(const struct wined3d_gl_info *gl_info, uint32_t bind_flags) +{ + if (!bind_flags) + return GL_PIXEL_UNPACK_BUFFER; + + if (bind_flags == WINED3D_BIND_INDEX_BUFFER) + return GL_ELEMENT_ARRAY_BUFFER; + + if (bind_flags & (WINED3D_BIND_SHADER_RESOURCE | WINED3D_BIND_UNORDERED_ACCESS) + && gl_info->supported[ARB_TEXTURE_BUFFER_OBJECT]) + return GL_TEXTURE_BUFFER; + + if (bind_flags & WINED3D_BIND_CONSTANT_BUFFER) + return GL_UNIFORM_BUFFER; + + if (bind_flags & WINED3D_BIND_STREAM_OUTPUT) + return GL_TRANSFORM_FEEDBACK_BUFFER; + + if (bind_flags & WINED3D_BIND_INDIRECT_BUFFER + && gl_info->supported[ARB_DRAW_INDIRECT]) + return GL_DRAW_INDIRECT_BUFFER; + + if (bind_flags & ~(WINED3D_BIND_VERTEX_BUFFER | WINED3D_BIND_INDEX_BUFFER)) + FIXME("Unhandled bind flags %#x.\n", bind_flags); + + return GL_ARRAY_BUFFER; +} + +static HRESULT wined3d_buffer_init(struct wined3d_buffer *buffer, struct wined3d_device *device, + const struct wined3d_buffer_desc *desc, const struct wined3d_sub_resource_data *data, + void *parent, const struct wined3d_parent_ops *parent_ops, const struct wined3d_buffer_ops *buffer_ops) +{ + const struct wined3d_format *format = wined3d_get_format(device->adapter, WINED3DFMT_UNKNOWN, desc->bind_flags); + struct wined3d_resource *resource = &buffer->resource; + HRESULT hr; + + TRACE("buffer %p, device %p, desc byte_width %u, usage %s, bind_flags %s, " + "access %s, data %p, parent %p, parent_ops %p.\n", + buffer, device, desc->byte_width, debug_d3dusage(desc->usage), wined3d_debug_bind_flags(desc->bind_flags), + wined3d_debug_resource_access(desc->access), data, parent, parent_ops); + + if (!desc->byte_width) + { + WARN("Size 0 requested, returning E_INVALIDARG.\n"); + return E_INVALIDARG; + } + + if (desc->bind_flags & WINED3D_BIND_CONSTANT_BUFFER && desc->byte_width & (WINED3D_CONSTANT_BUFFER_ALIGNMENT - 1)) + { + WARN("Size %#x is not suitably aligned for constant buffers.\n", desc->byte_width); + return E_INVALIDARG; + } + + if (data && !data->data) + { + WARN("Invalid sub-resource data specified.\n"); + return E_INVALIDARG; + } + + if (FAILED(hr = resource_init(resource, device, WINED3D_RTYPE_BUFFER, format, + WINED3D_MULTISAMPLE_NONE, 0, desc->usage, desc->bind_flags, desc->access, + desc->byte_width, 1, 1, desc->byte_width, parent, parent_ops, &buffer_resource_ops))) + { + WARN("Failed to initialize resource, hr %#x.\n", hr); + return hr; + } + buffer->buffer_ops = buffer_ops; + buffer->structure_byte_stride = desc->structure_byte_stride; + buffer->locations = data ? WINED3D_LOCATION_DISCARDED : WINED3D_LOCATION_SYSMEM; + + TRACE("buffer %p, size %#x, usage %#x, memory @ %p.\n", + buffer, buffer->resource.size, buffer->resource.usage, buffer->resource.heap_memory); + + if (device->create_parms.flags & WINED3DCREATE_SOFTWARE_VERTEXPROCESSING + || wined3d_resource_access_is_managed(desc->access)) + { + /* SWvp and managed buffers always return the same pointer in buffer + * maps and retain data in DISCARD maps. Keep a system memory copy of + * the buffer to provide the same behavior to the application. */ + TRACE("Pinning system memory.\n"); + buffer->flags |= WINED3D_BUFFER_PIN_SYSMEM; + buffer->locations = WINED3D_LOCATION_SYSMEM; + } + + if (buffer->locations & WINED3D_LOCATION_SYSMEM || !(buffer->flags & WINED3D_BUFFER_USE_BO)) + { + if (!wined3d_resource_prepare_sysmem(&buffer->resource)) + return E_OUTOFMEMORY; + } + + if (!(buffer->maps = heap_alloc(sizeof(*buffer->maps)))) + { + ERR("Out of memory.\n"); + buffer_resource_unload(resource); + resource_cleanup(resource); + wined3d_resource_wait_idle(resource); + return E_OUTOFMEMORY; + } + buffer->maps_size = 1; + + if (data) + wined3d_buffer_init_data(buffer, device, data); + + return WINED3D_OK; +} + +static BOOL wined3d_buffer_no3d_prepare_location(struct wined3d_buffer *buffer, + struct wined3d_context *context, unsigned int location) +{ + if (location == WINED3D_LOCATION_SYSMEM) + return wined3d_resource_prepare_sysmem(&buffer->resource); + + FIXME("Unhandled location %s.\n", wined3d_debug_location(location)); + + return FALSE; +} + +static void wined3d_buffer_no3d_unload_location(struct wined3d_buffer *buffer, + struct wined3d_context *context, unsigned int location) +{ + TRACE("buffer %p, context %p, location %s.\n", buffer, context, wined3d_debug_location(location)); +} + +static void wined3d_buffer_no3d_upload_ranges(struct wined3d_buffer *buffer, struct wined3d_context *context, + const void *data, unsigned int data_offset, unsigned int range_count, const struct wined3d_range *ranges) +{ + FIXME("Not implemented.\n"); +} + +static void wined3d_buffer_no3d_download_ranges(struct wined3d_buffer *buffer, struct wined3d_context *context, + void *data, unsigned int data_offset, unsigned int range_count, const struct wined3d_range *ranges) +{ + FIXME("Not implemented.\n"); +} + +static const struct wined3d_buffer_ops wined3d_buffer_no3d_ops = +{ + wined3d_buffer_no3d_prepare_location, + wined3d_buffer_no3d_unload_location, + wined3d_buffer_no3d_upload_ranges, + wined3d_buffer_no3d_download_ranges, +}; + +HRESULT wined3d_buffer_no3d_init(struct wined3d_buffer *buffer_no3d, struct wined3d_device *device, + const struct wined3d_buffer_desc *desc, const struct wined3d_sub_resource_data *data, + void *parent, const struct wined3d_parent_ops *parent_ops) +{ + TRACE("buffer_no3d %p, device %p, desc %p, data %p, parent %p, parent_ops %p.\n", + buffer_no3d, device, desc, data, parent, parent_ops); + + return wined3d_buffer_init(buffer_no3d, device, desc, data, parent, parent_ops, &wined3d_buffer_no3d_ops); +} + +static BOOL wined3d_buffer_gl_prepare_location(struct wined3d_buffer *buffer, + struct wined3d_context *context, unsigned int location) +{ + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + struct wined3d_buffer_gl *buffer_gl = wined3d_buffer_gl(buffer); + + switch (location) + { + case WINED3D_LOCATION_SYSMEM: + return wined3d_resource_prepare_sysmem(&buffer->resource); + + case WINED3D_LOCATION_BUFFER: + if (buffer->buffer_object) + return TRUE; + + if (!(buffer->flags & WINED3D_BUFFER_USE_BO)) + { + WARN("Trying to create BO for buffer %p with no WINED3D_BUFFER_USE_BO.\n", buffer); + return FALSE; + } + return wined3d_buffer_gl_create_buffer_object(buffer_gl, context_gl); + + default: + ERR("Invalid location %s.\n", wined3d_debug_location(location)); + return FALSE; + } +} + +static void wined3d_buffer_gl_unload_location(struct wined3d_buffer *buffer, + struct wined3d_context *context, unsigned int location) +{ + TRACE("buffer %p, context %p, location %s.\n", buffer, context, wined3d_debug_location(location)); + + switch (location) + { + case WINED3D_LOCATION_BUFFER: + wined3d_buffer_gl_destroy_buffer_object(wined3d_buffer_gl(buffer), wined3d_context_gl(context)); + break; + + default: + ERR("Unhandled location %s.\n", wined3d_debug_location(location)); + break; + } +} + +/* Context activation is done by the caller. */ +static void wined3d_buffer_gl_upload_ranges(struct wined3d_buffer *buffer, struct wined3d_context *context, + const void *data, unsigned int data_offset, unsigned int range_count, const struct wined3d_range *ranges) +{ + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + struct wined3d_buffer_gl *buffer_gl = wined3d_buffer_gl(buffer); + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + const struct wined3d_range *range; + + TRACE("buffer %p, context %p, data %p, data_offset %u, range_count %u, ranges %p.\n", + buffer, context, data, data_offset, range_count, ranges); + + wined3d_buffer_gl_bind(buffer_gl, context_gl); + + while (range_count--) + { + range = &ranges[range_count]; + GL_EXTCALL(glBufferSubData(buffer_gl->bo.binding, + range->offset, range->size, (BYTE *)data + range->offset - data_offset)); + } + wined3d_context_gl_reference_bo(context_gl, &buffer_gl->bo); + checkGLcall("buffer upload"); +} + +/* Context activation is done by the caller. */ +static void wined3d_buffer_gl_download_ranges(struct wined3d_buffer *buffer, struct wined3d_context *context, + void *data, unsigned int data_offset, unsigned int range_count, const struct wined3d_range *ranges) +{ + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + struct wined3d_buffer_gl *buffer_gl = wined3d_buffer_gl(buffer); + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + const struct wined3d_range *range; + + TRACE("buffer %p, context %p, data %p, data_offset %u, range_count %u, ranges %p.\n", + buffer, context, data, data_offset, range_count, ranges); + + wined3d_buffer_gl_bind(buffer_gl, context_gl); + + while (range_count--) + { + range = &ranges[range_count]; + GL_EXTCALL(glGetBufferSubData(buffer_gl->bo.binding, + range->offset, range->size, (BYTE *)data + range->offset - data_offset)); + } + checkGLcall("buffer download"); +} + +static const struct wined3d_buffer_ops wined3d_buffer_gl_ops = +{ + wined3d_buffer_gl_prepare_location, + wined3d_buffer_gl_unload_location, + wined3d_buffer_gl_upload_ranges, + wined3d_buffer_gl_download_ranges, +}; + +HRESULT wined3d_buffer_gl_init(struct wined3d_buffer_gl *buffer_gl, struct wined3d_device *device, + const struct wined3d_buffer_desc *desc, const struct wined3d_sub_resource_data *data, + void *parent, const struct wined3d_parent_ops *parent_ops) +{ + const struct wined3d_gl_info *gl_info = &device->adapter->gl_info; + + TRACE("buffer_gl %p, device %p, desc %p, data %p, parent %p, parent_ops %p.\n", + buffer_gl, device, desc, data, parent, parent_ops); + + /* Observations show that draw_primitive_immediate_mode() is faster on + * dynamic vertex buffers than converting + draw_primitive_arrays(). + * (Half-Life 2 and others.) */ + if (!(desc->access & WINED3D_RESOURCE_ACCESS_GPU)) + TRACE("Not creating a BO because the buffer is not GPU accessible.\n"); + else if (!gl_info->supported[ARB_VERTEX_BUFFER_OBJECT]) + TRACE("Not creating a BO because GL_ARB_vertex_buffer is not supported.\n"); + else if (!(gl_info->supported[APPLE_FLUSH_BUFFER_RANGE] || gl_info->supported[ARB_MAP_BUFFER_RANGE]) + && (desc->usage & WINED3DUSAGE_DYNAMIC)) + TRACE("Not creating a BO because the buffer has dynamic usage and no GL support.\n"); + else + buffer_gl->b.flags |= WINED3D_BUFFER_USE_BO; + + return wined3d_buffer_init(&buffer_gl->b, device, desc, data, parent, parent_ops, &wined3d_buffer_gl_ops); +} + +static BOOL wined3d_buffer_vk_create_buffer_object(struct wined3d_buffer_vk *buffer_vk, + struct wined3d_context_vk *context_vk) +{ + struct wined3d_resource *resource = &buffer_vk->b.resource; + uint32_t bind_flags = resource->bind_flags; + VkMemoryPropertyFlags memory_type; + VkBufferUsageFlags usage; + + usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + if (bind_flags & WINED3D_BIND_VERTEX_BUFFER) + usage |= VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; + if (bind_flags & WINED3D_BIND_INDEX_BUFFER) + usage |= VK_BUFFER_USAGE_INDEX_BUFFER_BIT; + if (bind_flags & WINED3D_BIND_CONSTANT_BUFFER) + usage |= VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; + if (bind_flags & WINED3D_BIND_SHADER_RESOURCE) + usage |= VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT; + if (bind_flags & WINED3D_BIND_STREAM_OUTPUT) + usage |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT; + if (bind_flags & WINED3D_BIND_UNORDERED_ACCESS) + usage |= VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT; + if (bind_flags & WINED3D_BIND_INDIRECT_BUFFER) + usage |= VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT; + if (bind_flags & (WINED3D_BIND_RENDER_TARGET | WINED3D_BIND_DEPTH_STENCIL)) + FIXME("Ignoring some bind flags %#x.\n", bind_flags); + + memory_type = 0; + if (!(resource->usage & WINED3DUSAGE_DYNAMIC)) + memory_type |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + if (resource->access & WINED3D_RESOURCE_ACCESS_MAP_R) + memory_type |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT; + else if (resource->access & WINED3D_RESOURCE_ACCESS_MAP_W) + memory_type |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; + + if (!(wined3d_context_vk_create_bo(context_vk, resource->size, usage, memory_type, &buffer_vk->bo))) + { + WARN("Failed to create Vulkan buffer.\n"); + return FALSE; + } + + list_init(&buffer_vk->bo_user.entry); + list_add_head(&buffer_vk->bo.users, &buffer_vk->bo_user.entry); + buffer_vk->b.buffer_object = (uintptr_t)&buffer_vk->bo; + buffer_invalidate_bo_range(&buffer_vk->b, 0, 0); + + return TRUE; +} + +const VkDescriptorBufferInfo *wined3d_buffer_vk_get_buffer_info(struct wined3d_buffer_vk *buffer_vk) +{ + if (buffer_vk->bo_user.valid) + return &buffer_vk->buffer_info; + + buffer_vk->buffer_info.buffer = buffer_vk->bo.vk_buffer; + buffer_vk->buffer_info.offset = buffer_vk->bo.buffer_offset; + buffer_vk->buffer_info.range = buffer_vk->b.resource.size; + buffer_vk->bo_user.valid = true; + + return &buffer_vk->buffer_info; +} + +static BOOL wined3d_buffer_vk_prepare_location(struct wined3d_buffer *buffer, + struct wined3d_context *context, unsigned int location) +{ + switch (location) + { + case WINED3D_LOCATION_SYSMEM: + return wined3d_resource_prepare_sysmem(&buffer->resource); + + case WINED3D_LOCATION_BUFFER: + if (buffer->buffer_object) + return TRUE; + + return wined3d_buffer_vk_create_buffer_object(wined3d_buffer_vk(buffer), wined3d_context_vk(context)); + + default: + FIXME("Unhandled location %s.\n", wined3d_debug_location(location)); + return FALSE; + } +} + +static void wined3d_buffer_vk_unload_location(struct wined3d_buffer *buffer, + struct wined3d_context *context, unsigned int location) +{ + struct wined3d_context_vk *context_vk = wined3d_context_vk(context); + struct wined3d_buffer_vk *buffer_vk = wined3d_buffer_vk(buffer); + + TRACE("buffer %p, context %p, location %s.\n", buffer, context, wined3d_debug_location(location)); + + switch (location) + { + case WINED3D_LOCATION_BUFFER: + buffer_vk->bo_user.valid = false; + list_remove(&buffer_vk->bo_user.entry); + wined3d_context_vk_destroy_bo(context_vk, &buffer_vk->bo); + buffer_vk->bo.vk_buffer = VK_NULL_HANDLE; + buffer_vk->bo.memory = NULL; + buffer_vk->b.buffer_object = 0u; + break; + + default: + ERR("Unhandled location %s.\n", wined3d_debug_location(location)); + break; + } +} + +static void wined3d_buffer_vk_upload_ranges(struct wined3d_buffer *buffer, struct wined3d_context *context, + const void *data, unsigned int data_offset, unsigned int range_count, const struct wined3d_range *ranges) +{ + struct wined3d_context_vk *context_vk = wined3d_context_vk(context); + struct wined3d_resource *resource = &buffer->resource; + struct wined3d_bo_address src, dst; + const struct wined3d_range *range; + struct wined3d_bo_vk *dst_bo; + unsigned int i = range_count; + uint32_t flags; + void *map_ptr; + + if (!range_count) + return; + + dst.buffer_object = buffer->buffer_object; + dst.addr = NULL; + + flags = WINED3D_MAP_WRITE; + if (!ranges->offset && ranges->size == resource->size) + flags |= WINED3D_MAP_DISCARD; + + dst_bo = &wined3d_buffer_vk(buffer)->bo; + if (!(dst_bo->memory_type & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) || (!(flags & WINED3D_MAP_DISCARD) + && dst_bo->command_buffer_id > context_vk->completed_command_buffer_id)) + { + src.buffer_object = 0; + while (range_count--) + { + range = &ranges[range_count]; + + src.addr = (uint8_t *)data + range->offset - data_offset; + dst.addr = (void *)(uintptr_t)range->offset; + wined3d_context_copy_bo_address(context, &dst, &src, range->size); + } + + return; + } + + if (!(map_ptr = wined3d_context_map_bo_address(context, &dst, resource->size, flags))) + { + FIXME("Failed to map buffer.\n"); + return; + } + + while (i--) + { + range = &ranges[i]; + memcpy((uint8_t *)map_ptr + range->offset, (uint8_t *)data + range->offset - data_offset, range->size); + } + + wined3d_context_unmap_bo_address(context, &dst, range_count, ranges); +} + +static void wined3d_buffer_vk_download_ranges(struct wined3d_buffer *buffer, struct wined3d_context *context, + void *data, unsigned int data_offset, unsigned int range_count, const struct wined3d_range *ranges) +{ + FIXME("Not implemented.\n"); +} + +static const struct wined3d_buffer_ops wined3d_buffer_vk_ops = +{ + wined3d_buffer_vk_prepare_location, + wined3d_buffer_vk_unload_location, + wined3d_buffer_vk_upload_ranges, + wined3d_buffer_vk_download_ranges, +}; + +HRESULT wined3d_buffer_vk_init(struct wined3d_buffer_vk *buffer_vk, struct wined3d_device *device, + const struct wined3d_buffer_desc *desc, const struct wined3d_sub_resource_data *data, + void *parent, const struct wined3d_parent_ops *parent_ops) +{ + const struct wined3d_vk_info *vk_info = &wined3d_adapter_vk(device->adapter)->vk_info; + + TRACE("buffer_vk %p, device %p, desc %p, data %p, parent %p, parent_ops %p.\n", + buffer_vk, device, desc, data, parent, parent_ops); + + if ((desc->bind_flags & WINED3D_BIND_STREAM_OUTPUT) + && !vk_info->supported[WINED3D_VK_EXT_TRANSFORM_FEEDBACK]) + { + WARN("The Vulkan implementation does not support transform feedback.\n"); + return WINED3DERR_INVALIDCALL; + } + + if (desc->access & WINED3D_RESOURCE_ACCESS_GPU) + buffer_vk->b.flags |= WINED3D_BUFFER_USE_BO; + + return wined3d_buffer_init(&buffer_vk->b, device, desc, data, parent, parent_ops, &wined3d_buffer_vk_ops); +} + +void wined3d_buffer_vk_barrier(struct wined3d_buffer_vk *buffer_vk, + struct wined3d_context_vk *context_vk, uint32_t bind_mask) +{ + TRACE("buffer_vk %p, context_vk %p, bind_mask %s.\n", + buffer_vk, context_vk, wined3d_debug_bind_flags(bind_mask)); + + if (buffer_vk->bind_mask && buffer_vk->bind_mask != bind_mask) + { + const struct wined3d_vk_info *vk_info = context_vk->vk_info; + VkBufferMemoryBarrier vk_barrier; + + TRACE(" %s -> %s.\n", + wined3d_debug_bind_flags(buffer_vk->bind_mask), wined3d_debug_bind_flags(bind_mask)); + + wined3d_context_vk_end_current_render_pass(context_vk); + + vk_barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; + vk_barrier.pNext = NULL; + vk_barrier.srcAccessMask = vk_access_mask_from_bind_flags(buffer_vk->bind_mask); + vk_barrier.dstAccessMask = vk_access_mask_from_bind_flags(bind_mask); + vk_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + vk_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + vk_barrier.buffer = buffer_vk->bo.vk_buffer; + vk_barrier.offset = buffer_vk->bo.buffer_offset; + vk_barrier.size = buffer_vk->b.resource.size; + VK_CALL(vkCmdPipelineBarrier(wined3d_context_vk_get_command_buffer(context_vk), + vk_pipeline_stage_mask_from_bind_flags(buffer_vk->bind_mask), + vk_pipeline_stage_mask_from_bind_flags(bind_mask), + 0, 0, NULL, 1, &vk_barrier, 0, NULL)); + } + buffer_vk->bind_mask = bind_mask; +} + +HRESULT CDECL wined3d_buffer_create(struct wined3d_device *device, const struct wined3d_buffer_desc *desc, + const struct wined3d_sub_resource_data *data, void *parent, const struct wined3d_parent_ops *parent_ops, + struct wined3d_buffer **buffer) +{ + TRACE("device %p, desc %p, data %p, parent %p, parent_ops %p, buffer %p.\n", + device, desc, data, parent, parent_ops, buffer); + + return device->adapter->adapter_ops->adapter_create_buffer(device, desc, data, parent, parent_ops, buffer); +} diff --git a/wrappers/directx/d3dwine_wrapper/context.c b/wrappers/directx/d3dwine_wrapper/context.c new file mode 100644 index 00000000000..118ac2aad8a --- /dev/null +++ b/wrappers/directx/d3dwine_wrapper/context.c @@ -0,0 +1,385 @@ +/* + * Context and render target management in wined3d + * + * Copyright 2002-2004 Jason Edmeades + * Copyright 2002-2004 Raphael Junqueira + * Copyright 2004 Christian Costa + * Copyright 2005 Oliver Stieber + * Copyright 2006, 2008 Henri Verbeet + * Copyright 2007-2011, 2013 Stefan Dösinger for CodeWeavers + * Copyright 2009-2011 Henri Verbeet for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "config.h" +#include "wine/port.h" + +#include "wined3d_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(d3d); +WINE_DECLARE_DEBUG_CHANNEL(d3d_perf); + +void context_resource_released(const struct wined3d_device *device, struct wined3d_resource *resource) +{ + unsigned int i; + + for (i = 0; i < device->context_count; ++i) + { + struct wined3d_context *context = device->contexts[i]; + + if (&context->current_rt.texture->resource == resource) + { + context->current_rt.texture = NULL; + context->current_rt.sub_resource_idx = 0; + } + } +} + +void wined3d_context_cleanup(struct wined3d_context *context) +{ +} + +/* This is used when a context for render target A is active, but a separate context is + * needed to access the WGL framebuffer for render target B. Re-acquire a context for rt + * A to avoid breaking caller code. */ +void context_restore(struct wined3d_context *context, struct wined3d_texture *texture, unsigned int sub_resource_idx) +{ + if (context->current_rt.texture != texture || context->current_rt.sub_resource_idx != sub_resource_idx) + { + context_release(context); + context = context_acquire(texture->resource.device, texture, sub_resource_idx); + } + + context_release(context); +} + +void context_invalidate_compute_state(struct wined3d_context *context, DWORD state_id) +{ + DWORD representative = context->state_table[state_id].representative - STATE_COMPUTE_OFFSET; + unsigned int index, shift; + + index = representative / (sizeof(*context->dirty_compute_states) * CHAR_BIT); + shift = representative & (sizeof(*context->dirty_compute_states) * CHAR_BIT - 1); + context->dirty_compute_states[index] |= (1u << shift); +} + +void context_invalidate_state(struct wined3d_context *context, unsigned int state_id) +{ + unsigned int representative = context->state_table[state_id].representative; + unsigned int index, shift; + + index = representative / (sizeof(*context->dirty_graphics_states) * CHAR_BIT); + shift = representative & ((sizeof(*context->dirty_graphics_states) * CHAR_BIT) - 1); + context->dirty_graphics_states[index] |= (1u << shift); +} + +void wined3d_context_init(struct wined3d_context *context, struct wined3d_swapchain *swapchain) +{ + struct wined3d_device *device = swapchain->device; + DWORD state; + + context->d3d_info = &device->adapter->d3d_info; + context->state_table = device->state_table; + + /* Mark all states dirty to force a proper initialization of the states on + * the first use of the context. Compute states do not need initialization. */ + for (state = 0; state <= STATE_HIGHEST; ++state) + { + if (context->state_table[state].representative && !STATE_IS_COMPUTE(state)) + context_invalidate_state(context, state); + } + + context->device = device; + context->swapchain = swapchain; + context->current_rt.texture = swapchain->front_buffer; + context->current_rt.sub_resource_idx = 0; + + context->shader_update_mask = (1u << WINED3D_SHADER_TYPE_PIXEL) + | (1u << WINED3D_SHADER_TYPE_VERTEX) + | (1u << WINED3D_SHADER_TYPE_GEOMETRY) + | (1u << WINED3D_SHADER_TYPE_HULL) + | (1u << WINED3D_SHADER_TYPE_DOMAIN) + | (1u << WINED3D_SHADER_TYPE_COMPUTE); +} + +HRESULT wined3d_context_no3d_init(struct wined3d_context *context_no3d, struct wined3d_swapchain *swapchain) +{ + TRACE("context_no3d %p, swapchain %p.\n", context_no3d, swapchain); + + wined3d_context_init(context_no3d, swapchain); + + return WINED3D_OK; +} + +static BOOL fixed_get_input(BYTE usage, BYTE usage_idx, unsigned int *regnum) +{ + if ((usage == WINED3D_DECL_USAGE_POSITION || usage == WINED3D_DECL_USAGE_POSITIONT) && !usage_idx) + *regnum = WINED3D_FFP_POSITION; + else if (usage == WINED3D_DECL_USAGE_BLEND_WEIGHT && !usage_idx) + *regnum = WINED3D_FFP_BLENDWEIGHT; + else if (usage == WINED3D_DECL_USAGE_BLEND_INDICES && !usage_idx) + *regnum = WINED3D_FFP_BLENDINDICES; + else if (usage == WINED3D_DECL_USAGE_NORMAL && !usage_idx) + *regnum = WINED3D_FFP_NORMAL; + else if (usage == WINED3D_DECL_USAGE_PSIZE && !usage_idx) + *regnum = WINED3D_FFP_PSIZE; + else if (usage == WINED3D_DECL_USAGE_COLOR && !usage_idx) + *regnum = WINED3D_FFP_DIFFUSE; + else if (usage == WINED3D_DECL_USAGE_COLOR && usage_idx == 1) + *regnum = WINED3D_FFP_SPECULAR; + else if (usage == WINED3D_DECL_USAGE_TEXCOORD && usage_idx < WINED3DDP_MAXTEXCOORD) + *regnum = WINED3D_FFP_TEXCOORD0 + usage_idx; + else + { + WARN("Unsupported input stream [usage=%s, usage_idx=%u].\n", debug_d3ddeclusage(usage), usage_idx); + *regnum = ~0u; + return FALSE; + } + + return TRUE; +} + +/* Context activation is done by the caller. */ +void wined3d_stream_info_from_declaration(struct wined3d_stream_info *stream_info, + const struct wined3d_state *state, const struct wined3d_d3d_info *d3d_info) +{ + /* We need to deal with frequency data! */ + struct wined3d_vertex_declaration *declaration = state->vertex_declaration; + BOOL generic_attributes = d3d_info->ffp_generic_attributes; + BOOL use_vshader = use_vs(state); + unsigned int i; + + stream_info->use_map = 0; + stream_info->swizzle_map = 0; + stream_info->position_transformed = 0; + + if (!declaration) + return; + + stream_info->position_transformed = declaration->position_transformed; + + /* Translate the declaration into strided data. */ + for (i = 0; i < declaration->element_count; ++i) + { + const struct wined3d_vertex_declaration_element *element = &declaration->elements[i]; + const struct wined3d_stream_state *stream = &state->streams[element->input_slot]; + BOOL stride_used; + unsigned int idx; + + TRACE("%p Element %p (%u of %u).\n", declaration->elements, + element, i + 1, declaration->element_count); + + if (!stream->buffer) + continue; + + TRACE("offset %u input_slot %u usage_idx %d.\n", element->offset, element->input_slot, element->usage_idx); + + if (use_vshader) + { + if (element->output_slot == WINED3D_OUTPUT_SLOT_UNUSED) + { + stride_used = FALSE; + } + else if (element->output_slot == WINED3D_OUTPUT_SLOT_SEMANTIC) + { + /* TODO: Assuming vertexdeclarations are usually used with the + * same or a similar shader, it might be worth it to store the + * last used output slot and try that one first. */ + stride_used = vshader_get_input(state->shader[WINED3D_SHADER_TYPE_VERTEX], + element->usage, element->usage_idx, &idx); + } + else + { + idx = element->output_slot; + stride_used = TRUE; + } + } + else + { + if (!generic_attributes && !element->ffp_valid) + { + WARN("Skipping unsupported fixed function element of format %s and usage %s.\n", + debug_d3dformat(element->format->id), debug_d3ddeclusage(element->usage)); + stride_used = FALSE; + } + else + { + stride_used = fixed_get_input(element->usage, element->usage_idx, &idx); + } + } + + if (stride_used) + { + TRACE("Load %s array %u [usage %s, usage_idx %u, " + "input_slot %u, offset %u, stride %u, format %s, class %s, step_rate %u].\n", + use_vshader ? "shader": "fixed function", idx, + debug_d3ddeclusage(element->usage), element->usage_idx, element->input_slot, + element->offset, stream->stride, debug_d3dformat(element->format->id), + debug_d3dinput_classification(element->input_slot_class), element->instance_data_step_rate); + + stream_info->elements[idx].format = element->format; + stream_info->elements[idx].data.buffer_object = 0; + stream_info->elements[idx].data.addr = (BYTE *)NULL + stream->offset + element->offset; + stream_info->elements[idx].stride = stream->stride; + stream_info->elements[idx].stream_idx = element->input_slot; + if (stream->flags & WINED3DSTREAMSOURCE_INSTANCEDATA) + { + stream_info->elements[idx].divisor = 1; + } + else if (element->input_slot_class == WINED3D_INPUT_PER_INSTANCE_DATA) + { + stream_info->elements[idx].divisor = element->instance_data_step_rate; + if (!element->instance_data_step_rate) + FIXME("Instance step rate 0 not implemented.\n"); + } + else + { + stream_info->elements[idx].divisor = 0; + } + + if (!d3d_info->vertex_bgra && element->format->id == WINED3DFMT_B8G8R8A8_UNORM) + { + stream_info->swizzle_map |= 1u << idx; + } + stream_info->use_map |= 1u << idx; + } + } +} + +/* Context activation is done by the caller. */ +void context_update_stream_info(struct wined3d_context *context, const struct wined3d_state *state) +{ + struct wined3d_stream_info *stream_info = &context->stream_info; + const struct wined3d_d3d_info *d3d_info = context->d3d_info; + DWORD prev_all_vbo = stream_info->all_vbo; + unsigned int i; + WORD map; + + wined3d_stream_info_from_declaration(stream_info, state, d3d_info); + + stream_info->all_vbo = 1; + for (i = 0, map = stream_info->use_map; map; map >>= 1, ++i) + { + struct wined3d_stream_info_element *element; + struct wined3d_bo_address data; + struct wined3d_buffer *buffer; + + if (!(map & 1)) + continue; + + element = &stream_info->elements[i]; + buffer = state->streams[element->stream_idx].buffer; + + /* We can't use VBOs if the base vertex index is negative. OpenGL + * doesn't accept negative offsets (or rather offsets bigger than the + * VBO, because the pointer is unsigned), so use system memory + * sources. In most sane cases the pointer - offset will still be > 0, + * otherwise it will wrap around to some big value. Hope that with the + * indices the driver wraps it back internally. If not, + * draw_primitive_immediate_mode() is needed, including a vertex buffer + * path. */ + if (state->load_base_vertex_index < 0) + { + WARN_(d3d_perf)("load_base_vertex_index is < 0 (%d), not using VBOs.\n", + state->load_base_vertex_index); + element->data.buffer_object = 0; + element->data.addr += (ULONG_PTR)wined3d_buffer_load_sysmem(buffer, context); + if ((UINT_PTR)element->data.addr < -state->load_base_vertex_index * element->stride) + FIXME("System memory vertex data load offset is negative!\n"); + } + else + { + wined3d_buffer_load(buffer, context, state); + wined3d_buffer_get_memory(buffer, &data, buffer->locations); + element->data.buffer_object = data.buffer_object; + element->data.addr += (ULONG_PTR)data.addr; + } + + if (!element->data.buffer_object) + stream_info->all_vbo = 0; + + TRACE("Load array %u %s.\n", i, debug_bo_address(&element->data)); + } + + if (prev_all_vbo != stream_info->all_vbo) + context_invalidate_state(context, STATE_INDEXBUFFER); + + context->use_immediate_mode_draw = FALSE; + + if (stream_info->all_vbo) + return; + + if (!use_vs(state)) + { + WORD slow_mask = -!d3d_info->ffp_generic_attributes & (1u << WINED3D_FFP_PSIZE); + slow_mask |= -(!d3d_info->vertex_bgra && !d3d_info->ffp_generic_attributes) + & ((1u << WINED3D_FFP_DIFFUSE) | (1u << WINED3D_FFP_SPECULAR) | (1u << WINED3D_FFP_BLENDWEIGHT)); + + if ((stream_info->position_transformed && !d3d_info->xyzrhw) + || (stream_info->use_map & slow_mask)) + context->use_immediate_mode_draw = TRUE; + } +} + +/* Context activation is done by the caller. */ +static void context_preload_texture(struct wined3d_context *context, + const struct wined3d_state *state, unsigned int idx) +{ + struct wined3d_texture *texture; + + if (!(texture = state->textures[idx])) + return; + + if ((texture->resource.rtv_full_bind_count_device + texture->resource.rtv_partial_bind_count_device) + || (state->fb.depth_stencil && state->fb.depth_stencil->resource == &texture->resource)) + context->uses_fbo_attached_resources = 1; + + wined3d_texture_load(texture, context, is_srgb_enabled(state->sampler_states[idx])); +} + +/* Context activation is done by the caller. */ +void context_preload_textures(struct wined3d_context *context, const struct wined3d_state *state) +{ + unsigned int i; + + if (use_vs(state)) + { + for (i = 0; i < WINED3D_MAX_VERTEX_SAMPLERS; ++i) + { + if (state->shader[WINED3D_SHADER_TYPE_VERTEX]->reg_maps.resource_info[i].type) + context_preload_texture(context, state, WINED3D_MAX_FRAGMENT_SAMPLERS + i); + } + } + + if (use_ps(state)) + { + for (i = 0; i < WINED3D_MAX_FRAGMENT_SAMPLERS; ++i) + { + if (state->shader[WINED3D_SHADER_TYPE_PIXEL]->reg_maps.resource_info[i].type) + context_preload_texture(context, state, i); + } + } + else + { + WORD ffu_map = context->fixed_function_usage_map; + + for (i = 0; ffu_map; ffu_map >>= 1, ++i) + { + if (ffu_map & 1) + context_preload_texture(context, state, i); + } + } +} diff --git a/wrappers/directx/d3dwine_wrapper/context_gl.c b/wrappers/directx/d3dwine_wrapper/context_gl.c new file mode 100644 index 00000000000..a51e5ec9095 --- /dev/null +++ b/wrappers/directx/d3dwine_wrapper/context_gl.c @@ -0,0 +1,5709 @@ +/* + * Context and render target management in wined3d + * + * Copyright 2002-2004 Jason Edmeades + * Copyright 2002-2004 Raphael Junqueira + * Copyright 2004 Christian Costa + * Copyright 2005 Oliver Stieber + * Copyright 2006, 2008 Henri Verbeet + * Copyright 2007-2011, 2013 Stefan Dösinger for CodeWeavers + * Copyright 2009-2011 Henri Verbeet for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "config.h" +#include "wine/port.h" + +#include "wined3d_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(d3d); +WINE_DECLARE_DEBUG_CHANNEL(d3d_perf); +WINE_DECLARE_DEBUG_CHANNEL(d3d_sync); + +#define WINED3D_MAX_FBO_ENTRIES 64 +#define WINED3D_ALL_LAYERS (~0u) + +static DWORD wined3d_context_tls_idx; + +/* Note that except for WINED3DPT_POINTLIST and WINED3DPT_LINELIST these + * actually have the same values in GL and D3D. */ +static GLenum gl_primitive_type_from_d3d(enum wined3d_primitive_type primitive_type) +{ + switch (primitive_type) + { + case WINED3D_PT_POINTLIST: + return GL_POINTS; + + case WINED3D_PT_LINELIST: + return GL_LINES; + + case WINED3D_PT_LINESTRIP: + return GL_LINE_STRIP; + + case WINED3D_PT_TRIANGLELIST: + return GL_TRIANGLES; + + case WINED3D_PT_TRIANGLESTRIP: + return GL_TRIANGLE_STRIP; + + case WINED3D_PT_TRIANGLEFAN: + return GL_TRIANGLE_FAN; + + case WINED3D_PT_LINELIST_ADJ: + return GL_LINES_ADJACENCY_ARB; + + case WINED3D_PT_LINESTRIP_ADJ: + return GL_LINE_STRIP_ADJACENCY_ARB; + + case WINED3D_PT_TRIANGLELIST_ADJ: + return GL_TRIANGLES_ADJACENCY_ARB; + + case WINED3D_PT_TRIANGLESTRIP_ADJ: + return GL_TRIANGLE_STRIP_ADJACENCY_ARB; + + case WINED3D_PT_PATCH: + return GL_PATCHES; + + default: + FIXME("Unhandled primitive type %s.\n", debug_d3dprimitivetype(primitive_type)); + case WINED3D_PT_UNDEFINED: + return ~0u; + } +} + +/* FBO helper functions */ + +/* Context activation is done by the caller. */ +static void wined3d_context_gl_bind_fbo(struct wined3d_context_gl *context_gl, GLenum target, GLuint fbo) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + + TRACE("context_gl %p, target %#x, fbo %u.\n", context_gl, target, fbo); + + switch (target) + { + case GL_READ_FRAMEBUFFER: + if (context_gl->fbo_read_binding == fbo) + return; + context_gl->fbo_read_binding = fbo; + break; + + case GL_DRAW_FRAMEBUFFER: + if (context_gl->fbo_draw_binding == fbo) + return; + context_gl->fbo_draw_binding = fbo; + break; + + case GL_FRAMEBUFFER: + if (context_gl->fbo_read_binding == fbo + && context_gl->fbo_draw_binding == fbo) + return; + context_gl->fbo_read_binding = fbo; + context_gl->fbo_draw_binding = fbo; + break; + + default: + FIXME("Unhandled target %#x.\n", target); + break; + } + + gl_info->fbo_ops.glBindFramebuffer(target, fbo); + checkGLcall("glBindFramebuffer()"); +} + +/* Context activation is done by the caller. */ +static void context_clean_fbo_attachments(const struct wined3d_gl_info *gl_info, GLenum target) +{ + unsigned int i; + + for (i = 0; i < gl_info->limits.buffers; ++i) + { + gl_info->fbo_ops.glFramebufferTexture2D(target, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, 0, 0); + checkGLcall("glFramebufferTexture2D()"); + } + gl_info->fbo_ops.glFramebufferTexture2D(target, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, 0, 0); + checkGLcall("glFramebufferTexture2D()"); + + gl_info->fbo_ops.glFramebufferTexture2D(target, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); + checkGLcall("glFramebufferTexture2D()"); +} + +/* Context activation is done by the caller. */ +static void wined3d_context_gl_destroy_fbo(struct wined3d_context_gl *context_gl, GLuint fbo) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + + wined3d_context_gl_bind_fbo(context_gl, GL_FRAMEBUFFER, fbo); + context_clean_fbo_attachments(gl_info, GL_FRAMEBUFFER); + wined3d_context_gl_bind_fbo(context_gl, GL_FRAMEBUFFER, 0); + + gl_info->fbo_ops.glDeleteFramebuffers(1, &fbo); + checkGLcall("glDeleteFramebuffers()"); +} + +static void context_attach_depth_stencil_rb(const struct wined3d_gl_info *gl_info, + GLenum fbo_target, DWORD flags, GLuint rb) +{ + if (flags & WINED3D_FBO_ENTRY_FLAG_DEPTH) + { + gl_info->fbo_ops.glFramebufferRenderbuffer(fbo_target, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rb); + checkGLcall("glFramebufferRenderbuffer()"); + } + + if (flags & WINED3D_FBO_ENTRY_FLAG_STENCIL) + { + gl_info->fbo_ops.glFramebufferRenderbuffer(fbo_target, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rb); + checkGLcall("glFramebufferRenderbuffer()"); + } +} + +static void wined3d_context_gl_attach_gl_texture_fbo(struct wined3d_context_gl *context_gl, + GLenum fbo_target, GLenum attachment, const struct wined3d_fbo_resource *resource) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + + if (!resource) + { + gl_info->fbo_ops.glFramebufferTexture2D(fbo_target, attachment, GL_TEXTURE_2D, 0, 0); + } + else if (resource->layer == WINED3D_ALL_LAYERS) + { + if (!gl_info->fbo_ops.glFramebufferTexture) + { + FIXME("OpenGL implementation doesn't support glFramebufferTexture().\n"); + return; + } + + gl_info->fbo_ops.glFramebufferTexture(fbo_target, attachment, + resource->object, resource->level); + } + else if (resource->target == GL_TEXTURE_1D_ARRAY || resource->target == GL_TEXTURE_2D_ARRAY + || resource->target == GL_TEXTURE_3D) + { + if (!gl_info->fbo_ops.glFramebufferTextureLayer) + { + FIXME("OpenGL implementation doesn't support glFramebufferTextureLayer().\n"); + return; + } + + gl_info->fbo_ops.glFramebufferTextureLayer(fbo_target, attachment, + resource->object, resource->level, resource->layer); + } + else if (resource->target == GL_TEXTURE_1D) + { + gl_info->fbo_ops.glFramebufferTexture1D(fbo_target, attachment, + resource->target, resource->object, resource->level); + } + else + { + gl_info->fbo_ops.glFramebufferTexture2D(fbo_target, attachment, + resource->target, resource->object, resource->level); + } + checkGLcall("attach texture to fbo"); +} + +/* Context activation is done by the caller. */ +static void wined3d_context_gl_attach_depth_stencil_fbo(struct wined3d_context_gl *context_gl, + GLenum fbo_target, const struct wined3d_fbo_resource *resource, BOOL rb_namespace, + uint32_t flags) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + + if (resource->object) + { + TRACE("Attach depth stencil %u.\n", resource->object); + + if (rb_namespace) + { + context_attach_depth_stencil_rb(gl_info, fbo_target, + flags, resource->object); + } + else + { + if (flags & WINED3D_FBO_ENTRY_FLAG_DEPTH) + wined3d_context_gl_attach_gl_texture_fbo(context_gl, fbo_target, GL_DEPTH_ATTACHMENT, resource); + + if (flags & WINED3D_FBO_ENTRY_FLAG_STENCIL) + wined3d_context_gl_attach_gl_texture_fbo(context_gl, fbo_target, GL_STENCIL_ATTACHMENT, resource); + } + + if (!(flags & WINED3D_FBO_ENTRY_FLAG_DEPTH)) + wined3d_context_gl_attach_gl_texture_fbo(context_gl, fbo_target, GL_DEPTH_ATTACHMENT, NULL); + + if (!(flags & WINED3D_FBO_ENTRY_FLAG_STENCIL)) + wined3d_context_gl_attach_gl_texture_fbo(context_gl, fbo_target, GL_STENCIL_ATTACHMENT, NULL); + } + else + { + TRACE("Attach depth stencil 0.\n"); + + wined3d_context_gl_attach_gl_texture_fbo(context_gl, fbo_target, GL_DEPTH_ATTACHMENT, NULL); + wined3d_context_gl_attach_gl_texture_fbo(context_gl, fbo_target, GL_STENCIL_ATTACHMENT, NULL); + } +} + +/* Context activation is done by the caller. */ +static void wined3d_context_gl_attach_surface_fbo(struct wined3d_context_gl *context_gl, + GLenum fbo_target, unsigned int idx, const struct wined3d_fbo_resource *resource, BOOL rb_namespace) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + + TRACE("Attach GL object %u to %u.\n", resource->object, idx); + + if (resource->object) + { + if (rb_namespace) + { + gl_info->fbo_ops.glFramebufferRenderbuffer(fbo_target, GL_COLOR_ATTACHMENT0 + idx, + GL_RENDERBUFFER, resource->object); + checkGLcall("glFramebufferRenderbuffer()"); + } + else + { + wined3d_context_gl_attach_gl_texture_fbo(context_gl, fbo_target, GL_COLOR_ATTACHMENT0 + idx, resource); + } + } + else + { + wined3d_context_gl_attach_gl_texture_fbo(context_gl, fbo_target, GL_COLOR_ATTACHMENT0 + idx, NULL); + } +} + +static void context_dump_fbo_attachment(const struct wined3d_gl_info *gl_info, GLenum target, + GLenum attachment) +{ + static const struct + { + GLenum target; + GLenum binding; + const char *str; + enum wined3d_gl_extension extension; + } + texture_type[] = + { + {GL_TEXTURE_1D, GL_TEXTURE_BINDING_1D, "1d", WINED3D_GL_EXT_NONE}, + {GL_TEXTURE_1D_ARRAY, GL_TEXTURE_BINDING_1D_ARRAY, "1d-array", EXT_TEXTURE_ARRAY}, + {GL_TEXTURE_2D, GL_TEXTURE_BINDING_2D, "2d", WINED3D_GL_EXT_NONE}, + {GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_BINDING_RECTANGLE_ARB, "rectangle", ARB_TEXTURE_RECTANGLE}, + {GL_TEXTURE_2D_ARRAY, GL_TEXTURE_BINDING_2D_ARRAY, "2d-array" , EXT_TEXTURE_ARRAY}, + {GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BINDING_CUBE_MAP, "cube", ARB_TEXTURE_CUBE_MAP}, + {GL_TEXTURE_2D_MULTISAMPLE, GL_TEXTURE_BINDING_2D_MULTISAMPLE, "2d-ms", ARB_TEXTURE_MULTISAMPLE}, + {GL_TEXTURE_2D_MULTISAMPLE_ARRAY, GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY, "2d-array-ms", ARB_TEXTURE_MULTISAMPLE}, + }; + + GLint type, name, samples, width, height, old_texture, level, face, fmt, tex_target; + const char *tex_type_str = NULL; + unsigned int i; + + gl_info->fbo_ops.glGetFramebufferAttachmentParameteriv(target, attachment, + GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &name); + gl_info->fbo_ops.glGetFramebufferAttachmentParameteriv(target, attachment, + GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &type); + + if (type == GL_RENDERBUFFER) + { + gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, name); + gl_info->fbo_ops.glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &width); + gl_info->fbo_ops.glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &height); + if (gl_info->limits.samples > 1) + gl_info->fbo_ops.glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples); + else + samples = 1; + gl_info->fbo_ops.glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_INTERNAL_FORMAT, &fmt); + FIXME(" %s: renderbuffer %d, %dx%d, %d samples, format %#x.\n", + debug_fboattachment(attachment), name, width, height, samples, fmt); + } + else if (type == GL_TEXTURE) + { + gl_info->fbo_ops.glGetFramebufferAttachmentParameteriv(target, attachment, + GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL, &level); + gl_info->fbo_ops.glGetFramebufferAttachmentParameteriv(target, attachment, + GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE, &face); + + if (gl_info->gl_ops.ext.p_glGetTextureParameteriv) + { + GL_EXTCALL(glGetTextureParameteriv(name, GL_TEXTURE_TARGET, &tex_target)); + + for (i = 0; i < ARRAY_SIZE(texture_type); ++i) + { + if (texture_type[i].target == tex_target) + { + tex_type_str = texture_type[i].str; + break; + } + } + if (i == ARRAY_SIZE(texture_type)) + tex_type_str = wine_dbg_sprintf("%#x", tex_target); + } + else if (face) + { + gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_CUBE_MAP, &old_texture); + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_CUBE_MAP, name); + + tex_target = GL_TEXTURE_CUBE_MAP; + tex_type_str = "cube"; + } + else + { + for (i = 0; i < ARRAY_SIZE(texture_type); ++i) + { + if (!gl_info->supported[texture_type[i].extension]) + continue; + + gl_info->gl_ops.gl.p_glGetIntegerv(texture_type[i].binding, &old_texture); + while (gl_info->gl_ops.gl.p_glGetError()); + + gl_info->gl_ops.gl.p_glBindTexture(texture_type[i].target, name); + if (!gl_info->gl_ops.gl.p_glGetError()) + { + tex_target = texture_type[i].target; + tex_type_str = texture_type[i].str; + break; + } + gl_info->gl_ops.gl.p_glBindTexture(texture_type[i].target, old_texture); + } + + if (!tex_type_str) + { + FIXME("Cannot find type of texture %d.\n", name); + return; + } + } + + if (gl_info->gl_ops.ext.p_glGetTextureParameteriv) + { + GL_EXTCALL(glGetTextureLevelParameteriv(name, level, GL_TEXTURE_INTERNAL_FORMAT, &fmt)); + GL_EXTCALL(glGetTextureLevelParameteriv(name, level, GL_TEXTURE_WIDTH, &width)); + GL_EXTCALL(glGetTextureLevelParameteriv(name, level, GL_TEXTURE_HEIGHT, &height)); + GL_EXTCALL(glGetTextureLevelParameteriv(name, level, GL_TEXTURE_SAMPLES, &samples)); + } + else + { + gl_info->gl_ops.gl.p_glGetTexLevelParameteriv(tex_target, level, GL_TEXTURE_INTERNAL_FORMAT, &fmt); + gl_info->gl_ops.gl.p_glGetTexLevelParameteriv(tex_target, level, GL_TEXTURE_WIDTH, &width); + gl_info->gl_ops.gl.p_glGetTexLevelParameteriv(tex_target, level, GL_TEXTURE_HEIGHT, &height); + if (gl_info->supported[ARB_TEXTURE_MULTISAMPLE]) + gl_info->gl_ops.gl.p_glGetTexLevelParameteriv(tex_target, level, GL_TEXTURE_SAMPLES, &samples); + else + samples = 1; + + gl_info->gl_ops.gl.p_glBindTexture(tex_target, old_texture); + } + + FIXME(" %s: %s texture %d, %dx%d, %d samples, format %#x.\n", + debug_fboattachment(attachment), tex_type_str, name, width, height, samples, fmt); + } + else if (type == GL_NONE) + { + FIXME(" %s: NONE.\n", debug_fboattachment(attachment)); + } + else + { + ERR(" %s: Unknown attachment %#x.\n", debug_fboattachment(attachment), type); + } + + checkGLcall("dump FBO attachment"); +} + +/* Context activation is done by the caller. */ +void wined3d_context_gl_check_fbo_status(const struct wined3d_context_gl *context_gl, GLenum target) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + GLenum status; + + if (!FIXME_ON(d3d)) + return; + + status = gl_info->fbo_ops.glCheckFramebufferStatus(target); + if (status == GL_FRAMEBUFFER_COMPLETE) + { + TRACE("FBO complete.\n"); + } + else + { + unsigned int i; + + FIXME("FBO status %s (%#x).\n", debug_fbostatus(status), status); + + if (!context_gl->current_fbo) + { + ERR("FBO 0 is incomplete, driver bug?\n"); + return; + } + + context_dump_fbo_attachment(gl_info, target, GL_DEPTH_ATTACHMENT); + context_dump_fbo_attachment(gl_info, target, GL_STENCIL_ATTACHMENT); + + for (i = 0; i < gl_info->limits.buffers; ++i) + context_dump_fbo_attachment(gl_info, target, GL_COLOR_ATTACHMENT0 + i); + } +} + +static inline DWORD context_generate_rt_mask(GLenum buffer) +{ + /* Should take care of all the GL_FRONT/GL_BACK/GL_AUXi/GL_NONE... cases */ + return buffer ? (1u << 31) | buffer : 0; +} + +static inline DWORD context_generate_rt_mask_from_resource(struct wined3d_resource *resource) +{ + if (resource->type != WINED3D_RTYPE_TEXTURE_2D) + { + FIXME("Not implemented for %s resources.\n", debug_d3dresourcetype(resource->type)); + return 0; + } + + return (1u << 31) | wined3d_texture_get_gl_buffer(texture_from_resource(resource)); +} + +static inline void wined3d_context_gl_set_fbo_key_for_render_target(const struct wined3d_context_gl *context_gl, + struct wined3d_fbo_entry_key *key, unsigned int idx, const struct wined3d_rendertarget_info *render_target, + DWORD location) +{ + unsigned int sub_resource_idx = render_target->sub_resource_idx; + struct wined3d_resource *resource = render_target->resource; + struct wined3d_texture_gl *texture_gl; + + if (!resource || resource->format->id == WINED3DFMT_NULL || resource->type == WINED3D_RTYPE_BUFFER) + { + if (resource && resource->type == WINED3D_RTYPE_BUFFER) + FIXME("Not implemented for %s resources.\n", debug_d3dresourcetype(resource->type)); + key->objects[idx].object = 0; + key->objects[idx].target = 0; + key->objects[idx].level = key->objects[idx].layer = 0; + return; + } + + if (render_target->gl_view.name) + { + key->objects[idx].object = render_target->gl_view.name; + key->objects[idx].target = render_target->gl_view.target; + key->objects[idx].level = 0; + key->objects[idx].layer = WINED3D_ALL_LAYERS; + return; + } + + texture_gl = wined3d_texture_gl(wined3d_texture_from_resource(resource)); + if (texture_gl->current_renderbuffer) + { + key->objects[idx].object = texture_gl->current_renderbuffer->id; + key->objects[idx].target = 0; + key->objects[idx].level = key->objects[idx].layer = 0; + key->rb_namespace |= 1 << idx; + return; + } + + key->objects[idx].target = wined3d_texture_gl_get_sub_resource_target(texture_gl, sub_resource_idx); + key->objects[idx].level = sub_resource_idx % texture_gl->t.level_count; + key->objects[idx].layer = sub_resource_idx / texture_gl->t.level_count; + + if (render_target->layer_count != 1) + key->objects[idx].layer = WINED3D_ALL_LAYERS; + + switch (location) + { + case WINED3D_LOCATION_TEXTURE_RGB: + key->objects[idx].object = wined3d_texture_gl_get_texture_name(texture_gl, &context_gl->c, FALSE); + break; + + case WINED3D_LOCATION_TEXTURE_SRGB: + key->objects[idx].object = wined3d_texture_gl_get_texture_name(texture_gl, &context_gl->c, TRUE); + break; + + case WINED3D_LOCATION_RB_MULTISAMPLE: + key->objects[idx].object = texture_gl->rb_multisample; + key->objects[idx].target = 0; + key->objects[idx].level = key->objects[idx].layer = 0; + key->rb_namespace |= 1 << idx; + break; + + case WINED3D_LOCATION_RB_RESOLVED: + key->objects[idx].object = texture_gl->rb_resolved; + key->objects[idx].target = 0; + key->objects[idx].level = key->objects[idx].layer = 0; + key->rb_namespace |= 1 << idx; + break; + } +} + +static void wined3d_context_gl_generate_fbo_key(const struct wined3d_context_gl *context_gl, + struct wined3d_fbo_entry_key *key, const struct wined3d_rendertarget_info *render_targets, + const struct wined3d_rendertarget_info *depth_stencil, DWORD color_location, DWORD ds_location) +{ + unsigned int buffers = context_gl->gl_info->limits.buffers; + unsigned int i; + + key->rb_namespace = 0; + wined3d_context_gl_set_fbo_key_for_render_target(context_gl, key, 0, depth_stencil, ds_location); + + for (i = 0; i < buffers; ++i) + wined3d_context_gl_set_fbo_key_for_render_target(context_gl, key, i + 1, &render_targets[i], color_location); + + memset(&key->objects[buffers + 1], 0, (ARRAY_SIZE(key->objects) - buffers - 1) * sizeof(*key->objects)); +} + +static struct fbo_entry *wined3d_context_gl_create_fbo_entry(const struct wined3d_context_gl *context_gl, + const struct wined3d_rendertarget_info *render_targets, const struct wined3d_rendertarget_info *depth_stencil, + DWORD color_location, DWORD ds_location) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + struct fbo_entry *entry; + + entry = heap_alloc(sizeof(*entry)); + wined3d_context_gl_generate_fbo_key(context_gl, &entry->key, + render_targets, depth_stencil, color_location, ds_location); + entry->flags = 0; + if (depth_stencil->resource) + { + if (depth_stencil->resource->format->depth_size) + entry->flags |= WINED3D_FBO_ENTRY_FLAG_DEPTH; + if (depth_stencil->resource->format->stencil_size) + entry->flags |= WINED3D_FBO_ENTRY_FLAG_STENCIL; + } + entry->rt_mask = context_generate_rt_mask(GL_COLOR_ATTACHMENT0); + gl_info->fbo_ops.glGenFramebuffers(1, &entry->id); + checkGLcall("glGenFramebuffers()"); + TRACE("Created FBO %u.\n", entry->id); + + return entry; +} + +/* Context activation is done by the caller. */ +static void wined3d_context_gl_reuse_fbo_entry(struct wined3d_context_gl *context_gl, GLenum target, + const struct wined3d_rendertarget_info *render_targets, const struct wined3d_rendertarget_info *depth_stencil, + DWORD color_location, DWORD ds_location, struct fbo_entry *entry) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + + wined3d_context_gl_bind_fbo(context_gl, target, entry->id); + context_clean_fbo_attachments(gl_info, target); + + wined3d_context_gl_generate_fbo_key(context_gl, &entry->key, + render_targets, depth_stencil, color_location, ds_location); + entry->flags = 0; + if (depth_stencil->resource) + { + if (depth_stencil->resource->format->depth_size) + entry->flags |= WINED3D_FBO_ENTRY_FLAG_DEPTH; + if (depth_stencil->resource->format->stencil_size) + entry->flags |= WINED3D_FBO_ENTRY_FLAG_STENCIL; + } +} + +/* Context activation is done by the caller. */ +static void wined3d_context_gl_destroy_fbo_entry(struct wined3d_context_gl *context_gl, struct fbo_entry *entry) +{ + if (entry->id) + { + TRACE("Destroy FBO %u.\n", entry->id); + wined3d_context_gl_destroy_fbo(context_gl, entry->id); + } + --context_gl->fbo_entry_count; + list_remove(&entry->entry); + heap_free(entry); +} + +/* Context activation is done by the caller. */ +static struct fbo_entry *wined3d_context_gl_find_fbo_entry(struct wined3d_context_gl *context_gl, GLenum target, + const struct wined3d_rendertarget_info *render_targets, const struct wined3d_rendertarget_info *depth_stencil, + DWORD color_location, DWORD ds_location) +{ + static const struct wined3d_rendertarget_info ds_null = {{0}}; + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + struct wined3d_texture *rt_texture, *ds_texture; + struct wined3d_fbo_entry_key fbo_key; + unsigned int i, ds_level, rt_level; + struct fbo_entry *entry; + + if (depth_stencil->resource && depth_stencil->resource->type != WINED3D_RTYPE_BUFFER + && render_targets[0].resource && render_targets[0].resource->type != WINED3D_RTYPE_BUFFER + && render_targets[0].resource->format->id != WINED3DFMT_NULL) + { + rt_texture = wined3d_texture_from_resource(render_targets[0].resource); + rt_level = render_targets[0].sub_resource_idx % rt_texture->level_count; + ds_texture = wined3d_texture_from_resource(depth_stencil->resource); + ds_level = depth_stencil->sub_resource_idx % ds_texture->level_count; + + if (wined3d_texture_get_level_width(ds_texture, ds_level) + < wined3d_texture_get_level_width(rt_texture, rt_level) + || wined3d_texture_get_level_height(ds_texture, ds_level) + < wined3d_texture_get_level_height(rt_texture, rt_level)) + { + WARN("Depth stencil is smaller than the primary color buffer, disabling.\n"); + depth_stencil = &ds_null; + } + else if (ds_texture->resource.multisample_type != rt_texture->resource.multisample_type + || (ds_texture->resource.multisample_type + && ds_texture->resource.multisample_quality != rt_texture->resource.multisample_quality)) + { + WARN("Color multisample type %u and quality %u, depth stencil has %u and %u, disabling ds buffer.\n", + rt_texture->resource.multisample_type, rt_texture->resource.multisample_quality, + ds_texture->resource.multisample_type, ds_texture->resource.multisample_quality); + depth_stencil = &ds_null; + } + else if (depth_stencil->resource->type == WINED3D_RTYPE_TEXTURE_2D) + { + wined3d_texture_gl_set_compatible_renderbuffer(wined3d_texture_gl(ds_texture), + context_gl, ds_level, &render_targets[0]); + } + } + + wined3d_context_gl_generate_fbo_key(context_gl, &fbo_key, + render_targets, depth_stencil, color_location, ds_location); + + if (TRACE_ON(d3d)) + { + struct wined3d_resource *resource; + unsigned int width, height; + const char *resource_type; + + TRACE("Dumping FBO attachments:\n"); + for (i = 0; i < gl_info->limits.buffers; ++i) + { + if ((resource = render_targets[i].resource)) + { + if (resource->type == WINED3D_RTYPE_BUFFER) + { + width = resource->size; + height = 1; + resource_type = "buffer"; + } + else + { + rt_texture = wined3d_texture_from_resource(resource); + rt_level = render_targets[i].sub_resource_idx % rt_texture->level_count; + width = wined3d_texture_get_level_pow2_width(rt_texture, rt_level); + height = wined3d_texture_get_level_pow2_height(rt_texture, rt_level); + resource_type = "texture"; + } + + TRACE(" Color attachment %u: %p, %u format %s, %s %u, %ux%u, %u samples.\n", + i, resource, render_targets[i].sub_resource_idx, debug_d3dformat(resource->format->id), + fbo_key.rb_namespace & (1 << (i + 1)) ? "renderbuffer" : resource_type, + fbo_key.objects[i + 1].object, width, height, resource->multisample_type); + } + } + if ((resource = depth_stencil->resource)) + { + if (resource->type == WINED3D_RTYPE_BUFFER) + { + width = resource->size; + height = 1; + resource_type = "buffer"; + } + else + { + ds_texture = wined3d_texture_from_resource(resource); + ds_level = depth_stencil->sub_resource_idx % ds_texture->level_count; + width = wined3d_texture_get_level_pow2_width(ds_texture, ds_level); + height = wined3d_texture_get_level_pow2_height(ds_texture, ds_level); + resource_type = "texture"; + } + + TRACE(" Depth attachment: %p, %u format %s, %s %u, %ux%u, %u samples.\n", + resource, depth_stencil->sub_resource_idx, debug_d3dformat(resource->format->id), + fbo_key.rb_namespace & (1 << 0) ? "renderbuffer" : resource_type, + fbo_key.objects[0].object, width, height, resource->multisample_type); + } + } + + LIST_FOR_EACH_ENTRY(entry, &context_gl->fbo_list, struct fbo_entry, entry) + { + if (memcmp(&fbo_key, &entry->key, sizeof(fbo_key))) + continue; + + list_remove(&entry->entry); + list_add_head(&context_gl->fbo_list, &entry->entry); + return entry; + } + + if (context_gl->fbo_entry_count < WINED3D_MAX_FBO_ENTRIES) + { + entry = wined3d_context_gl_create_fbo_entry(context_gl, + render_targets, depth_stencil, color_location, ds_location); + list_add_head(&context_gl->fbo_list, &entry->entry); + ++context_gl->fbo_entry_count; + } + else + { + entry = LIST_ENTRY(list_tail(&context_gl->fbo_list), struct fbo_entry, entry); + wined3d_context_gl_reuse_fbo_entry(context_gl, target, render_targets, + depth_stencil, color_location, ds_location, entry); + list_remove(&entry->entry); + list_add_head(&context_gl->fbo_list, &entry->entry); + } + + return entry; +} + +/* Context activation is done by the caller. */ +static void wined3d_context_gl_apply_fbo_entry(struct wined3d_context_gl *context_gl, + GLenum target, struct fbo_entry *entry) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + GLuint read_binding, draw_binding; + unsigned int i; + + if (entry->flags & WINED3D_FBO_ENTRY_FLAG_ATTACHED) + { + wined3d_context_gl_bind_fbo(context_gl, target, entry->id); + return; + } + + read_binding = context_gl->fbo_read_binding; + draw_binding = context_gl->fbo_draw_binding; + wined3d_context_gl_bind_fbo(context_gl, GL_FRAMEBUFFER, entry->id); + + if (gl_info->supported[ARB_FRAMEBUFFER_NO_ATTACHMENTS]) + { + GL_EXTCALL(glFramebufferParameteri(GL_FRAMEBUFFER, + GL_FRAMEBUFFER_DEFAULT_WIDTH, gl_info->limits.framebuffer_width)); + GL_EXTCALL(glFramebufferParameteri(GL_FRAMEBUFFER, + GL_FRAMEBUFFER_DEFAULT_HEIGHT, gl_info->limits.framebuffer_height)); + GL_EXTCALL(glFramebufferParameteri(GL_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_LAYERS, 1)); + GL_EXTCALL(glFramebufferParameteri(GL_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_SAMPLES, 1)); + } + + /* Apply render targets */ + for (i = 0; i < gl_info->limits.buffers; ++i) + { + wined3d_context_gl_attach_surface_fbo(context_gl, target, i, + &entry->key.objects[i + 1], entry->key.rb_namespace & (1 << (i + 1))); + } + + wined3d_context_gl_attach_depth_stencil_fbo(context_gl, target, + &entry->key.objects[0], entry->key.rb_namespace & 0x1, entry->flags); + + /* Set valid read and draw buffer bindings to satisfy pedantic pre-ES2_compatibility + * GL contexts requirements. */ + gl_info->gl_ops.gl.p_glReadBuffer(GL_NONE); + wined3d_context_gl_set_draw_buffer(context_gl, GL_NONE); + if (target != GL_FRAMEBUFFER) + { + if (target == GL_READ_FRAMEBUFFER) + wined3d_context_gl_bind_fbo(context_gl, GL_DRAW_FRAMEBUFFER, draw_binding); + else + wined3d_context_gl_bind_fbo(context_gl, GL_READ_FRAMEBUFFER, read_binding); + } + + entry->flags |= WINED3D_FBO_ENTRY_FLAG_ATTACHED; +} + +/* Context activation is done by the caller. */ +static void wined3d_context_gl_apply_fbo_state(struct wined3d_context_gl *context_gl, GLenum target, + const struct wined3d_rendertarget_info *render_targets, + const struct wined3d_rendertarget_info *depth_stencil, DWORD color_location, DWORD ds_location) +{ + struct fbo_entry *entry, *entry2; + + LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &context_gl->fbo_destroy_list, struct fbo_entry, entry) + { + wined3d_context_gl_destroy_fbo_entry(context_gl, entry); + } + + if (context_gl->rebind_fbo) + { + wined3d_context_gl_bind_fbo(context_gl, GL_FRAMEBUFFER, 0); + context_gl->rebind_fbo = FALSE; + } + + if (color_location == WINED3D_LOCATION_DRAWABLE) + { + context_gl->current_fbo = NULL; + wined3d_context_gl_bind_fbo(context_gl, target, 0); + } + else + { + context_gl->current_fbo = wined3d_context_gl_find_fbo_entry(context_gl, + target, render_targets, depth_stencil, color_location, ds_location); + wined3d_context_gl_apply_fbo_entry(context_gl, target, context_gl->current_fbo); + } +} + +/* Context activation is done by the caller. */ +void wined3d_context_gl_apply_fbo_state_blit(struct wined3d_context_gl *context_gl, GLenum target, + struct wined3d_resource *rt, unsigned int rt_sub_resource_idx, + struct wined3d_resource *ds, unsigned int ds_sub_resource_idx, DWORD location) +{ + struct wined3d_rendertarget_info ds_info = {{0}}; + + memset(context_gl->blit_targets, 0, sizeof(context_gl->blit_targets)); + if (rt) + { + context_gl->blit_targets[0].resource = rt; + context_gl->blit_targets[0].sub_resource_idx = rt_sub_resource_idx; + context_gl->blit_targets[0].layer_count = 1; + } + + if (ds) + { + ds_info.resource = ds; + ds_info.sub_resource_idx = ds_sub_resource_idx; + ds_info.layer_count = 1; + } + + wined3d_context_gl_apply_fbo_state(context_gl, target, context_gl->blit_targets, &ds_info, location, location); +} + +/* Context activation is done by the caller. */ +void wined3d_context_gl_alloc_occlusion_query(struct wined3d_context_gl *context_gl, + struct wined3d_occlusion_query *query) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + + if (context_gl->free_occlusion_query_count) + { + query->id = context_gl->free_occlusion_queries[--context_gl->free_occlusion_query_count]; + } + else + { + if (gl_info->supported[ARB_OCCLUSION_QUERY]) + { + GL_EXTCALL(glGenQueries(1, &query->id)); + checkGLcall("glGenQueries"); + + TRACE("Allocated occlusion query %u in context %p.\n", query->id, context_gl); + } + else + { + WARN("Occlusion queries not supported, not allocating query id.\n"); + query->id = 0; + } + } + + query->context_gl = context_gl; + list_add_head(&context_gl->occlusion_queries, &query->entry); +} + +void wined3d_context_gl_free_occlusion_query(struct wined3d_occlusion_query *query) +{ + struct wined3d_context_gl *context_gl = query->context_gl; + + list_remove(&query->entry); + query->context_gl = NULL; + + if (!wined3d_array_reserve((void **)&context_gl->free_occlusion_queries, + &context_gl->free_occlusion_query_size, context_gl->free_occlusion_query_count + 1, + sizeof(*context_gl->free_occlusion_queries))) + { + ERR("Failed to grow free list, leaking query %u in context %p.\n", query->id, context_gl); + return; + } + + context_gl->free_occlusion_queries[context_gl->free_occlusion_query_count++] = query->id; +} + +/* Context activation is done by the caller. */ +void wined3d_context_gl_alloc_fence(struct wined3d_context_gl *context_gl, struct wined3d_fence *fence) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + + if (context_gl->free_fence_count) + { + fence->object = context_gl->free_fences[--context_gl->free_fence_count]; + } + else + { + if (gl_info->supported[ARB_SYNC]) + { + /* Using ARB_sync, not much to do here. */ + fence->object.sync = NULL; + TRACE("Allocated sync object in context %p.\n", context_gl); + } + else if (gl_info->supported[APPLE_FENCE]) + { + GL_EXTCALL(glGenFencesAPPLE(1, &fence->object.id)); + checkGLcall("glGenFencesAPPLE"); + + TRACE("Allocated fence %u in context %p.\n", fence->object.id, context_gl); + } + else if(gl_info->supported[NV_FENCE]) + { + GL_EXTCALL(glGenFencesNV(1, &fence->object.id)); + checkGLcall("glGenFencesNV"); + + TRACE("Allocated fence %u in context %p.\n", fence->object.id, context_gl); + } + else + { + WARN("Fences not supported, not allocating fence.\n"); + fence->object.id = 0; + } + } + + fence->context_gl = context_gl; + list_add_head(&context_gl->fences, &fence->entry); +} + +void wined3d_context_gl_free_fence(struct wined3d_fence *fence) +{ + struct wined3d_context_gl *context_gl = fence->context_gl; + + list_remove(&fence->entry); + fence->context_gl = NULL; + + if (!wined3d_array_reserve((void **)&context_gl->free_fences, + &context_gl->free_fence_size, context_gl->free_fence_count + 1, + sizeof(*context_gl->free_fences))) + { + ERR("Failed to grow free list, leaking fence %u in context %p.\n", fence->object.id, context_gl); + return; + } + + context_gl->free_fences[context_gl->free_fence_count++] = fence->object; +} + +/* Context activation is done by the caller. */ +void wined3d_context_gl_alloc_timestamp_query(struct wined3d_context_gl *context_gl, + struct wined3d_timestamp_query *query) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + + if (context_gl->free_timestamp_query_count) + { + query->id = context_gl->free_timestamp_queries[--context_gl->free_timestamp_query_count]; + } + else + { + GL_EXTCALL(glGenQueries(1, &query->id)); + checkGLcall("glGenQueries"); + + TRACE("Allocated timestamp query %u in context %p.\n", query->id, context_gl); + } + + query->context_gl = context_gl; + list_add_head(&context_gl->timestamp_queries, &query->entry); +} + +void wined3d_context_gl_free_timestamp_query(struct wined3d_timestamp_query *query) +{ + struct wined3d_context_gl *context_gl = query->context_gl; + + list_remove(&query->entry); + query->context_gl = NULL; + + if (!wined3d_array_reserve((void **)&context_gl->free_timestamp_queries, + &context_gl->free_timestamp_query_size, context_gl->free_timestamp_query_count + 1, + sizeof(*context_gl->free_timestamp_queries))) + { + ERR("Failed to grow free list, leaking query %u in context %p.\n", query->id, context_gl); + return; + } + + context_gl->free_timestamp_queries[context_gl->free_timestamp_query_count++] = query->id; +} + +void wined3d_context_gl_alloc_so_statistics_query(struct wined3d_context_gl *context_gl, + struct wined3d_so_statistics_query *query) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + + if (context_gl->free_so_statistics_query_count) + { + query->u = context_gl->free_so_statistics_queries[--context_gl->free_so_statistics_query_count]; + } + else + { + GL_EXTCALL(glGenQueries(ARRAY_SIZE(query->u.id), query->u.id)); + checkGLcall("glGenQueries"); + + TRACE("Allocated SO statistics queries %u, %u in context %p.\n", + query->u.id[0], query->u.id[1], context_gl); + } + + query->context_gl = context_gl; + list_add_head(&context_gl->so_statistics_queries, &query->entry); +} + +void wined3d_context_gl_free_so_statistics_query(struct wined3d_so_statistics_query *query) +{ + struct wined3d_context_gl *context_gl = query->context_gl; + + list_remove(&query->entry); + query->context_gl = NULL; + + if (!wined3d_array_reserve((void **)&context_gl->free_so_statistics_queries, + &context_gl->free_so_statistics_query_size, context_gl->free_so_statistics_query_count + 1, + sizeof(*context_gl->free_so_statistics_queries))) + { + ERR("Failed to grow free list, leaking GL queries %u, %u in context %p.\n", + query->u.id[0], query->u.id[1], context_gl); + return; + } + + context_gl->free_so_statistics_queries[context_gl->free_so_statistics_query_count++] = query->u; +} + +void wined3d_context_gl_alloc_pipeline_statistics_query(struct wined3d_context_gl *context_gl, + struct wined3d_pipeline_statistics_query *query) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + + if (context_gl->free_pipeline_statistics_query_count) + { + query->u = context_gl->free_pipeline_statistics_queries[--context_gl->free_pipeline_statistics_query_count]; + } + else + { + GL_EXTCALL(glGenQueries(ARRAY_SIZE(query->u.id), query->u.id)); + checkGLcall("glGenQueries"); + } + + query->context_gl = context_gl; + list_add_head(&context_gl->pipeline_statistics_queries, &query->entry); +} + +void wined3d_context_gl_free_pipeline_statistics_query(struct wined3d_pipeline_statistics_query *query) +{ + struct wined3d_context_gl *context_gl = query->context_gl; + + list_remove(&query->entry); + query->context_gl = NULL; + + if (!wined3d_array_reserve((void **)&context_gl->free_pipeline_statistics_queries, + &context_gl->free_pipeline_statistics_query_size, context_gl->free_pipeline_statistics_query_count + 1, + sizeof(*context_gl->free_pipeline_statistics_queries))) + { + ERR("Failed to grow free list, leaking GL queries in context %p.\n", context_gl); + return; + } + + context_gl->free_pipeline_statistics_queries[context_gl->free_pipeline_statistics_query_count++] = query->u; +} + +typedef void (context_fbo_entry_func_t)(struct wined3d_context_gl *context_gl, struct fbo_entry *entry); + +static void wined3d_context_gl_enum_fbo_entries(const struct wined3d_device *device, + GLuint name, BOOL rb_namespace, context_fbo_entry_func_t *callback) +{ + unsigned int i, j; + + for (i = 0; i < device->context_count; ++i) + { + struct wined3d_context_gl *context_gl = wined3d_context_gl(device->contexts[i]); + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + struct fbo_entry *entry, *entry2; + + LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &context_gl->fbo_list, struct fbo_entry, entry) + { + for (j = 0; j < gl_info->limits.buffers + 1; ++j) + { + if (entry->key.objects[j].object == name + && !(entry->key.rb_namespace & (1 << j)) == !rb_namespace) + { + callback(context_gl, entry); + break; + } + } + } + } +} + +static void wined3d_context_gl_queue_fbo_entry_destruction(struct wined3d_context_gl *context_gl, + struct fbo_entry *entry) +{ + list_remove(&entry->entry); + list_add_head(&context_gl->fbo_destroy_list, &entry->entry); +} + +void context_gl_resource_released(struct wined3d_device *device, GLuint name, BOOL rb_namespace) +{ + wined3d_context_gl_enum_fbo_entries(device, name, rb_namespace, + wined3d_context_gl_queue_fbo_entry_destruction); +} + +void wined3d_context_gl_texture_update(struct wined3d_context_gl *context_gl, + const struct wined3d_texture_gl *texture_gl) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + struct fbo_entry *entry = context_gl->current_fbo; + unsigned int i; + + if (!entry || context_gl->rebind_fbo) + return; + + for (i = 0; i < gl_info->limits.buffers + 1; ++i) + { + if (texture_gl->texture_rgb.name == entry->key.objects[i].object + || texture_gl->texture_srgb.name == entry->key.objects[i].object) + { + TRACE("Updated texture %p is bound as attachment %u to the current FBO.\n", texture_gl, i); + context_gl->rebind_fbo = TRUE; + return; + } + } +} + +static BOOL wined3d_context_gl_restore_pixel_format(struct wined3d_context_gl *context_gl) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + BOOL ret = FALSE; + + if (context_gl->restore_pf && IsWindow(context_gl->restore_pf_win)) + { + if (gl_info->supported[WGL_WINE_PIXEL_FORMAT_PASSTHROUGH]) + { + HDC dc = GetDCEx(context_gl->restore_pf_win, 0, DCX_USESTYLE | DCX_CACHE); + if (dc) + { + if (!(ret = GL_EXTCALL(wglSetPixelFormatWINE(dc, context_gl->restore_pf)))) + { + ERR("Failed to restore pixel format %d on window %p.\n", + context_gl->restore_pf, context_gl->restore_pf_win); + } + ReleaseDC(context_gl->restore_pf_win, dc); + } + } + else + { + ERR("Unable to restore pixel format %d on window %p.\n", + context_gl->restore_pf, context_gl->restore_pf_win); + } + } + + context_gl->restore_pf = 0; + context_gl->restore_pf_win = NULL; + return ret; +} + +static BOOL wined3d_context_gl_set_pixel_format(struct wined3d_context_gl *context_gl) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + BOOL private = context_gl->dc_is_private; + int format = context_gl->pixel_format; + HDC dc = context_gl->dc; + int current; + + if (private && context_gl->dc_has_format) + return TRUE; + + if (!private && WindowFromDC(dc) != context_gl->window) + return FALSE; + + current = gl_info->gl_ops.wgl.p_wglGetPixelFormat(dc); + if (current == format) goto success; + + if (!current) + { + if (!SetPixelFormat(dc, format, NULL)) + { + /* This may also happen if the dc belongs to a destroyed window. */ + WARN("Failed to set pixel format %d on device context %p, last error %#x.\n", + format, dc, GetLastError()); + return FALSE; + } + + context_gl->restore_pf = 0; + context_gl->restore_pf_win = private ? NULL : WindowFromDC(dc); + goto success; + } + + /* By default WGL doesn't allow pixel format adjustments but we need it + * here. For this reason there's a Wine specific wglSetPixelFormat() + * which allows us to set the pixel format multiple times. Only use it + * when really needed. */ + if (gl_info->supported[WGL_WINE_PIXEL_FORMAT_PASSTHROUGH]) + { + HWND win; + + if (!GL_EXTCALL(wglSetPixelFormatWINE(dc, format))) + { + ERR("wglSetPixelFormatWINE failed to set pixel format %d on device context %p.\n", + format, dc); + return FALSE; + } + + win = private ? NULL : WindowFromDC(dc); + if (win != context_gl->restore_pf_win) + { + wined3d_context_gl_restore_pixel_format(context_gl); + + context_gl->restore_pf = private ? 0 : current; + context_gl->restore_pf_win = win; + } + + goto success; + } + + /* OpenGL doesn't allow pixel format adjustments. Print an error and + * continue using the old format. There's a big chance that the old + * format works although with a performance hit and perhaps rendering + * errors. */ + ERR("Unable to set pixel format %d on device context %p. Already using format %d.\n", + format, dc, current); + return TRUE; + +success: + if (private) + context_gl->dc_has_format = TRUE; + return TRUE; +} + +static BOOL wined3d_context_gl_set_gl_context(struct wined3d_context_gl *context_gl) +{ + struct wined3d_swapchain_gl *swapchain_gl = wined3d_swapchain_gl(context_gl->c.swapchain); + BOOL backup = FALSE; + + if (!wined3d_context_gl_set_pixel_format(context_gl)) + { + WARN("Failed to set pixel format %d on device context %p.\n", + context_gl->pixel_format, context_gl->dc); + backup = TRUE; + } + + if (backup || !wglMakeCurrent(context_gl->dc, context_gl->gl_ctx)) + { + WARN("Failed to make GL context %p current on device context %p, last error %#x.\n", + context_gl->gl_ctx, context_gl->dc, GetLastError()); + context_gl->valid = 0; + WARN("Trying fallback to the backup window.\n"); + + /* FIXME: If the context is destroyed it's no longer associated with + * a swapchain, so we can't use the swapchain to get a backup dc. To + * make this work windowless contexts would need to be handled by the + * device. */ + if (context_gl->c.destroyed || !swapchain_gl) + { + FIXME("Unable to get backup dc for destroyed context %p.\n", context_gl); + wined3d_context_gl_set_current(NULL); + return FALSE; + } + + if (!(context_gl->dc = wined3d_swapchain_gl_get_backup_dc(swapchain_gl))) + { + wined3d_context_gl_set_current(NULL); + return FALSE; + } + + context_gl->dc_is_private = TRUE; + context_gl->dc_has_format = FALSE; + + if (!wined3d_context_gl_set_pixel_format(context_gl)) + { + ERR("Failed to set pixel format %d on device context %p.\n", + context_gl->pixel_format, context_gl->dc); + wined3d_context_gl_set_current(NULL); + return FALSE; + } + + if (!wglMakeCurrent(context_gl->dc, context_gl->gl_ctx)) + { + ERR("Fallback to backup window (dc %p) failed too, last error %#x.\n", + context_gl->dc, GetLastError()); + wined3d_context_gl_set_current(NULL); + return FALSE; + } + + context_gl->valid = 1; + } + context_gl->needs_set = 0; + + return TRUE; +} + +static void context_restore_gl_context(const struct wined3d_gl_info *gl_info, HDC dc, HGLRC gl_ctx) +{ + if (!wglMakeCurrent(dc, gl_ctx)) + { + ERR("Failed to restore GL context %p on device context %p, last error %#x.\n", + gl_ctx, dc, GetLastError()); + wined3d_context_gl_set_current(NULL); + } +} + +static void wined3d_context_gl_update_window(struct wined3d_context_gl *context_gl) +{ + if (!context_gl->c.swapchain) + return; + + if (context_gl->window == context_gl->c.swapchain->win_handle) + return; + + TRACE("Updating context %p window from %p to %p.\n", + context_gl, context_gl->window, context_gl->c.swapchain->win_handle); + + if (context_gl->dc) + wined3d_release_dc(context_gl->window, context_gl->dc); + + context_gl->window = context_gl->c.swapchain->win_handle; + context_gl->dc_is_private = FALSE; + context_gl->dc_has_format = FALSE; + context_gl->needs_set = 1; + context_gl->valid = 1; + + if (!(context_gl->dc = GetDCEx(context_gl->window, 0, DCX_USESTYLE | DCX_CACHE))) + { + ERR("Failed to get a device context for window %p.\n", context_gl->window); + context_gl->valid = 0; + } +} + +static void wined3d_context_gl_cleanup(struct wined3d_context_gl *context_gl) +{ + struct wined3d_pipeline_statistics_query *pipeline_statistics_query; + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + struct wined3d_so_statistics_query *so_statistics_query; + struct wined3d_timestamp_query *timestamp_query; + struct wined3d_occlusion_query *occlusion_query; + struct fbo_entry *entry, *entry2; + struct wined3d_fence *fence; + HGLRC restore_ctx; + HDC restore_dc; + unsigned int i; + + restore_ctx = wglGetCurrentContext(); + restore_dc = wglGetCurrentDC(); + + if (restore_ctx == context_gl->gl_ctx) + restore_ctx = NULL; + else if (context_gl->valid) + wined3d_context_gl_set_gl_context(context_gl); + + if (context_gl->valid) + { + /* If we're here because we're switching away from a previously + * destroyed context, acquiring a context in order to submit a fence + * is problematic. (In particular, we'd end up back here again in the + * process of switching to the newly acquired context.) */ + if (context_gl->c.destroyed) + { + gl_info->gl_ops.gl.p_glFinish(); + } + else + { + wined3d_context_gl_submit_command_fence(context_gl); + wined3d_context_gl_wait_command_fence(context_gl, + wined3d_device_gl(context_gl->c.device)->current_fence_id - 1); + } + + if (context_gl->dummy_arbfp_prog) + GL_EXTCALL(glDeleteProgramsARB(1, &context_gl->dummy_arbfp_prog)); + + if (context_gl->blit_vbo) + GL_EXTCALL(glDeleteBuffers(1, &context_gl->blit_vbo)); + + for (i = 0; i < context_gl->free_pipeline_statistics_query_count; ++i) + { + union wined3d_gl_pipeline_statistics_query *q = &context_gl->free_pipeline_statistics_queries[i]; + GL_EXTCALL(glDeleteQueries(ARRAY_SIZE(q->id), q->id)); + } + + for (i = 0; i < context_gl->free_so_statistics_query_count; ++i) + { + union wined3d_gl_so_statistics_query *q = &context_gl->free_so_statistics_queries[i]; + GL_EXTCALL(glDeleteQueries(ARRAY_SIZE(q->id), q->id)); + } + + if (context_gl->free_timestamp_query_count) + GL_EXTCALL(glDeleteQueries(context_gl->free_timestamp_query_count, context_gl->free_timestamp_queries)); + + if (gl_info->supported[ARB_SYNC]) + { + for (i = 0; i < context_gl->free_fence_count; ++i) + { + GL_EXTCALL(glDeleteSync(context_gl->free_fences[i].sync)); + } + } + else if (gl_info->supported[APPLE_FENCE]) + { + for (i = 0; i < context_gl->free_fence_count; ++i) + { + GL_EXTCALL(glDeleteFencesAPPLE(1, &context_gl->free_fences[i].id)); + } + } + else if (gl_info->supported[NV_FENCE]) + { + for (i = 0; i < context_gl->free_fence_count; ++i) + { + GL_EXTCALL(glDeleteFencesNV(1, &context_gl->free_fences[i].id)); + } + } + + if (context_gl->free_occlusion_query_count) + GL_EXTCALL(glDeleteQueries(context_gl->free_occlusion_query_count, context_gl->free_occlusion_queries)); + + checkGLcall("context cleanup"); + } + heap_free(context_gl->submitted.fences); + heap_free(context_gl->free_pipeline_statistics_queries); + heap_free(context_gl->free_so_statistics_queries); + heap_free(context_gl->free_timestamp_queries); + heap_free(context_gl->free_fences); + heap_free(context_gl->free_occlusion_queries); + + LIST_FOR_EACH_ENTRY(pipeline_statistics_query, &context_gl->pipeline_statistics_queries, + struct wined3d_pipeline_statistics_query, entry) + { + if (context_gl->valid) + GL_EXTCALL(glDeleteQueries(ARRAY_SIZE(pipeline_statistics_query->u.id), pipeline_statistics_query->u.id)); + pipeline_statistics_query->context_gl = NULL; + } + + LIST_FOR_EACH_ENTRY(so_statistics_query, &context_gl->so_statistics_queries, + struct wined3d_so_statistics_query, entry) + { + if (context_gl->valid) + GL_EXTCALL(glDeleteQueries(ARRAY_SIZE(so_statistics_query->u.id), so_statistics_query->u.id)); + so_statistics_query->context_gl = NULL; + } + + LIST_FOR_EACH_ENTRY(timestamp_query, &context_gl->timestamp_queries, struct wined3d_timestamp_query, entry) + { + if (context_gl->valid) + GL_EXTCALL(glDeleteQueries(1, ×tamp_query->id)); + timestamp_query->context_gl = NULL; + } + + LIST_FOR_EACH_ENTRY(fence, &context_gl->fences, struct wined3d_fence, entry) + { + if (context_gl->valid) + { + if (gl_info->supported[ARB_SYNC]) + { + if (fence->object.sync) + GL_EXTCALL(glDeleteSync(fence->object.sync)); + } + else if (gl_info->supported[APPLE_FENCE]) + { + GL_EXTCALL(glDeleteFencesAPPLE(1, &fence->object.id)); + } + else if (gl_info->supported[NV_FENCE]) + { + GL_EXTCALL(glDeleteFencesNV(1, &fence->object.id)); + } + } + fence->context_gl = NULL; + } + + LIST_FOR_EACH_ENTRY(occlusion_query, &context_gl->occlusion_queries, struct wined3d_occlusion_query, entry) + { + if (context_gl->valid) + GL_EXTCALL(glDeleteQueries(1, &occlusion_query->id)); + occlusion_query->context_gl = NULL; + } + + LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &context_gl->fbo_destroy_list, struct fbo_entry, entry) + { + if (!context_gl->valid) + entry->id = 0; + wined3d_context_gl_destroy_fbo_entry(context_gl, entry); + } + + LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &context_gl->fbo_list, struct fbo_entry, entry) + { + if (!context_gl->valid) + entry->id = 0; + wined3d_context_gl_destroy_fbo_entry(context_gl, entry); + } + + heap_free(context_gl->texture_type); + + wined3d_context_gl_restore_pixel_format(context_gl); + if (restore_ctx) + context_restore_gl_context(gl_info, restore_dc, restore_ctx); + else if (wglGetCurrentContext() && !wglMakeCurrent(NULL, NULL)) + ERR("Failed to disable GL context.\n"); + + wined3d_release_dc(context_gl->window, context_gl->dc); + + if (!wglDeleteContext(context_gl->gl_ctx)) + { + DWORD err = GetLastError(); + ERR("Failed to delete GL context %p, last error %#x.\n", context_gl->gl_ctx, err); + } + + wined3d_context_cleanup(&context_gl->c); +} + +DWORD context_get_tls_idx(void) +{ + return wined3d_context_tls_idx; +} + +void context_set_tls_idx(DWORD idx) +{ + wined3d_context_tls_idx = idx; +} + +struct wined3d_context_gl *wined3d_context_gl_get_current(void) +{ + return TlsGetValue(wined3d_context_tls_idx); +} + +BOOL wined3d_context_gl_set_current(struct wined3d_context_gl *context_gl) +{ + struct wined3d_context_gl *old = wined3d_context_gl_get_current(); + + if (old == context_gl) + { + TRACE("Already using D3D context %p.\n", context_gl); + return TRUE; + } + + if (old) + { + if (old->c.destroyed) + { + TRACE("Switching away from destroyed context %p.\n", old); + wined3d_context_gl_cleanup(old); + heap_free((void *)old->gl_info); + heap_free(old); + } + else + { + if (wglGetCurrentContext()) + { + const struct wined3d_gl_info *gl_info = old->gl_info; + TRACE("Flushing context %p before switching to %p.\n", old, context_gl); + gl_info->gl_ops.gl.p_glFlush(); + } + old->c.current = 0; + } + } + + if (context_gl) + { + if (!context_gl->valid) + { + ERR("Trying to make invalid context %p current.\n", context_gl); + return FALSE; + } + + TRACE("Switching to D3D context %p, GL context %p, device context %p.\n", + context_gl, context_gl->gl_ctx, context_gl->dc); + if (!wined3d_context_gl_set_gl_context(context_gl)) + return FALSE; + context_gl->c.current = 1; + } + else if (wglGetCurrentContext()) + { + TRACE("Clearing current D3D context.\n"); + if (!wglMakeCurrent(NULL, NULL)) + { + DWORD err = GetLastError(); + ERR("Failed to clear current GL context, last error %#x.\n", err); + TlsSetValue(wined3d_context_tls_idx, NULL); + return FALSE; + } + } + + return TlsSetValue(wined3d_context_tls_idx, context_gl); +} + +void wined3d_context_gl_release(struct wined3d_context_gl *context_gl) +{ + TRACE("Releasing context %p, level %u.\n", context_gl, context_gl->level); + + if (WARN_ON(d3d)) + { + if (!context_gl->level) + WARN("Context %p is not active.\n", context_gl); + else if (context_gl != wined3d_context_gl_get_current()) + WARN("Context %p is not the current context.\n", context_gl); + } + + if (!--context_gl->level) + { + if (wined3d_context_gl_restore_pixel_format(context_gl)) + context_gl->needs_set = 1; + if (context_gl->restore_ctx) + { + TRACE("Restoring GL context %p on device context %p.\n", context_gl->restore_ctx, context_gl->restore_dc); + context_restore_gl_context(context_gl->gl_info, context_gl->restore_dc, context_gl->restore_ctx); + context_gl->restore_ctx = NULL; + context_gl->restore_dc = NULL; + } + + if (context_gl->c.destroy_delayed) + { + TRACE("Destroying context %p.\n", context_gl); + wined3d_context_gl_destroy(context_gl); + } + } +} + +static void wined3d_context_gl_enter(struct wined3d_context_gl *context_gl) +{ + TRACE("Entering context %p, level %u.\n", context_gl, context_gl->level + 1); + + if (!context_gl->level++) + { + const struct wined3d_context_gl *current_context = wined3d_context_gl_get_current(); + HGLRC current_gl = wglGetCurrentContext(); + + if (current_gl && (!current_context || current_context->gl_ctx != current_gl)) + { + TRACE("Another GL context (%p on device context %p) is already current.\n", + current_gl, wglGetCurrentDC()); + context_gl->restore_ctx = current_gl; + context_gl->restore_dc = wglGetCurrentDC(); + context_gl->needs_set = 1; + } + else if (!context_gl->needs_set && !(context_gl->dc_is_private && context_gl->dc_has_format) + && context_gl->pixel_format != context_gl->gl_info->gl_ops.wgl.p_wglGetPixelFormat(context_gl->dc)) + context_gl->needs_set = 1; + } +} + +/* This function takes care of wined3d pixel format selection. */ +static int context_choose_pixel_format(const struct wined3d_device *device, HDC hdc, + const struct wined3d_format *color_format, const struct wined3d_format *ds_format, + BOOL auxBuffers) +{ + unsigned int cfg_count = wined3d_adapter_gl(device->adapter)->pixel_format_count; + unsigned int current_value; + PIXELFORMATDESCRIPTOR pfd; + int iPixelFormat = 0; + unsigned int i; + + TRACE("device %p, dc %p, color_format %s, ds_format %s, aux_buffers %#x.\n", + device, hdc, debug_d3dformat(color_format->id), debug_d3dformat(ds_format->id), + auxBuffers); + + current_value = 0; + for (i = 0; i < cfg_count; ++i) + { + const struct wined3d_pixel_format *cfg = &wined3d_adapter_gl(device->adapter)->pixel_formats[i]; + unsigned int value; + + /* For now only accept RGBA formats. Perhaps some day we will + * allow floating point formats for pbuffers. */ + if (cfg->iPixelType != WGL_TYPE_RGBA_ARB) + continue; + /* In window mode we need a window drawable format and double buffering. */ + if (!(cfg->windowDrawable && cfg->doubleBuffer)) + continue; + if (cfg->redSize < color_format->red_size) + continue; + if (cfg->greenSize < color_format->green_size) + continue; + if (cfg->blueSize < color_format->blue_size) + continue; + if (cfg->alphaSize < color_format->alpha_size) + continue; + if (cfg->depthSize < ds_format->depth_size) + continue; + if (ds_format->stencil_size && cfg->stencilSize != ds_format->stencil_size) + continue; + /* Check multisampling support. */ + if (cfg->numSamples) + continue; + + value = 1; + /* We try to locate a format which matches our requirements exactly. In case of + * depth it is no problem to emulate 16-bit using e.g. 24-bit, so accept that. */ + if (cfg->depthSize == ds_format->depth_size) + value += 1; + if (cfg->stencilSize == ds_format->stencil_size) + value += 2; + if (cfg->alphaSize == color_format->alpha_size) + value += 4; + /* We like to have aux buffers in backbuffer mode */ + if (auxBuffers && cfg->auxBuffers) + value += 8; + if (cfg->redSize == color_format->red_size + && cfg->greenSize == color_format->green_size + && cfg->blueSize == color_format->blue_size) + value += 16; + + if (value > current_value) + { + iPixelFormat = cfg->iPixelFormat; + current_value = value; + } + } + + if (!iPixelFormat) + { + ERR("Trying to locate a compatible pixel format because an exact match failed.\n"); + + memset(&pfd, 0, sizeof(pfd)); + pfd.nSize = sizeof(pfd); + pfd.nVersion = 1; + pfd.dwFlags = PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER | PFD_DRAW_TO_WINDOW;/*PFD_GENERIC_ACCELERATED*/ + pfd.iPixelType = PFD_TYPE_RGBA; + pfd.cAlphaBits = color_format->alpha_size; + pfd.cColorBits = color_format->red_size + color_format->green_size + + color_format->blue_size + color_format->alpha_size; + pfd.cDepthBits = ds_format->depth_size; + pfd.cStencilBits = ds_format->stencil_size; + pfd.iLayerType = PFD_MAIN_PLANE; + + if (!(iPixelFormat = ChoosePixelFormat(hdc, &pfd))) + { + /* Something is very wrong as ChoosePixelFormat() barely fails. */ + ERR("Can't find a suitable pixel format.\n"); + return 0; + } + } + + TRACE("Found iPixelFormat=%d for ColorFormat=%s, DepthStencilFormat=%s.\n", + iPixelFormat, debug_d3dformat(color_format->id), debug_d3dformat(ds_format->id)); + return iPixelFormat; +} + +/* Context activation is done by the caller. */ +void wined3d_context_gl_bind_dummy_textures(const struct wined3d_context_gl *context_gl) +{ + const struct wined3d_dummy_textures *textures = &wined3d_device_gl(context_gl->c.device)->dummy_textures; + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + unsigned int i; + + for (i = 0; i < gl_info->limits.combined_samplers; ++i) + { + GL_EXTCALL(glActiveTexture(GL_TEXTURE0 + i)); + + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_1D, textures->tex_1d); + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_2D, textures->tex_2d); + + if (gl_info->supported[ARB_TEXTURE_RECTANGLE]) + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_RECTANGLE_ARB, textures->tex_rect); + + if (gl_info->supported[EXT_TEXTURE3D]) + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_3D, textures->tex_3d); + + if (gl_info->supported[ARB_TEXTURE_CUBE_MAP]) + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_CUBE_MAP, textures->tex_cube); + + if (gl_info->supported[ARB_TEXTURE_CUBE_MAP_ARRAY]) + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_CUBE_MAP_ARRAY, textures->tex_cube_array); + + if (gl_info->supported[EXT_TEXTURE_ARRAY]) + { + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_1D_ARRAY, textures->tex_1d_array); + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_2D_ARRAY, textures->tex_2d_array); + } + + if (gl_info->supported[ARB_TEXTURE_BUFFER_OBJECT]) + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_BUFFER, textures->tex_buffer); + + if (gl_info->supported[ARB_TEXTURE_MULTISAMPLE]) + { + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, textures->tex_2d_ms); + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, textures->tex_2d_ms_array); + } + } + + checkGLcall("bind dummy textures"); +} + +void wined3d_check_gl_call(const struct wined3d_gl_info *gl_info, + const char *file, unsigned int line, const char *name) +{ + GLint err; + + if (gl_info->supported[ARB_DEBUG_OUTPUT] || (err = gl_info->gl_ops.gl.p_glGetError()) == GL_NO_ERROR) + { + TRACE("%s call ok %s / %u.\n", name, file, line); + return; + } + + do + { + ERR(">>>>>>> %s (%#x) from %s @ %s / %u.\n", + debug_glerror(err), err, name, file,line); + err = gl_info->gl_ops.gl.p_glGetError(); + } while (err != GL_NO_ERROR); +} + +static BOOL context_debug_output_enabled(const struct wined3d_gl_info *gl_info) +{ + return gl_info->supported[ARB_DEBUG_OUTPUT] + && (ERR_ON(d3d) || FIXME_ON(d3d) || WARN_ON(d3d_perf)); +} + +static void WINE_GLAPI wined3d_debug_callback(GLenum source, GLenum type, GLuint id, + GLenum severity, GLsizei length, const char *message, void *ctx) +{ + switch (type) + { + case GL_DEBUG_TYPE_ERROR_ARB: + ERR("%p: %s.\n", ctx, debugstr_an(message, length)); + break; + + case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB: + case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB: + case GL_DEBUG_TYPE_PORTABILITY_ARB: + FIXME("%p: %s.\n", ctx, debugstr_an(message, length)); + break; + + case GL_DEBUG_TYPE_PERFORMANCE_ARB: + WARN_(d3d_perf)("%p: %s.\n", ctx, debugstr_an(message, length)); + break; + + default: + FIXME("ctx %p, type %#x: %s.\n", ctx, type, debugstr_an(message, length)); + break; + } +} + +HGLRC context_create_wgl_attribs(const struct wined3d_gl_info *gl_info, HDC hdc, HGLRC share_ctx) +{ + HGLRC ctx; + unsigned int ctx_attrib_idx = 0; + GLint ctx_attribs[7], ctx_flags = 0; + + if (context_debug_output_enabled(gl_info)) + ctx_flags = WGL_CONTEXT_DEBUG_BIT_ARB; + ctx_attribs[ctx_attrib_idx++] = WGL_CONTEXT_MAJOR_VERSION_ARB; + ctx_attribs[ctx_attrib_idx++] = gl_info->selected_gl_version >> 16; + ctx_attribs[ctx_attrib_idx++] = WGL_CONTEXT_MINOR_VERSION_ARB; + ctx_attribs[ctx_attrib_idx++] = gl_info->selected_gl_version & 0xffff; + if (ctx_flags) + { + ctx_attribs[ctx_attrib_idx++] = WGL_CONTEXT_FLAGS_ARB; + ctx_attribs[ctx_attrib_idx++] = ctx_flags; + } + ctx_attribs[ctx_attrib_idx] = 0; + + if (!(ctx = gl_info->p_wglCreateContextAttribsARB(hdc, share_ctx, ctx_attribs))) + { + if (gl_info->selected_gl_version >= MAKEDWORD_VERSION(3, 2)) + { + if (ctx_flags) + { + ctx_flags |= WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB; + ctx_attribs[ctx_attrib_idx - 1] = ctx_flags; + } + else + { + ctx_flags = WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB; + ctx_attribs[ctx_attrib_idx++] = WGL_CONTEXT_FLAGS_ARB; + ctx_attribs[ctx_attrib_idx++] = ctx_flags; + ctx_attribs[ctx_attrib_idx] = 0; + } + if (!(ctx = gl_info->p_wglCreateContextAttribsARB(hdc, share_ctx, ctx_attribs))) + WARN("Failed to create a WGL context with wglCreateContextAttribsARB, last error %#x.\n", + GetLastError()); + } + } + return ctx; +} + +static BOOL wined3d_context_gl_create_wgl_ctx(struct wined3d_context_gl *context_gl, + struct wined3d_swapchain_gl *swapchain_gl) +{ + const struct wined3d_format *colour_format, *ds_format; + struct wined3d_context *context = &context_gl->c; + const struct wined3d_gl_info *gl_info; + struct wined3d_resource *target; + struct wined3d_adapter *adapter; + unsigned int target_bind_flags; + struct wined3d_device *device; + HGLRC ctx, share_ctx; + unsigned int i; + + device = context->device; + adapter = device->adapter; + gl_info = &adapter->gl_info; + + target = &context->current_rt.texture->resource; + target_bind_flags = target->bind_flags; + + if (wined3d_settings.offscreen_rendering_mode == ORM_BACKBUFFER) + { + static const enum wined3d_format_id ds_formats[] = + { + WINED3DFMT_D24_UNORM_S8_UINT, + WINED3DFMT_D32_UNORM, + WINED3DFMT_R24_UNORM_X8_TYPELESS, + WINED3DFMT_D16_UNORM, + WINED3DFMT_S1_UINT_D15_UNORM, + }; + + colour_format = target->format; + + /* In case of ORM_BACKBUFFER, make sure to request an alpha component for + * X4R4G4B4/X8R8G8B8 as we might need it for the backbuffer. */ + if (colour_format->id == WINED3DFMT_B4G4R4X4_UNORM) + colour_format = wined3d_get_format(adapter, WINED3DFMT_B4G4R4A4_UNORM, target_bind_flags); + else if (colour_format->id == WINED3DFMT_B8G8R8X8_UNORM) + colour_format = wined3d_get_format(adapter, WINED3DFMT_B8G8R8A8_UNORM, target_bind_flags); + + /* DirectDraw supports 8bit paletted render targets and these are used by + * old games like StarCraft and C&C. Most modern hardware doesn't support + * 8bit natively so we perform some form of 8bit -> 32bit conversion. The + * conversion (ab)uses the alpha component for storing the palette index. + * For this reason we require a format with 8bit alpha, so request + * A8R8G8B8. */ + if (colour_format->id == WINED3DFMT_P8_UINT) + colour_format = wined3d_get_format(adapter, WINED3DFMT_B8G8R8A8_UNORM, target_bind_flags); + + /* Try to find a pixel format which matches our requirements. */ + if (!swapchain_gl->s.ds_format) + { + for (i = 0; i < ARRAY_SIZE(ds_formats); ++i) + { + ds_format = wined3d_get_format(adapter, ds_formats[i], WINED3D_BIND_DEPTH_STENCIL); + if ((context_gl->pixel_format = context_choose_pixel_format(device, + context_gl->dc, colour_format, ds_format, TRUE))) + { + swapchain_gl->s.ds_format = ds_format; + break; + } + + TRACE("Depth stencil format %s is not supported, trying next format.\n", + debug_d3dformat(ds_format->id)); + } + } + else + { + context_gl->pixel_format = context_choose_pixel_format(device, + context_gl->dc, colour_format, swapchain_gl->s.ds_format, TRUE); + } + } + else + { + /* When using FBOs for off-screen rendering, we only use the drawable for + * presentation blits, and don't do any rendering to it. That means we + * don't need depth or stencil buffers, and can mostly ignore the render + * target format. This wouldn't necessarily be quite correct for 10bpc + * display modes, but we don't currently support those. + * Using the same format regardless of the colour/depth/stencil targets + * makes it much less likely that different wined3d instances will set + * conflicting pixel formats. */ + colour_format = wined3d_get_format(adapter, WINED3DFMT_B8G8R8A8_UNORM, target_bind_flags); + ds_format = wined3d_get_format(adapter, WINED3DFMT_UNKNOWN, WINED3D_BIND_DEPTH_STENCIL); + context_gl->pixel_format = context_choose_pixel_format(device, + context_gl->dc, colour_format, ds_format, FALSE); + } + + if (!context_gl->pixel_format) + { + ERR("Failed to choose pixel format.\n"); + return FALSE; + } + + wined3d_context_gl_enter(context_gl); + + if (!wined3d_context_gl_set_pixel_format(context_gl)) + { + context_release(context); + + if (context_gl->dc_is_private) + { + ERR("Failed to set pixel format %d on device context %p.\n", context_gl->pixel_format, context_gl->dc); + + return FALSE; + } + + WARN("Failed to set pixel format %d on device context %p, trying backup DC.\n", + context_gl->pixel_format, context_gl->dc); + + wined3d_release_dc(context_gl->window, context_gl->dc); + if (!(context_gl->dc = wined3d_swapchain_gl_get_backup_dc(swapchain_gl))) + { + ERR("Failed to retrieve the backup device context.\n"); + return FALSE; + } + context_gl->dc_is_private = TRUE; + + return wined3d_context_gl_create_wgl_ctx(context_gl, swapchain_gl); + } + + share_ctx = device->context_count ? wined3d_context_gl(device->contexts[0])->gl_ctx : NULL; + if (gl_info->p_wglCreateContextAttribsARB) + { + if (!(ctx = context_create_wgl_attribs(gl_info, context_gl->dc, share_ctx))) + { + ERR("Failed to create a WGL context.\n"); + context_release(context); + return FALSE; + } + } + else + { + if (!(ctx = wglCreateContext(context_gl->dc))) + { + ERR("Failed to create a WGL context.\n"); + context_release(context); + return FALSE; + } + + if (share_ctx && !wglShareLists(share_ctx, ctx)) + { + ERR("wglShareLists(%p, %p) failed, last error %#x.\n", share_ctx, ctx, GetLastError()); + context_release(context); + if (!wglDeleteContext(ctx)) + ERR("wglDeleteContext(%p) failed, last error %#x.\n", ctx, GetLastError()); + return FALSE; + } + } + + context_gl->dc_has_format = TRUE; + context_gl->needs_set = 1; + context_gl->valid = 1; + context_gl->gl_ctx = ctx; + + return TRUE; +} + +HRESULT wined3d_context_gl_init(struct wined3d_context_gl *context_gl, struct wined3d_swapchain_gl *swapchain_gl) +{ + struct wined3d_context *context = &context_gl->c; + const struct wined3d_d3d_info *d3d_info; + const struct wined3d_gl_info *gl_info; + struct wined3d_device *device; + unsigned int i; + + TRACE("context_gl %p, swapchain %p.\n", context_gl, swapchain_gl); + + wined3d_context_init(&context_gl->c, &swapchain_gl->s); + + device = context->device; + gl_info = &device->adapter->gl_info; + context_gl->gl_info = gl_info; + d3d_info = context->d3d_info; + + context_gl->tid = GetCurrentThreadId(); + context_gl->window = context->swapchain->win_handle; + if (context_gl->window == GetDesktopWindow()) + { + TRACE("Swapchain is created on the desktop window, trying backup device context.\n"); + context_gl->dc = NULL; + } + else if (!(context_gl->dc = GetDCEx(context_gl->window, 0, DCX_USESTYLE | DCX_CACHE))) + WARN("Failed to retrieve device context, trying swapchain backup.\n"); + + if (!context_gl->dc) + { + if (!(context_gl->dc = wined3d_swapchain_gl_get_backup_dc(swapchain_gl))) + { + ERR("Failed to retrieve a device context.\n"); + return E_FAIL; + } + context_gl->dc_is_private = TRUE; + } + + list_init(&context_gl->fbo_list); + list_init(&context_gl->fbo_destroy_list); + + list_init(&context_gl->occlusion_queries); + list_init(&context_gl->fences); + list_init(&context_gl->timestamp_queries); + list_init(&context_gl->so_statistics_queries); + list_init(&context_gl->pipeline_statistics_queries); + + for (i = 0; i < ARRAY_SIZE(context_gl->tex_unit_map); ++i) + context_gl->tex_unit_map[i] = WINED3D_UNMAPPED_STAGE; + for (i = 0; i < ARRAY_SIZE(context_gl->rev_tex_unit_map); ++i) + context_gl->rev_tex_unit_map[i] = WINED3D_UNMAPPED_STAGE; + if (gl_info->limits.graphics_samplers >= WINED3D_MAX_COMBINED_SAMPLERS) + { + /* Initialize the texture unit mapping to a 1:1 mapping. */ + unsigned int base, count; + + wined3d_gl_limits_get_texture_unit_range(&gl_info->limits, WINED3D_SHADER_TYPE_PIXEL, &base, &count); + if (base + WINED3D_MAX_FRAGMENT_SAMPLERS > ARRAY_SIZE(context_gl->rev_tex_unit_map)) + { + ERR("Unexpected texture unit base index %u.\n", base); + goto fail; + } + for (i = 0; i < min(count, WINED3D_MAX_FRAGMENT_SAMPLERS); ++i) + { + context_gl->tex_unit_map[i] = base + i; + context_gl->rev_tex_unit_map[base + i] = i; + } + + wined3d_gl_limits_get_texture_unit_range(&gl_info->limits, WINED3D_SHADER_TYPE_VERTEX, &base, &count); + if (base + WINED3D_MAX_VERTEX_SAMPLERS > ARRAY_SIZE(context_gl->rev_tex_unit_map)) + { + ERR("Unexpected texture unit base index %u.\n", base); + goto fail; + } + for (i = 0; i < min(count, WINED3D_MAX_VERTEX_SAMPLERS); ++i) + { + context_gl->tex_unit_map[WINED3D_MAX_FRAGMENT_SAMPLERS + i] = base + i; + context_gl->rev_tex_unit_map[base + i] = WINED3D_MAX_FRAGMENT_SAMPLERS + i; + } + } + + if (!(context_gl->texture_type = heap_calloc(gl_info->limits.combined_samplers, + sizeof(*context_gl->texture_type)))) + goto fail; + + if (!wined3d_context_gl_create_wgl_ctx(context_gl, swapchain_gl)) + goto fail; + + /* Set up the context defaults. */ + + context->render_offscreen = wined3d_resource_is_offscreen(&context->current_rt.texture->resource); + context_gl->draw_buffers_mask = context_generate_rt_mask(GL_BACK); + + if (!wined3d_context_gl_set_current(context_gl)) + { + ERR("Cannot activate context to set up defaults.\n"); + context_release(context); + if (!wglDeleteContext(context_gl->gl_ctx)) + ERR("wglDeleteContext(%p) failed, last error %#x.\n", context_gl->gl_ctx, GetLastError()); + goto fail; + } + + if (context_debug_output_enabled(gl_info)) + { + GL_EXTCALL(glDebugMessageCallback(wined3d_debug_callback, context)); + if (TRACE_ON(d3d_sync)) + gl_info->gl_ops.gl.p_glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); + GL_EXTCALL(glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_FALSE)); + if (ERR_ON(d3d)) + { + GL_EXTCALL(glDebugMessageControl(GL_DONT_CARE, GL_DEBUG_TYPE_ERROR, + GL_DONT_CARE, 0, NULL, GL_TRUE)); + } + if (FIXME_ON(d3d)) + { + GL_EXTCALL(glDebugMessageControl(GL_DONT_CARE, GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR, + GL_DONT_CARE, 0, NULL, GL_TRUE)); + GL_EXTCALL(glDebugMessageControl(GL_DONT_CARE, GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR, + GL_DONT_CARE, 0, NULL, GL_TRUE)); + GL_EXTCALL(glDebugMessageControl(GL_DONT_CARE, GL_DEBUG_TYPE_PORTABILITY, + GL_DONT_CARE, 0, NULL, GL_TRUE)); + } + if (WARN_ON(d3d_perf)) + { + GL_EXTCALL(glDebugMessageControl(GL_DONT_CARE, GL_DEBUG_TYPE_PERFORMANCE, + GL_DONT_CARE, 0, NULL, GL_TRUE)); + } + } + + if (gl_info->supported[WINED3D_GL_LEGACY_CONTEXT]) + gl_info->gl_ops.gl.p_glGetIntegerv(GL_AUX_BUFFERS, &context_gl->aux_buffers); + + TRACE("Setting up the screen\n"); + + if (gl_info->supported[WINED3D_GL_LEGACY_CONTEXT]) + { + gl_info->gl_ops.gl.p_glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE); + checkGLcall("glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE);"); + + gl_info->gl_ops.gl.p_glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT); + checkGLcall("glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);"); + + gl_info->gl_ops.gl.p_glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR); + checkGLcall("glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR);"); + } + else + { + GLuint vao; + + GL_EXTCALL(glGenVertexArrays(1, &vao)); + GL_EXTCALL(glBindVertexArray(vao)); + checkGLcall("creating VAO"); + } + + gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ALIGNMENT, device->surface_alignment); + checkGLcall("glPixelStorei(GL_PACK_ALIGNMENT, device->surface_alignment);"); + gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + checkGLcall("glPixelStorei(GL_UNPACK_ALIGNMENT, 1);"); + + if (gl_info->supported[NV_TEXTURE_SHADER2]) + { + /* Set up the previous texture input for all shader units. This applies to bump mapping, and in d3d + * the previous texture where to source the offset from is always unit - 1. + */ + for (i = 1; i < gl_info->limits.textures; ++i) + { + wined3d_context_gl_active_texture(context_gl, gl_info, i); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_SHADER_NV, + GL_PREVIOUS_TEXTURE_INPUT_NV, GL_TEXTURE0_ARB + i - 1); + checkGLcall("glTexEnvi(GL_TEXTURE_SHADER_NV, GL_PREVIOUS_TEXTURE_INPUT_NV, ..."); + } + } + if (gl_info->supported[ARB_FRAGMENT_PROGRAM]) + { + /* MacOS(radeon X1600 at least, but most likely others too) refuses to draw if GLSL and ARBFP are + * enabled, but the currently bound arbfp program is 0. Enabling ARBFP with prog 0 is invalid, but + * GLSL should bypass this. This causes problems in programs that never use the fixed function pipeline, + * because the ARBFP extension is enabled by the ARBFP pipeline at context creation, but no program + * is ever assigned. + * + * So make sure a program is assigned to each context. The first real ARBFP use will set a different + * program and the dummy program is destroyed when the context is destroyed. + */ + static const char dummy_program[] = + "!!ARBfp1.0\n" + "MOV result.color, fragment.color.primary;\n" + "END\n"; + GL_EXTCALL(glGenProgramsARB(1, &context_gl->dummy_arbfp_prog)); + GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, context_gl->dummy_arbfp_prog)); + GL_EXTCALL(glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, + GL_PROGRAM_FORMAT_ASCII_ARB, strlen(dummy_program), dummy_program)); + } + + if (gl_info->supported[ARB_POINT_SPRITE]) + { + for (i = 0; i < gl_info->limits.textures; ++i) + { + wined3d_context_gl_active_texture(context_gl, gl_info, i); + gl_info->gl_ops.gl.p_glTexEnvi(GL_POINT_SPRITE_ARB, GL_COORD_REPLACE_ARB, GL_TRUE); + checkGLcall("glTexEnvi(GL_POINT_SPRITE_ARB, GL_COORD_REPLACE_ARB, GL_TRUE)"); + } + } + + if (gl_info->supported[ARB_PROVOKING_VERTEX]) + { + GL_EXTCALL(glProvokingVertex(GL_FIRST_VERTEX_CONVENTION)); + } + else if (gl_info->supported[EXT_PROVOKING_VERTEX]) + { + GL_EXTCALL(glProvokingVertexEXT(GL_FIRST_VERTEX_CONVENTION_EXT)); + } + if (!(d3d_info->wined3d_creation_flags & WINED3D_NO_PRIMITIVE_RESTART)) + { + if (gl_info->supported[ARB_ES3_COMPATIBILITY]) + { + gl_info->gl_ops.gl.p_glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX); + checkGLcall("enable GL_PRIMITIVE_RESTART_FIXED_INDEX"); + } + else + { + FIXME("OpenGL implementation does not support GL_PRIMITIVE_RESTART_FIXED_INDEX.\n"); + } + } + if (!(d3d_info->wined3d_creation_flags & WINED3D_LEGACY_CUBEMAP_FILTERING) + && gl_info->supported[ARB_SEAMLESS_CUBE_MAP]) + { + gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); + checkGLcall("enable seamless cube map filtering"); + } + if (gl_info->supported[ARB_CLIP_CONTROL]) + GL_EXTCALL(glPointParameteri(GL_POINT_SPRITE_COORD_ORIGIN, GL_LOWER_LEFT)); + + /* If this happens to be the first context for the device, dummy textures + * are not created yet. In that case, they will be created (and bound) by + * create_dummy_textures right after this context is initialized. */ + if (wined3d_device_gl(device)->dummy_textures.tex_2d) + wined3d_context_gl_bind_dummy_textures(context_gl); + + /* Initialise all rectangles to avoid resetting unused ones later. */ + gl_info->gl_ops.gl.p_glScissor(0, 0, 0, 0); + checkGLcall("glScissor"); + + return WINED3D_OK; + +fail: + heap_free(context_gl->texture_type); + wined3d_release_dc(context_gl->window, context_gl->dc); + return E_FAIL; +} + +void wined3d_context_gl_destroy(struct wined3d_context_gl *context_gl) +{ + struct wined3d_device *device = context_gl->c.device; + + TRACE("Destroying context %p.\n", context_gl); + + wined3d_from_cs(device->cs); + + /* We delay destroying a context when it is active. The context_release() + * function invokes wined3d_context_gl_destroy() again while leaving the + * last level. */ + if (context_gl->level) + { + TRACE("Delaying destruction of context %p.\n", context_gl); + context_gl->c.destroy_delayed = 1; + /* FIXME: Get rid of a pointer to swapchain from wined3d_context. */ + context_gl->c.swapchain = NULL; + return; + } + + device_context_remove(device, &context_gl->c); + + if (context_gl->c.current && context_gl->tid != GetCurrentThreadId()) + { + struct wined3d_gl_info *gl_info; + + /* Make a copy of gl_info for wined3d_context_gl_cleanup() use, the + * one in wined3d_adapter may go away in the meantime. */ + gl_info = heap_alloc(sizeof(*gl_info)); + *gl_info = *context_gl->gl_info; + context_gl->gl_info = gl_info; + context_gl->c.destroyed = 1; + + return; + } + + wined3d_context_gl_cleanup(context_gl); + TlsSetValue(context_get_tls_idx(), NULL); + heap_free(context_gl); +} + +const unsigned int *wined3d_context_gl_get_tex_unit_mapping(const struct wined3d_context_gl *context_gl, + const struct wined3d_shader_version *shader_version, unsigned int *base, unsigned int *count) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + + if (!shader_version) + { + *base = 0; + *count = WINED3D_MAX_TEXTURES; + return context_gl->tex_unit_map; + } + + if (shader_version->major >= 4) + { + wined3d_gl_limits_get_texture_unit_range(&gl_info->limits, shader_version->type, base, count); + return NULL; + } + + switch (shader_version->type) + { + case WINED3D_SHADER_TYPE_PIXEL: + *base = 0; + *count = WINED3D_MAX_FRAGMENT_SAMPLERS; + break; + case WINED3D_SHADER_TYPE_VERTEX: + *base = WINED3D_MAX_FRAGMENT_SAMPLERS; + *count = WINED3D_MAX_VERTEX_SAMPLERS; + break; + default: + ERR("Unhandled shader type %#x.\n", shader_version->type); + *base = 0; + *count = 0; + } + + return context_gl->tex_unit_map; +} + +static void wined3d_context_gl_get_rt_size(const struct wined3d_context_gl *context_gl, SIZE *size) +{ + const struct wined3d_texture *rt = context_gl->c.current_rt.texture; + unsigned int level; + + if (rt->swapchain) + { + RECT window_size; + + GetClientRect(context_gl->window, &window_size); + size->cx = window_size.right - window_size.left; + size->cy = window_size.bottom - window_size.top; + + return; + } + + level = context_gl->c.current_rt.sub_resource_idx % rt->level_count; + size->cx = wined3d_texture_get_level_width(rt, level); + size->cy = wined3d_texture_get_level_height(rt, level); +} + +void wined3d_context_gl_enable_clip_distances(struct wined3d_context_gl *context_gl, uint32_t enable_mask) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + unsigned int clip_distance_count, i; + uint32_t disable_mask, current_mask; + + clip_distance_count = gl_info->limits.user_clip_distances; + disable_mask = ~enable_mask; + enable_mask &= (1u << clip_distance_count) - 1; + disable_mask &= (1u << clip_distance_count) - 1; + current_mask = context_gl->c.clip_distance_mask; + context_gl->c.clip_distance_mask = enable_mask; + + enable_mask &= ~current_mask; + while (enable_mask) + { + i = wined3d_bit_scan(&enable_mask); + gl_info->gl_ops.gl.p_glEnable(GL_CLIP_DISTANCE0 + i); + } + disable_mask &= current_mask; + while (disable_mask) + { + i = wined3d_bit_scan(&disable_mask); + gl_info->gl_ops.gl.p_glDisable(GL_CLIP_DISTANCE0 + i); + } + checkGLcall("toggle clip distances"); +} + +static inline BOOL is_rt_mask_onscreen(DWORD rt_mask) +{ + return rt_mask & (1u << 31); +} + +static inline GLenum draw_buffer_from_rt_mask(DWORD rt_mask) +{ + return rt_mask & ~(1u << 31); +} + +/* Context activation is done by the caller. */ +static void wined3d_context_gl_apply_draw_buffers(struct wined3d_context_gl *context_gl, uint32_t rt_mask) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + GLenum draw_buffers[WINED3D_MAX_RENDER_TARGETS]; + + if (!rt_mask) + { + gl_info->gl_ops.gl.p_glDrawBuffer(GL_NONE); + } + else if (is_rt_mask_onscreen(rt_mask)) + { + gl_info->gl_ops.gl.p_glDrawBuffer(draw_buffer_from_rt_mask(rt_mask)); + } + else + { + if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) + { + unsigned int i = 0; + + while (rt_mask) + { + if (rt_mask & 1) + draw_buffers[i] = GL_COLOR_ATTACHMENT0 + i; + else + draw_buffers[i] = GL_NONE; + + rt_mask >>= 1; + ++i; + } + + if (gl_info->supported[ARB_DRAW_BUFFERS]) + { + GL_EXTCALL(glDrawBuffers(i, draw_buffers)); + } + else + { + gl_info->gl_ops.gl.p_glDrawBuffer(draw_buffers[0]); + } + } + else + { + ERR("Unexpected draw buffers mask with backbuffer ORM.\n"); + } + } + + checkGLcall("apply draw buffers"); +} + +/* Context activation is done by the caller. */ +void wined3d_context_gl_set_draw_buffer(struct wined3d_context_gl *context_gl, GLenum buffer) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + struct fbo_entry *current_fbo = context_gl->current_fbo; + uint32_t new_mask = context_generate_rt_mask(buffer); + uint32_t *current_mask; + + current_mask = current_fbo ? ¤t_fbo->rt_mask : &context_gl->draw_buffers_mask; + if (new_mask == *current_mask) + return; + + gl_info->gl_ops.gl.p_glDrawBuffer(buffer); + checkGLcall("glDrawBuffer()"); + + *current_mask = new_mask; +} + +/* Context activation is done by the caller. */ +void wined3d_context_gl_active_texture(struct wined3d_context_gl *context_gl, + const struct wined3d_gl_info *gl_info, unsigned int unit) +{ + GL_EXTCALL(glActiveTexture(GL_TEXTURE0 + unit)); + checkGLcall("glActiveTexture"); + context_gl->active_texture = unit; +} + +void wined3d_context_gl_bind_bo(struct wined3d_context_gl *context_gl, GLenum binding, GLuint name) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + + if (binding == GL_ELEMENT_ARRAY_BUFFER) + context_invalidate_state(&context_gl->c, STATE_INDEXBUFFER); + + GL_EXTCALL(glBindBuffer(binding, name)); +} + +void wined3d_context_gl_bind_texture(struct wined3d_context_gl *context_gl, GLenum target, GLuint name) +{ + const struct wined3d_dummy_textures *textures = &wined3d_device_gl(context_gl->c.device)->dummy_textures; + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + GLenum old_texture_type; + unsigned int unit; + + if (name) + gl_info->gl_ops.gl.p_glBindTexture(target, name); + else + target = GL_NONE; + + unit = context_gl->active_texture; + old_texture_type = context_gl->texture_type[unit]; + if (old_texture_type != target) + { + switch (old_texture_type) + { + case GL_NONE: + /* nothing to do */ + break; + case GL_TEXTURE_1D: + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_1D, textures->tex_1d); + break; + case GL_TEXTURE_1D_ARRAY: + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_1D_ARRAY, textures->tex_1d_array); + break; + case GL_TEXTURE_2D: + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_2D, textures->tex_2d); + break; + case GL_TEXTURE_2D_ARRAY: + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_2D_ARRAY, textures->tex_2d_array); + break; + case GL_TEXTURE_RECTANGLE_ARB: + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_RECTANGLE_ARB, textures->tex_rect); + break; + case GL_TEXTURE_CUBE_MAP: + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_CUBE_MAP, textures->tex_cube); + break; + case GL_TEXTURE_CUBE_MAP_ARRAY: + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_CUBE_MAP_ARRAY, textures->tex_cube_array); + break; + case GL_TEXTURE_3D: + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_3D, textures->tex_3d); + break; + case GL_TEXTURE_BUFFER: + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_BUFFER, textures->tex_buffer); + break; + case GL_TEXTURE_2D_MULTISAMPLE: + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, textures->tex_2d_ms); + break; + case GL_TEXTURE_2D_MULTISAMPLE_ARRAY: + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, textures->tex_2d_ms_array); + break; + default: + ERR("Unexpected texture target %#x.\n", old_texture_type); + } + + context_gl->texture_type[unit] = target; + } + + checkGLcall("bind texture"); +} + +static void wined3d_context_gl_poll_fences(struct wined3d_context_gl *context_gl) +{ + struct wined3d_device_gl *device_gl = wined3d_device_gl(context_gl->c.device); + struct wined3d_command_fence_gl *f; + SIZE_T i; + + for (i = 0; i < context_gl->submitted.fence_count; ++i) + { + f = &context_gl->submitted.fences[i]; + + if (f->id > device_gl->completed_fence_id) + { + if (wined3d_fence_test(f->fence, &device_gl->d, 0) != WINED3D_FENCE_OK) + continue; + device_gl->completed_fence_id = f->id; + } + + wined3d_fence_destroy(f->fence); + if (i != context_gl->submitted.fence_count - 1) + *f = context_gl->submitted.fences[context_gl->submitted.fence_count - 1]; + --context_gl->submitted.fence_count; + } +} + +void wined3d_context_gl_wait_command_fence(struct wined3d_context_gl *context_gl, uint64_t id) +{ + struct wined3d_device_gl *device_gl = wined3d_device_gl(context_gl->c.device); + enum wined3d_fence_result ret; + SIZE_T i; + + if (id <= device_gl->completed_fence_id + || id > device_gl->current_fence_id) /* In case the fence ID wrapped. */ + return; + + for (i = 0; i < context_gl->submitted.fence_count; ++i) + { + if (context_gl->submitted.fences[i].id != id) + continue; + + if ((ret = wined3d_fence_wait(context_gl->submitted.fences[i].fence, &device_gl->d)) != WINED3D_FENCE_OK) + ERR("Failed to wait for command fence with id 0x%s, ret %#x.\n", wine_dbgstr_longlong(id), ret); + wined3d_context_gl_poll_fences(context_gl); + return; + } + + ERR("Failed to find fence for command fence with id 0x%s.\n", wine_dbgstr_longlong(id)); +} + +void wined3d_context_gl_submit_command_fence(struct wined3d_context_gl *context_gl) +{ + struct wined3d_device_gl *device_gl = wined3d_device_gl(context_gl->c.device); + struct wined3d_command_fence_gl *f; + HRESULT hr; + + if (!wined3d_array_reserve((void **)&context_gl->submitted.fences, &context_gl->submitted.fences_size, + context_gl->submitted.fence_count + 1, sizeof(*context_gl->submitted.fences))) + ERR("Failed to grow submitted command buffer array.\n"); + + f = &context_gl->submitted.fences[context_gl->submitted.fence_count++]; + f->id = device_gl->current_fence_id; + if (FAILED(hr = wined3d_fence_create(&device_gl->d, &f->fence))) + ERR("Failed to create fence, hr %#x.\n", hr); + wined3d_fence_issue(f->fence, &device_gl->d); + + /* We don't expect this to ever happen, but handle it anyway. */ + if (!++device_gl->current_fence_id) + { + wined3d_context_gl_wait_command_fence(context_gl, device_gl->current_fence_id - 1); + device_gl->completed_fence_id = 0; + device_gl->current_fence_id = 1; + } + wined3d_context_gl_poll_fences(context_gl); +} + +static void *wined3d_bo_gl_map(struct wined3d_bo_gl *bo, + struct wined3d_context_gl *context_gl, size_t offset, size_t size, uint32_t flags) +{ + struct wined3d_device_gl *device_gl = wined3d_device_gl(context_gl->c.device); + const struct wined3d_gl_info *gl_info; + struct wined3d_bo_user *bo_user; + struct wined3d_bo_gl tmp; + uint8_t *map_ptr; + + if (flags & WINED3D_MAP_NOOVERWRITE) + goto map; + + if ((flags & WINED3D_MAP_DISCARD) && bo->command_fence_id > device_gl->completed_fence_id) + { + if (wined3d_context_gl_create_bo(context_gl, bo->size, + bo->binding, bo->usage, bo->coherent, bo->flags, &tmp)) + { + list_move_head(&tmp.users, &bo->users); + wined3d_context_gl_destroy_bo(context_gl, bo); + *bo = tmp; + list_init(&bo->users); + list_move_head(&bo->users, &tmp.users); + LIST_FOR_EACH_ENTRY(bo_user, &bo->users, struct wined3d_bo_user, entry) + { + bo_user->valid = false; + } + + goto map; + } + + ERR("Failed to create new buffer object.\n"); + } + + if (bo->command_fence_id == device_gl->current_fence_id) + wined3d_context_gl_submit_command_fence(context_gl); + wined3d_context_gl_wait_command_fence(context_gl, bo->command_fence_id); + +map: + gl_info = context_gl->gl_info; + wined3d_context_gl_bind_bo(context_gl, bo->binding, bo->id); + + if (gl_info->supported[ARB_MAP_BUFFER_RANGE]) + { + map_ptr = GL_EXTCALL(glMapBufferRange(bo->binding, offset, size, wined3d_resource_gl_map_flags(flags))); + } + else + { + map_ptr = GL_EXTCALL(glMapBuffer(bo->binding, wined3d_resource_gl_legacy_map_flags(flags))); + map_ptr += offset; + } + + wined3d_context_gl_bind_bo(context_gl, bo->binding, 0); + checkGLcall("Map buffer object"); + + return map_ptr; +} + +void *wined3d_context_gl_map_bo_address(struct wined3d_context_gl *context_gl, + const struct wined3d_bo_address *data, size_t size, uint32_t flags) +{ + struct wined3d_bo_gl *bo; + void *map_ptr; + + if (!(bo = (struct wined3d_bo_gl *)data->buffer_object)) + return data->addr; + + if (!(map_ptr = wined3d_bo_gl_map(bo, context_gl, (uintptr_t)data->addr, size, flags))) + ERR("Failed to map bo.\n"); + + return map_ptr; +} + +void wined3d_context_gl_unmap_bo_address(struct wined3d_context_gl *context_gl, + const struct wined3d_bo_address *data, unsigned int range_count, const struct wined3d_range *ranges) +{ + const struct wined3d_gl_info *gl_info; + struct wined3d_bo_gl *bo; + unsigned int i; + + if (!(bo = (struct wined3d_bo_gl *)data->buffer_object)) + return; + + gl_info = context_gl->gl_info; + wined3d_context_gl_bind_bo(context_gl, bo->binding, bo->id); + + if (gl_info->supported[ARB_MAP_BUFFER_RANGE]) + { + for (i = 0; i < range_count; ++i) + { + GL_EXTCALL(glFlushMappedBufferRange(bo->binding, + (UINT_PTR)data->addr + ranges[i].offset, ranges[i].size)); + } + } + else if (!bo->coherent && gl_info->supported[APPLE_FLUSH_BUFFER_RANGE]) + { + for (i = 0; i < range_count; ++i) + { + GL_EXTCALL(glFlushMappedBufferRangeAPPLE(bo->binding, + (uintptr_t)data->addr + ranges[i].offset, ranges[i].size)); + checkGLcall("glFlushMappedBufferRangeAPPLE"); + } + } + + GL_EXTCALL(glUnmapBuffer(bo->binding)); + wined3d_context_gl_bind_bo(context_gl, bo->binding, 0); + checkGLcall("Unmap buffer object"); +} + +void wined3d_context_gl_copy_bo_address(struct wined3d_context_gl *context_gl, + const struct wined3d_bo_address *dst, const struct wined3d_bo_address *src, size_t size) +{ + const struct wined3d_gl_info *gl_info; + struct wined3d_bo_gl *src_bo, *dst_bo; + struct wined3d_range range; + BYTE *dst_ptr, *src_ptr; + + gl_info = context_gl->gl_info; + src_bo = (struct wined3d_bo_gl *)src->buffer_object; + dst_bo = (struct wined3d_bo_gl *)dst->buffer_object; + + if (dst_bo && src_bo) + { + if (gl_info->supported[ARB_COPY_BUFFER]) + { + GL_EXTCALL(glBindBuffer(GL_COPY_READ_BUFFER, src_bo->id)); + GL_EXTCALL(glBindBuffer(GL_COPY_WRITE_BUFFER, dst_bo->id)); + GL_EXTCALL(glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, + (GLintptr)src->addr, (GLintptr)dst->addr, size)); + checkGLcall("direct buffer copy"); + + wined3d_context_gl_reference_bo(context_gl, src_bo); + wined3d_context_gl_reference_bo(context_gl, dst_bo); + } + else + { + src_ptr = wined3d_context_gl_map_bo_address(context_gl, src, size, WINED3D_MAP_READ); + dst_ptr = wined3d_context_gl_map_bo_address(context_gl, dst, size, WINED3D_MAP_WRITE); + + memcpy(dst_ptr, src_ptr, size); + + range.offset = 0; + range.size = size; + wined3d_context_gl_unmap_bo_address(context_gl, dst, 1, &range); + wined3d_context_gl_unmap_bo_address(context_gl, src, 0, NULL); + } + } + else if (!dst_bo && src_bo) + { + wined3d_context_gl_bind_bo(context_gl, src_bo->binding, src_bo->id); + GL_EXTCALL(glGetBufferSubData(src_bo->binding, (GLintptr)src->addr, size, dst->addr)); + checkGLcall("buffer download"); + + wined3d_context_gl_reference_bo(context_gl, src_bo); + } + else if (dst_bo && !src_bo) + { + wined3d_context_gl_bind_bo(context_gl, dst_bo->binding, dst_bo->id); + GL_EXTCALL(glBufferSubData(dst_bo->binding, (GLintptr)dst->addr, size, src->addr)); + checkGLcall("buffer upload"); + + wined3d_context_gl_reference_bo(context_gl, dst_bo); + } + else + { + memcpy(dst->addr, src->addr, size); + } +} + +void wined3d_context_gl_destroy_bo(struct wined3d_context_gl *context_gl, struct wined3d_bo_gl *bo) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + + TRACE("context_gl %p, bo %p.\n", context_gl, bo); + + TRACE("Destroying GL buffer %u.\n", bo->id); + GL_EXTCALL(glDeleteBuffers(1, &bo->id)); + checkGLcall("buffer object destruction"); + bo->id = 0; +} + +bool wined3d_context_gl_create_bo(struct wined3d_context_gl *context_gl, GLsizeiptr size, + GLenum binding, GLenum usage, bool coherent, GLbitfield flags, struct wined3d_bo_gl *bo) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + GLuint id = 0; + + TRACE("context_gl %p, size %lu, binding %#x, usage %#x, coherent %#x, flags %#x, bo %p.\n", + context_gl, size, binding, usage, coherent, flags, bo); + + GL_EXTCALL(glGenBuffers(1, &id)); + if (!id) + { + checkGLcall("buffer object creation"); + return false; + } + wined3d_context_gl_bind_bo(context_gl, binding, id); + + if (!coherent && gl_info->supported[APPLE_FLUSH_BUFFER_RANGE]) + { + GL_EXTCALL(glBufferParameteriAPPLE(binding, GL_BUFFER_FLUSHING_UNMAP_APPLE, GL_FALSE)); + GL_EXTCALL(glBufferParameteriAPPLE(binding, GL_BUFFER_SERIALIZED_MODIFY_APPLE, GL_FALSE)); + } + + if (gl_info->supported[ARB_BUFFER_STORAGE]) + GL_EXTCALL(glBufferStorage(binding, size, NULL, flags | GL_DYNAMIC_STORAGE_BIT)); + else + GL_EXTCALL(glBufferData(binding, size, NULL, usage)); + + wined3d_context_gl_bind_bo(context_gl, binding, 0); + checkGLcall("buffer object creation"); + + TRACE("Created buffer object %u.\n", id); + bo->id = id; + bo->size = size; + bo->binding = binding; + bo->usage = usage; + bo->flags = flags; + bo->coherent = coherent; + list_init(&bo->users); + bo->command_fence_id = 0; + + return true; +} + +static void wined3d_context_gl_set_render_offscreen(struct wined3d_context_gl *context_gl, BOOL offscreen) +{ + if (context_gl->c.render_offscreen == offscreen) + return; + + context_invalidate_state(&context_gl->c, STATE_VIEWPORT); + context_invalidate_state(&context_gl->c, STATE_SCISSORRECT); + if (!context_gl->gl_info->supported[ARB_CLIP_CONTROL]) + { + context_invalidate_state(&context_gl->c, STATE_RASTERIZER); + context_invalidate_state(&context_gl->c, STATE_POINTSPRITECOORDORIGIN); + context_invalidate_state(&context_gl->c, STATE_TRANSFORM(WINED3D_TS_PROJECTION)); + } + context_invalidate_state(&context_gl->c, STATE_SHADER(WINED3D_SHADER_TYPE_DOMAIN)); + if (context_gl->gl_info->supported[ARB_FRAGMENT_COORD_CONVENTIONS]) + context_invalidate_state(&context_gl->c, STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL)); + context_gl->c.render_offscreen = offscreen; +} + +GLenum wined3d_context_gl_get_offscreen_gl_buffer(const struct wined3d_context_gl *context_gl) +{ + switch (wined3d_settings.offscreen_rendering_mode) + { + case ORM_FBO: + return GL_COLOR_ATTACHMENT0; + + case ORM_BACKBUFFER: + return context_gl->aux_buffers > 0 ? GL_AUX0 : GL_BACK; + + default: + FIXME("Unhandled offscreen rendering mode %#x.\n", wined3d_settings.offscreen_rendering_mode); + return GL_BACK; + } +} + +static uint32_t wined3d_context_gl_generate_rt_mask_no_fbo(const struct wined3d_context_gl *context_gl, + struct wined3d_resource *rt) +{ + if (!rt || rt->format->id == WINED3DFMT_NULL) + return 0; + else if (rt->type != WINED3D_RTYPE_BUFFER && texture_from_resource(rt)->swapchain) + return context_generate_rt_mask_from_resource(rt); + else + return context_generate_rt_mask(wined3d_context_gl_get_offscreen_gl_buffer(context_gl)); +} + +/* Context activation is done by the caller. */ +void wined3d_context_gl_apply_blit_state(struct wined3d_context_gl *context_gl, const struct wined3d_device *device) +{ + struct wined3d_context *context = &context_gl->c; + const struct wined3d_gl_info *gl_info; + uint32_t rt_mask, *cur_mask; + struct wined3d_texture *rt; + unsigned int sampler; + SIZE rt_size; + + TRACE("Setting up context %p for blitting.\n", context); + + gl_info = context_gl->gl_info; + rt = context->current_rt.texture; + + if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) + { + if (context->render_offscreen) + { + wined3d_texture_load(rt, context, FALSE); + + wined3d_context_gl_apply_fbo_state_blit(context_gl, GL_FRAMEBUFFER, &rt->resource, + context->current_rt.sub_resource_idx, NULL, 0, rt->resource.draw_binding); + if (rt->resource.format->id != WINED3DFMT_NULL) + rt_mask = 1; + else + rt_mask = 0; + } + else + { + context_gl->current_fbo = NULL; + wined3d_context_gl_bind_fbo(context_gl, GL_FRAMEBUFFER, 0); + rt_mask = context_generate_rt_mask_from_resource(&rt->resource); + } + } + else + { + rt_mask = wined3d_context_gl_generate_rt_mask_no_fbo(context_gl, &rt->resource); + } + + cur_mask = context_gl->current_fbo ? &context_gl->current_fbo->rt_mask : &context_gl->draw_buffers_mask; + + if (rt_mask != *cur_mask) + { + wined3d_context_gl_apply_draw_buffers(context_gl, rt_mask); + *cur_mask = rt_mask; + } + + if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) + wined3d_context_gl_check_fbo_status(context_gl, GL_FRAMEBUFFER); + context_invalidate_state(context, STATE_FRAMEBUFFER); + + wined3d_context_gl_get_rt_size(context_gl, &rt_size); + + if (context->last_was_blit) + { + if (context_gl->blit_size.cx != rt_size.cx || context_gl->blit_size.cy != rt_size.cy) + { + gl_info->gl_ops.gl.p_glViewport(0, 0, rt_size.cx, rt_size.cy); + context->viewport_count = WINED3D_MAX_VIEWPORTS; + context_gl->blit_size = rt_size; + /* No need to dirtify here, the states are still dirtified because + * they weren't applied since the last context_apply_blit_state() + * call. */ + } + checkGLcall("blit state application"); + TRACE("Context is already set up for blitting, nothing to do.\n"); + return; + } + context->last_was_blit = TRUE; + + if (gl_info->supported[ARB_SAMPLER_OBJECTS]) + GL_EXTCALL(glBindSampler(0, 0)); + wined3d_context_gl_active_texture(context_gl, gl_info, 0); + + sampler = context_gl->rev_tex_unit_map[0]; + if (sampler != WINED3D_UNMAPPED_STAGE) + { + if (sampler < WINED3D_MAX_TEXTURES) + { + context_invalidate_state(context, STATE_TRANSFORM(WINED3D_TS_TEXTURE0 + sampler)); + context_invalidate_state(context, STATE_TEXTURESTAGE(sampler, WINED3D_TSS_COLOR_OP)); + } + context_invalidate_state(context, STATE_SAMPLER(sampler)); + } + context_invalidate_compute_state(context, STATE_COMPUTE_SHADER_RESOURCE_BINDING); + context_invalidate_state(context, STATE_GRAPHICS_SHADER_RESOURCE_BINDING); + + if (gl_info->supported[WINED3D_GL_LEGACY_CONTEXT]) + { + gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST); + context_invalidate_state(context, STATE_RENDER(WINED3D_RS_ALPHATESTENABLE)); + } + gl_info->gl_ops.gl.p_glDisable(GL_BLEND); + gl_info->gl_ops.gl.p_glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + context_invalidate_state(context, STATE_BLEND); + gl_info->gl_ops.gl.p_glDisable(GL_CULL_FACE); + gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST); + context_invalidate_state(context, STATE_RASTERIZER); + gl_info->gl_ops.gl.p_glDisable(GL_DEPTH_TEST); + gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST); + context_invalidate_state(context, STATE_DEPTH_STENCIL); + if (gl_info->supported[ARB_POINT_SPRITE]) + { + gl_info->gl_ops.gl.p_glDisable(GL_POINT_SPRITE_ARB); + context_invalidate_state(context, STATE_RENDER(WINED3D_RS_POINTSPRITEENABLE)); + } + if (gl_info->supported[ARB_FRAMEBUFFER_SRGB]) + { + gl_info->gl_ops.gl.p_glDisable(GL_FRAMEBUFFER_SRGB); + context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SRGBWRITEENABLE)); + } + + context->last_was_rhw = TRUE; + context_invalidate_state(context, STATE_VDECL); /* because of last_was_rhw = TRUE */ + + wined3d_context_gl_enable_clip_distances(context_gl, 0); + context_invalidate_state(context, STATE_RENDER(WINED3D_RS_CLIPPING)); + + /* FIXME: Make draw_textured_quad() able to work with a upper left origin. */ + if (gl_info->supported[ARB_CLIP_CONTROL]) + GL_EXTCALL(glClipControl(GL_LOWER_LEFT, GL_NEGATIVE_ONE_TO_ONE)); + gl_info->gl_ops.gl.p_glViewport(0, 0, rt_size.cx, rt_size.cy); + context->viewport_count = WINED3D_MAX_VIEWPORTS; + context_invalidate_state(context, STATE_VIEWPORT); + + device->shader_backend->shader_disable(device->shader_priv, context); + + context_gl->blit_size = rt_size; + + checkGLcall("blit state application"); +} + +static void wined3d_context_gl_apply_blit_projection(const struct wined3d_context_gl *context_gl, + unsigned int w, unsigned int h) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + const GLdouble projection[] = + { + 2.0 / w, 0.0, 0.0, 0.0, + 0.0, 2.0 / h, 0.0, 0.0, + 0.0, 0.0, 2.0, 0.0, + -1.0, -1.0, -1.0, 1.0, + }; + + gl_info->gl_ops.gl.p_glMatrixMode(GL_PROJECTION); + gl_info->gl_ops.gl.p_glLoadMatrixd(projection); +} + +/* Setup OpenGL states for fixed-function blitting. */ +/* Context activation is done by the caller. */ +void wined3d_context_gl_apply_ffp_blit_state(struct wined3d_context_gl *context_gl, + const struct wined3d_device *device) +{ + struct wined3d_context *context = &context_gl->c; + const struct wined3d_gl_info *gl_info; + unsigned int i, sampler; + + gl_info = context_gl->gl_info; + if (!gl_info->supported[WINED3D_GL_LEGACY_CONTEXT]) + ERR("Applying fixed-function state without legacy context support.\n"); + + if (context->last_was_ffp_blit) + { + SIZE rt_size; + + wined3d_context_gl_get_rt_size(context_gl, &rt_size); + if (context_gl->blit_size.cx != rt_size.cx || context_gl->blit_size.cy != rt_size.cy) + wined3d_context_gl_apply_blit_projection(context_gl, rt_size.cx, rt_size.cy); + wined3d_context_gl_apply_blit_state(context_gl, device); + + checkGLcall("ffp blit state application"); + return; + } + context->last_was_ffp_blit = TRUE; + + wined3d_context_gl_apply_blit_state(context_gl, device); + + /* Disable all textures. The caller can then bind a texture it wants to blit + * from. */ + for (i = gl_info->limits.textures - 1; i > 0 ; --i) + { + wined3d_context_gl_active_texture(context_gl, gl_info, i); + + if (gl_info->supported[ARB_TEXTURE_CUBE_MAP]) + gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_CUBE_MAP_ARB); + gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_3D); + if (gl_info->supported[ARB_TEXTURE_RECTANGLE]) + gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_RECTANGLE_ARB); + gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_2D); + + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + + sampler = context_gl->rev_tex_unit_map[i]; + if (sampler != WINED3D_UNMAPPED_STAGE) + { + if (sampler < WINED3D_MAX_TEXTURES) + context_invalidate_state(context, STATE_TEXTURESTAGE(sampler, WINED3D_TSS_COLOR_OP)); + context_invalidate_state(context, STATE_SAMPLER(sampler)); + } + } + + wined3d_context_gl_active_texture(context_gl, gl_info, 0); + + if (gl_info->supported[ARB_TEXTURE_CUBE_MAP]) + gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_CUBE_MAP_ARB); + gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_3D); + if (gl_info->supported[ARB_TEXTURE_RECTANGLE]) + gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_RECTANGLE_ARB); + gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_2D); + + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + if (gl_info->supported[EXT_TEXTURE_LOD_BIAS]) + gl_info->gl_ops.gl.p_glTexEnvf(GL_TEXTURE_FILTER_CONTROL_EXT, GL_TEXTURE_LOD_BIAS_EXT, 0.0f); + + gl_info->gl_ops.gl.p_glMatrixMode(GL_TEXTURE); + gl_info->gl_ops.gl.p_glLoadIdentity(); + + /* Setup transforms. */ + gl_info->gl_ops.gl.p_glMatrixMode(GL_MODELVIEW); + gl_info->gl_ops.gl.p_glLoadIdentity(); + context_invalidate_state(context, STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(0))); + wined3d_context_gl_apply_blit_projection(context_gl, context_gl->blit_size.cx, context_gl->blit_size.cy); + context_invalidate_state(context, STATE_TRANSFORM(WINED3D_TS_PROJECTION)); + + /* Other misc states. */ + gl_info->gl_ops.gl.p_glDisable(GL_LIGHTING); + context_invalidate_state(context, STATE_RENDER(WINED3D_RS_LIGHTING)); + gl_info->p_glDisableWINE(GL_FOG); + context_invalidate_state(context, STATE_RENDER(WINED3D_RS_FOGENABLE)); + + if (gl_info->supported[EXT_SECONDARY_COLOR]) + { + gl_info->gl_ops.gl.p_glDisable(GL_COLOR_SUM_EXT); + context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SPECULARENABLE)); + } + checkGLcall("ffp blit state application"); +} + +static BOOL have_framebuffer_attachment(unsigned int rt_count, struct wined3d_rendertarget_view * const *rts, + const struct wined3d_rendertarget_view *ds) +{ + unsigned int i; + + if (ds) + return TRUE; + + for (i = 0; i < rt_count; ++i) + { + if (rts[i] && rts[i]->format->id != WINED3DFMT_NULL) + return TRUE; + } + + return FALSE; +} + +/* Context activation is done by the caller. */ +BOOL wined3d_context_gl_apply_clear_state(struct wined3d_context_gl *context_gl, + const struct wined3d_state *state, unsigned int rt_count, const struct wined3d_fb_state *fb) +{ + struct wined3d_rendertarget_view * const *rts = fb->render_targets; + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + struct wined3d_rendertarget_view *dsv = fb->depth_stencil; + uint32_t rt_mask = 0, *cur_mask; + unsigned int i; + + if (isStateDirty(&context_gl->c, STATE_FRAMEBUFFER) || fb != &state->fb + || rt_count != gl_info->limits.buffers) + { + if (!have_framebuffer_attachment(rt_count, rts, dsv)) + { + WARN("Invalid render target config, need at least one attachment.\n"); + return FALSE; + } + + if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) + { + struct wined3d_rendertarget_info ds_info = {{0}}; + + if (!rt_count || wined3d_resource_is_offscreen(rts[0]->resource)) + { + memset(context_gl->blit_targets, 0, sizeof(context_gl->blit_targets)); + for (i = 0; i < rt_count; ++i) + { + if (rts[i]) + { + struct wined3d_rendertarget_view_gl *rtv_gl = wined3d_rendertarget_view_gl(rts[i]); + context_gl->blit_targets[i].gl_view = rtv_gl->gl_view; + context_gl->blit_targets[i].resource = rtv_gl->v.resource; + context_gl->blit_targets[i].sub_resource_idx = rtv_gl->v.sub_resource_idx; + context_gl->blit_targets[i].layer_count = rtv_gl->v.layer_count; + } + if (rts[i] && rts[i]->format->id != WINED3DFMT_NULL) + rt_mask |= (1u << i); + } + + if (dsv) + { + struct wined3d_rendertarget_view_gl *dsv_gl = wined3d_rendertarget_view_gl(dsv); + ds_info.gl_view = dsv_gl->gl_view; + ds_info.resource = dsv_gl->v.resource; + ds_info.sub_resource_idx = dsv_gl->v.sub_resource_idx; + ds_info.layer_count = dsv_gl->v.layer_count; + } + + wined3d_context_gl_apply_fbo_state(context_gl, GL_FRAMEBUFFER, context_gl->blit_targets, &ds_info, + rt_count ? rts[0]->resource->draw_binding : 0, dsv ? dsv->resource->draw_binding : 0); + } + else + { + wined3d_context_gl_apply_fbo_state(context_gl, GL_FRAMEBUFFER, NULL, &ds_info, + WINED3D_LOCATION_DRAWABLE, WINED3D_LOCATION_DRAWABLE); + rt_mask = context_generate_rt_mask_from_resource(rts[0]->resource); + } + + /* If the framebuffer is not the device's fb the device's fb has to be reapplied + * next draw. Otherwise we could mark the framebuffer state clean here, once the + * state management allows this */ + context_invalidate_state(&context_gl->c, STATE_FRAMEBUFFER); + } + else + { + rt_mask = wined3d_context_gl_generate_rt_mask_no_fbo(context_gl, rt_count ? rts[0]->resource : NULL); + } + } + else if (wined3d_settings.offscreen_rendering_mode == ORM_FBO + && (!rt_count || wined3d_resource_is_offscreen(rts[0]->resource))) + { + for (i = 0; i < rt_count; ++i) + { + if (rts[i] && rts[i]->format->id != WINED3DFMT_NULL) + rt_mask |= (1u << i); + } + } + else + { + rt_mask = wined3d_context_gl_generate_rt_mask_no_fbo(context_gl, rt_count ? rts[0]->resource : NULL); + } + + cur_mask = context_gl->current_fbo ? &context_gl->current_fbo->rt_mask : &context_gl->draw_buffers_mask; + + if (rt_mask != *cur_mask) + { + wined3d_context_gl_apply_draw_buffers(context_gl, rt_mask); + *cur_mask = rt_mask; + context_invalidate_state(&context_gl->c, STATE_FRAMEBUFFER); + } + + if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) + wined3d_context_gl_check_fbo_status(context_gl, GL_FRAMEBUFFER); + + context_gl->c.last_was_blit = FALSE; + context_gl->c.last_was_ffp_blit = FALSE; + + /* Blending and clearing should be orthogonal, but tests on the nvidia + * driver show that disabling blending when clearing improves the clearing + * performance incredibly. */ + gl_info->gl_ops.gl.p_glDisable(GL_BLEND); + gl_info->gl_ops.gl.p_glEnable(GL_SCISSOR_TEST); + if (rt_count && gl_info->supported[ARB_FRAMEBUFFER_SRGB]) + { + if (needs_srgb_write(context_gl->c.d3d_info, state, fb)) + gl_info->gl_ops.gl.p_glEnable(GL_FRAMEBUFFER_SRGB); + else + gl_info->gl_ops.gl.p_glDisable(GL_FRAMEBUFFER_SRGB); + context_invalidate_state(&context_gl->c, STATE_RENDER(WINED3D_RS_SRGBWRITEENABLE)); + } + checkGLcall("setting up state for clear"); + + context_invalidate_state(&context_gl->c, STATE_BLEND); + context_invalidate_state(&context_gl->c, STATE_RASTERIZER); + context_invalidate_state(&context_gl->c, STATE_SCISSORRECT); + + return TRUE; +} + +static uint32_t find_draw_buffers_mask(const struct wined3d_context_gl *context_gl, const struct wined3d_state *state) +{ + struct wined3d_rendertarget_view * const *rts = state->fb.render_targets; + struct wined3d_shader *ps = state->shader[WINED3D_SHADER_TYPE_PIXEL]; + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + unsigned int rt_mask, mask; + unsigned int i; + + if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) + return wined3d_context_gl_generate_rt_mask_no_fbo(context_gl, rts[0]->resource); + else if (!context_gl->c.render_offscreen) + return context_generate_rt_mask_from_resource(rts[0]->resource); + + rt_mask = ps ? ps->reg_maps.rt_mask : 1; + rt_mask &= (1u << gl_info->limits.buffers) - 1; + if (state->blend_state && state->blend_state->dual_source) + rt_mask = 1; + + mask = rt_mask; + while (mask) + { + i = wined3d_bit_scan(&mask); + if (!rts[i] || rts[i]->format->id == WINED3DFMT_NULL) + rt_mask &= ~(1u << i); + } + + return rt_mask; +} + +/* Context activation is done by the caller. */ +void context_state_fb(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + uint32_t rt_mask = find_draw_buffers_mask(context_gl, state); + const struct wined3d_fb_state *fb = &state->fb; + DWORD color_location = 0; + DWORD *cur_mask; + + if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) + { + struct wined3d_rendertarget_info ds_info = {{0}}; + + if (!context->render_offscreen) + { + wined3d_context_gl_apply_fbo_state(context_gl, GL_FRAMEBUFFER, NULL, &ds_info, + WINED3D_LOCATION_DRAWABLE, WINED3D_LOCATION_DRAWABLE); + } + else + { + const struct wined3d_rendertarget_view_gl *view_gl; + unsigned int i; + + memset(context_gl->blit_targets, 0, sizeof(context_gl->blit_targets)); + for (i = 0; i < context_gl->gl_info->limits.buffers; ++i) + { + if (!fb->render_targets[i]) + continue; + + view_gl = wined3d_rendertarget_view_gl(fb->render_targets[i]); + context_gl->blit_targets[i].gl_view = view_gl->gl_view; + context_gl->blit_targets[i].resource = view_gl->v.resource; + context_gl->blit_targets[i].sub_resource_idx = view_gl->v.sub_resource_idx; + context_gl->blit_targets[i].layer_count = view_gl->v.layer_count; + + if (!color_location) + color_location = view_gl->v.resource->draw_binding; + } + + if (fb->depth_stencil) + { + view_gl = wined3d_rendertarget_view_gl(fb->depth_stencil); + ds_info.gl_view = view_gl->gl_view; + ds_info.resource = view_gl->v.resource; + ds_info.sub_resource_idx = view_gl->v.sub_resource_idx; + ds_info.layer_count = view_gl->v.layer_count; + } + + wined3d_context_gl_apply_fbo_state(context_gl, GL_FRAMEBUFFER, context_gl->blit_targets, &ds_info, + color_location, fb->depth_stencil ? fb->depth_stencil->resource->draw_binding : 0); + } + } + + cur_mask = context_gl->current_fbo ? &context_gl->current_fbo->rt_mask : &context_gl->draw_buffers_mask; + if (rt_mask != *cur_mask) + { + wined3d_context_gl_apply_draw_buffers(context_gl, rt_mask); + *cur_mask = rt_mask; + } + context->constant_update_mask |= WINED3D_SHADER_CONST_PS_Y_CORR; +} + +static void wined3d_context_gl_map_stage(struct wined3d_context_gl *context_gl, unsigned int stage, unsigned int unit) +{ + unsigned int i = context_gl->rev_tex_unit_map[unit]; + unsigned int j = context_gl->tex_unit_map[stage]; + + TRACE("Mapping stage %u to unit %u.\n", stage, unit); + context_gl->tex_unit_map[stage] = unit; + if (i != WINED3D_UNMAPPED_STAGE && i != stage) + context_gl->tex_unit_map[i] = WINED3D_UNMAPPED_STAGE; + + context_gl->rev_tex_unit_map[unit] = stage; + if (j != WINED3D_UNMAPPED_STAGE && j != unit) + context_gl->rev_tex_unit_map[j] = WINED3D_UNMAPPED_STAGE; +} + +static void context_invalidate_texture_stage(struct wined3d_context *context, DWORD stage) +{ + DWORD i; + + for (i = 0; i <= WINED3D_HIGHEST_TEXTURE_STATE; ++i) + context_invalidate_state(context, STATE_TEXTURESTAGE(stage, i)); +} + +static void context_update_fixed_function_usage_map(struct wined3d_context *context, + const struct wined3d_state *state) +{ + UINT i, start, end; + + context->fixed_function_usage_map = 0; + for (i = 0; i < WINED3D_MAX_TEXTURES; ++i) + { + enum wined3d_texture_op color_op = state->texture_states[i][WINED3D_TSS_COLOR_OP]; + enum wined3d_texture_op alpha_op = state->texture_states[i][WINED3D_TSS_ALPHA_OP]; + DWORD color_arg1 = state->texture_states[i][WINED3D_TSS_COLOR_ARG1] & WINED3DTA_SELECTMASK; + DWORD color_arg2 = state->texture_states[i][WINED3D_TSS_COLOR_ARG2] & WINED3DTA_SELECTMASK; + DWORD color_arg3 = state->texture_states[i][WINED3D_TSS_COLOR_ARG0] & WINED3DTA_SELECTMASK; + DWORD alpha_arg1 = state->texture_states[i][WINED3D_TSS_ALPHA_ARG1] & WINED3DTA_SELECTMASK; + DWORD alpha_arg2 = state->texture_states[i][WINED3D_TSS_ALPHA_ARG2] & WINED3DTA_SELECTMASK; + DWORD alpha_arg3 = state->texture_states[i][WINED3D_TSS_ALPHA_ARG0] & WINED3DTA_SELECTMASK; + + /* Not used, and disable higher stages. */ + if (color_op == WINED3D_TOP_DISABLE) + break; + + if (((color_arg1 == WINED3DTA_TEXTURE) && color_op != WINED3D_TOP_SELECT_ARG2) + || ((color_arg2 == WINED3DTA_TEXTURE) && color_op != WINED3D_TOP_SELECT_ARG1) + || ((color_arg3 == WINED3DTA_TEXTURE) + && (color_op == WINED3D_TOP_MULTIPLY_ADD || color_op == WINED3D_TOP_LERP)) + || ((alpha_arg1 == WINED3DTA_TEXTURE) && alpha_op != WINED3D_TOP_SELECT_ARG2) + || ((alpha_arg2 == WINED3DTA_TEXTURE) && alpha_op != WINED3D_TOP_SELECT_ARG1) + || ((alpha_arg3 == WINED3DTA_TEXTURE) + && (alpha_op == WINED3D_TOP_MULTIPLY_ADD || alpha_op == WINED3D_TOP_LERP))) + context->fixed_function_usage_map |= (1u << i); + + if ((color_op == WINED3D_TOP_BUMPENVMAP || color_op == WINED3D_TOP_BUMPENVMAP_LUMINANCE) + && i < WINED3D_MAX_TEXTURES - 1) + context->fixed_function_usage_map |= (1u << (i + 1)); + } + + if (i < context->lowest_disabled_stage) + { + start = i; + end = context->lowest_disabled_stage; + } + else + { + start = context->lowest_disabled_stage; + end = i; + } + + context->lowest_disabled_stage = i; + for (i = start + 1; i < end; ++i) + { + context_invalidate_state(context, STATE_TEXTURESTAGE(i, WINED3D_TSS_COLOR_OP)); + } +} + +static void wined3d_context_gl_map_fixed_function_samplers(struct wined3d_context_gl *context_gl, + const struct wined3d_state *state) +{ + const struct wined3d_d3d_info *d3d_info = context_gl->c.d3d_info; + unsigned int i, tex; + WORD ffu_map; + + ffu_map = context_gl->c.fixed_function_usage_map; + + if (d3d_info->limits.ffp_textures == d3d_info->limits.ffp_blend_stages + || context_gl->c.lowest_disabled_stage <= d3d_info->limits.ffp_textures) + { + for (i = 0; ffu_map; ffu_map >>= 1, ++i) + { + if (!(ffu_map & 1)) + continue; + + if (context_gl->tex_unit_map[i] != i) + { + wined3d_context_gl_map_stage(context_gl, i, i); + context_invalidate_state(&context_gl->c, STATE_SAMPLER(i)); + context_invalidate_texture_stage(&context_gl->c, i); + } + } + return; + } + + /* Now work out the mapping */ + tex = 0; + for (i = 0; ffu_map; ffu_map >>= 1, ++i) + { + if (!(ffu_map & 1)) + continue; + + if (context_gl->tex_unit_map[i] != tex) + { + wined3d_context_gl_map_stage(context_gl, i, tex); + context_invalidate_state(&context_gl->c, STATE_SAMPLER(i)); + context_invalidate_texture_stage(&context_gl->c, i); + } + + ++tex; + } +} + +static void wined3d_context_gl_map_psamplers(struct wined3d_context_gl *context_gl, const struct wined3d_state *state) +{ + const struct wined3d_d3d_info *d3d_info = context_gl->c.d3d_info; + const struct wined3d_shader_resource_info *resource_info = + state->shader[WINED3D_SHADER_TYPE_PIXEL]->reg_maps.resource_info; + unsigned int i; + + for (i = 0; i < WINED3D_MAX_FRAGMENT_SAMPLERS; ++i) + { + if (resource_info[i].type && context_gl->tex_unit_map[i] != i) + { + wined3d_context_gl_map_stage(context_gl, i, i); + context_invalidate_state(&context_gl->c, STATE_SAMPLER(i)); + if (i < d3d_info->limits.ffp_blend_stages) + context_invalidate_texture_stage(&context_gl->c, i); + } + } +} + +static BOOL wined3d_context_gl_unit_free_for_vs(const struct wined3d_context_gl *context_gl, + const struct wined3d_shader_resource_info *ps_resource_info, unsigned int unit) +{ + unsigned int current_mapping = context_gl->rev_tex_unit_map[unit]; + + /* Not currently used */ + if (current_mapping == WINED3D_UNMAPPED_STAGE) + return TRUE; + + if (current_mapping < WINED3D_MAX_FRAGMENT_SAMPLERS) + { + /* Used by a fragment sampler */ + + if (!ps_resource_info) + { + /* No pixel shader, check fixed function */ + return current_mapping >= WINED3D_MAX_TEXTURES + || !(context_gl->c.fixed_function_usage_map & (1u << current_mapping)); + } + + /* Pixel shader, check the shader's sampler map */ + return !ps_resource_info[current_mapping].type; + } + + return TRUE; +} + +static void wined3d_context_gl_map_vsamplers(struct wined3d_context_gl *context_gl, + BOOL ps, const struct wined3d_state *state) +{ + const struct wined3d_shader_resource_info *vs_resource_info = + state->shader[WINED3D_SHADER_TYPE_VERTEX]->reg_maps.resource_info; + const struct wined3d_shader_resource_info *ps_resource_info = NULL; + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + int start = min(WINED3D_MAX_COMBINED_SAMPLERS, gl_info->limits.graphics_samplers) - 1; + int i; + + /* Note that we only care if a resource is used or not, not the + * resource's specific type. Otherwise we'd need to call + * shader_update_samplers() here for 1.x pixelshaders. */ + if (ps) + ps_resource_info = state->shader[WINED3D_SHADER_TYPE_PIXEL]->reg_maps.resource_info; + + for (i = 0; i < WINED3D_MAX_VERTEX_SAMPLERS; ++i) + { + DWORD vsampler_idx = i + WINED3D_MAX_FRAGMENT_SAMPLERS; + if (vs_resource_info[i].type) + { + while (start >= 0) + { + if (wined3d_context_gl_unit_free_for_vs(context_gl, ps_resource_info, start)) + { + if (context_gl->tex_unit_map[vsampler_idx] != start) + { + wined3d_context_gl_map_stage(context_gl, vsampler_idx, start); + context_invalidate_state(&context_gl->c, STATE_SAMPLER(vsampler_idx)); + } + + --start; + break; + } + + --start; + } + if (context_gl->tex_unit_map[vsampler_idx] == WINED3D_UNMAPPED_STAGE) + WARN("Couldn't find a free texture unit for vertex sampler %u.\n", i); + } + } +} + +static void wined3d_context_gl_update_tex_unit_map(struct wined3d_context_gl *context_gl, + const struct wined3d_state *state) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + BOOL vs = use_vs(state); + BOOL ps = use_ps(state); + + if (!ps) + context_update_fixed_function_usage_map(&context_gl->c, state); + + /* Try to go for a 1:1 mapping of the samplers when possible. Pixel shaders + * need a 1:1 map at the moment. + * When the mapping of a stage is changed, sampler and ALL texture stage + * states have to be reset. */ + + if (gl_info->limits.graphics_samplers >= WINED3D_MAX_COMBINED_SAMPLERS) + return; + + if (ps) + wined3d_context_gl_map_psamplers(context_gl, state); + else + wined3d_context_gl_map_fixed_function_samplers(context_gl, state); + + if (vs) + wined3d_context_gl_map_vsamplers(context_gl, ps, state); +} + +/* Context activation is done by the caller. */ +void context_state_drawbuf(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + uint32_t rt_mask, *cur_mask; + + if (isStateDirty(context, STATE_FRAMEBUFFER)) return; + + cur_mask = context_gl->current_fbo ? &context_gl->current_fbo->rt_mask : &context_gl->draw_buffers_mask; + rt_mask = find_draw_buffers_mask(context_gl, state); + if (rt_mask != *cur_mask) + { + wined3d_context_gl_apply_draw_buffers(context_gl, rt_mask); + *cur_mask = rt_mask; + } +} + +static void wined3d_context_gl_bind_shader_resources(struct wined3d_context_gl *context_gl, + const struct wined3d_state *state, enum wined3d_shader_type shader_type) +{ + unsigned int bind_idx, shader_sampler_count, base, count, i; + const struct wined3d_device *device = context_gl->c.device; + struct wined3d_shader_sampler_map_entry *entry; + struct wined3d_shader_resource_view *view; + const struct wined3d_shader *shader; + const unsigned int *tex_unit_map; + struct wined3d_sampler *sampler; + + if (!(shader = state->shader[shader_type])) + return; + + tex_unit_map = wined3d_context_gl_get_tex_unit_mapping(context_gl, + &shader->reg_maps.shader_version, &base, &count); + + shader_sampler_count = shader->reg_maps.sampler_map.count; + if (shader_sampler_count > count) + FIXME("Shader %p needs %u samplers, but only %u are supported.\n", + shader, shader_sampler_count, count); + count = min(shader_sampler_count, count); + + for (i = 0; i < count; ++i) + { + entry = &shader->reg_maps.sampler_map.entries[i]; + bind_idx = base + entry->bind_idx; + if (tex_unit_map) + bind_idx = tex_unit_map[bind_idx]; + + if (!(view = state->shader_resource_view[shader_type][entry->resource_idx])) + { + WARN("No resource view bound at index %u, %u.\n", shader_type, entry->resource_idx); + continue; + } + + if (entry->sampler_idx == WINED3D_SAMPLER_DEFAULT) + sampler = device->default_sampler; + else if (!(sampler = state->sampler[shader_type][entry->sampler_idx])) + sampler = device->null_sampler; + wined3d_shader_resource_view_gl_bind(wined3d_shader_resource_view_gl(view), + bind_idx, wined3d_sampler_gl(sampler), context_gl); + } +} + +static void wined3d_context_gl_bind_unordered_access_views(struct wined3d_context_gl *context_gl, + const struct wined3d_shader *shader, struct wined3d_unordered_access_view * const *views) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + struct wined3d_unordered_access_view_gl *view_gl; + const struct wined3d_format_gl *format_gl; + GLuint texture_name; + unsigned int i; + GLint level; + + if (!shader) + return; + + for (i = 0; i < MAX_UNORDERED_ACCESS_VIEWS; ++i) + { + if (!views[i]) + { + if (shader->reg_maps.uav_resource_info[i].type) + WARN("No unordered access view bound at index %u.\n", i); + GL_EXTCALL(glBindImageTexture(i, 0, 0, GL_FALSE, 0, GL_READ_WRITE, GL_R8)); + continue; + } + + view_gl = wined3d_unordered_access_view_gl(views[i]); + if (view_gl->gl_view.name) + { + texture_name = view_gl->gl_view.name; + level = 0; + } + else if (view_gl->v.resource->type != WINED3D_RTYPE_BUFFER) + { + struct wined3d_texture_gl *texture_gl = wined3d_texture_gl(texture_from_resource(view_gl->v.resource)); + texture_name = wined3d_texture_gl_get_texture_name(texture_gl, &context_gl->c, FALSE); + level = view_gl->v.desc.u.texture.level_idx; + } + else + { + FIXME("Unsupported buffer unordered access view.\n"); + GL_EXTCALL(glBindImageTexture(i, 0, 0, GL_FALSE, 0, GL_READ_WRITE, GL_R8)); + continue; + } + + format_gl = wined3d_format_gl(view_gl->v.format); + GL_EXTCALL(glBindImageTexture(i, texture_name, level, GL_TRUE, 0, GL_READ_WRITE, + format_gl->internal)); + + if (view_gl->counter_bo.id) + GL_EXTCALL(glBindBufferBase(GL_ATOMIC_COUNTER_BUFFER, i, view_gl->counter_bo.id)); + } + checkGLcall("Bind unordered access views"); +} + +static void context_gl_load_shader_resources(struct wined3d_context_gl *context_gl, + const struct wined3d_state *state, unsigned int shader_mask) +{ + struct wined3d_shader_sampler_map_entry *entry; + struct wined3d_shader_resource_view_gl *srv_gl; + struct wined3d_shader_resource_view *view; + struct wined3d_buffer_gl *buffer_gl; + struct wined3d_shader *shader; + unsigned int i, j; + + for (i = 0; i < WINED3D_SHADER_TYPE_COUNT; ++i) + { + if (!(shader_mask & (1u << i))) + continue; + + if (!(shader = state->shader[i])) + continue; + + for (j = 0; j < WINED3D_MAX_CBS; ++j) + { + if (!state->cb[i][j]) + continue; + + buffer_gl = wined3d_buffer_gl(state->cb[i][j]); + wined3d_buffer_load(&buffer_gl->b, &context_gl->c, state); + wined3d_context_gl_reference_bo(context_gl, &buffer_gl->bo); + if (!buffer_gl->bo_user.valid) + device_invalidate_state(context_gl->c.device, STATE_CONSTANT_BUFFER(i)); + } + + for (j = 0; j < shader->reg_maps.sampler_map.count; ++j) + { + entry = &shader->reg_maps.sampler_map.entries[j]; + + if (!(view = state->shader_resource_view[i][entry->resource_idx])) + continue; + + if (view->resource->type == WINED3D_RTYPE_BUFFER) + { + buffer_gl = wined3d_buffer_gl(buffer_from_resource(view->resource)); + wined3d_buffer_load(&buffer_gl->b, &context_gl->c, state); + wined3d_context_gl_reference_bo(context_gl, &buffer_gl->bo); + + srv_gl = wined3d_shader_resource_view_gl(view); + if (!srv_gl->bo_user.valid) + wined3d_shader_resource_view_gl_update(srv_gl, context_gl); + } + else + { + wined3d_texture_load(texture_from_resource(view->resource), &context_gl->c, FALSE); + } + } + } +} + +static void context_gl_load_unordered_access_resources(struct wined3d_context_gl *context_gl, + const struct wined3d_shader *shader, struct wined3d_unordered_access_view * const *views) +{ + struct wined3d_unordered_access_view_gl *uav_gl; + struct wined3d_unordered_access_view *view; + struct wined3d_buffer_gl *buffer_gl; + struct wined3d_texture *texture; + unsigned int i; + + context_gl->c.uses_uavs = 0; + + if (!shader) + return; + + for (i = 0; i < MAX_UNORDERED_ACCESS_VIEWS; ++i) + { + if (!(view = views[i])) + continue; + + if (view->resource->type == WINED3D_RTYPE_BUFFER) + { + buffer_gl = wined3d_buffer_gl(buffer_from_resource(view->resource)); + wined3d_buffer_load_location(&buffer_gl->b, &context_gl->c, WINED3D_LOCATION_BUFFER); + wined3d_unordered_access_view_invalidate_location(view, ~WINED3D_LOCATION_BUFFER); + wined3d_context_gl_reference_bo(context_gl, &buffer_gl->bo); + + uav_gl = wined3d_unordered_access_view_gl(view); + if (!uav_gl->bo_user.valid) + wined3d_unordered_access_view_gl_update(uav_gl, context_gl); + } + else + { + texture = texture_from_resource(view->resource); + wined3d_texture_load(texture, &context_gl->c, FALSE); + wined3d_unordered_access_view_invalidate_location(view, ~WINED3D_LOCATION_TEXTURE_RGB); + } + + context_gl->c.uses_uavs = 1; + } +} + +static void context_gl_load_stream_output_buffers(struct wined3d_context_gl *context_gl, + const struct wined3d_state *state) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(state->stream_output); ++i) + { + struct wined3d_buffer_gl *buffer_gl; + + if (!state->stream_output[i].buffer) + continue; + + buffer_gl = wined3d_buffer_gl(state->stream_output[i].buffer); + wined3d_buffer_load(&buffer_gl->b, &context_gl->c, state); + wined3d_buffer_invalidate_location(&buffer_gl->b, ~WINED3D_LOCATION_BUFFER); + wined3d_context_gl_reference_bo(context_gl, &buffer_gl->bo); + if (!buffer_gl->bo_user.valid) + device_invalidate_state(context_gl->c.device, STATE_STREAM_OUTPUT); + } +} + +/* Context activation is done by the caller. */ +static BOOL context_apply_draw_state(struct wined3d_context *context, + const struct wined3d_device *device, const struct wined3d_state *state, BOOL indexed) +{ + const struct wined3d_state_entry *state_table = context->state_table; + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + const struct wined3d_fb_state *fb = &state->fb; + unsigned int i, base; + uint32_t map; + + context->uses_fbo_attached_resources = 0; + + if (!have_framebuffer_attachment(gl_info->limits.buffers, fb->render_targets, fb->depth_stencil)) + { + if (!gl_info->supported[ARB_FRAMEBUFFER_NO_ATTACHMENTS]) + { + FIXME("OpenGL implementation does not support framebuffers with no attachments.\n"); + return FALSE; + } + + wined3d_context_gl_set_render_offscreen(context_gl, TRUE); + } + + /* Preload resources before FBO setup. Texture preload in particular may + * result in changes to the current FBO, due to using e.g. FBO blits for + * updating a resource location. */ + wined3d_context_gl_update_tex_unit_map(context_gl, state); + context_preload_textures(context, state); + context_gl_load_shader_resources(context_gl, state, ~(1u << WINED3D_SHADER_TYPE_COMPUTE)); + context_gl_load_unordered_access_resources(context_gl, state->shader[WINED3D_SHADER_TYPE_PIXEL], + state->unordered_access_view[WINED3D_PIPELINE_GRAPHICS]); + context_gl_load_stream_output_buffers(context_gl, state); + /* TODO: Right now the dependency on the vertex shader is necessary + * since wined3d_stream_info_from_declaration() depends on the reg_maps of + * the current VS but maybe it's possible to relax the coupling in some + * situations at least. */ + if (isStateDirty(context, STATE_VDECL) || isStateDirty(context, STATE_STREAMSRC) + || isStateDirty(context, STATE_SHADER(WINED3D_SHADER_TYPE_VERTEX))) + { + context_update_stream_info(context, state); + } + + map = context->stream_info.use_map; + while (map) + { + const struct wined3d_stream_info_element *e; + struct wined3d_buffer_gl *buffer_gl; + + e = &context->stream_info.elements[wined3d_bit_scan(&map)]; + buffer_gl = wined3d_buffer_gl(state->streams[e->stream_idx].buffer); + + if (!buffer_gl->bo_user.valid) + device_invalidate_state(device, STATE_STREAMSRC); + else + wined3d_buffer_load(&buffer_gl->b, context, state); + wined3d_context_gl_reference_bo(context_gl, &buffer_gl->bo); + } + /* Loading the buffers above may have invalidated the stream info. */ + if (wined3d_context_is_graphics_state_dirty(context, STATE_STREAMSRC)) + context_update_stream_info(context, state); + + if (indexed && state->index_buffer) + { + struct wined3d_buffer_gl *buffer_gl = wined3d_buffer_gl(state->index_buffer); + + if (context->stream_info.all_vbo) + { + wined3d_buffer_load(&buffer_gl->b, context, state); + if (!buffer_gl->bo_user.valid) + device_invalidate_state(device, STATE_INDEXBUFFER); + wined3d_context_gl_reference_bo(context_gl, &buffer_gl->bo); + } + else + { + wined3d_buffer_load_sysmem(&buffer_gl->b, context); + } + } + + for (i = 0, base = 0; i < ARRAY_SIZE(context->dirty_graphics_states); ++i) + { + uint32_t dirty_mask = context->dirty_graphics_states[i]; + + while (dirty_mask) + { + unsigned int state_id = base + wined3d_bit_scan(&dirty_mask); + + state_table[state_id].apply(context, state, state_id); + context->dirty_graphics_states[i] &= ~(1u << (state_id - base)); + } + base += sizeof(dirty_mask) * CHAR_BIT; + } + + if (context->shader_update_mask & ~(1u << WINED3D_SHADER_TYPE_COMPUTE)) + { + device->shader_backend->shader_select(device->shader_priv, context, state); + context->shader_update_mask &= 1u << WINED3D_SHADER_TYPE_COMPUTE; + } + + if (context->constant_update_mask) + { + device->shader_backend->shader_load_constants(device->shader_priv, context, state); + context->constant_update_mask = 0; + } + + if (context->update_shader_resource_bindings) + { + for (i = 0; i < WINED3D_SHADER_TYPE_GRAPHICS_COUNT; ++i) + wined3d_context_gl_bind_shader_resources(context_gl, state, i); + context->update_shader_resource_bindings = 0; + if (gl_info->limits.combined_samplers == gl_info->limits.graphics_samplers) + context->update_compute_shader_resource_bindings = 1; + } + + if (context->update_unordered_access_view_bindings) + { + wined3d_context_gl_bind_unordered_access_views(context_gl, + state->shader[WINED3D_SHADER_TYPE_PIXEL], + state->unordered_access_view[WINED3D_PIPELINE_GRAPHICS]); + context->update_unordered_access_view_bindings = 0; + context->update_compute_unordered_access_view_bindings = 1; + } + + if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) + wined3d_context_gl_check_fbo_status(context_gl, GL_FRAMEBUFFER); + + context->last_was_blit = FALSE; + context->last_was_ffp_blit = FALSE; + + return TRUE; +} + +static void wined3d_context_gl_apply_compute_state(struct wined3d_context_gl *context_gl, + const struct wined3d_device *device, const struct wined3d_state *state) +{ + const struct wined3d_state_entry *state_table = context_gl->c.state_table; + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + unsigned int state_id, i; + + context_gl_load_shader_resources(context_gl, state, 1u << WINED3D_SHADER_TYPE_COMPUTE); + context_gl_load_unordered_access_resources(context_gl, state->shader[WINED3D_SHADER_TYPE_COMPUTE], + state->unordered_access_view[WINED3D_PIPELINE_COMPUTE]); + + for (i = 0, state_id = STATE_COMPUTE_OFFSET; i < ARRAY_SIZE(context_gl->c.dirty_compute_states); ++i) + { + unsigned int dirty_mask = context_gl->c.dirty_compute_states[i]; + + while (dirty_mask) + { + unsigned int current_state_id = state_id + wined3d_bit_scan(&dirty_mask); + state_table[current_state_id].apply(&context_gl->c, state, current_state_id); + } + state_id += sizeof(*context_gl->c.dirty_compute_states) * CHAR_BIT; + } + memset(context_gl->c.dirty_compute_states, 0, sizeof(*context_gl->c.dirty_compute_states)); + + if (context_gl->c.shader_update_mask & (1u << WINED3D_SHADER_TYPE_COMPUTE)) + { + device->shader_backend->shader_select_compute(device->shader_priv, &context_gl->c, state); + context_gl->c.shader_update_mask &= ~(1u << WINED3D_SHADER_TYPE_COMPUTE); + } + + if (context_gl->c.update_compute_shader_resource_bindings) + { + wined3d_context_gl_bind_shader_resources(context_gl, state, WINED3D_SHADER_TYPE_COMPUTE); + context_gl->c.update_compute_shader_resource_bindings = 0; + if (gl_info->limits.combined_samplers == gl_info->limits.graphics_samplers) + context_gl->c.update_shader_resource_bindings = 1; + } + + if (context_gl->c.update_compute_unordered_access_view_bindings) + { + wined3d_context_gl_bind_unordered_access_views(context_gl, + state->shader[WINED3D_SHADER_TYPE_COMPUTE], + state->unordered_access_view[WINED3D_PIPELINE_COMPUTE]); + context_gl->c.update_compute_unordered_access_view_bindings = 0; + context_gl->c.update_unordered_access_view_bindings = 1; + } + + /* Updates to currently bound render targets aren't necessarily coherent + * between the graphics and compute pipelines. Unbind any currently bound + * FBO here to ensure preceding updates to its attachments by the graphics + * pipeline are visible to the compute pipeline. + * + * Without this, the bloom effect in Nier:Automata is too bright on the + * Mesa radeonsi driver, and presumably on other Mesa based drivers. */ + wined3d_context_gl_bind_fbo(context_gl, GL_FRAMEBUFFER, 0); + context_invalidate_state(&context_gl->c, STATE_FRAMEBUFFER); + + context_gl->c.last_was_blit = FALSE; + context_gl->c.last_was_ffp_blit = FALSE; +} + +void wined3d_context_gl_end_transform_feedback(struct wined3d_context_gl *context_gl) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + + if (context_gl->c.transform_feedback_active) + { + GL_EXTCALL(glEndTransformFeedback()); + checkGLcall("glEndTransformFeedback"); + context_gl->c.transform_feedback_active = 0; + context_gl->c.transform_feedback_paused = 0; + } +} + +static void wined3d_context_gl_pause_transform_feedback(struct wined3d_context_gl *context_gl, BOOL force) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + + if (!context_gl->c.transform_feedback_active || context_gl->c.transform_feedback_paused) + return; + + if (gl_info->supported[ARB_TRANSFORM_FEEDBACK2]) + { + GL_EXTCALL(glPauseTransformFeedback()); + checkGLcall("glPauseTransformFeedback"); + context_gl->c.transform_feedback_paused = 1; + return; + } + + WARN("Cannot pause transform feedback operations.\n"); + + if (force) + wined3d_context_gl_end_transform_feedback(context_gl); +} + +static void wined3d_context_gl_setup_target(struct wined3d_context_gl *context_gl, + struct wined3d_texture *texture, unsigned int sub_resource_idx) +{ + BOOL old_render_offscreen = context_gl->c.render_offscreen, render_offscreen; + + render_offscreen = wined3d_resource_is_offscreen(&texture->resource); + if (context_gl->c.current_rt.texture == texture + && context_gl->c.current_rt.sub_resource_idx == sub_resource_idx + && render_offscreen == old_render_offscreen) + return; + + /* To compensate the lack of format switching with some offscreen rendering methods and on onscreen buffers + * the alpha blend state changes with different render target formats. */ + if (!context_gl->c.current_rt.texture) + { + context_invalidate_state(&context_gl->c, STATE_BLEND); + } + else + { + const struct wined3d_format *old = context_gl->c.current_rt.texture->resource.format; + const struct wined3d_format *new = texture->resource.format; + + if (old->id != new->id) + { + /* Disable blending when the alpha mask has changed and when a format doesn't support blending. */ + if ((old->alpha_size && !new->alpha_size) || (!old->alpha_size && new->alpha_size) + || !(texture->resource.format_flags & WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING)) + context_invalidate_state(&context_gl->c, STATE_BLEND); + + /* Update sRGB writing when switching between formats that do/do not support sRGB writing */ + if ((context_gl->c.current_rt.texture->resource.format_flags & WINED3DFMT_FLAG_SRGB_WRITE) + != (texture->resource.format_flags & WINED3DFMT_FLAG_SRGB_WRITE)) + context_invalidate_state(&context_gl->c, STATE_RENDER(WINED3D_RS_SRGBWRITEENABLE)); + } + + /* When switching away from an offscreen render target, and we're not + * using FBOs, we have to read the drawable into the texture. This is + * done via PreLoad (and WINED3D_LOCATION_DRAWABLE set on the surface). + * There are some things that need care though. PreLoad needs a GL context, + * and FindContext is called before the context is activated. It also + * has to be called with the old rendertarget active, otherwise a + * wrong drawable is read. */ + if (wined3d_settings.offscreen_rendering_mode != ORM_FBO + && old_render_offscreen && (context_gl->c.current_rt.texture != texture + || context_gl->c.current_rt.sub_resource_idx != sub_resource_idx)) + { + struct wined3d_texture_gl *prev_texture = wined3d_texture_gl(context_gl->c.current_rt.texture); + unsigned int prev_sub_resource_idx = context_gl->c.current_rt.sub_resource_idx; + + /* Read the back buffer of the old drawable into the destination texture. */ + if (prev_texture->texture_srgb.name) + wined3d_texture_load(&prev_texture->t, &context_gl->c, TRUE); + wined3d_texture_load(&prev_texture->t, &context_gl->c, FALSE); + wined3d_texture_invalidate_location(&prev_texture->t, prev_sub_resource_idx, WINED3D_LOCATION_DRAWABLE); + } + } + + context_gl->c.current_rt.texture = texture; + context_gl->c.current_rt.sub_resource_idx = sub_resource_idx; + wined3d_context_gl_set_render_offscreen(context_gl, render_offscreen); +} + +static void wined3d_context_gl_activate(struct wined3d_context_gl *context_gl, + struct wined3d_texture *texture, unsigned int sub_resource_idx) +{ + wined3d_context_gl_enter(context_gl); + wined3d_context_gl_update_window(context_gl); + wined3d_context_gl_setup_target(context_gl, texture, sub_resource_idx); + if (!context_gl->valid) + return; + + if (context_gl != wined3d_context_gl_get_current()) + { + if (!wined3d_context_gl_set_current(context_gl)) + ERR("Failed to activate the new context.\n"); + } + else if (context_gl->needs_set) + { + wined3d_context_gl_set_gl_context(context_gl); + } +} + +struct wined3d_context *wined3d_context_gl_acquire(const struct wined3d_device *device, + struct wined3d_texture *texture, unsigned int sub_resource_idx) +{ + struct wined3d_context_gl *current_context = wined3d_context_gl_get_current(); + struct wined3d_context_gl *context_gl; + + TRACE("device %p, texture %p, sub_resource_idx %u.\n", device, texture, sub_resource_idx); + + if (current_context && current_context->c.destroyed) + current_context = NULL; + + if (!texture) + { + if (current_context + && current_context->c.current_rt.texture + && current_context->c.device == device) + { + texture = current_context->c.current_rt.texture; + sub_resource_idx = current_context->c.current_rt.sub_resource_idx; + } + else + { + struct wined3d_swapchain *swapchain = device->swapchains[0]; + + if (swapchain->back_buffers) + texture = swapchain->back_buffers[0]; + else + texture = swapchain->front_buffer; + sub_resource_idx = 0; + } + } + + if (current_context && current_context->c.current_rt.texture == texture) + { + context_gl = current_context; + } + else if (!wined3d_resource_is_offscreen(&texture->resource)) + { + TRACE("Rendering onscreen.\n"); + + if (!(context_gl = wined3d_swapchain_gl_get_context(wined3d_swapchain_gl(texture->swapchain)))) + return NULL; + } + else + { + TRACE("Rendering offscreen.\n"); + + /* Stay with the current context if possible. Otherwise use the + * context for the primary swapchain. */ + if (current_context && current_context->c.device == device) + context_gl = current_context; + else if (!(context_gl = wined3d_swapchain_gl_get_context(wined3d_swapchain_gl(device->swapchains[0])))) + return NULL; + } + + wined3d_context_gl_activate(context_gl, texture, sub_resource_idx); + + return &context_gl->c; +} + +struct wined3d_context_gl *wined3d_context_gl_reacquire(struct wined3d_context_gl *context_gl) +{ + struct wined3d_context *acquired_context; + struct wined3d_device *device; + + if (!context_gl || context_gl->tid != GetCurrentThreadId()) + return NULL; + + device = context_gl->c.device; + wined3d_from_cs(device->cs); + + if (context_gl->c.current_rt.texture) + { + wined3d_context_gl_activate(context_gl, context_gl->c.current_rt.texture, + context_gl->c.current_rt.sub_resource_idx); + return context_gl; + } + + acquired_context = context_acquire(device, NULL, 0); + if (acquired_context != &context_gl->c) + ERR("Acquired context %p instead of %p.\n", acquired_context, &context_gl->c); + return wined3d_context_gl(acquired_context); +} + +void dispatch_compute(struct wined3d_device *device, const struct wined3d_state *state, + const struct wined3d_dispatch_parameters *parameters) +{ + const struct wined3d_gl_info *gl_info; + struct wined3d_context_gl *context_gl; + + context_gl = wined3d_context_gl(context_acquire(device, NULL, 0)); + if (!context_gl->valid) + { + context_release(&context_gl->c); + WARN("Invalid context, skipping dispatch.\n"); + return; + } + gl_info = context_gl->gl_info; + + if (!gl_info->supported[ARB_COMPUTE_SHADER]) + { + context_release(&context_gl->c); + FIXME("OpenGL implementation does not support compute shaders.\n"); + return; + } + + if (parameters->indirect) + wined3d_buffer_load(parameters->u.indirect.buffer, &context_gl->c, state); + + wined3d_context_gl_apply_compute_state(context_gl, device, state); + + if (parameters->indirect) + { + const struct wined3d_indirect_dispatch_parameters *indirect = ¶meters->u.indirect; + struct wined3d_buffer_gl *buffer_gl = wined3d_buffer_gl(indirect->buffer); + + GL_EXTCALL(glBindBuffer(GL_DISPATCH_INDIRECT_BUFFER, buffer_gl->bo.id)); + GL_EXTCALL(glDispatchComputeIndirect((GLintptr)indirect->offset)); + GL_EXTCALL(glBindBuffer(GL_DISPATCH_INDIRECT_BUFFER, 0)); + wined3d_context_gl_reference_bo(context_gl, &buffer_gl->bo); + } + else + { + const struct wined3d_direct_dispatch_parameters *direct = ¶meters->u.direct; + GL_EXTCALL(glDispatchCompute(direct->group_count_x, direct->group_count_y, direct->group_count_z)); + } + checkGLcall("dispatch compute"); + + GL_EXTCALL(glMemoryBarrier(GL_ALL_BARRIER_BITS)); + checkGLcall("glMemoryBarrier"); + + context_release(&context_gl->c); +} + +/* Context activation is done by the caller. */ +static void wined3d_context_gl_draw_primitive_arrays(struct wined3d_context_gl *context_gl, + const struct wined3d_state *state, const void *idx_data, unsigned int idx_size, int base_vertex_idx, + unsigned int start_idx, unsigned int count, unsigned int start_instance, unsigned int instance_count) +{ + const struct wined3d_ffp_attrib_ops *ops = &context_gl->c.d3d_info->ffp_attrib_ops; + GLenum idx_type = idx_size == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT; + const struct wined3d_stream_info *si = &context_gl->c.stream_info; + GLenum mode = gl_primitive_type_from_d3d(state->primitive_type); + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + unsigned int instanced_elements[ARRAY_SIZE(si->elements)]; + unsigned int instanced_element_count = 0; + const void *indices; + unsigned int i, j; + + indices = (const char *)idx_data + idx_size * start_idx; + + if (!instance_count) + { + if (!idx_size) + { + gl_info->gl_ops.gl.p_glDrawArrays(mode, start_idx, count); + checkGLcall("glDrawArrays"); + return; + } + + if (gl_info->supported[ARB_DRAW_ELEMENTS_BASE_VERTEX]) + { + GL_EXTCALL(glDrawElementsBaseVertex(mode, count, idx_type, indices, base_vertex_idx)); + checkGLcall("glDrawElementsBaseVertex"); + return; + } + + gl_info->gl_ops.gl.p_glDrawElements(mode, count, idx_type, indices); + checkGLcall("glDrawElements"); + return; + } + + if (start_instance && !(gl_info->supported[ARB_BASE_INSTANCE] && gl_info->supported[ARB_INSTANCED_ARRAYS])) + FIXME("Start instance (%u) not supported.\n", start_instance); + + if (gl_info->supported[ARB_INSTANCED_ARRAYS]) + { + if (!idx_size) + { + if (gl_info->supported[ARB_BASE_INSTANCE]) + { + GL_EXTCALL(glDrawArraysInstancedBaseInstance(mode, start_idx, count, instance_count, start_instance)); + checkGLcall("glDrawArraysInstancedBaseInstance"); + return; + } + + GL_EXTCALL(glDrawArraysInstanced(mode, start_idx, count, instance_count)); + checkGLcall("glDrawArraysInstanced"); + return; + } + + if (gl_info->supported[ARB_BASE_INSTANCE]) + { + GL_EXTCALL(glDrawElementsInstancedBaseVertexBaseInstance(mode, count, idx_type, + indices, instance_count, base_vertex_idx, start_instance)); + checkGLcall("glDrawElementsInstancedBaseVertexBaseInstance"); + return; + } + if (gl_info->supported[ARB_DRAW_ELEMENTS_BASE_VERTEX]) + { + GL_EXTCALL(glDrawElementsInstancedBaseVertex(mode, count, idx_type, + indices, instance_count, base_vertex_idx)); + checkGLcall("glDrawElementsInstancedBaseVertex"); + return; + } + + GL_EXTCALL(glDrawElementsInstanced(mode, count, idx_type, indices, instance_count)); + checkGLcall("glDrawElementsInstanced"); + return; + } + + /* Instancing emulation by mixing immediate mode and arrays. */ + + /* This is a nasty thing. MSDN says no hardware supports this and + * applications have to use software vertex processing. We don't support + * this for now. + * + * Shouldn't be too hard to support with OpenGL, in theory just call + * glDrawArrays() instead of drawElements(). But the stream frequency value + * has a different meaning in that situation. */ + if (!idx_size) + { + FIXME("Non-indexed instanced drawing is not supported.\n"); + return; + } + + for (i = 0; i < ARRAY_SIZE(si->elements); ++i) + { + if (!(si->use_map & (1u << i))) + continue; + + if (state->streams[si->elements[i].stream_idx].flags & WINED3DSTREAMSOURCE_INSTANCEDATA) + instanced_elements[instanced_element_count++] = i; + } + + for (i = 0; i < instance_count; ++i) + { + /* Specify the instanced attributes using immediate mode calls. */ + for (j = 0; j < instanced_element_count; ++j) + { + const struct wined3d_stream_info_element *element; + unsigned int element_idx; + const BYTE *ptr; + + element_idx = instanced_elements[j]; + element = &si->elements[element_idx]; + ptr = element->data.addr + element->stride * i; + if (element->data.buffer_object) + ptr += (ULONG_PTR)wined3d_buffer_load_sysmem(state->streams[element->stream_idx].buffer, + &context_gl->c); + ops->generic[element->format->emit_idx](element_idx, ptr); + } + + if (gl_info->supported[ARB_DRAW_ELEMENTS_BASE_VERTEX]) + { + GL_EXTCALL(glDrawElementsBaseVertex(mode, count, idx_type, indices, base_vertex_idx)); + checkGLcall("glDrawElementsBaseVertex"); + } + else + { + gl_info->gl_ops.gl.p_glDrawElements(mode, count, idx_type, indices); + checkGLcall("glDrawElements"); + } + } +} + +static unsigned int get_stride_idx(const void *idx_data, unsigned int idx_size, + unsigned int base_vertex_idx, unsigned int start_idx, unsigned int vertex_idx) +{ + if (!idx_data) + return start_idx + vertex_idx; + if (idx_size == 2) + return ((const WORD *)idx_data)[start_idx + vertex_idx] + base_vertex_idx; + return ((const DWORD *)idx_data)[start_idx + vertex_idx] + base_vertex_idx; +} + +/* Context activation is done by the caller. */ +static void draw_primitive_immediate_mode(struct wined3d_context_gl *context_gl, const struct wined3d_state *state, + const struct wined3d_stream_info *si, const void *idx_data, unsigned int idx_size, + int base_vertex_idx, unsigned int start_idx, unsigned int vertex_count, unsigned int instance_count) +{ + const BYTE *position = NULL, *normal = NULL, *diffuse = NULL, *specular = NULL; + const struct wined3d_d3d_info *d3d_info = context_gl->c.d3d_info; + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + unsigned int coord_idx, stride_idx, texture_idx, vertex_idx; + const struct wined3d_stream_info_element *element; + const BYTE *tex_coords[WINED3DDP_MAXTEXCOORD]; + unsigned int texture_unit, texture_stages; + const struct wined3d_ffp_attrib_ops *ops; + unsigned int untracked_material_count; + unsigned int tex_mask = 0; + BOOL specular_fog = FALSE; + BOOL ps = use_ps(state); + const void *ptr; + + static unsigned int once; + + if (!once++) + FIXME_(d3d_perf)("Drawing using immediate mode.\n"); + else + WARN_(d3d_perf)("Drawing using immediate mode.\n"); + + if (!idx_size && idx_data) + ERR("Non-NULL idx_data with 0 idx_size, this should never happen.\n"); + + if (instance_count) + FIXME("Instancing not implemented.\n"); + + /* Immediate mode drawing can't make use of indices in a VBO - get the + * data from the index buffer. */ + if (idx_size) + idx_data = wined3d_buffer_load_sysmem(state->index_buffer, &context_gl->c) + state->index_offset; + + ops = &d3d_info->ffp_attrib_ops; + + gl_info->gl_ops.gl.p_glBegin(gl_primitive_type_from_d3d(state->primitive_type)); + + if (use_vs(state) || d3d_info->ffp_generic_attributes) + { + for (vertex_idx = 0; vertex_idx < vertex_count; ++vertex_idx) + { + unsigned int use_map = si->use_map; + unsigned int element_idx; + + stride_idx = get_stride_idx(idx_data, idx_size, base_vertex_idx, start_idx, vertex_idx); + for (element_idx = MAX_ATTRIBS - 1; use_map; use_map &= ~(1u << element_idx), --element_idx) + { + if (!(use_map & 1u << element_idx)) + continue; + + ptr = si->elements[element_idx].data.addr + si->elements[element_idx].stride * stride_idx; + ops->generic[si->elements[element_idx].format->emit_idx](element_idx, ptr); + } + } + + gl_info->gl_ops.gl.p_glEnd(); + return; + } + + if (si->use_map & (1u << WINED3D_FFP_POSITION)) + position = si->elements[WINED3D_FFP_POSITION].data.addr; + + if (si->use_map & (1u << WINED3D_FFP_NORMAL)) + normal = si->elements[WINED3D_FFP_NORMAL].data.addr; + else + gl_info->gl_ops.gl.p_glNormal3f(0.0f, 0.0f, 0.0f); + + untracked_material_count = context_gl->untracked_material_count; + if (si->use_map & (1u << WINED3D_FFP_DIFFUSE)) + { + element = &si->elements[WINED3D_FFP_DIFFUSE]; + diffuse = element->data.addr; + + if (untracked_material_count && element->format->id != WINED3DFMT_B8G8R8A8_UNORM) + FIXME("Implement diffuse color tracking from %s.\n", debug_d3dformat(element->format->id)); + } + else + { + gl_info->gl_ops.gl.p_glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + } + + if (si->use_map & (1u << WINED3D_FFP_SPECULAR)) + { + element = &si->elements[WINED3D_FFP_SPECULAR]; + specular = element->data.addr; + + /* Special case where the fog density is stored in the specular alpha channel. */ + if (state->render_states[WINED3D_RS_FOGENABLE] + && (state->render_states[WINED3D_RS_FOGVERTEXMODE] == WINED3D_FOG_NONE + || si->elements[WINED3D_FFP_POSITION].format->id == WINED3DFMT_R32G32B32A32_FLOAT) + && state->render_states[WINED3D_RS_FOGTABLEMODE] == WINED3D_FOG_NONE) + { + if (gl_info->supported[EXT_FOG_COORD]) + { + if (element->format->id == WINED3DFMT_B8G8R8A8_UNORM) + specular_fog = TRUE; + else + FIXME("Implement fog coordinates from %s.\n", debug_d3dformat(element->format->id)); + } + else + { + static unsigned int once; + + if (!once++) + FIXME("Implement fog for transformed vertices in software.\n"); + } + } + } + else if (gl_info->supported[EXT_SECONDARY_COLOR]) + { + GL_EXTCALL(glSecondaryColor3fEXT)(0.0f, 0.0f, 0.0f); + } + + texture_stages = d3d_info->limits.ffp_blend_stages; + for (texture_idx = 0; texture_idx < texture_stages; ++texture_idx) + { + if (!gl_info->supported[ARB_MULTITEXTURE] && texture_idx > 0) + { + FIXME("Program using multiple concurrent textures which this OpenGL implementation doesn't support.\n"); + continue; + } + + if (!ps && !state->textures[texture_idx]) + continue; + + texture_unit = context_gl->tex_unit_map[texture_idx]; + if (texture_unit == WINED3D_UNMAPPED_STAGE) + continue; + + coord_idx = state->texture_states[texture_idx][WINED3D_TSS_TEXCOORD_INDEX]; + if (coord_idx > 7) + { + TRACE("Skipping generated coordinates (%#x) for texture %u.\n", coord_idx, texture_idx); + continue; + } + + if (si->use_map & (1u << (WINED3D_FFP_TEXCOORD0 + coord_idx))) + { + tex_coords[coord_idx] = si->elements[WINED3D_FFP_TEXCOORD0 + coord_idx].data.addr; + tex_mask |= (1u << texture_idx); + } + else + { + TRACE("Setting default coordinates for texture %u.\n", texture_idx); + if (gl_info->supported[ARB_MULTITEXTURE]) + GL_EXTCALL(glMultiTexCoord4fARB(GL_TEXTURE0_ARB + texture_unit, 0.0f, 0.0f, 0.0f, 1.0f)); + else + gl_info->gl_ops.gl.p_glTexCoord4f(0.0f, 0.0f, 0.0f, 1.0f); + } + } + + /* Blending data and point sizes are not supported by this function. They + * are not supported by the fixed function pipeline at all. A FIXME for + * them is printed after decoding the vertex declaration. */ + for (vertex_idx = 0; vertex_idx < vertex_count; ++vertex_idx) + { + unsigned int tmp_tex_mask; + + stride_idx = get_stride_idx(idx_data, idx_size, base_vertex_idx, start_idx, vertex_idx); + + if (normal) + { + ptr = normal + stride_idx * si->elements[WINED3D_FFP_NORMAL].stride; + ops->normal[si->elements[WINED3D_FFP_NORMAL].format->emit_idx](ptr); + } + + if (diffuse) + { + ptr = diffuse + stride_idx * si->elements[WINED3D_FFP_DIFFUSE].stride; + ops->diffuse[si->elements[WINED3D_FFP_DIFFUSE].format->emit_idx](ptr); + + if (untracked_material_count) + { + struct wined3d_color color; + unsigned int i; + + wined3d_color_from_d3dcolor(&color, *(const DWORD *)ptr); + for (i = 0; i < untracked_material_count; ++i) + { + gl_info->gl_ops.gl.p_glMaterialfv(GL_FRONT_AND_BACK, + context_gl->untracked_materials[i], &color.r); + } + } + } + + if (specular) + { + ptr = specular + stride_idx * si->elements[WINED3D_FFP_SPECULAR].stride; + ops->specular[si->elements[WINED3D_FFP_SPECULAR].format->emit_idx](ptr); + + if (specular_fog) + GL_EXTCALL(glFogCoordfEXT((float)(*(const DWORD *)ptr >> 24))); + } + + tmp_tex_mask = tex_mask; + for (texture_idx = 0; tmp_tex_mask; tmp_tex_mask >>= 1, ++texture_idx) + { + if (!(tmp_tex_mask & 1)) + continue; + + coord_idx = state->texture_states[texture_idx][WINED3D_TSS_TEXCOORD_INDEX]; + ptr = tex_coords[coord_idx] + (stride_idx * si->elements[WINED3D_FFP_TEXCOORD0 + coord_idx].stride); + ops->texcoord[si->elements[WINED3D_FFP_TEXCOORD0 + coord_idx].format->emit_idx]( + GL_TEXTURE0_ARB + context_gl->tex_unit_map[texture_idx], ptr); + } + + if (position) + { + ptr = position + stride_idx * si->elements[WINED3D_FFP_POSITION].stride; + ops->position[si->elements[WINED3D_FFP_POSITION].format->emit_idx](ptr); + } + } + + gl_info->gl_ops.gl.p_glEnd(); + checkGLcall("draw immediate mode"); +} + +static void wined3d_context_gl_draw_indirect(struct wined3d_context_gl *context_gl, const struct wined3d_state *state, + const struct wined3d_indirect_draw_parameters *parameters, unsigned int idx_size) +{ + GLenum gl_primitive_type = gl_primitive_type_from_d3d(state->primitive_type); + struct wined3d_buffer_gl *buffer_gl = wined3d_buffer_gl(parameters->buffer); + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + const void *offset; + + if (!gl_info->supported[ARB_DRAW_INDIRECT]) + { + FIXME("OpenGL implementation does not support indirect draws.\n"); + return; + } + + GL_EXTCALL(glBindBuffer(GL_DRAW_INDIRECT_BUFFER, buffer_gl->bo.id)); + + offset = (void *)(GLintptr)parameters->offset; + if (idx_size) + { + GLenum idx_type = idx_size == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT; + if (state->index_offset) + FIXME("Ignoring index offset %u.\n", state->index_offset); + GL_EXTCALL(glDrawElementsIndirect(gl_primitive_type, idx_type, offset)); + } + else + { + GL_EXTCALL(glDrawArraysIndirect(gl_primitive_type, offset)); + } + + GL_EXTCALL(glBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0)); + wined3d_context_gl_reference_bo(context_gl, &buffer_gl->bo); + + checkGLcall("draw indirect"); +} + +static void remove_vbos(struct wined3d_context *context, + const struct wined3d_state *state, struct wined3d_stream_info *s) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(s->elements); ++i) + { + struct wined3d_stream_info_element *e; + + if (!(s->use_map & (1u << i))) + continue; + + e = &s->elements[i]; + if (e->data.buffer_object) + { + struct wined3d_buffer *vb = state->streams[e->stream_idx].buffer; + e->data.buffer_object = 0; + e->data.addr += (ULONG_PTR)wined3d_buffer_load_sysmem(vb, context); + } + } +} + +static GLenum gl_tfb_primitive_type_from_d3d(enum wined3d_primitive_type primitive_type) +{ + GLenum gl_primitive_type = gl_primitive_type_from_d3d(primitive_type); + switch (gl_primitive_type) + { + case GL_POINTS: + return GL_POINTS; + + case GL_LINE_STRIP: + case GL_LINE_STRIP_ADJACENCY: + case GL_LINES_ADJACENCY: + case GL_LINES: + return GL_LINES; + + case GL_TRIANGLE_FAN: + case GL_TRIANGLE_STRIP: + case GL_TRIANGLE_STRIP_ADJACENCY: + case GL_TRIANGLES_ADJACENCY: + case GL_TRIANGLES: + return GL_TRIANGLES; + + default: + return gl_primitive_type; + } +} + +/* Routine common to the draw primitive and draw indexed primitive routines */ +void draw_primitive(struct wined3d_device *device, const struct wined3d_state *state, + const struct wined3d_draw_parameters *parameters) +{ + BOOL emulation = FALSE, rasterizer_discard = FALSE; + const struct wined3d_fb_state *fb = &state->fb; + const struct wined3d_stream_info *stream_info; + struct wined3d_rendertarget_view *dsv, *rtv; + struct wined3d_stream_info si_emulated; + const struct wined3d_gl_info *gl_info; + struct wined3d_context_gl *context_gl; + struct wined3d_context *context; + unsigned int i, idx_size = 0; + const void *idx_data = NULL; + + TRACE("device %p, state %p, parameters %p.\n", device, state, parameters); + + if (!parameters->indirect && !parameters->u.direct.index_count) + return; + + if (!parameters->indirect) + TRACE("base_vertex_idx %d, start_idx %u, index_count %u, start_instance %u, instance_count %u.\n", + parameters->u.direct.base_vertex_idx, parameters->u.direct.start_idx, + parameters->u.direct.index_count, parameters->u.direct.start_instance, + parameters->u.direct.instance_count); + + if (!(rtv = fb->render_targets[0])) + rtv = fb->depth_stencil; + + if (rtv && rtv->resource->type == WINED3D_RTYPE_BUFFER) + { + FIXME("Buffer render targets not implemented.\n"); + return; + } + + if (rtv) + context = context_acquire(device, wined3d_texture_from_resource(rtv->resource), rtv->sub_resource_idx); + else + context = context_acquire(device, NULL, 0); + context_gl = wined3d_context_gl(context); + if (!context_gl->valid) + { + context_release(context); + WARN("Invalid context, skipping draw.\n"); + return; + } + gl_info = context_gl->gl_info; + + if (!use_transform_feedback(state)) + wined3d_context_gl_pause_transform_feedback(context_gl, TRUE); + + for (i = 0; i < gl_info->limits.buffers; ++i) + { + if (!(rtv = fb->render_targets[i]) || rtv->format->id == WINED3DFMT_NULL) + continue; + + if (wined3d_blend_state_get_writemask(state->blend_state, i)) + { + wined3d_rendertarget_view_load_location(rtv, context, rtv->resource->draw_binding); + wined3d_rendertarget_view_invalidate_location(rtv, ~rtv->resource->draw_binding); + } + else + { + wined3d_rendertarget_view_prepare_location(rtv, context, rtv->resource->draw_binding); + } + } + + if ((dsv = fb->depth_stencil)) + { + /* Note that this depends on the context_acquire() call above to set + * context->render_offscreen properly. We don't currently take the + * Z-compare function into account, but we could skip loading the + * depthstencil for D3DCMP_NEVER and D3DCMP_ALWAYS as well. Also note + * that we never copy the stencil data.*/ + DWORD location = context->render_offscreen ? dsv->resource->draw_binding : WINED3D_LOCATION_DRAWABLE; + + if (wined3d_state_uses_depth_buffer(state)) + wined3d_rendertarget_view_load_location(dsv, context, location); + else + wined3d_rendertarget_view_prepare_location(dsv, context, location); + } + + if (parameters->indirect) + wined3d_buffer_load(parameters->u.indirect.buffer, context, state); + + if (!context_apply_draw_state(context, device, state, parameters->indexed)) + { + context_release(context); + WARN("Unable to apply draw state, skipping draw.\n"); + return; + } + + if (dsv && (!state->depth_stencil_state || state->depth_stencil_state->desc.depth_write)) + { + DWORD location = context->render_offscreen ? dsv->resource->draw_binding : WINED3D_LOCATION_DRAWABLE; + + wined3d_rendertarget_view_validate_location(dsv, location); + wined3d_rendertarget_view_invalidate_location(dsv, ~location); + } + + stream_info = &context->stream_info; + + if (parameters->indexed) + { + struct wined3d_buffer *index_buffer = state->index_buffer; + if (!index_buffer->buffer_object || !stream_info->all_vbo) + idx_data = index_buffer->resource.heap_memory; + else + idx_data = NULL; + idx_data = (const BYTE *)idx_data + state->index_offset; + + if (state->index_format == WINED3DFMT_R16_UINT) + idx_size = 2; + else + idx_size = 4; + } + + if (!use_vs(state)) + { + if (!stream_info->position_transformed && context_gl->untracked_material_count + && state->render_states[WINED3D_RS_LIGHTING]) + { + static BOOL warned; + + if (!warned++) + FIXME("Using software emulation because not all material properties could be tracked.\n"); + else + WARN_(d3d_perf)("Using software emulation because not all material properties could be tracked.\n"); + emulation = TRUE; + } + else if (context->fog_coord && state->render_states[WINED3D_RS_FOGENABLE]) + { + static BOOL warned; + + /* Either write a pipeline replacement shader or convert the + * specular alpha from unsigned byte to a float in the vertex + * buffer. */ + if (!warned++) + FIXME("Using software emulation because manual fog coordinates are provided.\n"); + else + WARN_(d3d_perf)("Using software emulation because manual fog coordinates are provided.\n"); + emulation = TRUE; + } + + if (emulation) + { + si_emulated = context->stream_info; + remove_vbos(context, state, &si_emulated); + stream_info = &si_emulated; + } + } + + if (use_transform_feedback(state)) + { + const struct wined3d_shader *shader = state->shader[WINED3D_SHADER_TYPE_GEOMETRY]; + + if (is_rasterization_disabled(shader)) + { + glEnable(GL_RASTERIZER_DISCARD); + checkGLcall("enable rasterizer discard"); + rasterizer_discard = TRUE; + } + + if (context->transform_feedback_paused) + { + GL_EXTCALL(glResumeTransformFeedback()); + checkGLcall("glResumeTransformFeedback"); + context->transform_feedback_paused = 0; + } + else if (!context->transform_feedback_active) + { + enum wined3d_primitive_type primitive_type = shader->u.gs.output_type + ? shader->u.gs.output_type : state->primitive_type; + GLenum mode = gl_tfb_primitive_type_from_d3d(primitive_type); + GL_EXTCALL(glBeginTransformFeedback(mode)); + checkGLcall("glBeginTransformFeedback"); + context->transform_feedback_active = 1; + } + } + + if (state->primitive_type == WINED3D_PT_PATCH) + { + GL_EXTCALL(glPatchParameteri(GL_PATCH_VERTICES, state->patch_vertex_count)); + checkGLcall("glPatchParameteri"); + } + + if (context->uses_fbo_attached_resources) + { + static unsigned int fixme_once; + + if (gl_info->supported[ARB_TEXTURE_BARRIER]) + { + GL_EXTCALL(glTextureBarrier()); + } + else if (gl_info->supported[NV_TEXTURE_BARRIER]) + { + GL_EXTCALL(glTextureBarrierNV()); + } + else + { + if (!fixme_once++) + FIXME("Sampling attached render targets is not supported.\n"); + + WARN("Sampling attached render targets is not supported, skipping draw.\n"); + context_release(context); + return; + } + checkGLcall("glTextureBarrier"); + } + + if (parameters->indirect) + { + if (!context->use_immediate_mode_draw && !emulation) + wined3d_context_gl_draw_indirect(context_gl, state, ¶meters->u.indirect, idx_size); + else + FIXME("Indirect draws with immediate mode/emulation are not supported.\n"); + } + else + { + unsigned int instance_count = parameters->u.direct.instance_count; + if (context->instance_count) + instance_count = context->instance_count; + + if (context->use_immediate_mode_draw || emulation) + draw_primitive_immediate_mode(wined3d_context_gl(context), state, stream_info, idx_data, + idx_size, parameters->u.direct.base_vertex_idx, + parameters->u.direct.start_idx, parameters->u.direct.index_count, instance_count); + else + wined3d_context_gl_draw_primitive_arrays(context_gl, state, idx_data, idx_size, + parameters->u.direct.base_vertex_idx, parameters->u.direct.start_idx, + parameters->u.direct.index_count, parameters->u.direct.start_instance, instance_count); + } + + if (context->uses_uavs) + { + GL_EXTCALL(glMemoryBarrier(GL_ALL_BARRIER_BITS)); + checkGLcall("glMemoryBarrier"); + } + + wined3d_context_gl_pause_transform_feedback(context_gl, FALSE); + + if (rasterizer_discard) + { + glDisable(GL_RASTERIZER_DISCARD); + checkGLcall("disable rasterizer discard"); + } + + context_release(context); + + TRACE("Draw completed.\n"); +} + +void wined3d_context_gl_unload_tex_coords(const struct wined3d_context_gl *context_gl) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + unsigned int texture_idx; + + for (texture_idx = 0; texture_idx < gl_info->limits.texture_coords; ++texture_idx) + { + gl_info->gl_ops.ext.p_glClientActiveTextureARB(GL_TEXTURE0_ARB + texture_idx); + gl_info->gl_ops.gl.p_glDisableClientState(GL_TEXTURE_COORD_ARRAY); + } +} + +void wined3d_context_gl_load_tex_coords(const struct wined3d_context_gl *context_gl, + const struct wined3d_stream_info *si, GLuint *current_bo, const struct wined3d_state *state) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + const struct wined3d_format_gl *format_gl; + unsigned int mapped_stage = 0; + unsigned int texture_idx; + GLuint bo; + + for (texture_idx = 0; texture_idx < context_gl->c.d3d_info->limits.ffp_blend_stages; ++texture_idx) + { + unsigned int coord_idx = state->texture_states[texture_idx][WINED3D_TSS_TEXCOORD_INDEX]; + + if ((mapped_stage = context_gl->tex_unit_map[texture_idx]) == WINED3D_UNMAPPED_STAGE) + continue; + + if (mapped_stage >= gl_info->limits.texture_coords) + { + FIXME("Attempted to load unsupported texture coordinate %u.\n", mapped_stage); + continue; + } + + if (coord_idx < WINED3D_MAX_TEXTURES && (si->use_map & (1u << (WINED3D_FFP_TEXCOORD0 + coord_idx)))) + { + const struct wined3d_stream_info_element *e = &si->elements[WINED3D_FFP_TEXCOORD0 + coord_idx]; + + TRACE("Setting up texture %u, idx %u, coord_idx %u, data %s.\n", + texture_idx, mapped_stage, coord_idx, debug_bo_address(&e->data)); + + bo = wined3d_bo_gl_id(e->data.buffer_object); + if (*current_bo != bo) + { + GL_EXTCALL(glBindBuffer(GL_ARRAY_BUFFER, bo)); + checkGLcall("glBindBuffer"); + *current_bo = bo; + } + + GL_EXTCALL(glClientActiveTextureARB(GL_TEXTURE0_ARB + mapped_stage)); + checkGLcall("glClientActiveTextureARB"); + + /* The coords to supply depend completely on the fvf/vertex shader. */ + format_gl = wined3d_format_gl(e->format); + gl_info->gl_ops.gl.p_glTexCoordPointer(format_gl->vtx_format, format_gl->vtx_type, e->stride, + e->data.addr + state->load_base_vertex_index * e->stride); + gl_info->gl_ops.gl.p_glEnableClientState(GL_TEXTURE_COORD_ARRAY); + wined3d_buffer_gl(state->streams[e->stream_idx].buffer)->bo_user.valid = true; + } + else + { + GL_EXTCALL(glMultiTexCoord4fARB(GL_TEXTURE0_ARB + mapped_stage, 0, 0, 0, 1)); + } + } + if (gl_info->supported[NV_REGISTER_COMBINERS]) + { + /* The number of the mapped stages increases monotonically, so it's fine to use the last used one. */ + for (texture_idx = mapped_stage + 1; texture_idx < gl_info->limits.textures; ++texture_idx) + { + GL_EXTCALL(glMultiTexCoord4fARB(GL_TEXTURE0_ARB + texture_idx, 0, 0, 0, 1)); + } + } + + checkGLcall("loadTexCoords"); +} + +/* This should match any arrays loaded in wined3d_context_gl_load_vertex_data(). */ +static void wined3d_context_gl_unload_vertex_data(struct wined3d_context_gl *context_gl) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + + if (!context_gl->c.namedArraysLoaded) + return; + gl_info->gl_ops.gl.p_glDisableClientState(GL_VERTEX_ARRAY); + gl_info->gl_ops.gl.p_glDisableClientState(GL_NORMAL_ARRAY); + gl_info->gl_ops.gl.p_glDisableClientState(GL_COLOR_ARRAY); + if (gl_info->supported[EXT_SECONDARY_COLOR]) + gl_info->gl_ops.gl.p_glDisableClientState(GL_SECONDARY_COLOR_ARRAY_EXT); + wined3d_context_gl_unload_tex_coords(context_gl); + context_gl->c.namedArraysLoaded = FALSE; +} + +static void wined3d_context_gl_load_vertex_data(struct wined3d_context_gl *context_gl, + const struct wined3d_stream_info *si, const struct wined3d_state *state) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + const struct wined3d_stream_info_element *e; + const struct wined3d_format_gl *format_gl; + GLuint current_bo, bo; + + TRACE("context_gl %p, si %p, state %p.\n", context_gl, si, state); + + /* This is used for the fixed-function pipeline only, and the + * fixed-function pipeline doesn't do instancing. */ + context_gl->c.instance_count = 0; + current_bo = gl_info->supported[ARB_VERTEX_BUFFER_OBJECT] ? ~0u : 0; + + /* Blend data */ + if ((si->use_map & (1u << WINED3D_FFP_BLENDWEIGHT)) + || si->use_map & (1u << WINED3D_FFP_BLENDINDICES)) + { + /* TODO: Support vertex blending in immediate mode draws. No need to + * write a FIXME here, this is done after the general vertex + * declaration decoding. */ + WARN("Vertex blending not supported.\n"); + } + + /* Point Size */ + if (si->use_map & (1u << WINED3D_FFP_PSIZE)) + { + /* No such functionality in the fixed-function GL pipeline. */ + WARN("Per-vertex point size not supported.\n"); + } + + /* Position */ + if (si->use_map & (1u << WINED3D_FFP_POSITION)) + { + e = &si->elements[WINED3D_FFP_POSITION]; + format_gl = wined3d_format_gl(e->format); + + bo = wined3d_bo_gl_id(e->data.buffer_object); + if (current_bo != bo) + { + GL_EXTCALL(glBindBuffer(GL_ARRAY_BUFFER, bo)); + checkGLcall("glBindBuffer"); + current_bo = bo; + } + + TRACE("glVertexPointer(%#x, %#x, %#x, %p);\n", + format_gl->vtx_format, format_gl->vtx_type, e->stride, + e->data.addr + state->load_base_vertex_index * e->stride); + gl_info->gl_ops.gl.p_glVertexPointer(format_gl->vtx_format, format_gl->vtx_type, e->stride, + e->data.addr + state->load_base_vertex_index * e->stride); + checkGLcall("glVertexPointer(...)"); + gl_info->gl_ops.gl.p_glEnableClientState(GL_VERTEX_ARRAY); + checkGLcall("glEnableClientState(GL_VERTEX_ARRAY)"); + wined3d_buffer_gl(state->streams[e->stream_idx].buffer)->bo_user.valid = true; + } + + /* Normals */ + if (si->use_map & (1u << WINED3D_FFP_NORMAL)) + { + e = &si->elements[WINED3D_FFP_NORMAL]; + format_gl = wined3d_format_gl(e->format); + + bo = wined3d_bo_gl_id(e->data.buffer_object); + if (current_bo != bo) + { + GL_EXTCALL(glBindBuffer(GL_ARRAY_BUFFER, bo)); + checkGLcall("glBindBuffer"); + current_bo = bo; + } + + TRACE("glNormalPointer(%#x, %#x, %p);\n", format_gl->vtx_type, e->stride, + e->data.addr + state->load_base_vertex_index * e->stride); + gl_info->gl_ops.gl.p_glNormalPointer(format_gl->vtx_type, e->stride, + e->data.addr + state->load_base_vertex_index * e->stride); + checkGLcall("glNormalPointer(...)"); + gl_info->gl_ops.gl.p_glEnableClientState(GL_NORMAL_ARRAY); + checkGLcall("glEnableClientState(GL_NORMAL_ARRAY)"); + wined3d_buffer_gl(state->streams[e->stream_idx].buffer)->bo_user.valid = true; + } + else + { + gl_info->gl_ops.gl.p_glNormal3f(0, 0, 0); + checkGLcall("glNormal3f(0, 0, 0)"); + } + + /* Diffuse colour */ + if (si->use_map & (1u << WINED3D_FFP_DIFFUSE)) + { + e = &si->elements[WINED3D_FFP_DIFFUSE]; + format_gl = wined3d_format_gl(e->format); + + bo = wined3d_bo_gl_id(e->data.buffer_object); + if (current_bo != bo) + { + GL_EXTCALL(glBindBuffer(GL_ARRAY_BUFFER, bo)); + checkGLcall("glBindBuffer"); + current_bo = bo; + } + + TRACE("glColorPointer(%#x, %#x %#x, %p);\n", + format_gl->vtx_format, format_gl->vtx_type, e->stride, + e->data.addr + state->load_base_vertex_index * e->stride); + gl_info->gl_ops.gl.p_glColorPointer(format_gl->vtx_format, format_gl->vtx_type, e->stride, + e->data.addr + state->load_base_vertex_index * e->stride); + checkGLcall("glColorPointer(4, GL_UNSIGNED_BYTE, ...)"); + gl_info->gl_ops.gl.p_glEnableClientState(GL_COLOR_ARRAY); + checkGLcall("glEnableClientState(GL_COLOR_ARRAY)"); + wined3d_buffer_gl(state->streams[e->stream_idx].buffer)->bo_user.valid = true; + } + else + { + gl_info->gl_ops.gl.p_glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + checkGLcall("glColor4f(1, 1, 1, 1)"); + } + + /* Specular colour */ + if (si->use_map & (1u << WINED3D_FFP_SPECULAR)) + { + TRACE("Setting specular colour.\n"); + + e = &si->elements[WINED3D_FFP_SPECULAR]; + + if (gl_info->supported[EXT_SECONDARY_COLOR]) + { + GLint format; + GLenum type; + + format_gl = wined3d_format_gl(e->format); + type = format_gl->vtx_type; + format = format_gl->vtx_format; + + bo = wined3d_bo_gl_id(e->data.buffer_object); + if (current_bo != bo) + { + GL_EXTCALL(glBindBuffer(GL_ARRAY_BUFFER, bo)); + checkGLcall("glBindBuffer"); + current_bo = bo; + } + + if (format != 4 || (gl_info->quirks & WINED3D_QUIRK_ALLOWS_SPECULAR_ALPHA)) + { + /* Usually specular colors only allow 3 components, since they have no alpha. In D3D, the specular alpha + * contains the fog coordinate, which is passed to GL with GL_EXT_fog_coord. However, the fixed function + * vertex pipeline can pass the specular alpha through, and pixel shaders can read it. So it GL accepts + * 4 component secondary colors use it + */ + TRACE("glSecondaryColorPointer(%#x, %#x, %#x, %p);\n", format, type, e->stride, + e->data.addr + state->load_base_vertex_index * e->stride); + GL_EXTCALL(glSecondaryColorPointerEXT(format, type, e->stride, + e->data.addr + state->load_base_vertex_index * e->stride)); + checkGLcall("glSecondaryColorPointerEXT(format, type, ...)"); + } + else + { + switch (type) + { + case GL_UNSIGNED_BYTE: + TRACE("glSecondaryColorPointer(3, GL_UNSIGNED_BYTE, %#x, %p);\n", e->stride, + e->data.addr + state->load_base_vertex_index * e->stride); + GL_EXTCALL(glSecondaryColorPointerEXT(3, GL_UNSIGNED_BYTE, e->stride, + e->data.addr + state->load_base_vertex_index * e->stride)); + checkGLcall("glSecondaryColorPointerEXT(3, GL_UNSIGNED_BYTE, ...)"); + break; + + default: + FIXME("Add 4 component specular colour pointers for type %#x.\n", type); + /* Make sure that the right colour component is dropped. */ + TRACE("glSecondaryColorPointer(3, %#x, %#x, %p);\n", type, e->stride, + e->data.addr + state->load_base_vertex_index * e->stride); + GL_EXTCALL(glSecondaryColorPointerEXT(3, type, e->stride, + e->data.addr + state->load_base_vertex_index * e->stride)); + checkGLcall("glSecondaryColorPointerEXT(3, type, ...)"); + } + } + gl_info->gl_ops.gl.p_glEnableClientState(GL_SECONDARY_COLOR_ARRAY_EXT); + checkGLcall("glEnableClientState(GL_SECONDARY_COLOR_ARRAY_EXT)"); + wined3d_buffer_gl(state->streams[e->stream_idx].buffer)->bo_user.valid = true; + } + else + { + WARN("Specular colour is not supported in this GL implementation.\n"); + } + } + else + { + if (gl_info->supported[EXT_SECONDARY_COLOR]) + { + GL_EXTCALL(glSecondaryColor3fEXT)(0, 0, 0); + checkGLcall("glSecondaryColor3fEXT(0, 0, 0)"); + } + else + { + WARN("Specular colour is not supported in this GL implementation.\n"); + } + } + + /* Texture coordinates */ + wined3d_context_gl_load_tex_coords(context_gl, si, ¤t_bo, state); +} + +static void wined3d_context_gl_unload_numbered_array(struct wined3d_context_gl *context_gl, unsigned int i) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + + GL_EXTCALL(glDisableVertexAttribArray(i)); + checkGLcall("glDisableVertexAttribArray"); + if (gl_info->supported[ARB_INSTANCED_ARRAYS]) + GL_EXTCALL(glVertexAttribDivisor(i, 0)); + + context_gl->c.numbered_array_mask &= ~(1u << i); +} + +static void wined3d_context_gl_unload_numbered_arrays(struct wined3d_context_gl *context_gl) +{ + uint32_t mask = context_gl->c.numbered_array_mask; + unsigned int i; + + while (mask) + { + i = wined3d_bit_scan(&mask); + wined3d_context_gl_unload_numbered_array(context_gl, i); + } +} + +static void wined3d_context_gl_load_numbered_arrays(struct wined3d_context_gl *context_gl, + const struct wined3d_stream_info *stream_info, const struct wined3d_state *state) +{ + struct wined3d_context *context = &context_gl->c; + const struct wined3d_shader *vs = state->shader[WINED3D_SHADER_TYPE_VERTEX]; + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + GLuint current_bo, bo; + unsigned int i; + + /* Default to no instancing. */ + context->instance_count = 0; + current_bo = gl_info->supported[ARB_VERTEX_BUFFER_OBJECT] ? ~0u : 0; + + for (i = 0; i < MAX_ATTRIBS; ++i) + { + const struct wined3d_stream_info_element *element = &stream_info->elements[i]; + const struct wined3d_stream_state *stream; + const struct wined3d_format_gl *format_gl; + + if (!(stream_info->use_map & (1u << i))) + { + if (context->numbered_array_mask & (1u << i)) + wined3d_context_gl_unload_numbered_array(context_gl, i); + if (!use_vs(state) && i == WINED3D_FFP_DIFFUSE) + { + if (!(context_gl->default_attrib_value_set & (1u << i)) || !context_gl->diffuse_attrib_to_1) + { + GL_EXTCALL(glVertexAttrib4f(i, 1.0f, 1.0f, 1.0f, 1.0f)); + context_gl->diffuse_attrib_to_1 = 1; + } + } + else + { + if (!(context_gl->default_attrib_value_set & (1u << i))) + { + GL_EXTCALL(glVertexAttrib4f(i, 0.0f, 0.0f, 0.0f, 0.0f)); + if (i == WINED3D_FFP_DIFFUSE) + context_gl->diffuse_attrib_to_1 = 0; + } + } + context_gl->default_attrib_value_set |= 1u << i; + continue; + } + + format_gl = wined3d_format_gl(element->format); + stream = &state->streams[element->stream_idx]; + wined3d_buffer_gl(stream->buffer)->bo_user.valid = true; + + if ((stream->flags & WINED3DSTREAMSOURCE_INSTANCEDATA) && !context->instance_count) + context->instance_count = state->streams[0].frequency; + + if (gl_info->supported[ARB_INSTANCED_ARRAYS]) + { + GL_EXTCALL(glVertexAttribDivisor(i, element->divisor)); + } + else if (element->divisor) + { + /* Unload instanced arrays, they will be loaded using immediate + * mode instead. */ + if (context->numbered_array_mask & (1u << i)) + wined3d_context_gl_unload_numbered_array(context_gl, i); + context_gl->default_attrib_value_set &= ~(1u << i); + continue; + } + + TRACE("Loading array %u %s.\n", i, debug_bo_address(&element->data)); + + if (element->stride) + { + DWORD format_flags = format_gl->f.flags[WINED3D_GL_RES_TYPE_BUFFER]; + + bo = wined3d_bo_gl_id(element->data.buffer_object); + if (current_bo != bo) + { + GL_EXTCALL(glBindBuffer(GL_ARRAY_BUFFER, bo)); + checkGLcall("glBindBuffer"); + current_bo = bo; + } + /* Use the VBO to find out if a vertex buffer exists, not the vb + * pointer. vb can point to a user pointer data blob. In that case + * current_bo will be 0. If there is a vertex buffer but no vbo we + * won't be load converted attributes anyway. */ + if (vs && vs->reg_maps.shader_version.major >= 4 && (format_flags & WINED3DFMT_FLAG_INTEGER)) + { + GL_EXTCALL(glVertexAttribIPointer(i, format_gl->vtx_format, format_gl->vtx_type, + element->stride, element->data.addr + state->load_base_vertex_index * element->stride)); + } + else + { + GL_EXTCALL(glVertexAttribPointer(i, format_gl->vtx_format, format_gl->vtx_type, + !!(format_flags & WINED3DFMT_FLAG_NORMALISED), element->stride, + element->data.addr + state->load_base_vertex_index * element->stride)); + } + + if (!(context->numbered_array_mask & (1u << i))) + { + GL_EXTCALL(glEnableVertexAttribArray(i)); + context->numbered_array_mask |= (1u << i); + } + } + else + { + /* Stride = 0 means always the same values. + * glVertexAttribPointer() doesn't do that. Instead disable the + * pointer and set up the attribute statically. But we have to + * figure out the system memory address. */ + const BYTE *ptr = element->data.addr; + if (element->data.buffer_object) + ptr += (ULONG_PTR)wined3d_buffer_load_sysmem(stream->buffer, context); + + if (context->numbered_array_mask & (1u << i)) + wined3d_context_gl_unload_numbered_array(context_gl, i); + + switch (format_gl->f.id) + { + case WINED3DFMT_R32_FLOAT: + GL_EXTCALL(glVertexAttrib1fv(i, (const GLfloat *)ptr)); + break; + case WINED3DFMT_R32G32_FLOAT: + GL_EXTCALL(glVertexAttrib2fv(i, (const GLfloat *)ptr)); + break; + case WINED3DFMT_R32G32B32_FLOAT: + GL_EXTCALL(glVertexAttrib3fv(i, (const GLfloat *)ptr)); + break; + case WINED3DFMT_R32G32B32A32_FLOAT: + GL_EXTCALL(glVertexAttrib4fv(i, (const GLfloat *)ptr)); + break; + case WINED3DFMT_R8G8B8A8_UINT: + GL_EXTCALL(glVertexAttrib4ubv(i, ptr)); + break; + case WINED3DFMT_B8G8R8A8_UNORM: + if (gl_info->supported[ARB_VERTEX_ARRAY_BGRA]) + { + const DWORD *src = (const DWORD *)ptr; + DWORD c = *src & 0xff00ff00u; + c |= (*src & 0xff0000u) >> 16; + c |= (*src & 0xffu) << 16; + GL_EXTCALL(glVertexAttrib4Nubv(i, (GLubyte *)&c)); + break; + } + /* else fallthrough */ + case WINED3DFMT_R8G8B8A8_UNORM: + GL_EXTCALL(glVertexAttrib4Nubv(i, ptr)); + break; + case WINED3DFMT_R16G16_SINT: + GL_EXTCALL(glVertexAttrib2sv(i, (const GLshort *)ptr)); + break; + case WINED3DFMT_R16G16B16A16_SINT: + GL_EXTCALL(glVertexAttrib4sv(i, (const GLshort *)ptr)); + break; + case WINED3DFMT_R16G16_SNORM: + { + const GLshort s[4] = {((const GLshort *)ptr)[0], ((const GLshort *)ptr)[1], 0, 1}; + GL_EXTCALL(glVertexAttrib4Nsv(i, s)); + break; + } + case WINED3DFMT_R16G16_UNORM: + { + const GLushort s[4] = {((const GLushort *)ptr)[0], ((const GLushort *)ptr)[1], 0, 1}; + GL_EXTCALL(glVertexAttrib4Nusv(i, s)); + break; + } + case WINED3DFMT_R16G16B16A16_SNORM: + GL_EXTCALL(glVertexAttrib4Nsv(i, (const GLshort *)ptr)); + break; + case WINED3DFMT_R16G16B16A16_UNORM: + GL_EXTCALL(glVertexAttrib4Nusv(i, (const GLushort *)ptr)); + break; + case WINED3DFMT_R10G10B10X2_UINT: + FIXME("Unsure about WINED3DDECLTYPE_UDEC3.\n"); + /*glVertexAttrib3usvARB(i, (const GLushort *)ptr); Does not exist */ + break; + case WINED3DFMT_R10G10B10X2_SNORM: + FIXME("Unsure about WINED3DDECLTYPE_DEC3N.\n"); + /*glVertexAttrib3NusvARB(i, (const GLushort *)ptr); Does not exist */ + break; + case WINED3DFMT_R16G16_FLOAT: + if (gl_info->supported[NV_HALF_FLOAT] && gl_info->supported[NV_VERTEX_PROGRAM]) + { + /* Not supported by GL_ARB_half_float_vertex. */ + GL_EXTCALL(glVertexAttrib2hvNV(i, (const GLhalfNV *)ptr)); + } + else + { + float x = float_16_to_32(((const unsigned short *)ptr) + 0); + float y = float_16_to_32(((const unsigned short *)ptr) + 1); + GL_EXTCALL(glVertexAttrib2f(i, x, y)); + } + break; + case WINED3DFMT_R16G16B16A16_FLOAT: + if (gl_info->supported[NV_HALF_FLOAT] && gl_info->supported[NV_VERTEX_PROGRAM]) + { + /* Not supported by GL_ARB_half_float_vertex. */ + GL_EXTCALL(glVertexAttrib4hvNV(i, (const GLhalfNV *)ptr)); + } + else + { + float x = float_16_to_32(((const unsigned short *)ptr) + 0); + float y = float_16_to_32(((const unsigned short *)ptr) + 1); + float z = float_16_to_32(((const unsigned short *)ptr) + 2); + float w = float_16_to_32(((const unsigned short *)ptr) + 3); + GL_EXTCALL(glVertexAttrib4f(i, x, y, z, w)); + } + break; + default: + ERR("Unexpected declaration in stride 0 attributes.\n"); + break; + + } + context_gl->default_attrib_value_set &= ~(1u << i); + } + } + checkGLcall("Loading numbered arrays"); +} + +void wined3d_context_gl_update_stream_sources(struct wined3d_context_gl *context_gl, + const struct wined3d_state *state) +{ + if (context_gl->c.use_immediate_mode_draw) + return; + + wined3d_context_gl_unload_vertex_data(context_gl); + if (context_gl->c.d3d_info->ffp_generic_attributes || use_vs(state)) + { + TRACE("Loading numbered arrays.\n"); + wined3d_context_gl_load_numbered_arrays(context_gl, &context_gl->c.stream_info, state); + return; + } + + TRACE("Loading named arrays.\n"); + wined3d_context_gl_unload_numbered_arrays(context_gl); + wined3d_context_gl_load_vertex_data(context_gl, &context_gl->c.stream_info, state); + context_gl->c.namedArraysLoaded = TRUE; +} + +static void apply_texture_blit_state(const struct wined3d_gl_info *gl_info, struct gl_texture *texture, + GLenum target, unsigned int level, enum wined3d_texture_filter_type filter) +{ + gl_info->gl_ops.gl.p_glTexParameteri(target, GL_TEXTURE_MAG_FILTER, wined3d_gl_mag_filter(filter)); + gl_info->gl_ops.gl.p_glTexParameteri(target, GL_TEXTURE_MIN_FILTER, + wined3d_gl_min_mip_filter(filter, WINED3D_TEXF_NONE)); + gl_info->gl_ops.gl.p_glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + gl_info->gl_ops.gl.p_glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + if (gl_info->supported[EXT_TEXTURE_SRGB_DECODE]) + gl_info->gl_ops.gl.p_glTexParameteri(target, GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT); + gl_info->gl_ops.gl.p_glTexParameteri(target, GL_TEXTURE_BASE_LEVEL, level); + + /* We changed the filtering settings on the texture. Make sure they get + * reset on subsequent draws. */ + texture->sampler_desc.mag_filter = WINED3D_TEXF_POINT; + texture->sampler_desc.min_filter = WINED3D_TEXF_POINT; + texture->sampler_desc.mip_filter = WINED3D_TEXF_NONE; + texture->sampler_desc.address_u = WINED3D_TADDRESS_CLAMP; + texture->sampler_desc.address_v = WINED3D_TADDRESS_CLAMP; + texture->sampler_desc.srgb_decode = FALSE; + texture->base_level = level; +} + +/* Context activation is done by the caller. */ +void wined3d_context_gl_draw_shaded_quad(struct wined3d_context_gl *context_gl, struct wined3d_texture_gl *texture_gl, + unsigned int sub_resource_idx, const RECT *src_rect, const RECT *dst_rect, + enum wined3d_texture_filter_type filter) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + struct wined3d_blt_info info; + unsigned int level, w, h, i; + SIZE dst_size; + struct blit_vertex + { + float x, y; + struct wined3d_vec3 texcoord; + } + quad[4]; + + texture2d_get_blt_info(texture_gl, sub_resource_idx, src_rect, &info); + + level = sub_resource_idx % texture_gl->t.level_count; + wined3d_context_gl_bind_texture(context_gl, info.bind_target, texture_gl->texture_rgb.name); + apply_texture_blit_state(gl_info, &texture_gl->texture_rgb, info.bind_target, level, filter); + gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MAX_LEVEL, level); + + wined3d_context_gl_get_rt_size(context_gl, &dst_size); + w = dst_size.cx; + h = dst_size.cy; + + quad[0].x = dst_rect->left * 2.0f / w - 1.0f; + quad[0].y = dst_rect->top * 2.0f / h - 1.0f; + quad[0].texcoord = info.texcoords[0]; + + quad[1].x = dst_rect->right * 2.0f / w - 1.0f; + quad[1].y = dst_rect->top * 2.0f / h - 1.0f; + quad[1].texcoord = info.texcoords[1]; + + quad[2].x = dst_rect->left * 2.0f / w - 1.0f; + quad[2].y = dst_rect->bottom * 2.0f / h - 1.0f; + quad[2].texcoord = info.texcoords[2]; + + quad[3].x = dst_rect->right * 2.0f / w - 1.0f; + quad[3].y = dst_rect->bottom * 2.0f / h - 1.0f; + quad[3].texcoord = info.texcoords[3]; + + /* Draw a quad. */ + if (gl_info->supported[ARB_VERTEX_BUFFER_OBJECT]) + { + if (!context_gl->blit_vbo) + GL_EXTCALL(glGenBuffers(1, &context_gl->blit_vbo)); + GL_EXTCALL(glBindBuffer(GL_ARRAY_BUFFER, context_gl->blit_vbo)); + + wined3d_context_gl_unload_vertex_data(context_gl); + wined3d_context_gl_unload_numbered_arrays(context_gl); + + GL_EXTCALL(glBufferData(GL_ARRAY_BUFFER, sizeof(quad), quad, GL_STREAM_DRAW)); + GL_EXTCALL(glVertexAttribPointer(0, 2, GL_FLOAT, FALSE, sizeof(*quad), NULL)); + GL_EXTCALL(glVertexAttribPointer(1, 3, GL_FLOAT, FALSE, sizeof(*quad), + (void *)FIELD_OFFSET(struct blit_vertex, texcoord))); + + GL_EXTCALL(glEnableVertexAttribArray(0)); + GL_EXTCALL(glEnableVertexAttribArray(1)); + + gl_info->gl_ops.gl.p_glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + GL_EXTCALL(glBindBuffer(GL_ARRAY_BUFFER, 0)); + GL_EXTCALL(glDisableVertexAttribArray(1)); + GL_EXTCALL(glDisableVertexAttribArray(0)); + } + else + { + gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP); + + for (i = 0; i < ARRAY_SIZE(quad); ++i) + { + GL_EXTCALL(glVertexAttrib3fv(1, &quad[i].texcoord.x)); + GL_EXTCALL(glVertexAttrib2fv(0, &quad[i].x)); + } + + gl_info->gl_ops.gl.p_glEnd(); + } + checkGLcall("draw"); + + gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MAX_LEVEL, texture_gl->t.level_count - 1); + wined3d_context_gl_bind_texture(context_gl, info.bind_target, 0); +} + +/* Context activation is done by the caller. */ +void wined3d_context_gl_draw_textured_quad(struct wined3d_context_gl *context_gl, + struct wined3d_texture_gl *texture_gl, unsigned int sub_resource_idx, + const RECT *src_rect, const RECT *dst_rect, enum wined3d_texture_filter_type filter) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + struct wined3d_blt_info info; + unsigned int level; + + texture2d_get_blt_info(texture_gl, sub_resource_idx, src_rect, &info); + + gl_info->gl_ops.gl.p_glEnable(info.bind_target); + checkGLcall("glEnable(bind_target)"); + + level = sub_resource_idx % texture_gl->t.level_count; + wined3d_context_gl_bind_texture(context_gl, info.bind_target, texture_gl->texture_rgb.name); + apply_texture_blit_state(gl_info, &texture_gl->texture_rgb, info.bind_target, level, filter); + gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MAX_LEVEL, level); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + checkGLcall("glTexEnvi"); + + /* Draw a quad. */ + gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP); + gl_info->gl_ops.gl.p_glTexCoord3fv(&info.texcoords[0].x); + gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->top); + + gl_info->gl_ops.gl.p_glTexCoord3fv(&info.texcoords[1].x); + gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->top); + + gl_info->gl_ops.gl.p_glTexCoord3fv(&info.texcoords[2].x); + gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->bottom); + + gl_info->gl_ops.gl.p_glTexCoord3fv(&info.texcoords[3].x); + gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->bottom); + gl_info->gl_ops.gl.p_glEnd(); + + gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MAX_LEVEL, texture_gl->t.level_count - 1); + wined3d_context_gl_bind_texture(context_gl, info.bind_target, 0); +} diff --git a/wrappers/directx/d3dwine_wrapper/context_vk.c b/wrappers/directx/d3dwine_wrapper/context_vk.c new file mode 100644 index 00000000000..f24f6bec6df --- /dev/null +++ b/wrappers/directx/d3dwine_wrapper/context_vk.c @@ -0,0 +1,3219 @@ +/* + * Copyright 2002-2004 Jason Edmeades + * Copyright 2002-2004 Raphael Junqueira + * Copyright 2004 Christian Costa + * Copyright 2005 Oliver Stieber + * Copyright 2006, 2008 Henri Verbeet + * Copyright 2007-2011, 2013 Stefan Dösinger for CodeWeavers + * Copyright 2009-2020 Henri Verbeet for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "config.h" +#include "wine/port.h" + +#include "wined3d_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(d3d); + +VkCompareOp vk_compare_op_from_wined3d(enum wined3d_cmp_func op) +{ + switch (op) + { + case WINED3D_CMP_NEVER: + return VK_COMPARE_OP_NEVER; + case WINED3D_CMP_LESS: + return VK_COMPARE_OP_LESS; + case WINED3D_CMP_EQUAL: + return VK_COMPARE_OP_EQUAL; + case WINED3D_CMP_LESSEQUAL: + return VK_COMPARE_OP_LESS_OR_EQUAL; + case WINED3D_CMP_GREATER: + return VK_COMPARE_OP_GREATER; + case WINED3D_CMP_NOTEQUAL: + return VK_COMPARE_OP_NOT_EQUAL; + case WINED3D_CMP_GREATEREQUAL: + return VK_COMPARE_OP_GREATER_OR_EQUAL; + case WINED3D_CMP_ALWAYS: + return VK_COMPARE_OP_ALWAYS; + default: + if (!op) + WARN("Unhandled compare operation %#x.\n", op); + else + FIXME("Unhandled compare operation %#x.\n", op); + return VK_COMPARE_OP_NEVER; + } +} + +VkShaderStageFlagBits vk_shader_stage_from_wined3d(enum wined3d_shader_type shader_type) +{ + switch (shader_type) + { + case WINED3D_SHADER_TYPE_VERTEX: + return VK_SHADER_STAGE_VERTEX_BIT; + case WINED3D_SHADER_TYPE_HULL: + return VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT; + case WINED3D_SHADER_TYPE_DOMAIN: + return VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT; + case WINED3D_SHADER_TYPE_GEOMETRY: + return VK_SHADER_STAGE_GEOMETRY_BIT; + case WINED3D_SHADER_TYPE_PIXEL: + return VK_SHADER_STAGE_FRAGMENT_BIT; + case WINED3D_SHADER_TYPE_COMPUTE: + return VK_SHADER_STAGE_COMPUTE_BIT; + default: + ERR("Unhandled shader type %s.\n", debug_shader_type(shader_type)); + return 0; + } +} + +static VkBlendFactor vk_blend_factor_from_wined3d(enum wined3d_blend blend, + const struct wined3d_format *dst_format, bool alpha) +{ + switch (blend) + { + case WINED3D_BLEND_ZERO: + return VK_BLEND_FACTOR_ZERO; + case WINED3D_BLEND_ONE: + return VK_BLEND_FACTOR_ONE; + case WINED3D_BLEND_SRCCOLOR: + return VK_BLEND_FACTOR_SRC_COLOR; + case WINED3D_BLEND_INVSRCCOLOR: + return VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR; + case WINED3D_BLEND_SRCALPHA: + return VK_BLEND_FACTOR_SRC_ALPHA; + case WINED3D_BLEND_INVSRCALPHA: + return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + case WINED3D_BLEND_DESTALPHA: + if (dst_format->alpha_size) + return VK_BLEND_FACTOR_DST_ALPHA; + return VK_BLEND_FACTOR_ONE; + case WINED3D_BLEND_INVDESTALPHA: + if (dst_format->alpha_size) + return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA; + return VK_BLEND_FACTOR_ZERO; + case WINED3D_BLEND_DESTCOLOR: + return VK_BLEND_FACTOR_DST_COLOR; + case WINED3D_BLEND_INVDESTCOLOR: + return VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR; + case WINED3D_BLEND_SRCALPHASAT: + return VK_BLEND_FACTOR_SRC_ALPHA_SATURATE; + case WINED3D_BLEND_BLENDFACTOR: + if (alpha) + return VK_BLEND_FACTOR_CONSTANT_ALPHA; + return VK_BLEND_FACTOR_CONSTANT_COLOR; + case WINED3D_BLEND_INVBLENDFACTOR: + if (alpha) + return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA; + return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR; + case WINED3D_BLEND_SRC1COLOR: + return VK_BLEND_FACTOR_SRC1_COLOR; + case WINED3D_BLEND_INVSRC1COLOR: + return VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR; + case WINED3D_BLEND_SRC1ALPHA: + return VK_BLEND_FACTOR_SRC1_ALPHA; + case WINED3D_BLEND_INVSRC1ALPHA: + return VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA; + default: + FIXME("Unhandled blend %#x.\n", blend); + return VK_BLEND_FACTOR_ZERO; + } +} + +static VkBlendOp vk_blend_op_from_wined3d(enum wined3d_blend_op op) +{ + switch (op) + { + case WINED3D_BLEND_OP_ADD: + return VK_BLEND_OP_ADD; + case WINED3D_BLEND_OP_SUBTRACT: + return VK_BLEND_OP_SUBTRACT; + case WINED3D_BLEND_OP_REVSUBTRACT: + return VK_BLEND_OP_REVERSE_SUBTRACT; + case WINED3D_BLEND_OP_MIN: + return VK_BLEND_OP_MIN; + case WINED3D_BLEND_OP_MAX: + return VK_BLEND_OP_MAX; + default: + FIXME("Unhandled blend op %#x.\n", op); + return VK_BLEND_OP_ADD; + } +} + +static VkColorComponentFlags vk_colour_write_mask_from_wined3d(uint32_t wined3d_mask) +{ + VkColorComponentFlags vk_mask = 0; + + if (wined3d_mask & WINED3DCOLORWRITEENABLE_RED) + vk_mask |= VK_COLOR_COMPONENT_R_BIT; + if (wined3d_mask & WINED3DCOLORWRITEENABLE_GREEN) + vk_mask |= VK_COLOR_COMPONENT_G_BIT; + if (wined3d_mask & WINED3DCOLORWRITEENABLE_BLUE) + vk_mask |= VK_COLOR_COMPONENT_B_BIT; + if (wined3d_mask & WINED3DCOLORWRITEENABLE_ALPHA) + vk_mask |= VK_COLOR_COMPONENT_A_BIT; + + return vk_mask; +} + +static VkCullModeFlags vk_cull_mode_from_wined3d(enum wined3d_cull mode) +{ + switch (mode) + { + case WINED3D_CULL_NONE: + return VK_CULL_MODE_NONE; + case WINED3D_CULL_FRONT: + return VK_CULL_MODE_FRONT_BIT; + case WINED3D_CULL_BACK: + return VK_CULL_MODE_BACK_BIT; + default: + FIXME("Unhandled cull mode %#x.\n", mode); + return VK_CULL_MODE_NONE; + } +} + +static VkPrimitiveTopology vk_topology_from_wined3d(enum wined3d_primitive_type t) +{ + switch (t) + { + case WINED3D_PT_POINTLIST: + return VK_PRIMITIVE_TOPOLOGY_POINT_LIST; + case WINED3D_PT_LINELIST: + return VK_PRIMITIVE_TOPOLOGY_LINE_LIST; + case WINED3D_PT_LINESTRIP: + return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP; + case WINED3D_PT_TRIANGLELIST: + return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + case WINED3D_PT_TRIANGLESTRIP: + return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP; + case WINED3D_PT_TRIANGLEFAN: + return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN; + case WINED3D_PT_LINELIST_ADJ: + return VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY; + case WINED3D_PT_LINESTRIP_ADJ: + return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY; + case WINED3D_PT_TRIANGLELIST_ADJ: + return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY; + case WINED3D_PT_TRIANGLESTRIP_ADJ: + return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY; + case WINED3D_PT_PATCH: + return VK_PRIMITIVE_TOPOLOGY_PATCH_LIST; + default: + FIXME("Unhandled primitive type %s.\n", debug_d3dprimitivetype(t)); + case WINED3D_PT_UNDEFINED: + return ~0u; + } +} + +static VkStencilOp vk_stencil_op_from_wined3d(enum wined3d_stencil_op op) +{ + switch (op) + { + case WINED3D_STENCIL_OP_KEEP: + return VK_STENCIL_OP_KEEP; + case WINED3D_STENCIL_OP_ZERO: + return VK_STENCIL_OP_ZERO; + case WINED3D_STENCIL_OP_REPLACE: + return VK_STENCIL_OP_REPLACE; + case WINED3D_STENCIL_OP_INCR_SAT: + return VK_STENCIL_OP_INCREMENT_AND_CLAMP; + case WINED3D_STENCIL_OP_DECR_SAT: + return VK_STENCIL_OP_DECREMENT_AND_CLAMP; + case WINED3D_STENCIL_OP_INVERT: + return VK_STENCIL_OP_INVERT; + case WINED3D_STENCIL_OP_INCR: + return VK_STENCIL_OP_INCREMENT_AND_WRAP; + case WINED3D_STENCIL_OP_DECR: + return VK_STENCIL_OP_DECREMENT_AND_WRAP; + default: + if (!op) + WARN("Unhandled stencil operation %#x.\n", op); + else + FIXME("Unhandled stencil operation %#x.\n", op); + return VK_STENCIL_OP_KEEP; + } +} + +void *wined3d_allocator_chunk_vk_map(struct wined3d_allocator_chunk_vk *chunk_vk, + struct wined3d_context_vk *context_vk) +{ + struct wined3d_device_vk *device_vk = wined3d_device_vk(context_vk->c.device); + const struct wined3d_vk_info *vk_info = context_vk->vk_info; + VkResult vr; + + TRACE("chunk %p, memory 0x%s, map_ptr %p.\n", chunk_vk, + wine_dbgstr_longlong(chunk_vk->vk_memory), chunk_vk->c.map_ptr); + + if (!chunk_vk->c.map_ptr && (vr = VK_CALL(vkMapMemory(device_vk->vk_device, + chunk_vk->vk_memory, 0, VK_WHOLE_SIZE, 0, &chunk_vk->c.map_ptr))) < 0) + { + ERR("Failed to map chunk memory, vr %s.\n", wined3d_debug_vkresult(vr)); + return NULL; + } + + ++chunk_vk->c.map_count; + + return chunk_vk->c.map_ptr; +} + +void wined3d_allocator_chunk_vk_unmap(struct wined3d_allocator_chunk_vk *chunk_vk, + struct wined3d_context_vk *context_vk) +{ + struct wined3d_device_vk *device_vk = wined3d_device_vk(context_vk->c.device); + const struct wined3d_vk_info *vk_info = context_vk->vk_info; + + TRACE("chunk_vk %p, context_vk %p.\n", chunk_vk, context_vk); + + if (--chunk_vk->c.map_count) + return; + + VK_CALL(vkUnmapMemory(device_vk->vk_device, chunk_vk->vk_memory)); + chunk_vk->c.map_ptr = NULL; +} + +VkDeviceMemory wined3d_context_vk_allocate_vram_chunk_memory(struct wined3d_context_vk *context_vk, + unsigned int pool, size_t size) +{ + struct wined3d_device_vk *device_vk = wined3d_device_vk(context_vk->c.device); + const struct wined3d_vk_info *vk_info = context_vk->vk_info; + VkMemoryAllocateInfo allocate_info; + VkDeviceMemory vk_memory; + VkResult vr; + + allocate_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + allocate_info.pNext = NULL; + allocate_info.allocationSize = size; + allocate_info.memoryTypeIndex = pool; + if ((vr = VK_CALL(vkAllocateMemory(device_vk->vk_device, &allocate_info, NULL, &vk_memory))) < 0) + { + ERR("Failed to allocate memory, vr %s.\n", wined3d_debug_vkresult(vr)); + return VK_NULL_HANDLE; + } + + return vk_memory; +} + +struct wined3d_allocator_block *wined3d_context_vk_allocate_memory(struct wined3d_context_vk *context_vk, + unsigned int memory_type, VkDeviceSize size, VkDeviceMemory *vk_memory) +{ + struct wined3d_device_vk *device_vk = wined3d_device_vk(context_vk->c.device); + struct wined3d_allocator *allocator = &device_vk->allocator; + struct wined3d_allocator_block *block; + + if (size > WINED3D_ALLOCATOR_CHUNK_SIZE / 2) + { + *vk_memory = wined3d_context_vk_allocate_vram_chunk_memory(context_vk, memory_type, size); + return NULL; + } + + if (!(block = wined3d_allocator_allocate(allocator, &context_vk->c, memory_type, size))) + { + *vk_memory = VK_NULL_HANDLE; + return NULL; + } + + *vk_memory = wined3d_allocator_chunk_vk(block->chunk)->vk_memory; + + return block; +} + +static bool wined3d_context_vk_create_slab_bo(struct wined3d_context_vk *context_vk, + VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags memory_type, struct wined3d_bo_vk *bo) +{ + const struct wined3d_adapter_vk *adapter_vk = wined3d_adapter_vk(context_vk->c.device->adapter); + const VkPhysicalDeviceLimits *limits = &adapter_vk->device_limits; + struct wined3d_bo_slab_vk_key key; + struct wined3d_bo_slab_vk *slab; + struct wine_rb_entry *entry; + size_t object_size, idx; + size_t alignment; + + if (size > WINED3D_ALLOCATOR_MIN_BLOCK_SIZE / 2) + return false; + + alignment = WINED3D_SLAB_BO_MIN_OBJECT_ALIGN; + if ((usage & (VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT)) + && limits->minTexelBufferOffsetAlignment > alignment) + alignment = limits->minTexelBufferOffsetAlignment; + if ((usage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) && limits->minUniformBufferOffsetAlignment) + alignment = limits->minUniformBufferOffsetAlignment; + if ((usage & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) && limits->minStorageBufferOffsetAlignment) + alignment = limits->minStorageBufferOffsetAlignment; + + object_size = (size + (alignment - 1)) & ~(alignment - 1); + if (object_size < WINED3D_ALLOCATOR_MIN_BLOCK_SIZE / 32) + object_size = WINED3D_ALLOCATOR_MIN_BLOCK_SIZE / 32; + key.memory_type = memory_type; + key.usage = usage; + key.size = 32 * object_size; + + if ((entry = wine_rb_get(&context_vk->bo_slab_available, &key))) + { + slab = WINE_RB_ENTRY_VALUE(entry, struct wined3d_bo_slab_vk, entry); + TRACE("Using existing bo slab %p.\n", slab); + } + else + { + if (!(slab = heap_alloc_zero(sizeof(*slab)))) + { + ERR("Failed to allocate bo slab.\n"); + return false; + } + + slab->requested_memory_type = memory_type; + if (!wined3d_context_vk_create_bo(context_vk, key.size, usage, memory_type, &slab->bo)) + { + ERR("Failed to create slab bo.\n"); + heap_free(slab); + return false; + } + slab->map = ~0u; + + if (wine_rb_put(&context_vk->bo_slab_available, &key, &slab->entry) < 0) + { + ERR("Failed to add slab to available tree.\n"); + wined3d_context_vk_destroy_bo(context_vk, &slab->bo); + heap_free(slab); + return false; + } + + TRACE("Created new bo slab %p.\n", slab); + } + + idx = wined3d_bit_scan(&slab->map); + if (!slab->map) + { + if (slab->next) + { + wine_rb_replace(&context_vk->bo_slab_available, &slab->entry, &slab->next->entry); + slab->next = NULL; + } + else + { + wine_rb_remove(&context_vk->bo_slab_available, &slab->entry); + } + } + + *bo = slab->bo; + bo->memory = NULL; + bo->slab = slab; + bo->buffer_offset = idx * object_size; + bo->memory_offset = slab->bo.memory_offset + bo->buffer_offset; + bo->size = size; + list_init(&bo->users); + bo->command_buffer_id = 0; + + TRACE("Using buffer 0x%s, memory 0x%s, offset 0x%s for bo %p.\n", + wine_dbgstr_longlong(bo->vk_buffer), wine_dbgstr_longlong(bo->vk_memory), + wine_dbgstr_longlong(bo->buffer_offset), bo); + + return true; +} + +BOOL wined3d_context_vk_create_bo(struct wined3d_context_vk *context_vk, VkDeviceSize size, + VkBufferUsageFlags usage, VkMemoryPropertyFlags memory_type, struct wined3d_bo_vk *bo) +{ + struct wined3d_device_vk *device_vk = wined3d_device_vk(context_vk->c.device); + const struct wined3d_vk_info *vk_info = context_vk->vk_info; + VkMemoryRequirements memory_requirements; + struct wined3d_adapter_vk *adapter_vk; + VkBufferCreateInfo create_info; + unsigned int memory_type_idx; + VkResult vr; + + if (wined3d_context_vk_create_slab_bo(context_vk, size, usage, memory_type, bo)) + return TRUE; + + adapter_vk = wined3d_adapter_vk(device_vk->d.adapter); + + create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + create_info.pNext = NULL; + create_info.flags = 0; + create_info.size = size; + create_info.usage = usage; + create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + create_info.queueFamilyIndexCount = 0; + create_info.pQueueFamilyIndices = NULL; + + if ((vr = VK_CALL(vkCreateBuffer(device_vk->vk_device, &create_info, NULL, &bo->vk_buffer))) < 0) + { + ERR("Failed to create Vulkan buffer, vr %s.\n", wined3d_debug_vkresult(vr)); + return FALSE; + } + + VK_CALL(vkGetBufferMemoryRequirements(device_vk->vk_device, bo->vk_buffer, &memory_requirements)); + + memory_type_idx = wined3d_adapter_vk_get_memory_type_index(adapter_vk, + memory_requirements.memoryTypeBits, memory_type); + if (memory_type_idx == ~0u) + { + ERR("Failed to find suitable memory type.\n"); + VK_CALL(vkDestroyBuffer(device_vk->vk_device, bo->vk_buffer, NULL)); + return FALSE; + } + bo->memory = wined3d_context_vk_allocate_memory(context_vk, + memory_type_idx, memory_requirements.size, &bo->vk_memory); + if (!bo->vk_memory) + { + ERR("Failed to allocate buffer memory.\n"); + VK_CALL(vkDestroyBuffer(device_vk->vk_device, bo->vk_buffer, NULL)); + return FALSE; + } + bo->memory_offset = bo->memory ? bo->memory->offset : 0; + + if ((vr = VK_CALL(vkBindBufferMemory(device_vk->vk_device, bo->vk_buffer, + bo->vk_memory, bo->memory_offset))) < 0) + { + ERR("Failed to bind buffer memory, vr %s.\n", wined3d_debug_vkresult(vr)); + if (bo->memory) + wined3d_allocator_block_free(bo->memory); + else + VK_CALL(vkFreeMemory(device_vk->vk_device, bo->vk_memory, NULL)); + VK_CALL(vkDestroyBuffer(device_vk->vk_device, bo->vk_buffer, NULL)); + return FALSE; + } + + bo->map_ptr = NULL; + bo->buffer_offset = 0; + bo->size = size; + bo->usage = usage; + bo->memory_type = adapter_vk->memory_properties.memoryTypes[memory_type_idx].propertyFlags; + list_init(&bo->users); + bo->command_buffer_id = 0; + bo->slab = NULL; + + TRACE("Created buffer 0x%s, memory 0x%s for bo %p.\n", + wine_dbgstr_longlong(bo->vk_buffer), wine_dbgstr_longlong(bo->vk_memory), bo); + + return TRUE; +} + +static struct wined3d_retired_object_vk *wined3d_context_vk_get_retired_object_vk(struct wined3d_context_vk *context_vk) +{ + struct wined3d_retired_objects_vk *retired = &context_vk->retired; + struct wined3d_retired_object_vk *o; + + if (retired->free) + { + o = retired->free; + retired->free = o->u.next; + return o; + } + + if (!wined3d_array_reserve((void **)&retired->objects, &retired->size, + retired->count + 1, sizeof(*retired->objects))) + return NULL; + + return &retired->objects[retired->count++]; +} + +void wined3d_context_vk_destroy_framebuffer(struct wined3d_context_vk *context_vk, + VkFramebuffer vk_framebuffer, uint64_t command_buffer_id) +{ + struct wined3d_device_vk *device_vk = wined3d_device_vk(context_vk->c.device); + const struct wined3d_vk_info *vk_info = context_vk->vk_info; + struct wined3d_retired_object_vk *o; + + if (context_vk->completed_command_buffer_id > command_buffer_id) + { + VK_CALL(vkDestroyFramebuffer(device_vk->vk_device, vk_framebuffer, NULL)); + TRACE("Destroyed framebuffer 0x%s.\n", wine_dbgstr_longlong(vk_framebuffer)); + return; + } + + if (!(o = wined3d_context_vk_get_retired_object_vk(context_vk))) + { + ERR("Leaking framebuffer 0x%s.\n", wine_dbgstr_longlong(vk_framebuffer)); + return; + } + + o->type = WINED3D_RETIRED_FRAMEBUFFER_VK; + o->u.vk_framebuffer = vk_framebuffer; + o->command_buffer_id = command_buffer_id; +} + +static void wined3d_context_vk_destroy_descriptor_pool(struct wined3d_context_vk *context_vk, + VkDescriptorPool vk_descriptor_pool, uint64_t command_buffer_id) +{ + struct wined3d_device_vk *device_vk = wined3d_device_vk(context_vk->c.device); + const struct wined3d_vk_info *vk_info = context_vk->vk_info; + struct wined3d_retired_object_vk *o; + + if (context_vk->completed_command_buffer_id > command_buffer_id) + { + VK_CALL(vkDestroyDescriptorPool(device_vk->vk_device, vk_descriptor_pool, NULL)); + TRACE("Destroyed descriptor pool 0x%s.\n", wine_dbgstr_longlong(vk_descriptor_pool)); + return; + } + + if (!(o = wined3d_context_vk_get_retired_object_vk(context_vk))) + { + ERR("Leaking descriptor pool 0x%s.\n", wine_dbgstr_longlong(vk_descriptor_pool)); + return; + } + + o->type = WINED3D_RETIRED_DESCRIPTOR_POOL_VK; + o->u.vk_descriptor_pool = vk_descriptor_pool; + o->command_buffer_id = command_buffer_id; +} + +void wined3d_context_vk_destroy_memory(struct wined3d_context_vk *context_vk, + VkDeviceMemory vk_memory, uint64_t command_buffer_id) +{ + struct wined3d_device_vk *device_vk = wined3d_device_vk(context_vk->c.device); + const struct wined3d_vk_info *vk_info = context_vk->vk_info; + struct wined3d_retired_object_vk *o; + + if (context_vk->completed_command_buffer_id > command_buffer_id) + { + VK_CALL(vkFreeMemory(device_vk->vk_device, vk_memory, NULL)); + TRACE("Freed memory 0x%s.\n", wine_dbgstr_longlong(vk_memory)); + return; + } + + if (!(o = wined3d_context_vk_get_retired_object_vk(context_vk))) + { + ERR("Leaking memory 0x%s.\n", wine_dbgstr_longlong(vk_memory)); + return; + } + + o->type = WINED3D_RETIRED_MEMORY_VK; + o->u.vk_memory = vk_memory; + o->command_buffer_id = command_buffer_id; +} + +void wined3d_context_vk_destroy_allocator_block(struct wined3d_context_vk *context_vk, + struct wined3d_allocator_block *block, uint64_t command_buffer_id) +{ + struct wined3d_retired_object_vk *o; + + if (context_vk->completed_command_buffer_id > command_buffer_id) + { + wined3d_allocator_block_free(block); + TRACE("Freed block %p.\n", block); + return; + } + + if (!(o = wined3d_context_vk_get_retired_object_vk(context_vk))) + { + ERR("Leaking block %p.\n", block); + return; + } + + o->type = WINED3D_RETIRED_ALLOCATOR_BLOCK_VK; + o->u.block = block; + o->command_buffer_id = command_buffer_id; +} + +static void wined3d_bo_slab_vk_free_slice(struct wined3d_bo_slab_vk *slab, + SIZE_T idx, struct wined3d_context_vk *context_vk) +{ + struct wined3d_bo_slab_vk_key key; + struct wine_rb_entry *entry; + + TRACE("slab %p, idx %lu, context_vk %p.\n", slab, idx, context_vk); + + if (!slab->map) + { + key.memory_type = slab->requested_memory_type; + key.usage = slab->bo.usage; + key.size = slab->bo.size; + + if ((entry = wine_rb_get(&context_vk->bo_slab_available, &key))) + { + slab->next = WINE_RB_ENTRY_VALUE(entry, struct wined3d_bo_slab_vk, entry); + wine_rb_replace(&context_vk->bo_slab_available, entry, &slab->entry); + } + else if (wine_rb_put(&context_vk->bo_slab_available, &key, &slab->entry) < 0) + { + ERR("Unable to return slab %p (map 0x%08x) to available tree.\n", slab, slab->map); + } + } + slab->map |= 1u << idx; +} + +static void wined3d_context_vk_destroy_bo_slab_slice(struct wined3d_context_vk *context_vk, + struct wined3d_bo_slab_vk *slab, SIZE_T idx, uint64_t command_buffer_id) +{ + struct wined3d_retired_object_vk *o; + + if (context_vk->completed_command_buffer_id > command_buffer_id) + { + wined3d_bo_slab_vk_free_slice(slab, idx, context_vk); + return; + } + + if (!(o = wined3d_context_vk_get_retired_object_vk(context_vk))) + { + ERR("Leaking slab %p, slice %#lx.\n", slab, idx); + return; + } + + o->type = WINED3D_RETIRED_BO_SLAB_SLICE_VK; + o->u.slice.slab = slab; + o->u.slice.idx = idx; + o->command_buffer_id = command_buffer_id; +} + +static void wined3d_context_vk_destroy_buffer(struct wined3d_context_vk *context_vk, + VkBuffer vk_buffer, uint64_t command_buffer_id) +{ + struct wined3d_device_vk *device_vk = wined3d_device_vk(context_vk->c.device); + const struct wined3d_vk_info *vk_info = context_vk->vk_info; + struct wined3d_retired_object_vk *o; + + if (context_vk->completed_command_buffer_id > command_buffer_id) + { + VK_CALL(vkDestroyBuffer(device_vk->vk_device, vk_buffer, NULL)); + TRACE("Destroyed buffer 0x%s.\n", wine_dbgstr_longlong(vk_buffer)); + return; + } + + if (!(o = wined3d_context_vk_get_retired_object_vk(context_vk))) + { + ERR("Leaking buffer 0x%s.\n", wine_dbgstr_longlong(vk_buffer)); + return; + } + + o->type = WINED3D_RETIRED_BUFFER_VK; + o->u.vk_buffer = vk_buffer; + o->command_buffer_id = command_buffer_id; +} + +void wined3d_context_vk_destroy_image(struct wined3d_context_vk *context_vk, + VkImage vk_image, uint64_t command_buffer_id) +{ + struct wined3d_device_vk *device_vk = wined3d_device_vk(context_vk->c.device); + const struct wined3d_vk_info *vk_info = context_vk->vk_info; + struct wined3d_retired_object_vk *o; + + if (context_vk->completed_command_buffer_id > command_buffer_id) + { + VK_CALL(vkDestroyImage(device_vk->vk_device, vk_image, NULL)); + TRACE("Destroyed image 0x%s.\n", wine_dbgstr_longlong(vk_image)); + return; + } + + if (!(o = wined3d_context_vk_get_retired_object_vk(context_vk))) + { + ERR("Leaking image 0x%s.\n", wine_dbgstr_longlong(vk_image)); + return; + } + + o->type = WINED3D_RETIRED_IMAGE_VK; + o->u.vk_image = vk_image; + o->command_buffer_id = command_buffer_id; +} + +void wined3d_context_vk_destroy_buffer_view(struct wined3d_context_vk *context_vk, + VkBufferView vk_view, uint64_t command_buffer_id) +{ + struct wined3d_device_vk *device_vk = wined3d_device_vk(context_vk->c.device); + const struct wined3d_vk_info *vk_info = context_vk->vk_info; + struct wined3d_retired_object_vk *o; + + if (context_vk->completed_command_buffer_id > command_buffer_id) + { + VK_CALL(vkDestroyBufferView(device_vk->vk_device, vk_view, NULL)); + TRACE("Destroyed buffer view 0x%s.\n", wine_dbgstr_longlong(vk_view)); + return; + } + + if (!(o = wined3d_context_vk_get_retired_object_vk(context_vk))) + { + ERR("Leaking buffer view 0x%s.\n", wine_dbgstr_longlong(vk_view)); + return; + } + + o->type = WINED3D_RETIRED_BUFFER_VIEW_VK; + o->u.vk_buffer_view = vk_view; + o->command_buffer_id = command_buffer_id; +} + +void wined3d_context_vk_destroy_image_view(struct wined3d_context_vk *context_vk, + VkImageView vk_view, uint64_t command_buffer_id) +{ + struct wined3d_device_vk *device_vk = wined3d_device_vk(context_vk->c.device); + const struct wined3d_vk_info *vk_info = context_vk->vk_info; + struct wined3d_retired_object_vk *o; + + if (context_vk->completed_command_buffer_id > command_buffer_id) + { + VK_CALL(vkDestroyImageView(device_vk->vk_device, vk_view, NULL)); + TRACE("Destroyed image view 0x%s.\n", wine_dbgstr_longlong(vk_view)); + return; + } + + if (!(o = wined3d_context_vk_get_retired_object_vk(context_vk))) + { + ERR("Leaking image view 0x%s.\n", wine_dbgstr_longlong(vk_view)); + return; + } + + o->type = WINED3D_RETIRED_IMAGE_VIEW_VK; + o->u.vk_image_view = vk_view; + o->command_buffer_id = command_buffer_id; +} + +void wined3d_context_vk_destroy_sampler(struct wined3d_context_vk *context_vk, + VkSampler vk_sampler, uint64_t command_buffer_id) +{ + struct wined3d_device_vk *device_vk = wined3d_device_vk(context_vk->c.device); + const struct wined3d_vk_info *vk_info = context_vk->vk_info; + struct wined3d_retired_object_vk *o; + + if (context_vk->completed_command_buffer_id > command_buffer_id) + { + VK_CALL(vkDestroySampler(device_vk->vk_device, vk_sampler, NULL)); + TRACE("Destroyed sampler 0x%s.\n", wine_dbgstr_longlong(vk_sampler)); + return; + } + + if (!(o = wined3d_context_vk_get_retired_object_vk(context_vk))) + { + ERR("Leaking sampler 0x%s.\n", wine_dbgstr_longlong(vk_sampler)); + return; + } + + o->type = WINED3D_RETIRED_SAMPLER_VK; + o->u.vk_sampler = vk_sampler; + o->command_buffer_id = command_buffer_id; +} + +void wined3d_context_vk_destroy_bo(struct wined3d_context_vk *context_vk, const struct wined3d_bo_vk *bo) +{ + struct wined3d_device_vk *device_vk = wined3d_device_vk(context_vk->c.device); + const struct wined3d_vk_info *vk_info = context_vk->vk_info; + struct wined3d_bo_slab_vk *slab_vk; + size_t object_size, idx; + + TRACE("context_vk %p, bo %p.\n", context_vk, bo); + + if ((slab_vk = bo->slab)) + { + if (bo->map_ptr) + wined3d_bo_slab_vk_unmap(slab_vk, context_vk); + object_size = slab_vk->bo.size / 32; + idx = bo->buffer_offset / object_size; + wined3d_context_vk_destroy_bo_slab_slice(context_vk, slab_vk, idx, bo->command_buffer_id); + return; + } + + wined3d_context_vk_destroy_buffer(context_vk, bo->vk_buffer, bo->command_buffer_id); + if (bo->memory) + { + if (bo->map_ptr) + wined3d_allocator_chunk_vk_unmap(wined3d_allocator_chunk_vk(bo->memory->chunk), context_vk); + wined3d_context_vk_destroy_allocator_block(context_vk, bo->memory, bo->command_buffer_id); + return; + } + + if (bo->map_ptr) + VK_CALL(vkUnmapMemory(device_vk->vk_device, bo->vk_memory)); + wined3d_context_vk_destroy_memory(context_vk, bo->vk_memory, bo->command_buffer_id); +} + +void wined3d_context_vk_poll_command_buffers(struct wined3d_context_vk *context_vk) +{ + struct wined3d_device_vk *device_vk = wined3d_device_vk(context_vk->c.device); + const struct wined3d_vk_info *vk_info = context_vk->vk_info; + struct wined3d_command_buffer_vk *buffer; + SIZE_T i = 0; + + while (i < context_vk->submitted.buffer_count) + { + buffer = &context_vk->submitted.buffers[i]; + if (VK_CALL(vkGetFenceStatus(device_vk->vk_device, buffer->vk_fence)) == VK_NOT_READY) + { + ++i; + continue; + } + + TRACE("Command buffer %p with id 0x%s has finished.\n", + buffer->vk_command_buffer, wine_dbgstr_longlong(buffer->id)); + VK_CALL(vkDestroyFence(device_vk->vk_device, buffer->vk_fence, NULL)); + VK_CALL(vkFreeCommandBuffers(device_vk->vk_device, + context_vk->vk_command_pool, 1, &buffer->vk_command_buffer)); + + if (buffer->id > context_vk->completed_command_buffer_id) + context_vk->completed_command_buffer_id = buffer->id; + *buffer = context_vk->submitted.buffers[--context_vk->submitted.buffer_count]; + } +} + +static void wined3d_context_vk_cleanup_resources(struct wined3d_context_vk *context_vk) +{ + struct wined3d_device_vk *device_vk = wined3d_device_vk(context_vk->c.device); + struct wined3d_retired_objects_vk *retired = &context_vk->retired; + const struct wined3d_vk_info *vk_info = context_vk->vk_info; + struct wined3d_retired_object_vk *o; + uint64_t command_buffer_id; + SIZE_T i = 0; + + wined3d_context_vk_poll_command_buffers(context_vk); + command_buffer_id = context_vk->completed_command_buffer_id; + + retired->free = NULL; + for (i = retired->count; i; --i) + { + o = &retired->objects[i - 1]; + + if (o->type != WINED3D_RETIRED_FREE_VK && o->command_buffer_id > command_buffer_id) + continue; + + switch (o->type) + { + case WINED3D_RETIRED_FREE_VK: + /* Nothing to do. */ + break; + + case WINED3D_RETIRED_FRAMEBUFFER_VK: + VK_CALL(vkDestroyFramebuffer(device_vk->vk_device, o->u.vk_framebuffer, NULL)); + TRACE("Destroyed framebuffer 0x%s.\n", wine_dbgstr_longlong(o->u.vk_framebuffer)); + break; + + case WINED3D_RETIRED_DESCRIPTOR_POOL_VK: + VK_CALL(vkDestroyDescriptorPool(device_vk->vk_device, o->u.vk_descriptor_pool, NULL)); + TRACE("Destroyed descriptor pool 0x%s.\n", wine_dbgstr_longlong(o->u.vk_descriptor_pool)); + break; + + case WINED3D_RETIRED_MEMORY_VK: + VK_CALL(vkFreeMemory(device_vk->vk_device, o->u.vk_memory, NULL)); + TRACE("Freed memory 0x%s.\n", wine_dbgstr_longlong(o->u.vk_memory)); + break; + + case WINED3D_RETIRED_ALLOCATOR_BLOCK_VK: + TRACE("Destroying block %p.\n", o->u.block); + wined3d_allocator_block_free(o->u.block); + break; + + case WINED3D_RETIRED_BO_SLAB_SLICE_VK: + wined3d_bo_slab_vk_free_slice(o->u.slice.slab, o->u.slice.idx, context_vk); + break; + + case WINED3D_RETIRED_BUFFER_VK: + VK_CALL(vkDestroyBuffer(device_vk->vk_device, o->u.vk_buffer, NULL)); + TRACE("Destroyed buffer 0x%s.\n", wine_dbgstr_longlong(o->u.vk_buffer)); + break; + + case WINED3D_RETIRED_IMAGE_VK: + VK_CALL(vkDestroyImage(device_vk->vk_device, o->u.vk_image, NULL)); + TRACE("Destroyed image 0x%s.\n", wine_dbgstr_longlong(o->u.vk_image)); + break; + + case WINED3D_RETIRED_BUFFER_VIEW_VK: + VK_CALL(vkDestroyBufferView(device_vk->vk_device, o->u.vk_buffer_view, NULL)); + TRACE("Destroyed buffer view 0x%s.\n", wine_dbgstr_longlong(o->u.vk_buffer_view)); + break; + + case WINED3D_RETIRED_IMAGE_VIEW_VK: + VK_CALL(vkDestroyImageView(device_vk->vk_device, o->u.vk_image_view, NULL)); + TRACE("Destroyed image view 0x%s.\n", wine_dbgstr_longlong(o->u.vk_image_view)); + break; + + case WINED3D_RETIRED_SAMPLER_VK: + VK_CALL(vkDestroySampler(device_vk->vk_device, o->u.vk_sampler, NULL)); + TRACE("Destroyed sampler 0x%s.\n", wine_dbgstr_longlong(o->u.vk_sampler)); + break; + + default: + ERR("Unhandled object type %#x.\n", o->type); + break; + } + + if (i == retired->count) + { + --retired->count; + continue; + } + + o->type = WINED3D_RETIRED_FREE_VK; + o->u.next = retired->free; + retired->free = o; + } +} + +static void wined3d_context_vk_destroy_bo_slab(struct wine_rb_entry *entry, void *ctx) +{ + struct wined3d_context_vk *context_vk = ctx; + struct wined3d_bo_slab_vk *slab, *next; + + slab = WINE_RB_ENTRY_VALUE(entry, struct wined3d_bo_slab_vk, entry); + while (slab) + { + next = slab->next; + wined3d_context_vk_destroy_bo(context_vk, &slab->bo); + heap_free(slab); + slab = next; + } +} + +static void wined3d_context_vk_destroy_graphics_pipeline(struct wine_rb_entry *entry, void *ctx) +{ + struct wined3d_graphics_pipeline_vk *pipeline_vk = WINE_RB_ENTRY_VALUE(entry, + struct wined3d_graphics_pipeline_vk, entry); + struct wined3d_context_vk *context_vk = ctx; + const struct wined3d_vk_info *vk_info; + struct wined3d_device_vk *device_vk; + + vk_info = context_vk->vk_info; + device_vk = wined3d_device_vk(context_vk->c.device); + + VK_CALL(vkDestroyPipeline(device_vk->vk_device, pipeline_vk->vk_pipeline, NULL)); + heap_free(pipeline_vk); +} + +static void wined3d_context_vk_destroy_pipeline_layout(struct wine_rb_entry *entry, void *ctx) +{ + struct wined3d_pipeline_layout_vk *layout = WINE_RB_ENTRY_VALUE(entry, + struct wined3d_pipeline_layout_vk, entry); + struct wined3d_context_vk *context_vk = ctx; + const struct wined3d_vk_info *vk_info; + struct wined3d_device_vk *device_vk; + + vk_info = context_vk->vk_info; + device_vk = wined3d_device_vk(context_vk->c.device); + + VK_CALL(vkDestroyPipelineLayout(device_vk->vk_device, layout->vk_pipeline_layout, NULL)); + VK_CALL(vkDestroyDescriptorSetLayout(device_vk->vk_device, layout->vk_set_layout, NULL)); + heap_free(layout->key.bindings); + heap_free(layout); +} + +static void wined3d_render_pass_key_vk_init(struct wined3d_render_pass_key_vk *key, + const struct wined3d_fb_state *fb, unsigned int rt_count, bool depth_stencil, uint32_t clear_flags) +{ + struct wined3d_render_pass_attachment_vk *a; + struct wined3d_rendertarget_view *view; + unsigned int i; + + memset(key, 0, sizeof(*key)); + + for (i = 0; i < rt_count; ++i) + { + if (!(view = fb->render_targets[i]) || view->format->id == WINED3DFMT_NULL) + continue; + + a = &key->rt[i]; + a->vk_format = wined3d_format_vk(view->format)->vk_format; + a->vk_samples = max(1, wined3d_resource_get_sample_count(view->resource)); + a->vk_layout = wined3d_texture_vk(wined3d_texture_from_resource(view->resource))->layout; + key->rt_mask |= 1u << i; + } + + if (depth_stencil && (view = fb->depth_stencil)) + { + a = &key->ds; + a->vk_format = wined3d_format_vk(view->format)->vk_format; + a->vk_samples = max(1, wined3d_resource_get_sample_count(view->resource)); + a->vk_layout = wined3d_texture_vk(wined3d_texture_from_resource(view->resource))->layout; + key->rt_mask |= 1u << WINED3D_MAX_RENDER_TARGETS; + } + + key->clear_flags = clear_flags; +} + +static void wined3d_render_pass_vk_cleanup(struct wined3d_render_pass_vk *pass, + struct wined3d_context_vk *context_vk) +{ + struct wined3d_device_vk *device_vk = wined3d_device_vk(context_vk->c.device); + const struct wined3d_vk_info *vk_info = context_vk->vk_info; + + VK_CALL(vkDestroyRenderPass(device_vk->vk_device, pass->vk_render_pass, NULL)); +} + +static bool wined3d_render_pass_vk_init(struct wined3d_render_pass_vk *pass, + struct wined3d_context_vk *context_vk, const struct wined3d_render_pass_key_vk *key) +{ + struct wined3d_device_vk *device_vk = wined3d_device_vk(context_vk->c.device); + VkAttachmentReference attachment_references[WINED3D_MAX_RENDER_TARGETS]; + VkAttachmentDescription attachments[WINED3D_MAX_RENDER_TARGETS + 1]; + const struct wined3d_vk_info *vk_info = context_vk->vk_info; + const struct wined3d_render_pass_attachment_vk *a; + VkAttachmentReference ds_attachment_reference; + VkAttachmentReference *ds_reference = NULL; + unsigned int attachment_count, rt_count, i; + VkAttachmentDescription *attachment; + VkSubpassDescription sub_pass_desc; + VkRenderPassCreateInfo pass_desc; + uint32_t mask; + VkResult vr; + + rt_count = 0; + attachment_count = 0; + mask = key->rt_mask & ((1u << WINED3D_MAX_RENDER_TARGETS) - 1); + while (mask) + { + i = wined3d_bit_scan(&mask); + a = &key->rt[i]; + + attachment = &attachments[attachment_count]; + attachment->flags = 0; + attachment->format = a->vk_format; + attachment->samples = a->vk_samples; + if (key->clear_flags & WINED3DCLEAR_TARGET) + attachment->loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + else + attachment->loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + attachment->storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachment->stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachment->stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachment->initialLayout = a->vk_layout; + attachment->finalLayout = a->vk_layout; + + attachment_references[i].attachment = attachment_count; + attachment_references[i].layout = a->vk_layout; + + ++attachment_count; + rt_count = i + 1; + } + + mask = ~key->rt_mask & ((1u << rt_count) - 1); + while (mask) + { + i = wined3d_bit_scan(&mask); + attachment_references[i].attachment = VK_ATTACHMENT_UNUSED; + attachment_references[i].layout = VK_IMAGE_LAYOUT_UNDEFINED; + } + + if (key->rt_mask & (1u << WINED3D_MAX_RENDER_TARGETS)) + { + a = &key->ds; + + attachment = &attachments[attachment_count]; + attachment->flags = 0; + attachment->format = a->vk_format; + attachment->samples = a->vk_samples; + if (key->clear_flags & WINED3DCLEAR_ZBUFFER) + attachment->loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + else + attachment->loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + attachment->storeOp = VK_ATTACHMENT_STORE_OP_STORE; + if (key->clear_flags & WINED3DCLEAR_STENCIL) + attachment->stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + else + attachment->stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + attachment->stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE; + attachment->initialLayout = a->vk_layout; + attachment->finalLayout = a->vk_layout; + + ds_reference = &ds_attachment_reference; + ds_reference->attachment = attachment_count; + ds_reference->layout = a->vk_layout; + + ++attachment_count; + } + + sub_pass_desc.flags = 0; + sub_pass_desc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + sub_pass_desc.inputAttachmentCount = 0; + sub_pass_desc.pInputAttachments = NULL; + sub_pass_desc.colorAttachmentCount = rt_count; + sub_pass_desc.pColorAttachments = attachment_references; + sub_pass_desc.pResolveAttachments = NULL; + sub_pass_desc.pDepthStencilAttachment = ds_reference; + sub_pass_desc.preserveAttachmentCount = 0; + sub_pass_desc.pPreserveAttachments = NULL; + + pass_desc.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + pass_desc.pNext = NULL; + pass_desc.flags = 0; + pass_desc.attachmentCount = attachment_count; + pass_desc.pAttachments = attachments; + pass_desc.subpassCount = 1; + pass_desc.pSubpasses = &sub_pass_desc; + pass_desc.dependencyCount = 0; + pass_desc.pDependencies = NULL; + + pass->key = *key; + if ((vr = VK_CALL(vkCreateRenderPass(device_vk->vk_device, + &pass_desc, NULL, &pass->vk_render_pass))) < 0) + { + WARN("Failed to create Vulkan render pass, vr %d.\n", vr); + return false; + } + + return true; +} + +VkRenderPass wined3d_context_vk_get_render_pass(struct wined3d_context_vk *context_vk, + const struct wined3d_fb_state *fb, unsigned int rt_count, bool depth_stencil, uint32_t clear_flags) +{ + struct wined3d_render_pass_key_vk key; + struct wined3d_render_pass_vk *pass; + struct wine_rb_entry *entry; + + wined3d_render_pass_key_vk_init(&key, fb, rt_count, depth_stencil, clear_flags); + if ((entry = wine_rb_get(&context_vk->render_passes, &key))) + return WINE_RB_ENTRY_VALUE(entry, struct wined3d_render_pass_vk, entry)->vk_render_pass; + + if (!(pass = heap_alloc(sizeof(*pass)))) + return VK_NULL_HANDLE; + + if (!wined3d_render_pass_vk_init(pass, context_vk, &key)) + { + heap_free(pass); + return VK_NULL_HANDLE; + } + + if (wine_rb_put(&context_vk->render_passes, &pass->key, &pass->entry) == -1) + { + ERR("Failed to insert render pass.\n"); + wined3d_render_pass_vk_cleanup(pass, context_vk); + heap_free(pass); + return VK_NULL_HANDLE; + } + + return pass->vk_render_pass; +} + +void wined3d_context_vk_end_current_render_pass(struct wined3d_context_vk *context_vk) +{ + const struct wined3d_vk_info *vk_info = context_vk->vk_info; + VkCommandBuffer vk_command_buffer; + + if (context_vk->vk_render_pass) + { + vk_command_buffer = context_vk->current_command_buffer.vk_command_buffer; + VK_CALL(vkCmdEndRenderPass(vk_command_buffer)); + context_vk->vk_render_pass = VK_NULL_HANDLE; + VK_CALL(vkCmdPipelineBarrier(vk_command_buffer, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, 0, NULL)); + } + + if (context_vk->vk_framebuffer) + { + wined3d_context_vk_destroy_framebuffer(context_vk, + context_vk->vk_framebuffer, context_vk->current_command_buffer.id); + context_vk->vk_framebuffer = VK_NULL_HANDLE; + } +} + +static void wined3d_context_vk_destroy_render_pass(struct wine_rb_entry *entry, void *ctx) +{ + struct wined3d_render_pass_vk *pass = WINE_RB_ENTRY_VALUE(entry, + struct wined3d_render_pass_vk, entry); + + wined3d_render_pass_vk_cleanup(pass, ctx); + heap_free(pass); +} + +static void wined3d_shader_descriptor_writes_vk_cleanup(struct wined3d_shader_descriptor_writes_vk *writes) +{ + heap_free(writes->writes); +} + +static void wined3d_context_vk_destroy_query_pools(struct wined3d_context_vk *context_vk, struct list *free_pools) +{ + struct wined3d_query_pool_vk *pool_vk, *entry; + + LIST_FOR_EACH_ENTRY_SAFE(pool_vk, entry, free_pools, struct wined3d_query_pool_vk, entry) + { + wined3d_query_pool_vk_cleanup(pool_vk, context_vk); + heap_free(pool_vk); + } +} + +bool wined3d_context_vk_allocate_query(struct wined3d_context_vk *context_vk, + enum wined3d_query_type type, struct wined3d_query_pool_idx_vk *pool_idx) +{ + struct wined3d_query_pool_vk *pool_vk, *entry; + struct list *free_pools; + size_t idx; + + switch (type) + { + case WINED3D_QUERY_TYPE_OCCLUSION: + free_pools = &context_vk->free_occlusion_query_pools; + break; + + case WINED3D_QUERY_TYPE_TIMESTAMP: + free_pools = &context_vk->free_timestamp_query_pools; + break; + + case WINED3D_QUERY_TYPE_PIPELINE_STATISTICS: + free_pools = &context_vk->free_pipeline_statistics_query_pools; + break; + + case WINED3D_QUERY_TYPE_SO_STATISTICS: + case WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM0: + case WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM1: + case WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM2: + case WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM3: + free_pools = &context_vk->free_stream_output_statistics_query_pools; + break; + + default: + FIXME("Unhandled query type %#x.\n", type); + return false; + } + + LIST_FOR_EACH_ENTRY_SAFE(pool_vk, entry, free_pools, struct wined3d_query_pool_vk, entry) + { + if (wined3d_query_pool_vk_allocate_query(pool_vk, &idx)) + goto done; + list_remove(&pool_vk->entry); + } + + if (!(pool_vk = heap_alloc_zero(sizeof(*pool_vk)))) + return false; + if (!wined3d_query_pool_vk_init(pool_vk, context_vk, type, free_pools)) + { + heap_free(pool_vk); + return false; + } + + if (!wined3d_query_pool_vk_allocate_query(pool_vk, &idx)) + { + wined3d_query_pool_vk_cleanup(pool_vk, context_vk); + heap_free(pool_vk); + return false; + } + +done: + pool_idx->pool_vk = pool_vk; + pool_idx->idx = idx; + + return true; +} + +void wined3d_context_vk_cleanup(struct wined3d_context_vk *context_vk) +{ + struct wined3d_command_buffer_vk *buffer = &context_vk->current_command_buffer; + struct wined3d_device_vk *device_vk = wined3d_device_vk(context_vk->c.device); + const struct wined3d_vk_info *vk_info = context_vk->vk_info; + + if (buffer->vk_command_buffer) + { + VK_CALL(vkFreeCommandBuffers(device_vk->vk_device, + context_vk->vk_command_pool, 1, &buffer->vk_command_buffer)); + buffer->vk_command_buffer = VK_NULL_HANDLE; + } + + wined3d_context_vk_wait_command_buffer(context_vk, buffer->id - 1); + context_vk->completed_command_buffer_id = buffer->id; + + heap_free(context_vk->compute.bindings.bindings); + heap_free(context_vk->graphics.bindings.bindings); + if (context_vk->vk_descriptor_pool) + VK_CALL(vkDestroyDescriptorPool(device_vk->vk_device, context_vk->vk_descriptor_pool, NULL)); + if (context_vk->vk_framebuffer) + VK_CALL(vkDestroyFramebuffer(device_vk->vk_device, context_vk->vk_framebuffer, NULL)); + VK_CALL(vkDestroyCommandPool(device_vk->vk_device, context_vk->vk_command_pool, NULL)); + if (context_vk->vk_so_counter_bo.vk_buffer) + wined3d_context_vk_destroy_bo(context_vk, &context_vk->vk_so_counter_bo); + wined3d_context_vk_cleanup_resources(context_vk); + wined3d_context_vk_destroy_query_pools(context_vk, &context_vk->free_occlusion_query_pools); + wined3d_context_vk_destroy_query_pools(context_vk, &context_vk->free_timestamp_query_pools); + wined3d_context_vk_destroy_query_pools(context_vk, &context_vk->free_pipeline_statistics_query_pools); + wined3d_context_vk_destroy_query_pools(context_vk, &context_vk->free_stream_output_statistics_query_pools); + wine_rb_destroy(&context_vk->bo_slab_available, wined3d_context_vk_destroy_bo_slab, context_vk); + heap_free(context_vk->pending_queries.queries); + heap_free(context_vk->submitted.buffers); + heap_free(context_vk->retired.objects); + + wined3d_shader_descriptor_writes_vk_cleanup(&context_vk->descriptor_writes); + wine_rb_destroy(&context_vk->graphics_pipelines, wined3d_context_vk_destroy_graphics_pipeline, context_vk); + wine_rb_destroy(&context_vk->pipeline_layouts, wined3d_context_vk_destroy_pipeline_layout, context_vk); + wine_rb_destroy(&context_vk->render_passes, wined3d_context_vk_destroy_render_pass, context_vk); + + wined3d_context_cleanup(&context_vk->c); +} + +void wined3d_context_vk_remove_pending_queries(struct wined3d_context_vk *context_vk, + struct wined3d_query_vk *query_vk) +{ + struct wined3d_pending_queries_vk *pending = &context_vk->pending_queries; + struct wined3d_pending_query_vk *p; + size_t i; + + pending->free_idx = ~(size_t)0; + for (i = pending->count; i; --i) + { + p = &pending->queries[i - 1]; + + if (p->query_vk) + { + if (p->query_vk != query_vk && !wined3d_query_vk_accumulate_data(p->query_vk, context_vk, &p->pool_idx)) + continue; + wined3d_query_pool_vk_free_query(p->pool_idx.pool_vk, p->pool_idx.idx); + --p->query_vk->pending_count; + } + + if (i == pending->count) + { + --pending->count; + continue; + } + + p->query_vk = NULL; + p->pool_idx.pool_vk = NULL; + p->pool_idx.idx = pending->free_idx; + pending->free_idx = i - 1; + } +} + +void wined3d_context_vk_accumulate_pending_queries(struct wined3d_context_vk *context_vk) +{ + wined3d_context_vk_remove_pending_queries(context_vk, NULL); +} + +void wined3d_context_vk_add_pending_query(struct wined3d_context_vk *context_vk, struct wined3d_query_vk *query_vk) +{ + struct wined3d_pending_queries_vk *pending = &context_vk->pending_queries; + struct wined3d_pending_query_vk *p; + + if (pending->free_idx != ~(size_t)0) + { + p = &pending->queries[pending->free_idx]; + pending->free_idx = p->pool_idx.idx; + } + else + { + if (!wined3d_array_reserve((void **)&pending->queries, &pending->size, + pending->count + 1, sizeof(*pending->queries))) + { + ERR("Failed to allocate entry.\n"); + return; + } + p = &pending->queries[pending->count++]; + } + + p->query_vk = query_vk; + p->pool_idx = query_vk->pool_idx; + ++query_vk->pending_count; +} + +VkCommandBuffer wined3d_context_vk_get_command_buffer(struct wined3d_context_vk *context_vk) +{ + struct wined3d_device_vk *device_vk = wined3d_device_vk(context_vk->c.device); + const struct wined3d_vk_info *vk_info = context_vk->vk_info; + VkCommandBufferAllocateInfo command_buffer_info; + struct wined3d_command_buffer_vk *buffer; + VkCommandBufferBeginInfo begin_info; + struct wined3d_query_vk *query_vk; + VkResult vr; + + TRACE("context_vk %p.\n", context_vk); + + buffer = &context_vk->current_command_buffer; + if (buffer->vk_command_buffer) + { + TRACE("Returning existing command buffer %p with id 0x%s.\n", + buffer->vk_command_buffer, wine_dbgstr_longlong(buffer->id)); + return buffer->vk_command_buffer; + } + + command_buffer_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + command_buffer_info.pNext = NULL; + command_buffer_info.commandPool = context_vk->vk_command_pool; + command_buffer_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + command_buffer_info.commandBufferCount = 1; + if ((vr = VK_CALL(vkAllocateCommandBuffers(device_vk->vk_device, + &command_buffer_info, &buffer->vk_command_buffer))) < 0) + { + WARN("Failed to allocate Vulkan command buffer, vr %s.\n", wined3d_debug_vkresult(vr)); + return VK_NULL_HANDLE; + } + + begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + begin_info.pNext = NULL; + begin_info.flags = 0; + begin_info.pInheritanceInfo = NULL; + if ((vr = VK_CALL(vkBeginCommandBuffer(buffer->vk_command_buffer, &begin_info))) < 0) + { + WARN("Failed to begin command buffer, vr %s.\n", wined3d_debug_vkresult(vr)); + VK_CALL(vkFreeCommandBuffers(device_vk->vk_device, context_vk->vk_command_pool, + 1, &buffer->vk_command_buffer)); + return buffer->vk_command_buffer = VK_NULL_HANDLE; + } + + wined3d_context_vk_accumulate_pending_queries(context_vk); + LIST_FOR_EACH_ENTRY(query_vk, &context_vk->active_queries, struct wined3d_query_vk, entry) + { + wined3d_query_vk_resume(query_vk, context_vk); + } + + TRACE("Created new command buffer %p with id 0x%s.\n", + buffer->vk_command_buffer, wine_dbgstr_longlong(buffer->id)); + + return buffer->vk_command_buffer; +} + +void wined3d_context_vk_submit_command_buffer(struct wined3d_context_vk *context_vk, + unsigned int wait_semaphore_count, const VkSemaphore *wait_semaphores, const VkPipelineStageFlags *wait_stages, + unsigned int signal_semaphore_count, const VkSemaphore *signal_semaphores) +{ + struct wined3d_device_vk *device_vk = wined3d_device_vk(context_vk->c.device); + const struct wined3d_vk_info *vk_info = context_vk->vk_info; + struct wined3d_command_buffer_vk *buffer; + struct wined3d_query_vk *query_vk; + VkFenceCreateInfo fence_desc; + VkSubmitInfo submit_info; + VkResult vr; + + TRACE("context_vk %p, wait_semaphore_count %u, wait_semaphores %p, wait_stages %p," + "signal_semaphore_count %u, signal_semaphores %p.\n", + context_vk, wait_semaphore_count, wait_semaphores, wait_stages, + signal_semaphore_count, signal_semaphores); + + buffer = &context_vk->current_command_buffer; + if (!buffer->vk_command_buffer) + return; + + TRACE("Submitting command buffer %p with id 0x%s.\n", + buffer->vk_command_buffer, wine_dbgstr_longlong(buffer->id)); + + LIST_FOR_EACH_ENTRY(query_vk, &context_vk->active_queries, struct wined3d_query_vk, entry) + { + wined3d_query_vk_suspend(query_vk, context_vk); + } + + wined3d_context_vk_end_current_render_pass(context_vk); + context_vk->graphics.vk_pipeline = VK_NULL_HANDLE; + context_vk->update_compute_pipeline = 1; + context_vk->update_stream_output = 1; + context_vk->c.update_shader_resource_bindings = 1; + context_vk->c.update_compute_shader_resource_bindings = 1; + context_vk->c.update_unordered_access_view_bindings = 1; + context_vk->c.update_compute_unordered_access_view_bindings = 1; + context_invalidate_state(&context_vk->c, STATE_STREAMSRC); + context_invalidate_state(&context_vk->c, STATE_INDEXBUFFER); + context_invalidate_state(&context_vk->c, STATE_BLEND_FACTOR); + context_invalidate_state(&context_vk->c, STATE_STENCIL_REF); + + VK_CALL(vkEndCommandBuffer(buffer->vk_command_buffer)); + + fence_desc.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + fence_desc.pNext = NULL; + fence_desc.flags = 0; + if ((vr = VK_CALL(vkCreateFence(device_vk->vk_device, &fence_desc, NULL, &buffer->vk_fence))) < 0) + ERR("Failed to create fence, vr %s.\n", wined3d_debug_vkresult(vr)); + + submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit_info.pNext = NULL; + submit_info.waitSemaphoreCount = wait_semaphore_count; + submit_info.pWaitSemaphores = wait_semaphores; + submit_info.pWaitDstStageMask = wait_stages; + submit_info.commandBufferCount = 1; + submit_info.pCommandBuffers = &buffer->vk_command_buffer; + submit_info.signalSemaphoreCount = signal_semaphore_count; + submit_info.pSignalSemaphores = signal_semaphores; + + if ((vr = VK_CALL(vkQueueSubmit(device_vk->vk_queue, 1, &submit_info, buffer->vk_fence))) < 0) + ERR("Failed to submit command buffer %p, vr %s.\n", + buffer->vk_command_buffer, wined3d_debug_vkresult(vr)); + + if (!wined3d_array_reserve((void **)&context_vk->submitted.buffers, &context_vk->submitted.buffers_size, + context_vk->submitted.buffer_count + 1, sizeof(*context_vk->submitted.buffers))) + ERR("Failed to grow submitted command buffer array.\n"); + + context_vk->submitted.buffers[context_vk->submitted.buffer_count++] = *buffer; + + buffer->vk_command_buffer = VK_NULL_HANDLE; + /* We don't expect this to ever happen, but handle it anyway. */ + if (!++buffer->id) + { + wined3d_context_vk_wait_command_buffer(context_vk, buffer->id - 1); + context_vk->completed_command_buffer_id = 0; + buffer->id = 1; + } + wined3d_context_vk_cleanup_resources(context_vk); +} + +void wined3d_context_vk_wait_command_buffer(struct wined3d_context_vk *context_vk, uint64_t id) +{ + struct wined3d_device_vk *device_vk = wined3d_device_vk(context_vk->c.device); + const struct wined3d_vk_info *vk_info = context_vk->vk_info; + SIZE_T i; + + if (id <= context_vk->completed_command_buffer_id + || id > context_vk->current_command_buffer.id) /* In case the buffer ID wrapped. */ + return; + + for (i = 0; i < context_vk->submitted.buffer_count; ++i) + { + if (context_vk->submitted.buffers[i].id != id) + continue; + + VK_CALL(vkWaitForFences(device_vk->vk_device, 1, + &context_vk->submitted.buffers[i].vk_fence, VK_TRUE, UINT64_MAX)); + wined3d_context_vk_cleanup_resources(context_vk); + return; + } + + ERR("Failed to find fence for command buffer with id 0x%s.\n", wine_dbgstr_longlong(id)); +} + +void wined3d_context_vk_image_barrier(struct wined3d_context_vk *context_vk, + VkCommandBuffer vk_command_buffer, VkPipelineStageFlags src_stage_mask, VkPipelineStageFlags dst_stage_mask, + VkAccessFlags src_access_mask, VkAccessFlags dst_access_mask, VkImageLayout old_layout, + VkImageLayout new_layout, VkImage image, VkImageAspectFlags aspect_mask) +{ + const struct wined3d_vk_info *vk_info = context_vk->vk_info; + VkImageMemoryBarrier barrier; + + wined3d_context_vk_end_current_render_pass(context_vk); + + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.pNext = NULL; + barrier.srcAccessMask = src_access_mask; + barrier.dstAccessMask = dst_access_mask; + barrier.oldLayout = old_layout; + barrier.newLayout = new_layout; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.image = image; + barrier.subresourceRange.aspectMask = aspect_mask; + barrier.subresourceRange.baseMipLevel = 0; + barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS; + barrier.subresourceRange.baseArrayLayer = 0; + barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS; + + VK_CALL(vkCmdPipelineBarrier(vk_command_buffer, src_stage_mask, dst_stage_mask, 0, 0, NULL, 0, NULL, 1, &barrier)); +} + +static int wined3d_render_pass_vk_compare(const void *key, const struct wine_rb_entry *entry) +{ + const struct wined3d_render_pass_key_vk *k = key; + const struct wined3d_render_pass_vk *pass = WINE_RB_ENTRY_VALUE(entry, + const struct wined3d_render_pass_vk, entry); + + return memcmp(k, &pass->key, sizeof(*k)); +} + +static int wined3d_pipeline_layout_vk_compare(const void *key, const struct wine_rb_entry *entry) +{ + const struct wined3d_pipeline_layout_key_vk *a = key; + const struct wined3d_pipeline_layout_key_vk *b = &WINE_RB_ENTRY_VALUE(entry, + const struct wined3d_pipeline_layout_vk, entry)->key; + + if (a->binding_count != b->binding_count) + return a->binding_count - b->binding_count; + return memcmp(a->bindings, b->bindings, a->binding_count * sizeof(*a->bindings)); +} + +static int wined3d_graphics_pipeline_vk_compare(const void *key, const struct wine_rb_entry *entry) +{ + const struct wined3d_graphics_pipeline_key_vk *a = key; + const struct wined3d_graphics_pipeline_key_vk *b = &WINE_RB_ENTRY_VALUE(entry, + const struct wined3d_graphics_pipeline_vk, entry)->key; + unsigned int i; + int ret; + + if (a->pipeline_desc.stageCount != b->pipeline_desc.stageCount) + return a->pipeline_desc.stageCount - b->pipeline_desc.stageCount; + for (i = 0; i < a->pipeline_desc.stageCount; ++i) + { + if (a->stages[i].module != b->stages[i].module) + return a->stages[i].module - b->stages[i].module; + } + + if (a->divisor_desc.vertexBindingDivisorCount != b->divisor_desc.vertexBindingDivisorCount) + return a->divisor_desc.vertexBindingDivisorCount - b->divisor_desc.vertexBindingDivisorCount; + if ((ret = memcmp(a->divisors, b->divisors, + a->divisor_desc.vertexBindingDivisorCount * sizeof(*a->divisors)))) + return ret; + + if (a->input_desc.vertexAttributeDescriptionCount != b->input_desc.vertexAttributeDescriptionCount) + return a->input_desc.vertexAttributeDescriptionCount - b->input_desc.vertexAttributeDescriptionCount; + if ((ret = memcmp(a->attributes, b->attributes, + a->input_desc.vertexAttributeDescriptionCount * sizeof(*a->attributes)))) + return ret; + if (a->input_desc.vertexBindingDescriptionCount != b->input_desc.vertexBindingDescriptionCount) + return a->input_desc.vertexBindingDescriptionCount - b->input_desc.vertexBindingDescriptionCount; + if ((ret = memcmp(a->bindings, b->bindings, + a->input_desc.vertexBindingDescriptionCount * sizeof(*a->bindings)))) + return ret; + + if (a->ia_desc.topology != b->ia_desc.topology) + return a->ia_desc.topology - b->ia_desc.topology; + if (a->ia_desc.primitiveRestartEnable != b->ia_desc.primitiveRestartEnable) + return a->ia_desc.primitiveRestartEnable - b->ia_desc.primitiveRestartEnable; + + if (a->ts_desc.patchControlPoints != b->ts_desc.patchControlPoints) + return a->ts_desc.patchControlPoints - b->ts_desc.patchControlPoints; + + if ((ret = memcmp(&a->viewport, &b->viewport, sizeof(a->viewport)))) + return ret; + + if ((ret = memcmp(&a->scissor, &b->scissor, sizeof(a->scissor)))) + return ret; + + if ((ret = memcmp(&a->rs_desc, &b->rs_desc, sizeof(a->rs_desc)))) + return ret; + + if (a->ms_desc.rasterizationSamples != b->ms_desc.rasterizationSamples) + return a->ms_desc.rasterizationSamples - b->ms_desc.rasterizationSamples; + if (a->ms_desc.alphaToCoverageEnable != b->ms_desc.alphaToCoverageEnable) + return a->ms_desc.alphaToCoverageEnable - b->ms_desc.alphaToCoverageEnable; + if (a->sample_mask != b->sample_mask) + return a->sample_mask - b->sample_mask; + + if ((ret = memcmp(&a->ds_desc, &b->ds_desc, sizeof(a->ds_desc)))) + return ret; + + if (a->blend_desc.attachmentCount != b->blend_desc.attachmentCount) + return a->blend_desc.attachmentCount - b->blend_desc.attachmentCount; + if ((ret = memcmp(a->blend_attachments, b->blend_attachments, + a->blend_desc.attachmentCount * sizeof(*a->blend_attachments)))) + return ret; + + if (a->pipeline_desc.layout != b->pipeline_desc.layout) + return a->pipeline_desc.layout - b->pipeline_desc.layout; + + if (a->pipeline_desc.renderPass != b->pipeline_desc.renderPass) + return a->pipeline_desc.renderPass - b->pipeline_desc.renderPass; + + return 0; +} + +static int wined3d_bo_slab_vk_compare(const void *key, const struct wine_rb_entry *entry) +{ + const struct wined3d_bo_slab_vk *slab = WINE_RB_ENTRY_VALUE(entry, const struct wined3d_bo_slab_vk, entry); + const struct wined3d_bo_slab_vk_key *k = key; + + if (k->memory_type != slab->requested_memory_type) + return k->memory_type - slab->requested_memory_type; + if (k->usage != slab->bo.usage) + return k->usage - slab->bo.usage; + return k->size - slab->bo.size; +} + +static void wined3d_context_vk_init_graphics_pipeline_key(struct wined3d_context_vk *context_vk) +{ + struct wined3d_graphics_pipeline_key_vk *key; + VkPipelineShaderStageCreateInfo *stage; + unsigned int i; + + static const VkDynamicState dynamic_states[] = + { + VK_DYNAMIC_STATE_BLEND_CONSTANTS, + VK_DYNAMIC_STATE_STENCIL_REFERENCE, + }; + + key = &context_vk->graphics.pipeline_key_vk; + memset(key, 0, sizeof(*key)); + + for (i = 0; i < ARRAY_SIZE(context_vk->graphics.vk_modules); ++i) + { + stage = &key->stages[i]; + stage->sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + stage->pName = "main"; + } + + key->input_desc.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + key->input_desc.pVertexBindingDescriptions = key->bindings; + key->input_desc.pVertexAttributeDescriptions = key->attributes; + + key->divisor_desc.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_DIVISOR_STATE_CREATE_INFO_EXT; + key->divisor_desc.pVertexBindingDivisors = key->divisors; + + key->ia_desc.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + + key->ts_desc.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO; + + key->vp_desc.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + key->vp_desc.viewportCount = 1; + key->vp_desc.pViewports = &key->viewport; + key->vp_desc.scissorCount = 1; + key->vp_desc.pScissors = &key->scissor; + + key->rs_desc.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + key->rs_desc.lineWidth = 1.0f; + + key->ms_desc.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + key->ms_desc.pSampleMask = &key->sample_mask; + + key->ds_desc.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; + key->ds_desc.maxDepthBounds = 1.0f; + + key->blend_desc.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + key->blend_desc.logicOp = VK_LOGIC_OP_COPY; + key->blend_desc.pAttachments = key->blend_attachments; + key->blend_desc.blendConstants[0] = 1.0f; + key->blend_desc.blendConstants[1] = 1.0f; + key->blend_desc.blendConstants[2] = 1.0f; + key->blend_desc.blendConstants[3] = 1.0f; + + key->dynamic_desc.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + key->dynamic_desc.dynamicStateCount = ARRAY_SIZE(dynamic_states); + key->dynamic_desc.pDynamicStates = dynamic_states; + + key->pipeline_desc.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + key->pipeline_desc.pStages = key->stages; + key->pipeline_desc.pVertexInputState = &key->input_desc; + key->pipeline_desc.pInputAssemblyState = &key->ia_desc; + key->pipeline_desc.pTessellationState = &key->ts_desc; + key->pipeline_desc.pViewportState = &key->vp_desc; + key->pipeline_desc.pRasterizationState = &key->rs_desc; + key->pipeline_desc.pMultisampleState = &key->ms_desc; + key->pipeline_desc.pDepthStencilState = &key->ds_desc; + key->pipeline_desc.pColorBlendState = &key->blend_desc; + key->pipeline_desc.pDynamicState = &key->dynamic_desc; + key->pipeline_desc.basePipelineIndex = -1; +} + +static void wined3d_context_vk_update_rasterisation_state(const struct wined3d_context_vk *context_vk, + const struct wined3d_state *state, struct wined3d_graphics_pipeline_key_vk *key) +{ + const struct wined3d_d3d_info *d3d_info = context_vk->c.d3d_info; + VkPipelineRasterizationStateCreateInfo *desc = &key->rs_desc; + const struct wined3d_rasterizer_state_desc *r; + float scale_bias; + union + { + uint32_t u32; + float f32; + } const_bias; + + if (!state->rasterizer_state) + { + desc->depthClampEnable = VK_FALSE; + desc->rasterizerDiscardEnable = is_rasterization_disabled(state->shader[WINED3D_SHADER_TYPE_GEOMETRY]); + desc->cullMode = VK_CULL_MODE_BACK_BIT; + desc->frontFace = VK_FRONT_FACE_CLOCKWISE; + desc->depthBiasEnable = VK_FALSE; + desc->depthBiasConstantFactor = 0.0f; + desc->depthBiasClamp = 0.0f; + desc->depthBiasSlopeFactor = 0.0f; + + return; + } + + r = &state->rasterizer_state->desc; + desc->depthClampEnable = !r->depth_clip; + desc->rasterizerDiscardEnable = is_rasterization_disabled(state->shader[WINED3D_SHADER_TYPE_GEOMETRY]); + desc->cullMode = vk_cull_mode_from_wined3d(r->cull_mode); + desc->frontFace = r->front_ccw ? VK_FRONT_FACE_COUNTER_CLOCKWISE : VK_FRONT_FACE_CLOCKWISE; + + scale_bias = r->scale_bias; + const_bias.f32 = r->depth_bias; + if (!scale_bias && !const_bias.f32) + { + desc->depthBiasEnable = VK_FALSE; + desc->depthBiasConstantFactor = 0.0f; + desc->depthBiasClamp = 0.0f; + desc->depthBiasSlopeFactor = 0.0f; + + return; + } + + desc->depthBiasEnable = VK_TRUE; + if (d3d_info->wined3d_creation_flags & WINED3D_LEGACY_DEPTH_BIAS) + { + const struct wined3d_rendertarget_view *dsv; + + if ((dsv = state->fb.depth_stencil)) + { + desc->depthBiasConstantFactor = -(float)const_bias.u32 / dsv->format->depth_bias_scale; + desc->depthBiasSlopeFactor = -(float)const_bias.u32; + } + else + { + desc->depthBiasConstantFactor = 0.0f; + desc->depthBiasSlopeFactor = 0.0f; + } + } + else + { + desc->depthBiasConstantFactor = const_bias.f32; + desc->depthBiasSlopeFactor = scale_bias; + } + desc->depthBiasClamp = r->depth_bias_clamp; +} + +static void wined3d_context_vk_update_blend_state(const struct wined3d_context_vk *context_vk, + const struct wined3d_state *state, struct wined3d_graphics_pipeline_key_vk *key) +{ + VkPipelineColorBlendStateCreateInfo *desc = &key->blend_desc; + const struct wined3d_blend_state_desc *b; + unsigned int i; + + desc->attachmentCount = context_vk->rt_count; + + memset(key->blend_attachments, 0, sizeof(key->blend_attachments)); + if (!state->blend_state) + { + for (i = 0; i < context_vk->rt_count; ++i) + { + key->blend_attachments[i].colorWriteMask = VK_COLOR_COMPONENT_R_BIT + | VK_COLOR_COMPONENT_G_BIT + | VK_COLOR_COMPONENT_B_BIT + | VK_COLOR_COMPONENT_A_BIT; + } + + return; + } + + b = &state->blend_state->desc; + for (i = 0; i < context_vk->rt_count; ++i) + { + const struct wined3d_rendertarget_blend_state_desc *rt = &b->rt[b->independent ? i : 0]; + const struct wined3d_rendertarget_view *rtv = state->fb.render_targets[i]; + VkPipelineColorBlendAttachmentState *a = &key->blend_attachments[i]; + enum wined3d_blend src_blend, dst_blend; + const struct wined3d_format *rt_format; + + a->colorWriteMask = vk_colour_write_mask_from_wined3d(rt->writemask); + if (!rt->enable) + continue; + + if (rtv) + rt_format = rtv->format; + else + rt_format = wined3d_get_format(context_vk->c.device->adapter, WINED3DFMT_NULL, 0); + a->blendEnable = VK_TRUE; + + src_blend = rt->src; + dst_blend = rt->dst; + if (src_blend == WINED3D_BLEND_BOTHSRCALPHA) + { + src_blend = WINED3D_BLEND_SRCALPHA; + dst_blend = WINED3D_BLEND_INVSRCALPHA; + } + else if (src_blend == WINED3D_BLEND_BOTHINVSRCALPHA) + { + src_blend = WINED3D_BLEND_INVSRCALPHA; + dst_blend = WINED3D_BLEND_SRCALPHA; + } + a->srcColorBlendFactor = vk_blend_factor_from_wined3d(src_blend, rt_format, FALSE); + a->dstColorBlendFactor = vk_blend_factor_from_wined3d(dst_blend, rt_format, FALSE); + a->colorBlendOp = vk_blend_op_from_wined3d(rt->op); + + src_blend = rt->src_alpha; + dst_blend = rt->dst_alpha; + a->srcAlphaBlendFactor = vk_blend_factor_from_wined3d(src_blend, rt_format, TRUE); + a->dstAlphaBlendFactor = vk_blend_factor_from_wined3d(dst_blend, rt_format, TRUE); + a->alphaBlendOp = vk_blend_op_from_wined3d(rt->op_alpha); + } +} + +static bool wined3d_context_vk_update_graphics_pipeline_key(struct wined3d_context_vk *context_vk, + const struct wined3d_state *state, VkPipelineLayout vk_pipeline_layout) +{ + unsigned int i, attribute_count, binding_count, divisor_count, stage_count; + const struct wined3d_d3d_info *d3d_info = context_vk->c.d3d_info; + struct wined3d_graphics_pipeline_key_vk *key; + VkPipelineShaderStageCreateInfo *stage; + struct wined3d_stream_info stream_info; + VkPrimitiveTopology vk_topology; + VkShaderModule module; + bool update = false; + uint32_t mask; + + key = &context_vk->graphics.pipeline_key_vk; + + if (context_vk->c.shader_update_mask & ~(1u << WINED3D_SHADER_TYPE_COMPUTE)) + { + stage_count = 0; + for (i = 0; i < ARRAY_SIZE(context_vk->graphics.vk_modules); ++i) + { + if (!(module = context_vk->graphics.vk_modules[i])) + continue; + + stage = &key->stages[stage_count++]; + stage->stage = vk_shader_stage_from_wined3d(i); + stage->module = module; + } + + key->pipeline_desc.stageCount = stage_count; + + update = true; + } + + if (wined3d_context_is_graphics_state_dirty(&context_vk->c, STATE_VDECL) + || wined3d_context_is_graphics_state_dirty(&context_vk->c, STATE_STREAMSRC) + || wined3d_context_is_graphics_state_dirty(&context_vk->c, STATE_SHADER(WINED3D_SHADER_TYPE_VERTEX))) + { + wined3d_stream_info_from_declaration(&stream_info, state, d3d_info); + divisor_count = 0; + for (i = 0, mask = 0, attribute_count = 0, binding_count = 0; i < ARRAY_SIZE(stream_info.elements); ++i) + { + VkVertexInputBindingDivisorDescriptionEXT *d; + struct wined3d_stream_info_element *e; + VkVertexInputAttributeDescription *a; + VkVertexInputBindingDescription *b; + uint32_t binding; + + if (!(stream_info.use_map & (1u << i))) + continue; + + a = &key->attributes[attribute_count++]; + e = &stream_info.elements[i]; + binding = e->stream_idx; + + a->location = i; + a->binding = binding; + a->format = wined3d_format_vk(e->format)->vk_format; + a->offset = (UINT_PTR)e->data.addr - state->streams[binding].offset; + + if (mask & (1u << binding)) + continue; + mask |= 1u << binding; + + b = &key->bindings[binding_count++]; + b->binding = binding; + b->stride = e->stride; + b->inputRate = e->divisor ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX; + + if (e->divisor > 1) + { + d = &key->divisors[divisor_count++]; + d->binding = binding; + d->divisor = e->divisor; + } + } + + key->input_desc.pNext = NULL; + key->input_desc.vertexBindingDescriptionCount = binding_count; + key->input_desc.vertexAttributeDescriptionCount = attribute_count; + + if (divisor_count) + { + key->input_desc.pNext = &key->divisor_desc; + key->divisor_desc.vertexBindingDivisorCount = divisor_count; + } + + update = true; + } + + vk_topology = vk_topology_from_wined3d(state->primitive_type); + if (key->ia_desc.topology != vk_topology) + { + key->ia_desc.topology = vk_topology; + key->ia_desc.primitiveRestartEnable = !(d3d_info->wined3d_creation_flags & WINED3D_NO_PRIMITIVE_RESTART) + && !wined3d_primitive_type_is_list(state->primitive_type); + + update = true; + } + + if (key->ts_desc.patchControlPoints != state->patch_vertex_count) + { + key->ts_desc.patchControlPoints = state->patch_vertex_count; + + update = true; + } + + if (wined3d_context_is_graphics_state_dirty(&context_vk->c, STATE_VIEWPORT) + || wined3d_context_is_graphics_state_dirty(&context_vk->c, STATE_SCISSORRECT) + || wined3d_context_is_graphics_state_dirty(&context_vk->c, STATE_RASTERIZER)) + { + key->viewport.x = state->viewports[0].x; + key->viewport.y = state->viewports[0].y; + key->viewport.width = state->viewports[0].width; + key->viewport.height = state->viewports[0].height; + key->viewport.minDepth = state->viewports[0].min_z; + key->viewport.maxDepth = state->viewports[0].max_z; + + if (state->rasterizer_state && state->rasterizer_state->desc.scissor) + { + const RECT *r = &state->scissor_rects[0]; + + key->scissor.offset.x = r->left; + key->scissor.offset.y = r->top; + key->scissor.extent.width = r->right - r->left; + key->scissor.extent.height = r->bottom - r->top; + } + else + { + key->scissor.offset.x = key->viewport.x; + key->scissor.offset.y = key->viewport.y; + key->scissor.extent.width = key->viewport.width; + key->scissor.extent.height = key->viewport.height; + } + key->viewport.y += key->viewport.height; + key->viewport.height = -key->viewport.height; + + update = true; + } + + if (wined3d_context_is_graphics_state_dirty(&context_vk->c, STATE_RASTERIZER) + || wined3d_context_is_graphics_state_dirty(&context_vk->c, STATE_SHADER(WINED3D_SHADER_TYPE_GEOMETRY))) + { + wined3d_context_vk_update_rasterisation_state(context_vk, state, key); + + update = true; + } + + if (key->ms_desc.rasterizationSamples != context_vk->sample_count + || isStateDirty(&context_vk->c, STATE_BLEND) || isStateDirty(&context_vk->c, STATE_SAMPLE_MASK)) + { + key->ms_desc.rasterizationSamples = context_vk->sample_count; + key->ms_desc.alphaToCoverageEnable = state->blend_state && state->blend_state->desc.alpha_to_coverage; + key->sample_mask = state->sample_mask; + + update = true; + } + + if (wined3d_context_is_graphics_state_dirty(&context_vk->c, STATE_DEPTH_STENCIL) + || wined3d_context_is_graphics_state_dirty(&context_vk->c, STATE_FRAMEBUFFER)) + { + const struct wined3d_depth_stencil_state *d = state->depth_stencil_state; + + if (d) + { + key->ds_desc.depthTestEnable = d->desc.depth; + key->ds_desc.depthWriteEnable = d->desc.depth_write; + key->ds_desc.depthCompareOp = vk_compare_op_from_wined3d(d->desc.depth_func); + key->ds_desc.stencilTestEnable = state->fb.depth_stencil && d->desc.stencil; + if (key->ds_desc.stencilTestEnable) + { + key->ds_desc.front.failOp = vk_stencil_op_from_wined3d(d->desc.front.fail_op); + key->ds_desc.front.passOp = vk_stencil_op_from_wined3d(d->desc.front.pass_op); + key->ds_desc.front.depthFailOp = vk_stencil_op_from_wined3d(d->desc.front.depth_fail_op); + key->ds_desc.front.compareOp = vk_compare_op_from_wined3d(d->desc.front.func); + key->ds_desc.front.compareMask = d->desc.stencil_read_mask; + key->ds_desc.front.writeMask = d->desc.stencil_write_mask; + + key->ds_desc.back.failOp = vk_stencil_op_from_wined3d(d->desc.back.fail_op); + key->ds_desc.back.passOp = vk_stencil_op_from_wined3d(d->desc.back.pass_op); + key->ds_desc.back.depthFailOp = vk_stencil_op_from_wined3d(d->desc.back.depth_fail_op); + key->ds_desc.back.compareOp = vk_compare_op_from_wined3d(d->desc.back.func); + key->ds_desc.back.compareMask = d->desc.stencil_read_mask; + key->ds_desc.back.writeMask = d->desc.stencil_write_mask; + } + else + { + memset(&key->ds_desc.front, 0, sizeof(key->ds_desc.front)); + memset(&key->ds_desc.back, 0, sizeof(key->ds_desc.back)); + } + } + else + { + key->ds_desc.depthTestEnable = VK_TRUE; + key->ds_desc.depthWriteEnable = VK_TRUE; + key->ds_desc.depthCompareOp = VK_COMPARE_OP_LESS; + key->ds_desc.stencilTestEnable = VK_FALSE; + } + + update = true; + } + + if (wined3d_context_is_graphics_state_dirty(&context_vk->c, STATE_BLEND) + || wined3d_context_is_graphics_state_dirty(&context_vk->c, STATE_FRAMEBUFFER)) + { + wined3d_context_vk_update_blend_state(context_vk, state, key); + + update = true; + } + + if (key->pipeline_desc.layout != vk_pipeline_layout) + { + key->pipeline_desc.layout = vk_pipeline_layout; + + update = true; + } + + if (key->pipeline_desc.renderPass != context_vk->vk_render_pass) + { + key->pipeline_desc.renderPass = context_vk->vk_render_pass; + + update = true; + } + + return update; +} + +static bool wined3d_context_vk_begin_render_pass(struct wined3d_context_vk *context_vk, + VkCommandBuffer vk_command_buffer, const struct wined3d_state *state, const struct wined3d_vk_info *vk_info) +{ + struct wined3d_device_vk *device_vk = wined3d_device_vk(context_vk->c.device); + VkImageView vk_views[WINED3D_MAX_RENDER_TARGETS + 1]; + unsigned int fb_width, fb_height, fb_layer_count; + struct wined3d_rendertarget_view_vk *rtv_vk; + struct wined3d_rendertarget_view *view; + const VkPhysicalDeviceLimits *limits; + VkRenderPassBeginInfo begin_info; + unsigned int attachment_count, i; + VkFramebufferCreateInfo fb_desc; + VkResult vr; + + if (context_vk->vk_render_pass) + return true; + + limits = &wined3d_adapter_vk(device_vk->d.adapter)->device_limits; + fb_width = limits->maxFramebufferWidth; + fb_height = limits->maxFramebufferHeight; + fb_layer_count = limits->maxFramebufferLayers; + attachment_count = 0; + + context_vk->rt_count = 0; + for (i = 0; i < ARRAY_SIZE(state->fb.render_targets); ++i) + { + if (!(view = state->fb.render_targets[i]) || view->format->id == WINED3DFMT_NULL) + continue; + + rtv_vk = wined3d_rendertarget_view_vk(view); + vk_views[attachment_count] = wined3d_rendertarget_view_vk_get_image_view(rtv_vk, context_vk); + wined3d_rendertarget_view_vk_barrier(rtv_vk, context_vk, WINED3D_BIND_RENDER_TARGET); + wined3d_context_vk_reference_rendertarget_view(context_vk, rtv_vk); + + if (view->width < fb_width) + fb_width = view->width; + if (view->height < fb_height) + fb_height = view->height; + if (view->layer_count < fb_layer_count) + fb_layer_count = view->layer_count; + context_vk->rt_count = i + 1; + ++attachment_count; + } + + if ((view = state->fb.depth_stencil)) + { + rtv_vk = wined3d_rendertarget_view_vk(view); + vk_views[attachment_count] = wined3d_rendertarget_view_vk_get_image_view(rtv_vk, context_vk); + wined3d_rendertarget_view_vk_barrier(rtv_vk, context_vk, WINED3D_BIND_DEPTH_STENCIL); + wined3d_context_vk_reference_rendertarget_view(context_vk, rtv_vk); + + if (view->width < fb_width) + fb_width = view->width; + if (view->height < fb_height) + fb_height = view->height; + if (view->layer_count < fb_layer_count) + fb_layer_count = view->layer_count; + ++attachment_count; + } + + if (!(context_vk->vk_render_pass = wined3d_context_vk_get_render_pass(context_vk, &state->fb, + ARRAY_SIZE(state->fb.render_targets), !!state->fb.depth_stencil, 0))) + { + ERR("Failed to get render pass.\n"); + return false; + } + + fb_desc.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + fb_desc.pNext = NULL; + fb_desc.flags = 0; + fb_desc.renderPass = context_vk->vk_render_pass; + fb_desc.attachmentCount = attachment_count; + fb_desc.pAttachments = vk_views; + fb_desc.width = fb_width; + fb_desc.height = fb_height; + fb_desc.layers = fb_layer_count; + + if ((vr = VK_CALL(vkCreateFramebuffer(device_vk->vk_device, &fb_desc, NULL, &context_vk->vk_framebuffer))) < 0) + { + WARN("Failed to create Vulkan framebuffer, vr %s.\n", wined3d_debug_vkresult(vr)); + return false; + } + + begin_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + begin_info.pNext = NULL; + begin_info.renderPass = context_vk->vk_render_pass; + begin_info.framebuffer = context_vk->vk_framebuffer; + begin_info.renderArea.offset.x = 0; + begin_info.renderArea.offset.y = 0; + begin_info.renderArea.extent.width = fb_width; + begin_info.renderArea.extent.height = fb_height; + begin_info.clearValueCount = 0; + begin_info.pClearValues = NULL; + VK_CALL(vkCmdBeginRenderPass(vk_command_buffer, &begin_info, VK_SUBPASS_CONTENTS_INLINE)); + + return true; +} + +static void wined3d_context_vk_bind_vertex_buffers(struct wined3d_context_vk *context_vk, + VkCommandBuffer vk_command_buffer, const struct wined3d_state *state, const struct wined3d_vk_info *vk_info) +{ + VkDeviceSize offsets[ARRAY_SIZE(state->streams)] = {0}; + VkBuffer buffers[ARRAY_SIZE(state->streams)]; + const struct wined3d_stream_state *stream; + const VkDescriptorBufferInfo *buffer_info; + struct wined3d_buffer_vk *buffer_vk; + struct wined3d_buffer *buffer; + unsigned int i, first, count; + + first = 0; + count = 0; + for (i = 0; i < ARRAY_SIZE(state->streams); ++i) + { + stream = &state->streams[i]; + + if ((buffer = stream->buffer)) + { + buffer_vk = wined3d_buffer_vk(buffer); + buffer_info = wined3d_buffer_vk_get_buffer_info(buffer_vk); + wined3d_context_vk_reference_bo(context_vk, &buffer_vk->bo); + buffers[count] = buffer_info->buffer; + offsets[count] = buffer_info->offset + stream->offset; + ++count; + continue; + } + + if (count) + VK_CALL(vkCmdBindVertexBuffers(vk_command_buffer, first, count, buffers, offsets)); + first = i + 1; + count = 0; + } + + if (count) + VK_CALL(vkCmdBindVertexBuffers(vk_command_buffer, first, count, buffers, offsets)); +} + +static void wined3d_context_vk_bind_stream_output_buffers(struct wined3d_context_vk *context_vk, + VkCommandBuffer vk_command_buffer, const struct wined3d_state *state, const struct wined3d_vk_info *vk_info) +{ + VkDeviceSize offsets[ARRAY_SIZE(state->stream_output)]; + VkDeviceSize sizes[ARRAY_SIZE(state->stream_output)]; + VkBuffer buffers[ARRAY_SIZE(state->stream_output)]; + const struct wined3d_stream_output *stream; + const VkDescriptorBufferInfo *buffer_info; + struct wined3d_buffer_vk *buffer_vk; + struct wined3d_buffer *buffer; + unsigned int i, first, count; + + first = 0; + count = 0; + for (i = 0; i < ARRAY_SIZE(state->stream_output); ++i) + { + stream = &state->stream_output[i]; + + if ((buffer = stream->buffer)) + { + buffer_vk = wined3d_buffer_vk(buffer); + buffer_info = wined3d_buffer_vk_get_buffer_info(buffer_vk); + wined3d_context_vk_reference_bo(context_vk, &buffer_vk->bo); + buffers[count] = buffer_info->buffer; + if ((offsets[count] = stream->offset) == ~0u) + { + FIXME("Appending to stream output buffers not implemented.\n"); + offsets[count] = 0; + } + sizes[count] = buffer_info->range - offsets[count]; + offsets[count] += buffer_info->offset; + ++count; + continue; + } + + if (count) + VK_CALL(vkCmdBindTransformFeedbackBuffersEXT(vk_command_buffer, first, count, buffers, offsets, sizes)); + first = i + 1; + count = 0; + } + + if (count) + VK_CALL(vkCmdBindTransformFeedbackBuffersEXT(vk_command_buffer, first, count, buffers, offsets, sizes)); +} + +static VkResult wined3d_context_vk_create_descriptor_pool(struct wined3d_device_vk *device_vk, + const struct wined3d_vk_info *vk_info, VkDescriptorPool *vk_pool) +{ + struct VkDescriptorPoolCreateInfo pool_desc; + VkResult vr; + + static const VkDescriptorPoolSize pool_sizes[] = + { + {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1024}, + {VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1024}, + {VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1024}, + {VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1024}, + {VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1024}, + {VK_DESCRIPTOR_TYPE_SAMPLER, 1024}, + }; + + pool_desc.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + pool_desc.pNext = NULL; + pool_desc.flags = 0; + pool_desc.maxSets = 512; + pool_desc.poolSizeCount = ARRAY_SIZE(pool_sizes); + pool_desc.pPoolSizes = pool_sizes; + + if ((vr = VK_CALL(vkCreateDescriptorPool(device_vk->vk_device, &pool_desc, NULL, vk_pool))) < 0) + ERR("Failed to create descriptor pool, vr %s.\n", wined3d_debug_vkresult(vr)); + + return vr; +} + +static VkResult wined3d_context_vk_create_descriptor_set(struct wined3d_context_vk *context_vk, + VkDescriptorSetLayout vk_set_layout, VkDescriptorSet *vk_descriptor_set) +{ + struct wined3d_device_vk *device_vk = wined3d_device_vk(context_vk->c.device); + const struct wined3d_vk_info *vk_info = context_vk->vk_info; + struct VkDescriptorSetAllocateInfo set_desc; + VkResult vr; + + if (!context_vk->vk_descriptor_pool && (vr = wined3d_context_vk_create_descriptor_pool(device_vk, + vk_info, &context_vk->vk_descriptor_pool))) + { + WARN("Failed to create descriptor pool, vr %s.\n", wined3d_debug_vkresult(vr)); + return vr; + } + + set_desc.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + set_desc.pNext = NULL; + set_desc.descriptorPool = context_vk->vk_descriptor_pool; + set_desc.descriptorSetCount = 1; + set_desc.pSetLayouts = &vk_set_layout; + if ((vr = VK_CALL(vkAllocateDescriptorSets(device_vk->vk_device, &set_desc, vk_descriptor_set))) >= 0) + return vr; + + if (vr == VK_ERROR_FRAGMENTED_POOL || vr == VK_ERROR_OUT_OF_POOL_MEMORY) + { + wined3d_context_vk_destroy_descriptor_pool(context_vk, + context_vk->vk_descriptor_pool, context_vk->current_command_buffer.id); + context_vk->vk_descriptor_pool = VK_NULL_HANDLE; + if ((vr = wined3d_context_vk_create_descriptor_pool(device_vk, vk_info, &context_vk->vk_descriptor_pool))) + { + WARN("Failed to create descriptor pool, vr %s.\n", wined3d_debug_vkresult(vr)); + return vr; + } + + set_desc.descriptorPool = context_vk->vk_descriptor_pool; + if ((vr = VK_CALL(vkAllocateDescriptorSets(device_vk->vk_device, &set_desc, vk_descriptor_set))) >= 0) + return vr; + } + + WARN("Failed to allocate descriptor set, vr %s.\n", wined3d_debug_vkresult(vr)); + + return vr; +} + +static bool wined3d_shader_descriptor_writes_vk_add_write(struct wined3d_shader_descriptor_writes_vk *writes, + VkDescriptorSet vk_descriptor_set, size_t binding_idx, VkDescriptorType type, + const VkDescriptorBufferInfo *buffer_info, const VkDescriptorImageInfo *image_info, + const VkBufferView *buffer_view) +{ + SIZE_T write_count = writes->count; + VkWriteDescriptorSet *write; + + if (!wined3d_array_reserve((void **)&writes->writes, &writes->size, + write_count + 1, sizeof(*writes->writes))) + return false; + + write = &writes->writes[write_count]; + write->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + write->pNext = NULL; + write->dstSet = vk_descriptor_set; + write->dstBinding = binding_idx; + write->dstArrayElement = 0; + write->descriptorCount = 1; + write->descriptorType = type; + write->pImageInfo = image_info; + write->pBufferInfo = buffer_info; + write->pTexelBufferView = buffer_view; + + ++writes->count; + + return true; +} + +static bool wined3d_shader_resource_bindings_add_null_srv_binding(struct wined3d_shader_descriptor_writes_vk *writes, + VkDescriptorSet vk_descriptor_set, size_t binding_idx, enum wined3d_shader_resource_type type, + enum wined3d_data_type data_type, struct wined3d_context_vk *context_vk) +{ + const struct wined3d_null_views_vk *v = &wined3d_device_vk(context_vk->c.device)->null_views_vk; + + switch (type) + { + case WINED3D_SHADER_RESOURCE_BUFFER: + if (data_type == WINED3D_DATA_FLOAT) + return wined3d_shader_descriptor_writes_vk_add_write(writes, vk_descriptor_set, binding_idx, + VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, NULL, NULL, &v->vk_view_buffer_float); + return wined3d_shader_descriptor_writes_vk_add_write(writes, vk_descriptor_set, binding_idx, + VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, NULL, NULL, &v->vk_view_buffer_uint); + + case WINED3D_SHADER_RESOURCE_TEXTURE_1D: + return wined3d_shader_descriptor_writes_vk_add_write(writes, vk_descriptor_set, + binding_idx, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, NULL, &v->vk_info_1d, NULL); + + case WINED3D_SHADER_RESOURCE_TEXTURE_2D: + return wined3d_shader_descriptor_writes_vk_add_write(writes, vk_descriptor_set, + binding_idx, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, NULL, &v->vk_info_2d, NULL); + + case WINED3D_SHADER_RESOURCE_TEXTURE_2DMS: + return wined3d_shader_descriptor_writes_vk_add_write(writes, vk_descriptor_set, + binding_idx, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, NULL, &v->vk_info_2dms, NULL); + + case WINED3D_SHADER_RESOURCE_TEXTURE_3D: + return wined3d_shader_descriptor_writes_vk_add_write(writes, vk_descriptor_set, + binding_idx, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, NULL, &v->vk_info_3d, NULL); + + case WINED3D_SHADER_RESOURCE_TEXTURE_CUBE: + return wined3d_shader_descriptor_writes_vk_add_write(writes, vk_descriptor_set, + binding_idx, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, NULL, &v->vk_info_cube, NULL); + + case WINED3D_SHADER_RESOURCE_TEXTURE_2DARRAY: + return wined3d_shader_descriptor_writes_vk_add_write(writes, vk_descriptor_set, + binding_idx, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, NULL, &v->vk_info_2d_array, NULL); + + case WINED3D_SHADER_RESOURCE_TEXTURE_2DMSARRAY: + return wined3d_shader_descriptor_writes_vk_add_write(writes, vk_descriptor_set, + binding_idx, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, NULL, &v->vk_info_2dms_array, NULL); + + default: + FIXME("Unhandled resource type %#x.\n", type); + return false; + } +} + +static bool wined3d_context_vk_update_descriptors(struct wined3d_context_vk *context_vk, + VkCommandBuffer vk_command_buffer, const struct wined3d_state *state, enum wined3d_pipeline pipeline) +{ + struct wined3d_shader_descriptor_writes_vk *writes = &context_vk->descriptor_writes; + struct wined3d_device_vk *device_vk = wined3d_device_vk(context_vk->c.device); + const struct wined3d_vk_info *vk_info = context_vk->vk_info; + const struct wined3d_shader_resource_binding *binding; + struct wined3d_shader_resource_bindings *bindings; + struct wined3d_unordered_access_view_vk *uav_vk; + struct wined3d_shader_resource_view_vk *srv_vk; + struct wined3d_unordered_access_view *uav; + const VkDescriptorBufferInfo *buffer_info; + struct wined3d_shader_resource_view *srv; + const VkDescriptorImageInfo *image_info; + struct wined3d_buffer_vk *buffer_vk; + VkDescriptorSetLayout vk_set_layout; + VkPipelineLayout vk_pipeline_layout; + struct wined3d_resource *resource; + VkPipelineBindPoint vk_bind_point; + VkDescriptorSet vk_descriptor_set; + struct wined3d_view_vk *view_vk; + struct wined3d_sampler *sampler; + struct wined3d_buffer *buffer; + VkBufferView *buffer_view; + VkDescriptorType type; + VkResult vr; + size_t i; + + switch (pipeline) + { + case WINED3D_PIPELINE_GRAPHICS: + bindings = &context_vk->graphics.bindings; + vk_bind_point = VK_PIPELINE_BIND_POINT_GRAPHICS; + vk_set_layout = context_vk->graphics.vk_set_layout; + vk_pipeline_layout = context_vk->graphics.vk_pipeline_layout; + break; + + case WINED3D_PIPELINE_COMPUTE: + bindings = &context_vk->compute.bindings; + vk_bind_point = VK_PIPELINE_BIND_POINT_COMPUTE; + vk_set_layout = context_vk->compute.vk_set_layout; + vk_pipeline_layout = context_vk->compute.vk_pipeline_layout; + break; + + default: + ERR("Invalid pipeline %#x.\n", pipeline); + return false; + } + + if ((vr = wined3d_context_vk_create_descriptor_set(context_vk, vk_set_layout, &vk_descriptor_set))) + { + WARN("Failed to create descriptor set, vr %s.\n", wined3d_debug_vkresult(vr)); + return false; + } + + writes->count = 0; + for (i = 0; i < bindings->count; ++i) + { + binding = &bindings->bindings[i]; + + switch (binding->shader_descriptor_type) + { + case WINED3D_SHADER_DESCRIPTOR_TYPE_CBV: + if (!(buffer = state->cb[binding->shader_type][binding->resource_idx])) + { + if (!wined3d_shader_descriptor_writes_vk_add_write(writes, vk_descriptor_set, + binding->binding_idx, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + &device_vk->null_resources_vk.buffer_info, NULL, NULL)) + return false; + break; + } + buffer_vk = wined3d_buffer_vk(buffer); + buffer_info = wined3d_buffer_vk_get_buffer_info(buffer_vk); + if (!wined3d_shader_descriptor_writes_vk_add_write(writes, vk_descriptor_set, + binding->binding_idx, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, buffer_info, NULL, NULL)) + return false; + wined3d_context_vk_reference_bo(context_vk, &buffer_vk->bo); + break; + + case WINED3D_SHADER_DESCRIPTOR_TYPE_SRV: + if (!(srv = state->shader_resource_view[binding->shader_type][binding->resource_idx])) + { + if (!wined3d_shader_resource_bindings_add_null_srv_binding(writes, vk_descriptor_set, + binding->binding_idx, binding->resource_type, binding->resource_data_type, context_vk)) + return false; + break; + } + resource = srv->resource; + + srv_vk = wined3d_shader_resource_view_vk(srv); + view_vk = &srv_vk->view_vk; + if (resource->type == WINED3D_RTYPE_BUFFER) + { + image_info = NULL; + buffer_view = &view_vk->u.vk_buffer_view; + type = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; + } + else + { + struct wined3d_texture_vk *texture_vk = wined3d_texture_vk(texture_from_resource(resource)); + + if (view_vk->u.vk_image_info.imageView) + image_info = &view_vk->u.vk_image_info; + else + image_info = wined3d_texture_vk_get_default_image_info(texture_vk, context_vk); + buffer_view = NULL; + type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + } + + if (!wined3d_shader_descriptor_writes_vk_add_write(writes, vk_descriptor_set, + binding->binding_idx, type, NULL, image_info, buffer_view)) + return false; + wined3d_context_vk_reference_shader_resource_view(context_vk, srv_vk); + break; + + case WINED3D_SHADER_DESCRIPTOR_TYPE_UAV: + if (!(uav = state->unordered_access_view[pipeline][binding->resource_idx])) + { + FIXME("NULL unordered access views not implemented.\n"); + return false; + } + resource = uav->resource; + + uav_vk = wined3d_unordered_access_view_vk(uav); + view_vk = &uav_vk->view_vk; + if (resource->type == WINED3D_RTYPE_BUFFER) + { + image_info = NULL; + buffer_view = &view_vk->u.vk_buffer_view; + type = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER; + } + else + { + struct wined3d_texture_vk *texture_vk = wined3d_texture_vk(texture_from_resource(resource)); + + if (view_vk->u.vk_image_info.imageView) + image_info = &view_vk->u.vk_image_info; + else + image_info = wined3d_texture_vk_get_default_image_info(texture_vk, context_vk); + buffer_view = NULL; + type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + } + + if (!wined3d_shader_descriptor_writes_vk_add_write(writes, vk_descriptor_set, + binding->binding_idx, type, NULL, image_info, buffer_view)) + return false; + wined3d_context_vk_reference_unordered_access_view(context_vk, uav_vk); + break; + + case WINED3D_SHADER_DESCRIPTOR_TYPE_UAV_COUNTER: + if (!(uav = state->unordered_access_view[pipeline][binding->resource_idx])) + { + FIXME("NULL unordered access view counters not implemented.\n"); + return false; + } + + uav_vk = wined3d_unordered_access_view_vk(uav); + if (!uav_vk->vk_counter_view || !wined3d_shader_descriptor_writes_vk_add_write(writes, + vk_descriptor_set, binding->binding_idx, VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, + NULL, NULL, &uav_vk->vk_counter_view)) + return false; + break; + + case WINED3D_SHADER_DESCRIPTOR_TYPE_SAMPLER: + if (!(sampler = state->sampler[binding->shader_type][binding->resource_idx])) + sampler = context_vk->c.device->null_sampler; + if (!wined3d_shader_descriptor_writes_vk_add_write(writes, vk_descriptor_set, binding->binding_idx, + VK_DESCRIPTOR_TYPE_SAMPLER, NULL, &wined3d_sampler_vk(sampler)->vk_image_info, NULL)) + return false; + wined3d_context_vk_reference_sampler(context_vk, wined3d_sampler_vk(sampler)); + break; + + default: + ERR("Invalid descriptor type %#x.\n", binding->shader_descriptor_type); + return false; + } + } + + VK_CALL(vkUpdateDescriptorSets(device_vk->vk_device, writes->count, writes->writes, 0, NULL)); + VK_CALL(vkCmdBindDescriptorSets(vk_command_buffer, vk_bind_point, + vk_pipeline_layout, 0, 1, &vk_descriptor_set, 0, NULL)); + + return true; +} + +static VkResult wined3d_context_vk_create_descriptor_set_layout(struct wined3d_device_vk *device_vk, + const struct wined3d_vk_info *vk_info, const struct wined3d_pipeline_layout_key_vk *key, + VkDescriptorSetLayout *vk_set_layout) +{ + VkDescriptorSetLayoutCreateInfo layout_desc; + VkResult vr; + + layout_desc.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + layout_desc.pNext = NULL; + layout_desc.flags = 0; + layout_desc.bindingCount = key->binding_count; + layout_desc.pBindings = key->bindings; + + if ((vr = VK_CALL(vkCreateDescriptorSetLayout(device_vk->vk_device, &layout_desc, NULL, vk_set_layout))) < 0) + WARN("Failed to create Vulkan descriptor set layout, vr %s.\n", wined3d_debug_vkresult(vr)); + + return vr; +} + +struct wined3d_pipeline_layout_vk *wined3d_context_vk_get_pipeline_layout( + struct wined3d_context_vk *context_vk, VkDescriptorSetLayoutBinding *bindings, SIZE_T binding_count) +{ + struct wined3d_device_vk *device_vk = wined3d_device_vk(context_vk->c.device); + const struct wined3d_vk_info *vk_info = context_vk->vk_info; + struct wined3d_pipeline_layout_key_vk key; + struct wined3d_pipeline_layout_vk *layout; + VkPipelineLayoutCreateInfo layout_desc; + struct wine_rb_entry *entry; + VkResult vr; + + key.bindings = bindings; + key.binding_count = binding_count; + if ((entry = wine_rb_get(&context_vk->pipeline_layouts, &key))) + return WINE_RB_ENTRY_VALUE(entry, struct wined3d_pipeline_layout_vk, entry); + + if (!(layout = heap_alloc(sizeof(*layout)))) + return NULL; + + if (!(layout->key.bindings = heap_alloc(sizeof(*layout->key.bindings) * key.binding_count))) + { + heap_free(layout); + return NULL; + } + memcpy(layout->key.bindings, key.bindings, sizeof(*layout->key.bindings) * key.binding_count); + layout->key.binding_count = key.binding_count; + + if ((vr = wined3d_context_vk_create_descriptor_set_layout(device_vk, vk_info, &key, &layout->vk_set_layout))) + { + WARN("Failed to create descriptor set layout, vr %s.\n", wined3d_debug_vkresult(vr)); + goto fail; + } + + layout_desc.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + layout_desc.pNext = NULL; + layout_desc.flags = 0; + layout_desc.setLayoutCount = 1; + layout_desc.pSetLayouts = &layout->vk_set_layout; + layout_desc.pushConstantRangeCount = 0; + layout_desc.pPushConstantRanges = NULL; + + if ((vr = VK_CALL(vkCreatePipelineLayout(device_vk->vk_device, + &layout_desc, NULL, &layout->vk_pipeline_layout))) < 0) + { + WARN("Failed to create Vulkan pipeline layout, vr %s.\n", wined3d_debug_vkresult(vr)); + VK_CALL(vkDestroyDescriptorSetLayout(device_vk->vk_device, layout->vk_set_layout, NULL)); + goto fail; + } + + if (wine_rb_put(&context_vk->pipeline_layouts, &layout->key, &layout->entry) == -1) + { + ERR("Failed to insert pipeline layout.\n"); + VK_CALL(vkDestroyPipelineLayout(device_vk->vk_device, layout->vk_pipeline_layout, NULL)); + VK_CALL(vkDestroyDescriptorSetLayout(device_vk->vk_device, layout->vk_set_layout, NULL)); + goto fail; + } + + return layout; + +fail: + heap_free(layout->key.bindings); + heap_free(layout); + return NULL; +} + +static VkPipeline wined3d_context_vk_get_graphics_pipeline(struct wined3d_context_vk *context_vk) +{ + struct wined3d_device_vk *device_vk = wined3d_device_vk(context_vk->c.device); + const struct wined3d_vk_info *vk_info = context_vk->vk_info; + struct wined3d_graphics_pipeline_vk *pipeline_vk; + struct wined3d_graphics_pipeline_key_vk *key; + struct wine_rb_entry *entry; + VkResult vr; + + key = &context_vk->graphics.pipeline_key_vk; + if ((entry = wine_rb_get(&context_vk->graphics_pipelines, key))) + return WINE_RB_ENTRY_VALUE(entry, struct wined3d_graphics_pipeline_vk, entry)->vk_pipeline; + + if (!(pipeline_vk = heap_alloc(sizeof(*pipeline_vk)))) + return VK_NULL_HANDLE; + pipeline_vk->key = *key; + + if ((vr = VK_CALL(vkCreateGraphicsPipelines(device_vk->vk_device, + VK_NULL_HANDLE, 1, &key->pipeline_desc, NULL, &pipeline_vk->vk_pipeline))) < 0) + { + WARN("Failed to create graphics pipeline, vr %s.\n", wined3d_debug_vkresult(vr)); + heap_free(pipeline_vk); + return VK_NULL_HANDLE; + } + + if (wine_rb_put(&context_vk->graphics_pipelines, &pipeline_vk->key, &pipeline_vk->entry) == -1) + ERR("Failed to insert pipeline.\n"); + + return pipeline_vk->vk_pipeline; +} + +static void wined3d_context_vk_load_shader_resources(struct wined3d_context_vk *context_vk, + const struct wined3d_state *state, enum wined3d_pipeline pipeline) +{ + struct wined3d_shader_descriptor_writes_vk *writes = &context_vk->descriptor_writes; + const struct wined3d_shader_resource_bindings *bindings; + const struct wined3d_shader_resource_binding *binding; + struct wined3d_unordered_access_view_vk *uav_vk; + struct wined3d_shader_resource_view_vk *srv_vk; + struct wined3d_unordered_access_view *uav; + struct wined3d_shader_resource_view *srv; + struct wined3d_buffer_vk *buffer_vk; + struct wined3d_sampler *sampler; + struct wined3d_buffer *buffer; + size_t i; + + switch (pipeline) + { + case WINED3D_PIPELINE_GRAPHICS: + bindings = &context_vk->graphics.bindings; + break; + + case WINED3D_PIPELINE_COMPUTE: + bindings = &context_vk->compute.bindings; + break; + + default: + ERR("Invalid pipeline %#x.\n", pipeline); + return; + } + + writes->count = 0; + for (i = 0; i < bindings->count; ++i) + { + binding = &bindings->bindings[i]; + + switch (binding->shader_descriptor_type) + { + case WINED3D_SHADER_DESCRIPTOR_TYPE_CBV: + if (!(buffer = state->cb[binding->shader_type][binding->resource_idx])) + break; + + buffer_vk = wined3d_buffer_vk(buffer); + wined3d_buffer_load(buffer, &context_vk->c, state); + if (!buffer_vk->bo_user.valid) + { + if (pipeline == WINED3D_PIPELINE_GRAPHICS) + context_invalidate_state(&context_vk->c, STATE_GRAPHICS_CONSTANT_BUFFER(binding->shader_type)); + else + context_invalidate_compute_state(&context_vk->c, STATE_COMPUTE_CONSTANT_BUFFER); + } + wined3d_buffer_vk_barrier(buffer_vk, context_vk, WINED3D_BIND_CONSTANT_BUFFER); + break; + + case WINED3D_SHADER_DESCRIPTOR_TYPE_SRV: + if (!(srv = state->shader_resource_view[binding->shader_type][binding->resource_idx])) + break; + + srv_vk = wined3d_shader_resource_view_vk(srv); + if (srv->resource->type == WINED3D_RTYPE_BUFFER) + { + if (!srv_vk->view_vk.bo_user.valid) + { + wined3d_shader_resource_view_vk_update(srv_vk, context_vk); + if (pipeline == WINED3D_PIPELINE_GRAPHICS) + context_invalidate_state(&context_vk->c, STATE_GRAPHICS_SHADER_RESOURCE_BINDING); + else + context_invalidate_compute_state(&context_vk->c, STATE_COMPUTE_SHADER_RESOURCE_BINDING); + } + wined3d_buffer_load(buffer_from_resource(srv->resource), &context_vk->c, state); + } + else + { + wined3d_texture_load(texture_from_resource(srv->resource), &context_vk->c, FALSE); + } + wined3d_shader_resource_view_vk_barrier(srv_vk, context_vk, WINED3D_BIND_SHADER_RESOURCE); + break; + + case WINED3D_SHADER_DESCRIPTOR_TYPE_UAV: + if (!(uav = state->unordered_access_view[pipeline][binding->resource_idx])) + break; + + uav_vk = wined3d_unordered_access_view_vk(uav); + if (uav->resource->type == WINED3D_RTYPE_BUFFER) + { + if (!uav_vk->view_vk.bo_user.valid) + { + wined3d_unordered_access_view_vk_update(uav_vk, context_vk); + if (pipeline == WINED3D_PIPELINE_GRAPHICS) + context_invalidate_state(&context_vk->c, STATE_GRAPHICS_UNORDERED_ACCESS_VIEW_BINDING); + else + context_invalidate_compute_state(&context_vk->c, + STATE_COMPUTE_UNORDERED_ACCESS_VIEW_BINDING); + } + wined3d_buffer_load(buffer_from_resource(uav->resource), &context_vk->c, state); + wined3d_unordered_access_view_invalidate_location(uav, ~WINED3D_LOCATION_BUFFER); + } + else + { + wined3d_texture_load(texture_from_resource(uav->resource), &context_vk->c, FALSE); + wined3d_unordered_access_view_invalidate_location(uav, ~WINED3D_LOCATION_TEXTURE_RGB); + } + wined3d_unordered_access_view_vk_barrier(uav_vk, context_vk, WINED3D_BIND_UNORDERED_ACCESS); + break; + + case WINED3D_SHADER_DESCRIPTOR_TYPE_UAV_COUNTER: + break; + + case WINED3D_SHADER_DESCRIPTOR_TYPE_SAMPLER: + if (!(sampler = state->sampler[binding->shader_type][binding->resource_idx])) + sampler = context_vk->c.device->null_sampler; + break; + + default: + ERR("Invalid descriptor type %#x.\n", binding->shader_descriptor_type); + break; + } + } +} + +VkCommandBuffer wined3d_context_vk_apply_draw_state(struct wined3d_context_vk *context_vk, + const struct wined3d_state *state, struct wined3d_buffer_vk *indirect_vk, bool indexed) +{ + struct wined3d_device_vk *device_vk = wined3d_device_vk(context_vk->c.device); + const struct wined3d_vk_info *vk_info = context_vk->vk_info; + struct wined3d_rendertarget_view *dsv; + struct wined3d_buffer_vk *buffer_vk; + VkSampleCountFlagBits sample_count; + VkCommandBuffer vk_command_buffer; + struct wined3d_buffer *buffer; + unsigned int i; + + if (wined3d_context_is_graphics_state_dirty(&context_vk->c, STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL)) + || wined3d_context_is_graphics_state_dirty(&context_vk->c, STATE_FRAMEBUFFER)) + context_vk->c.shader_update_mask |= (1u << WINED3D_SHADER_TYPE_PIXEL); + if (wined3d_context_is_graphics_state_dirty(&context_vk->c, STATE_SHADER(WINED3D_SHADER_TYPE_VERTEX))) + context_vk->c.shader_update_mask |= (1u << WINED3D_SHADER_TYPE_VERTEX); + if (wined3d_context_is_graphics_state_dirty(&context_vk->c, STATE_SHADER(WINED3D_SHADER_TYPE_GEOMETRY))) + context_vk->c.shader_update_mask |= (1u << WINED3D_SHADER_TYPE_GEOMETRY); + if (wined3d_context_is_graphics_state_dirty(&context_vk->c, STATE_SHADER(WINED3D_SHADER_TYPE_HULL))) + context_vk->c.shader_update_mask |= (1u << WINED3D_SHADER_TYPE_HULL) | (1u << WINED3D_SHADER_TYPE_DOMAIN); + if (wined3d_context_is_graphics_state_dirty(&context_vk->c, STATE_SHADER(WINED3D_SHADER_TYPE_DOMAIN))) + context_vk->c.shader_update_mask |= (1u << WINED3D_SHADER_TYPE_DOMAIN); + + context_vk->sample_count = 0; + for (i = 0; i < ARRAY_SIZE(state->fb.render_targets); ++i) + { + struct wined3d_rendertarget_view *rtv; + + if (!(rtv = state->fb.render_targets[i]) || rtv->format->id == WINED3DFMT_NULL) + continue; + + if (wined3d_blend_state_get_writemask(state->blend_state, i)) + { + wined3d_rendertarget_view_load_location(rtv, &context_vk->c, rtv->resource->draw_binding); + wined3d_rendertarget_view_invalidate_location(rtv, ~rtv->resource->draw_binding); + } + else + { + wined3d_rendertarget_view_prepare_location(rtv, &context_vk->c, rtv->resource->draw_binding); + } + + sample_count = max(1, wined3d_resource_get_sample_count(rtv->resource)); + if (!context_vk->sample_count) + context_vk->sample_count = sample_count; + else if (context_vk->sample_count != sample_count) + FIXME("Inconsistent sample counts (%u != %u).\n", context_vk->sample_count, sample_count); + } + + if ((dsv = state->fb.depth_stencil)) + { + if (wined3d_state_uses_depth_buffer(state)) + wined3d_rendertarget_view_load_location(dsv, &context_vk->c, dsv->resource->draw_binding); + else + wined3d_rendertarget_view_prepare_location(dsv, &context_vk->c, dsv->resource->draw_binding); + if (!state->depth_stencil_state || state->depth_stencil_state->desc.depth_write) + wined3d_rendertarget_view_invalidate_location(dsv, ~dsv->resource->draw_binding); + + sample_count = max(1, wined3d_resource_get_sample_count(dsv->resource)); + if (!context_vk->sample_count) + context_vk->sample_count = sample_count; + else if (context_vk->sample_count != sample_count) + FIXME("Inconsistent sample counts (%u != %u).\n", context_vk->sample_count, sample_count); + } + + if (!context_vk->sample_count) + context_vk->sample_count = VK_SAMPLE_COUNT_1_BIT; + if (context_vk->c.shader_update_mask & ~(1u << WINED3D_SHADER_TYPE_COMPUTE)) + { + device_vk->d.shader_backend->shader_select(device_vk->d.shader_priv, &context_vk->c, state); + if (!context_vk->graphics.vk_pipeline_layout) + { + ERR("No pipeline layout set.\n"); + return VK_NULL_HANDLE; + } + context_vk->c.update_shader_resource_bindings = 1; + context_vk->c.update_unordered_access_view_bindings = 1; + } + + wined3d_context_vk_load_shader_resources(context_vk, state, WINED3D_PIPELINE_GRAPHICS); + + for (i = 0; i < ARRAY_SIZE(state->streams); ++i) + { + if (!(buffer = state->streams[i].buffer)) + continue; + + buffer_vk = wined3d_buffer_vk(buffer); + wined3d_buffer_load(&buffer_vk->b, &context_vk->c, state); + wined3d_buffer_vk_barrier(buffer_vk, context_vk, WINED3D_BIND_VERTEX_BUFFER); + if (!buffer_vk->bo_user.valid) + context_invalidate_state(&context_vk->c, STATE_STREAMSRC); + } + + if (use_transform_feedback(state) && vk_info->supported[WINED3D_VK_EXT_TRANSFORM_FEEDBACK]) + { + for (i = 0; i < ARRAY_SIZE(state->stream_output); ++i) + { + if (!(buffer = state->stream_output[i].buffer)) + continue; + + buffer_vk = wined3d_buffer_vk(buffer); + wined3d_buffer_load(&buffer_vk->b, &context_vk->c, state); + wined3d_buffer_vk_barrier(buffer_vk, context_vk, WINED3D_BIND_STREAM_OUTPUT); + wined3d_buffer_invalidate_location(&buffer_vk->b, ~WINED3D_LOCATION_BUFFER); + if (!buffer_vk->bo_user.valid) + context_vk->update_stream_output = 1; + } + context_vk->c.transform_feedback_active = 1; + } + + if (indexed || (wined3d_context_is_graphics_state_dirty(&context_vk->c, STATE_INDEXBUFFER) && state->index_buffer)) + { + buffer_vk = wined3d_buffer_vk(state->index_buffer); + wined3d_buffer_load(&buffer_vk->b, &context_vk->c, state); + wined3d_buffer_vk_barrier(buffer_vk, context_vk, WINED3D_BIND_INDEX_BUFFER); + if (!buffer_vk->bo_user.valid) + context_invalidate_state(&context_vk->c, STATE_INDEXBUFFER); + } + + if (indirect_vk) + { + wined3d_buffer_load(&indirect_vk->b, &context_vk->c, state); + wined3d_buffer_vk_barrier(indirect_vk, context_vk, WINED3D_BIND_INDIRECT_BUFFER); + } + + if (!(vk_command_buffer = wined3d_context_vk_get_command_buffer(context_vk))) + { + ERR("Failed to get command buffer.\n"); + return VK_NULL_HANDLE; + } + + if (wined3d_context_is_graphics_state_dirty(&context_vk->c, STATE_FRAMEBUFFER)) + wined3d_context_vk_end_current_render_pass(context_vk); + if (!wined3d_context_vk_begin_render_pass(context_vk, vk_command_buffer, state, vk_info)) + { + ERR("Failed to begin render pass.\n"); + return VK_NULL_HANDLE; + } + + if (wined3d_context_vk_update_graphics_pipeline_key(context_vk, state, context_vk->graphics.vk_pipeline_layout) + || !context_vk->graphics.vk_pipeline) + { + if (!(context_vk->graphics.vk_pipeline = wined3d_context_vk_get_graphics_pipeline(context_vk))) + { + ERR("Failed to get graphics pipeline.\n"); + return VK_NULL_HANDLE; + } + + VK_CALL(vkCmdBindPipeline(vk_command_buffer, + VK_PIPELINE_BIND_POINT_GRAPHICS, context_vk->graphics.vk_pipeline)); + } + + if (wined3d_context_is_graphics_state_dirty(&context_vk->c, STATE_STENCIL_REF) && dsv) + { + VK_CALL(vkCmdSetStencilReference(vk_command_buffer, VK_STENCIL_FACE_FRONT_AND_BACK, + state->stencil_ref & ((1 << dsv->format->stencil_size) - 1))); + } + + if (wined3d_context_is_graphics_state_dirty(&context_vk->c, STATE_STREAMSRC)) + wined3d_context_vk_bind_vertex_buffers(context_vk, vk_command_buffer, state, vk_info); + + if (wined3d_context_is_graphics_state_dirty(&context_vk->c, STATE_STREAM_OUTPUT)) + { + context_vk->update_stream_output = 1; + context_vk->c.transform_feedback_paused = 0; + } + if (context_vk->c.transform_feedback_active && context_vk->update_stream_output) + { + wined3d_context_vk_bind_stream_output_buffers(context_vk, vk_command_buffer, state, vk_info); + context_vk->update_stream_output = 0; + } + + if (wined3d_context_is_graphics_state_dirty(&context_vk->c, STATE_INDEXBUFFER) && state->index_buffer) + { + const VkDescriptorBufferInfo *buffer_info; + VkIndexType idx_type; + + if (state->index_format == WINED3DFMT_R16_UINT) + idx_type = VK_INDEX_TYPE_UINT16; + else + idx_type = VK_INDEX_TYPE_UINT32; + buffer_vk = wined3d_buffer_vk(state->index_buffer); + buffer_info = wined3d_buffer_vk_get_buffer_info(buffer_vk); + wined3d_context_vk_reference_bo(context_vk, &buffer_vk->bo); + VK_CALL(vkCmdBindIndexBuffer(vk_command_buffer, buffer_info->buffer, + buffer_info->offset + state->index_offset, idx_type)); + } + + if (wined3d_context_is_graphics_state_dirty(&context_vk->c, STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_PIXEL)) + || wined3d_context_is_graphics_state_dirty(&context_vk->c, + STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_VERTEX)) + || wined3d_context_is_graphics_state_dirty(&context_vk->c, + STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_GEOMETRY)) + || wined3d_context_is_graphics_state_dirty(&context_vk->c, + STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_HULL)) + || wined3d_context_is_graphics_state_dirty(&context_vk->c, + STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_DOMAIN)) + || wined3d_context_is_graphics_state_dirty(&context_vk->c, STATE_GRAPHICS_SHADER_RESOURCE_BINDING)) + context_vk->c.update_shader_resource_bindings = 1; + if (wined3d_context_is_graphics_state_dirty(&context_vk->c, STATE_GRAPHICS_UNORDERED_ACCESS_VIEW_BINDING)) + context_vk->c.update_unordered_access_view_bindings = 1; + + if (context_vk->c.update_shader_resource_bindings || context_vk->c.update_unordered_access_view_bindings) + { + if (!wined3d_context_vk_update_descriptors(context_vk, vk_command_buffer, state, WINED3D_PIPELINE_GRAPHICS)) + { + ERR("Failed to update shader descriptors.\n"); + return VK_NULL_HANDLE; + } + + context_vk->c.update_shader_resource_bindings = 0; + context_vk->c.update_unordered_access_view_bindings = 0; + } + + if (wined3d_context_is_graphics_state_dirty(&context_vk->c, STATE_BLEND_FACTOR)) + VK_CALL(vkCmdSetBlendConstants(vk_command_buffer, &state->blend_factor.r)); + + memset(context_vk->c.dirty_graphics_states, 0, sizeof(context_vk->c.dirty_graphics_states)); + context_vk->c.shader_update_mask &= 1u << WINED3D_SHADER_TYPE_COMPUTE; + + return vk_command_buffer; +} + +VkCommandBuffer wined3d_context_vk_apply_compute_state(struct wined3d_context_vk *context_vk, + const struct wined3d_state *state, struct wined3d_buffer_vk *indirect_vk) +{ + struct wined3d_device_vk *device_vk = wined3d_device_vk(context_vk->c.device); + const struct wined3d_vk_info *vk_info = context_vk->vk_info; + VkCommandBuffer vk_command_buffer; + + wined3d_context_vk_end_current_render_pass(context_vk); + + if (wined3d_context_is_compute_state_dirty(&context_vk->c, STATE_COMPUTE_SHADER)) + context_vk->c.shader_update_mask |= 1u << WINED3D_SHADER_TYPE_COMPUTE; + + if (context_vk->c.shader_update_mask & (1u << WINED3D_SHADER_TYPE_COMPUTE)) + { + device_vk->d.shader_backend->shader_select_compute(device_vk->d.shader_priv, &context_vk->c, state); + if (!context_vk->compute.vk_pipeline) + { + ERR("No compute pipeline set.\n"); + return VK_NULL_HANDLE; + } + context_vk->c.update_compute_shader_resource_bindings = 1; + context_vk->c.update_compute_unordered_access_view_bindings = 1; + context_vk->update_compute_pipeline = 1; + } + + wined3d_context_vk_load_shader_resources(context_vk, state, WINED3D_PIPELINE_COMPUTE); + + if (indirect_vk) + { + wined3d_buffer_load_location(&indirect_vk->b, &context_vk->c, WINED3D_LOCATION_BUFFER); + wined3d_buffer_vk_barrier(indirect_vk, context_vk, WINED3D_BIND_INDIRECT_BUFFER); + } + + if (!(vk_command_buffer = wined3d_context_vk_get_command_buffer(context_vk))) + { + ERR("Failed to get command buffer.\n"); + return VK_NULL_HANDLE; + } + + if (context_vk->update_compute_pipeline) + { + VK_CALL(vkCmdBindPipeline(vk_command_buffer, + VK_PIPELINE_BIND_POINT_COMPUTE, context_vk->compute.vk_pipeline)); + context_vk->update_compute_pipeline = 0; + } + + if (wined3d_context_is_compute_state_dirty(&context_vk->c, STATE_COMPUTE_CONSTANT_BUFFER) + || wined3d_context_is_compute_state_dirty(&context_vk->c, STATE_COMPUTE_SHADER_RESOURCE_BINDING)) + context_vk->c.update_compute_shader_resource_bindings = 1; + if (wined3d_context_is_compute_state_dirty(&context_vk->c, STATE_COMPUTE_UNORDERED_ACCESS_VIEW_BINDING)) + context_vk->c.update_compute_unordered_access_view_bindings = 1; + + if (context_vk->c.update_compute_shader_resource_bindings + || context_vk->c.update_compute_unordered_access_view_bindings) + { + if (!wined3d_context_vk_update_descriptors(context_vk, vk_command_buffer, state, WINED3D_PIPELINE_COMPUTE)) + { + ERR("Failed to update shader descriptors.\n"); + return VK_NULL_HANDLE; + } + + context_vk->c.update_compute_shader_resource_bindings = 0; + context_vk->c.update_compute_unordered_access_view_bindings = 0; + } + + memset(context_vk->c.dirty_compute_states, 0, sizeof(context_vk->c.dirty_compute_states)); + context_vk->c.shader_update_mask &= ~(1u << WINED3D_SHADER_TYPE_COMPUTE); + + return vk_command_buffer; +} + +HRESULT wined3d_context_vk_init(struct wined3d_context_vk *context_vk, struct wined3d_swapchain *swapchain) +{ + VkCommandPoolCreateInfo command_pool_info; + const struct wined3d_vk_info *vk_info; + struct wined3d_adapter_vk *adapter_vk; + struct wined3d_device_vk *device_vk; + VkResult vr; + + TRACE("context_vk %p, swapchain %p.\n", context_vk, swapchain); + + memset(context_vk, 0, sizeof(*context_vk)); + wined3d_context_init(&context_vk->c, swapchain); + device_vk = wined3d_device_vk(swapchain->device); + adapter_vk = wined3d_adapter_vk(device_vk->d.adapter); + context_vk->vk_info = vk_info = &adapter_vk->vk_info; + + command_pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + command_pool_info.pNext = NULL; + command_pool_info.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT; + command_pool_info.queueFamilyIndex = device_vk->vk_queue_family_index; + if ((vr = VK_CALL(vkCreateCommandPool(device_vk->vk_device, + &command_pool_info, NULL, &context_vk->vk_command_pool))) < 0) + { + ERR("Failed to create Vulkan command pool, vr %s.\n", wined3d_debug_vkresult(vr)); + wined3d_context_cleanup(&context_vk->c); + return E_FAIL; + } + context_vk->current_command_buffer.id = 1; + + wined3d_context_vk_init_graphics_pipeline_key(context_vk); + + list_init(&context_vk->active_queries); + list_init(&context_vk->free_occlusion_query_pools); + list_init(&context_vk->free_timestamp_query_pools); + list_init(&context_vk->free_pipeline_statistics_query_pools); + list_init(&context_vk->free_stream_output_statistics_query_pools); + + wine_rb_init(&context_vk->render_passes, wined3d_render_pass_vk_compare); + wine_rb_init(&context_vk->pipeline_layouts, wined3d_pipeline_layout_vk_compare); + wine_rb_init(&context_vk->graphics_pipelines, wined3d_graphics_pipeline_vk_compare); + wine_rb_init(&context_vk->bo_slab_available, wined3d_bo_slab_vk_compare); + + return WINED3D_OK; +} diff --git a/wrappers/directx/d3dwine_wrapper/cs.c b/wrappers/directx/d3dwine_wrapper/cs.c new file mode 100644 index 00000000000..0385b8f4693 --- /dev/null +++ b/wrappers/directx/d3dwine_wrapper/cs.c @@ -0,0 +1,3034 @@ +/* + * Copyright 2013 Henri Verbeet for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "config.h" +#include "wine/port.h" +#include "wined3d_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(d3d); +WINE_DECLARE_DEBUG_CHANNEL(d3d_sync); +WINE_DECLARE_DEBUG_CHANNEL(fps); + +#define WINED3D_INITIAL_CS_SIZE 4096 + +enum wined3d_cs_op +{ + WINED3D_CS_OP_NOP, + WINED3D_CS_OP_PRESENT, + WINED3D_CS_OP_CLEAR, + WINED3D_CS_OP_DISPATCH, + WINED3D_CS_OP_DRAW, + WINED3D_CS_OP_FLUSH, + WINED3D_CS_OP_SET_PREDICATION, + WINED3D_CS_OP_SET_VIEWPORTS, + WINED3D_CS_OP_SET_SCISSOR_RECTS, + WINED3D_CS_OP_SET_RENDERTARGET_VIEW, + WINED3D_CS_OP_SET_DEPTH_STENCIL_VIEW, + WINED3D_CS_OP_SET_VERTEX_DECLARATION, + WINED3D_CS_OP_SET_STREAM_SOURCE, + WINED3D_CS_OP_SET_STREAM_SOURCE_FREQ, + WINED3D_CS_OP_SET_STREAM_OUTPUT, + WINED3D_CS_OP_SET_INDEX_BUFFER, + WINED3D_CS_OP_SET_CONSTANT_BUFFER, + WINED3D_CS_OP_SET_TEXTURE, + WINED3D_CS_OP_SET_SHADER_RESOURCE_VIEW, + WINED3D_CS_OP_SET_UNORDERED_ACCESS_VIEW, + WINED3D_CS_OP_SET_SAMPLER, + WINED3D_CS_OP_SET_SHADER, + WINED3D_CS_OP_SET_BLEND_STATE, + WINED3D_CS_OP_SET_DEPTH_STENCIL_STATE, + WINED3D_CS_OP_SET_RASTERIZER_STATE, + WINED3D_CS_OP_SET_RENDER_STATE, + WINED3D_CS_OP_SET_TEXTURE_STATE, + WINED3D_CS_OP_SET_SAMPLER_STATE, + WINED3D_CS_OP_SET_TRANSFORM, + WINED3D_CS_OP_SET_CLIP_PLANE, + WINED3D_CS_OP_SET_COLOR_KEY, + WINED3D_CS_OP_SET_MATERIAL, + WINED3D_CS_OP_SET_LIGHT, + WINED3D_CS_OP_SET_LIGHT_ENABLE, + WINED3D_CS_OP_PUSH_CONSTANTS, + WINED3D_CS_OP_RESET_STATE, + WINED3D_CS_OP_CALLBACK, + WINED3D_CS_OP_QUERY_ISSUE, + WINED3D_CS_OP_PRELOAD_RESOURCE, + WINED3D_CS_OP_UNLOAD_RESOURCE, + WINED3D_CS_OP_MAP, + WINED3D_CS_OP_UNMAP, + WINED3D_CS_OP_BLT_SUB_RESOURCE, + WINED3D_CS_OP_UPDATE_SUB_RESOURCE, + WINED3D_CS_OP_ADD_DIRTY_TEXTURE_REGION, + WINED3D_CS_OP_CLEAR_UNORDERED_ACCESS_VIEW, + WINED3D_CS_OP_COPY_UAV_COUNTER, + WINED3D_CS_OP_GENERATE_MIPMAPS, + WINED3D_CS_OP_STOP, +}; + +struct wined3d_cs_packet +{ + size_t size; + BYTE data[1]; +}; + +struct wined3d_cs_nop +{ + enum wined3d_cs_op opcode; +}; + +struct wined3d_cs_present +{ + enum wined3d_cs_op opcode; + HWND dst_window_override; + struct wined3d_swapchain *swapchain; + RECT src_rect; + RECT dst_rect; + unsigned int swap_interval; + DWORD flags; +}; + +struct wined3d_cs_clear +{ + enum wined3d_cs_op opcode; + DWORD flags; + unsigned int rt_count; + struct wined3d_fb_state *fb; + RECT draw_rect; + struct wined3d_color color; + float depth; + DWORD stencil; + unsigned int rect_count; + RECT rects[1]; +}; + +struct wined3d_cs_dispatch +{ + enum wined3d_cs_op opcode; + struct wined3d_dispatch_parameters parameters; +}; + +struct wined3d_cs_draw +{ + enum wined3d_cs_op opcode; + enum wined3d_primitive_type primitive_type; + GLint patch_vertex_count; + struct wined3d_draw_parameters parameters; +}; + +struct wined3d_cs_flush +{ + enum wined3d_cs_op opcode; +}; + +struct wined3d_cs_set_predication +{ + enum wined3d_cs_op opcode; + struct wined3d_query *predicate; + BOOL value; +}; + +struct wined3d_cs_set_viewports +{ + enum wined3d_cs_op opcode; + unsigned int viewport_count; + struct wined3d_viewport viewports[1]; +}; + +struct wined3d_cs_set_scissor_rects +{ + enum wined3d_cs_op opcode; + unsigned int rect_count; + RECT rects[1]; +}; + +struct wined3d_cs_set_rendertarget_view +{ + enum wined3d_cs_op opcode; + unsigned int view_idx; + struct wined3d_rendertarget_view *view; +}; + +struct wined3d_cs_set_depth_stencil_view +{ + enum wined3d_cs_op opcode; + struct wined3d_rendertarget_view *view; +}; + +struct wined3d_cs_set_vertex_declaration +{ + enum wined3d_cs_op opcode; + struct wined3d_vertex_declaration *declaration; +}; + +struct wined3d_cs_set_stream_source +{ + enum wined3d_cs_op opcode; + UINT stream_idx; + struct wined3d_buffer *buffer; + UINT offset; + UINT stride; +}; + +struct wined3d_cs_set_stream_source_freq +{ + enum wined3d_cs_op opcode; + UINT stream_idx; + UINT frequency; + UINT flags; +}; + +struct wined3d_cs_set_stream_output +{ + enum wined3d_cs_op opcode; + UINT stream_idx; + struct wined3d_buffer *buffer; + UINT offset; +}; + +struct wined3d_cs_set_index_buffer +{ + enum wined3d_cs_op opcode; + struct wined3d_buffer *buffer; + enum wined3d_format_id format_id; + unsigned int offset; +}; + +struct wined3d_cs_set_constant_buffer +{ + enum wined3d_cs_op opcode; + enum wined3d_shader_type type; + UINT cb_idx; + struct wined3d_buffer *buffer; +}; + +struct wined3d_cs_set_texture +{ + enum wined3d_cs_op opcode; + UINT stage; + struct wined3d_texture *texture; +}; + +struct wined3d_cs_set_color_key +{ + enum wined3d_cs_op opcode; + struct wined3d_texture *texture; + WORD flags; + WORD set; + struct wined3d_color_key color_key; +}; + +struct wined3d_cs_set_shader_resource_view +{ + enum wined3d_cs_op opcode; + enum wined3d_shader_type type; + UINT view_idx; + struct wined3d_shader_resource_view *view; +}; + +struct wined3d_cs_set_unordered_access_view +{ + enum wined3d_cs_op opcode; + enum wined3d_pipeline pipeline; + unsigned int view_idx; + struct wined3d_unordered_access_view *view; + unsigned int initial_count; +}; + +struct wined3d_cs_set_sampler +{ + enum wined3d_cs_op opcode; + enum wined3d_shader_type type; + UINT sampler_idx; + struct wined3d_sampler *sampler; +}; + +struct wined3d_cs_set_shader +{ + enum wined3d_cs_op opcode; + enum wined3d_shader_type type; + struct wined3d_shader *shader; +}; + +struct wined3d_cs_set_blend_state +{ + enum wined3d_cs_op opcode; + struct wined3d_blend_state *state; + struct wined3d_color factor; + unsigned int sample_mask; +}; + +struct wined3d_cs_set_depth_stencil_state +{ + enum wined3d_cs_op opcode; + struct wined3d_depth_stencil_state *state; + unsigned int stencil_ref; +}; + +struct wined3d_cs_set_rasterizer_state +{ + enum wined3d_cs_op opcode; + struct wined3d_rasterizer_state *state; +}; + +struct wined3d_cs_set_render_state +{ + enum wined3d_cs_op opcode; + enum wined3d_render_state state; + DWORD value; +}; + +struct wined3d_cs_set_texture_state +{ + enum wined3d_cs_op opcode; + UINT stage; + enum wined3d_texture_stage_state state; + DWORD value; +}; + +struct wined3d_cs_set_sampler_state +{ + enum wined3d_cs_op opcode; + UINT sampler_idx; + enum wined3d_sampler_state state; + DWORD value; +}; + +struct wined3d_cs_set_transform +{ + enum wined3d_cs_op opcode; + enum wined3d_transform_state state; + struct wined3d_matrix matrix; +}; + +struct wined3d_cs_set_clip_plane +{ + enum wined3d_cs_op opcode; + UINT plane_idx; + struct wined3d_vec4 plane; +}; + +struct wined3d_cs_set_material +{ + enum wined3d_cs_op opcode; + struct wined3d_material material; +}; + +struct wined3d_cs_set_light +{ + enum wined3d_cs_op opcode; + struct wined3d_light_info light; +}; + +struct wined3d_cs_set_light_enable +{ + enum wined3d_cs_op opcode; + unsigned int idx; + BOOL enable; +}; + +struct wined3d_cs_push_constants +{ + enum wined3d_cs_op opcode; + enum wined3d_push_constants type; + unsigned int start_idx; + unsigned int count; + BYTE constants[1]; +}; + +struct wined3d_cs_reset_state +{ + enum wined3d_cs_op opcode; +}; + +struct wined3d_cs_callback +{ + enum wined3d_cs_op opcode; + void (*callback)(void *object); + void *object; +}; + +struct wined3d_cs_query_issue +{ + enum wined3d_cs_op opcode; + struct wined3d_query *query; + DWORD flags; +}; + +struct wined3d_cs_preload_resource +{ + enum wined3d_cs_op opcode; + struct wined3d_resource *resource; +}; + +struct wined3d_cs_unload_resource +{ + enum wined3d_cs_op opcode; + struct wined3d_resource *resource; +}; + +struct wined3d_cs_map +{ + enum wined3d_cs_op opcode; + struct wined3d_resource *resource; + unsigned int sub_resource_idx; + struct wined3d_map_desc *map_desc; + const struct wined3d_box *box; + DWORD flags; + HRESULT *hr; +}; + +struct wined3d_cs_unmap +{ + enum wined3d_cs_op opcode; + struct wined3d_resource *resource; + unsigned int sub_resource_idx; + HRESULT *hr; +}; + +struct wined3d_cs_blt_sub_resource +{ + enum wined3d_cs_op opcode; + struct wined3d_resource *dst_resource; + unsigned int dst_sub_resource_idx; + struct wined3d_box dst_box; + struct wined3d_resource *src_resource; + unsigned int src_sub_resource_idx; + struct wined3d_box src_box; + DWORD flags; + struct wined3d_blt_fx fx; + enum wined3d_texture_filter_type filter; +}; + +struct wined3d_cs_update_sub_resource +{ + enum wined3d_cs_op opcode; + struct wined3d_resource *resource; + unsigned int sub_resource_idx; + struct wined3d_box box; + struct wined3d_sub_resource_data data; +}; + +struct wined3d_cs_add_dirty_texture_region +{ + enum wined3d_cs_op opcode; + struct wined3d_texture *texture; + unsigned int layer; +}; + +struct wined3d_cs_clear_unordered_access_view +{ + enum wined3d_cs_op opcode; + struct wined3d_unordered_access_view *view; + struct wined3d_uvec4 clear_value; +}; + +struct wined3d_cs_copy_uav_counter +{ + enum wined3d_cs_op opcode; + struct wined3d_buffer *buffer; + unsigned int offset; + struct wined3d_unordered_access_view *view; +}; + +struct wined3d_cs_generate_mipmaps +{ + enum wined3d_cs_op opcode; + struct wined3d_shader_resource_view *view; +}; + +struct wined3d_cs_stop +{ + enum wined3d_cs_op opcode; +}; + +static inline void *wined3d_cs_require_space(struct wined3d_cs *cs, + size_t size, enum wined3d_cs_queue_id queue_id) +{ + return cs->ops->require_space(cs, size, queue_id); +} + +static inline void wined3d_cs_submit(struct wined3d_cs *cs, enum wined3d_cs_queue_id queue_id) +{ + cs->ops->submit(cs, queue_id); +} + +static const char *debug_cs_op(enum wined3d_cs_op op) +{ + switch (op) + { +#define WINED3D_TO_STR(type) case type: return #type + WINED3D_TO_STR(WINED3D_CS_OP_NOP); + WINED3D_TO_STR(WINED3D_CS_OP_PRESENT); + WINED3D_TO_STR(WINED3D_CS_OP_CLEAR); + WINED3D_TO_STR(WINED3D_CS_OP_DISPATCH); + WINED3D_TO_STR(WINED3D_CS_OP_DRAW); + WINED3D_TO_STR(WINED3D_CS_OP_FLUSH); + WINED3D_TO_STR(WINED3D_CS_OP_SET_PREDICATION); + WINED3D_TO_STR(WINED3D_CS_OP_SET_VIEWPORTS); + WINED3D_TO_STR(WINED3D_CS_OP_SET_SCISSOR_RECTS); + WINED3D_TO_STR(WINED3D_CS_OP_SET_RENDERTARGET_VIEW); + WINED3D_TO_STR(WINED3D_CS_OP_SET_DEPTH_STENCIL_VIEW); + WINED3D_TO_STR(WINED3D_CS_OP_SET_VERTEX_DECLARATION); + WINED3D_TO_STR(WINED3D_CS_OP_SET_STREAM_SOURCE); + WINED3D_TO_STR(WINED3D_CS_OP_SET_STREAM_SOURCE_FREQ); + WINED3D_TO_STR(WINED3D_CS_OP_SET_STREAM_OUTPUT); + WINED3D_TO_STR(WINED3D_CS_OP_SET_INDEX_BUFFER); + WINED3D_TO_STR(WINED3D_CS_OP_SET_CONSTANT_BUFFER); + WINED3D_TO_STR(WINED3D_CS_OP_SET_TEXTURE); + WINED3D_TO_STR(WINED3D_CS_OP_SET_SHADER_RESOURCE_VIEW); + WINED3D_TO_STR(WINED3D_CS_OP_SET_UNORDERED_ACCESS_VIEW); + WINED3D_TO_STR(WINED3D_CS_OP_SET_SAMPLER); + WINED3D_TO_STR(WINED3D_CS_OP_SET_SHADER); + WINED3D_TO_STR(WINED3D_CS_OP_SET_BLEND_STATE); + WINED3D_TO_STR(WINED3D_CS_OP_SET_DEPTH_STENCIL_STATE); + WINED3D_TO_STR(WINED3D_CS_OP_SET_RASTERIZER_STATE); + WINED3D_TO_STR(WINED3D_CS_OP_SET_RENDER_STATE); + WINED3D_TO_STR(WINED3D_CS_OP_SET_TEXTURE_STATE); + WINED3D_TO_STR(WINED3D_CS_OP_SET_SAMPLER_STATE); + WINED3D_TO_STR(WINED3D_CS_OP_SET_TRANSFORM); + WINED3D_TO_STR(WINED3D_CS_OP_SET_CLIP_PLANE); + WINED3D_TO_STR(WINED3D_CS_OP_SET_COLOR_KEY); + WINED3D_TO_STR(WINED3D_CS_OP_SET_MATERIAL); + WINED3D_TO_STR(WINED3D_CS_OP_SET_LIGHT); + WINED3D_TO_STR(WINED3D_CS_OP_SET_LIGHT_ENABLE); + WINED3D_TO_STR(WINED3D_CS_OP_PUSH_CONSTANTS); + WINED3D_TO_STR(WINED3D_CS_OP_RESET_STATE); + WINED3D_TO_STR(WINED3D_CS_OP_CALLBACK); + WINED3D_TO_STR(WINED3D_CS_OP_QUERY_ISSUE); + WINED3D_TO_STR(WINED3D_CS_OP_PRELOAD_RESOURCE); + WINED3D_TO_STR(WINED3D_CS_OP_UNLOAD_RESOURCE); + WINED3D_TO_STR(WINED3D_CS_OP_MAP); + WINED3D_TO_STR(WINED3D_CS_OP_UNMAP); + WINED3D_TO_STR(WINED3D_CS_OP_BLT_SUB_RESOURCE); + WINED3D_TO_STR(WINED3D_CS_OP_UPDATE_SUB_RESOURCE); + WINED3D_TO_STR(WINED3D_CS_OP_ADD_DIRTY_TEXTURE_REGION); + WINED3D_TO_STR(WINED3D_CS_OP_CLEAR_UNORDERED_ACCESS_VIEW); + WINED3D_TO_STR(WINED3D_CS_OP_COPY_UAV_COUNTER); + WINED3D_TO_STR(WINED3D_CS_OP_GENERATE_MIPMAPS); + WINED3D_TO_STR(WINED3D_CS_OP_STOP); +#undef WINED3D_TO_STR + } + return wine_dbg_sprintf("UNKNOWN_OP(%#x)", op); +} + +static void wined3d_cs_exec_nop(struct wined3d_cs *cs, const void *data) +{ +} + +static void wined3d_cs_exec_present(struct wined3d_cs *cs, const void *data) +{ + struct wined3d_texture *logo_texture, *cursor_texture, *back_buffer; + struct wined3d_rendertarget_view *dsv = cs->state.fb.depth_stencil; + const struct wined3d_cs_present *op = data; + const struct wined3d_swapchain_desc *desc; + struct wined3d_swapchain *swapchain; + unsigned int i; + + swapchain = op->swapchain; + desc = &swapchain->state.desc; + back_buffer = swapchain->back_buffers[0]; + wined3d_swapchain_set_window(swapchain, op->dst_window_override); + + if ((logo_texture = swapchain->device->logo_texture)) + { + RECT rect = {0, 0, logo_texture->resource.width, logo_texture->resource.height}; + + /* Blit the logo into the upper left corner of the back-buffer. */ + wined3d_texture_blt(back_buffer, 0, &rect, logo_texture, 0, + &rect, WINED3D_BLT_SRC_CKEY, NULL, WINED3D_TEXF_POINT); + } + + if ((cursor_texture = swapchain->device->cursor_texture) + && swapchain->device->bCursorVisible && !swapchain->device->hardwareCursor) + { + RECT dst_rect = + { + swapchain->device->xScreenSpace - swapchain->device->xHotSpot, + swapchain->device->yScreenSpace - swapchain->device->yHotSpot, + swapchain->device->xScreenSpace + swapchain->device->cursorWidth - swapchain->device->xHotSpot, + swapchain->device->yScreenSpace + swapchain->device->cursorHeight - swapchain->device->yHotSpot, + }; + RECT src_rect = + { + 0, 0, cursor_texture->resource.width, cursor_texture->resource.height + }; + const RECT clip_rect = {0, 0, back_buffer->resource.width, back_buffer->resource.height}; + + TRACE("Rendering the software cursor.\n"); + + if (desc->windowed) + MapWindowPoints(NULL, swapchain->win_handle, (POINT *)&dst_rect, 2); + if (wined3d_clip_blit(&clip_rect, &dst_rect, &src_rect)) + wined3d_texture_blt(back_buffer, 0, &dst_rect, cursor_texture, 0, + &src_rect, WINED3D_BLT_ALPHA_TEST, NULL, WINED3D_TEXF_POINT); + } + + swapchain->swapchain_ops->swapchain_present(swapchain, &op->src_rect, &op->dst_rect, op->swap_interval, op->flags); + + /* Discard buffers if the swap effect allows it. */ + back_buffer = swapchain->back_buffers[desc->backbuffer_count - 1]; + if (desc->swap_effect == WINED3D_SWAP_EFFECT_DISCARD || desc->swap_effect == WINED3D_SWAP_EFFECT_FLIP_DISCARD) + wined3d_texture_validate_location(back_buffer, 0, WINED3D_LOCATION_DISCARDED); + + if (dsv && dsv->resource->type != WINED3D_RTYPE_BUFFER) + { + struct wined3d_texture *ds = texture_from_resource(dsv->resource); + + if ((desc->flags & WINED3D_SWAPCHAIN_DISCARD_DEPTHSTENCIL || ds->flags & WINED3D_TEXTURE_DISCARD)) + wined3d_rendertarget_view_validate_location(dsv, WINED3D_LOCATION_DISCARDED); + } + + if (TRACE_ON(fps)) + { + DWORD time = GetTickCount(); + ++swapchain->frames; + + /* every 1.5 seconds */ + if (time - swapchain->prev_time > 1500) + { + TRACE_(fps)("%p @ approx %.2ffps\n", + swapchain, 1000.0 * swapchain->frames / (time - swapchain->prev_time)); + swapchain->prev_time = time; + swapchain->frames = 0; + } + } + + wined3d_resource_release(&swapchain->front_buffer->resource); + for (i = 0; i < desc->backbuffer_count; ++i) + { + wined3d_resource_release(&swapchain->back_buffers[i]->resource); + } + + InterlockedDecrement(&cs->pending_presents); +} + +void wined3d_cs_emit_present(struct wined3d_cs *cs, struct wined3d_swapchain *swapchain, + const RECT *src_rect, const RECT *dst_rect, HWND dst_window_override, + unsigned int swap_interval, DWORD flags) +{ + struct wined3d_cs_present *op; + unsigned int i; + LONG pending; + + op = wined3d_cs_require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_PRESENT; + op->dst_window_override = dst_window_override; + op->swapchain = swapchain; + op->src_rect = *src_rect; + op->dst_rect = *dst_rect; + op->swap_interval = swap_interval; + op->flags = flags; + + pending = InterlockedIncrement(&cs->pending_presents); + + wined3d_resource_acquire(&swapchain->front_buffer->resource); + for (i = 0; i < swapchain->state.desc.backbuffer_count; ++i) + { + wined3d_resource_acquire(&swapchain->back_buffers[i]->resource); + } + + wined3d_cs_submit(cs, WINED3D_CS_QUEUE_DEFAULT); + + /* Limit input latency by limiting the number of presents that we can get + * ahead of the worker thread. */ + while (pending >= swapchain->max_frame_latency) + { + YieldProcessor(); + pending = InterlockedCompareExchange(&cs->pending_presents, 0, 0); + } +} + +static void wined3d_cs_exec_clear(struct wined3d_cs *cs, const void *data) +{ + const struct wined3d_cs_clear *op = data; + struct wined3d_device *device; + unsigned int i; + + device = cs->device; + device->blitter->ops->blitter_clear(device->blitter, device, op->rt_count, op->fb, + op->rect_count, op->rects, &op->draw_rect, op->flags, &op->color, op->depth, op->stencil); + + if (op->flags & WINED3DCLEAR_TARGET) + { + for (i = 0; i < op->rt_count; ++i) + { + if (op->fb->render_targets[i]) + wined3d_resource_release(op->fb->render_targets[i]->resource); + } + } + if (op->flags & (WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL)) + wined3d_resource_release(op->fb->depth_stencil->resource); +} + +void wined3d_cs_emit_clear(struct wined3d_cs *cs, DWORD rect_count, const RECT *rects, + DWORD flags, const struct wined3d_color *color, float depth, DWORD stencil) +{ + const struct wined3d_state *state = &cs->device->state; + const struct wined3d_viewport *vp = &state->viewports[0]; + struct wined3d_rendertarget_view *view; + struct wined3d_cs_clear *op; + unsigned int rt_count, i; + + rt_count = flags & WINED3DCLEAR_TARGET ? cs->device->adapter->d3d_info.limits.max_rt_count : 0; + + op = wined3d_cs_require_space(cs, FIELD_OFFSET(struct wined3d_cs_clear, rects[rect_count]), + WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_CLEAR; + op->flags = flags & (WINED3DCLEAR_TARGET | WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL); + op->rt_count = rt_count; + op->fb = &cs->state.fb; + SetRect(&op->draw_rect, vp->x, vp->y, vp->x + vp->width, vp->y + vp->height); + if (state->rasterizer_state && state->rasterizer_state->desc.scissor) + IntersectRect(&op->draw_rect, &op->draw_rect, &state->scissor_rects[0]); + op->color = *color; + op->depth = depth; + op->stencil = stencil; + op->rect_count = rect_count; + memcpy(op->rects, rects, sizeof(*rects) * rect_count); + + for (i = 0; i < rt_count; ++i) + { + if ((view = state->fb.render_targets[i])) + wined3d_resource_acquire(view->resource); + } + if (flags & (WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL)) + { + view = state->fb.depth_stencil; + wined3d_resource_acquire(view->resource); + } + + wined3d_cs_submit(cs, WINED3D_CS_QUEUE_DEFAULT); +} + +void wined3d_cs_emit_clear_rendertarget_view(struct wined3d_cs *cs, struct wined3d_rendertarget_view *view, + const RECT *rect, DWORD flags, const struct wined3d_color *color, float depth, DWORD stencil) +{ + struct wined3d_cs_clear *op; + size_t size; + + size = FIELD_OFFSET(struct wined3d_cs_clear, rects[1]) + sizeof(struct wined3d_fb_state); + op = wined3d_cs_require_space(cs, size, WINED3D_CS_QUEUE_DEFAULT); + op->fb = (void *)&op->rects[1]; + + op->opcode = WINED3D_CS_OP_CLEAR; + op->flags = flags & (WINED3DCLEAR_TARGET | WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL); + if (flags & WINED3DCLEAR_TARGET) + { + op->rt_count = 1; + op->fb->render_targets[0] = view; + op->fb->depth_stencil = NULL; + op->color = *color; + } + else + { + op->rt_count = 0; + op->fb->render_targets[0] = NULL; + op->fb->depth_stencil = view; + op->depth = depth; + op->stencil = stencil; + } + SetRect(&op->draw_rect, 0, 0, view->width, view->height); + op->rect_count = 1; + op->rects[0] = *rect; + + wined3d_resource_acquire(view->resource); + + wined3d_cs_submit(cs, WINED3D_CS_QUEUE_DEFAULT); + if (flags & WINED3DCLEAR_SYNCHRONOUS) + wined3d_cs_finish(cs, WINED3D_CS_QUEUE_DEFAULT); +} + +static void acquire_shader_resources(const struct wined3d_state *state, unsigned int shader_mask) +{ + struct wined3d_shader_sampler_map_entry *entry; + struct wined3d_shader_resource_view *view; + struct wined3d_shader *shader; + unsigned int i, j; + + for (i = 0; i < WINED3D_SHADER_TYPE_COUNT; ++i) + { + if (!(shader_mask & (1u << i))) + continue; + + if (!(shader = state->shader[i])) + continue; + + for (j = 0; j < WINED3D_MAX_CBS; ++j) + { + if (state->cb[i][j]) + wined3d_resource_acquire(&state->cb[i][j]->resource); + } + + for (j = 0; j < shader->reg_maps.sampler_map.count; ++j) + { + entry = &shader->reg_maps.sampler_map.entries[j]; + + if (!(view = state->shader_resource_view[i][entry->resource_idx])) + continue; + + wined3d_resource_acquire(view->resource); + } + } +} + +static void release_shader_resources(const struct wined3d_state *state, unsigned int shader_mask) +{ + struct wined3d_shader_sampler_map_entry *entry; + struct wined3d_shader_resource_view *view; + struct wined3d_shader *shader; + unsigned int i, j; + + for (i = 0; i < WINED3D_SHADER_TYPE_COUNT; ++i) + { + if (!(shader_mask & (1u << i))) + continue; + + if (!(shader = state->shader[i])) + continue; + + for (j = 0; j < WINED3D_MAX_CBS; ++j) + { + if (state->cb[i][j]) + wined3d_resource_release(&state->cb[i][j]->resource); + } + + for (j = 0; j < shader->reg_maps.sampler_map.count; ++j) + { + entry = &shader->reg_maps.sampler_map.entries[j]; + + if (!(view = state->shader_resource_view[i][entry->resource_idx])) + continue; + + wined3d_resource_release(view->resource); + } + } +} + +static void acquire_unordered_access_resources(const struct wined3d_shader *shader, + struct wined3d_unordered_access_view * const *views) +{ + unsigned int i; + + if (!shader) + return; + + for (i = 0; i < MAX_UNORDERED_ACCESS_VIEWS; ++i) + { + if (!shader->reg_maps.uav_resource_info[i].type) + continue; + + if (!views[i]) + continue; + + wined3d_resource_acquire(views[i]->resource); + } +} + +static void release_unordered_access_resources(const struct wined3d_shader *shader, + struct wined3d_unordered_access_view * const *views) +{ + unsigned int i; + + if (!shader) + return; + + for (i = 0; i < MAX_UNORDERED_ACCESS_VIEWS; ++i) + { + if (!shader->reg_maps.uav_resource_info[i].type) + continue; + + if (!views[i]) + continue; + + wined3d_resource_release(views[i]->resource); + } +} + +static void wined3d_cs_exec_dispatch(struct wined3d_cs *cs, const void *data) +{ + const struct wined3d_cs_dispatch *op = data; + struct wined3d_state *state = &cs->state; + + if (!state->shader[WINED3D_SHADER_TYPE_COMPUTE]) + WARN("No compute shader bound, skipping dispatch.\n"); + else + cs->device->adapter->adapter_ops->adapter_dispatch_compute(cs->device, state, &op->parameters); + + if (op->parameters.indirect) + wined3d_resource_release(&op->parameters.u.indirect.buffer->resource); + + release_shader_resources(state, 1u << WINED3D_SHADER_TYPE_COMPUTE); + release_unordered_access_resources(state->shader[WINED3D_SHADER_TYPE_COMPUTE], + state->unordered_access_view[WINED3D_PIPELINE_COMPUTE]); +} + +static void acquire_compute_pipeline_resources(const struct wined3d_state *state) +{ + acquire_shader_resources(state, 1u << WINED3D_SHADER_TYPE_COMPUTE); + acquire_unordered_access_resources(state->shader[WINED3D_SHADER_TYPE_COMPUTE], + state->unordered_access_view[WINED3D_PIPELINE_COMPUTE]); +} + +void wined3d_cs_emit_dispatch(struct wined3d_cs *cs, + unsigned int group_count_x, unsigned int group_count_y, unsigned int group_count_z) +{ + const struct wined3d_state *state = &cs->device->state; + struct wined3d_cs_dispatch *op; + + op = wined3d_cs_require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_DISPATCH; + op->parameters.indirect = FALSE; + op->parameters.u.direct.group_count_x = group_count_x; + op->parameters.u.direct.group_count_y = group_count_y; + op->parameters.u.direct.group_count_z = group_count_z; + + acquire_compute_pipeline_resources(state); + + wined3d_cs_submit(cs, WINED3D_CS_QUEUE_DEFAULT); +} + +void wined3d_cs_emit_dispatch_indirect(struct wined3d_cs *cs, + struct wined3d_buffer *buffer, unsigned int offset) +{ + const struct wined3d_state *state = &cs->device->state; + struct wined3d_cs_dispatch *op; + + op = wined3d_cs_require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_DISPATCH; + op->parameters.indirect = TRUE; + op->parameters.u.indirect.buffer = buffer; + op->parameters.u.indirect.offset = offset; + + acquire_compute_pipeline_resources(state); + wined3d_resource_acquire(&buffer->resource); + + wined3d_cs_submit(cs, WINED3D_CS_QUEUE_DEFAULT); +} + +static void wined3d_cs_exec_draw(struct wined3d_cs *cs, const void *data) +{ + const struct wined3d_d3d_info *d3d_info = &cs->device->adapter->d3d_info; + const struct wined3d_shader *geometry_shader; + struct wined3d_device *device = cs->device; + int base_vertex_idx, load_base_vertex_idx; + struct wined3d_state *state = &cs->state; + const struct wined3d_cs_draw *op = data; + unsigned int i; + + base_vertex_idx = 0; + if (!op->parameters.indirect) + { + const struct wined3d_direct_draw_parameters *direct = &op->parameters.u.direct; + + if (op->parameters.indexed && d3d_info->draw_base_vertex_offset) + base_vertex_idx = direct->base_vertex_idx; + else if (!op->parameters.indexed) + base_vertex_idx = direct->start_idx; + } + + /* ARB_draw_indirect always supports a base vertex offset. */ + if (!op->parameters.indirect && !d3d_info->draw_base_vertex_offset) + load_base_vertex_idx = op->parameters.u.direct.base_vertex_idx; + else + load_base_vertex_idx = 0; + + if (state->base_vertex_index != base_vertex_idx) + { + state->base_vertex_index = base_vertex_idx; + for (i = 0; i < device->context_count; ++i) + device->contexts[i]->constant_update_mask |= WINED3D_SHADER_CONST_BASE_VERTEX_ID; + } + + if (state->load_base_vertex_index != load_base_vertex_idx) + { + state->load_base_vertex_index = load_base_vertex_idx; + device_invalidate_state(cs->device, STATE_BASEVERTEXINDEX); + } + + if (state->primitive_type != op->primitive_type) + { + if ((geometry_shader = state->shader[WINED3D_SHADER_TYPE_GEOMETRY]) && !geometry_shader->function) + device_invalidate_state(cs->device, STATE_SHADER(WINED3D_SHADER_TYPE_GEOMETRY)); + if (state->primitive_type == WINED3D_PT_POINTLIST || op->primitive_type == WINED3D_PT_POINTLIST) + device_invalidate_state(cs->device, STATE_POINT_ENABLE); + state->primitive_type = op->primitive_type; + } + state->patch_vertex_count = op->patch_vertex_count; + + cs->device->adapter->adapter_ops->adapter_draw_primitive(cs->device, state, &op->parameters); + + if (op->parameters.indirect) + { + struct wined3d_buffer *buffer = op->parameters.u.indirect.buffer; + wined3d_resource_release(&buffer->resource); + } + + if (op->parameters.indexed) + wined3d_resource_release(&state->index_buffer->resource); + for (i = 0; i < ARRAY_SIZE(state->streams); ++i) + { + if (state->streams[i].buffer) + wined3d_resource_release(&state->streams[i].buffer->resource); + } + for (i = 0; i < ARRAY_SIZE(state->stream_output); ++i) + { + if (state->stream_output[i].buffer) + wined3d_resource_release(&state->stream_output[i].buffer->resource); + } + for (i = 0; i < ARRAY_SIZE(state->textures); ++i) + { + if (state->textures[i]) + wined3d_resource_release(&state->textures[i]->resource); + } + for (i = 0; i < d3d_info->limits.max_rt_count; ++i) + { + if (state->fb.render_targets[i]) + wined3d_resource_release(state->fb.render_targets[i]->resource); + } + if (state->fb.depth_stencil) + wined3d_resource_release(state->fb.depth_stencil->resource); + release_shader_resources(state, ~(1u << WINED3D_SHADER_TYPE_COMPUTE)); + release_unordered_access_resources(state->shader[WINED3D_SHADER_TYPE_PIXEL], + state->unordered_access_view[WINED3D_PIPELINE_GRAPHICS]); +} + +static void acquire_graphics_pipeline_resources(const struct wined3d_state *state, + BOOL indexed, const struct wined3d_d3d_info *d3d_info) +{ + unsigned int i; + + if (indexed) + wined3d_resource_acquire(&state->index_buffer->resource); + for (i = 0; i < ARRAY_SIZE(state->streams); ++i) + { + if (state->streams[i].buffer) + wined3d_resource_acquire(&state->streams[i].buffer->resource); + } + for (i = 0; i < ARRAY_SIZE(state->stream_output); ++i) + { + if (state->stream_output[i].buffer) + wined3d_resource_acquire(&state->stream_output[i].buffer->resource); + } + for (i = 0; i < ARRAY_SIZE(state->textures); ++i) + { + if (state->textures[i]) + wined3d_resource_acquire(&state->textures[i]->resource); + } + for (i = 0; i < d3d_info->limits.max_rt_count; ++i) + { + if (state->fb.render_targets[i]) + wined3d_resource_acquire(state->fb.render_targets[i]->resource); + } + if (state->fb.depth_stencil) + wined3d_resource_acquire(state->fb.depth_stencil->resource); + acquire_shader_resources(state, ~(1u << WINED3D_SHADER_TYPE_COMPUTE)); + acquire_unordered_access_resources(state->shader[WINED3D_SHADER_TYPE_PIXEL], + state->unordered_access_view[WINED3D_PIPELINE_GRAPHICS]); +} + +void wined3d_cs_emit_draw(struct wined3d_cs *cs, enum wined3d_primitive_type primitive_type, + unsigned int patch_vertex_count, int base_vertex_idx, unsigned int start_idx, + unsigned int index_count, unsigned int start_instance, unsigned int instance_count, bool indexed) +{ + const struct wined3d_d3d_info *d3d_info = &cs->device->adapter->d3d_info; + const struct wined3d_state *state = &cs->device->state; + struct wined3d_cs_draw *op; + + op = wined3d_cs_require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_DRAW; + op->primitive_type = primitive_type; + op->patch_vertex_count = patch_vertex_count; + op->parameters.indirect = FALSE; + op->parameters.u.direct.base_vertex_idx = base_vertex_idx; + op->parameters.u.direct.start_idx = start_idx; + op->parameters.u.direct.index_count = index_count; + op->parameters.u.direct.start_instance = start_instance; + op->parameters.u.direct.instance_count = instance_count; + op->parameters.indexed = indexed; + + acquire_graphics_pipeline_resources(state, indexed, d3d_info); + + wined3d_cs_submit(cs, WINED3D_CS_QUEUE_DEFAULT); +} + +void wined3d_cs_emit_draw_indirect(struct wined3d_cs *cs, enum wined3d_primitive_type primitive_type, + unsigned int patch_vertex_count, struct wined3d_buffer *buffer, unsigned int offset, bool indexed) +{ + const struct wined3d_d3d_info *d3d_info = &cs->device->adapter->d3d_info; + const struct wined3d_state *state = &cs->device->state; + struct wined3d_cs_draw *op; + + op = wined3d_cs_require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_DRAW; + op->primitive_type = primitive_type; + op->patch_vertex_count = patch_vertex_count; + op->parameters.indirect = TRUE; + op->parameters.u.indirect.buffer = buffer; + op->parameters.u.indirect.offset = offset; + op->parameters.indexed = indexed; + + acquire_graphics_pipeline_resources(state, indexed, d3d_info); + wined3d_resource_acquire(&buffer->resource); + + wined3d_cs_submit(cs, WINED3D_CS_QUEUE_DEFAULT); +} + +static void wined3d_cs_exec_flush(struct wined3d_cs *cs, const void *data) +{ + struct wined3d_context *context; + + context = context_acquire(cs->device, NULL, 0); + cs->device->adapter->adapter_ops->adapter_flush_context(context); + context_release(context); +} + +void wined3d_cs_emit_flush(struct wined3d_cs *cs) +{ + struct wined3d_cs_flush *op; + + op = wined3d_cs_require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_FLUSH; + + wined3d_cs_submit(cs, WINED3D_CS_QUEUE_DEFAULT); + cs->queries_flushed = TRUE; +} + +static void wined3d_cs_exec_set_predication(struct wined3d_cs *cs, const void *data) +{ + const struct wined3d_cs_set_predication *op = data; + + cs->state.predicate = op->predicate; + cs->state.predicate_value = op->value; +} + +void wined3d_cs_emit_set_predication(struct wined3d_cs *cs, struct wined3d_query *predicate, BOOL value) +{ + struct wined3d_cs_set_predication *op; + + op = wined3d_cs_require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_SET_PREDICATION; + op->predicate = predicate; + op->value = value; + + wined3d_cs_submit(cs, WINED3D_CS_QUEUE_DEFAULT); +} + +static void wined3d_cs_exec_set_viewports(struct wined3d_cs *cs, const void *data) +{ + const struct wined3d_cs_set_viewports *op = data; + + if (op->viewport_count) + memcpy(cs->state.viewports, op->viewports, op->viewport_count * sizeof(*op->viewports)); + else + memset(cs->state.viewports, 0, sizeof(*cs->state.viewports)); + cs->state.viewport_count = op->viewport_count; + device_invalidate_state(cs->device, STATE_VIEWPORT); +} + +void wined3d_cs_emit_set_viewports(struct wined3d_cs *cs, unsigned int viewport_count, + const struct wined3d_viewport *viewports) +{ + struct wined3d_cs_set_viewports *op; + + op = wined3d_cs_require_space(cs, FIELD_OFFSET(struct wined3d_cs_set_viewports, viewports[viewport_count]), + WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_SET_VIEWPORTS; + memcpy(op->viewports, viewports, viewport_count * sizeof(*viewports)); + op->viewport_count = viewport_count; + + wined3d_cs_submit(cs, WINED3D_CS_QUEUE_DEFAULT); +} + +static void wined3d_cs_exec_set_scissor_rects(struct wined3d_cs *cs, const void *data) +{ + const struct wined3d_cs_set_scissor_rects *op = data; + + if (op->rect_count) + memcpy(cs->state.scissor_rects, op->rects, op->rect_count * sizeof(*op->rects)); + else + SetRectEmpty(cs->state.scissor_rects); + cs->state.scissor_rect_count = op->rect_count; + device_invalidate_state(cs->device, STATE_SCISSORRECT); +} + +void wined3d_cs_emit_set_scissor_rects(struct wined3d_cs *cs, unsigned int rect_count, const RECT *rects) +{ + struct wined3d_cs_set_scissor_rects *op; + + op = wined3d_cs_require_space(cs, FIELD_OFFSET(struct wined3d_cs_set_scissor_rects, rects[rect_count]), + WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_SET_SCISSOR_RECTS; + memcpy(op->rects, rects, rect_count * sizeof(*rects)); + op->rect_count = rect_count; + + wined3d_cs_submit(cs, WINED3D_CS_QUEUE_DEFAULT); +} + +static void wined3d_cs_exec_set_rendertarget_view(struct wined3d_cs *cs, const void *data) +{ + const struct wined3d_cs_set_rendertarget_view *op = data; + BOOL prev_alpha_swizzle, curr_alpha_swizzle; + struct wined3d_rendertarget_view *prev; + + prev = cs->state.fb.render_targets[op->view_idx]; + cs->state.fb.render_targets[op->view_idx] = op->view; + device_invalidate_state(cs->device, STATE_FRAMEBUFFER); + + prev_alpha_swizzle = prev && prev->format->id == WINED3DFMT_A8_UNORM; + curr_alpha_swizzle = op->view && op->view->format->id == WINED3DFMT_A8_UNORM; + if (prev_alpha_swizzle != curr_alpha_swizzle) + device_invalidate_state(cs->device, STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL)); +} + +void wined3d_cs_emit_set_rendertarget_view(struct wined3d_cs *cs, unsigned int view_idx, + struct wined3d_rendertarget_view *view) +{ + struct wined3d_cs_set_rendertarget_view *op; + + op = wined3d_cs_require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_SET_RENDERTARGET_VIEW; + op->view_idx = view_idx; + op->view = view; + + wined3d_cs_submit(cs, WINED3D_CS_QUEUE_DEFAULT); +} + +static void wined3d_cs_exec_set_depth_stencil_view(struct wined3d_cs *cs, const void *data) +{ + const struct wined3d_cs_set_depth_stencil_view *op = data; + struct wined3d_device *device = cs->device; + struct wined3d_rendertarget_view *prev; + + if ((prev = cs->state.fb.depth_stencil) && prev->resource->type != WINED3D_RTYPE_BUFFER) + { + struct wined3d_texture *prev_texture = texture_from_resource(prev->resource); + + if (device->swapchains[0]->state.desc.flags & WINED3D_SWAPCHAIN_DISCARD_DEPTHSTENCIL + || prev_texture->flags & WINED3D_TEXTURE_DISCARD) + wined3d_texture_validate_location(prev_texture, + prev->sub_resource_idx, WINED3D_LOCATION_DISCARDED); + } + + cs->state.fb.depth_stencil = op->view; + + if (!prev != !op->view) + { + /* Swapping NULL / non NULL depth stencil affects the depth and tests */ + device_invalidate_state(device, STATE_DEPTH_STENCIL); + device_invalidate_state(device, STATE_STENCIL_REF); + device_invalidate_state(device, STATE_RASTERIZER); + } + else if (prev) + { + if (prev->format->depth_bias_scale != op->view->format->depth_bias_scale) + device_invalidate_state(device, STATE_RASTERIZER); + if (prev->format->stencil_size != op->view->format->stencil_size) + device_invalidate_state(device, STATE_STENCIL_REF); + } + + device_invalidate_state(device, STATE_FRAMEBUFFER); +} + +void wined3d_cs_emit_set_depth_stencil_view(struct wined3d_cs *cs, struct wined3d_rendertarget_view *view) +{ + struct wined3d_cs_set_depth_stencil_view *op; + + op = wined3d_cs_require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_SET_DEPTH_STENCIL_VIEW; + op->view = view; + + wined3d_cs_submit(cs, WINED3D_CS_QUEUE_DEFAULT); +} + +static void wined3d_cs_exec_set_vertex_declaration(struct wined3d_cs *cs, const void *data) +{ + const struct wined3d_cs_set_vertex_declaration *op = data; + + cs->state.vertex_declaration = op->declaration; + device_invalidate_state(cs->device, STATE_VDECL); +} + +void wined3d_cs_emit_set_vertex_declaration(struct wined3d_cs *cs, struct wined3d_vertex_declaration *declaration) +{ + struct wined3d_cs_set_vertex_declaration *op; + + op = wined3d_cs_require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_SET_VERTEX_DECLARATION; + op->declaration = declaration; + + wined3d_cs_submit(cs, WINED3D_CS_QUEUE_DEFAULT); +} + +static void wined3d_cs_exec_set_stream_source(struct wined3d_cs *cs, const void *data) +{ + const struct wined3d_cs_set_stream_source *op = data; + struct wined3d_stream_state *stream; + struct wined3d_buffer *prev; + + stream = &cs->state.streams[op->stream_idx]; + prev = stream->buffer; + stream->buffer = op->buffer; + stream->offset = op->offset; + stream->stride = op->stride; + + if (op->buffer) + InterlockedIncrement(&op->buffer->resource.bind_count); + if (prev) + InterlockedDecrement(&prev->resource.bind_count); + + device_invalidate_state(cs->device, STATE_STREAMSRC); +} + +void wined3d_cs_emit_set_stream_source(struct wined3d_cs *cs, UINT stream_idx, + struct wined3d_buffer *buffer, UINT offset, UINT stride) +{ + struct wined3d_cs_set_stream_source *op; + + op = wined3d_cs_require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_SET_STREAM_SOURCE; + op->stream_idx = stream_idx; + op->buffer = buffer; + op->offset = offset; + op->stride = stride; + + wined3d_cs_submit(cs, WINED3D_CS_QUEUE_DEFAULT); +} + +static void wined3d_cs_exec_set_stream_source_freq(struct wined3d_cs *cs, const void *data) +{ + const struct wined3d_cs_set_stream_source_freq *op = data; + struct wined3d_stream_state *stream; + + stream = &cs->state.streams[op->stream_idx]; + stream->frequency = op->frequency; + stream->flags = op->flags; + + device_invalidate_state(cs->device, STATE_STREAMSRC); +} + +void wined3d_cs_emit_set_stream_source_freq(struct wined3d_cs *cs, UINT stream_idx, UINT frequency, UINT flags) +{ + struct wined3d_cs_set_stream_source_freq *op; + + op = wined3d_cs_require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_SET_STREAM_SOURCE_FREQ; + op->stream_idx = stream_idx; + op->frequency = frequency; + op->flags = flags; + + wined3d_cs_submit(cs, WINED3D_CS_QUEUE_DEFAULT); +} + +static void wined3d_cs_exec_set_stream_output(struct wined3d_cs *cs, const void *data) +{ + const struct wined3d_cs_set_stream_output *op = data; + struct wined3d_stream_output *stream; + struct wined3d_buffer *prev; + + stream = &cs->state.stream_output[op->stream_idx]; + prev = stream->buffer; + stream->buffer = op->buffer; + stream->offset = op->offset; + + if (op->buffer) + InterlockedIncrement(&op->buffer->resource.bind_count); + if (prev) + InterlockedDecrement(&prev->resource.bind_count); + + device_invalidate_state(cs->device, STATE_STREAM_OUTPUT); +} + +void wined3d_cs_emit_set_stream_output(struct wined3d_cs *cs, UINT stream_idx, + struct wined3d_buffer *buffer, UINT offset) +{ + struct wined3d_cs_set_stream_output *op; + + op = wined3d_cs_require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_SET_STREAM_OUTPUT; + op->stream_idx = stream_idx; + op->buffer = buffer; + op->offset = offset; + + wined3d_cs_submit(cs, WINED3D_CS_QUEUE_DEFAULT); +} + +static void wined3d_cs_exec_set_index_buffer(struct wined3d_cs *cs, const void *data) +{ + const struct wined3d_cs_set_index_buffer *op = data; + struct wined3d_buffer *prev; + + prev = cs->state.index_buffer; + cs->state.index_buffer = op->buffer; + cs->state.index_format = op->format_id; + cs->state.index_offset = op->offset; + + if (op->buffer) + InterlockedIncrement(&op->buffer->resource.bind_count); + if (prev) + InterlockedDecrement(&prev->resource.bind_count); + + device_invalidate_state(cs->device, STATE_INDEXBUFFER); +} + +void wined3d_cs_emit_set_index_buffer(struct wined3d_cs *cs, struct wined3d_buffer *buffer, + enum wined3d_format_id format_id, unsigned int offset) +{ + struct wined3d_cs_set_index_buffer *op; + + op = wined3d_cs_require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_SET_INDEX_BUFFER; + op->buffer = buffer; + op->format_id = format_id; + op->offset = offset; + + wined3d_cs_submit(cs, WINED3D_CS_QUEUE_DEFAULT); +} + +static void wined3d_cs_exec_set_constant_buffer(struct wined3d_cs *cs, const void *data) +{ + const struct wined3d_cs_set_constant_buffer *op = data; + struct wined3d_buffer *prev; + + prev = cs->state.cb[op->type][op->cb_idx]; + cs->state.cb[op->type][op->cb_idx] = op->buffer; + + if (op->buffer) + InterlockedIncrement(&op->buffer->resource.bind_count); + if (prev) + InterlockedDecrement(&prev->resource.bind_count); + + device_invalidate_state(cs->device, STATE_CONSTANT_BUFFER(op->type)); +} + +void wined3d_cs_emit_set_constant_buffer(struct wined3d_cs *cs, enum wined3d_shader_type type, + UINT cb_idx, struct wined3d_buffer *buffer) +{ + struct wined3d_cs_set_constant_buffer *op; + + op = wined3d_cs_require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_SET_CONSTANT_BUFFER; + op->type = type; + op->cb_idx = cb_idx; + op->buffer = buffer; + + wined3d_cs_submit(cs, WINED3D_CS_QUEUE_DEFAULT); +} + +static void wined3d_cs_exec_set_texture(struct wined3d_cs *cs, const void *data) +{ + const struct wined3d_d3d_info *d3d_info = &cs->device->adapter->d3d_info; + const struct wined3d_cs_set_texture *op = data; + struct wined3d_texture *prev; + BOOL old_use_color_key = FALSE, new_use_color_key = FALSE; + + prev = cs->state.textures[op->stage]; + cs->state.textures[op->stage] = op->texture; + + if (op->texture) + { + const struct wined3d_format *new_format = op->texture->resource.format; + const struct wined3d_format *old_format = prev ? prev->resource.format : NULL; + unsigned int old_fmt_flags = prev ? prev->resource.format_flags : 0; + unsigned int new_fmt_flags = op->texture->resource.format_flags; + + if (InterlockedIncrement(&op->texture->resource.bind_count) == 1) + op->texture->sampler = op->stage; + + if (!prev || wined3d_texture_gl(op->texture)->target != wined3d_texture_gl(prev)->target + || (!is_same_fixup(new_format->color_fixup, old_format->color_fixup) + && !(can_use_texture_swizzle(d3d_info, new_format) && can_use_texture_swizzle(d3d_info, old_format))) + || (new_fmt_flags & WINED3DFMT_FLAG_SHADOW) != (old_fmt_flags & WINED3DFMT_FLAG_SHADOW)) + device_invalidate_state(cs->device, STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL)); + + if (!prev && op->stage < d3d_info->limits.ffp_blend_stages) + { + /* The source arguments for color and alpha ops have different + * meanings when a NULL texture is bound, so the COLOR_OP and + * ALPHA_OP have to be dirtified. */ + device_invalidate_state(cs->device, STATE_TEXTURESTAGE(op->stage, WINED3D_TSS_COLOR_OP)); + device_invalidate_state(cs->device, STATE_TEXTURESTAGE(op->stage, WINED3D_TSS_ALPHA_OP)); + } + + if (!op->stage && op->texture->async.color_key_flags & WINED3D_CKEY_SRC_BLT) + new_use_color_key = TRUE; + } + + if (prev) + { + if (InterlockedDecrement(&prev->resource.bind_count) && prev->sampler == op->stage) + { + unsigned int i; + + /* Search for other stages the texture is bound to. Shouldn't + * happen if applications bind textures to a single stage only. */ + TRACE("Searching for other stages the texture is bound to.\n"); + for (i = 0; i < WINED3D_MAX_COMBINED_SAMPLERS; ++i) + { + if (cs->state.textures[i] == prev) + { + TRACE("Texture is also bound to stage %u.\n", i); + prev->sampler = i; + break; + } + } + } + + if (!op->texture && op->stage < d3d_info->limits.ffp_blend_stages) + { + device_invalidate_state(cs->device, STATE_TEXTURESTAGE(op->stage, WINED3D_TSS_COLOR_OP)); + device_invalidate_state(cs->device, STATE_TEXTURESTAGE(op->stage, WINED3D_TSS_ALPHA_OP)); + } + + if (!op->stage && prev->async.color_key_flags & WINED3D_CKEY_SRC_BLT) + old_use_color_key = TRUE; + } + + device_invalidate_state(cs->device, STATE_SAMPLER(op->stage)); + + if (new_use_color_key != old_use_color_key) + device_invalidate_state(cs->device, STATE_RENDER(WINED3D_RS_COLORKEYENABLE)); + + if (new_use_color_key) + device_invalidate_state(cs->device, STATE_COLOR_KEY); +} + +void wined3d_cs_emit_set_texture(struct wined3d_cs *cs, UINT stage, struct wined3d_texture *texture) +{ + struct wined3d_cs_set_texture *op; + + op = wined3d_cs_require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_SET_TEXTURE; + op->stage = stage; + op->texture = texture; + + wined3d_cs_submit(cs, WINED3D_CS_QUEUE_DEFAULT); +} + +static void wined3d_cs_exec_set_shader_resource_view(struct wined3d_cs *cs, const void *data) +{ + const struct wined3d_cs_set_shader_resource_view *op = data; + struct wined3d_shader_resource_view *prev; + + prev = cs->state.shader_resource_view[op->type][op->view_idx]; + cs->state.shader_resource_view[op->type][op->view_idx] = op->view; + + if (op->view) + InterlockedIncrement(&op->view->resource->bind_count); + if (prev) + InterlockedDecrement(&prev->resource->bind_count); + + if (op->type != WINED3D_SHADER_TYPE_COMPUTE) + device_invalidate_state(cs->device, STATE_GRAPHICS_SHADER_RESOURCE_BINDING); + else + device_invalidate_state(cs->device, STATE_COMPUTE_SHADER_RESOURCE_BINDING); +} + +void wined3d_cs_emit_set_shader_resource_view(struct wined3d_cs *cs, enum wined3d_shader_type type, + UINT view_idx, struct wined3d_shader_resource_view *view) +{ + struct wined3d_cs_set_shader_resource_view *op; + + op = wined3d_cs_require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_SET_SHADER_RESOURCE_VIEW; + op->type = type; + op->view_idx = view_idx; + op->view = view; + + wined3d_cs_submit(cs, WINED3D_CS_QUEUE_DEFAULT); +} + +static void wined3d_cs_exec_set_unordered_access_view(struct wined3d_cs *cs, const void *data) +{ + const struct wined3d_cs_set_unordered_access_view *op = data; + struct wined3d_unordered_access_view *prev; + + prev = cs->state.unordered_access_view[op->pipeline][op->view_idx]; + cs->state.unordered_access_view[op->pipeline][op->view_idx] = op->view; + + if (op->view) + InterlockedIncrement(&op->view->resource->bind_count); + if (prev) + InterlockedDecrement(&prev->resource->bind_count); + + if (op->view && op->initial_count != ~0u) + wined3d_unordered_access_view_set_counter(op->view, op->initial_count); + + device_invalidate_state(cs->device, STATE_UNORDERED_ACCESS_VIEW_BINDING(op->pipeline)); +} + +void wined3d_cs_emit_set_unordered_access_view(struct wined3d_cs *cs, enum wined3d_pipeline pipeline, + unsigned int view_idx, struct wined3d_unordered_access_view *view, unsigned int initial_count) +{ + struct wined3d_cs_set_unordered_access_view *op; + + op = wined3d_cs_require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_SET_UNORDERED_ACCESS_VIEW; + op->pipeline = pipeline; + op->view_idx = view_idx; + op->view = view; + op->initial_count = initial_count; + + wined3d_cs_submit(cs, WINED3D_CS_QUEUE_DEFAULT); +} + +static void wined3d_cs_exec_set_sampler(struct wined3d_cs *cs, const void *data) +{ + const struct wined3d_cs_set_sampler *op = data; + + cs->state.sampler[op->type][op->sampler_idx] = op->sampler; + if (op->type != WINED3D_SHADER_TYPE_COMPUTE) + device_invalidate_state(cs->device, STATE_GRAPHICS_SHADER_RESOURCE_BINDING); + else + device_invalidate_state(cs->device, STATE_COMPUTE_SHADER_RESOURCE_BINDING); +} + +void wined3d_cs_emit_set_sampler(struct wined3d_cs *cs, enum wined3d_shader_type type, + UINT sampler_idx, struct wined3d_sampler *sampler) +{ + struct wined3d_cs_set_sampler *op; + + op = wined3d_cs_require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_SET_SAMPLER; + op->type = type; + op->sampler_idx = sampler_idx; + op->sampler = sampler; + + wined3d_cs_submit(cs, WINED3D_CS_QUEUE_DEFAULT); +} + +static void wined3d_cs_exec_set_shader(struct wined3d_cs *cs, const void *data) +{ + const struct wined3d_cs_set_shader *op = data; + + cs->state.shader[op->type] = op->shader; + device_invalidate_state(cs->device, STATE_SHADER(op->type)); + if (op->type != WINED3D_SHADER_TYPE_COMPUTE) + device_invalidate_state(cs->device, STATE_GRAPHICS_SHADER_RESOURCE_BINDING); + else + device_invalidate_state(cs->device, STATE_COMPUTE_SHADER_RESOURCE_BINDING); +} + +void wined3d_cs_emit_set_shader(struct wined3d_cs *cs, enum wined3d_shader_type type, struct wined3d_shader *shader) +{ + struct wined3d_cs_set_shader *op; + + op = wined3d_cs_require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_SET_SHADER; + op->type = type; + op->shader = shader; + + wined3d_cs_submit(cs, WINED3D_CS_QUEUE_DEFAULT); +} + +static void wined3d_cs_exec_set_blend_state(struct wined3d_cs *cs, const void *data) +{ + const struct wined3d_cs_set_blend_state *op = data; + struct wined3d_state *state = &cs->state; + + if (state->blend_state != op->state) + { + state->blend_state = op->state; + device_invalidate_state(cs->device, STATE_BLEND); + } + state->blend_factor = op->factor; + device_invalidate_state(cs->device, STATE_BLEND_FACTOR); + state->sample_mask = op->sample_mask; + device_invalidate_state(cs->device, STATE_SAMPLE_MASK); +} + +void wined3d_cs_emit_set_blend_state(struct wined3d_cs *cs, struct wined3d_blend_state *state, + const struct wined3d_color *blend_factor, unsigned int sample_mask) +{ + struct wined3d_cs_set_blend_state *op; + + op = wined3d_cs_require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_SET_BLEND_STATE; + op->state = state; + op->factor = *blend_factor; + op->sample_mask = sample_mask; + + wined3d_cs_submit(cs, WINED3D_CS_QUEUE_DEFAULT); +} + +static void wined3d_cs_exec_set_depth_stencil_state(struct wined3d_cs *cs, const void *data) +{ + const struct wined3d_cs_set_depth_stencil_state *op = data; + struct wined3d_state *state = &cs->state; + + if (state->depth_stencil_state != op->state) + { + state->depth_stencil_state = op->state; + device_invalidate_state(cs->device, STATE_DEPTH_STENCIL); + } + state->stencil_ref = op->stencil_ref; + device_invalidate_state(cs->device, STATE_STENCIL_REF); +} + +void wined3d_cs_emit_set_depth_stencil_state(struct wined3d_cs *cs, + struct wined3d_depth_stencil_state *state, unsigned int stencil_ref) +{ + struct wined3d_cs_set_depth_stencil_state *op; + + op = wined3d_cs_require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_SET_DEPTH_STENCIL_STATE; + op->state = state; + op->stencil_ref = stencil_ref; + + wined3d_cs_submit(cs, WINED3D_CS_QUEUE_DEFAULT); +} + +static void wined3d_cs_exec_set_rasterizer_state(struct wined3d_cs *cs, const void *data) +{ + const struct wined3d_cs_set_rasterizer_state *op = data; + + cs->state.rasterizer_state = op->state; + device_invalidate_state(cs->device, STATE_RASTERIZER); +} + +void wined3d_cs_emit_set_rasterizer_state(struct wined3d_cs *cs, + struct wined3d_rasterizer_state *rasterizer_state) +{ + struct wined3d_cs_set_rasterizer_state *op; + + op = wined3d_cs_require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_SET_RASTERIZER_STATE; + op->state = rasterizer_state; + + wined3d_cs_submit(cs, WINED3D_CS_QUEUE_DEFAULT); +} + +static void wined3d_cs_exec_set_render_state(struct wined3d_cs *cs, const void *data) +{ + const struct wined3d_cs_set_render_state *op = data; + + cs->state.render_states[op->state] = op->value; + device_invalidate_state(cs->device, STATE_RENDER(op->state)); +} + +void wined3d_cs_emit_set_render_state(struct wined3d_cs *cs, enum wined3d_render_state state, DWORD value) +{ + struct wined3d_cs_set_render_state *op; + + op = wined3d_cs_require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_SET_RENDER_STATE; + op->state = state; + op->value = value; + + wined3d_cs_submit(cs, WINED3D_CS_QUEUE_DEFAULT); +} + +static void wined3d_cs_exec_set_texture_state(struct wined3d_cs *cs, const void *data) +{ + const struct wined3d_cs_set_texture_state *op = data; + + cs->state.texture_states[op->stage][op->state] = op->value; + device_invalidate_state(cs->device, STATE_TEXTURESTAGE(op->stage, op->state)); +} + +void wined3d_cs_emit_set_texture_state(struct wined3d_cs *cs, UINT stage, + enum wined3d_texture_stage_state state, DWORD value) +{ + struct wined3d_cs_set_texture_state *op; + + op = wined3d_cs_require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_SET_TEXTURE_STATE; + op->stage = stage; + op->state = state; + op->value = value; + + wined3d_cs_submit(cs, WINED3D_CS_QUEUE_DEFAULT); +} + +static void wined3d_cs_exec_set_sampler_state(struct wined3d_cs *cs, const void *data) +{ + const struct wined3d_cs_set_sampler_state *op = data; + + cs->state.sampler_states[op->sampler_idx][op->state] = op->value; + device_invalidate_state(cs->device, STATE_SAMPLER(op->sampler_idx)); +} + +void wined3d_cs_emit_set_sampler_state(struct wined3d_cs *cs, UINT sampler_idx, + enum wined3d_sampler_state state, DWORD value) +{ + struct wined3d_cs_set_sampler_state *op; + + op = wined3d_cs_require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_SET_SAMPLER_STATE; + op->sampler_idx = sampler_idx; + op->state = state; + op->value = value; + + wined3d_cs_submit(cs, WINED3D_CS_QUEUE_DEFAULT); +} + +static void wined3d_cs_exec_set_transform(struct wined3d_cs *cs, const void *data) +{ + const struct wined3d_cs_set_transform *op = data; + + cs->state.transforms[op->state] = op->matrix; + if (op->state < WINED3D_TS_WORLD_MATRIX(cs->device->adapter->d3d_info.limits.ffp_vertex_blend_matrices)) + device_invalidate_state(cs->device, STATE_TRANSFORM(op->state)); +} + +void wined3d_cs_emit_set_transform(struct wined3d_cs *cs, enum wined3d_transform_state state, + const struct wined3d_matrix *matrix) +{ + struct wined3d_cs_set_transform *op; + + op = wined3d_cs_require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_SET_TRANSFORM; + op->state = state; + op->matrix = *matrix; + + wined3d_cs_submit(cs, WINED3D_CS_QUEUE_DEFAULT); +} + +static void wined3d_cs_exec_set_clip_plane(struct wined3d_cs *cs, const void *data) +{ + const struct wined3d_cs_set_clip_plane *op = data; + + cs->state.clip_planes[op->plane_idx] = op->plane; + device_invalidate_state(cs->device, STATE_CLIPPLANE(op->plane_idx)); +} + +void wined3d_cs_emit_set_clip_plane(struct wined3d_cs *cs, UINT plane_idx, const struct wined3d_vec4 *plane) +{ + struct wined3d_cs_set_clip_plane *op; + + op = wined3d_cs_require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_SET_CLIP_PLANE; + op->plane_idx = plane_idx; + op->plane = *plane; + + wined3d_cs_submit(cs, WINED3D_CS_QUEUE_DEFAULT); +} + +static void wined3d_cs_exec_set_color_key(struct wined3d_cs *cs, const void *data) +{ + const struct wined3d_cs_set_color_key *op = data; + struct wined3d_texture *texture = op->texture; + + if (op->set) + { + switch (op->flags) + { + case WINED3D_CKEY_DST_BLT: + texture->async.dst_blt_color_key = op->color_key; + texture->async.color_key_flags |= WINED3D_CKEY_DST_BLT; + break; + + case WINED3D_CKEY_DST_OVERLAY: + texture->async.dst_overlay_color_key = op->color_key; + texture->async.color_key_flags |= WINED3D_CKEY_DST_OVERLAY; + break; + + case WINED3D_CKEY_SRC_BLT: + if (texture == cs->state.textures[0]) + { + device_invalidate_state(cs->device, STATE_COLOR_KEY); + if (!(texture->async.color_key_flags & WINED3D_CKEY_SRC_BLT)) + device_invalidate_state(cs->device, STATE_RENDER(WINED3D_RS_COLORKEYENABLE)); + } + + texture->async.src_blt_color_key = op->color_key; + texture->async.color_key_flags |= WINED3D_CKEY_SRC_BLT; + break; + + case WINED3D_CKEY_SRC_OVERLAY: + texture->async.src_overlay_color_key = op->color_key; + texture->async.color_key_flags |= WINED3D_CKEY_SRC_OVERLAY; + break; + } + } + else + { + switch (op->flags) + { + case WINED3D_CKEY_DST_BLT: + texture->async.color_key_flags &= ~WINED3D_CKEY_DST_BLT; + break; + + case WINED3D_CKEY_DST_OVERLAY: + texture->async.color_key_flags &= ~WINED3D_CKEY_DST_OVERLAY; + break; + + case WINED3D_CKEY_SRC_BLT: + if (texture == cs->state.textures[0] && texture->async.color_key_flags & WINED3D_CKEY_SRC_BLT) + device_invalidate_state(cs->device, STATE_RENDER(WINED3D_RS_COLORKEYENABLE)); + + texture->async.color_key_flags &= ~WINED3D_CKEY_SRC_BLT; + break; + + case WINED3D_CKEY_SRC_OVERLAY: + texture->async.color_key_flags &= ~WINED3D_CKEY_SRC_OVERLAY; + break; + } + } +} + +void wined3d_cs_emit_set_color_key(struct wined3d_cs *cs, struct wined3d_texture *texture, + WORD flags, const struct wined3d_color_key *color_key) +{ + struct wined3d_cs_set_color_key *op; + + op = wined3d_cs_require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_SET_COLOR_KEY; + op->texture = texture; + op->flags = flags; + if (color_key) + { + op->color_key = *color_key; + op->set = 1; + } + else + op->set = 0; + + wined3d_cs_submit(cs, WINED3D_CS_QUEUE_DEFAULT); +} + +static void wined3d_cs_exec_set_material(struct wined3d_cs *cs, const void *data) +{ + const struct wined3d_cs_set_material *op = data; + + cs->state.material = op->material; + device_invalidate_state(cs->device, STATE_MATERIAL); +} + +void wined3d_cs_emit_set_material(struct wined3d_cs *cs, const struct wined3d_material *material) +{ + struct wined3d_cs_set_material *op; + + op = wined3d_cs_require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_SET_MATERIAL; + op->material = *material; + + wined3d_cs_submit(cs, WINED3D_CS_QUEUE_DEFAULT); +} + +static void wined3d_cs_exec_set_light(struct wined3d_cs *cs, const void *data) +{ + const struct wined3d_cs_set_light *op = data; + struct wined3d_light_info *light_info; + unsigned int light_idx, hash_idx; + + light_idx = op->light.OriginalIndex; + + if (!(light_info = wined3d_light_state_get_light(&cs->state.light_state, light_idx))) + { + TRACE("Adding new light.\n"); + if (!(light_info = heap_alloc_zero(sizeof(*light_info)))) + { + ERR("Failed to allocate light info.\n"); + return; + } + + hash_idx = LIGHTMAP_HASHFUNC(light_idx); + list_add_head(&cs->state.light_state.light_map[hash_idx], &light_info->entry); + light_info->glIndex = -1; + light_info->OriginalIndex = light_idx; + } + + if (light_info->glIndex != -1) + { + if (light_info->OriginalParms.type != op->light.OriginalParms.type) + device_invalidate_state(cs->device, STATE_LIGHT_TYPE); + device_invalidate_state(cs->device, STATE_ACTIVELIGHT(light_info->glIndex)); + } + + light_info->OriginalParms = op->light.OriginalParms; + light_info->position = op->light.position; + light_info->direction = op->light.direction; + light_info->exponent = op->light.exponent; + light_info->cutoff = op->light.cutoff; +} + +void wined3d_cs_emit_set_light(struct wined3d_cs *cs, const struct wined3d_light_info *light) +{ + struct wined3d_cs_set_light *op; + + op = wined3d_cs_require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_SET_LIGHT; + op->light = *light; + + wined3d_cs_submit(cs, WINED3D_CS_QUEUE_DEFAULT); +} + +static void wined3d_cs_exec_set_light_enable(struct wined3d_cs *cs, const void *data) +{ + const struct wined3d_cs_set_light_enable *op = data; + struct wined3d_device *device = cs->device; + struct wined3d_light_info *light_info; + int prev_idx; + + if (!(light_info = wined3d_light_state_get_light(&cs->state.light_state, op->idx))) + { + ERR("Light doesn't exist.\n"); + return; + } + + prev_idx = light_info->glIndex; + wined3d_light_state_enable_light(&cs->state.light_state, &device->adapter->d3d_info, light_info, op->enable); + if (light_info->glIndex != prev_idx) + { + device_invalidate_state(device, STATE_LIGHT_TYPE); + device_invalidate_state(device, STATE_ACTIVELIGHT(op->enable ? light_info->glIndex : prev_idx)); + } +} + +void wined3d_cs_emit_set_light_enable(struct wined3d_cs *cs, unsigned int idx, BOOL enable) +{ + struct wined3d_cs_set_light_enable *op; + + op = wined3d_cs_require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_SET_LIGHT_ENABLE; + op->idx = idx; + op->enable = enable; + + wined3d_cs_submit(cs, WINED3D_CS_QUEUE_DEFAULT); +} + +static const struct +{ + size_t offset; + size_t size; + DWORD mask; +} +wined3d_cs_push_constant_info[] = +{ + /* WINED3D_PUSH_CONSTANTS_VS_F */ + {FIELD_OFFSET(struct wined3d_state, vs_consts_f), sizeof(struct wined3d_vec4), WINED3D_SHADER_CONST_VS_F}, + /* WINED3D_PUSH_CONSTANTS_PS_F */ + {FIELD_OFFSET(struct wined3d_state, ps_consts_f), sizeof(struct wined3d_vec4), WINED3D_SHADER_CONST_PS_F}, + /* WINED3D_PUSH_CONSTANTS_VS_I */ + {FIELD_OFFSET(struct wined3d_state, vs_consts_i), sizeof(struct wined3d_ivec4), WINED3D_SHADER_CONST_VS_I}, + /* WINED3D_PUSH_CONSTANTS_PS_I */ + {FIELD_OFFSET(struct wined3d_state, ps_consts_i), sizeof(struct wined3d_ivec4), WINED3D_SHADER_CONST_PS_I}, + /* WINED3D_PUSH_CONSTANTS_VS_B */ + {FIELD_OFFSET(struct wined3d_state, vs_consts_b), sizeof(BOOL), WINED3D_SHADER_CONST_VS_B}, + /* WINED3D_PUSH_CONSTANTS_PS_B */ + {FIELD_OFFSET(struct wined3d_state, ps_consts_b), sizeof(BOOL), WINED3D_SHADER_CONST_PS_B}, +}; + +static void wined3d_cs_st_push_constants(struct wined3d_cs *cs, enum wined3d_push_constants p, + unsigned int start_idx, unsigned int count, const void *constants) +{ + struct wined3d_device *device = cs->device; + unsigned int context_count; + unsigned int i; + size_t offset; + + if (p == WINED3D_PUSH_CONSTANTS_VS_F) + device->shader_backend->shader_update_float_vertex_constants(device, start_idx, count); + else if (p == WINED3D_PUSH_CONSTANTS_PS_F) + device->shader_backend->shader_update_float_pixel_constants(device, start_idx, count); + + offset = wined3d_cs_push_constant_info[p].offset + start_idx * wined3d_cs_push_constant_info[p].size; + memcpy((BYTE *)&cs->state + offset, constants, count * wined3d_cs_push_constant_info[p].size); + for (i = 0, context_count = device->context_count; i < context_count; ++i) + { + device->contexts[i]->constant_update_mask |= wined3d_cs_push_constant_info[p].mask; + } +} + +static void wined3d_cs_exec_push_constants(struct wined3d_cs *cs, const void *data) +{ + const struct wined3d_cs_push_constants *op = data; + + wined3d_cs_st_push_constants(cs, op->type, op->start_idx, op->count, op->constants); +} + +static void wined3d_cs_mt_push_constants(struct wined3d_cs *cs, enum wined3d_push_constants p, + unsigned int start_idx, unsigned int count, const void *constants) +{ + struct wined3d_cs_push_constants *op; + size_t size; + + size = count * wined3d_cs_push_constant_info[p].size; + op = wined3d_cs_require_space(cs, FIELD_OFFSET(struct wined3d_cs_push_constants, constants[size]), + WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_PUSH_CONSTANTS; + op->type = p; + op->start_idx = start_idx; + op->count = count; + memcpy(op->constants, constants, size); + + wined3d_cs_submit(cs, WINED3D_CS_QUEUE_DEFAULT); +} + +static void wined3d_cs_exec_reset_state(struct wined3d_cs *cs, const void *data) +{ + struct wined3d_adapter *adapter = cs->device->adapter; + + state_cleanup(&cs->state); + memset(&cs->state, 0, sizeof(cs->state)); + state_init(&cs->state, &adapter->d3d_info, WINED3D_STATE_NO_REF | WINED3D_STATE_INIT_DEFAULT); +} + +void wined3d_cs_emit_reset_state(struct wined3d_cs *cs) +{ + struct wined3d_cs_reset_state *op; + + op = wined3d_cs_require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_RESET_STATE; + + wined3d_cs_submit(cs, WINED3D_CS_QUEUE_DEFAULT); +} + +static void wined3d_cs_exec_callback(struct wined3d_cs *cs, const void *data) +{ + const struct wined3d_cs_callback *op = data; + + op->callback(op->object); +} + +static void wined3d_cs_emit_callback(struct wined3d_cs *cs, void (*callback)(void *object), void *object) +{ + struct wined3d_cs_callback *op; + + op = wined3d_cs_require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_CALLBACK; + op->callback = callback; + op->object = object; + + wined3d_cs_submit(cs, WINED3D_CS_QUEUE_DEFAULT); +} + +void wined3d_cs_destroy_object(struct wined3d_cs *cs, void (*callback)(void *object), void *object) +{ + wined3d_cs_emit_callback(cs, callback, object); +} + +void wined3d_cs_init_object(struct wined3d_cs *cs, void (*callback)(void *object), void *object) +{ + wined3d_cs_emit_callback(cs, callback, object); +} + +static void wined3d_cs_exec_query_issue(struct wined3d_cs *cs, const void *data) +{ + const struct wined3d_cs_query_issue *op = data; + struct wined3d_query *query = op->query; + BOOL poll; + + poll = query->query_ops->query_issue(query, op->flags); + + if (!cs->thread) + return; + + if (poll && list_empty(&query->poll_list_entry)) + { + if (query->buffer_object) + InterlockedIncrement(&query->counter_retrieved); + else + list_add_tail(&cs->query_poll_list, &query->poll_list_entry); + return; + } + + /* This can happen if occlusion queries are restarted. This discards the + * old result, since polling it could result in a GL error. */ + if ((op->flags & WINED3DISSUE_BEGIN) && !poll && !list_empty(&query->poll_list_entry)) + { + list_remove(&query->poll_list_entry); + list_init(&query->poll_list_entry); + InterlockedIncrement(&query->counter_retrieved); + return; + } + + /* This can happen when an occlusion query is ended without being started, + * in which case we don't want to poll, but still have to counter-balance + * the increment of the main counter. + * + * This can also happen if an event query is re-issued before the first + * fence was reached. In this case the query is already in the list and + * the poll function will check the new fence. We have to counter-balance + * the discarded increment. */ + if (op->flags & WINED3DISSUE_END) + InterlockedIncrement(&query->counter_retrieved); +} + +void wined3d_cs_emit_query_issue(struct wined3d_cs *cs, struct wined3d_query *query, DWORD flags) +{ + struct wined3d_cs_query_issue *op; + + op = wined3d_cs_require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_QUERY_ISSUE; + op->query = query; + op->flags = flags; + + wined3d_cs_submit(cs, WINED3D_CS_QUEUE_DEFAULT); + cs->queries_flushed = FALSE; +} + +static void wined3d_cs_exec_preload_resource(struct wined3d_cs *cs, const void *data) +{ + const struct wined3d_cs_preload_resource *op = data; + struct wined3d_resource *resource = op->resource; + + resource->resource_ops->resource_preload(resource); + wined3d_resource_release(resource); +} + +void wined3d_cs_emit_preload_resource(struct wined3d_cs *cs, struct wined3d_resource *resource) +{ + struct wined3d_cs_preload_resource *op; + + op = wined3d_cs_require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_PRELOAD_RESOURCE; + op->resource = resource; + + wined3d_resource_acquire(resource); + + wined3d_cs_submit(cs, WINED3D_CS_QUEUE_DEFAULT); +} + +static void wined3d_cs_exec_unload_resource(struct wined3d_cs *cs, const void *data) +{ + const struct wined3d_cs_unload_resource *op = data; + struct wined3d_resource *resource = op->resource; + + resource->resource_ops->resource_unload(resource); + wined3d_resource_release(resource); +} + +void wined3d_cs_emit_unload_resource(struct wined3d_cs *cs, struct wined3d_resource *resource) +{ + struct wined3d_cs_unload_resource *op; + + op = wined3d_cs_require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_UNLOAD_RESOURCE; + op->resource = resource; + + wined3d_resource_acquire(resource); + + wined3d_cs_submit(cs, WINED3D_CS_QUEUE_DEFAULT); +} + +static void wined3d_cs_exec_map(struct wined3d_cs *cs, const void *data) +{ + const struct wined3d_cs_map *op = data; + struct wined3d_resource *resource = op->resource; + + *op->hr = resource->resource_ops->resource_sub_resource_map(resource, + op->sub_resource_idx, op->map_desc, op->box, op->flags); +} + +HRESULT wined3d_cs_map(struct wined3d_cs *cs, struct wined3d_resource *resource, unsigned int sub_resource_idx, + struct wined3d_map_desc *map_desc, const struct wined3d_box *box, unsigned int flags) +{ + struct wined3d_cs_map *op; + HRESULT hr; + + /* Mapping resources from the worker thread isn't an issue by itself, but + * increasing the map count would be visible to applications. */ + wined3d_not_from_cs(cs); + + op = wined3d_cs_require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_MAP); + op->opcode = WINED3D_CS_OP_MAP; + op->resource = resource; + op->sub_resource_idx = sub_resource_idx; + op->map_desc = map_desc; + op->box = box; + op->flags = flags; + op->hr = &hr; + + wined3d_cs_submit(cs, WINED3D_CS_QUEUE_MAP); + wined3d_cs_finish(cs, WINED3D_CS_QUEUE_MAP); + + return hr; +} + +static void wined3d_cs_exec_unmap(struct wined3d_cs *cs, const void *data) +{ + const struct wined3d_cs_unmap *op = data; + struct wined3d_resource *resource = op->resource; + + *op->hr = resource->resource_ops->resource_sub_resource_unmap(resource, op->sub_resource_idx); +} + +HRESULT wined3d_cs_unmap(struct wined3d_cs *cs, struct wined3d_resource *resource, unsigned int sub_resource_idx) +{ + struct wined3d_cs_unmap *op; + HRESULT hr; + + wined3d_not_from_cs(cs); + + op = wined3d_cs_require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_MAP); + op->opcode = WINED3D_CS_OP_UNMAP; + op->resource = resource; + op->sub_resource_idx = sub_resource_idx; + op->hr = &hr; + + wined3d_cs_submit(cs, WINED3D_CS_QUEUE_MAP); + wined3d_cs_finish(cs, WINED3D_CS_QUEUE_MAP); + + return hr; +} + +static void wined3d_cs_exec_blt_sub_resource(struct wined3d_cs *cs, const void *data) +{ + const struct wined3d_cs_blt_sub_resource *op = data; + + if (op->dst_resource->type == WINED3D_RTYPE_BUFFER) + { + wined3d_buffer_copy(buffer_from_resource(op->dst_resource), op->dst_box.left, + buffer_from_resource(op->src_resource), op->src_box.left, + op->src_box.right - op->src_box.left); + } + else if (op->dst_resource->type == WINED3D_RTYPE_TEXTURE_3D) + { + struct wined3d_texture *src_texture, *dst_texture; + unsigned int level, update_w, update_h, update_d; + unsigned int row_pitch, slice_pitch; + struct wined3d_context *context; + struct wined3d_bo_address addr; + + if (op->flags & ~WINED3D_BLT_RAW) + { + FIXME("Flags %#x not implemented for %s resources.\n", + op->flags, debug_d3dresourcetype(op->dst_resource->type)); + goto error; + } + + if (!(op->flags & WINED3D_BLT_RAW) && op->src_resource->format != op->dst_resource->format) + { + FIXME("Format conversion not implemented for %s resources.\n", + debug_d3dresourcetype(op->dst_resource->type)); + goto error; + } + + update_w = op->dst_box.right - op->dst_box.left; + update_h = op->dst_box.bottom - op->dst_box.top; + update_d = op->dst_box.back - op->dst_box.front; + if (op->src_box.right - op->src_box.left != update_w + || op->src_box.bottom - op->src_box.top != update_h + || op->src_box.back - op->src_box.front != update_d) + { + FIXME("Stretching not implemented for %s resources.\n", + debug_d3dresourcetype(op->dst_resource->type)); + goto error; + } + + dst_texture = texture_from_resource(op->dst_resource); + src_texture = texture_from_resource(op->src_resource); + + context = context_acquire(cs->device, NULL, 0); + + if (!wined3d_texture_load_location(src_texture, op->src_sub_resource_idx, + context, src_texture->resource.map_binding)) + { + ERR("Failed to load source sub-resource into %s.\n", + wined3d_debug_location(src_texture->resource.map_binding)); + context_release(context); + goto error; + } + + level = op->dst_sub_resource_idx % dst_texture->level_count; + if (update_w == wined3d_texture_get_level_width(dst_texture, level) + && update_h == wined3d_texture_get_level_height(dst_texture, level) + && update_d == wined3d_texture_get_level_depth(dst_texture, level)) + { + wined3d_texture_prepare_location(dst_texture, op->dst_sub_resource_idx, + context, WINED3D_LOCATION_TEXTURE_RGB); + } + else if (!wined3d_texture_load_location(dst_texture, op->dst_sub_resource_idx, + context, WINED3D_LOCATION_TEXTURE_RGB)) + { + ERR("Failed to load destination sub-resource.\n"); + context_release(context); + goto error; + } + + wined3d_texture_get_memory(src_texture, op->src_sub_resource_idx, &addr, src_texture->resource.map_binding); + wined3d_texture_get_pitch(src_texture, op->src_sub_resource_idx % src_texture->level_count, + &row_pitch, &slice_pitch); + + dst_texture->texture_ops->texture_upload_data(context, wined3d_const_bo_address(&addr), + dst_texture->resource.format, &op->src_box, row_pitch, slice_pitch, dst_texture, + op->dst_sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB, + op->dst_box.left, op->dst_box.top, op->dst_box.front); + wined3d_texture_validate_location(dst_texture, op->dst_sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB); + wined3d_texture_invalidate_location(dst_texture, op->dst_sub_resource_idx, ~WINED3D_LOCATION_TEXTURE_RGB); + + context_release(context); + } + else + { + if (FAILED(texture2d_blt(texture_from_resource(op->dst_resource), op->dst_sub_resource_idx, + &op->dst_box, texture_from_resource(op->src_resource), op->src_sub_resource_idx, + &op->src_box, op->flags, &op->fx, op->filter))) + FIXME("Blit failed.\n"); + } + +error: + if (op->src_resource) + wined3d_resource_release(op->src_resource); + wined3d_resource_release(op->dst_resource); +} + +void wined3d_cs_emit_blt_sub_resource(struct wined3d_cs *cs, struct wined3d_resource *dst_resource, + unsigned int dst_sub_resource_idx, const struct wined3d_box *dst_box, struct wined3d_resource *src_resource, + unsigned int src_sub_resource_idx, const struct wined3d_box *src_box, DWORD flags, + const struct wined3d_blt_fx *fx, enum wined3d_texture_filter_type filter) +{ + struct wined3d_cs_blt_sub_resource *op; + + op = wined3d_cs_require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_BLT_SUB_RESOURCE; + op->dst_resource = dst_resource; + op->dst_sub_resource_idx = dst_sub_resource_idx; + op->dst_box = *dst_box; + op->src_resource = src_resource; + op->src_sub_resource_idx = src_sub_resource_idx; + op->src_box = *src_box; + op->flags = flags; + if (fx) + op->fx = *fx; + else + memset(&op->fx, 0, sizeof(op->fx)); + op->filter = filter; + + wined3d_resource_acquire(dst_resource); + if (src_resource) + wined3d_resource_acquire(src_resource); + + wined3d_cs_submit(cs, WINED3D_CS_QUEUE_DEFAULT); + if (flags & WINED3D_BLT_SYNCHRONOUS) + wined3d_cs_finish(cs, WINED3D_CS_QUEUE_DEFAULT); +} + +static void wined3d_cs_exec_update_sub_resource(struct wined3d_cs *cs, const void *data) +{ + const struct wined3d_cs_update_sub_resource *op = data; + struct wined3d_resource *resource = op->resource; + const struct wined3d_box *box = &op->box; + unsigned int width, height, depth, level; + struct wined3d_const_bo_address addr; + struct wined3d_context *context; + struct wined3d_texture *texture; + struct wined3d_box src_box; + + context = context_acquire(cs->device, NULL, 0); + + if (resource->type == WINED3D_RTYPE_BUFFER) + { + struct wined3d_buffer *buffer = buffer_from_resource(resource); + + if (!wined3d_buffer_load_location(buffer, context, WINED3D_LOCATION_BUFFER)) + { + ERR("Failed to load buffer location.\n"); + goto done; + } + + wined3d_buffer_upload_data(buffer, context, box, op->data.data); + wined3d_buffer_invalidate_location(buffer, ~WINED3D_LOCATION_BUFFER); + goto done; + } + + texture = wined3d_texture_from_resource(resource); + + level = op->sub_resource_idx % texture->level_count; + width = wined3d_texture_get_level_width(texture, level); + height = wined3d_texture_get_level_height(texture, level); + depth = wined3d_texture_get_level_depth(texture, level); + + addr.buffer_object = 0; + addr.addr = op->data.data; + + /* Only load the sub-resource for partial updates. */ + if (!box->left && !box->top && !box->front + && box->right == width && box->bottom == height && box->back == depth) + wined3d_texture_prepare_location(texture, op->sub_resource_idx, context, WINED3D_LOCATION_TEXTURE_RGB); + else + wined3d_texture_load_location(texture, op->sub_resource_idx, context, WINED3D_LOCATION_TEXTURE_RGB); + + wined3d_box_set(&src_box, 0, 0, box->right - box->left, box->bottom - box->top, 0, box->back - box->front); + texture->texture_ops->texture_upload_data(context, &addr, texture->resource.format, &src_box, + op->data.row_pitch, op->data.slice_pitch, texture, op->sub_resource_idx, + WINED3D_LOCATION_TEXTURE_RGB, box->left, box->top, box->front); + + wined3d_texture_validate_location(texture, op->sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB); + wined3d_texture_invalidate_location(texture, op->sub_resource_idx, ~WINED3D_LOCATION_TEXTURE_RGB); + +done: + context_release(context); + + wined3d_resource_release(resource); +} + +void wined3d_cs_emit_update_sub_resource(struct wined3d_cs *cs, struct wined3d_resource *resource, + unsigned int sub_resource_idx, const struct wined3d_box *box, const void *data, unsigned int row_pitch, + unsigned int slice_pitch) +{ + struct wined3d_cs_update_sub_resource *op; + + op = wined3d_cs_require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_MAP); + op->opcode = WINED3D_CS_OP_UPDATE_SUB_RESOURCE; + op->resource = resource; + op->sub_resource_idx = sub_resource_idx; + op->box = *box; + op->data.row_pitch = row_pitch; + op->data.slice_pitch = slice_pitch; + op->data.data = data; + + wined3d_resource_acquire(resource); + + wined3d_cs_submit(cs, WINED3D_CS_QUEUE_MAP); + /* The data pointer may go away, so we need to wait until it is read. + * Copying the data may be faster if it's small. */ + wined3d_cs_finish(cs, WINED3D_CS_QUEUE_MAP); +} + +static void wined3d_cs_exec_add_dirty_texture_region(struct wined3d_cs *cs, const void *data) +{ + const struct wined3d_cs_add_dirty_texture_region *op = data; + struct wined3d_texture *texture = op->texture; + unsigned int sub_resource_idx, i; + struct wined3d_context *context; + + context = context_acquire(cs->device, NULL, 0); + sub_resource_idx = op->layer * texture->level_count; + for (i = 0; i < texture->level_count; ++i, ++sub_resource_idx) + { + if (wined3d_texture_load_location(texture, sub_resource_idx, context, texture->resource.map_binding)) + wined3d_texture_invalidate_location(texture, sub_resource_idx, ~texture->resource.map_binding); + else + ERR("Failed to load location %s.\n", wined3d_debug_location(texture->resource.map_binding)); + } + context_release(context); + + wined3d_resource_release(&texture->resource); +} + +void wined3d_cs_emit_add_dirty_texture_region(struct wined3d_cs *cs, + struct wined3d_texture *texture, unsigned int layer) +{ + struct wined3d_cs_add_dirty_texture_region *op; + + op = wined3d_cs_require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_ADD_DIRTY_TEXTURE_REGION; + op->texture = texture; + op->layer = layer; + + wined3d_resource_acquire(&texture->resource); + + wined3d_cs_submit(cs, WINED3D_CS_QUEUE_DEFAULT); +} + +static void wined3d_cs_exec_clear_unordered_access_view(struct wined3d_cs *cs, const void *data) +{ + const struct wined3d_cs_clear_unordered_access_view *op = data; + struct wined3d_unordered_access_view *view = op->view; + struct wined3d_context *context; + + context = context_acquire(cs->device, NULL, 0); + cs->device->adapter->adapter_ops->adapter_clear_uav(context, view, &op->clear_value); + context_release(context); + + wined3d_resource_release(view->resource); +} + +void wined3d_cs_emit_clear_unordered_access_view_uint(struct wined3d_cs *cs, + struct wined3d_unordered_access_view *view, const struct wined3d_uvec4 *clear_value) +{ + struct wined3d_cs_clear_unordered_access_view *op; + + op = wined3d_cs_require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_CLEAR_UNORDERED_ACCESS_VIEW; + op->view = view; + op->clear_value = *clear_value; + + wined3d_resource_acquire(view->resource); + + wined3d_cs_submit(cs, WINED3D_CS_QUEUE_DEFAULT); +} + +static void wined3d_cs_exec_copy_uav_counter(struct wined3d_cs *cs, const void *data) +{ + const struct wined3d_cs_copy_uav_counter *op = data; + struct wined3d_unordered_access_view *view = op->view; + struct wined3d_context *context; + + context = context_acquire(cs->device, NULL, 0); + wined3d_unordered_access_view_copy_counter(view, op->buffer, op->offset, context); + context_release(context); + + wined3d_resource_release(&op->buffer->resource); +} + +void wined3d_cs_emit_copy_uav_counter(struct wined3d_cs *cs, struct wined3d_buffer *dst_buffer, + unsigned int offset, struct wined3d_unordered_access_view *uav) +{ + struct wined3d_cs_copy_uav_counter *op; + + op = wined3d_cs_require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_COPY_UAV_COUNTER; + op->buffer = dst_buffer; + op->offset = offset; + op->view = uav; + + wined3d_resource_acquire(&dst_buffer->resource); + + wined3d_cs_submit(cs, WINED3D_CS_QUEUE_DEFAULT); +} + +static void wined3d_cs_exec_generate_mipmaps(struct wined3d_cs *cs, const void *data) +{ + const struct wined3d_cs_generate_mipmaps *op = data; + struct wined3d_shader_resource_view *view = op->view; + + shader_resource_view_generate_mipmaps(view); + wined3d_resource_release(view->resource); +} + +void wined3d_cs_emit_generate_mipmaps(struct wined3d_cs *cs, struct wined3d_shader_resource_view *view) +{ + struct wined3d_cs_generate_mipmaps *op; + + op = wined3d_cs_require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_GENERATE_MIPMAPS; + op->view = view; + + wined3d_resource_acquire(view->resource); + + wined3d_cs_submit(cs, WINED3D_CS_QUEUE_DEFAULT); +} + +static void wined3d_cs_emit_stop(struct wined3d_cs *cs) +{ + struct wined3d_cs_stop *op; + + op = wined3d_cs_require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_STOP; + + wined3d_cs_submit(cs, WINED3D_CS_QUEUE_DEFAULT); + wined3d_cs_finish(cs, WINED3D_CS_QUEUE_DEFAULT); +} + +static void (* const wined3d_cs_op_handlers[])(struct wined3d_cs *cs, const void *data) = +{ + /* WINED3D_CS_OP_NOP */ wined3d_cs_exec_nop, + /* WINED3D_CS_OP_PRESENT */ wined3d_cs_exec_present, + /* WINED3D_CS_OP_CLEAR */ wined3d_cs_exec_clear, + /* WINED3D_CS_OP_DISPATCH */ wined3d_cs_exec_dispatch, + /* WINED3D_CS_OP_DRAW */ wined3d_cs_exec_draw, + /* WINED3D_CS_OP_FLUSH */ wined3d_cs_exec_flush, + /* WINED3D_CS_OP_SET_PREDICATION */ wined3d_cs_exec_set_predication, + /* WINED3D_CS_OP_SET_VIEWPORTS */ wined3d_cs_exec_set_viewports, + /* WINED3D_CS_OP_SET_SCISSOR_RECTS */ wined3d_cs_exec_set_scissor_rects, + /* WINED3D_CS_OP_SET_RENDERTARGET_VIEW */ wined3d_cs_exec_set_rendertarget_view, + /* WINED3D_CS_OP_SET_DEPTH_STENCIL_VIEW */ wined3d_cs_exec_set_depth_stencil_view, + /* WINED3D_CS_OP_SET_VERTEX_DECLARATION */ wined3d_cs_exec_set_vertex_declaration, + /* WINED3D_CS_OP_SET_STREAM_SOURCE */ wined3d_cs_exec_set_stream_source, + /* WINED3D_CS_OP_SET_STREAM_SOURCE_FREQ */ wined3d_cs_exec_set_stream_source_freq, + /* WINED3D_CS_OP_SET_STREAM_OUTPUT */ wined3d_cs_exec_set_stream_output, + /* WINED3D_CS_OP_SET_INDEX_BUFFER */ wined3d_cs_exec_set_index_buffer, + /* WINED3D_CS_OP_SET_CONSTANT_BUFFER */ wined3d_cs_exec_set_constant_buffer, + /* WINED3D_CS_OP_SET_TEXTURE */ wined3d_cs_exec_set_texture, + /* WINED3D_CS_OP_SET_SHADER_RESOURCE_VIEW */ wined3d_cs_exec_set_shader_resource_view, + /* WINED3D_CS_OP_SET_UNORDERED_ACCESS_VIEW */ wined3d_cs_exec_set_unordered_access_view, + /* WINED3D_CS_OP_SET_SAMPLER */ wined3d_cs_exec_set_sampler, + /* WINED3D_CS_OP_SET_SHADER */ wined3d_cs_exec_set_shader, + /* WINED3D_CS_OP_SET_BLEND_STATE */ wined3d_cs_exec_set_blend_state, + /* WINED3D_CS_OP_SET_DEPTH_STENCIL_STATE */ wined3d_cs_exec_set_depth_stencil_state, + /* WINED3D_CS_OP_SET_RASTERIZER_STATE */ wined3d_cs_exec_set_rasterizer_state, + /* WINED3D_CS_OP_SET_RENDER_STATE */ wined3d_cs_exec_set_render_state, + /* WINED3D_CS_OP_SET_TEXTURE_STATE */ wined3d_cs_exec_set_texture_state, + /* WINED3D_CS_OP_SET_SAMPLER_STATE */ wined3d_cs_exec_set_sampler_state, + /* WINED3D_CS_OP_SET_TRANSFORM */ wined3d_cs_exec_set_transform, + /* WINED3D_CS_OP_SET_CLIP_PLANE */ wined3d_cs_exec_set_clip_plane, + /* WINED3D_CS_OP_SET_COLOR_KEY */ wined3d_cs_exec_set_color_key, + /* WINED3D_CS_OP_SET_MATERIAL */ wined3d_cs_exec_set_material, + /* WINED3D_CS_OP_SET_LIGHT */ wined3d_cs_exec_set_light, + /* WINED3D_CS_OP_SET_LIGHT_ENABLE */ wined3d_cs_exec_set_light_enable, + /* WINED3D_CS_OP_PUSH_CONSTANTS */ wined3d_cs_exec_push_constants, + /* WINED3D_CS_OP_RESET_STATE */ wined3d_cs_exec_reset_state, + /* WINED3D_CS_OP_CALLBACK */ wined3d_cs_exec_callback, + /* WINED3D_CS_OP_QUERY_ISSUE */ wined3d_cs_exec_query_issue, + /* WINED3D_CS_OP_PRELOAD_RESOURCE */ wined3d_cs_exec_preload_resource, + /* WINED3D_CS_OP_UNLOAD_RESOURCE */ wined3d_cs_exec_unload_resource, + /* WINED3D_CS_OP_MAP */ wined3d_cs_exec_map, + /* WINED3D_CS_OP_UNMAP */ wined3d_cs_exec_unmap, + /* WINED3D_CS_OP_BLT_SUB_RESOURCE */ wined3d_cs_exec_blt_sub_resource, + /* WINED3D_CS_OP_UPDATE_SUB_RESOURCE */ wined3d_cs_exec_update_sub_resource, + /* WINED3D_CS_OP_ADD_DIRTY_TEXTURE_REGION */ wined3d_cs_exec_add_dirty_texture_region, + /* WINED3D_CS_OP_CLEAR_UNORDERED_ACCESS_VIEW */ wined3d_cs_exec_clear_unordered_access_view, + /* WINED3D_CS_OP_COPY_UAV_COUNTER */ wined3d_cs_exec_copy_uav_counter, + /* WINED3D_CS_OP_GENERATE_MIPMAPS */ wined3d_cs_exec_generate_mipmaps, +}; + +static void *wined3d_cs_st_require_space(struct wined3d_cs *cs, size_t size, enum wined3d_cs_queue_id queue_id) +{ + if (size > (cs->data_size - cs->end)) + { + size_t new_size; + void *new_data; + + new_size = max(size, cs->data_size * 2); + if (!cs->end) + new_data = heap_realloc(cs->data, new_size); + else + new_data = heap_alloc(new_size); + if (!new_data) + return NULL; + + cs->data_size = new_size; + cs->start = cs->end = 0; + cs->data = new_data; + } + + cs->end += size; + + return (BYTE *)cs->data + cs->start; +} + +static void wined3d_cs_st_submit(struct wined3d_cs *cs, enum wined3d_cs_queue_id queue_id) +{ + enum wined3d_cs_op opcode; + size_t start; + BYTE *data; + + data = cs->data; + start = cs->start; + cs->start = cs->end; + + opcode = *(const enum wined3d_cs_op *)&data[start]; + if (opcode >= WINED3D_CS_OP_STOP) + ERR("Invalid opcode %#x.\n", opcode); + else + wined3d_cs_op_handlers[opcode](cs, &data[start]); + + if (cs->data == data) + cs->start = cs->end = start; + else if (!start) + heap_free(data); +} + +static void wined3d_cs_st_finish(struct wined3d_cs *cs, enum wined3d_cs_queue_id queue_id) +{ +} + +static const struct wined3d_cs_ops wined3d_cs_st_ops = +{ + wined3d_cs_st_require_space, + wined3d_cs_st_submit, + wined3d_cs_st_finish, + wined3d_cs_st_push_constants, +}; + +static BOOL wined3d_cs_queue_is_empty(const struct wined3d_cs *cs, const struct wined3d_cs_queue *queue) +{ + wined3d_from_cs(cs); + return *(volatile LONG *)&queue->head == queue->tail; +} + +static void wined3d_cs_queue_submit(struct wined3d_cs_queue *queue, struct wined3d_cs *cs) +{ + struct wined3d_cs_packet *packet; + size_t packet_size; + + packet = (struct wined3d_cs_packet *)&queue->data[queue->head]; + packet_size = FIELD_OFFSET(struct wined3d_cs_packet, data[packet->size]); + InterlockedExchange(&queue->head, (queue->head + packet_size) & (WINED3D_CS_QUEUE_SIZE - 1)); + + if (InterlockedCompareExchange(&cs->waiting_for_event, FALSE, TRUE)) + SetEvent(cs->event); +} + +static void wined3d_cs_mt_submit(struct wined3d_cs *cs, enum wined3d_cs_queue_id queue_id) +{ + if (cs->thread_id == GetCurrentThreadId()) + return wined3d_cs_st_submit(cs, queue_id); + + wined3d_cs_queue_submit(&cs->queue[queue_id], cs); +} + +static void *wined3d_cs_queue_require_space(struct wined3d_cs_queue *queue, size_t size, struct wined3d_cs *cs) +{ + size_t queue_size = ARRAY_SIZE(queue->data); + size_t header_size, packet_size, remaining; + struct wined3d_cs_packet *packet; + + header_size = FIELD_OFFSET(struct wined3d_cs_packet, data[0]); + packet_size = FIELD_OFFSET(struct wined3d_cs_packet, data[size]); + packet_size = (packet_size + header_size - 1) & ~(header_size - 1); + size = packet_size - header_size; + if (packet_size >= WINED3D_CS_QUEUE_SIZE) + { + ERR("Packet size %lu >= queue size %u.\n", + (unsigned long)packet_size, WINED3D_CS_QUEUE_SIZE); + return NULL; + } + + remaining = queue_size - queue->head; + if (remaining < packet_size) + { + size_t nop_size = remaining - header_size; + struct wined3d_cs_nop *nop; + + TRACE("Inserting a nop for %lu + %lu bytes.\n", + (unsigned long)header_size, (unsigned long)nop_size); + + nop = wined3d_cs_queue_require_space(queue, nop_size, cs); + if (nop_size) + nop->opcode = WINED3D_CS_OP_NOP; + + wined3d_cs_queue_submit(queue, cs); + assert(!queue->head); + } + + for (;;) + { + LONG tail = *(volatile LONG *)&queue->tail; + LONG head = queue->head; + LONG new_pos; + + /* Empty. */ + if (head == tail) + break; + new_pos = (head + packet_size) & (WINED3D_CS_QUEUE_SIZE - 1); + /* Head ahead of tail. We checked the remaining size above, so we only + * need to make sure we don't make head equal to tail. */ + if (head > tail && (new_pos != tail)) + break; + /* Tail ahead of head. Make sure the new head is before the tail as + * well. Note that new_pos is 0 when it's at the end of the queue. */ + if (new_pos < tail && new_pos) + break; + + TRACE("Waiting for free space. Head %u, tail %u, packet size %lu.\n", + head, tail, (unsigned long)packet_size); + } + + packet = (struct wined3d_cs_packet *)&queue->data[queue->head]; + packet->size = size; + return packet->data; +} + +static void *wined3d_cs_mt_require_space(struct wined3d_cs *cs, size_t size, enum wined3d_cs_queue_id queue_id) +{ + if (cs->thread_id == GetCurrentThreadId()) + return wined3d_cs_st_require_space(cs, size, queue_id); + + return wined3d_cs_queue_require_space(&cs->queue[queue_id], size, cs); +} + +static void wined3d_cs_mt_finish(struct wined3d_cs *cs, enum wined3d_cs_queue_id queue_id) +{ + if (cs->thread_id == GetCurrentThreadId()) + return wined3d_cs_st_finish(cs, queue_id); + + while (cs->queue[queue_id].head != *(volatile LONG *)&cs->queue[queue_id].tail) + YieldProcessor(); +} + +static const struct wined3d_cs_ops wined3d_cs_mt_ops = +{ + wined3d_cs_mt_require_space, + wined3d_cs_mt_submit, + wined3d_cs_mt_finish, + wined3d_cs_mt_push_constants, +}; + +static void poll_queries(struct wined3d_cs *cs) +{ + struct wined3d_query *query, *cursor; + + LIST_FOR_EACH_ENTRY_SAFE(query, cursor, &cs->query_poll_list, struct wined3d_query, poll_list_entry) + { + if (!query->query_ops->query_poll(query, 0)) + continue; + + list_remove(&query->poll_list_entry); + list_init(&query->poll_list_entry); + InterlockedIncrement(&query->counter_retrieved); + } +} + +static void wined3d_cs_wait_event(struct wined3d_cs *cs) +{ + InterlockedExchange(&cs->waiting_for_event, TRUE); + + /* The main thread might have enqueued a command and blocked on it after + * the CS thread decided to enter wined3d_cs_wait_event(), but before + * "waiting_for_event" was set. + * + * Likewise, we can race with the main thread when resetting + * "waiting_for_event", in which case we would need to call + * WaitForSingleObject() because the main thread called SetEvent(). */ + if (!(wined3d_cs_queue_is_empty(cs, &cs->queue[WINED3D_CS_QUEUE_DEFAULT]) + && wined3d_cs_queue_is_empty(cs, &cs->queue[WINED3D_CS_QUEUE_MAP])) + && InterlockedCompareExchange(&cs->waiting_for_event, FALSE, TRUE)) + return; + + WaitForSingleObject(cs->event, INFINITE); +} + +static void wined3d_cs_command_lock(const struct wined3d_cs *cs) +{ + if (cs->serialize_commands) + EnterCriticalSection(&wined3d_command_cs); +} + +static void wined3d_cs_command_unlock(const struct wined3d_cs *cs) +{ + if (cs->serialize_commands) + LeaveCriticalSection(&wined3d_command_cs); +} + +static DWORD WINAPI wined3d_cs_run(void *ctx) +{ + struct wined3d_cs_packet *packet; + struct wined3d_cs_queue *queue; + unsigned int spin_count = 0; + struct wined3d_cs *cs = ctx; + enum wined3d_cs_op opcode; + HMODULE wined3d_module; + unsigned int poll = 0; + LONG tail; + + TRACE("Started.\n"); + + /* Copy the module handle to a local variable to avoid racing with the + * thread freeing "cs" before the FreeLibraryAndExitThread() call. */ + wined3d_module = cs->wined3d_module; + + list_init(&cs->query_poll_list); + cs->thread_id = GetCurrentThreadId(); + for (;;) + { + if (++poll == WINED3D_CS_QUERY_POLL_INTERVAL) + { + wined3d_cs_command_lock(cs); + poll_queries(cs); + wined3d_cs_command_unlock(cs); + poll = 0; + } + + queue = &cs->queue[WINED3D_CS_QUEUE_MAP]; + if (wined3d_cs_queue_is_empty(cs, queue)) + { + queue = &cs->queue[WINED3D_CS_QUEUE_DEFAULT]; + if (wined3d_cs_queue_is_empty(cs, queue)) + { + if (++spin_count >= WINED3D_CS_SPIN_COUNT && list_empty(&cs->query_poll_list)) + wined3d_cs_wait_event(cs); + continue; + } + } + spin_count = 0; + + tail = queue->tail; + packet = (struct wined3d_cs_packet *)&queue->data[tail]; + if (packet->size) + { + opcode = *(const enum wined3d_cs_op *)packet->data; + + TRACE("Executing %s.\n", debug_cs_op(opcode)); + if (opcode >= WINED3D_CS_OP_STOP) + { + if (opcode > WINED3D_CS_OP_STOP) + ERR("Invalid opcode %#x.\n", opcode); + break; + } + + wined3d_cs_command_lock(cs); + wined3d_cs_op_handlers[opcode](cs, packet->data); + wined3d_cs_command_unlock(cs); + TRACE("%s executed.\n", debug_cs_op(opcode)); + } + + tail += FIELD_OFFSET(struct wined3d_cs_packet, data[packet->size]); + tail &= (WINED3D_CS_QUEUE_SIZE - 1); + InterlockedExchange(&queue->tail, tail); + } + + cs->queue[WINED3D_CS_QUEUE_MAP].tail = cs->queue[WINED3D_CS_QUEUE_MAP].head; + cs->queue[WINED3D_CS_QUEUE_DEFAULT].tail = cs->queue[WINED3D_CS_QUEUE_DEFAULT].head; + TRACE("Stopped.\n"); + FreeLibraryAndExitThread(wined3d_module, 0); +} + +struct wined3d_cs *wined3d_cs_create(struct wined3d_device *device) +{ + const struct wined3d_d3d_info *d3d_info = &device->adapter->d3d_info; + struct wined3d_cs *cs; + + if (!(cs = heap_alloc_zero(sizeof(*cs)))) + return NULL; + + cs->ops = &wined3d_cs_st_ops; + cs->device = device; + cs->serialize_commands = TRACE_ON(d3d_sync) || wined3d_settings.cs_multithreaded & WINED3D_CSMT_SERIALIZE; + + state_init(&cs->state, d3d_info, WINED3D_STATE_NO_REF | WINED3D_STATE_INIT_DEFAULT); + + cs->data_size = WINED3D_INITIAL_CS_SIZE; + if (!(cs->data = heap_alloc(cs->data_size))) + goto fail; + + if (wined3d_settings.cs_multithreaded & WINED3D_CSMT_ENABLE + && !RtlIsCriticalSectionLockedByThread(NtCurrentTeb()->Peb->LoaderLock)) + { + cs->ops = &wined3d_cs_mt_ops; + + if (!(cs->event = CreateEventW(NULL, FALSE, FALSE, NULL))) + { + ERR("Failed to create command stream event.\n"); + heap_free(cs->data); + goto fail; + } + + if (!(GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, + (const WCHAR *)wined3d_cs_run, &cs->wined3d_module))) + { + ERR("Failed to get wined3d module handle.\n"); + CloseHandle(cs->event); + heap_free(cs->data); + goto fail; + } + + if (!(cs->thread = CreateThread(NULL, 0, wined3d_cs_run, cs, 0, NULL))) + { + ERR("Failed to create wined3d command stream thread.\n"); + FreeLibrary(cs->wined3d_module); + CloseHandle(cs->event); + heap_free(cs->data); + goto fail; + } + } + + return cs; + +fail: + state_cleanup(&cs->state); + heap_free(cs); + return NULL; +} + +void wined3d_cs_destroy(struct wined3d_cs *cs) +{ + if (cs->thread) + { + wined3d_cs_emit_stop(cs); + CloseHandle(cs->thread); + if (!CloseHandle(cs->event)) + ERR("Closing event failed.\n"); + } + + state_cleanup(&cs->state); + heap_free(cs->data); + heap_free(cs); +} diff --git a/wrappers/directx/d3dwine_wrapper/device.c b/wrappers/directx/d3dwine_wrapper/device.c new file mode 100644 index 00000000000..1fd5f4b012e --- /dev/null +++ b/wrappers/directx/d3dwine_wrapper/device.c @@ -0,0 +1,5997 @@ +/* + * Copyright 2002 Lionel Ulmer + * Copyright 2002-2005 Jason Edmeades + * Copyright 2003-2004 Raphael Junqueira + * Copyright 2004 Christian Costa + * Copyright 2005 Oliver Stieber + * Copyright 2006-2008 Stefan Dösinger for CodeWeavers + * Copyright 2006-2008 Henri Verbeet + * Copyright 2007 Andrew Riedi + * Copyright 2009-2011 Henri Verbeet for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "config.h" +#include "wine/port.h" + +#ifdef HAVE_FLOAT_H +# include +#endif + +#include "wined3d_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(d3d); +WINE_DECLARE_DEBUG_CHANNEL(winediag); + +struct wined3d_matrix_3x3 +{ + float _11, _12, _13; + float _21, _22, _23; + float _31, _32, _33; +}; + +struct light_transformed +{ + struct wined3d_color diffuse, specular, ambient; + struct wined3d_vec4 position; + struct wined3d_vec3 direction; + float range, falloff, c_att, l_att, q_att, cos_htheta, cos_hphi; +}; + +struct lights_settings +{ + struct light_transformed lights[WINED3D_MAX_SOFTWARE_ACTIVE_LIGHTS]; + struct wined3d_color ambient_light; + struct wined3d_matrix modelview_matrix; + struct wined3d_matrix_3x3 normal_matrix; + struct wined3d_vec4 position_transformed; + + float fog_start, fog_end, fog_density; + + uint32_t point_light_count : 8; + uint32_t spot_light_count : 8; + uint32_t directional_light_count : 8; + uint32_t parallel_point_light_count : 8; + uint32_t lighting : 1; + uint32_t legacy_lighting : 1; + uint32_t normalise : 1; + uint32_t localviewer : 1; + uint32_t fog_coord_mode : 2; + uint32_t fog_mode : 2; + uint32_t padding : 24; +}; + +/* Define the default light parameters as specified by MSDN. */ +const struct wined3d_light WINED3D_default_light = +{ + WINED3D_LIGHT_DIRECTIONAL, /* Type */ + { 1.0f, 1.0f, 1.0f, 0.0f }, /* Diffuse r,g,b,a */ + { 0.0f, 0.0f, 0.0f, 0.0f }, /* Specular r,g,b,a */ + { 0.0f, 0.0f, 0.0f, 0.0f }, /* Ambient r,g,b,a, */ + { 0.0f, 0.0f, 0.0f }, /* Position x,y,z */ + { 0.0f, 0.0f, 1.0f }, /* Direction x,y,z */ + 0.0f, /* Range */ + 0.0f, /* Falloff */ + 0.0f, 0.0f, 0.0f, /* Attenuation 0,1,2 */ + 0.0f, /* Theta */ + 0.0f /* Phi */ +}; + +BOOL device_context_add(struct wined3d_device *device, struct wined3d_context *context) +{ + struct wined3d_context **new_array; + + TRACE("Adding context %p.\n", context); + + if (!device->shader_backend->shader_allocate_context_data(context)) + { + ERR("Failed to allocate shader backend context data.\n"); + return FALSE; + } + device->shader_backend->shader_init_context_state(context); + + if (!device->adapter->fragment_pipe->allocate_context_data(context)) + { + ERR("Failed to allocate fragment pipeline context data.\n"); + device->shader_backend->shader_free_context_data(context); + return FALSE; + } + + if (!(new_array = heap_realloc(device->contexts, sizeof(*new_array) * (device->context_count + 1)))) + { + ERR("Failed to grow the context array.\n"); + device->adapter->fragment_pipe->free_context_data(context); + device->shader_backend->shader_free_context_data(context); + return FALSE; + } + + new_array[device->context_count++] = context; + device->contexts = new_array; + + return TRUE; +} + +void device_context_remove(struct wined3d_device *device, struct wined3d_context *context) +{ + struct wined3d_context **new_array; + BOOL found = FALSE; + UINT i; + + TRACE("Removing context %p.\n", context); + + device->adapter->fragment_pipe->free_context_data(context); + device->shader_backend->shader_free_context_data(context); + + for (i = 0; i < device->context_count; ++i) + { + if (device->contexts[i] == context) + { + found = TRUE; + break; + } + } + + if (!found) + { + ERR("Context %p doesn't exist in context array.\n", context); + return; + } + + if (!--device->context_count) + { + heap_free(device->contexts); + device->contexts = NULL; + return; + } + + memmove(&device->contexts[i], &device->contexts[i + 1], (device->context_count - i) * sizeof(*device->contexts)); + if (!(new_array = heap_realloc(device->contexts, device->context_count * sizeof(*device->contexts)))) + { + ERR("Failed to shrink context array. Oh well.\n"); + return; + } + + device->contexts = new_array; +} + +ULONG CDECL wined3d_device_incref(struct wined3d_device *device) +{ + ULONG refcount = InterlockedIncrement(&device->ref); + + TRACE("%p increasing refcount to %u.\n", device, refcount); + + return refcount; +} + +static void device_free_so_desc(struct wine_rb_entry *entry, void *context) +{ + struct wined3d_so_desc_entry *s = WINE_RB_ENTRY_VALUE(entry, struct wined3d_so_desc_entry, entry); + + heap_free(s); +} + +static void device_leftover_sampler(struct wine_rb_entry *entry, void *context) +{ + struct wined3d_sampler *sampler = WINE_RB_ENTRY_VALUE(entry, struct wined3d_sampler, entry); + + ERR("Leftover sampler %p.\n", sampler); +} + +static void device_leftover_rasterizer_state(struct wine_rb_entry *entry, void *context) +{ + struct wined3d_rasterizer_state *state = WINE_RB_ENTRY_VALUE(entry, struct wined3d_rasterizer_state, entry); + + ERR("Leftover rasterizer state %p.\n", state); +} + +static void device_leftover_blend_state(struct wine_rb_entry *entry, void *context) +{ + struct wined3d_blend_state *blend_state = WINE_RB_ENTRY_VALUE(entry, struct wined3d_blend_state, entry); + + ERR("Leftover blend state %p.\n", blend_state); +} + +static void device_leftover_depth_stencil_state(struct wine_rb_entry *entry, void *context) +{ + struct wined3d_depth_stencil_state *state = WINE_RB_ENTRY_VALUE(entry, struct wined3d_depth_stencil_state, entry); + + ERR("Leftover depth/stencil state %p.\n", state); +} + +void wined3d_device_cleanup(struct wined3d_device *device) +{ + unsigned int i; + + if (device->swapchain_count) + wined3d_device_uninit_3d(device); + + wined3d_cs_destroy(device->cs); + + for (i = 0; i < ARRAY_SIZE(device->multistate_funcs); ++i) + { + heap_free(device->multistate_funcs[i]); + device->multistate_funcs[i] = NULL; + } + + if (!list_empty(&device->resources)) + { + struct wined3d_resource *resource; + + ERR("Device released with resources still bound.\n"); + + LIST_FOR_EACH_ENTRY(resource, &device->resources, struct wined3d_resource, resource_list_entry) + { + ERR("Leftover resource %p with type %s (%#x).\n", + resource, debug_d3dresourcetype(resource->type), resource->type); + } + } + + if (device->contexts) + ERR("Context array not freed!\n"); + if (device->hardwareCursor) + DestroyCursor(device->hardwareCursor); + device->hardwareCursor = 0; + + wine_rb_destroy(&device->samplers, device_leftover_sampler, NULL); + wine_rb_destroy(&device->rasterizer_states, device_leftover_rasterizer_state, NULL); + wine_rb_destroy(&device->blend_states, device_leftover_blend_state, NULL); + wine_rb_destroy(&device->depth_stencil_states, device_leftover_depth_stencil_state, NULL); + wine_rb_destroy(&device->so_descs, device_free_so_desc, NULL); + + wined3d_decref(device->wined3d); + device->wined3d = NULL; +} + +ULONG CDECL wined3d_device_decref(struct wined3d_device *device) +{ + ULONG refcount = InterlockedDecrement(&device->ref); + + TRACE("%p decreasing refcount to %u.\n", device, refcount); + + if (!refcount) + { + device->adapter->adapter_ops->adapter_destroy_device(device); + TRACE("Destroyed device %p.\n", device); + } + + return refcount; +} + +UINT CDECL wined3d_device_get_swapchain_count(const struct wined3d_device *device) +{ + TRACE("device %p.\n", device); + + return device->swapchain_count; +} + +struct wined3d_swapchain * CDECL wined3d_device_get_swapchain(const struct wined3d_device *device, UINT swapchain_idx) +{ + TRACE("device %p, swapchain_idx %u.\n", device, swapchain_idx); + + if (swapchain_idx >= device->swapchain_count) + { + WARN("swapchain_idx %u >= swapchain_count %u.\n", + swapchain_idx, device->swapchain_count); + return NULL; + } + + return device->swapchains[swapchain_idx]; +} + +static void device_load_logo(struct wined3d_device *device, const char *filename) +{ + struct wined3d_color_key color_key; + struct wined3d_resource_desc desc; + HBITMAP hbm; + BITMAP bm; + HRESULT hr; + HDC dcb = NULL, dcs = NULL; + + if (!(hbm = LoadImageA(NULL, filename, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_CREATEDIBSECTION))) + { + ERR_(winediag)("Failed to load logo %s.\n", wine_dbgstr_a(filename)); + return; + } + GetObjectA(hbm, sizeof(BITMAP), &bm); + + if (!(dcb = CreateCompatibleDC(NULL))) + goto out; + SelectObject(dcb, hbm); + + desc.resource_type = WINED3D_RTYPE_TEXTURE_2D; + desc.format = WINED3DFMT_B5G6R5_UNORM; + desc.multisample_type = WINED3D_MULTISAMPLE_NONE; + desc.multisample_quality = 0; + desc.usage = WINED3DUSAGE_DYNAMIC; + desc.bind_flags = 0; + desc.access = WINED3D_RESOURCE_ACCESS_GPU; + desc.width = bm.bmWidth; + desc.height = bm.bmHeight; + desc.depth = 1; + desc.size = 0; + if (FAILED(hr = wined3d_texture_create(device, &desc, 1, 1, WINED3D_TEXTURE_CREATE_GET_DC, + NULL, NULL, &wined3d_null_parent_ops, &device->logo_texture))) + { + ERR("Wine logo requested, but failed to create texture, hr %#x.\n", hr); + goto out; + } + + if (FAILED(hr = wined3d_texture_get_dc(device->logo_texture, 0, &dcs))) + { + wined3d_texture_decref(device->logo_texture); + device->logo_texture = NULL; + goto out; + } + BitBlt(dcs, 0, 0, bm.bmWidth, bm.bmHeight, dcb, 0, 0, SRCCOPY); + wined3d_texture_release_dc(device->logo_texture, 0, dcs); + + color_key.color_space_low_value = 0; + color_key.color_space_high_value = 0; + wined3d_texture_set_color_key(device->logo_texture, WINED3D_CKEY_SRC_BLT, &color_key); + +out: + if (dcb) DeleteDC(dcb); + if (hbm) DeleteObject(hbm); +} + +/* Context activation is done by the caller. */ +static void wined3d_device_gl_create_dummy_textures(struct wined3d_device_gl *device_gl, + struct wined3d_context_gl *context_gl) +{ + struct wined3d_dummy_textures *textures = &device_gl->dummy_textures; + const struct wined3d_d3d_info *d3d_info = context_gl->c.d3d_info; + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + unsigned int i; + DWORD color; + + if (d3d_info->wined3d_creation_flags & WINED3D_LEGACY_UNBOUND_RESOURCE_COLOR) + color = 0x000000ff; + else + color = 0x00000000; + + /* Under DirectX you can sample even if no texture is bound, whereas + * OpenGL will only allow that when a valid texture is bound. + * We emulate this by creating dummy textures and binding them + * to each texture stage when the currently set D3D texture is NULL. */ + wined3d_context_gl_active_texture(context_gl, gl_info, 0); + + gl_info->gl_ops.gl.p_glGenTextures(1, &textures->tex_1d); + TRACE("Dummy 1D texture given name %u.\n", textures->tex_1d); + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_1D, textures->tex_1d); + gl_info->gl_ops.gl.p_glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA8, 1, 0, + GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, &color); + + gl_info->gl_ops.gl.p_glGenTextures(1, &textures->tex_2d); + TRACE("Dummy 2D texture given name %u.\n", textures->tex_2d); + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_2D, textures->tex_2d); + gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, + GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, &color); + + if (gl_info->supported[ARB_TEXTURE_RECTANGLE]) + { + gl_info->gl_ops.gl.p_glGenTextures(1, &textures->tex_rect); + TRACE("Dummy rectangle texture given name %u.\n", textures->tex_rect); + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_RECTANGLE_ARB, textures->tex_rect); + gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8, 1, 1, 0, + GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, &color); + } + + if (gl_info->supported[EXT_TEXTURE3D]) + { + gl_info->gl_ops.gl.p_glGenTextures(1, &textures->tex_3d); + TRACE("Dummy 3D texture given name %u.\n", textures->tex_3d); + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_3D, textures->tex_3d); + GL_EXTCALL(glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA8, 1, 1, 1, 0, + GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, &color)); + } + + if (gl_info->supported[ARB_TEXTURE_CUBE_MAP]) + { + gl_info->gl_ops.gl.p_glGenTextures(1, &textures->tex_cube); + TRACE("Dummy cube texture given name %u.\n", textures->tex_cube); + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_CUBE_MAP, textures->tex_cube); + for (i = GL_TEXTURE_CUBE_MAP_POSITIVE_X; i <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z; ++i) + { + gl_info->gl_ops.gl.p_glTexImage2D(i, 0, GL_RGBA8, 1, 1, 0, + GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, &color); + } + } + + if (gl_info->supported[ARB_TEXTURE_CUBE_MAP_ARRAY]) + { + DWORD cube_array_data[6]; + + gl_info->gl_ops.gl.p_glGenTextures(1, &textures->tex_cube_array); + TRACE("Dummy cube array texture given name %u.\n", textures->tex_cube_array); + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_CUBE_MAP_ARRAY, textures->tex_cube_array); + for (i = 0; i < ARRAY_SIZE(cube_array_data); ++i) + cube_array_data[i] = color; + GL_EXTCALL(glTexImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, 0, GL_RGBA8, 1, 1, 6, 0, + GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, cube_array_data)); + } + + if (gl_info->supported[EXT_TEXTURE_ARRAY]) + { + gl_info->gl_ops.gl.p_glGenTextures(1, &textures->tex_1d_array); + TRACE("Dummy 1D array texture given name %u.\n", textures->tex_1d_array); + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_1D_ARRAY, textures->tex_1d_array); + gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_1D_ARRAY, 0, GL_RGBA8, 1, 1, 0, + GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, &color); + + gl_info->gl_ops.gl.p_glGenTextures(1, &textures->tex_2d_array); + TRACE("Dummy 2D array texture given name %u.\n", textures->tex_2d_array); + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_2D_ARRAY, textures->tex_2d_array); + GL_EXTCALL(glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, 1, 1, 1, 0, + GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, &color)); + } + + if (gl_info->supported[ARB_TEXTURE_BUFFER_OBJECT]) + { + GLuint buffer; + + GL_EXTCALL(glGenBuffers(1, &buffer)); + GL_EXTCALL(glBindBuffer(GL_TEXTURE_BUFFER, buffer)); + GL_EXTCALL(glBufferData(GL_TEXTURE_BUFFER, sizeof(color), &color, GL_STATIC_DRAW)); + GL_EXTCALL(glBindBuffer(GL_TEXTURE_BUFFER, 0)); + + gl_info->gl_ops.gl.p_glGenTextures(1, &textures->tex_buffer); + TRACE("Dummy buffer texture given name %u.\n", textures->tex_buffer); + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_BUFFER, textures->tex_buffer); + GL_EXTCALL(glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA8, buffer)); + GL_EXTCALL(glDeleteBuffers(1, &buffer)); + } + + if (gl_info->supported[ARB_TEXTURE_MULTISAMPLE]) + { + gl_info->gl_ops.gl.p_glGenTextures(1, &textures->tex_2d_ms); + TRACE("Dummy multisample texture given name %u.\n", textures->tex_2d_ms); + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, textures->tex_2d_ms); + GL_EXTCALL(glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 1, GL_RGBA8, 1, 1, GL_TRUE)); + + gl_info->gl_ops.gl.p_glGenTextures(1, &textures->tex_2d_ms_array); + TRACE("Dummy multisample array texture given name %u.\n", textures->tex_2d_ms_array); + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, textures->tex_2d_ms_array); + GL_EXTCALL(glTexImage3DMultisample(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, 1, GL_RGBA8, 1, 1, 1, GL_TRUE)); + + if (gl_info->supported[ARB_CLEAR_TEXTURE]) + { + GL_EXTCALL(glClearTexImage(textures->tex_2d_ms, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, &color)); + GL_EXTCALL(glClearTexImage(textures->tex_2d_ms_array, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, &color)); + } + else + { + WARN("ARB_clear_texture is currently required to clear dummy multisample textures.\n"); + } + } + + checkGLcall("create dummy textures"); + + wined3d_context_gl_bind_dummy_textures(context_gl); +} + +/* Context activation is done by the caller. */ +static void wined3d_device_gl_destroy_dummy_textures(struct wined3d_device_gl *device_gl, + struct wined3d_context_gl *context_gl) +{ + struct wined3d_dummy_textures *dummy_textures = &device_gl->dummy_textures; + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + + if (gl_info->supported[ARB_TEXTURE_MULTISAMPLE]) + { + gl_info->gl_ops.gl.p_glDeleteTextures(1, &dummy_textures->tex_2d_ms); + gl_info->gl_ops.gl.p_glDeleteTextures(1, &dummy_textures->tex_2d_ms_array); + } + + if (gl_info->supported[ARB_TEXTURE_BUFFER_OBJECT]) + gl_info->gl_ops.gl.p_glDeleteTextures(1, &dummy_textures->tex_buffer); + + if (gl_info->supported[EXT_TEXTURE_ARRAY]) + { + gl_info->gl_ops.gl.p_glDeleteTextures(1, &dummy_textures->tex_2d_array); + gl_info->gl_ops.gl.p_glDeleteTextures(1, &dummy_textures->tex_1d_array); + } + + if (gl_info->supported[ARB_TEXTURE_CUBE_MAP_ARRAY]) + gl_info->gl_ops.gl.p_glDeleteTextures(1, &dummy_textures->tex_cube_array); + + if (gl_info->supported[ARB_TEXTURE_CUBE_MAP]) + gl_info->gl_ops.gl.p_glDeleteTextures(1, &dummy_textures->tex_cube); + + if (gl_info->supported[EXT_TEXTURE3D]) + gl_info->gl_ops.gl.p_glDeleteTextures(1, &dummy_textures->tex_3d); + + if (gl_info->supported[ARB_TEXTURE_RECTANGLE]) + gl_info->gl_ops.gl.p_glDeleteTextures(1, &dummy_textures->tex_rect); + + gl_info->gl_ops.gl.p_glDeleteTextures(1, &dummy_textures->tex_2d); + gl_info->gl_ops.gl.p_glDeleteTextures(1, &dummy_textures->tex_1d); + + checkGLcall("delete dummy textures"); + + memset(dummy_textures, 0, sizeof(*dummy_textures)); +} + +/* Context activation is done by the caller. */ +void wined3d_device_create_default_samplers(struct wined3d_device *device, struct wined3d_context *context) +{ + struct wined3d_sampler_desc desc; + HRESULT hr; + + desc.address_u = WINED3D_TADDRESS_WRAP; + desc.address_v = WINED3D_TADDRESS_WRAP; + desc.address_w = WINED3D_TADDRESS_WRAP; + memset(desc.border_color, 0, sizeof(desc.border_color)); + desc.mag_filter = WINED3D_TEXF_POINT; + desc.min_filter = WINED3D_TEXF_POINT; + desc.mip_filter = WINED3D_TEXF_NONE; + desc.lod_bias = 0.0f; + desc.min_lod = -1000.0f; + desc.max_lod = 1000.0f; + desc.mip_base_level = 0; + desc.max_anisotropy = 1; + desc.compare = FALSE; + desc.comparison_func = WINED3D_CMP_NEVER; + desc.srgb_decode = TRUE; + + /* In SM4+ shaders there is a separation between resources and samplers. Some shader + * instructions allow access to resources without using samplers. + * In GLSL, resources are always accessed through sampler or image variables. The default + * sampler object is used to emulate the direct resource access when there is no sampler state + * to use. + */ + if (FAILED(hr = wined3d_sampler_create(device, &desc, NULL, &wined3d_null_parent_ops, &device->default_sampler))) + { + ERR("Failed to create default sampler, hr %#x.\n", hr); + device->default_sampler = NULL; + } + + /* In D3D10+, a NULL sampler maps to the default sampler state. */ + desc.address_u = WINED3D_TADDRESS_CLAMP; + desc.address_v = WINED3D_TADDRESS_CLAMP; + desc.address_w = WINED3D_TADDRESS_CLAMP; + desc.mag_filter = WINED3D_TEXF_LINEAR; + desc.min_filter = WINED3D_TEXF_LINEAR; + desc.mip_filter = WINED3D_TEXF_LINEAR; + if (FAILED(hr = wined3d_sampler_create(device, &desc, NULL, &wined3d_null_parent_ops, &device->null_sampler))) + { + ERR("Failed to create null sampler, hr %#x.\n", hr); + device->null_sampler = NULL; + } +} + +/* Context activation is done by the caller. */ +void wined3d_device_destroy_default_samplers(struct wined3d_device *device, struct wined3d_context *context) +{ + wined3d_sampler_decref(device->default_sampler); + device->default_sampler = NULL; + wined3d_sampler_decref(device->null_sampler); + device->null_sampler = NULL; +} + +static void wined3d_null_image_vk_cleanup(struct wined3d_null_image_vk *image, + struct wined3d_context_vk *context_vk, uint64_t command_buffer_id) +{ + wined3d_context_vk_destroy_image(context_vk, image->vk_image, command_buffer_id); + if (image->memory) + wined3d_context_vk_destroy_allocator_block(context_vk, image->memory, command_buffer_id); + else + wined3d_context_vk_destroy_memory(context_vk, image->vk_memory, command_buffer_id); +} + +static bool wined3d_null_image_vk_init(struct wined3d_null_image_vk *image, struct wined3d_context_vk *context_vk, + VkCommandBuffer vk_command_buffer, VkImageType type, unsigned int layer_count, unsigned int sample_count) +{ + struct wined3d_device_vk *device_vk = wined3d_device_vk(context_vk->c.device); + const struct wined3d_vk_info *vk_info = context_vk->vk_info; + VkMemoryRequirements memory_requirements; + VkImageSubresourceRange range; + VkImageCreateInfo image_desc; + unsigned int memory_type_idx; + uint32_t flags = 0; + VkResult vr; + + static const VkClearColorValue colour = {{0}}; + + TRACE("image %p, context_vk %p, vk_command_buffer %p, type %#x, layer_count %u, sample_count %u.\n", + image, context_vk, vk_command_buffer, type, layer_count, sample_count); + + if (type == VK_IMAGE_TYPE_2D && layer_count >= 6) + flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT; + + image_desc.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + image_desc.pNext = NULL; + image_desc.flags = flags; + image_desc.imageType = type; + image_desc.format = VK_FORMAT_R8G8B8A8_UNORM; + image_desc.extent.width = 1; + image_desc.extent.height = 1; + image_desc.extent.depth = 1; + image_desc.mipLevels = 1; + image_desc.arrayLayers = layer_count; + image_desc.samples = sample_count; + image_desc.tiling = VK_IMAGE_TILING_OPTIMAL; + image_desc.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; + image_desc.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + image_desc.queueFamilyIndexCount = 0; + image_desc.pQueueFamilyIndices = NULL; + image_desc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + if ((vr = VK_CALL(vkCreateImage(device_vk->vk_device, &image_desc, NULL, &image->vk_image))) < 0) + { + ERR("Failed to create Vulkan image, vr %s.\n", wined3d_debug_vkresult(vr)); + return false; + } + + VK_CALL(vkGetImageMemoryRequirements(device_vk->vk_device, image->vk_image, &memory_requirements)); + + memory_type_idx = wined3d_adapter_vk_get_memory_type_index(wined3d_adapter_vk(device_vk->d.adapter), + memory_requirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + if (memory_type_idx == ~0u) + { + ERR("Failed to find suitable image memory type.\n"); + VK_CALL(vkDestroyImage(device_vk->vk_device, image->vk_image, NULL)); + image->vk_image = VK_NULL_HANDLE; + return false; + } + + image->memory = wined3d_context_vk_allocate_memory(context_vk, + memory_type_idx, memory_requirements.size, &image->vk_memory); + if (!image->vk_memory) + { + ERR("Failed to allocate image memory.\n"); + VK_CALL(vkDestroyImage(device_vk->vk_device, image->vk_image, NULL)); + image->vk_image = VK_NULL_HANDLE; + return false; + } + + if ((vr = VK_CALL(vkBindImageMemory(device_vk->vk_device, image->vk_image, + image->vk_memory, image->memory ? image->memory->offset : 0))) < 0) + { + ERR("Failed to bind image memory, vr %s.\n", wined3d_debug_vkresult(vr)); + if (image->memory) + wined3d_allocator_block_free(image->memory); + else + VK_CALL(vkFreeMemory(device_vk->vk_device, image->vk_memory, NULL)); + VK_CALL(vkDestroyImage(device_vk->vk_device, image->vk_image, NULL)); + image->vk_image = VK_NULL_HANDLE; + return false; + } + + wined3d_context_vk_image_barrier(context_vk, vk_command_buffer, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, VK_ACCESS_TRANSFER_WRITE_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + image->vk_image, VK_IMAGE_ASPECT_COLOR_BIT); + + range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + range.baseMipLevel = 0; + range.levelCount = 1; + range.baseArrayLayer = 0; + range.layerCount = layer_count; + VK_CALL(vkCmdClearColorImage(vk_command_buffer, image->vk_image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &colour, 1, &range)); + + wined3d_context_vk_image_barrier(context_vk, vk_command_buffer, + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, 0, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + image->vk_image, VK_IMAGE_ASPECT_COLOR_BIT); + + TRACE("Created NULL image 0x%s, memory 0x%s.\n", + wine_dbgstr_longlong(image->vk_image), wine_dbgstr_longlong(image->vk_memory)); + + return true; +} + +bool wined3d_device_vk_create_null_resources(struct wined3d_device_vk *device_vk, + struct wined3d_context_vk *context_vk) +{ + struct wined3d_null_resources_vk *r = &device_vk->null_resources_vk; + const struct wined3d_vk_info *vk_info; + const struct wined3d_format *format; + VkMemoryPropertyFlags memory_type; + VkCommandBuffer vk_command_buffer; + unsigned int sample_count = 2; + VkBufferUsageFlags usage; + uint64_t id; + + format = wined3d_get_format(device_vk->d.adapter, WINED3DFMT_R8G8B8A8_UNORM, WINED3D_BIND_SHADER_RESOURCE); + while (sample_count && !(sample_count & format->multisample_types)) + sample_count <<= 1; + + if (!(vk_command_buffer = wined3d_context_vk_get_command_buffer(context_vk))) + { + ERR("Failed to get command buffer.\n"); + return false; + } + + vk_info = context_vk->vk_info; + + usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT + | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; + memory_type = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + if (!wined3d_context_vk_create_bo(context_vk, 16, usage, memory_type, &r->bo)) + return false; + VK_CALL(vkCmdFillBuffer(vk_command_buffer, r->bo.vk_buffer, r->bo.buffer_offset, r->bo.size, 0x00000000u)); + r->buffer_info.buffer = r->bo.vk_buffer; + r->buffer_info.offset = r->bo.buffer_offset; + r->buffer_info.range = r->bo.size; + + if (!wined3d_null_image_vk_init(&r->image_1d, context_vk, vk_command_buffer, VK_IMAGE_TYPE_1D, 1, 1)) + { + ERR("Failed to create 1D image.\n"); + goto fail; + } + + if (!wined3d_null_image_vk_init(&r->image_2d, context_vk, vk_command_buffer, VK_IMAGE_TYPE_2D, 6, 1)) + { + ERR("Failed to create 2D image.\n"); + goto fail; + } + + if (!wined3d_null_image_vk_init(&r->image_2dms, context_vk, vk_command_buffer, VK_IMAGE_TYPE_2D, 1, sample_count)) + { + ERR("Failed to create 2D MSAA image.\n"); + goto fail; + } + + if (!wined3d_null_image_vk_init(&r->image_3d, context_vk, vk_command_buffer, VK_IMAGE_TYPE_3D, 1, 1)) + { + ERR("Failed to create 3D image.\n"); + goto fail; + } + + return true; + +fail: + id = context_vk->current_command_buffer.id; + if (r->image_2dms.vk_image) + wined3d_null_image_vk_cleanup(&r->image_2dms, context_vk, id); + if (r->image_2d.vk_image) + wined3d_null_image_vk_cleanup(&r->image_2d, context_vk, id); + if (r->image_1d.vk_image) + wined3d_null_image_vk_cleanup(&r->image_1d, context_vk, id); + wined3d_context_vk_reference_bo(context_vk, &r->bo); + wined3d_context_vk_destroy_bo(context_vk, &r->bo); + return false; +} + +void wined3d_device_vk_destroy_null_resources(struct wined3d_device_vk *device_vk, + struct wined3d_context_vk *context_vk) +{ + struct wined3d_null_resources_vk *r = &device_vk->null_resources_vk; + uint64_t id = context_vk->current_command_buffer.id; + + /* We don't track command buffer references to NULL resources. We easily + * could, but it doesn't seem worth it. */ + wined3d_null_image_vk_cleanup(&r->image_3d, context_vk, id); + wined3d_null_image_vk_cleanup(&r->image_2dms, context_vk, id); + wined3d_null_image_vk_cleanup(&r->image_2d, context_vk, id); + wined3d_null_image_vk_cleanup(&r->image_1d, context_vk, id); + wined3d_context_vk_reference_bo(context_vk, &r->bo); + wined3d_context_vk_destroy_bo(context_vk, &r->bo); +} + +bool wined3d_device_vk_create_null_views(struct wined3d_device_vk *device_vk, struct wined3d_context_vk *context_vk) +{ + struct wined3d_null_resources_vk *r = &device_vk->null_resources_vk; + struct wined3d_null_views_vk *v = &device_vk->null_views_vk; + VkBufferViewCreateInfo buffer_create_info; + const struct wined3d_vk_info *vk_info; + VkImageViewCreateInfo view_desc; + VkResult vr; + + vk_info = context_vk->vk_info; + + buffer_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO; + buffer_create_info.pNext = NULL; + buffer_create_info.flags = 0; + buffer_create_info.buffer = r->bo.vk_buffer; + buffer_create_info.format = VK_FORMAT_R32_UINT; + buffer_create_info.offset = r->bo.buffer_offset; + buffer_create_info.range = r->bo.size; + + if ((vr = VK_CALL(vkCreateBufferView(device_vk->vk_device, + &buffer_create_info, NULL, &v->vk_view_buffer_uint))) < 0) + { + ERR("Failed to create buffer view, vr %s.\n", wined3d_debug_vkresult(vr)); + return false; + } + TRACE("Created buffer view 0x%s.\n", wine_dbgstr_longlong(v->vk_view_buffer_uint)); + + buffer_create_info.format = VK_FORMAT_R32G32B32A32_SFLOAT; + if ((vr = VK_CALL(vkCreateBufferView(device_vk->vk_device, + &buffer_create_info, NULL, &v->vk_view_buffer_float))) < 0) + { + ERR("Failed to create buffer view, vr %s.\n", wined3d_debug_vkresult(vr)); + goto fail; + } + TRACE("Created buffer view 0x%s.\n", wine_dbgstr_longlong(v->vk_view_buffer_float)); + + view_desc.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + view_desc.pNext = NULL; + view_desc.flags = 0; + view_desc.image = r->image_1d.vk_image; + view_desc.viewType = VK_IMAGE_VIEW_TYPE_1D; + view_desc.format = VK_FORMAT_R8G8B8A8_UNORM; + view_desc.components.r = VK_COMPONENT_SWIZZLE_ZERO; + view_desc.components.g = VK_COMPONENT_SWIZZLE_ZERO; + view_desc.components.b = VK_COMPONENT_SWIZZLE_ZERO; + view_desc.components.a = VK_COMPONENT_SWIZZLE_ZERO; + view_desc.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + view_desc.subresourceRange.baseMipLevel = 0; + view_desc.subresourceRange.levelCount = 1; + view_desc.subresourceRange.baseArrayLayer = 0; + view_desc.subresourceRange.layerCount = 1; + if ((vr = VK_CALL(vkCreateImageView(device_vk->vk_device, &view_desc, NULL, &v->vk_info_1d.imageView))) < 0) + { + ERR("Failed to create 1D image view, vr %s.\n", wined3d_debug_vkresult(vr)); + goto fail; + } + v->vk_info_1d.sampler = VK_NULL_HANDLE; + v->vk_info_1d.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + TRACE("Created 1D image view 0x%s.\n", wine_dbgstr_longlong(v->vk_info_1d.imageView)); + + view_desc.image = r->image_2d.vk_image; + view_desc.viewType = VK_IMAGE_VIEW_TYPE_2D; + if ((vr = VK_CALL(vkCreateImageView(device_vk->vk_device, &view_desc, NULL, &v->vk_info_2d.imageView))) < 0) + { + ERR("Failed to create 2D image view, vr %s.\n", wined3d_debug_vkresult(vr)); + goto fail; + } + v->vk_info_2d.sampler = VK_NULL_HANDLE; + v->vk_info_2d.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + TRACE("Created 2D image view 0x%s.\n", wine_dbgstr_longlong(v->vk_info_2d.imageView)); + + view_desc.image = r->image_2dms.vk_image; + view_desc.viewType = VK_IMAGE_VIEW_TYPE_2D; + if ((vr = VK_CALL(vkCreateImageView(device_vk->vk_device, &view_desc, NULL, &v->vk_info_2dms.imageView))) < 0) + { + ERR("Failed to create 2D MSAA image view, vr %s.\n", wined3d_debug_vkresult(vr)); + goto fail; + } + v->vk_info_2dms.sampler = VK_NULL_HANDLE; + v->vk_info_2dms.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + TRACE("Created 2D MSAA image view 0x%s.\n", wine_dbgstr_longlong(v->vk_info_2dms.imageView)); + + view_desc.image = r->image_3d.vk_image; + view_desc.viewType = VK_IMAGE_VIEW_TYPE_3D; + if ((vr = VK_CALL(vkCreateImageView(device_vk->vk_device, &view_desc, NULL, &v->vk_info_3d.imageView))) < 0) + { + ERR("Failed to create 3D image view, vr %s.\n", wined3d_debug_vkresult(vr)); + goto fail; + } + v->vk_info_3d.sampler = VK_NULL_HANDLE; + v->vk_info_3d.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + TRACE("Created 3D image view 0x%s.\n", wine_dbgstr_longlong(v->vk_info_3d.imageView)); + + view_desc.image = r->image_2d.vk_image; + view_desc.subresourceRange.layerCount = 6; + view_desc.viewType = VK_IMAGE_VIEW_TYPE_CUBE; + if ((vr = VK_CALL(vkCreateImageView(device_vk->vk_device, &view_desc, NULL, &v->vk_info_cube.imageView))) < 0) + { + ERR("Failed to create cube image view, vr %s.\n", wined3d_debug_vkresult(vr)); + goto fail; + } + v->vk_info_cube.sampler = VK_NULL_HANDLE; + v->vk_info_cube.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + TRACE("Created cube image view 0x%s.\n", wine_dbgstr_longlong(v->vk_info_cube.imageView)); + + view_desc.subresourceRange.layerCount = 1; + view_desc.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY; + if ((vr = VK_CALL(vkCreateImageView(device_vk->vk_device, &view_desc, NULL, &v->vk_info_2d_array.imageView))) < 0) + { + ERR("Failed to create 2D array image view, vr %s.\n", wined3d_debug_vkresult(vr)); + goto fail; + } + v->vk_info_2d_array.sampler = VK_NULL_HANDLE; + v->vk_info_2d_array.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + TRACE("Created 2D array image view 0x%s.\n", wine_dbgstr_longlong(v->vk_info_2d_array.imageView)); + + view_desc.image = r->image_2dms.vk_image; + view_desc.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY; + if ((vr = VK_CALL(vkCreateImageView(device_vk->vk_device, &view_desc, NULL, &v->vk_info_2dms_array.imageView))) < 0) + { + ERR("Failed to create 2D MSAA array image view, vr %s.\n", wined3d_debug_vkresult(vr)); + goto fail; + } + v->vk_info_2dms_array.sampler = VK_NULL_HANDLE; + v->vk_info_2dms_array.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + TRACE("Created 2D MSAA array image view 0x%s.\n", wine_dbgstr_longlong(v->vk_info_2dms_array.imageView)); + + return true; + +fail: + if (v->vk_info_2d_array.imageView) + VK_CALL(vkDestroyImageView(device_vk->vk_device, v->vk_info_2d_array.imageView, NULL)); + if (v->vk_info_cube.imageView) + VK_CALL(vkDestroyImageView(device_vk->vk_device, v->vk_info_cube.imageView, NULL)); + if (v->vk_info_3d.imageView) + VK_CALL(vkDestroyImageView(device_vk->vk_device, v->vk_info_3d.imageView, NULL)); + if (v->vk_info_2dms.imageView) + VK_CALL(vkDestroyImageView(device_vk->vk_device, v->vk_info_2dms.imageView, NULL)); + if (v->vk_info_2d.imageView) + VK_CALL(vkDestroyImageView(device_vk->vk_device, v->vk_info_2d.imageView, NULL)); + if (v->vk_info_1d.imageView) + VK_CALL(vkDestroyImageView(device_vk->vk_device, v->vk_info_1d.imageView, NULL)); + if (v->vk_view_buffer_float) + VK_CALL(vkDestroyBufferView(device_vk->vk_device, v->vk_view_buffer_float, NULL)); + VK_CALL(vkDestroyBufferView(device_vk->vk_device, v->vk_view_buffer_uint, NULL)); + return false; +} + +void wined3d_device_vk_destroy_null_views(struct wined3d_device_vk *device_vk, struct wined3d_context_vk *context_vk) +{ + struct wined3d_null_views_vk *v = &device_vk->null_views_vk; + uint64_t id = context_vk->current_command_buffer.id; + + wined3d_context_vk_destroy_image_view(context_vk, v->vk_info_2dms_array.imageView, id); + wined3d_context_vk_destroy_image_view(context_vk, v->vk_info_2d_array.imageView, id); + wined3d_context_vk_destroy_image_view(context_vk, v->vk_info_cube.imageView, id); + wined3d_context_vk_destroy_image_view(context_vk, v->vk_info_3d.imageView, id); + wined3d_context_vk_destroy_image_view(context_vk, v->vk_info_2dms.imageView, id); + wined3d_context_vk_destroy_image_view(context_vk, v->vk_info_2d.imageView, id); + wined3d_context_vk_destroy_image_view(context_vk, v->vk_info_1d.imageView, id); + + wined3d_context_vk_destroy_buffer_view(context_vk, v->vk_view_buffer_float, id); + wined3d_context_vk_destroy_buffer_view(context_vk, v->vk_view_buffer_uint, id); +} + +HRESULT CDECL wined3d_device_acquire_focus_window(struct wined3d_device *device, HWND window) +{ + unsigned int screensaver_active; + + TRACE("device %p, window %p.\n", device, window); + + if (!wined3d_register_window(NULL, window, device, 0)) + { + ERR("Failed to register window %p.\n", window); + return E_FAIL; + } + + InterlockedExchangePointer((void **)&device->focus_window, window); + SetWindowPos(window, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); + SystemParametersInfoW(SPI_GETSCREENSAVEACTIVE, 0, &screensaver_active, 0); + if ((device->restore_screensaver = !!screensaver_active)) + SystemParametersInfoW(SPI_SETSCREENSAVEACTIVE, FALSE, NULL, 0); + + return WINED3D_OK; +} + +void CDECL wined3d_device_release_focus_window(struct wined3d_device *device) +{ + TRACE("device %p.\n", device); + + if (device->focus_window) wined3d_unregister_window(device->focus_window); + InterlockedExchangePointer((void **)&device->focus_window, NULL); + if (device->restore_screensaver) + { + SystemParametersInfoW(SPI_SETSCREENSAVEACTIVE, TRUE, NULL, 0); + device->restore_screensaver = FALSE; + } +} + +static void device_init_swapchain_state(struct wined3d_device *device, struct wined3d_swapchain *swapchain) +{ + BOOL ds_enable = swapchain->state.desc.enable_auto_depth_stencil; + unsigned int i; + + for (i = 0; i < device->adapter->d3d_info.limits.max_rt_count; ++i) + { + wined3d_device_set_rendertarget_view(device, i, NULL, FALSE); + } + if (device->back_buffer_view) + wined3d_device_set_rendertarget_view(device, 0, device->back_buffer_view, TRUE); + + wined3d_device_set_depth_stencil_view(device, ds_enable ? device->auto_depth_stencil_view : NULL); +} + +void wined3d_device_delete_opengl_contexts_cs(void *object) +{ + struct wined3d_swapchain_gl *swapchain_gl; + struct wined3d_device *device = object; + struct wined3d_context_gl *context_gl; + struct wined3d_device_gl *device_gl; + struct wined3d_context *context; + struct wined3d_shader *shader; + + TRACE("device %p.\n", device); + + device_gl = wined3d_device_gl(device); + + LIST_FOR_EACH_ENTRY(shader, &device->shaders, struct wined3d_shader, shader_list_entry) + { + device->shader_backend->shader_destroy(shader); + } + + context = context_acquire(device, NULL, 0); + context_gl = wined3d_context_gl(context); + device->blitter->ops->blitter_destroy(device->blitter, context); + device->shader_backend->shader_free_private(device, context); + wined3d_device_gl_destroy_dummy_textures(device_gl, context_gl); + wined3d_device_destroy_default_samplers(device, context); + context_release(context); + + while (device->context_count) + { + if ((swapchain_gl = wined3d_swapchain_gl(device->contexts[0]->swapchain))) + wined3d_swapchain_gl_destroy_contexts(swapchain_gl); + else + wined3d_context_gl_destroy(wined3d_context_gl(device->contexts[0])); + } +} + +void wined3d_device_create_primary_opengl_context_cs(void *object) +{ + struct wined3d_device *device = object; + struct wined3d_context_gl *context_gl; + struct wined3d_swapchain *swapchain; + struct wined3d_context *context; + struct wined3d_texture *target; + HRESULT hr; + + TRACE("device %p.\n", device); + + swapchain = device->swapchains[0]; + target = swapchain->back_buffers ? swapchain->back_buffers[0] : swapchain->front_buffer; + if (!(context = context_acquire(device, target, 0))) + { + WARN("Failed to acquire context.\n"); + return; + } + + if (FAILED(hr = device->shader_backend->shader_alloc_private(device, + device->adapter->vertex_pipe, device->adapter->fragment_pipe))) + { + ERR("Failed to allocate shader private data, hr %#x.\n", hr); + context_release(context); + return; + } + + if (!(device->blitter = wined3d_cpu_blitter_create())) + { + ERR("Failed to create CPU blitter.\n"); + device->shader_backend->shader_free_private(device, NULL); + context_release(context); + return; + } + + context_gl = wined3d_context_gl(context); + + wined3d_ffp_blitter_create(&device->blitter, context_gl->gl_info); + if (!wined3d_glsl_blitter_create(&device->blitter, device)) + wined3d_arbfp_blitter_create(&device->blitter, device); + wined3d_fbo_blitter_create(&device->blitter, context_gl->gl_info); + wined3d_raw_blitter_create(&device->blitter, context_gl->gl_info); + + wined3d_device_gl_create_dummy_textures(wined3d_device_gl(device), context_gl); + wined3d_device_create_default_samplers(device, context); + context_release(context); +} + +HRESULT wined3d_device_set_implicit_swapchain(struct wined3d_device *device, struct wined3d_swapchain *swapchain) +{ + static const struct wined3d_color black = {0.0f, 0.0f, 0.0f, 0.0f}; + const struct wined3d_swapchain_desc *swapchain_desc; + DWORD clear_flags = 0; + unsigned int i; + HRESULT hr; + + TRACE("device %p, swapchain %p.\n", device, swapchain); + + if (device->d3d_initialized) + return WINED3DERR_INVALIDCALL; + + device->swapchain_count = 1; + if (!(device->swapchains = heap_calloc(device->swapchain_count, sizeof(*device->swapchains)))) + { + ERR("Failed to allocate swapchain array.\n"); + hr = E_OUTOFMEMORY; + goto err_out; + } + device->swapchains[0] = swapchain; + + for (i = 0; i < ARRAY_SIZE(device->state.fb.render_targets); ++i) + if (device->state.fb.render_targets[i]) + wined3d_rtv_bind_count_dec(device->state.fb.render_targets[i]); + + memset(device->state.fb.render_targets, 0, sizeof(device->state.fb.render_targets)); + if (FAILED(hr = device->adapter->adapter_ops->adapter_init_3d(device))) + goto err_out; + device->d3d_initialized = TRUE; + + swapchain_desc = &swapchain->state.desc; + if (swapchain_desc->backbuffer_count && swapchain_desc->backbuffer_bind_flags & WINED3D_BIND_RENDER_TARGET) + { + struct wined3d_resource *back_buffer = &swapchain->back_buffers[0]->resource; + struct wined3d_view_desc view_desc; + + view_desc.format_id = back_buffer->format->id; + view_desc.flags = 0; + view_desc.u.texture.level_idx = 0; + view_desc.u.texture.level_count = 1; + view_desc.u.texture.layer_idx = 0; + view_desc.u.texture.layer_count = 1; + if (FAILED(hr = wined3d_rendertarget_view_create(&view_desc, back_buffer, + NULL, &wined3d_null_parent_ops, &device->back_buffer_view))) + { + ERR("Failed to create rendertarget view, hr %#x.\n", hr); + device->adapter->adapter_ops->adapter_uninit_3d(device); + device->d3d_initialized = FALSE; + goto err_out; + } + } + + device_init_swapchain_state(device, swapchain); + + TRACE("All defaults now set up.\n"); + + /* Clear the screen. */ + if (device->back_buffer_view) + clear_flags |= WINED3DCLEAR_TARGET; + if (swapchain_desc->enable_auto_depth_stencil) + clear_flags |= WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL; + if (clear_flags) + wined3d_device_clear(device, 0, NULL, clear_flags, &black, 1.0f, 0); + + if (wined3d_settings.logo) + device_load_logo(device, wined3d_settings.logo); + + return WINED3D_OK; + +err_out: + heap_free(device->swapchains); + device->swapchains = NULL; + device->swapchain_count = 0; + + return hr; +} + +static void device_free_sampler(struct wine_rb_entry *entry, void *context) +{ + struct wined3d_sampler *sampler = WINE_RB_ENTRY_VALUE(entry, struct wined3d_sampler, entry); + + wined3d_sampler_decref(sampler); +} + +static void device_free_rasterizer_state(struct wine_rb_entry *entry, void *context) +{ + struct wined3d_rasterizer_state *state = WINE_RB_ENTRY_VALUE(entry, struct wined3d_rasterizer_state, entry); + + wined3d_rasterizer_state_decref(state); +} + +static void device_free_blend_state(struct wine_rb_entry *entry, void *context) +{ + struct wined3d_blend_state *blend_state = WINE_RB_ENTRY_VALUE(entry, struct wined3d_blend_state, entry); + + wined3d_blend_state_decref(blend_state); +} + +static void device_free_depth_stencil_state(struct wine_rb_entry *entry, void *context) +{ + struct wined3d_depth_stencil_state *state = WINE_RB_ENTRY_VALUE(entry, struct wined3d_depth_stencil_state, entry); + + wined3d_depth_stencil_state_decref(state); +} + +void wined3d_device_uninit_3d(struct wined3d_device *device) +{ + struct wined3d_resource *resource, *cursor; + struct wined3d_rendertarget_view *view; + struct wined3d_texture *texture; + unsigned int i; + + TRACE("device %p.\n", device); + + if (!device->d3d_initialized) + { + ERR("Called while 3D support was not initialised.\n"); + return; + } + + wined3d_cs_finish(device->cs, WINED3D_CS_QUEUE_DEFAULT); + + device->swapchain_count = 0; + + if ((texture = device->logo_texture)) + { + device->logo_texture = NULL; + wined3d_texture_decref(texture); + } + + if ((texture = device->cursor_texture)) + { + device->cursor_texture = NULL; + wined3d_texture_decref(texture); + } + + wined3d_cs_emit_reset_state(device->cs); + state_cleanup(&device->state); + for (i = 0; i < device->adapter->d3d_info.limits.max_rt_count; ++i) + { + wined3d_device_set_rendertarget_view(device, i, NULL, FALSE); + } + + wine_rb_clear(&device->samplers, device_free_sampler, NULL); + wine_rb_clear(&device->rasterizer_states, device_free_rasterizer_state, NULL); + wine_rb_clear(&device->blend_states, device_free_blend_state, NULL); + wine_rb_clear(&device->depth_stencil_states, device_free_depth_stencil_state, NULL); + + LIST_FOR_EACH_ENTRY_SAFE(resource, cursor, &device->resources, struct wined3d_resource, resource_list_entry) + { + TRACE("Unloading resource %p.\n", resource); + wined3d_cs_emit_unload_resource(device->cs, resource); + } + + device->adapter->adapter_ops->adapter_uninit_3d(device); + device->d3d_initialized = FALSE; + + if ((view = device->state.fb.depth_stencil)) + { + TRACE("Releasing depth/stencil view %p.\n", view); + + device->state.fb.depth_stencil = NULL; + wined3d_rendertarget_view_decref(view); + } + + if ((view = device->auto_depth_stencil_view)) + { + device->auto_depth_stencil_view = NULL; + if (wined3d_rendertarget_view_decref(view)) + ERR("Something's still holding the auto depth/stencil view (%p).\n", view); + } + + if ((view = device->back_buffer_view)) + { + device->back_buffer_view = NULL; + wined3d_rendertarget_view_decref(view); + } + + heap_free(device->swapchains); + device->swapchains = NULL; + + memset(&device->state, 0, sizeof(device->state)); + state_init(&device->state, &device->adapter->d3d_info, WINED3D_STATE_INIT_DEFAULT); +} + +/* Enables thread safety in the wined3d device and its resources. Called by DirectDraw + * from SetCooperativeLevel if DDSCL_MULTITHREADED is specified, and by d3d8/9 from + * CreateDevice if D3DCREATE_MULTITHREADED is passed. + * + * There is no way to deactivate thread safety once it is enabled. + */ +void CDECL wined3d_device_set_multithreaded(struct wined3d_device *device) +{ + TRACE("device %p.\n", device); + + /* For now just store the flag (needed in case of ddraw). */ + device->create_parms.flags |= WINED3DCREATE_MULTITHREADED; +} + +UINT CDECL wined3d_device_get_available_texture_mem(const struct wined3d_device *device) +{ + const struct wined3d_driver_info *driver_info; + + TRACE("device %p.\n", device); + + driver_info = &device->adapter->driver_info; + + TRACE("Emulating 0x%s bytes. 0x%s used, returning 0x%s left.\n", + wine_dbgstr_longlong(driver_info->vram_bytes), + wine_dbgstr_longlong(device->adapter->vram_bytes_used), + wine_dbgstr_longlong(driver_info->vram_bytes - device->adapter->vram_bytes_used)); + + return min(UINT_MAX, driver_info->vram_bytes - device->adapter->vram_bytes_used); +} + +void CDECL wined3d_device_set_stream_output(struct wined3d_device *device, UINT idx, + struct wined3d_buffer *buffer, UINT offset) +{ + struct wined3d_stream_output *stream; + struct wined3d_buffer *prev_buffer; + + TRACE("device %p, idx %u, buffer %p, offset %u.\n", device, idx, buffer, offset); + + if (idx >= WINED3D_MAX_STREAM_OUTPUT_BUFFERS) + { + WARN("Invalid stream output %u.\n", idx); + return; + } + + stream = &device->state.stream_output[idx]; + prev_buffer = stream->buffer; + + if (buffer) + wined3d_buffer_incref(buffer); + stream->buffer = buffer; + stream->offset = offset; + wined3d_cs_emit_set_stream_output(device->cs, idx, buffer, offset); + if (prev_buffer) + wined3d_buffer_decref(prev_buffer); +} + +struct wined3d_buffer * CDECL wined3d_device_get_stream_output(struct wined3d_device *device, + UINT idx, UINT *offset) +{ + TRACE("device %p, idx %u, offset %p.\n", device, idx, offset); + + if (idx >= WINED3D_MAX_STREAM_OUTPUT_BUFFERS) + { + WARN("Invalid stream output %u.\n", idx); + return NULL; + } + + if (offset) + *offset = device->state.stream_output[idx].offset; + return device->state.stream_output[idx].buffer; +} + +HRESULT CDECL wined3d_device_set_stream_source(struct wined3d_device *device, UINT stream_idx, + struct wined3d_buffer *buffer, UINT offset, UINT stride) +{ + struct wined3d_stream_state *stream; + struct wined3d_buffer *prev_buffer; + + TRACE("device %p, stream_idx %u, buffer %p, offset %u, stride %u.\n", + device, stream_idx, buffer, offset, stride); + + if (stream_idx >= WINED3D_MAX_STREAMS) + { + WARN("Stream index %u out of range.\n", stream_idx); + return WINED3DERR_INVALIDCALL; + } + else if (offset & 0x3) + { + WARN("Offset %u is not 4 byte aligned.\n", offset); + return WINED3DERR_INVALIDCALL; + } + + stream = &device->state.streams[stream_idx]; + prev_buffer = stream->buffer; + + if (prev_buffer == buffer + && stream->stride == stride + && stream->offset == offset) + { + TRACE("Application is setting the old values over, nothing to do.\n"); + return WINED3D_OK; + } + + stream->buffer = buffer; + stream->stride = stride; + stream->offset = offset; + if (buffer) + wined3d_buffer_incref(buffer); + wined3d_cs_emit_set_stream_source(device->cs, stream_idx, buffer, offset, stride); + if (prev_buffer) + wined3d_buffer_decref(prev_buffer); + + return WINED3D_OK; +} + +HRESULT CDECL wined3d_device_get_stream_source(const struct wined3d_device *device, + UINT stream_idx, struct wined3d_buffer **buffer, UINT *offset, UINT *stride) +{ + const struct wined3d_stream_state *stream; + + TRACE("device %p, stream_idx %u, buffer %p, offset %p, stride %p.\n", + device, stream_idx, buffer, offset, stride); + + if (stream_idx >= WINED3D_MAX_STREAMS) + { + WARN("Stream index %u out of range.\n", stream_idx); + return WINED3DERR_INVALIDCALL; + } + + stream = &device->state.streams[stream_idx]; + *buffer = stream->buffer; + if (offset) + *offset = stream->offset; + *stride = stream->stride; + + return WINED3D_OK; +} + +static void wined3d_device_set_stream_source_freq(struct wined3d_device *device, UINT stream_idx, UINT divider) +{ + struct wined3d_stream_state *stream; + UINT old_flags, old_freq; + + TRACE("device %p, stream_idx %u, divider %#x.\n", device, stream_idx, divider); + + stream = &device->state.streams[stream_idx]; + old_flags = stream->flags; + old_freq = stream->frequency; + + stream->flags = divider & (WINED3DSTREAMSOURCE_INSTANCEDATA | WINED3DSTREAMSOURCE_INDEXEDDATA); + stream->frequency = divider & 0x7fffff; + if (stream->frequency != old_freq || stream->flags != old_flags) + wined3d_cs_emit_set_stream_source_freq(device->cs, stream_idx, stream->frequency, stream->flags); +} + +static void wined3d_device_set_transform(struct wined3d_device *device, + enum wined3d_transform_state d3dts, const struct wined3d_matrix *matrix) +{ + TRACE("device %p, state %s, matrix %p.\n", + device, debug_d3dtstype(d3dts), matrix); + TRACE("%.8e %.8e %.8e %.8e\n", matrix->_11, matrix->_12, matrix->_13, matrix->_14); + TRACE("%.8e %.8e %.8e %.8e\n", matrix->_21, matrix->_22, matrix->_23, matrix->_24); + TRACE("%.8e %.8e %.8e %.8e\n", matrix->_31, matrix->_32, matrix->_33, matrix->_34); + TRACE("%.8e %.8e %.8e %.8e\n", matrix->_41, matrix->_42, matrix->_43, matrix->_44); + + /* If the new matrix is the same as the current one, + * we cut off any further processing. this seems to be a reasonable + * optimization because as was noticed, some apps (warcraft3 for example) + * tend towards setting the same matrix repeatedly for some reason. + * + * From here on we assume that the new matrix is different, wherever it matters. */ + if (!memcmp(&device->state.transforms[d3dts], matrix, sizeof(*matrix))) + { + TRACE("The application is setting the same matrix over again.\n"); + return; + } + + device->state.transforms[d3dts] = *matrix; + wined3d_cs_emit_set_transform(device->cs, d3dts, matrix); +} + +static void wined3d_device_get_transform(const struct wined3d_device *device, + enum wined3d_transform_state state, struct wined3d_matrix *matrix) +{ + TRACE("device %p, state %s, matrix %p.\n", device, debug_d3dtstype(state), matrix); + + *matrix = device->state.transforms[state]; +} + +/* Note lights are real special cases. Although the device caps state only + * e.g. 8 are supported, you can reference any indexes you want as long as + * that number max are enabled at any one point in time. Therefore since the + * indices can be anything, we need a hashmap of them. However, this causes + * stateblock problems. When capturing the state block, I duplicate the + * hashmap, but when recording, just build a chain pretty much of commands to + * be replayed. */ +static void wined3d_device_set_light(struct wined3d_device *device, + UINT light_idx, const struct wined3d_light *light) +{ + struct wined3d_light_info *object = NULL; + float rho; + + TRACE("device %p, light_idx %u, light %p.\n", device, light_idx, light); + + if (FAILED(wined3d_light_state_set_light(&device->state.light_state, light_idx, light, &object))) + return; + + /* Initialize the object. */ + TRACE("Light %u setting to type %#x, diffuse %s, specular %s, ambient %s, " + "position {%.8e, %.8e, %.8e}, direction {%.8e, %.8e, %.8e}, " + "range %.8e, falloff %.8e, theta %.8e, phi %.8e.\n", + light_idx, light->type, debug_color(&light->diffuse), + debug_color(&light->specular), debug_color(&light->ambient), + light->position.x, light->position.y, light->position.z, + light->direction.x, light->direction.y, light->direction.z, + light->range, light->falloff, light->theta, light->phi); + + switch (light->type) + { + case WINED3D_LIGHT_POINT: + /* Position */ + object->position.x = light->position.x; + object->position.y = light->position.y; + object->position.z = light->position.z; + object->position.w = 1.0f; + object->cutoff = 180.0f; + /* FIXME: Range */ + break; + + case WINED3D_LIGHT_DIRECTIONAL: + /* Direction */ + object->direction.x = -light->direction.x; + object->direction.y = -light->direction.y; + object->direction.z = -light->direction.z; + object->direction.w = 0.0f; + object->exponent = 0.0f; + object->cutoff = 180.0f; + break; + + case WINED3D_LIGHT_SPOT: + /* Position */ + object->position.x = light->position.x; + object->position.y = light->position.y; + object->position.z = light->position.z; + object->position.w = 1.0f; + + /* Direction */ + object->direction.x = light->direction.x; + object->direction.y = light->direction.y; + object->direction.z = light->direction.z; + object->direction.w = 0.0f; + + /* opengl-ish and d3d-ish spot lights use too different models + * for the light "intensity" as a function of the angle towards + * the main light direction, so we only can approximate very + * roughly. However, spot lights are rather rarely used in games + * (if ever used at all). Furthermore if still used, probably + * nobody pays attention to such details. */ + if (!light->falloff) + { + /* Falloff = 0 is easy, because d3d's and opengl's spot light + * equations have the falloff resp. exponent parameter as an + * exponent, so the spot light lighting will always be 1.0 for + * both of them, and we don't have to care for the rest of the + * rather complex calculation. */ + object->exponent = 0.0f; + } + else + { + rho = light->theta + (light->phi - light->theta) / (2 * light->falloff); + if (rho < 0.0001f) + rho = 0.0001f; + object->exponent = -0.3f / logf(cosf(rho / 2)); + } + + if (object->exponent > 128.0f) + object->exponent = 128.0f; + + object->cutoff = (float)(light->phi * 90 / M_PI); + /* FIXME: Range */ + break; + + case WINED3D_LIGHT_PARALLELPOINT: + object->position.x = light->position.x; + object->position.y = light->position.y; + object->position.z = light->position.z; + object->position.w = 1.0f; + break; + + default: + FIXME("Unrecognized light type %#x.\n", light->type); + } + + wined3d_cs_emit_set_light(device->cs, object); +} + +static void wined3d_device_set_light_enable(struct wined3d_device *device, UINT light_idx, BOOL enable) +{ + struct wined3d_light_info *light_info; + + TRACE("device %p, light_idx %u, enable %#x.\n", device, light_idx, enable); + + /* Special case - enabling an undefined light creates one with a strict set of parameters. */ + if (!(light_info = wined3d_light_state_get_light(&device->state.light_state, light_idx))) + { + TRACE("Light enabled requested but light not defined, so defining one!\n"); + wined3d_device_set_light(device, light_idx, &WINED3D_default_light); + + if (!(light_info = wined3d_light_state_get_light(&device->state.light_state, light_idx))) + { + FIXME("Adding default lights has failed dismally\n"); + return; + } + } + + wined3d_light_state_enable_light(&device->state.light_state, &device->adapter->d3d_info, light_info, enable); + wined3d_cs_emit_set_light_enable(device->cs, light_idx, enable); +} + +static HRESULT wined3d_device_set_clip_plane(struct wined3d_device *device, + UINT plane_idx, const struct wined3d_vec4 *plane) +{ + TRACE("device %p, plane_idx %u, plane %p.\n", device, plane_idx, plane); + + if (plane_idx >= device->adapter->d3d_info.limits.max_clip_distances) + { + TRACE("Application has requested clipplane this device doesn't support.\n"); + return WINED3DERR_INVALIDCALL; + } + + if (!memcmp(&device->state.clip_planes[plane_idx], plane, sizeof(*plane))) + { + TRACE("Application is setting old values over, nothing to do.\n"); + return WINED3D_OK; + } + + device->state.clip_planes[plane_idx] = *plane; + + wined3d_cs_emit_set_clip_plane(device->cs, plane_idx, plane); + + return WINED3D_OK; +} + +HRESULT CDECL wined3d_device_set_clip_status(struct wined3d_device *device, + const struct wined3d_clip_status *clip_status) +{ + FIXME("device %p, clip_status %p stub!\n", device, clip_status); + + if (!clip_status) + return WINED3DERR_INVALIDCALL; + + return WINED3D_OK; +} + +HRESULT CDECL wined3d_device_get_clip_status(const struct wined3d_device *device, + struct wined3d_clip_status *clip_status) +{ + FIXME("device %p, clip_status %p stub!\n", device, clip_status); + + if (!clip_status) + return WINED3DERR_INVALIDCALL; + + return WINED3D_OK; +} + +static void wined3d_device_set_material(struct wined3d_device *device, const struct wined3d_material *material) +{ + TRACE("device %p, material %p.\n", device, material); + + device->state.material = *material; + wined3d_cs_emit_set_material(device->cs, material); +} + +void CDECL wined3d_device_set_index_buffer(struct wined3d_device *device, + struct wined3d_buffer *buffer, enum wined3d_format_id format_id, unsigned int offset) +{ + enum wined3d_format_id prev_format; + struct wined3d_buffer *prev_buffer; + unsigned int prev_offset; + + TRACE("device %p, buffer %p, format %s, offset %u.\n", + device, buffer, debug_d3dformat(format_id), offset); + + prev_buffer = device->state.index_buffer; + prev_format = device->state.index_format; + prev_offset = device->state.index_offset; + + if (prev_buffer == buffer && prev_format == format_id && prev_offset == offset) + return; + + if (buffer) + wined3d_buffer_incref(buffer); + device->state.index_buffer = buffer; + device->state.index_format = format_id; + device->state.index_offset = offset; + wined3d_cs_emit_set_index_buffer(device->cs, buffer, format_id, offset); + if (prev_buffer) + wined3d_buffer_decref(prev_buffer); +} + +struct wined3d_buffer * CDECL wined3d_device_get_index_buffer(const struct wined3d_device *device, + enum wined3d_format_id *format, unsigned int *offset) +{ + TRACE("device %p, format %p, offset %p.\n", device, format, offset); + + *format = device->state.index_format; + if (offset) + *offset = device->state.index_offset; + return device->state.index_buffer; +} + +void CDECL wined3d_device_set_base_vertex_index(struct wined3d_device *device, INT base_index) +{ + TRACE("device %p, base_index %d.\n", device, base_index); + + device->state.base_vertex_index = base_index; +} + +void CDECL wined3d_device_set_viewports(struct wined3d_device *device, unsigned int viewport_count, + const struct wined3d_viewport *viewports) +{ + unsigned int i; + + TRACE("device %p, viewport_count %u, viewports %p.\n", device, viewport_count, viewports); + + for (i = 0; i < viewport_count; ++i) + { + TRACE("%u: x %.8e, y %.8e, w %.8e, h %.8e, min_z %.8e, max_z %.8e.\n", i, viewports[i].x, viewports[i].y, + viewports[i].width, viewports[i].height, viewports[i].min_z, viewports[i].max_z); + } + + if (viewport_count) + memcpy(device->state.viewports, viewports, viewport_count * sizeof(*viewports)); + else + memset(device->state.viewports, 0, sizeof(device->state.viewports)); + device->state.viewport_count = viewport_count; + + wined3d_cs_emit_set_viewports(device->cs, viewport_count, viewports); +} + +void CDECL wined3d_device_get_viewports(const struct wined3d_device *device, unsigned int *viewport_count, + struct wined3d_viewport *viewports) +{ + unsigned int count; + + TRACE("device %p, viewport_count %p, viewports %p.\n", device, viewport_count, viewports); + + count = viewport_count ? min(*viewport_count, device->state.viewport_count) : 1; + if (count && viewports) + memcpy(viewports, device->state.viewports, count * sizeof(*viewports)); + if (viewport_count) + *viewport_count = device->state.viewport_count; +} + +static void resolve_depth_buffer(struct wined3d_device *device) +{ + const struct wined3d_state *state = &device->state; + struct wined3d_rendertarget_view *src_view; + struct wined3d_resource *dst_resource; + struct wined3d_texture *dst_texture; + + if (!(dst_texture = state->textures[0])) + return; + dst_resource = &dst_texture->resource; + if (!dst_resource->format->depth_size) + return; + if (!(src_view = state->fb.depth_stencil)) + return; + + wined3d_device_resolve_sub_resource(device, dst_resource, 0, + src_view->resource, src_view->sub_resource_idx, dst_resource->format->id); +} + +void CDECL wined3d_device_set_blend_state(struct wined3d_device *device, + struct wined3d_blend_state *blend_state, const struct wined3d_color *blend_factor, unsigned int sample_mask) +{ + struct wined3d_state *state = &device->state; + struct wined3d_blend_state *prev; + + TRACE("device %p, blend_state %p, blend_factor %s, sample_mask %#x.\n", + device, blend_state, debug_color(blend_factor), sample_mask); + + prev = state->blend_state; + if (prev == blend_state && !memcmp(blend_factor, &state->blend_factor, sizeof(*blend_factor)) + && sample_mask == state->sample_mask) + return; + + if (blend_state) + wined3d_blend_state_incref(blend_state); + state->blend_state = blend_state; + state->blend_factor = *blend_factor; + state->sample_mask = sample_mask; + wined3d_cs_emit_set_blend_state(device->cs, blend_state, blend_factor, sample_mask); + if (prev) + wined3d_blend_state_decref(prev); +} + +struct wined3d_blend_state * CDECL wined3d_device_get_blend_state(const struct wined3d_device *device, + struct wined3d_color *blend_factor, unsigned int *sample_mask) +{ + const struct wined3d_state *state = &device->state; + + TRACE("device %p, blend_factor %p, sample_mask %p.\n", device, blend_factor, sample_mask); + + *blend_factor = state->blend_factor; + *sample_mask = state->sample_mask; + return state->blend_state; +} + +void CDECL wined3d_device_set_depth_stencil_state(struct wined3d_device *device, + struct wined3d_depth_stencil_state *state, unsigned int stencil_ref) +{ + struct wined3d_depth_stencil_state *prev; + + TRACE("device %p, state %p, stencil_ref %u.\n", device, state, stencil_ref); + + prev = device->state.depth_stencil_state; + if (prev == state && device->state.stencil_ref == stencil_ref) + return; + + if (state) + wined3d_depth_stencil_state_incref(state); + device->state.depth_stencil_state = state; + device->state.stencil_ref = stencil_ref; + wined3d_cs_emit_set_depth_stencil_state(device->cs, state, stencil_ref); + if (prev) + wined3d_depth_stencil_state_decref(prev); +} + +struct wined3d_depth_stencil_state * CDECL wined3d_device_get_depth_stencil_state(const struct wined3d_device *device, unsigned int *stencil_ref) +{ + TRACE("device %p, stencil_ref %p.\n", device, stencil_ref); + + *stencil_ref = device->state.stencil_ref; + return device->state.depth_stencil_state; +} + +void CDECL wined3d_device_set_rasterizer_state(struct wined3d_device *device, + struct wined3d_rasterizer_state *rasterizer_state) +{ + struct wined3d_rasterizer_state *prev; + + TRACE("device %p, rasterizer_state %p.\n", device, rasterizer_state); + + prev = device->state.rasterizer_state; + if (prev == rasterizer_state) + return; + + if (rasterizer_state) + wined3d_rasterizer_state_incref(rasterizer_state); + device->state.rasterizer_state = rasterizer_state; + wined3d_cs_emit_set_rasterizer_state(device->cs, rasterizer_state); + if (prev) + wined3d_rasterizer_state_decref(prev); +} + +struct wined3d_rasterizer_state * CDECL wined3d_device_get_rasterizer_state(struct wined3d_device *device) +{ + TRACE("device %p.\n", device); + + return device->state.rasterizer_state; +} + +void CDECL wined3d_device_set_render_state(struct wined3d_device *device, + enum wined3d_render_state state, DWORD value) +{ + TRACE("device %p, state %s (%#x), value %#x.\n", device, debug_d3drenderstate(state), state, value); + + if (state > WINEHIGHEST_RENDER_STATE) + { + WARN("Unhandled render state %#x.\n", state); + return; + } + + if (value == device->state.render_states[state]) + TRACE("Application is setting the old value over, nothing to do.\n"); + else + { + device->state.render_states[state] = value; + wined3d_cs_emit_set_render_state(device->cs, state, value); + } + + if (state == WINED3D_RS_POINTSIZE && value == WINED3D_RESZ_CODE) + { + TRACE("RESZ multisampled depth buffer resolve triggered.\n"); + resolve_depth_buffer(device); + } +} + +DWORD CDECL wined3d_device_get_render_state(const struct wined3d_device *device, enum wined3d_render_state state) +{ + TRACE("device %p, state %s (%#x).\n", device, debug_d3drenderstate(state), state); + + return device->state.render_states[state]; +} + +static void wined3d_device_set_sampler_state(struct wined3d_device *device, + UINT sampler_idx, enum wined3d_sampler_state state, DWORD value) +{ + TRACE("device %p, sampler_idx %u, state %s, value %#x.\n", + device, sampler_idx, debug_d3dsamplerstate(state), value); + + if (value == device->state.sampler_states[sampler_idx][state]) + { + TRACE("Application is setting the old value over, nothing to do.\n"); + return; + } + + device->state.sampler_states[sampler_idx][state] = value; + wined3d_cs_emit_set_sampler_state(device->cs, sampler_idx, state, value); +} + +void CDECL wined3d_device_set_scissor_rects(struct wined3d_device *device, unsigned int rect_count, + const RECT *rects) +{ + unsigned int i; + + TRACE("device %p, rect_count %u, rects %p.\n", device, rect_count, rects); + + for (i = 0; i < rect_count; ++i) + { + TRACE("%u: %s\n", i, wine_dbgstr_rect(&rects[i])); + } + + if (device->state.scissor_rect_count == rect_count + && !memcmp(device->state.scissor_rects, rects, rect_count * sizeof(*rects))) + { + TRACE("App is setting the old scissor rectangles over, nothing to do.\n"); + return; + } + + if (rect_count) + memcpy(device->state.scissor_rects, rects, rect_count * sizeof(*rects)); + else + memset(device->state.scissor_rects, 0, sizeof(device->state.scissor_rects)); + device->state.scissor_rect_count = rect_count; + + wined3d_cs_emit_set_scissor_rects(device->cs, rect_count, rects); +} + +void CDECL wined3d_device_get_scissor_rects(const struct wined3d_device *device, unsigned int *rect_count, RECT *rects) +{ + unsigned int count; + + TRACE("device %p, rect_count %p, rects %p.\n", device, rect_count, rects); + + count = rect_count ? min(*rect_count, device->state.scissor_rect_count) : 1; + if (count && rects) + memcpy(rects, device->state.scissor_rects, count * sizeof(*rects)); + if (rect_count) + *rect_count = device->state.scissor_rect_count; +} + +void CDECL wined3d_device_set_vertex_declaration(struct wined3d_device *device, + struct wined3d_vertex_declaration *declaration) +{ + struct wined3d_vertex_declaration *prev = device->state.vertex_declaration; + + TRACE("device %p, declaration %p.\n", device, declaration); + + if (declaration == prev) + return; + + if (declaration) + wined3d_vertex_declaration_incref(declaration); + device->state.vertex_declaration = declaration; + wined3d_cs_emit_set_vertex_declaration(device->cs, declaration); + if (prev) + wined3d_vertex_declaration_decref(prev); +} + +struct wined3d_vertex_declaration * CDECL wined3d_device_get_vertex_declaration(const struct wined3d_device *device) +{ + TRACE("device %p.\n", device); + + return device->state.vertex_declaration; +} + +void CDECL wined3d_device_set_vertex_shader(struct wined3d_device *device, struct wined3d_shader *shader) +{ + struct wined3d_shader *prev = device->state.shader[WINED3D_SHADER_TYPE_VERTEX]; + + TRACE("device %p, shader %p.\n", device, shader); + + if (shader == prev) + return; + + if (shader) + wined3d_shader_incref(shader); + device->state.shader[WINED3D_SHADER_TYPE_VERTEX] = shader; + wined3d_cs_emit_set_shader(device->cs, WINED3D_SHADER_TYPE_VERTEX, shader); + if (prev) + wined3d_shader_decref(prev); +} + +struct wined3d_shader * CDECL wined3d_device_get_vertex_shader(const struct wined3d_device *device) +{ + TRACE("device %p.\n", device); + + return device->state.shader[WINED3D_SHADER_TYPE_VERTEX]; +} + +void CDECL wined3d_device_set_constant_buffer(struct wined3d_device *device, + enum wined3d_shader_type type, UINT idx, struct wined3d_buffer *buffer) +{ + struct wined3d_buffer *prev; + + TRACE("device %p, type %#x, idx %u, buffer %p.\n", device, type, idx, buffer); + + if (idx >= MAX_CONSTANT_BUFFERS) + { + WARN("Invalid constant buffer index %u.\n", idx); + return; + } + + prev = device->state.cb[type][idx]; + if (buffer == prev) + return; + + if (buffer) + wined3d_buffer_incref(buffer); + device->state.cb[type][idx] = buffer; + wined3d_cs_emit_set_constant_buffer(device->cs, type, idx, buffer); + if (prev) + wined3d_buffer_decref(prev); +} + +struct wined3d_buffer * CDECL wined3d_device_get_constant_buffer(const struct wined3d_device *device, + enum wined3d_shader_type shader_type, unsigned int idx) +{ + TRACE("device %p, shader_type %#x, idx %u.\n", device, shader_type, idx); + + if (idx >= MAX_CONSTANT_BUFFERS) + { + WARN("Invalid constant buffer index %u.\n", idx); + return NULL; + } + + return device->state.cb[shader_type][idx]; +} + +static void wined3d_device_set_shader_resource_view(struct wined3d_device *device, + enum wined3d_shader_type type, UINT idx, struct wined3d_shader_resource_view *view) +{ + const struct wined3d_rendertarget_view *dsv; + struct wined3d_shader_resource_view *prev; + + if (idx >= MAX_SHADER_RESOURCE_VIEWS) + { + WARN("Invalid view index %u.\n", idx); + return; + } + + prev = device->state.shader_resource_view[type][idx]; + if (view == prev) + return; + + if (view && (wined3d_is_srv_rtv_bound(view) + || ((dsv = device->state.fb.depth_stencil) + && dsv->resource == view->resource && wined3d_dsv_srv_conflict(dsv, view->format)))) + { + WARN("Application is trying to bind resource which is attached as render target.\n"); + view = NULL; + } + + if (view) + { + wined3d_shader_resource_view_incref(view); + wined3d_srv_bind_count_inc(view); + } + + device->state.shader_resource_view[type][idx] = view; + wined3d_cs_emit_set_shader_resource_view(device->cs, type, idx, view); + if (prev) + { + wined3d_srv_bind_count_dec(prev); + wined3d_shader_resource_view_decref(prev); + } +} + +void CDECL wined3d_device_set_vs_resource_view(struct wined3d_device *device, + UINT idx, struct wined3d_shader_resource_view *view) +{ + TRACE("device %p, idx %u, view %p.\n", device, idx, view); + + wined3d_device_set_shader_resource_view(device, WINED3D_SHADER_TYPE_VERTEX, idx, view); +} + +static struct wined3d_shader_resource_view *wined3d_device_get_shader_resource_view( + const struct wined3d_device *device, enum wined3d_shader_type shader_type, unsigned int idx) +{ + if (idx >= MAX_SHADER_RESOURCE_VIEWS) + { + WARN("Invalid view index %u.\n", idx); + return NULL; + } + + return device->state.shader_resource_view[shader_type][idx]; +} + +struct wined3d_shader_resource_view * CDECL wined3d_device_get_vs_resource_view(const struct wined3d_device *device, + UINT idx) +{ + TRACE("device %p, idx %u.\n", device, idx); + + return wined3d_device_get_shader_resource_view(device, WINED3D_SHADER_TYPE_VERTEX, idx); +} + +static void wined3d_device_set_sampler(struct wined3d_device *device, + enum wined3d_shader_type type, UINT idx, struct wined3d_sampler *sampler) +{ + struct wined3d_sampler *prev; + + if (idx >= MAX_SAMPLER_OBJECTS) + { + WARN("Invalid sampler index %u.\n", idx); + return; + } + + prev = device->state.sampler[type][idx]; + if (sampler == prev) + return; + + if (sampler) + wined3d_sampler_incref(sampler); + device->state.sampler[type][idx] = sampler; + wined3d_cs_emit_set_sampler(device->cs, type, idx, sampler); + if (prev) + wined3d_sampler_decref(prev); +} + +void CDECL wined3d_device_set_vs_sampler(struct wined3d_device *device, UINT idx, struct wined3d_sampler *sampler) +{ + TRACE("device %p, idx %u, sampler %p.\n", device, idx, sampler); + + wined3d_device_set_sampler(device, WINED3D_SHADER_TYPE_VERTEX, idx, sampler); +} + +static struct wined3d_sampler *wined3d_device_get_sampler(const struct wined3d_device *device, + enum wined3d_shader_type shader_type, unsigned int idx) +{ + if (idx >= MAX_SAMPLER_OBJECTS) + { + WARN("Invalid sampler index %u.\n", idx); + return NULL; + } + + return device->state.sampler[shader_type][idx]; +} + +struct wined3d_sampler * CDECL wined3d_device_get_vs_sampler(const struct wined3d_device *device, UINT idx) +{ + TRACE("device %p, idx %u.\n", device, idx); + + return wined3d_device_get_sampler(device, WINED3D_SHADER_TYPE_VERTEX, idx); +} + +static void wined3d_device_set_vs_consts_b(struct wined3d_device *device, + unsigned int start_idx, unsigned int count, const BOOL *constants) +{ + unsigned int i; + + TRACE("device %p, start_idx %u, count %u, constants %p.\n", + device, start_idx, count, constants); + + memcpy(&device->state.vs_consts_b[start_idx], constants, count * sizeof(*constants)); + if (TRACE_ON(d3d)) + { + for (i = 0; i < count; ++i) + TRACE("Set BOOL constant %u to %#x.\n", start_idx + i, constants[i]); + } + + wined3d_cs_push_constants(device->cs, WINED3D_PUSH_CONSTANTS_VS_B, start_idx, count, constants); +} + +static void wined3d_device_set_vs_consts_i(struct wined3d_device *device, + unsigned int start_idx, unsigned int count, const struct wined3d_ivec4 *constants) +{ + unsigned int i; + + TRACE("device %p, start_idx %u, count %u, constants %p.\n", + device, start_idx, count, constants); + + memcpy(&device->state.vs_consts_i[start_idx], constants, count * sizeof(*constants)); + if (TRACE_ON(d3d)) + { + for (i = 0; i < count; ++i) + TRACE("Set ivec4 constant %u to %s.\n", start_idx + i, debug_ivec4(&constants[i])); + } + + wined3d_cs_push_constants(device->cs, WINED3D_PUSH_CONSTANTS_VS_I, start_idx, count, constants); +} + +static void wined3d_device_set_vs_consts_f(struct wined3d_device *device, + unsigned int start_idx, unsigned int count, const struct wined3d_vec4 *constants) +{ + unsigned int i; + + TRACE("device %p, start_idx %u, count %u, constants %p.\n", + device, start_idx, count, constants); + + memcpy(&device->state.vs_consts_f[start_idx], constants, count * sizeof(*constants)); + if (TRACE_ON(d3d)) + { + for (i = 0; i < count; ++i) + TRACE("Set vec4 constant %u to %s.\n", start_idx + i, debug_vec4(&constants[i])); + } + + wined3d_cs_push_constants(device->cs, WINED3D_PUSH_CONSTANTS_VS_F, start_idx, count, constants); +} + +void CDECL wined3d_device_set_pixel_shader(struct wined3d_device *device, struct wined3d_shader *shader) +{ + struct wined3d_shader *prev = device->state.shader[WINED3D_SHADER_TYPE_PIXEL]; + + TRACE("device %p, shader %p.\n", device, shader); + + if (shader == prev) + return; + + if (shader) + wined3d_shader_incref(shader); + device->state.shader[WINED3D_SHADER_TYPE_PIXEL] = shader; + wined3d_cs_emit_set_shader(device->cs, WINED3D_SHADER_TYPE_PIXEL, shader); + if (prev) + wined3d_shader_decref(prev); +} + +struct wined3d_shader * CDECL wined3d_device_get_pixel_shader(const struct wined3d_device *device) +{ + TRACE("device %p.\n", device); + + return device->state.shader[WINED3D_SHADER_TYPE_PIXEL]; +} + +void CDECL wined3d_device_set_ps_resource_view(struct wined3d_device *device, + UINT idx, struct wined3d_shader_resource_view *view) +{ + TRACE("device %p, idx %u, view %p.\n", device, idx, view); + + wined3d_device_set_shader_resource_view(device, WINED3D_SHADER_TYPE_PIXEL, idx, view); +} + +struct wined3d_shader_resource_view * CDECL wined3d_device_get_ps_resource_view(const struct wined3d_device *device, + UINT idx) +{ + TRACE("device %p, idx %u.\n", device, idx); + + return wined3d_device_get_shader_resource_view(device, WINED3D_SHADER_TYPE_PIXEL, idx); +} + +void CDECL wined3d_device_set_ps_sampler(struct wined3d_device *device, UINT idx, struct wined3d_sampler *sampler) +{ + TRACE("device %p, idx %u, sampler %p.\n", device, idx, sampler); + + wined3d_device_set_sampler(device, WINED3D_SHADER_TYPE_PIXEL, idx, sampler); +} + +struct wined3d_sampler * CDECL wined3d_device_get_ps_sampler(const struct wined3d_device *device, UINT idx) +{ + TRACE("device %p, idx %u.\n", device, idx); + + return wined3d_device_get_sampler(device, WINED3D_SHADER_TYPE_PIXEL, idx); +} + +static void wined3d_device_set_ps_consts_b(struct wined3d_device *device, + unsigned int start_idx, unsigned int count, const BOOL *constants) +{ + unsigned int i; + + TRACE("device %p, start_idx %u, count %u, constants %p.\n", + device, start_idx, count, constants); + + memcpy(&device->state.ps_consts_b[start_idx], constants, count * sizeof(*constants)); + if (TRACE_ON(d3d)) + { + for (i = 0; i < count; ++i) + TRACE("Set BOOL constant %u to %#x.\n", start_idx + i, constants[i]); + } + + wined3d_cs_push_constants(device->cs, WINED3D_PUSH_CONSTANTS_PS_B, start_idx, count, constants); +} + +static void wined3d_device_set_ps_consts_i(struct wined3d_device *device, + unsigned int start_idx, unsigned int count, const struct wined3d_ivec4 *constants) +{ + unsigned int i; + + TRACE("device %p, start_idx %u, count %u, constants %p.\n", + device, start_idx, count, constants); + + memcpy(&device->state.ps_consts_i[start_idx], constants, count * sizeof(*constants)); + if (TRACE_ON(d3d)) + { + for (i = 0; i < count; ++i) + TRACE("Set ivec4 constant %u to %s.\n", start_idx + i, debug_ivec4(&constants[i])); + } + + wined3d_cs_push_constants(device->cs, WINED3D_PUSH_CONSTANTS_PS_I, start_idx, count, constants); +} + +static void wined3d_device_set_ps_consts_f(struct wined3d_device *device, + unsigned int start_idx, unsigned int count, const struct wined3d_vec4 *constants) +{ + unsigned int i; + + TRACE("device %p, start_idx %u, count %u, constants %p.\n", + device, start_idx, count, constants); + + memcpy(&device->state.ps_consts_f[start_idx], constants, count * sizeof(*constants)); + if (TRACE_ON(d3d)) + { + for (i = 0; i < count; ++i) + TRACE("Set vec4 constant %u to %s.\n", start_idx + i, debug_vec4(&constants[i])); + } + + wined3d_cs_push_constants(device->cs, WINED3D_PUSH_CONSTANTS_PS_F, start_idx, count, constants); +} + +void CDECL wined3d_device_set_hull_shader(struct wined3d_device *device, struct wined3d_shader *shader) +{ + struct wined3d_shader *prev; + + TRACE("device %p, shader %p.\n", device, shader); + + prev = device->state.shader[WINED3D_SHADER_TYPE_HULL]; + if (shader == prev) + return; + if (shader) + wined3d_shader_incref(shader); + device->state.shader[WINED3D_SHADER_TYPE_HULL] = shader; + wined3d_cs_emit_set_shader(device->cs, WINED3D_SHADER_TYPE_HULL, shader); + if (prev) + wined3d_shader_decref(prev); +} + +struct wined3d_shader * CDECL wined3d_device_get_hull_shader(const struct wined3d_device *device) +{ + TRACE("device %p.\n", device); + + return device->state.shader[WINED3D_SHADER_TYPE_HULL]; +} + +void CDECL wined3d_device_set_hs_resource_view(struct wined3d_device *device, + unsigned int idx, struct wined3d_shader_resource_view *view) +{ + TRACE("device %p, idx %u, view %p.\n", device, idx, view); + + wined3d_device_set_shader_resource_view(device, WINED3D_SHADER_TYPE_HULL, idx, view); +} + +struct wined3d_shader_resource_view * CDECL wined3d_device_get_hs_resource_view(const struct wined3d_device *device, + unsigned int idx) +{ + TRACE("device %p, idx %u.\n", device, idx); + + return wined3d_device_get_shader_resource_view(device, WINED3D_SHADER_TYPE_HULL, idx); +} + +void CDECL wined3d_device_set_hs_sampler(struct wined3d_device *device, + unsigned int idx, struct wined3d_sampler *sampler) +{ + TRACE("device %p, idx %u, sampler %p.\n", device, idx, sampler); + + wined3d_device_set_sampler(device, WINED3D_SHADER_TYPE_HULL, idx, sampler); +} + +struct wined3d_sampler * CDECL wined3d_device_get_hs_sampler(const struct wined3d_device *device, unsigned int idx) +{ + TRACE("device %p, idx %u.\n", device, idx); + + return wined3d_device_get_sampler(device, WINED3D_SHADER_TYPE_HULL, idx); +} + +void CDECL wined3d_device_set_domain_shader(struct wined3d_device *device, struct wined3d_shader *shader) +{ + struct wined3d_shader *prev; + + TRACE("device %p, shader %p.\n", device, shader); + + prev = device->state.shader[WINED3D_SHADER_TYPE_DOMAIN]; + if (shader == prev) + return; + if (shader) + wined3d_shader_incref(shader); + device->state.shader[WINED3D_SHADER_TYPE_DOMAIN] = shader; + wined3d_cs_emit_set_shader(device->cs, WINED3D_SHADER_TYPE_DOMAIN, shader); + if (prev) + wined3d_shader_decref(prev); +} + +struct wined3d_shader * CDECL wined3d_device_get_domain_shader(const struct wined3d_device *device) +{ + TRACE("device %p.\n", device); + + return device->state.shader[WINED3D_SHADER_TYPE_DOMAIN]; +} + +void CDECL wined3d_device_set_ds_resource_view(struct wined3d_device *device, + unsigned int idx, struct wined3d_shader_resource_view *view) +{ + TRACE("device %p, idx %u, view %p.\n", device, idx, view); + + wined3d_device_set_shader_resource_view(device, WINED3D_SHADER_TYPE_DOMAIN, idx, view); +} + +struct wined3d_shader_resource_view * CDECL wined3d_device_get_ds_resource_view(const struct wined3d_device *device, + unsigned int idx) +{ + TRACE("device %p, idx %u.\n", device, idx); + + return wined3d_device_get_shader_resource_view(device, WINED3D_SHADER_TYPE_DOMAIN, idx); +} + +void CDECL wined3d_device_set_ds_sampler(struct wined3d_device *device, + unsigned int idx, struct wined3d_sampler *sampler) +{ + TRACE("device %p, idx %u, sampler %p.\n", device, idx, sampler); + + wined3d_device_set_sampler(device, WINED3D_SHADER_TYPE_DOMAIN, idx, sampler); +} + +struct wined3d_sampler * CDECL wined3d_device_get_ds_sampler(const struct wined3d_device *device, unsigned int idx) +{ + TRACE("device %p, idx %u.\n", device, idx); + + return wined3d_device_get_sampler(device, WINED3D_SHADER_TYPE_DOMAIN, idx); +} + +void CDECL wined3d_device_set_geometry_shader(struct wined3d_device *device, struct wined3d_shader *shader) +{ + struct wined3d_shader *prev = device->state.shader[WINED3D_SHADER_TYPE_GEOMETRY]; + + TRACE("device %p, shader %p.\n", device, shader); + + if (shader == prev) + return; + if (shader) + wined3d_shader_incref(shader); + device->state.shader[WINED3D_SHADER_TYPE_GEOMETRY] = shader; + wined3d_cs_emit_set_shader(device->cs, WINED3D_SHADER_TYPE_GEOMETRY, shader); + if (prev) + wined3d_shader_decref(prev); +} + +struct wined3d_shader * CDECL wined3d_device_get_geometry_shader(const struct wined3d_device *device) +{ + TRACE("device %p.\n", device); + + return device->state.shader[WINED3D_SHADER_TYPE_GEOMETRY]; +} + +void CDECL wined3d_device_set_gs_resource_view(struct wined3d_device *device, + UINT idx, struct wined3d_shader_resource_view *view) +{ + TRACE("device %p, idx %u, view %p.\n", device, idx, view); + + wined3d_device_set_shader_resource_view(device, WINED3D_SHADER_TYPE_GEOMETRY, idx, view); +} + +struct wined3d_shader_resource_view * CDECL wined3d_device_get_gs_resource_view(const struct wined3d_device *device, + UINT idx) +{ + TRACE("device %p, idx %u.\n", device, idx); + + return wined3d_device_get_shader_resource_view(device, WINED3D_SHADER_TYPE_GEOMETRY, idx); +} + +void CDECL wined3d_device_set_gs_sampler(struct wined3d_device *device, UINT idx, struct wined3d_sampler *sampler) +{ + TRACE("device %p, idx %u, sampler %p.\n", device, idx, sampler); + + wined3d_device_set_sampler(device, WINED3D_SHADER_TYPE_GEOMETRY, idx, sampler); +} + +struct wined3d_sampler * CDECL wined3d_device_get_gs_sampler(const struct wined3d_device *device, UINT idx) +{ + TRACE("device %p, idx %u.\n", device, idx); + + return wined3d_device_get_sampler(device, WINED3D_SHADER_TYPE_GEOMETRY, idx); +} + +void CDECL wined3d_device_set_compute_shader(struct wined3d_device *device, struct wined3d_shader *shader) +{ + struct wined3d_shader *prev; + + TRACE("device %p, shader %p.\n", device, shader); + + prev = device->state.shader[WINED3D_SHADER_TYPE_COMPUTE]; + if (shader == prev) + return; + if (shader) + wined3d_shader_incref(shader); + device->state.shader[WINED3D_SHADER_TYPE_COMPUTE] = shader; + wined3d_cs_emit_set_shader(device->cs, WINED3D_SHADER_TYPE_COMPUTE, shader); + if (prev) + wined3d_shader_decref(prev); +} + +struct wined3d_shader * CDECL wined3d_device_get_compute_shader(const struct wined3d_device *device) +{ + TRACE("device %p.\n", device); + + return device->state.shader[WINED3D_SHADER_TYPE_COMPUTE]; +} + +void CDECL wined3d_device_set_cs_resource_view(struct wined3d_device *device, + unsigned int idx, struct wined3d_shader_resource_view *view) +{ + TRACE("device %p, idx %u, view %p.\n", device, idx, view); + + wined3d_device_set_shader_resource_view(device, WINED3D_SHADER_TYPE_COMPUTE, idx, view); +} + +struct wined3d_shader_resource_view * CDECL wined3d_device_get_cs_resource_view(const struct wined3d_device *device, + unsigned int idx) +{ + TRACE("device %p, idx %u.\n", device, idx); + + return wined3d_device_get_shader_resource_view(device, WINED3D_SHADER_TYPE_COMPUTE, idx); +} + +void CDECL wined3d_device_set_cs_sampler(struct wined3d_device *device, + unsigned int idx, struct wined3d_sampler *sampler) +{ + TRACE("device %p, idx %u, sampler %p.\n", device, idx, sampler); + + wined3d_device_set_sampler(device, WINED3D_SHADER_TYPE_COMPUTE, idx, sampler); +} + +struct wined3d_sampler * CDECL wined3d_device_get_cs_sampler(const struct wined3d_device *device, unsigned int idx) +{ + TRACE("device %p, idx %u.\n", device, idx); + + return wined3d_device_get_sampler(device, WINED3D_SHADER_TYPE_COMPUTE, idx); +} + +static void wined3d_device_set_pipeline_unordered_access_view(struct wined3d_device *device, + enum wined3d_pipeline pipeline, unsigned int idx, struct wined3d_unordered_access_view *uav, + unsigned int initial_count) +{ + struct wined3d_unordered_access_view *prev; + + if (idx >= MAX_UNORDERED_ACCESS_VIEWS) + { + WARN("Invalid UAV index %u.\n", idx); + return; + } + + prev = device->state.unordered_access_view[pipeline][idx]; + if (uav == prev && initial_count == ~0u) + return; + + if (uav) + wined3d_unordered_access_view_incref(uav); + device->state.unordered_access_view[pipeline][idx] = uav; + wined3d_cs_emit_set_unordered_access_view(device->cs, pipeline, idx, uav, initial_count); + if (prev) + wined3d_unordered_access_view_decref(prev); +} + +static struct wined3d_unordered_access_view *wined3d_device_get_pipeline_unordered_access_view( + const struct wined3d_device *device, enum wined3d_pipeline pipeline, unsigned int idx) +{ + if (idx >= MAX_UNORDERED_ACCESS_VIEWS) + { + WARN("Invalid UAV index %u.\n", idx); + return NULL; + } + + return device->state.unordered_access_view[pipeline][idx]; +} + +void CDECL wined3d_device_set_cs_uav(struct wined3d_device *device, unsigned int idx, + struct wined3d_unordered_access_view *uav, unsigned int initial_count) +{ + TRACE("device %p, idx %u, uav %p, initial_count %#x.\n", device, idx, uav, initial_count); + + wined3d_device_set_pipeline_unordered_access_view(device, WINED3D_PIPELINE_COMPUTE, idx, uav, initial_count); +} + +struct wined3d_unordered_access_view * CDECL wined3d_device_get_cs_uav(const struct wined3d_device *device, + unsigned int idx) +{ + TRACE("device %p, idx %u.\n", device, idx); + + return wined3d_device_get_pipeline_unordered_access_view(device, WINED3D_PIPELINE_COMPUTE, idx); +} + +void CDECL wined3d_device_set_unordered_access_view(struct wined3d_device *device, + unsigned int idx, struct wined3d_unordered_access_view *uav, unsigned int initial_count) +{ + TRACE("device %p, idx %u, uav %p, initial_count %#x.\n", device, idx, uav, initial_count); + + wined3d_device_set_pipeline_unordered_access_view(device, WINED3D_PIPELINE_GRAPHICS, idx, uav, initial_count); +} + +struct wined3d_unordered_access_view * CDECL wined3d_device_get_unordered_access_view( + const struct wined3d_device *device, unsigned int idx) +{ + TRACE("device %p, idx %u.\n", device, idx); + + return wined3d_device_get_pipeline_unordered_access_view(device, WINED3D_PIPELINE_GRAPHICS, idx); +} + +void CDECL wined3d_device_set_max_frame_latency(struct wined3d_device *device, unsigned int latency) +{ + unsigned int i; + + if (!latency) + latency = 3; + + device->max_frame_latency = latency; + for (i = 0; i < device->swapchain_count; ++i) + swapchain_set_max_frame_latency(device->swapchains[i], device); +} + +unsigned int CDECL wined3d_device_get_max_frame_latency(const struct wined3d_device *device) +{ + return device->max_frame_latency; +} + +static unsigned int wined3d_get_flexible_vertex_size(DWORD fvf) +{ + unsigned int texcoord_count = (fvf & WINED3DFVF_TEXCOUNT_MASK) >> WINED3DFVF_TEXCOUNT_SHIFT; + unsigned int i, size = 0; + + if (fvf & WINED3DFVF_NORMAL) size += 3 * sizeof(float); + if (fvf & WINED3DFVF_DIFFUSE) size += sizeof(DWORD); + if (fvf & WINED3DFVF_SPECULAR) size += sizeof(DWORD); + if (fvf & WINED3DFVF_PSIZE) size += sizeof(DWORD); + switch (fvf & WINED3DFVF_POSITION_MASK) + { + case WINED3DFVF_XYZ: size += 3 * sizeof(float); break; + case WINED3DFVF_XYZRHW: size += 4 * sizeof(float); break; + case WINED3DFVF_XYZB1: size += 4 * sizeof(float); break; + case WINED3DFVF_XYZB2: size += 5 * sizeof(float); break; + case WINED3DFVF_XYZB3: size += 6 * sizeof(float); break; + case WINED3DFVF_XYZB4: size += 7 * sizeof(float); break; + case WINED3DFVF_XYZB5: size += 8 * sizeof(float); break; + case WINED3DFVF_XYZW: size += 4 * sizeof(float); break; + default: FIXME("Unexpected position mask %#x.\n", fvf & WINED3DFVF_POSITION_MASK); + } + for (i = 0; i < texcoord_count; ++i) + { + size += GET_TEXCOORD_SIZE_FROM_FVF(fvf, i) * sizeof(float); + } + + return size; +} + +static void wined3d_format_get_colour(const struct wined3d_format *format, + const void *data, struct wined3d_color *colour) +{ + float *output = &colour->r; + const uint32_t *u32_data; + const uint16_t *u16_data; + const float *f32_data; + unsigned int i; + + static const struct wined3d_color default_colour = {0.0f, 0.0f, 0.0f, 1.0f}; + static unsigned int warned; + + switch (format->id) + { + case WINED3DFMT_B8G8R8A8_UNORM: + u32_data = data; + wined3d_color_from_d3dcolor(colour, *u32_data); + break; + + case WINED3DFMT_R8G8B8A8_UNORM: + u32_data = data; + colour->r = (*u32_data & 0xffu) / 255.0f; + colour->g = ((*u32_data >> 8) & 0xffu) / 255.0f; + colour->b = ((*u32_data >> 16) & 0xffu) / 255.0f; + colour->a = ((*u32_data >> 24) & 0xffu) / 255.0f; + break; + + case WINED3DFMT_R16G16_UNORM: + case WINED3DFMT_R16G16B16A16_UNORM: + u16_data = data; + *colour = default_colour; + for (i = 0; i < format->component_count; ++i) + output[i] = u16_data[i] / 65535.0f; + break; + + case WINED3DFMT_R32_FLOAT: + case WINED3DFMT_R32G32_FLOAT: + case WINED3DFMT_R32G32B32_FLOAT: + case WINED3DFMT_R32G32B32A32_FLOAT: + f32_data = data; + *colour = default_colour; + for (i = 0; i < format->component_count; ++i) + output[i] = f32_data[i]; + break; + + default: + *colour = default_colour; + if (!warned++) + FIXME("Unhandled colour format conversion, format %s.\n", debug_d3dformat(format->id)); + break; + } +} + +static void wined3d_colour_from_mcs(struct wined3d_color *colour, enum wined3d_material_color_source mcs, + const struct wined3d_color *material_colour, unsigned int index, + const struct wined3d_stream_info *stream_info) +{ + const struct wined3d_stream_info_element *element = NULL; + + switch (mcs) + { + case WINED3D_MCS_MATERIAL: + *colour = *material_colour; + return; + + case WINED3D_MCS_COLOR1: + if (!(stream_info->use_map & (1u << WINED3D_FFP_DIFFUSE))) + { + colour->r = colour->g = colour->b = colour->a = 1.0f; + return; + } + element = &stream_info->elements[WINED3D_FFP_DIFFUSE]; + break; + + case WINED3D_MCS_COLOR2: + if (!(stream_info->use_map & (1u << WINED3D_FFP_SPECULAR))) + { + colour->r = colour->g = colour->b = colour->a = 0.0f; + return; + } + element = &stream_info->elements[WINED3D_FFP_SPECULAR]; + break; + + default: + colour->r = colour->g = colour->b = colour->a = 0.0f; + ERR("Invalid material colour source %#x.\n", mcs); + return; + } + + wined3d_format_get_colour(element->format, &element->data.addr[index * element->stride], colour); +} + +static float wined3d_clamp(float value, float min_value, float max_value) +{ + return value < min_value ? min_value : value > max_value ? max_value : value; +} + +static float wined3d_vec3_dot(const struct wined3d_vec3 *v0, const struct wined3d_vec3 *v1) +{ + return v0->x * v1->x + v0->y * v1->y + v0->z * v1->z; +} + +static void wined3d_vec3_subtract(struct wined3d_vec3 *v0, const struct wined3d_vec3 *v1) +{ + v0->x -= v1->x; + v0->y -= v1->y; + v0->z -= v1->z; +} + +static void wined3d_vec3_scale(struct wined3d_vec3 *v, float s) +{ + v->x *= s; + v->y *= s; + v->z *= s; +} + +static void wined3d_vec3_normalise(struct wined3d_vec3 *v) +{ + float rnorm = 1.0f / sqrtf(wined3d_vec3_dot(v, v)); + + if (isfinite(rnorm)) + wined3d_vec3_scale(v, rnorm); +} + +static void wined3d_vec3_transform(struct wined3d_vec3 *dst, + const struct wined3d_vec3 *v, const struct wined3d_matrix_3x3 *m) +{ + struct wined3d_vec3 tmp; + + tmp.x = v->x * m->_11 + v->y * m->_21 + v->z * m->_31; + tmp.y = v->x * m->_12 + v->y * m->_22 + v->z * m->_32; + tmp.z = v->x * m->_13 + v->y * m->_23 + v->z * m->_33; + + *dst = tmp; +} + +static void wined3d_color_clamp(struct wined3d_color *dst, const struct wined3d_color *src, + float min_value, float max_value) +{ + dst->r = wined3d_clamp(src->r, min_value, max_value); + dst->g = wined3d_clamp(src->g, min_value, max_value); + dst->b = wined3d_clamp(src->b, min_value, max_value); + dst->a = wined3d_clamp(src->a, min_value, max_value); +} + +static void wined3d_color_rgb_mul_add(struct wined3d_color *dst, const struct wined3d_color *src, float c) +{ + dst->r += src->r * c; + dst->g += src->g * c; + dst->b += src->b * c; +} + +static void init_transformed_lights(struct lights_settings *ls, + const struct wined3d_state *state, BOOL legacy_lighting, BOOL compute_lighting) +{ + const struct wined3d_light_info *lights[WINED3D_MAX_SOFTWARE_ACTIVE_LIGHTS]; + const struct wined3d_light_info *light_info; + struct light_transformed *light; + struct wined3d_vec4 vec4; + unsigned int light_count; + unsigned int i, index; + + memset(ls, 0, sizeof(*ls)); + + ls->lighting = !!compute_lighting; + ls->fog_mode = state->render_states[WINED3D_RS_FOGVERTEXMODE]; + ls->fog_coord_mode = state->render_states[WINED3D_RS_RANGEFOGENABLE] + ? WINED3D_FFP_VS_FOG_RANGE : WINED3D_FFP_VS_FOG_DEPTH; + ls->fog_start = wined3d_get_float_state(state, WINED3D_RS_FOGSTART); + ls->fog_end = wined3d_get_float_state(state, WINED3D_RS_FOGEND); + ls->fog_density = wined3d_get_float_state(state, WINED3D_RS_FOGDENSITY); + + if (ls->fog_mode == WINED3D_FOG_NONE && !compute_lighting) + return; + + multiply_matrix(&ls->modelview_matrix, &state->transforms[WINED3D_TS_VIEW], + &state->transforms[WINED3D_TS_WORLD_MATRIX(0)]); + + if (!compute_lighting) + return; + + compute_normal_matrix(&ls->normal_matrix._11, legacy_lighting, &ls->modelview_matrix); + + wined3d_color_from_d3dcolor(&ls->ambient_light, state->render_states[WINED3D_RS_AMBIENT]); + ls->legacy_lighting = !!legacy_lighting; + ls->normalise = !!state->render_states[WINED3D_RS_NORMALIZENORMALS]; + ls->localviewer = !!state->render_states[WINED3D_RS_LOCALVIEWER]; + + for (i = 0, index = 0; i < LIGHTMAP_SIZE && index < ARRAY_SIZE(lights); ++i) + { + LIST_FOR_EACH_ENTRY(light_info, &state->light_state.light_map[i], struct wined3d_light_info, entry) + { + if (!light_info->enabled) + continue; + + switch (light_info->OriginalParms.type) + { + case WINED3D_LIGHT_DIRECTIONAL: + ++ls->directional_light_count; + break; + + case WINED3D_LIGHT_POINT: + ++ls->point_light_count; + break; + + case WINED3D_LIGHT_SPOT: + ++ls->spot_light_count; + break; + + case WINED3D_LIGHT_PARALLELPOINT: + ++ls->parallel_point_light_count; + break; + + default: + FIXME("Unhandled light type %#x.\n", light_info->OriginalParms.type); + continue; + } + lights[index++] = light_info; + if (index == WINED3D_MAX_SOFTWARE_ACTIVE_LIGHTS) + break; + } + } + + light_count = index; + for (i = 0, index = 0; i < light_count; ++i) + { + light_info = lights[i]; + if (light_info->OriginalParms.type != WINED3D_LIGHT_DIRECTIONAL) + continue; + + light = &ls->lights[index]; + wined3d_vec4_transform(&vec4, &light_info->direction, &state->transforms[WINED3D_TS_VIEW]); + light->direction = *(struct wined3d_vec3 *)&vec4; + wined3d_vec3_normalise(&light->direction); + + light->diffuse = light_info->OriginalParms.diffuse; + light->ambient = light_info->OriginalParms.ambient; + light->specular = light_info->OriginalParms.specular; + ++index; + } + + for (i = 0; i < light_count; ++i) + { + light_info = lights[i]; + if (light_info->OriginalParms.type != WINED3D_LIGHT_POINT) + continue; + + light = &ls->lights[index]; + + wined3d_vec4_transform(&light->position, &light_info->position, &state->transforms[WINED3D_TS_VIEW]); + light->range = light_info->OriginalParms.range; + light->c_att = light_info->OriginalParms.attenuation0; + light->l_att = light_info->OriginalParms.attenuation1; + light->q_att = light_info->OriginalParms.attenuation2; + + light->diffuse = light_info->OriginalParms.diffuse; + light->ambient = light_info->OriginalParms.ambient; + light->specular = light_info->OriginalParms.specular; + ++index; + } + + for (i = 0; i < light_count; ++i) + { + light_info = lights[i]; + if (light_info->OriginalParms.type != WINED3D_LIGHT_SPOT) + continue; + + light = &ls->lights[index]; + + wined3d_vec4_transform(&light->position, &light_info->position, &state->transforms[WINED3D_TS_VIEW]); + wined3d_vec4_transform(&vec4, &light_info->direction, &state->transforms[WINED3D_TS_VIEW]); + light->direction = *(struct wined3d_vec3 *)&vec4; + wined3d_vec3_normalise(&light->direction); + light->range = light_info->OriginalParms.range; + light->falloff = light_info->OriginalParms.falloff; + light->c_att = light_info->OriginalParms.attenuation0; + light->l_att = light_info->OriginalParms.attenuation1; + light->q_att = light_info->OriginalParms.attenuation2; + light->cos_htheta = cosf(light_info->OriginalParms.theta / 2.0f); + light->cos_hphi = cosf(light_info->OriginalParms.phi / 2.0f); + + light->diffuse = light_info->OriginalParms.diffuse; + light->ambient = light_info->OriginalParms.ambient; + light->specular = light_info->OriginalParms.specular; + ++index; + } + + for (i = 0; i < light_count; ++i) + { + light_info = lights[i]; + if (light_info->OriginalParms.type != WINED3D_LIGHT_PARALLELPOINT) + continue; + + light = &ls->lights[index]; + + wined3d_vec4_transform(&vec4, &light_info->position, &state->transforms[WINED3D_TS_VIEW]); + *(struct wined3d_vec3 *)&light->position = *(struct wined3d_vec3 *)&vec4; + wined3d_vec3_normalise((struct wined3d_vec3 *)&light->position); + light->diffuse = light_info->OriginalParms.diffuse; + light->ambient = light_info->OriginalParms.ambient; + light->specular = light_info->OriginalParms.specular; + ++index; + } +} + +static void update_light_diffuse_specular(struct wined3d_color *diffuse, struct wined3d_color *specular, + const struct wined3d_vec3 *dir, float att, float material_shininess, + const struct wined3d_vec3 *normal_transformed, + const struct wined3d_vec3 *position_transformed_normalised, + const struct light_transformed *light, const struct lights_settings *ls) +{ + struct wined3d_vec3 vec3; + float t, c; + + c = wined3d_clamp(wined3d_vec3_dot(dir, normal_transformed), 0.0f, 1.0f); + wined3d_color_rgb_mul_add(diffuse, &light->diffuse, c * att); + + vec3 = *dir; + if (ls->localviewer) + wined3d_vec3_subtract(&vec3, position_transformed_normalised); + else + vec3.z -= 1.0f; + wined3d_vec3_normalise(&vec3); + t = wined3d_vec3_dot(normal_transformed, &vec3); + if (t > 0.0f && (!ls->legacy_lighting || material_shininess > 0.0f) + && wined3d_vec3_dot(dir, normal_transformed) > 0.0f) + wined3d_color_rgb_mul_add(specular, &light->specular, att * powf(t, material_shininess)); +} + +static void light_set_vertex_data(struct lights_settings *ls, + const struct wined3d_vec4 *position) +{ + if (ls->fog_mode == WINED3D_FOG_NONE && !ls->lighting) + return; + + wined3d_vec4_transform(&ls->position_transformed, position, &ls->modelview_matrix); + wined3d_vec3_scale((struct wined3d_vec3 *)&ls->position_transformed, 1.0f / ls->position_transformed.w); +} + +static void compute_light(struct wined3d_color *ambient, struct wined3d_color *diffuse, + struct wined3d_color *specular, struct lights_settings *ls, const struct wined3d_vec3 *normal, + float material_shininess) +{ + struct wined3d_vec3 position_transformed_normalised; + struct wined3d_vec3 normal_transformed = {0.0f}; + const struct light_transformed *light; + struct wined3d_vec3 dir, dst; + unsigned int i, index; + float att; + + position_transformed_normalised = *(const struct wined3d_vec3 *)&ls->position_transformed; + wined3d_vec3_normalise(&position_transformed_normalised); + + if (normal) + { + wined3d_vec3_transform(&normal_transformed, normal, &ls->normal_matrix); + if (ls->normalise) + wined3d_vec3_normalise(&normal_transformed); + } + + diffuse->r = diffuse->g = diffuse->b = diffuse->a = 0.0f; + *specular = *diffuse; + *ambient = ls->ambient_light; + + index = 0; + for (i = 0; i < ls->directional_light_count; ++i, ++index) + { + light = &ls->lights[index]; + + wined3d_color_rgb_mul_add(ambient, &light->ambient, 1.0f); + if (normal) + update_light_diffuse_specular(diffuse, specular, &light->direction, 1.0f, material_shininess, + &normal_transformed, &position_transformed_normalised, light, ls); + } + + for (i = 0; i < ls->point_light_count; ++i, ++index) + { + light = &ls->lights[index]; + dir.x = light->position.x - ls->position_transformed.x; + dir.y = light->position.y - ls->position_transformed.y; + dir.z = light->position.z - ls->position_transformed.z; + + dst.z = wined3d_vec3_dot(&dir, &dir); + dst.y = sqrtf(dst.z); + dst.x = 1.0f; + if (ls->legacy_lighting) + { + dst.y = (light->range - dst.y) / light->range; + if (!(dst.y > 0.0f)) + continue; + dst.z = dst.y * dst.y; + } + else + { + if (!(dst.y <= light->range)) + continue; + } + att = dst.x * light->c_att + dst.y * light->l_att + dst.z * light->q_att; + if (!ls->legacy_lighting) + att = 1.0f / att; + + wined3d_color_rgb_mul_add(ambient, &light->ambient, att); + if (normal) + { + wined3d_vec3_normalise(&dir); + update_light_diffuse_specular(diffuse, specular, &dir, att, material_shininess, + &normal_transformed, &position_transformed_normalised, light, ls); + } + } + + for (i = 0; i < ls->spot_light_count; ++i, ++index) + { + float t; + + light = &ls->lights[index]; + + dir.x = light->position.x - ls->position_transformed.x; + dir.y = light->position.y - ls->position_transformed.y; + dir.z = light->position.z - ls->position_transformed.z; + + dst.z = wined3d_vec3_dot(&dir, &dir); + dst.y = sqrtf(dst.z); + dst.x = 1.0f; + + if (ls->legacy_lighting) + { + dst.y = (light->range - dst.y) / light->range; + if (!(dst.y > 0.0f)) + continue; + dst.z = dst.y * dst.y; + } + else + { + if (!(dst.y <= light->range)) + continue; + } + wined3d_vec3_normalise(&dir); + t = -wined3d_vec3_dot(&dir, &light->direction); + if (t > light->cos_htheta) + att = 1.0f; + else if (t <= light->cos_hphi) + att = 0.0f; + else + att = powf((t - light->cos_hphi) / (light->cos_htheta - light->cos_hphi), light->falloff); + + t = dst.x * light->c_att + dst.y * light->l_att + dst.z * light->q_att; + if (ls->legacy_lighting) + att *= t; + else + att /= t; + + wined3d_color_rgb_mul_add(ambient, &light->ambient, att); + + if (normal) + update_light_diffuse_specular(diffuse, specular, &dir, att, material_shininess, + &normal_transformed, &position_transformed_normalised, light, ls); + } + + for (i = 0; i < ls->parallel_point_light_count; ++i, ++index) + { + light = &ls->lights[index]; + + wined3d_color_rgb_mul_add(ambient, &light->ambient, 1.0f); + if (normal) + update_light_diffuse_specular(diffuse, specular, (const struct wined3d_vec3 *)&light->position, + 1.0f, material_shininess, &normal_transformed, &position_transformed_normalised, light, ls); + } +} + +static float wined3d_calculate_fog_factor(float fog_coord, const struct lights_settings *ls) +{ + switch (ls->fog_mode) + { + case WINED3D_FOG_NONE: + return fog_coord; + case WINED3D_FOG_LINEAR: + return (ls->fog_end - fog_coord) / (ls->fog_end - ls->fog_start); + case WINED3D_FOG_EXP: + return expf(-fog_coord * ls->fog_density); + case WINED3D_FOG_EXP2: + return expf(-fog_coord * fog_coord * ls->fog_density * ls->fog_density); + default: + ERR("Unhandled fog mode %#x.\n", ls->fog_mode); + return 0.0f; + } +} + +static void update_fog_factor(float *fog_factor, struct lights_settings *ls) +{ + float fog_coord; + + if (ls->fog_mode == WINED3D_FOG_NONE) + return; + + switch (ls->fog_coord_mode) + { + case WINED3D_FFP_VS_FOG_RANGE: + fog_coord = sqrtf(wined3d_vec3_dot((const struct wined3d_vec3 *)&ls->position_transformed, + (const struct wined3d_vec3 *)&ls->position_transformed)); + break; + + case WINED3D_FFP_VS_FOG_DEPTH: + fog_coord = fabsf(ls->position_transformed.z); + break; + + default: + ERR("Unhandled fog coordinate mode %#x.\n", ls->fog_coord_mode); + return; + } + *fog_factor = wined3d_calculate_fog_factor(fog_coord, ls); +} + +/* Context activation is done by the caller. */ +#define copy_and_next(dest, src, size) memcpy(dest, src, size); dest += (size) +static HRESULT process_vertices_strided(const struct wined3d_device *device, DWORD dwDestIndex, DWORD dwCount, + const struct wined3d_stream_info *stream_info, struct wined3d_buffer *dest, DWORD flags, DWORD dst_fvf) +{ + enum wined3d_material_color_source diffuse_source, specular_source, ambient_source, emissive_source; + const struct wined3d_color *material_specular_state_colour; + struct wined3d_matrix mat, proj_mat, view_mat, world_mat; + const struct wined3d_state *state = &device->state; + const struct wined3d_format *output_colour_format; + static const struct wined3d_color black; + struct wined3d_map_desc map_desc; + struct wined3d_box box = {0}; + struct wined3d_viewport vp; + unsigned int texture_count; + struct lights_settings ls; + unsigned int vertex_size; + BOOL do_clip, lighting; + float min_z, max_z; + unsigned int i; + BYTE *dest_ptr; + HRESULT hr; + + if (!(stream_info->use_map & (1u << WINED3D_FFP_POSITION))) + { + ERR("Source has no position mask.\n"); + return WINED3DERR_INVALIDCALL; + } + + if (state->render_states[WINED3D_RS_CLIPPING]) + { + static BOOL warned = FALSE; + /* + * The clipping code is not quite correct. Some things need + * to be checked against IDirect3DDevice3 (!), d3d8 and d3d9, + * so disable clipping for now. + * (The graphics in Half-Life are broken, and my processvertices + * test crashes with IDirect3DDevice3) + do_clip = TRUE; + */ + do_clip = FALSE; + if (!warned) + { + warned = TRUE; + FIXME("Clipping is broken and disabled for now\n"); + } + } + else + do_clip = FALSE; + + vertex_size = wined3d_get_flexible_vertex_size(dst_fvf); + box.left = dwDestIndex * vertex_size; + box.right = box.left + dwCount * vertex_size; + if (FAILED(hr = wined3d_resource_map(&dest->resource, 0, &map_desc, &box, WINED3D_MAP_WRITE))) + { + WARN("Failed to map buffer, hr %#x.\n", hr); + return hr; + } + dest_ptr = map_desc.data; + + wined3d_device_get_transform(device, WINED3D_TS_VIEW, &view_mat); + wined3d_device_get_transform(device, WINED3D_TS_PROJECTION, &proj_mat); + wined3d_device_get_transform(device, WINED3D_TS_WORLD_MATRIX(0), &world_mat); + + TRACE("View mat:\n"); + TRACE("%.8e %.8e %.8e %.8e\n", view_mat._11, view_mat._12, view_mat._13, view_mat._14); + TRACE("%.8e %.8e %.8e %.8e\n", view_mat._21, view_mat._22, view_mat._23, view_mat._24); + TRACE("%.8e %.8e %.8e %.8e\n", view_mat._31, view_mat._32, view_mat._33, view_mat._34); + TRACE("%.8e %.8e %.8e %.8e\n", view_mat._41, view_mat._42, view_mat._43, view_mat._44); + + TRACE("Proj mat:\n"); + TRACE("%.8e %.8e %.8e %.8e\n", proj_mat._11, proj_mat._12, proj_mat._13, proj_mat._14); + TRACE("%.8e %.8e %.8e %.8e\n", proj_mat._21, proj_mat._22, proj_mat._23, proj_mat._24); + TRACE("%.8e %.8e %.8e %.8e\n", proj_mat._31, proj_mat._32, proj_mat._33, proj_mat._34); + TRACE("%.8e %.8e %.8e %.8e\n", proj_mat._41, proj_mat._42, proj_mat._43, proj_mat._44); + + TRACE("World mat:\n"); + TRACE("%.8e %.8e %.8e %.8e\n", world_mat._11, world_mat._12, world_mat._13, world_mat._14); + TRACE("%.8e %.8e %.8e %.8e\n", world_mat._21, world_mat._22, world_mat._23, world_mat._24); + TRACE("%.8e %.8e %.8e %.8e\n", world_mat._31, world_mat._32, world_mat._33, world_mat._34); + TRACE("%.8e %.8e %.8e %.8e\n", world_mat._41, world_mat._42, world_mat._43, world_mat._44); + + /* Get the viewport */ + wined3d_device_get_viewports(device, NULL, &vp); + TRACE("viewport x %.8e, y %.8e, width %.8e, height %.8e, min_z %.8e, max_z %.8e.\n", + vp.x, vp.y, vp.width, vp.height, vp.min_z, vp.max_z); + + multiply_matrix(&mat,&view_mat,&world_mat); + multiply_matrix(&mat,&proj_mat,&mat); + + texture_count = (dst_fvf & WINED3DFVF_TEXCOUNT_MASK) >> WINED3DFVF_TEXCOUNT_SHIFT; + + lighting = state->render_states[WINED3D_RS_LIGHTING] + && (dst_fvf & (WINED3DFVF_DIFFUSE | WINED3DFVF_SPECULAR)); + wined3d_get_material_colour_source(&diffuse_source, &emissive_source, + &ambient_source, &specular_source, state, stream_info); + output_colour_format = wined3d_get_format(device->adapter, WINED3DFMT_B8G8R8A8_UNORM, 0); + material_specular_state_colour = state->render_states[WINED3D_RS_SPECULARENABLE] + ? &state->material.specular : &black; + init_transformed_lights(&ls, state, device->adapter->d3d_info.wined3d_creation_flags + & WINED3D_LEGACY_FFP_LIGHTING, lighting); + + wined3d_viewport_get_z_range(&vp, &min_z, &max_z); + + for (i = 0; i < dwCount; ++i) + { + const struct wined3d_stream_info_element *position_element = &stream_info->elements[WINED3D_FFP_POSITION]; + const float *p = (const float *)&position_element->data.addr[i * position_element->stride]; + struct wined3d_color ambient, diffuse, specular; + struct wined3d_vec4 position; + unsigned int tex_index; + + position.x = p[0]; + position.y = p[1]; + position.z = p[2]; + position.w = 1.0f; + + light_set_vertex_data(&ls, &position); + + if ( ((dst_fvf & WINED3DFVF_POSITION_MASK) == WINED3DFVF_XYZ ) || + ((dst_fvf & WINED3DFVF_POSITION_MASK) == WINED3DFVF_XYZRHW ) ) { + /* The position first */ + float x, y, z, rhw; + TRACE("In: ( %06.2f %06.2f %06.2f )\n", p[0], p[1], p[2]); + + /* Multiplication with world, view and projection matrix. */ + x = (p[0] * mat._11) + (p[1] * mat._21) + (p[2] * mat._31) + mat._41; + y = (p[0] * mat._12) + (p[1] * mat._22) + (p[2] * mat._32) + mat._42; + z = (p[0] * mat._13) + (p[1] * mat._23) + (p[2] * mat._33) + mat._43; + rhw = (p[0] * mat._14) + (p[1] * mat._24) + (p[2] * mat._34) + mat._44; + + TRACE("x=%f y=%f z=%f rhw=%f\n", x, y, z, rhw); + + /* WARNING: The following things are taken from d3d7 and were not yet checked + * against d3d8 or d3d9! + */ + + /* Clipping conditions: From msdn + * + * A vertex is clipped if it does not match the following requirements + * -rhw < x <= rhw + * -rhw < y <= rhw + * 0 < z <= rhw + * 0 < rhw ( Not in d3d7, but tested in d3d7) + * + * If clipping is on is determined by the D3DVOP_CLIP flag in D3D7, and + * by the D3DRS_CLIPPING in D3D9(according to the msdn, not checked) + * + */ + + if (!do_clip || (-rhw - eps < x && -rhw - eps < y && -eps < z && x <= rhw + eps + && y <= rhw + eps && z <= rhw + eps && rhw > eps)) + { + /* "Normal" viewport transformation (not clipped) + * 1) The values are divided by rhw + * 2) The y axis is negative, so multiply it with -1 + * 3) Screen coordinates go from -(Width/2) to +(Width/2) and + * -(Height/2) to +(Height/2). The z range is MinZ to MaxZ + * 4) Multiply x with Width/2 and add Width/2 + * 5) The same for the height + * 6) Add the viewpoint X and Y to the 2D coordinates and + * The minimum Z value to z + * 7) rhw = 1 / rhw Reciprocal of Homogeneous W.... + * + * Well, basically it's simply a linear transformation into viewport + * coordinates + */ + + x /= rhw; + y /= rhw; + z /= rhw; + + y *= -1; + + x *= vp.width / 2; + y *= vp.height / 2; + z *= max_z - min_z; + + x += vp.width / 2 + vp.x; + y += vp.height / 2 + vp.y; + z += min_z; + + rhw = 1 / rhw; + } else { + /* That vertex got clipped + * Contrary to OpenGL it is not dropped completely, it just + * undergoes a different calculation. + */ + TRACE("Vertex got clipped\n"); + x += rhw; + y += rhw; + + x /= 2; + y /= 2; + + /* Msdn mentions that Direct3D9 keeps a list of clipped vertices + * outside of the main vertex buffer memory. That needs some more + * investigation... + */ + } + + TRACE("Writing (%f %f %f) %f\n", x, y, z, rhw); + + + ( (float *) dest_ptr)[0] = x; + ( (float *) dest_ptr)[1] = y; + ( (float *) dest_ptr)[2] = z; + ( (float *) dest_ptr)[3] = rhw; /* SIC, see ddraw test! */ + + dest_ptr += 3 * sizeof(float); + + if ((dst_fvf & WINED3DFVF_POSITION_MASK) == WINED3DFVF_XYZRHW) + dest_ptr += sizeof(float); + } + + if (dst_fvf & WINED3DFVF_PSIZE) + dest_ptr += sizeof(DWORD); + + if (dst_fvf & WINED3DFVF_NORMAL) + { + const struct wined3d_stream_info_element *element = &stream_info->elements[WINED3D_FFP_NORMAL]; + const float *normal = (const float *)(element->data.addr + i * element->stride); + /* AFAIK this should go into the lighting information */ + FIXME("Didn't expect the destination to have a normal\n"); + copy_and_next(dest_ptr, normal, 3 * sizeof(float)); + } + + if (lighting) + { + const struct wined3d_stream_info_element *element; + struct wined3d_vec3 *normal; + + if (stream_info->use_map & (1u << WINED3D_FFP_NORMAL)) + { + element = &stream_info->elements[WINED3D_FFP_NORMAL]; + normal = (struct wined3d_vec3 *)&element->data.addr[i * element->stride]; + } + else + { + normal = NULL; + } + compute_light(&ambient, &diffuse, &specular, &ls, normal, + state->render_states[WINED3D_RS_SPECULARENABLE] ? state->material.power : 0.0f); + } + + if (dst_fvf & WINED3DFVF_DIFFUSE) + { + struct wined3d_color material_diffuse, material_ambient, material_emissive, diffuse_colour; + + wined3d_colour_from_mcs(&material_diffuse, diffuse_source, + &state->material.diffuse, i, stream_info); + + if (lighting) + { + wined3d_colour_from_mcs(&material_ambient, ambient_source, + &state->material.ambient, i, stream_info); + wined3d_colour_from_mcs(&material_emissive, emissive_source, + &state->material.emissive, i, stream_info); + + diffuse_colour.r = ambient.r * material_ambient.r + + diffuse.r * material_diffuse.r + material_emissive.r; + diffuse_colour.g = ambient.g * material_ambient.g + + diffuse.g * material_diffuse.g + material_emissive.g; + diffuse_colour.b = ambient.b * material_ambient.b + + diffuse.b * material_diffuse.b + material_emissive.b; + diffuse_colour.a = material_diffuse.a; + } + else + { + diffuse_colour = material_diffuse; + } + wined3d_color_clamp(&diffuse_colour, &diffuse_colour, 0.0f, 1.0f); + *((DWORD *)dest_ptr) = wined3d_format_convert_from_float(output_colour_format, &diffuse_colour); + dest_ptr += sizeof(DWORD); + } + + if (dst_fvf & WINED3DFVF_SPECULAR) + { + struct wined3d_color material_specular, specular_colour; + + wined3d_colour_from_mcs(&material_specular, specular_source, + material_specular_state_colour, i, stream_info); + + if (lighting) + { + specular_colour.r = specular.r * material_specular.r; + specular_colour.g = specular.g * material_specular.g; + specular_colour.b = specular.b * material_specular.b; + specular_colour.a = ls.legacy_lighting ? 0.0f : material_specular.a; + } + else + { + specular_colour = material_specular; + } + update_fog_factor(&specular_colour.a, &ls); + wined3d_color_clamp(&specular_colour, &specular_colour, 0.0f, 1.0f); + *((DWORD *)dest_ptr) = wined3d_format_convert_from_float(output_colour_format, &specular_colour); + dest_ptr += sizeof(DWORD); + } + + for (tex_index = 0; tex_index < texture_count; ++tex_index) + { + const struct wined3d_stream_info_element *element = &stream_info->elements[WINED3D_FFP_TEXCOORD0 + tex_index]; + const float *tex_coord = (const float *)(element->data.addr + i * element->stride); + if (!(stream_info->use_map & (1u << (WINED3D_FFP_TEXCOORD0 + tex_index)))) + { + ERR("No source texture, but destination requests one\n"); + dest_ptr += GET_TEXCOORD_SIZE_FROM_FVF(dst_fvf, tex_index) * sizeof(float); + } + else + { + copy_and_next(dest_ptr, tex_coord, GET_TEXCOORD_SIZE_FROM_FVF(dst_fvf, tex_index) * sizeof(float)); + } + } + } + + wined3d_resource_unmap(&dest->resource, 0); + + return WINED3D_OK; +} +#undef copy_and_next + +HRESULT CDECL wined3d_device_process_vertices(struct wined3d_device *device, + UINT src_start_idx, UINT dst_idx, UINT vertex_count, struct wined3d_buffer *dst_buffer, + const struct wined3d_vertex_declaration *declaration, DWORD flags, DWORD dst_fvf) +{ + struct wined3d_state *state = &device->state; + struct wined3d_stream_info stream_info; + struct wined3d_resource *resource; + struct wined3d_box box = {0}; + struct wined3d_shader *vs; + unsigned int i, j; + HRESULT hr; + WORD map; + + TRACE("device %p, src_start_idx %u, dst_idx %u, vertex_count %u, " + "dst_buffer %p, declaration %p, flags %#x, dst_fvf %#x.\n", + device, src_start_idx, dst_idx, vertex_count, + dst_buffer, declaration, flags, dst_fvf); + + if (declaration) + FIXME("Output vertex declaration not implemented yet.\n"); + + vs = state->shader[WINED3D_SHADER_TYPE_VERTEX]; + state->shader[WINED3D_SHADER_TYPE_VERTEX] = NULL; + wined3d_stream_info_from_declaration(&stream_info, state, &device->adapter->d3d_info); + state->shader[WINED3D_SHADER_TYPE_VERTEX] = vs; + + /* We can't convert FROM a VBO, and vertex buffers used to source into + * process_vertices() are unlikely to ever be used for drawing. Release + * VBOs in those buffers and fix up the stream_info structure. + * + * Also apply the start index. */ + for (i = 0, map = stream_info.use_map; map; map >>= 1, ++i) + { + struct wined3d_stream_info_element *e; + struct wined3d_map_desc map_desc; + + if (!(map & 1)) + continue; + + e = &stream_info.elements[i]; + resource = &state->streams[e->stream_idx].buffer->resource; + box.left = src_start_idx * e->stride; + box.right = box.left + vertex_count * e->stride; + if (FAILED(wined3d_resource_map(resource, 0, &map_desc, &box, WINED3D_MAP_READ))) + { + ERR("Failed to map resource.\n"); + for (j = 0, map = stream_info.use_map; map && j < i; map >>= 1, ++j) + { + if (!(map & 1)) + continue; + + e = &stream_info.elements[j]; + resource = &state->streams[e->stream_idx].buffer->resource; + if (FAILED(wined3d_resource_unmap(resource, 0))) + ERR("Failed to unmap resource.\n"); + } + return WINED3DERR_INVALIDCALL; + } + e->data.buffer_object = 0; + e->data.addr += (ULONG_PTR)map_desc.data; + } + + hr = process_vertices_strided(device, dst_idx, vertex_count, + &stream_info, dst_buffer, flags, dst_fvf); + + for (i = 0, map = stream_info.use_map; map; map >>= 1, ++i) + { + if (!(map & 1)) + continue; + + resource = &state->streams[stream_info.elements[i].stream_idx].buffer->resource; + if (FAILED(wined3d_resource_unmap(resource, 0))) + ERR("Failed to unmap resource.\n"); + } + + return hr; +} + +static void wined3d_device_set_texture_stage_state(struct wined3d_device *device, + UINT stage, enum wined3d_texture_stage_state state, DWORD value) +{ + const struct wined3d_d3d_info *d3d_info = &device->adapter->d3d_info; + + TRACE("device %p, stage %u, state %s, value %#x.\n", + device, stage, debug_d3dtexturestate(state), value); + + if (stage >= d3d_info->limits.ffp_blend_stages) + { + WARN("Attempting to set stage %u which is higher than the max stage %u, ignoring.\n", + stage, d3d_info->limits.ffp_blend_stages - 1); + return; + } + + if (value == device->state.texture_states[stage][state]) + { + TRACE("Application is setting the old value over, nothing to do.\n"); + return; + } + + device->state.texture_states[stage][state] = value; + + wined3d_cs_emit_set_texture_state(device->cs, stage, state, value); +} + +static void wined3d_device_set_texture(struct wined3d_device *device, + UINT stage, struct wined3d_texture *texture) +{ + struct wined3d_texture *prev; + + TRACE("device %p, stage %u, texture %p.\n", device, stage, texture); + + /* Windows accepts overflowing this array... we do not. */ + if (stage >= ARRAY_SIZE(device->state.textures)) + { + WARN("Ignoring invalid stage %u.\n", stage); + return; + } + + prev = device->state.textures[stage]; + TRACE("Previous texture %p.\n", prev); + + if (texture == prev) + { + TRACE("App is setting the same texture again, nothing to do.\n"); + return; + } + + TRACE("Setting new texture to %p.\n", texture); + device->state.textures[stage] = texture; + + if (texture) + wined3d_texture_incref(texture); + wined3d_cs_emit_set_texture(device->cs, stage, texture); + if (prev) + wined3d_texture_decref(prev); + + return; +} + +void CDECL wined3d_device_apply_stateblock(struct wined3d_device *device, + struct wined3d_stateblock *stateblock) +{ + BOOL set_blend_state = FALSE, set_depth_stencil_state = FALSE, set_rasterizer_state = FALSE; + const struct wined3d_stateblock_state *state = &stateblock->stateblock_state; + const struct wined3d_saved_states *changed = &stateblock->changed; + const unsigned int word_bit_count = sizeof(DWORD) * CHAR_BIT; + unsigned int i, j, start, idx; + struct wined3d_range range; + uint32_t map; + + TRACE("device %p, stateblock %p.\n", device, stateblock); + + if (changed->vertexShader) + wined3d_device_set_vertex_shader(device, state->vs); + if (changed->pixelShader) + wined3d_device_set_pixel_shader(device, state->ps); + + for (start = 0; ; start = range.offset + range.size) + { + if (!wined3d_bitmap_get_range(changed->vs_consts_f, WINED3D_MAX_VS_CONSTS_F, start, &range)) + break; + + wined3d_device_set_vs_consts_f(device, range.offset, range.size, &state->vs_consts_f[range.offset]); + } + + map = changed->vertexShaderConstantsI; + for (start = 0; ; start = range.offset + range.size) + { + if (!wined3d_bitmap_get_range(&map, WINED3D_MAX_CONSTS_I, start, &range)) + break; + + wined3d_device_set_vs_consts_i(device, range.offset, range.size, &state->vs_consts_i[range.offset]); + } + + map = changed->vertexShaderConstantsB; + for (start = 0; ; start = range.offset + range.size) + { + if (!wined3d_bitmap_get_range(&map, WINED3D_MAX_CONSTS_B, start, &range)) + break; + + wined3d_device_set_vs_consts_b(device, range.offset, range.size, &state->vs_consts_b[range.offset]); + } + + for (start = 0; ; start = range.offset + range.size) + { + if (!wined3d_bitmap_get_range(changed->ps_consts_f, WINED3D_MAX_PS_CONSTS_F, start, &range)) + break; + + wined3d_device_set_ps_consts_f(device, range.offset, range.size, &state->ps_consts_f[range.offset]); + } + + map = changed->pixelShaderConstantsI; + for (start = 0; ; start = range.offset + range.size) + { + if (!wined3d_bitmap_get_range(&map, WINED3D_MAX_CONSTS_I, start, &range)) + break; + + wined3d_device_set_ps_consts_i(device, range.offset, range.size, &state->ps_consts_i[range.offset]); + } + + map = changed->pixelShaderConstantsB; + for (start = 0; ; start = range.offset + range.size) + { + if (!wined3d_bitmap_get_range(&map, WINED3D_MAX_CONSTS_B, start, &range)) + break; + + wined3d_device_set_ps_consts_b(device, range.offset, range.size, &state->ps_consts_b[range.offset]); + } + + if (changed->lights) + { + for (i = 0; i < ARRAY_SIZE(state->light_state->light_map); ++i) + { + const struct wined3d_light_info *light; + + LIST_FOR_EACH_ENTRY(light, &state->light_state->light_map[i], struct wined3d_light_info, entry) + { + wined3d_device_set_light(device, light->OriginalIndex, &light->OriginalParms); + wined3d_device_set_light_enable(device, light->OriginalIndex, light->glIndex != -1); + } + } + } + + for (i = 0; i < ARRAY_SIZE(changed->renderState); ++i) + { + map = changed->renderState[i]; + while (map) + { + j = wined3d_bit_scan(&map); + idx = i * word_bit_count + j; + + switch (idx) + { + case WINED3D_RS_BLENDFACTOR: + case WINED3D_RS_MULTISAMPLEMASK: + case WINED3D_RS_ALPHABLENDENABLE: + case WINED3D_RS_SRCBLEND: + case WINED3D_RS_DESTBLEND: + case WINED3D_RS_BLENDOP: + case WINED3D_RS_SEPARATEALPHABLENDENABLE: + case WINED3D_RS_SRCBLENDALPHA: + case WINED3D_RS_DESTBLENDALPHA: + case WINED3D_RS_BLENDOPALPHA: + case WINED3D_RS_COLORWRITEENABLE: + case WINED3D_RS_COLORWRITEENABLE1: + case WINED3D_RS_COLORWRITEENABLE2: + case WINED3D_RS_COLORWRITEENABLE3: + set_blend_state = TRUE; + break; + + case WINED3D_RS_BACK_STENCILFAIL: + case WINED3D_RS_BACK_STENCILFUNC: + case WINED3D_RS_BACK_STENCILPASS: + case WINED3D_RS_BACK_STENCILZFAIL: + case WINED3D_RS_STENCILENABLE: + case WINED3D_RS_STENCILFAIL: + case WINED3D_RS_STENCILFUNC: + case WINED3D_RS_STENCILREF: + case WINED3D_RS_STENCILMASK: + case WINED3D_RS_STENCILPASS: + case WINED3D_RS_STENCILWRITEMASK: + case WINED3D_RS_STENCILZFAIL: + case WINED3D_RS_TWOSIDEDSTENCILMODE: + case WINED3D_RS_ZENABLE: + case WINED3D_RS_ZFUNC: + case WINED3D_RS_ZWRITEENABLE: + set_depth_stencil_state = TRUE; + break; + + case WINED3D_RS_FILLMODE: + case WINED3D_RS_CULLMODE: + case WINED3D_RS_SLOPESCALEDEPTHBIAS: + case WINED3D_RS_DEPTHBIAS: + case WINED3D_RS_SCISSORTESTENABLE: + case WINED3D_RS_ANTIALIASEDLINEENABLE: + set_rasterizer_state = TRUE; + break; + + default: + wined3d_device_set_render_state(device, idx, state->rs[idx]); + break; + } + } + } + + if (set_rasterizer_state) + { + struct wined3d_rasterizer_state *rasterizer_state; + struct wined3d_rasterizer_state_desc desc; + struct wine_rb_entry *entry; + union + { + DWORD d; + float f; + } bias; + + memset(&desc, 0, sizeof(desc)); + desc.fill_mode = state->rs[WINED3D_RS_FILLMODE]; + desc.cull_mode = state->rs[WINED3D_RS_CULLMODE]; + bias.d = state->rs[WINED3D_RS_DEPTHBIAS]; + desc.depth_bias = bias.f; + bias.d = state->rs[WINED3D_RS_SLOPESCALEDEPTHBIAS]; + desc.scale_bias = bias.f; + desc.depth_clip = TRUE; + desc.scissor = state->rs[WINED3D_RS_SCISSORTESTENABLE]; + desc.line_antialias = state->rs[WINED3D_RS_ANTIALIASEDLINEENABLE]; + + if ((entry = wine_rb_get(&device->rasterizer_states, &desc))) + { + rasterizer_state = WINE_RB_ENTRY_VALUE(entry, struct wined3d_rasterizer_state, entry); + wined3d_device_set_rasterizer_state(device, rasterizer_state); + } + else if (SUCCEEDED(wined3d_rasterizer_state_create(device, &desc, NULL, + &wined3d_null_parent_ops, &rasterizer_state))) + { + wined3d_device_set_rasterizer_state(device, rasterizer_state); + if (wine_rb_put(&device->rasterizer_states, &desc, &rasterizer_state->entry) == -1) + { + ERR("Failed to insert rasterizer state.\n"); + wined3d_rasterizer_state_decref(rasterizer_state); + } + } + } + + if (set_blend_state || changed->alpha_to_coverage + || wined3d_bitmap_is_set(changed->renderState, WINED3D_RS_ADAPTIVETESS_Y)) + { + struct wined3d_blend_state *blend_state; + struct wined3d_blend_state_desc desc; + struct wine_rb_entry *entry; + struct wined3d_color colour; + unsigned int sample_mask; + + memset(&desc, 0, sizeof(desc)); + desc.alpha_to_coverage = state->alpha_to_coverage; + desc.independent = FALSE; + if (state->rs[WINED3D_RS_ADAPTIVETESS_Y] == WINED3DFMT_ATOC) + desc.alpha_to_coverage = TRUE; + desc.rt[0].enable = state->rs[WINED3D_RS_ALPHABLENDENABLE]; + desc.rt[0].src = state->rs[WINED3D_RS_SRCBLEND]; + desc.rt[0].dst = state->rs[WINED3D_RS_DESTBLEND]; + desc.rt[0].op = state->rs[WINED3D_RS_BLENDOP]; + if (state->rs[WINED3D_RS_SEPARATEALPHABLENDENABLE]) + { + desc.rt[0].src_alpha = state->rs[WINED3D_RS_SRCBLENDALPHA]; + desc.rt[0].dst_alpha = state->rs[WINED3D_RS_DESTBLENDALPHA]; + desc.rt[0].op_alpha = state->rs[WINED3D_RS_BLENDOPALPHA]; + } + else + { + desc.rt[0].src_alpha = state->rs[WINED3D_RS_SRCBLEND]; + desc.rt[0].dst_alpha = state->rs[WINED3D_RS_DESTBLEND]; + desc.rt[0].op_alpha = state->rs[WINED3D_RS_BLENDOP]; + } + desc.rt[0].writemask = state->rs[WINED3D_RS_COLORWRITEENABLE]; + desc.rt[1].writemask = state->rs[WINED3D_RS_COLORWRITEENABLE1]; + desc.rt[2].writemask = state->rs[WINED3D_RS_COLORWRITEENABLE2]; + desc.rt[3].writemask = state->rs[WINED3D_RS_COLORWRITEENABLE3]; + if (desc.rt[1].writemask != desc.rt[0].writemask + || desc.rt[2].writemask != desc.rt[0].writemask + || desc.rt[3].writemask != desc.rt[0].writemask) + { + desc.independent = TRUE; + for (i = 1; i < 4; ++i) + { + desc.rt[i].enable = desc.rt[0].enable; + desc.rt[i].src = desc.rt[0].src; + desc.rt[i].dst = desc.rt[0].dst; + desc.rt[i].op = desc.rt[0].op; + desc.rt[i].src_alpha = desc.rt[0].src_alpha; + desc.rt[i].dst_alpha = desc.rt[0].dst_alpha; + desc.rt[i].op_alpha = desc.rt[0].op_alpha; + } + } + + if (wined3d_bitmap_is_set(changed->renderState, WINED3D_RS_BLENDFACTOR)) + wined3d_color_from_d3dcolor(&colour, state->rs[WINED3D_RS_BLENDFACTOR]); + else + wined3d_device_get_blend_state(device, &colour, &sample_mask); + + if ((entry = wine_rb_get(&device->blend_states, &desc))) + { + blend_state = WINE_RB_ENTRY_VALUE(entry, struct wined3d_blend_state, entry); + wined3d_device_set_blend_state(device, blend_state, &colour, state->rs[WINED3D_RS_MULTISAMPLEMASK]); + } + else if (SUCCEEDED(wined3d_blend_state_create(device, &desc, NULL, + &wined3d_null_parent_ops, &blend_state))) + { + wined3d_device_set_blend_state(device, blend_state, &colour, state->rs[WINED3D_RS_MULTISAMPLEMASK]); + if (wine_rb_put(&device->blend_states, &desc, &blend_state->entry) == -1) + { + ERR("Failed to insert blend state.\n"); + wined3d_blend_state_decref(blend_state); + } + } + } + + if (set_depth_stencil_state) + { + struct wined3d_depth_stencil_state *depth_stencil_state; + struct wined3d_depth_stencil_state_desc desc; + struct wine_rb_entry *entry; + unsigned int stencil_ref; + + memset(&desc, 0, sizeof(desc)); + switch (state->rs[WINED3D_RS_ZENABLE]) + { + case WINED3D_ZB_FALSE: + desc.depth = FALSE; + break; + + case WINED3D_ZB_USEW: + FIXME("W buffer is not well handled.\n"); + case WINED3D_ZB_TRUE: + desc.depth = TRUE; + break; + + default: + FIXME("Unrecognized depth buffer type %#x.\n", state->rs[WINED3D_RS_ZENABLE]); + } + desc.depth_write = state->rs[WINED3D_RS_ZWRITEENABLE]; + desc.depth_func = state->rs[WINED3D_RS_ZFUNC]; + desc.stencil = state->rs[WINED3D_RS_STENCILENABLE]; + desc.stencil_read_mask = state->rs[WINED3D_RS_STENCILMASK]; + desc.stencil_write_mask = state->rs[WINED3D_RS_STENCILWRITEMASK]; + desc.front.fail_op = state->rs[WINED3D_RS_STENCILFAIL]; + desc.front.depth_fail_op = state->rs[WINED3D_RS_STENCILZFAIL]; + desc.front.pass_op = state->rs[WINED3D_RS_STENCILPASS]; + desc.front.func = state->rs[WINED3D_RS_STENCILFUNC]; + + if (state->rs[WINED3D_RS_TWOSIDEDSTENCILMODE]) + { + desc.back.fail_op = state->rs[WINED3D_RS_BACK_STENCILFAIL]; + desc.back.depth_fail_op = state->rs[WINED3D_RS_BACK_STENCILZFAIL]; + desc.back.pass_op = state->rs[WINED3D_RS_BACK_STENCILPASS]; + desc.back.func = state->rs[WINED3D_RS_BACK_STENCILFUNC]; + } + else + { + desc.back = desc.front; + } + + if (wined3d_bitmap_is_set(changed->renderState, WINED3D_RS_STENCILREF)) + stencil_ref = state->rs[WINED3D_RS_STENCILREF]; + else + wined3d_device_get_depth_stencil_state(device, &stencil_ref); + + if ((entry = wine_rb_get(&device->depth_stencil_states, &desc))) + { + depth_stencil_state = WINE_RB_ENTRY_VALUE(entry, struct wined3d_depth_stencil_state, entry); + wined3d_device_set_depth_stencil_state(device, depth_stencil_state, stencil_ref); + } + else if (SUCCEEDED(wined3d_depth_stencil_state_create(device, &desc, NULL, + &wined3d_null_parent_ops, &depth_stencil_state))) + { + wined3d_device_set_depth_stencil_state(device, depth_stencil_state, stencil_ref); + if (wine_rb_put(&device->depth_stencil_states, &desc, &depth_stencil_state->entry) == -1) + { + ERR("Failed to insert depth/stencil state.\n"); + wined3d_depth_stencil_state_decref(depth_stencil_state); + } + } + } + + for (i = 0; i < ARRAY_SIZE(changed->textureState); ++i) + { + map = changed->textureState[i]; + while (map) + { + j = wined3d_bit_scan(&map); + wined3d_device_set_texture_stage_state(device, i, j, state->texture_states[i][j]); + } + } + + for (i = 0; i < ARRAY_SIZE(changed->samplerState); ++i) + { + map = changed->samplerState[i]; + while (map) + { + j = wined3d_bit_scan(&map); + wined3d_device_set_sampler_state(device, i, j, state->sampler_states[i][j]); + } + } + + if (changed->transforms) + { + for (i = 0; i < ARRAY_SIZE(changed->transform); ++i) + { + map = changed->transform[i]; + while (map) + { + j = wined3d_bit_scan(&map); + idx = i * word_bit_count + j; + wined3d_device_set_transform(device, idx, &state->transforms[idx]); + } + } + } + + if (changed->indices) + wined3d_device_set_index_buffer(device, state->index_buffer, state->index_format, 0); + wined3d_device_set_base_vertex_index(device, state->base_vertex_index); + if (changed->vertexDecl) + wined3d_device_set_vertex_declaration(device, state->vertex_declaration); + if (changed->material) + wined3d_device_set_material(device, &state->material); + if (changed->viewport) + wined3d_device_set_viewports(device, 1, &state->viewport); + if (changed->scissorRect) + wined3d_device_set_scissor_rects(device, 1, &state->scissor_rect); + + map = changed->streamSource; + while (map) + { + i = wined3d_bit_scan(&map); + wined3d_device_set_stream_source(device, i, state->streams[i].buffer, + state->streams[i].offset, state->streams[i].stride); + } + map = changed->streamFreq; + while (map) + { + i = wined3d_bit_scan(&map); + wined3d_device_set_stream_source_freq(device, i, + state->streams[i].frequency | state->streams[i].flags); + } + + map = changed->textures; + while (map) + { + i = wined3d_bit_scan(&map); + wined3d_device_set_texture(device, i, state->textures[i]); + } + + map = changed->clipplane; + while (map) + { + i = wined3d_bit_scan(&map); + wined3d_device_set_clip_plane(device, i, &state->clip_planes[i]); + } + + memset(&stateblock->changed, 0, sizeof(stateblock->changed)); + + TRACE("Applied stateblock %p.\n", stateblock); +} + +HRESULT CDECL wined3d_device_get_device_caps(const struct wined3d_device *device, struct wined3d_caps *caps) +{ + TRACE("device %p, caps %p.\n", device, caps); + + return wined3d_get_device_caps(device->adapter, device->create_parms.device_type, caps); +} + +HRESULT CDECL wined3d_device_get_display_mode(const struct wined3d_device *device, UINT swapchain_idx, + struct wined3d_display_mode *mode, enum wined3d_display_rotation *rotation) +{ + struct wined3d_swapchain *swapchain; + + TRACE("device %p, swapchain_idx %u, mode %p, rotation %p.\n", + device, swapchain_idx, mode, rotation); + + if (!(swapchain = wined3d_device_get_swapchain(device, swapchain_idx))) + return WINED3DERR_INVALIDCALL; + + return wined3d_swapchain_get_display_mode(swapchain, mode, rotation); +} + +HRESULT CDECL wined3d_device_begin_scene(struct wined3d_device *device) +{ + /* At the moment we have no need for any functionality at the beginning + * of a scene. */ + TRACE("device %p.\n", device); + + if (device->inScene) + { + WARN("Already in scene, returning WINED3DERR_INVALIDCALL.\n"); + return WINED3DERR_INVALIDCALL; + } + device->inScene = TRUE; + return WINED3D_OK; +} + +HRESULT CDECL wined3d_device_end_scene(struct wined3d_device *device) +{ + TRACE("device %p.\n", device); + + if (!device->inScene) + { + WARN("Not in scene, returning WINED3DERR_INVALIDCALL.\n"); + return WINED3DERR_INVALIDCALL; + } + + device->inScene = FALSE; + return WINED3D_OK; +} + +HRESULT CDECL wined3d_device_clear(struct wined3d_device *device, DWORD rect_count, + const RECT *rects, DWORD flags, const struct wined3d_color *color, float depth, DWORD stencil) +{ + TRACE("device %p, rect_count %u, rects %p, flags %#x, color %s, depth %.8e, stencil %u.\n", + device, rect_count, rects, flags, debug_color(color), depth, stencil); + + if (!rect_count && rects) + { + WARN("Rects is %p, but rect_count is 0, ignoring clear\n", rects); + return WINED3D_OK; + } + + if (flags & (WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL)) + { + struct wined3d_rendertarget_view *ds = device->state.fb.depth_stencil; + if (!ds) + { + WARN("Clearing depth and/or stencil without a depth stencil buffer attached, returning WINED3DERR_INVALIDCALL\n"); + /* TODO: What about depth stencil buffers without stencil bits? */ + return WINED3DERR_INVALIDCALL; + } + else if (flags & WINED3DCLEAR_TARGET) + { + if (ds->width < device->state.fb.render_targets[0]->width + || ds->height < device->state.fb.render_targets[0]->height) + { + WARN("Silently ignoring depth and target clear with mismatching sizes\n"); + return WINED3D_OK; + } + } + } + + wined3d_cs_emit_clear(device->cs, rect_count, rects, flags, color, depth, stencil); + + return WINED3D_OK; +} + +void CDECL wined3d_device_set_predication(struct wined3d_device *device, + struct wined3d_query *predicate, BOOL value) +{ + struct wined3d_query *prev; + + TRACE("device %p, predicate %p, value %#x.\n", device, predicate, value); + + prev = device->state.predicate; + if (predicate) + { + FIXME("Predicated rendering not implemented.\n"); + wined3d_query_incref(predicate); + } + device->state.predicate = predicate; + device->state.predicate_value = value; + wined3d_cs_emit_set_predication(device->cs, predicate, value); + if (prev) + wined3d_query_decref(prev); +} + +struct wined3d_query * CDECL wined3d_device_get_predication(struct wined3d_device *device, BOOL *value) +{ + TRACE("device %p, value %p.\n", device, value); + + if (value) + *value = device->state.predicate_value; + return device->state.predicate; +} + +void CDECL wined3d_device_dispatch_compute(struct wined3d_device *device, + unsigned int group_count_x, unsigned int group_count_y, unsigned int group_count_z) +{ + TRACE("device %p, group_count_x %u, group_count_y %u, group_count_z %u.\n", + device, group_count_x, group_count_y, group_count_z); + + wined3d_cs_emit_dispatch(device->cs, group_count_x, group_count_y, group_count_z); +} + +void CDECL wined3d_device_dispatch_compute_indirect(struct wined3d_device *device, + struct wined3d_buffer *buffer, unsigned int offset) +{ + TRACE("device %p, buffer %p, offset %u.\n", device, buffer, offset); + + wined3d_cs_emit_dispatch_indirect(device->cs, buffer, offset); +} + +void CDECL wined3d_device_set_primitive_type(struct wined3d_device *device, + enum wined3d_primitive_type primitive_type, unsigned int patch_vertex_count) +{ + TRACE("device %p, primitive_type %s, patch_vertex_count %u.\n", + device, debug_d3dprimitivetype(primitive_type), patch_vertex_count); + + device->state.primitive_type = primitive_type; + device->state.patch_vertex_count = patch_vertex_count; +} + +void CDECL wined3d_device_get_primitive_type(const struct wined3d_device *device, + enum wined3d_primitive_type *primitive_type, unsigned int *patch_vertex_count) +{ + TRACE("device %p, primitive_type %p, patch_vertex_count %p.\n", + device, primitive_type, patch_vertex_count); + + *primitive_type = device->state.primitive_type; + if (patch_vertex_count) + *patch_vertex_count = device->state.patch_vertex_count; + + TRACE("Returning %s.\n", debug_d3dprimitivetype(*primitive_type)); +} + +HRESULT CDECL wined3d_device_draw_primitive(struct wined3d_device *device, UINT start_vertex, UINT vertex_count) +{ + TRACE("device %p, start_vertex %u, vertex_count %u.\n", device, start_vertex, vertex_count); + + wined3d_cs_emit_draw(device->cs, device->state.primitive_type, + device->state.patch_vertex_count, 0, start_vertex, vertex_count, 0, 0, false); + + return WINED3D_OK; +} + +void CDECL wined3d_device_draw_primitive_instanced(struct wined3d_device *device, + UINT start_vertex, UINT vertex_count, UINT start_instance, UINT instance_count) +{ + TRACE("device %p, start_vertex %u, vertex_count %u, start_instance %u, instance_count %u.\n", + device, start_vertex, vertex_count, start_instance, instance_count); + + wined3d_cs_emit_draw(device->cs, device->state.primitive_type, device->state.patch_vertex_count, + 0, start_vertex, vertex_count, start_instance, instance_count, false); +} + +void CDECL wined3d_device_draw_primitive_instanced_indirect(struct wined3d_device *device, + struct wined3d_buffer *buffer, unsigned int offset) +{ + TRACE("device %p, buffer %p, offset %u.\n", device, buffer, offset); + + wined3d_cs_emit_draw_indirect(device->cs, device->state.primitive_type, + device->state.patch_vertex_count, buffer, offset, false); +} + +HRESULT CDECL wined3d_device_draw_indexed_primitive(struct wined3d_device *device, UINT start_idx, UINT index_count) +{ + TRACE("device %p, start_idx %u, index_count %u.\n", device, start_idx, index_count); + + if (!device->state.index_buffer) + { + /* D3D9 returns D3DERR_INVALIDCALL when DrawIndexedPrimitive is called + * without an index buffer set. (The first time at least...) + * D3D8 simply dies, but I doubt it can do much harm to return + * D3DERR_INVALIDCALL there as well. */ + WARN("Called without a valid index buffer set, returning WINED3DERR_INVALIDCALL.\n"); + return WINED3DERR_INVALIDCALL; + } + + wined3d_cs_emit_draw(device->cs, device->state.primitive_type, device->state.patch_vertex_count, + device->state.base_vertex_index, start_idx, index_count, 0, 0, true); + + return WINED3D_OK; +} + +void CDECL wined3d_device_draw_indexed_primitive_instanced(struct wined3d_device *device, + UINT start_idx, UINT index_count, UINT start_instance, UINT instance_count) +{ + TRACE("device %p, start_idx %u, index_count %u, start_instance %u, instance_count %u.\n", + device, start_idx, index_count, start_instance, instance_count); + + wined3d_cs_emit_draw(device->cs, device->state.primitive_type, device->state.patch_vertex_count, + device->state.base_vertex_index, start_idx, index_count, start_instance, instance_count, true); +} + +void CDECL wined3d_device_draw_indexed_primitive_instanced_indirect(struct wined3d_device *device, + struct wined3d_buffer *buffer, unsigned int offset) +{ + TRACE("device %p, buffer %p, offset %u.\n", device, buffer, offset); + + wined3d_cs_emit_draw_indirect(device->cs, device->state.primitive_type, + device->state.patch_vertex_count, buffer, offset, true); +} + +HRESULT CDECL wined3d_device_update_texture(struct wined3d_device *device, + struct wined3d_texture *src_texture, struct wined3d_texture *dst_texture) +{ + unsigned int src_size, dst_size, src_skip_levels = 0; + unsigned int src_level_count, dst_level_count; + const struct wined3d_dirty_regions *regions; + unsigned int layer_count, level_count, i, j; + enum wined3d_resource_type type; + BOOL entire_texture = TRUE; + struct wined3d_box box; + + TRACE("device %p, src_texture %p, dst_texture %p.\n", device, src_texture, dst_texture); + + /* Verify that the source and destination textures are non-NULL. */ + if (!src_texture || !dst_texture) + { + WARN("Source and destination textures must be non-NULL, returning WINED3DERR_INVALIDCALL.\n"); + return WINED3DERR_INVALIDCALL; + } + + if (src_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU + || src_texture->resource.usage & WINED3DUSAGE_SCRATCH) + { + WARN("Source resource is GPU accessible or a scratch resource.\n"); + return WINED3DERR_INVALIDCALL; + } + if (dst_texture->resource.access & WINED3D_RESOURCE_ACCESS_CPU) + { + WARN("Destination resource is CPU accessible.\n"); + return WINED3DERR_INVALIDCALL; + } + + /* Verify that the source and destination textures are the same type. */ + type = src_texture->resource.type; + if (dst_texture->resource.type != type) + { + WARN("Source and destination have different types, returning WINED3DERR_INVALIDCALL.\n"); + return WINED3DERR_INVALIDCALL; + } + + layer_count = src_texture->layer_count; + if (layer_count != dst_texture->layer_count) + { + WARN("Source and destination have different layer counts.\n"); + return WINED3DERR_INVALIDCALL; + } + + if (src_texture->resource.format != dst_texture->resource.format) + { + WARN("Source and destination formats do not match.\n"); + return WINED3DERR_INVALIDCALL; + } + + src_level_count = src_texture->level_count; + dst_level_count = dst_texture->level_count; + level_count = min(src_level_count, dst_level_count); + + src_size = max(src_texture->resource.width, src_texture->resource.height); + src_size = max(src_size, src_texture->resource.depth); + dst_size = max(dst_texture->resource.width, dst_texture->resource.height); + dst_size = max(dst_size, dst_texture->resource.depth); + while (src_size > dst_size) + { + src_size >>= 1; + ++src_skip_levels; + } + + if (wined3d_texture_get_level_width(src_texture, src_skip_levels) != dst_texture->resource.width + || wined3d_texture_get_level_height(src_texture, src_skip_levels) != dst_texture->resource.height + || wined3d_texture_get_level_depth(src_texture, src_skip_levels) != dst_texture->resource.depth) + { + WARN("Source and destination dimensions do not match.\n"); + return WINED3DERR_INVALIDCALL; + } + + if ((regions = src_texture->dirty_regions)) + { + for (i = 0; i < layer_count && entire_texture; ++i) + { + if (regions[i].box_count >= WINED3D_MAX_DIRTY_REGION_COUNT) + continue; + + entire_texture = FALSE; + break; + } + } + + /* Update every surface level of the texture. */ + if (entire_texture) + { + for (i = 0; i < level_count; ++i) + { + wined3d_texture_get_level_box(dst_texture, i, &box); + for (j = 0; j < layer_count; ++j) + { + wined3d_cs_emit_blt_sub_resource(device->cs, + &dst_texture->resource, j * dst_level_count + i, &box, + &src_texture->resource, j * src_level_count + i + src_skip_levels, &box, + 0, NULL, WINED3D_TEXF_POINT); + } + } + } + else + { + unsigned int src_level, box_count, k; + const struct wined3d_box *boxes; + struct wined3d_box b; + + for (i = 0; i < layer_count; ++i) + { + boxes = regions[i].boxes; + box_count = regions[i].box_count; + if (regions[i].box_count >= WINED3D_MAX_DIRTY_REGION_COUNT) + { + boxes = &b; + box_count = 1; + wined3d_texture_get_level_box(dst_texture, i, &b); + } + + for (j = 0; j < level_count; ++j) + { + src_level = j + src_skip_levels; + + /* TODO: We could pass an array of boxes here to avoid + * multiple context acquisitions for the same resource. */ + for (k = 0; k < box_count; ++k) + { + box = boxes[k]; + if (src_level) + { + box.left >>= src_level; + box.top >>= src_level; + box.right = min((box.right + (1u << src_level) - 1) >> src_level, + wined3d_texture_get_level_width(src_texture, src_level)); + box.bottom = min((box.bottom + (1u << src_level) - 1) >> src_level, + wined3d_texture_get_level_height(src_texture, src_level)); + box.front >>= src_level; + box.back = min((box.back + (1u << src_level) - 1) >> src_level, + wined3d_texture_get_level_depth(src_texture, src_level)); + } + + wined3d_cs_emit_blt_sub_resource(device->cs, + &dst_texture->resource, i * dst_level_count + j, &box, + &src_texture->resource, i * src_level_count + src_level, &box, + 0, NULL, WINED3D_TEXF_POINT); + } + } + } + } + + wined3d_texture_clear_dirty_regions(src_texture); + + return WINED3D_OK; +} + +HRESULT CDECL wined3d_device_validate_device(const struct wined3d_device *device, DWORD *num_passes) +{ + const struct wined3d_state *state = &device->state; + struct wined3d_texture *texture; + DWORD i; + + TRACE("device %p, num_passes %p.\n", device, num_passes); + + for (i = 0; i < WINED3D_MAX_COMBINED_SAMPLERS; ++i) + { + if (state->sampler_states[i][WINED3D_SAMP_MIN_FILTER] == WINED3D_TEXF_NONE) + { + WARN("Sampler state %u has minfilter D3DTEXF_NONE, returning D3DERR_UNSUPPORTEDTEXTUREFILTER\n", i); + return WINED3DERR_UNSUPPORTEDTEXTUREFILTER; + } + if (state->sampler_states[i][WINED3D_SAMP_MAG_FILTER] == WINED3D_TEXF_NONE) + { + WARN("Sampler state %u has magfilter D3DTEXF_NONE, returning D3DERR_UNSUPPORTEDTEXTUREFILTER\n", i); + return WINED3DERR_UNSUPPORTEDTEXTUREFILTER; + } + + texture = state->textures[i]; + if (!texture || texture->resource.format_flags & WINED3DFMT_FLAG_FILTERING) continue; + + if (state->sampler_states[i][WINED3D_SAMP_MAG_FILTER] != WINED3D_TEXF_POINT) + { + WARN("Non-filterable texture and mag filter enabled on sampler %u, returning E_FAIL\n", i); + return E_FAIL; + } + if (state->sampler_states[i][WINED3D_SAMP_MIN_FILTER] != WINED3D_TEXF_POINT) + { + WARN("Non-filterable texture and min filter enabled on sampler %u, returning E_FAIL\n", i); + return E_FAIL; + } + if (state->sampler_states[i][WINED3D_SAMP_MIP_FILTER] != WINED3D_TEXF_NONE + && state->sampler_states[i][WINED3D_SAMP_MIP_FILTER] != WINED3D_TEXF_POINT) + { + WARN("Non-filterable texture and mip filter enabled on sampler %u, returning E_FAIL\n", i); + return E_FAIL; + } + } + + if (wined3d_state_uses_depth_buffer(state) + || (state->depth_stencil_state && state->depth_stencil_state->desc.stencil)) + { + struct wined3d_rendertarget_view *rt = device->state.fb.render_targets[0]; + struct wined3d_rendertarget_view *ds = device->state.fb.depth_stencil; + + if (ds && rt && (ds->width < rt->width || ds->height < rt->height)) + { + WARN("Depth stencil is smaller than the color buffer, returning D3DERR_CONFLICTINGRENDERSTATE\n"); + return WINED3DERR_CONFLICTINGRENDERSTATE; + } + } + + /* return a sensible default */ + *num_passes = 1; + + TRACE("returning D3D_OK\n"); + return WINED3D_OK; +} + +void CDECL wined3d_device_set_software_vertex_processing(struct wined3d_device *device, BOOL software) +{ + static BOOL warned; + + TRACE("device %p, software %#x.\n", device, software); + + if (!warned) + { + FIXME("device %p, software %#x stub!\n", device, software); + warned = TRUE; + } + + device->softwareVertexProcessing = software; +} + +BOOL CDECL wined3d_device_get_software_vertex_processing(const struct wined3d_device *device) +{ + static BOOL warned; + + TRACE("device %p.\n", device); + + if (!warned) + { + TRACE("device %p stub!\n", device); + warned = TRUE; + } + + return device->softwareVertexProcessing; +} + +HRESULT CDECL wined3d_device_get_raster_status(const struct wined3d_device *device, + UINT swapchain_idx, struct wined3d_raster_status *raster_status) +{ + struct wined3d_swapchain *swapchain; + + TRACE("device %p, swapchain_idx %u, raster_status %p.\n", + device, swapchain_idx, raster_status); + + if (!(swapchain = wined3d_device_get_swapchain(device, swapchain_idx))) + return WINED3DERR_INVALIDCALL; + + return wined3d_swapchain_get_raster_status(swapchain, raster_status); +} + +HRESULT CDECL wined3d_device_set_npatch_mode(struct wined3d_device *device, float segments) +{ + static BOOL warned; + + TRACE("device %p, segments %.8e.\n", device, segments); + + if (segments != 0.0f) + { + if (!warned) + { + FIXME("device %p, segments %.8e stub!\n", device, segments); + warned = TRUE; + } + } + + return WINED3D_OK; +} + +float CDECL wined3d_device_get_npatch_mode(const struct wined3d_device *device) +{ + static BOOL warned; + + TRACE("device %p.\n", device); + + if (!warned) + { + FIXME("device %p stub!\n", device); + warned = TRUE; + } + + return 0.0f; +} + +void CDECL wined3d_device_copy_uav_counter(struct wined3d_device *device, + struct wined3d_buffer *dst_buffer, unsigned int offset, struct wined3d_unordered_access_view *uav) +{ + TRACE("device %p, dst_buffer %p, offset %u, uav %p.\n", + device, dst_buffer, offset, uav); + + wined3d_cs_emit_copy_uav_counter(device->cs, dst_buffer, offset, uav); +} + +static bool resources_format_compatible(const struct wined3d_resource *src_resource, + const struct wined3d_resource *dst_resource) +{ + if (src_resource->format->id == dst_resource->format->id) + return true; + if (src_resource->format->typeless_id && src_resource->format->typeless_id == dst_resource->format->typeless_id) + return true; + if (src_resource->device->feature_level < WINED3D_FEATURE_LEVEL_10_1) + return false; + if ((src_resource->format_flags & WINED3DFMT_FLAG_BLOCKS) + && (dst_resource->format_flags & WINED3DFMT_FLAG_CAST_TO_BLOCK)) + return src_resource->format->block_byte_count == dst_resource->format->byte_count; + if ((src_resource->format_flags & WINED3DFMT_FLAG_CAST_TO_BLOCK) + && (dst_resource->format_flags & WINED3DFMT_FLAG_BLOCKS)) + return src_resource->format->byte_count == dst_resource->format->block_byte_count; + return false; +} + +void CDECL wined3d_device_copy_resource(struct wined3d_device *device, + struct wined3d_resource *dst_resource, struct wined3d_resource *src_resource) +{ + unsigned int src_row_block_count, dst_row_block_count; + struct wined3d_texture *dst_texture, *src_texture; + unsigned int src_row_count, dst_row_count; + struct wined3d_box src_box, dst_box; + unsigned int i, j; + + TRACE("device %p, dst_resource %p, src_resource %p.\n", device, dst_resource, src_resource); + + if (src_resource == dst_resource) + { + WARN("Source and destination are the same resource.\n"); + return; + } + + if (src_resource->type != dst_resource->type) + { + WARN("Resource types (%s / %s) don't match.\n", + debug_d3dresourcetype(dst_resource->type), + debug_d3dresourcetype(src_resource->type)); + return; + } + + if (!resources_format_compatible(src_resource, dst_resource)) + { + WARN("Resource formats %s and %s are incompatible.\n", + debug_d3dformat(dst_resource->format->id), + debug_d3dformat(src_resource->format->id)); + return; + } + + src_row_block_count = (src_resource->width + (src_resource->format->block_width - 1)) + / src_resource->format->block_width; + dst_row_block_count = (dst_resource->width + (dst_resource->format->block_width - 1)) + / dst_resource->format->block_width; + src_row_count = (src_resource->height + (src_resource->format->block_height - 1)) + / src_resource->format->block_height; + dst_row_count = (dst_resource->height + (dst_resource->format->block_height - 1)) + / dst_resource->format->block_height; + + if (src_row_block_count != dst_row_block_count || src_row_count != dst_row_count + || src_resource->depth != dst_resource->depth) + { + WARN("Resource block dimensions (%ux%ux%u / %ux%ux%u) don't match.\n", + dst_row_block_count, dst_row_count, dst_resource->depth, + src_row_block_count, src_row_count, src_resource->depth); + return; + } + + if (dst_resource->type == WINED3D_RTYPE_BUFFER) + { + wined3d_box_set(&src_box, 0, 0, src_resource->size, 1, 0, 1); + wined3d_cs_emit_blt_sub_resource(device->cs, dst_resource, 0, &src_box, + src_resource, 0, &src_box, WINED3D_BLT_RAW, NULL, WINED3D_TEXF_POINT); + return; + } + + dst_texture = texture_from_resource(dst_resource); + src_texture = texture_from_resource(src_resource); + + if (src_texture->layer_count != dst_texture->layer_count + || src_texture->level_count != dst_texture->level_count) + { + WARN("Subresource layouts (%ux%u / %ux%u) don't match.\n", + dst_texture->layer_count, dst_texture->level_count, + src_texture->layer_count, src_texture->level_count); + return; + } + + for (i = 0; i < dst_texture->level_count; ++i) + { + wined3d_texture_get_level_box(src_texture, i, &src_box); + wined3d_texture_get_level_box(dst_texture, i, &dst_box); + for (j = 0; j < dst_texture->layer_count; ++j) + { + unsigned int idx = j * dst_texture->level_count + i; + + wined3d_cs_emit_blt_sub_resource(device->cs, dst_resource, idx, &dst_box, + src_resource, idx, &src_box, WINED3D_BLT_RAW, NULL, WINED3D_TEXF_POINT); + } + } +} + +HRESULT CDECL wined3d_device_copy_sub_resource_region(struct wined3d_device *device, + struct wined3d_resource *dst_resource, unsigned int dst_sub_resource_idx, unsigned int dst_x, + unsigned int dst_y, unsigned int dst_z, struct wined3d_resource *src_resource, + unsigned int src_sub_resource_idx, const struct wined3d_box *src_box, unsigned int flags) +{ + struct wined3d_box dst_box, b; + + TRACE("device %p, dst_resource %p, dst_sub_resource_idx %u, dst_x %u, dst_y %u, dst_z %u, " + "src_resource %p, src_sub_resource_idx %u, src_box %s, flags %#x.\n", + device, dst_resource, dst_sub_resource_idx, dst_x, dst_y, dst_z, + src_resource, src_sub_resource_idx, debug_box(src_box), flags); + + if (flags) + FIXME("Ignoring flags %#x.\n", flags); + + if (src_resource == dst_resource && src_sub_resource_idx == dst_sub_resource_idx) + { + WARN("Source and destination are the same sub-resource.\n"); + return WINED3DERR_INVALIDCALL; + } + + if (!resources_format_compatible(src_resource, dst_resource)) + { + WARN("Resource formats %s and %s are incompatible.\n", + debug_d3dformat(dst_resource->format->id), + debug_d3dformat(src_resource->format->id)); + return WINED3DERR_INVALIDCALL; + } + + if (dst_resource->type == WINED3D_RTYPE_BUFFER) + { + if (src_resource->type != WINED3D_RTYPE_BUFFER) + { + WARN("Resource types (%s / %s) don't match.\n", + debug_d3dresourcetype(dst_resource->type), + debug_d3dresourcetype(src_resource->type)); + return WINED3DERR_INVALIDCALL; + } + + if (dst_sub_resource_idx) + { + WARN("Invalid dst_sub_resource_idx %u.\n", dst_sub_resource_idx); + return WINED3DERR_INVALIDCALL; + } + + if (src_sub_resource_idx) + { + WARN("Invalid src_sub_resource_idx %u.\n", src_sub_resource_idx); + return WINED3DERR_INVALIDCALL; + } + + if (!src_box) + { + unsigned int dst_w; + + dst_w = dst_resource->size - dst_x; + wined3d_box_set(&b, 0, 0, min(src_resource->size, dst_w), 1, 0, 1); + src_box = &b; + } + else if ((src_box->left >= src_box->right + || src_box->top >= src_box->bottom + || src_box->front >= src_box->back)) + { + WARN("Invalid box %s specified.\n", debug_box(src_box)); + return WINED3DERR_INVALIDCALL; + } + + if (src_box->right > src_resource->size || dst_x >= dst_resource->size + || src_box->right - src_box->left > dst_resource->size - dst_x) + { + WARN("Invalid range specified, dst_offset %u, src_offset %u, size %u.\n", + dst_x, src_box->left, src_box->right - src_box->left); + return WINED3DERR_INVALIDCALL; + } + + wined3d_box_set(&dst_box, dst_x, 0, dst_x + (src_box->right - src_box->left), 1, 0, 1); + } + else + { + struct wined3d_texture *dst_texture = texture_from_resource(dst_resource); + struct wined3d_texture *src_texture = texture_from_resource(src_resource); + unsigned int src_level = src_sub_resource_idx % src_texture->level_count; + unsigned int src_row_block_count, src_row_count; + + if (dst_sub_resource_idx >= dst_texture->level_count * dst_texture->layer_count) + { + WARN("Invalid destination sub-resource %u.\n", dst_sub_resource_idx); + return WINED3DERR_INVALIDCALL; + } + + if (src_sub_resource_idx >= src_texture->level_count * src_texture->layer_count) + { + WARN("Invalid source sub-resource %u.\n", src_sub_resource_idx); + return WINED3DERR_INVALIDCALL; + } + + if (dst_texture->sub_resources[dst_sub_resource_idx].map_count) + { + WARN("Destination sub-resource %u is mapped.\n", dst_sub_resource_idx); + return WINED3DERR_INVALIDCALL; + } + + if (src_texture->sub_resources[src_sub_resource_idx].map_count) + { + WARN("Source sub-resource %u is mapped.\n", src_sub_resource_idx); + return WINED3DERR_INVALIDCALL; + } + + if (!src_box) + { + unsigned int src_w, src_h, src_d, dst_w, dst_h, dst_d, dst_level; + + src_w = wined3d_texture_get_level_width(src_texture, src_level); + src_h = wined3d_texture_get_level_height(src_texture, src_level); + src_d = wined3d_texture_get_level_depth(src_texture, src_level); + + dst_level = dst_sub_resource_idx % dst_texture->level_count; + dst_w = wined3d_texture_get_level_width(dst_texture, dst_level) - dst_x; + dst_h = wined3d_texture_get_level_height(dst_texture, dst_level) - dst_y; + dst_d = wined3d_texture_get_level_depth(dst_texture, dst_level) - dst_z; + + wined3d_box_set(&b, 0, 0, min(src_w, dst_w), min(src_h, dst_h), 0, min(src_d, dst_d)); + src_box = &b; + } + else if (FAILED(wined3d_texture_check_box_dimensions(src_texture, src_level, src_box))) + { + WARN("Invalid source box %s.\n", debug_box(src_box)); + return WINED3DERR_INVALIDCALL; + } + + if (src_resource->format->block_width == dst_resource->format->block_width + && src_resource->format->block_height == dst_resource->format->block_height) + { + wined3d_box_set(&dst_box, dst_x, dst_y, dst_x + (src_box->right - src_box->left), + dst_y + (src_box->bottom - src_box->top), dst_z, dst_z + (src_box->back - src_box->front)); + } + else + { + src_row_block_count = (src_box->right - src_box->left + src_resource->format->block_width - 1) + / src_resource->format->block_width; + src_row_count = (src_box->bottom - src_box->top + src_resource->format->block_height - 1) + / src_resource->format->block_height; + wined3d_box_set(&dst_box, dst_x, dst_y, + dst_x + (src_row_block_count * dst_resource->format->block_width), + dst_y + (src_row_count * dst_resource->format->block_height), + dst_z, dst_z + (src_box->back - src_box->front)); + } + if (FAILED(wined3d_texture_check_box_dimensions(dst_texture, + dst_sub_resource_idx % dst_texture->level_count, &dst_box))) + { + WARN("Invalid destination box %s.\n", debug_box(&dst_box)); + return WINED3DERR_INVALIDCALL; + } + } + + wined3d_cs_emit_blt_sub_resource(device->cs, dst_resource, dst_sub_resource_idx, &dst_box, + src_resource, src_sub_resource_idx, src_box, WINED3D_BLT_RAW, NULL, WINED3D_TEXF_POINT); + + return WINED3D_OK; +} + +void CDECL wined3d_device_update_sub_resource(struct wined3d_device *device, struct wined3d_resource *resource, + unsigned int sub_resource_idx, const struct wined3d_box *box, const void *data, unsigned int row_pitch, + unsigned int depth_pitch, unsigned int flags) +{ + unsigned int width, height, depth; + struct wined3d_box b; + + TRACE("device %p, resource %p, sub_resource_idx %u, box %s, data %p, row_pitch %u, depth_pitch %u, " + "flags %#x.\n", + device, resource, sub_resource_idx, debug_box(box), data, row_pitch, depth_pitch, flags); + + if (flags) + FIXME("Ignoring flags %#x.\n", flags); + + if (!(resource->access & WINED3D_RESOURCE_ACCESS_GPU)) + { + WARN("Resource %p is not GPU accessible.\n", resource); + return; + } + + if (resource->type == WINED3D_RTYPE_BUFFER) + { + if (sub_resource_idx > 0) + { + WARN("Invalid sub_resource_idx %u.\n", sub_resource_idx); + return; + } + + width = resource->size; + height = 1; + depth = 1; + } + else + { + struct wined3d_texture *texture = texture_from_resource(resource); + unsigned int level; + + if (sub_resource_idx >= texture->level_count * texture->layer_count) + { + WARN("Invalid sub_resource_idx %u.\n", sub_resource_idx); + return; + } + + level = sub_resource_idx % texture->level_count; + width = wined3d_texture_get_level_width(texture, level); + height = wined3d_texture_get_level_height(texture, level); + depth = wined3d_texture_get_level_depth(texture, level); + } + + if (!box) + { + wined3d_box_set(&b, 0, 0, width, height, 0, depth); + box = &b; + } + else if (box->left >= box->right || box->right > width + || box->top >= box->bottom || box->bottom > height + || box->front >= box->back || box->back > depth) + { + WARN("Invalid box %s specified.\n", debug_box(box)); + return; + } + + wined3d_resource_wait_idle(resource); + + wined3d_cs_emit_update_sub_resource(device->cs, resource, sub_resource_idx, box, data, row_pitch, depth_pitch); +} + +void CDECL wined3d_device_resolve_sub_resource(struct wined3d_device *device, + struct wined3d_resource *dst_resource, unsigned int dst_sub_resource_idx, + struct wined3d_resource *src_resource, unsigned int src_sub_resource_idx, + enum wined3d_format_id format_id) +{ + struct wined3d_texture *dst_texture, *src_texture; + unsigned int dst_level, src_level; + RECT dst_rect, src_rect; + + TRACE("device %p, dst_resource %p, dst_sub_resource_idx %u, " + "src_resource %p, src_sub_resource_idx %u, format %s.\n", + device, dst_resource, dst_sub_resource_idx, + src_resource, src_sub_resource_idx, debug_d3dformat(format_id)); + + if (wined3d_format_is_typeless(dst_resource->format) + || wined3d_format_is_typeless(src_resource->format)) + { + FIXME("Multisample resolve is not fully supported for typeless formats " + "(dst_format %s, src_format %s, format %s).\n", + debug_d3dformat(dst_resource->format->id), debug_d3dformat(src_resource->format->id), + debug_d3dformat(format_id)); + } + if (dst_resource->type != WINED3D_RTYPE_TEXTURE_2D) + { + WARN("Invalid destination resource type %s.\n", debug_d3dresourcetype(dst_resource->type)); + return; + } + if (src_resource->type != WINED3D_RTYPE_TEXTURE_2D) + { + WARN("Invalid source resource type %s.\n", debug_d3dresourcetype(src_resource->type)); + return; + } + + dst_texture = texture_from_resource(dst_resource); + src_texture = texture_from_resource(src_resource); + + dst_level = dst_sub_resource_idx % dst_texture->level_count; + SetRect(&dst_rect, 0, 0, wined3d_texture_get_level_width(dst_texture, dst_level), + wined3d_texture_get_level_height(dst_texture, dst_level)); + src_level = src_sub_resource_idx % src_texture->level_count; + SetRect(&src_rect, 0, 0, wined3d_texture_get_level_width(src_texture, src_level), + wined3d_texture_get_level_height(src_texture, src_level)); + wined3d_texture_blt(dst_texture, dst_sub_resource_idx, &dst_rect, + src_texture, src_sub_resource_idx, &src_rect, 0, NULL, WINED3D_TEXF_POINT); +} + +HRESULT CDECL wined3d_device_clear_rendertarget_view(struct wined3d_device *device, + struct wined3d_rendertarget_view *view, const RECT *rect, DWORD flags, + const struct wined3d_color *color, float depth, DWORD stencil) +{ + struct wined3d_resource *resource; + RECT r; + + TRACE("device %p, view %p, rect %s, flags %#x, color %s, depth %.8e, stencil %u.\n", + device, view, wine_dbgstr_rect(rect), flags, debug_color(color), depth, stencil); + + if (!flags) + return WINED3D_OK; + + resource = view->resource; + if (resource->type == WINED3D_RTYPE_BUFFER) + { + FIXME("Not implemented for %s resources.\n", debug_d3dresourcetype(resource->type)); + return WINED3DERR_INVALIDCALL; + } + + if (view->layer_count != max(1, resource->depth >> view->desc.u.texture.level_idx)) + { + FIXME("Layered clears not implemented.\n"); + return WINED3DERR_INVALIDCALL; + } + + if (!rect) + { + SetRect(&r, 0, 0, view->width, view->height); + rect = &r; + } + else + { + struct wined3d_box b = {rect->left, rect->top, rect->right, rect->bottom, 0, 1}; + struct wined3d_texture *texture = texture_from_resource(view->resource); + HRESULT hr; + + if (FAILED(hr = wined3d_texture_check_box_dimensions(texture, + view->sub_resource_idx % texture->level_count, &b))) + return hr; + } + + wined3d_cs_emit_clear_rendertarget_view(device->cs, view, rect, flags, color, depth, stencil); + + return WINED3D_OK; +} + +void CDECL wined3d_device_clear_unordered_access_view_uint(struct wined3d_device *device, + struct wined3d_unordered_access_view *view, const struct wined3d_uvec4 *clear_value) +{ + TRACE("device %p, view %p, clear_value %s.\n", device, view, debug_uvec4(clear_value)); + + wined3d_cs_emit_clear_unordered_access_view_uint(device->cs, view, clear_value); +} + +struct wined3d_rendertarget_view * CDECL wined3d_device_get_rendertarget_view(const struct wined3d_device *device, + unsigned int view_idx) +{ + unsigned int max_rt_count; + + TRACE("device %p, view_idx %u.\n", device, view_idx); + + max_rt_count = device->adapter->d3d_info.limits.max_rt_count; + if (view_idx >= max_rt_count) + { + WARN("Only %u render targets are supported.\n", max_rt_count); + return NULL; + } + + return device->state.fb.render_targets[view_idx]; +} + +struct wined3d_rendertarget_view * CDECL wined3d_device_get_depth_stencil_view(const struct wined3d_device *device) +{ + TRACE("device %p.\n", device); + + return device->state.fb.depth_stencil; +} + +static void wined3d_unbind_srv_for_rtv(struct wined3d_device *device, + const struct wined3d_rendertarget_view *view, BOOL dsv) +{ + if (view && wined3d_is_rtv_srv_bound(view)) + { + const struct wined3d_resource *resource = view->resource; + const struct wined3d_shader_resource_view *srv; + unsigned int i, j; + + WARN("Application sets bound resource as render target.\n"); + + for (i = 0; i < WINED3D_SHADER_TYPE_COUNT; ++i) + for (j = 0; j < MAX_SHADER_RESOURCE_VIEWS; ++j) + if ((srv = device->state.shader_resource_view[i][j]) && srv->resource == resource + && ((!dsv && wined3d_is_srv_rtv_bound(srv)) + || (dsv && wined3d_dsv_srv_conflict(view, srv->format)))) + wined3d_device_set_shader_resource_view(device, i, j, NULL); + } +} + +HRESULT CDECL wined3d_device_set_rendertarget_view(struct wined3d_device *device, + unsigned int view_idx, struct wined3d_rendertarget_view *view, BOOL set_viewport) +{ + struct wined3d_rendertarget_view *prev; + unsigned int max_rt_count; + + TRACE("device %p, view_idx %u, view %p, set_viewport %#x.\n", + device, view_idx, view, set_viewport); + + max_rt_count = device->adapter->d3d_info.limits.max_rt_count; + if (view_idx >= max_rt_count) + { + WARN("Only %u render targets are supported.\n", max_rt_count); + return WINED3DERR_INVALIDCALL; + } + + if (view && !(view->resource->bind_flags & WINED3D_BIND_RENDER_TARGET)) + { + WARN("View resource %p doesn't have render target bind flags.\n", view->resource); + return WINED3DERR_INVALIDCALL; + } + + /* Set the viewport and scissor rectangles, if requested. Tests show that + * stateblock recording is ignored, the change goes directly into the + * primary stateblock. */ + if (!view_idx && set_viewport) + { + struct wined3d_state *state = &device->state; + + state->viewports[0].x = 0; + state->viewports[0].y = 0; + state->viewports[0].width = view->width; + state->viewports[0].height = view->height; + state->viewports[0].min_z = 0.0f; + state->viewports[0].max_z = 1.0f; + state->viewport_count = 1; + wined3d_cs_emit_set_viewports(device->cs, 1, state->viewports); + + SetRect(&state->scissor_rects[0], 0, 0, view->width, view->height); + state->scissor_rect_count = 1; + wined3d_cs_emit_set_scissor_rects(device->cs, 1, state->scissor_rects); + } + + prev = device->state.fb.render_targets[view_idx]; + if (view == prev) + return WINED3D_OK; + + if (view) + { + wined3d_rendertarget_view_incref(view); + wined3d_rtv_bind_count_inc(view); + } + device->state.fb.render_targets[view_idx] = view; + wined3d_cs_emit_set_rendertarget_view(device->cs, view_idx, view); + /* Release after the assignment, to prevent device_resource_released() + * from seeing the surface as still in use. */ + if (prev) + { + wined3d_rtv_bind_count_dec(prev); + wined3d_rendertarget_view_decref(prev); + } + + wined3d_unbind_srv_for_rtv(device, view, FALSE); + + return WINED3D_OK; +} + +HRESULT CDECL wined3d_device_set_depth_stencil_view(struct wined3d_device *device, + struct wined3d_rendertarget_view *view) +{ + struct wined3d_rendertarget_view *prev; + + TRACE("device %p, view %p.\n", device, view); + + if (view && !(view->resource->bind_flags & WINED3D_BIND_DEPTH_STENCIL)) + { + WARN("View resource %p has incompatible %s bind flags.\n", + view->resource, wined3d_debug_bind_flags(view->resource->bind_flags)); + return WINED3DERR_INVALIDCALL; + } + + prev = device->state.fb.depth_stencil; + if (prev == view) + { + TRACE("Trying to do a NOP SetRenderTarget operation.\n"); + return WINED3D_OK; + } + + if ((device->state.fb.depth_stencil = view)) + wined3d_rendertarget_view_incref(view); + wined3d_cs_emit_set_depth_stencil_view(device->cs, view); + if (prev) + wined3d_rendertarget_view_decref(prev); + wined3d_unbind_srv_for_rtv(device, view, TRUE); + + return WINED3D_OK; +} + +static struct wined3d_texture *wined3d_device_create_cursor_texture(struct wined3d_device *device, + struct wined3d_texture *cursor_image, unsigned int sub_resource_idx) +{ + unsigned int texture_level = sub_resource_idx % cursor_image->level_count; + struct wined3d_sub_resource_data data; + struct wined3d_resource_desc desc; + struct wined3d_map_desc map_desc; + struct wined3d_texture *texture; + HRESULT hr; + + if (FAILED(wined3d_resource_map(&cursor_image->resource, sub_resource_idx, &map_desc, NULL, WINED3D_MAP_READ))) + { + ERR("Failed to map source texture.\n"); + return NULL; + } + + data.data = map_desc.data; + data.row_pitch = map_desc.row_pitch; + data.slice_pitch = map_desc.slice_pitch; + + desc.resource_type = WINED3D_RTYPE_TEXTURE_2D; + desc.format = WINED3DFMT_B8G8R8A8_UNORM; + desc.multisample_type = WINED3D_MULTISAMPLE_NONE; + desc.multisample_quality = 0; + desc.usage = WINED3DUSAGE_DYNAMIC; + desc.bind_flags = 0; + desc.access = WINED3D_RESOURCE_ACCESS_GPU; + desc.width = wined3d_texture_get_level_width(cursor_image, texture_level); + desc.height = wined3d_texture_get_level_height(cursor_image, texture_level); + desc.depth = 1; + desc.size = 0; + + hr = wined3d_texture_create(device, &desc, 1, 1, 0, &data, NULL, &wined3d_null_parent_ops, &texture); + wined3d_resource_unmap(&cursor_image->resource, sub_resource_idx); + if (FAILED(hr)) + { + ERR("Failed to create cursor texture.\n"); + return NULL; + } + + return texture; +} + +HRESULT CDECL wined3d_device_set_cursor_properties(struct wined3d_device *device, + UINT x_hotspot, UINT y_hotspot, struct wined3d_texture *texture, unsigned int sub_resource_idx) +{ + unsigned int texture_level = sub_resource_idx % texture->level_count; + unsigned int cursor_width, cursor_height; + struct wined3d_map_desc map_desc; + + TRACE("device %p, x_hotspot %u, y_hotspot %u, texture %p, sub_resource_idx %u.\n", + device, x_hotspot, y_hotspot, texture, sub_resource_idx); + + if (sub_resource_idx >= texture->level_count * texture->layer_count + || texture->resource.type != WINED3D_RTYPE_TEXTURE_2D) + return WINED3DERR_INVALIDCALL; + + if (device->cursor_texture) + { + wined3d_texture_decref(device->cursor_texture); + device->cursor_texture = NULL; + } + + if (texture->resource.format->id != WINED3DFMT_B8G8R8A8_UNORM) + { + WARN("Texture %p has invalid format %s.\n", + texture, debug_d3dformat(texture->resource.format->id)); + return WINED3DERR_INVALIDCALL; + } + + /* Cursor width and height must all be powers of two */ + cursor_width = wined3d_texture_get_level_width(texture, texture_level); + cursor_height = wined3d_texture_get_level_height(texture, texture_level); + if ((cursor_width & (cursor_width - 1)) || (cursor_height & (cursor_height - 1))) + { + WARN("Cursor size %ux%u are not all powers of two.\n", cursor_width, cursor_height); + return WINED3DERR_INVALIDCALL; + } + + /* Do not store the surface's pointer because the application may + * release it after setting the cursor image. Windows doesn't + * addref the set surface, so we can't do this either without + * creating circular refcount dependencies. */ + if (!(device->cursor_texture = wined3d_device_create_cursor_texture(device, texture, sub_resource_idx))) + { + ERR("Failed to create cursor texture.\n"); + return WINED3DERR_INVALIDCALL; + } + + if (cursor_width == 32 && cursor_height == 32) + { + UINT mask_size = cursor_width * cursor_height / 8; + ICONINFO cursor_info; + DWORD *mask_bits; + HCURSOR cursor; + + /* 32-bit user32 cursors ignore the alpha channel if it's all + * zeroes, and use the mask instead. Fill the mask with all ones + * to ensure we still get a fully transparent cursor. */ + if (!(mask_bits = heap_alloc(mask_size))) + return E_OUTOFMEMORY; + memset(mask_bits, 0xff, mask_size); + + wined3d_resource_map(&texture->resource, sub_resource_idx, &map_desc, NULL, + WINED3D_MAP_NO_DIRTY_UPDATE | WINED3D_MAP_READ); + cursor_info.fIcon = FALSE; + cursor_info.xHotspot = x_hotspot; + cursor_info.yHotspot = y_hotspot; + cursor_info.hbmMask = CreateBitmap(cursor_width, cursor_height, 1, 1, mask_bits); + cursor_info.hbmColor = CreateBitmap(cursor_width, cursor_height, 1, 32, map_desc.data); + wined3d_resource_unmap(&texture->resource, sub_resource_idx); + + /* Create our cursor and clean up. */ + cursor = CreateIconIndirect(&cursor_info); + if (cursor_info.hbmMask) + DeleteObject(cursor_info.hbmMask); + if (cursor_info.hbmColor) + DeleteObject(cursor_info.hbmColor); + if (device->hardwareCursor) + DestroyCursor(device->hardwareCursor); + device->hardwareCursor = cursor; + if (device->bCursorVisible) + SetCursor(cursor); + + heap_free(mask_bits); + } + + TRACE("New cursor dimensions are %ux%u.\n", cursor_width, cursor_height); + device->cursorWidth = cursor_width; + device->cursorHeight = cursor_height; + device->xHotSpot = x_hotspot; + device->yHotSpot = y_hotspot; + + return WINED3D_OK; +} + +void CDECL wined3d_device_set_cursor_position(struct wined3d_device *device, + int x_screen_space, int y_screen_space, DWORD flags) +{ + TRACE("device %p, x %d, y %d, flags %#x.\n", + device, x_screen_space, y_screen_space, flags); + + device->xScreenSpace = x_screen_space; + device->yScreenSpace = y_screen_space; + + if (device->hardwareCursor) + { + POINT pt; + + GetCursorPos( &pt ); + if (x_screen_space == pt.x && y_screen_space == pt.y) + return; + SetCursorPos( x_screen_space, y_screen_space ); + + /* Switch to the software cursor if position diverges from the hardware one. */ + GetCursorPos( &pt ); + if (x_screen_space != pt.x || y_screen_space != pt.y) + { + if (device->bCursorVisible) SetCursor( NULL ); + DestroyCursor( device->hardwareCursor ); + device->hardwareCursor = 0; + } + } +} + +BOOL CDECL wined3d_device_show_cursor(struct wined3d_device *device, BOOL show) +{ + BOOL oldVisible = device->bCursorVisible; + + TRACE("device %p, show %#x.\n", device, show); + + /* + * When ShowCursor is first called it should make the cursor appear at the OS's last + * known cursor position. + */ + if (show && !oldVisible) + { + POINT pt; + GetCursorPos(&pt); + device->xScreenSpace = pt.x; + device->yScreenSpace = pt.y; + } + + if (device->hardwareCursor) + { + device->bCursorVisible = show; + if (show) + SetCursor(device->hardwareCursor); + else + SetCursor(NULL); + } + else if (device->cursor_texture) + { + device->bCursorVisible = show; + } + + return oldVisible; +} + +void CDECL wined3d_device_evict_managed_resources(struct wined3d_device *device) +{ + struct wined3d_resource *resource, *cursor; + + TRACE("device %p.\n", device); + + LIST_FOR_EACH_ENTRY_SAFE(resource, cursor, &device->resources, struct wined3d_resource, resource_list_entry) + { + TRACE("Checking resource %p for eviction.\n", resource); + + if (wined3d_resource_access_is_managed(resource->access) && !resource->map_count) + { + TRACE("Evicting %p.\n", resource); + wined3d_cs_emit_unload_resource(device->cs, resource); + } + } +} + +void CDECL wined3d_device_flush(struct wined3d_device *device) +{ + TRACE("device %p.\n", device); + + wined3d_cs_emit_flush(device->cs); +} + +static void update_swapchain_flags(struct wined3d_texture *texture) +{ + unsigned int flags = texture->swapchain->state.desc.flags; + + if (flags & WINED3D_SWAPCHAIN_LOCKABLE_BACKBUFFER) + texture->resource.access |= WINED3D_RESOURCE_ACCESS_MAP_R | WINED3D_RESOURCE_ACCESS_MAP_W; + else + texture->resource.access &= ~(WINED3D_RESOURCE_ACCESS_MAP_R | WINED3D_RESOURCE_ACCESS_MAP_W); + + if (flags & WINED3D_SWAPCHAIN_GDI_COMPATIBLE) + texture->flags |= WINED3D_TEXTURE_GET_DC; + else + texture->flags &= ~WINED3D_TEXTURE_GET_DC; +} + +HRESULT CDECL wined3d_device_reset(struct wined3d_device *device, + const struct wined3d_swapchain_desc *swapchain_desc, const struct wined3d_display_mode *mode, + wined3d_device_reset_cb callback, BOOL reset_state) +{ + const struct wined3d_d3d_info *d3d_info = &device->adapter->d3d_info; + struct wined3d_swapchain_state *swapchain_state; + struct wined3d_swapchain_desc *current_desc; + struct wined3d_resource *resource, *cursor; + struct wined3d_rendertarget_view *view; + struct wined3d_swapchain *swapchain; + struct wined3d_view_desc view_desc; + BOOL backbuffer_resized, windowed; + HRESULT hr = WINED3D_OK; + unsigned int i; + + TRACE("device %p, swapchain_desc %p, mode %p, callback %p, reset_state %#x.\n", + device, swapchain_desc, mode, callback, reset_state); + + wined3d_cs_finish(device->cs, WINED3D_CS_QUEUE_DEFAULT); + + if (!(swapchain = wined3d_device_get_swapchain(device, 0))) + { + ERR("Failed to get the first implicit swapchain.\n"); + return WINED3DERR_INVALIDCALL; + } + swapchain_state = &swapchain->state; + current_desc = &swapchain_state->desc; + + if (reset_state) + { + if (device->logo_texture) + { + wined3d_texture_decref(device->logo_texture); + device->logo_texture = NULL; + } + if (device->cursor_texture) + { + wined3d_texture_decref(device->cursor_texture); + device->cursor_texture = NULL; + } + state_unbind_resources(&device->state); + } + + for (i = 0; i < d3d_info->limits.max_rt_count; ++i) + { + wined3d_device_set_rendertarget_view(device, i, NULL, FALSE); + } + wined3d_device_set_depth_stencil_view(device, NULL); + + if (reset_state) + { + LIST_FOR_EACH_ENTRY_SAFE(resource, cursor, &device->resources, struct wined3d_resource, resource_list_entry) + { + TRACE("Enumerating resource %p.\n", resource); + if (FAILED(hr = callback(resource))) + return hr; + } + } + + TRACE("New params:\n"); + TRACE("output %p\n", swapchain_desc->output); + TRACE("backbuffer_width %u\n", swapchain_desc->backbuffer_width); + TRACE("backbuffer_height %u\n", swapchain_desc->backbuffer_height); + TRACE("backbuffer_format %s\n", debug_d3dformat(swapchain_desc->backbuffer_format)); + TRACE("backbuffer_count %u\n", swapchain_desc->backbuffer_count); + TRACE("multisample_type %#x\n", swapchain_desc->multisample_type); + TRACE("multisample_quality %u\n", swapchain_desc->multisample_quality); + TRACE("swap_effect %#x\n", swapchain_desc->swap_effect); + TRACE("device_window %p\n", swapchain_desc->device_window); + TRACE("windowed %#x\n", swapchain_desc->windowed); + TRACE("enable_auto_depth_stencil %#x\n", swapchain_desc->enable_auto_depth_stencil); + if (swapchain_desc->enable_auto_depth_stencil) + TRACE("auto_depth_stencil_format %s\n", debug_d3dformat(swapchain_desc->auto_depth_stencil_format)); + TRACE("flags %#x\n", swapchain_desc->flags); + TRACE("refresh_rate %u\n", swapchain_desc->refresh_rate); + TRACE("auto_restore_display_mode %#x\n", swapchain_desc->auto_restore_display_mode); + + if (swapchain_desc->backbuffer_bind_flags && swapchain_desc->backbuffer_bind_flags != WINED3D_BIND_RENDER_TARGET) + FIXME("Got unexpected backbuffer bind flags %#x.\n", swapchain_desc->backbuffer_bind_flags); + + if (swapchain_desc->swap_effect != WINED3D_SWAP_EFFECT_DISCARD + && swapchain_desc->swap_effect != WINED3D_SWAP_EFFECT_SEQUENTIAL + && swapchain_desc->swap_effect != WINED3D_SWAP_EFFECT_COPY) + FIXME("Unimplemented swap effect %#x.\n", swapchain_desc->swap_effect); + + /* No special treatment of these parameters. Just store them */ + current_desc->swap_effect = swapchain_desc->swap_effect; + current_desc->enable_auto_depth_stencil = swapchain_desc->enable_auto_depth_stencil; + current_desc->auto_depth_stencil_format = swapchain_desc->auto_depth_stencil_format; + current_desc->refresh_rate = swapchain_desc->refresh_rate; + current_desc->auto_restore_display_mode = swapchain_desc->auto_restore_display_mode; + + if (swapchain_desc->device_window && swapchain_desc->device_window != current_desc->device_window) + { + TRACE("Changing the device window from %p to %p.\n", + current_desc->device_window, swapchain_desc->device_window); + current_desc->device_window = swapchain_desc->device_window; + swapchain_state->device_window = swapchain_desc->device_window; + wined3d_swapchain_set_window(swapchain, NULL); + } + + backbuffer_resized = swapchain_desc->backbuffer_width != current_desc->backbuffer_width + || swapchain_desc->backbuffer_height != current_desc->backbuffer_height; + windowed = current_desc->windowed; + + if (!swapchain_desc->windowed != !windowed || swapchain->reapply_mode + || mode || (!swapchain_desc->windowed && backbuffer_resized)) + { + /* Switch from windowed to fullscreen. */ + if (windowed && !swapchain_desc->windowed) + { + HWND focus_window = device->create_parms.focus_window; + + if (!focus_window) + focus_window = swapchain->state.device_window; + if (FAILED(hr = wined3d_device_acquire_focus_window(device, focus_window))) + { + ERR("Failed to acquire focus window, hr %#x.\n", hr); + return hr; + } + } + + if (FAILED(hr = wined3d_swapchain_state_set_fullscreen(&swapchain->state, + swapchain_desc, mode))) + return hr; + + /* Switch from fullscreen to windowed. */ + if (!windowed && swapchain_desc->windowed) + wined3d_device_release_focus_window(device); + } + else if (!swapchain_desc->windowed) + { + DWORD style = swapchain_state->style; + DWORD exstyle = swapchain_state->exstyle; + struct wined3d_output_desc output_desc; + + /* If we're in fullscreen, and the mode wasn't changed, we have to get + * the window back into the right position. Some applications + * (Battlefield 2, Guild Wars) move it and then call Reset() to clean + * up their mess. Guild Wars also loses the device during that. */ + if (FAILED(hr = wined3d_output_get_desc(swapchain_desc->output, &output_desc))) + { + ERR("Failed to get output description, hr %#x.\n", hr); + return hr; + } + + swapchain_state->style = 0; + swapchain_state->exstyle = 0; + wined3d_swapchain_state_setup_fullscreen(swapchain_state, swapchain_state->device_window, + output_desc.desktop_rect.left, output_desc.desktop_rect.top, + swapchain_desc->backbuffer_width, swapchain_desc->backbuffer_height); + swapchain_state->style = style; + swapchain_state->exstyle = exstyle; + } + + if (FAILED(hr = wined3d_swapchain_resize_buffers(swapchain, swapchain_desc->backbuffer_count, + swapchain_desc->backbuffer_width, swapchain_desc->backbuffer_height, swapchain_desc->backbuffer_format, + swapchain_desc->multisample_type, swapchain_desc->multisample_quality))) + return hr; + + if (swapchain_desc->flags != current_desc->flags) + { + current_desc->flags = swapchain_desc->flags; + + update_swapchain_flags(swapchain->front_buffer); + for (i = 0; i < current_desc->backbuffer_count; ++i) + { + update_swapchain_flags(swapchain->back_buffers[i]); + } + } + + if ((view = device->auto_depth_stencil_view)) + { + device->auto_depth_stencil_view = NULL; + wined3d_rendertarget_view_decref(view); + } + if (current_desc->enable_auto_depth_stencil) + { + struct wined3d_resource_desc texture_desc; + struct wined3d_texture *texture; + + TRACE("Creating the depth stencil buffer.\n"); + + texture_desc.resource_type = WINED3D_RTYPE_TEXTURE_2D; + texture_desc.format = current_desc->auto_depth_stencil_format; + texture_desc.multisample_type = current_desc->multisample_type; + texture_desc.multisample_quality = current_desc->multisample_quality; + texture_desc.usage = 0; + texture_desc.bind_flags = WINED3D_BIND_DEPTH_STENCIL; + texture_desc.access = WINED3D_RESOURCE_ACCESS_GPU; + texture_desc.width = current_desc->backbuffer_width; + texture_desc.height = current_desc->backbuffer_height; + texture_desc.depth = 1; + texture_desc.size = 0; + + if (FAILED(hr = device->device_parent->ops->create_swapchain_texture(device->device_parent, + device->device_parent, &texture_desc, 0, &texture))) + { + ERR("Failed to create the auto depth/stencil surface, hr %#x.\n", hr); + return WINED3DERR_INVALIDCALL; + } + + view_desc.format_id = texture->resource.format->id; + view_desc.flags = 0; + view_desc.u.texture.level_idx = 0; + view_desc.u.texture.level_count = 1; + view_desc.u.texture.layer_idx = 0; + view_desc.u.texture.layer_count = 1; + hr = wined3d_rendertarget_view_create(&view_desc, &texture->resource, + NULL, &wined3d_null_parent_ops, &device->auto_depth_stencil_view); + wined3d_texture_decref(texture); + if (FAILED(hr)) + { + ERR("Failed to create rendertarget view, hr %#x.\n", hr); + return hr; + } + } + + if ((view = device->back_buffer_view)) + { + device->back_buffer_view = NULL; + wined3d_rendertarget_view_decref(view); + } + if (current_desc->backbuffer_count && current_desc->backbuffer_bind_flags & WINED3D_BIND_RENDER_TARGET) + { + struct wined3d_resource *back_buffer = &swapchain->back_buffers[0]->resource; + + view_desc.format_id = back_buffer->format->id; + view_desc.flags = 0; + view_desc.u.texture.level_idx = 0; + view_desc.u.texture.level_count = 1; + view_desc.u.texture.layer_idx = 0; + view_desc.u.texture.layer_count = 1; + if (FAILED(hr = wined3d_rendertarget_view_create(&view_desc, back_buffer, + NULL, &wined3d_null_parent_ops, &device->back_buffer_view))) + { + ERR("Failed to create rendertarget view, hr %#x.\n", hr); + return hr; + } + } + + wine_rb_clear(&device->samplers, device_free_sampler, NULL); + wine_rb_clear(&device->rasterizer_states, device_free_rasterizer_state, NULL); + wine_rb_clear(&device->blend_states, device_free_blend_state, NULL); + wine_rb_clear(&device->depth_stencil_states, device_free_depth_stencil_state, NULL); + + if (reset_state) + { + TRACE("Resetting state.\n"); + wined3d_cs_emit_reset_state(device->cs); + state_cleanup(&device->state); + + LIST_FOR_EACH_ENTRY_SAFE(resource, cursor, &device->resources, struct wined3d_resource, resource_list_entry) + { + TRACE("Unloading resource %p.\n", resource); + wined3d_cs_emit_unload_resource(device->cs, resource); + } + + device->adapter->adapter_ops->adapter_uninit_3d(device); + + memset(&device->state, 0, sizeof(device->state)); + state_init(&device->state, &device->adapter->d3d_info, WINED3D_STATE_INIT_DEFAULT); + + device_init_swapchain_state(device, swapchain); + if (wined3d_settings.logo) + device_load_logo(device, wined3d_settings.logo); + } + else + { + if ((view = device->back_buffer_view)) + wined3d_device_set_rendertarget_view(device, 0, view, FALSE); + if ((view = device->auto_depth_stencil_view)) + wined3d_device_set_depth_stencil_view(device, view); + } + + if (reset_state) + hr = device->adapter->adapter_ops->adapter_init_3d(device); + + /* All done. There is no need to reload resources or shaders, this will happen automatically on the + * first use + */ + return hr; +} + +HRESULT CDECL wined3d_device_set_dialog_box_mode(struct wined3d_device *device, BOOL enable_dialogs) +{ + TRACE("device %p, enable_dialogs %#x.\n", device, enable_dialogs); + + if (!enable_dialogs) FIXME("Dialogs cannot be disabled yet.\n"); + + return WINED3D_OK; +} + + +void CDECL wined3d_device_get_creation_parameters(const struct wined3d_device *device, + struct wined3d_device_creation_parameters *parameters) +{ + TRACE("device %p, parameters %p.\n", device, parameters); + + *parameters = device->create_parms; +} + +struct wined3d * CDECL wined3d_device_get_wined3d(const struct wined3d_device *device) +{ + TRACE("device %p.\n", device); + + return device->wined3d; +} + +enum wined3d_feature_level CDECL wined3d_device_get_feature_level(const struct wined3d_device *device) +{ + TRACE("device %p.\n", device); + + return device->feature_level; +} + +void CDECL wined3d_device_set_gamma_ramp(const struct wined3d_device *device, + UINT swapchain_idx, DWORD flags, const struct wined3d_gamma_ramp *ramp) +{ + struct wined3d_swapchain *swapchain; + + TRACE("device %p, swapchain_idx %u, flags %#x, ramp %p.\n", + device, swapchain_idx, flags, ramp); + + if ((swapchain = wined3d_device_get_swapchain(device, swapchain_idx))) + wined3d_swapchain_set_gamma_ramp(swapchain, flags, ramp); +} + +void CDECL wined3d_device_get_gamma_ramp(const struct wined3d_device *device, + UINT swapchain_idx, struct wined3d_gamma_ramp *ramp) +{ + struct wined3d_swapchain *swapchain; + + TRACE("device %p, swapchain_idx %u, ramp %p.\n", + device, swapchain_idx, ramp); + + if ((swapchain = wined3d_device_get_swapchain(device, swapchain_idx))) + wined3d_swapchain_get_gamma_ramp(swapchain, ramp); +} + +void device_resource_add(struct wined3d_device *device, struct wined3d_resource *resource) +{ + TRACE("device %p, resource %p.\n", device, resource); + + wined3d_not_from_cs(device->cs); + + list_add_head(&device->resources, &resource->resource_list_entry); +} + +static void device_resource_remove(struct wined3d_device *device, struct wined3d_resource *resource) +{ + TRACE("device %p, resource %p.\n", device, resource); + + wined3d_not_from_cs(device->cs); + + list_remove(&resource->resource_list_entry); +} + +void device_resource_released(struct wined3d_device *device, struct wined3d_resource *resource) +{ + enum wined3d_resource_type type = resource->type; + struct wined3d_rendertarget_view *rtv; + unsigned int i; + + TRACE("device %p, resource %p, type %s.\n", device, resource, debug_d3dresourcetype(type)); + + for (i = 0; i < ARRAY_SIZE(device->state.fb.render_targets); ++i) + { + if ((rtv = device->state.fb.render_targets[i]) && rtv->resource == resource) + ERR("Resource %p is still in use as render target %u.\n", resource, i); + } + + if ((rtv = device->state.fb.depth_stencil) && rtv->resource == resource) + ERR("Resource %p is still in use as depth/stencil buffer.\n", resource); + + switch (type) + { + case WINED3D_RTYPE_TEXTURE_1D: + case WINED3D_RTYPE_TEXTURE_2D: + case WINED3D_RTYPE_TEXTURE_3D: + for (i = 0; i < WINED3D_MAX_COMBINED_SAMPLERS; ++i) + { + if (&device->state.textures[i]->resource == resource) + { + ERR("Texture resource %p is still in use, stage %u.\n", resource, i); + device->state.textures[i] = NULL; + } + } + break; + + case WINED3D_RTYPE_BUFFER: + for (i = 0; i < WINED3D_MAX_STREAMS; ++i) + { + if (&device->state.streams[i].buffer->resource == resource) + { + ERR("Buffer resource %p is still in use, stream %u.\n", resource, i); + device->state.streams[i].buffer = NULL; + } + } + + if (&device->state.index_buffer->resource == resource) + { + ERR("Buffer resource %p is still in use as index buffer.\n", resource); + device->state.index_buffer = NULL; + } + break; + + default: + break; + } + + /* Remove the resource from the resourceStore */ + device_resource_remove(device, resource); + + TRACE("Resource released.\n"); +} + +static int wined3d_so_desc_compare(const void *key, const struct wine_rb_entry *entry) +{ + const struct wined3d_stream_output_desc *desc = &WINE_RB_ENTRY_VALUE(entry, + struct wined3d_so_desc_entry, entry)->desc; + const struct wined3d_stream_output_desc *k = key; + unsigned int i; + int ret; + + if ((ret = (k->element_count - desc->element_count))) + return ret; + if ((ret = (k->buffer_stride_count - desc->buffer_stride_count))) + return ret; + if ((ret = (k->rasterizer_stream_idx - desc->rasterizer_stream_idx))) + return ret; + + for (i = 0; i < k->element_count; ++i) + { + const struct wined3d_stream_output_element *b = &desc->elements[i]; + const struct wined3d_stream_output_element *a = &k->elements[i]; + + if ((ret = (a->stream_idx - b->stream_idx))) + return ret; + if ((ret = strcmp(a->semantic_name, b->semantic_name))) + return ret; + if ((ret = (a->semantic_idx - b->semantic_idx))) + return ret; + if ((ret = (a->component_idx - b->component_idx))) + return ret; + if ((ret = (a->component_count - b->component_count))) + return ret; + if ((ret = (a->output_slot - b->output_slot))) + return ret; + } + + for (i = 0; i < k->buffer_stride_count; ++i) + { + if ((ret = (k->buffer_strides[i] - desc->buffer_strides[i]))) + return ret; + } + + return 0; +} + +static int wined3d_sampler_compare(const void *key, const struct wine_rb_entry *entry) +{ + const struct wined3d_sampler *sampler = WINE_RB_ENTRY_VALUE(entry, struct wined3d_sampler, entry); + + return memcmp(&sampler->desc, key, sizeof(sampler->desc)); +} + +static int wined3d_rasterizer_state_compare(const void *key, const struct wine_rb_entry *entry) +{ + const struct wined3d_rasterizer_state *state = WINE_RB_ENTRY_VALUE(entry, struct wined3d_rasterizer_state, entry); + + return memcmp(&state->desc, key, sizeof(state->desc)); +} + +static int wined3d_blend_state_compare(const void *key, const struct wine_rb_entry *entry) +{ + const struct wined3d_blend_state *state = WINE_RB_ENTRY_VALUE(entry, struct wined3d_blend_state, entry); + + return memcmp(&state->desc, key, sizeof(state->desc)); +} + +static int wined3d_depth_stencil_state_compare(const void *key, const struct wine_rb_entry *entry) +{ + const struct wined3d_depth_stencil_state *state + = WINE_RB_ENTRY_VALUE(entry, struct wined3d_depth_stencil_state, entry); + + return memcmp(&state->desc, key, sizeof(state->desc)); +} + +static BOOL wined3d_select_feature_level(const struct wined3d_adapter *adapter, + const enum wined3d_feature_level *levels, unsigned int level_count, + enum wined3d_feature_level *selected_level) +{ + const struct wined3d_d3d_info *d3d_info = &adapter->d3d_info; + unsigned int i; + + for (i = 0; i < level_count; ++i) + { + if (levels[i] && d3d_info->feature_level >= levels[i]) + { + *selected_level = levels[i]; + return TRUE; + } + } + + FIXME_(winediag)("None of the requested D3D feature levels is supported on this GPU " + "with the current shader backend.\n"); + return FALSE; +} + +HRESULT wined3d_device_init(struct wined3d_device *device, struct wined3d *wined3d, + unsigned int adapter_idx, enum wined3d_device_type device_type, HWND focus_window, unsigned int flags, + BYTE surface_alignment, const enum wined3d_feature_level *levels, unsigned int level_count, + const BOOL *supported_extensions, struct wined3d_device_parent *device_parent) +{ + struct wined3d_adapter *adapter = wined3d->adapters[adapter_idx]; + const struct wined3d_fragment_pipe_ops *fragment_pipeline; + const struct wined3d_vertex_pipe_ops *vertex_pipeline; + unsigned int i; + HRESULT hr; + + if (!wined3d_select_feature_level(adapter, levels, level_count, &device->feature_level)) + return E_FAIL; + + TRACE("Device feature level %s.\n", wined3d_debug_feature_level(device->feature_level)); + + device->ref = 1; + device->wined3d = wined3d; + wined3d_incref(device->wined3d); + device->adapter = adapter; + device->device_parent = device_parent; + list_init(&device->resources); + list_init(&device->shaders); + device->surface_alignment = surface_alignment; + + /* Save the creation parameters. */ + device->create_parms.adapter_idx = adapter_idx; + device->create_parms.device_type = device_type; + device->create_parms.focus_window = focus_window; + device->create_parms.flags = flags; + + device->shader_backend = adapter->shader_backend; + + vertex_pipeline = adapter->vertex_pipe; + + fragment_pipeline = adapter->fragment_pipe; + + wine_rb_init(&device->so_descs, wined3d_so_desc_compare); + wine_rb_init(&device->samplers, wined3d_sampler_compare); + wine_rb_init(&device->rasterizer_states, wined3d_rasterizer_state_compare); + wine_rb_init(&device->blend_states, wined3d_blend_state_compare); + wine_rb_init(&device->depth_stencil_states, wined3d_depth_stencil_state_compare); + + if (vertex_pipeline->vp_states && fragment_pipeline->states + && FAILED(hr = compile_state_table(device->state_table, device->multistate_funcs, + &adapter->d3d_info, supported_extensions, vertex_pipeline, + fragment_pipeline, adapter->misc_state_template))) + { + ERR("Failed to compile state table, hr %#x.\n", hr); + wine_rb_destroy(&device->samplers, NULL, NULL); + wine_rb_destroy(&device->rasterizer_states, NULL, NULL); + wine_rb_destroy(&device->blend_states, NULL, NULL); + wine_rb_destroy(&device->depth_stencil_states, NULL, NULL); + wine_rb_destroy(&device->so_descs, NULL, NULL); + wined3d_decref(device->wined3d); + return hr; + } + + state_init(&device->state, &adapter->d3d_info, WINED3D_STATE_INIT_DEFAULT); + + device->max_frame_latency = 3; + + if (!(device->cs = wined3d_cs_create(device))) + { + WARN("Failed to create command stream.\n"); + state_cleanup(&device->state); + hr = E_FAIL; + goto err; + } + + return WINED3D_OK; + +err: + for (i = 0; i < ARRAY_SIZE(device->multistate_funcs); ++i) + { + heap_free(device->multistate_funcs[i]); + } + wine_rb_destroy(&device->samplers, NULL, NULL); + wine_rb_destroy(&device->rasterizer_states, NULL, NULL); + wine_rb_destroy(&device->blend_states, NULL, NULL); + wine_rb_destroy(&device->depth_stencil_states, NULL, NULL); + wine_rb_destroy(&device->so_descs, NULL, NULL); + wined3d_decref(device->wined3d); + return hr; +} + +void device_invalidate_state(const struct wined3d_device *device, unsigned int state_id) +{ + unsigned int representative, i, idx, shift; + struct wined3d_context *context; + + wined3d_from_cs(device->cs); + + if (STATE_IS_COMPUTE(state_id)) + { + for (i = 0; i < device->context_count; ++i) + context_invalidate_compute_state(device->contexts[i], state_id); + return; + } + + representative = device->state_table[state_id].representative; + idx = representative / (sizeof(*context->dirty_graphics_states) * CHAR_BIT); + shift = representative & ((sizeof(*context->dirty_graphics_states) * CHAR_BIT) - 1); + for (i = 0; i < device->context_count; ++i) + { + device->contexts[i]->dirty_graphics_states[idx] |= (1u << shift); + } +} + +LRESULT device_process_message(struct wined3d_device *device, HWND window, BOOL unicode, + UINT message, WPARAM wparam, LPARAM lparam, WNDPROC proc) +{ + if (message == WM_DESTROY) + { + TRACE("unregister window %p.\n", window); + wined3d_unregister_window(window); + + if (InterlockedCompareExchangePointer((void **)&device->focus_window, NULL, window) != window) + ERR("Window %p is not the focus window for device %p.\n", window, device); + } + else if (message == WM_DISPLAYCHANGE) + { + device->device_parent->ops->mode_changed(device->device_parent); + } + else if (message == WM_ACTIVATEAPP) + { + unsigned int i = device->swapchain_count; + + /* Deactivating the implicit swapchain may cause the application + * (e.g. Deus Ex: GOTY) to destroy the device, so take care to + * deactivate the implicit swapchain last, and to avoid accessing the + * "device" pointer afterwards. */ + while (i--) + wined3d_swapchain_activate(device->swapchains[i], wparam); + } + else if (message == WM_SYSCOMMAND) + { + if (wparam == SC_RESTORE && device->wined3d->flags & WINED3D_HANDLE_RESTORE) + { + if (unicode) + DefWindowProcW(window, message, wparam, lparam); + else + DefWindowProcA(window, message, wparam, lparam); + } + } + + if (unicode) + return CallWindowProcW(proc, window, message, wparam, lparam); + else + return CallWindowProcA(proc, window, message, wparam, lparam); +} diff --git a/wrappers/directx/d3dwine_wrapper/directx.c b/wrappers/directx/d3dwine_wrapper/directx.c new file mode 100644 index 00000000000..c673f61e429 --- /dev/null +++ b/wrappers/directx/d3dwine_wrapper/directx.c @@ -0,0 +1,3253 @@ +/* + * Copyright 2002-2004 Jason Edmeades + * Copyright 2003-2004 Raphael Junqueira + * Copyright 2004 Christian Costa + * Copyright 2005 Oliver Stieber + * Copyright 2007-2008 Stefan Dösinger for CodeWeavers + * Copyright 2009-2011 Henri Verbeet for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "config.h" +#include "wine/port.h" + +#include "wined3d_private.h" +#include "winternl.h" + +WINE_DEFAULT_DEBUG_CHANNEL(d3d); +WINE_DECLARE_DEBUG_CHANNEL(winediag); + +#define DEFAULT_REFRESH_RATE 0 + +enum wined3d_driver_model +{ + DRIVER_MODEL_GENERIC, + DRIVER_MODEL_WIN9X, + DRIVER_MODEL_NT40, + DRIVER_MODEL_NT5X, + DRIVER_MODEL_NT6X +}; + +/* The d3d device ID */ +static const GUID IID_D3DDEVICE_D3DUID = { 0xaeb2cdd4, 0x6e41, 0x43ea, { 0x94,0x1c,0x83,0x61,0xcc,0x76,0x07,0x81 } }; + +/********************************************************** + * Utility functions follow + **********************************************************/ + +const struct min_lookup minMipLookup[] = +{ + /* NONE POINT LINEAR */ + {{GL_NEAREST, GL_NEAREST, GL_NEAREST}}, /* NONE */ + {{GL_NEAREST, GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST_MIPMAP_LINEAR}}, /* POINT*/ + {{GL_LINEAR, GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_LINEAR}}, /* LINEAR */ +}; + +const GLenum magLookup[] = +{ + /* NONE POINT LINEAR */ + GL_NEAREST, GL_NEAREST, GL_LINEAR, +}; + +void CDECL wined3d_output_release_ownership(const struct wined3d_output *output) +{ + D3DKMT_SETVIDPNSOURCEOWNER set_owner_desc = {0}; + + TRACE("output %p.\n", output); + + set_owner_desc.hDevice = output->kmt_device; + D3DKMTSetVidPnSourceOwner(&set_owner_desc); +} + +HRESULT CDECL wined3d_output_take_ownership(const struct wined3d_output *output, BOOL exclusive) +{ + D3DKMT_SETVIDPNSOURCEOWNER set_owner_desc; + D3DKMT_VIDPNSOURCEOWNER_TYPE owner_type; + NTSTATUS status; + + TRACE("output %p, exclusive %#x.\n", output, exclusive); + + owner_type = exclusive ? D3DKMT_VIDPNSOURCEOWNER_EXCLUSIVE : D3DKMT_VIDPNSOURCEOWNER_SHARED; + set_owner_desc.pType = &owner_type; + set_owner_desc.pVidPnSourceId = &output->vidpn_source_id; + set_owner_desc.VidPnSourceCount = 1; + set_owner_desc.hDevice = output->kmt_device; + status = D3DKMTSetVidPnSourceOwner(&set_owner_desc); + + switch (status) + { + case STATUS_GRAPHICS_VIDPN_SOURCE_IN_USE: + return DXGI_ERROR_NOT_CURRENTLY_AVAILABLE; + case STATUS_INVALID_PARAMETER: + return E_INVALIDARG; + case STATUS_PROCEDURE_NOT_FOUND: + return E_NOINTERFACE; + case STATUS_SUCCESS: + return S_OK; + default: + FIXME("Unhandled error %#x.\n", status); + return E_FAIL; + } +} + +static void wined3d_output_cleanup(const struct wined3d_output *output) +{ + D3DKMT_DESTROYDEVICE destroy_device_desc; + D3DKMT_CLOSEADAPTER close_adapter_desc; + + TRACE("output %p.\n", output); + + destroy_device_desc.hDevice = output->kmt_device; + D3DKMTDestroyDevice(&destroy_device_desc); + close_adapter_desc.hAdapter = output->kmt_adapter; + D3DKMTCloseAdapter(&close_adapter_desc); +} + +static HRESULT wined3d_output_init(struct wined3d_output *output, unsigned int ordinal, + struct wined3d_adapter *adapter, const WCHAR *device_name) +{ + D3DKMT_OPENADAPTERFROMGDIDISPLAYNAME open_adapter_desc; + D3DKMT_CREATEDEVICE create_device_desc = {{0}}; + D3DKMT_CLOSEADAPTER close_adapter_desc; + + TRACE("output %p, device_name %s.\n", output, wine_dbgstr_w(device_name)); + + lstrcpyW(open_adapter_desc.DeviceName, device_name); + if (D3DKMTOpenAdapterFromGdiDisplayName(&open_adapter_desc)) + return E_INVALIDARG; + + create_device_desc.u.hAdapter = open_adapter_desc.hAdapter; + if (D3DKMTCreateDevice(&create_device_desc)) + { + close_adapter_desc.hAdapter = open_adapter_desc.hAdapter; + D3DKMTCloseAdapter(&close_adapter_desc); + return E_FAIL; + } + + output->ordinal = ordinal; + lstrcpyW(output->device_name, device_name); + output->adapter = adapter; + output->screen_format = WINED3DFMT_UNKNOWN; + output->kmt_adapter = open_adapter_desc.hAdapter; + output->kmt_device = create_device_desc.hDevice; + output->vidpn_source_id = open_adapter_desc.VidPnSourceId; + + return WINED3D_OK; +} + +/* Adjust the amount of used texture memory */ +UINT64 adapter_adjust_memory(struct wined3d_adapter *adapter, INT64 amount) +{ + adapter->vram_bytes_used += amount; + TRACE("Adjusted used adapter memory by 0x%s to 0x%s.\n", + wine_dbgstr_longlong(amount), + wine_dbgstr_longlong(adapter->vram_bytes_used)); + return adapter->vram_bytes_used; +} + +void wined3d_adapter_cleanup(struct wined3d_adapter *adapter) +{ + unsigned int output_idx; + + for (output_idx = 0; output_idx < adapter->output_count; ++output_idx) + wined3d_output_cleanup(&adapter->outputs[output_idx]); + heap_free(adapter->outputs); + heap_free(adapter->formats); +} + +ULONG CDECL wined3d_incref(struct wined3d *wined3d) +{ + ULONG refcount = InterlockedIncrement(&wined3d->ref); + + TRACE("%p increasing refcount to %u.\n", wined3d, refcount); + + return refcount; +} + +ULONG CDECL wined3d_decref(struct wined3d *wined3d) +{ + ULONG refcount = InterlockedDecrement(&wined3d->ref); + + TRACE("%p decreasing refcount to %u.\n", wined3d, refcount); + + if (!refcount) + { + unsigned int i; + + for (i = 0; i < wined3d->adapter_count; ++i) + { + struct wined3d_adapter *adapter = wined3d->adapters[i]; + + adapter->adapter_ops->adapter_destroy(adapter); + } + heap_free(wined3d); + } + + return refcount; +} + +/* Certain applications (e.g. Steam) complain if we report an outdated driver + * version. + * + * The driver version has the form "x.y.z.w". + * + * "x" is the Windows version / driver model the driver is meant for: + * 4 -> 95/98/NT4 + * 5 -> 2000 + * 6 -> XP + * 7 -> Vista - WDDM 1.0 + * 8 -> Windows 7 - WDDM 1.1 + * 9 -> Windows 8 - WDDM 1.2 + * 10 -> Windows 8.1 - WDDM 1.3 + * 20 -> Windows 10 - WDDM 2.0 + * 21 -> Windows 10 Anniversary Update - WDDM 2.1 + * 22 -> Windows 10 Creators Update - WDDM 2.2 + * 23 -> Windows 10 Fall Creators Update - WDDM 2.3 + * 24 -> Windows 10 April 2018 Update - WDDM 2.4 + * 25 -> Windows 10 October 2018 Update - WDDM 2.5 + * 26 -> Windows 10 May 2019 Update - WDDM 2.6 + * + * "y" is the maximum Direct3D version / feature level the driver supports. + * 11 -> 6 + * 12 -> 7 + * 13 -> 8 + * 14 -> 9 + * 15 -> 10_0 + * 16 -> 10_1 + * 17 -> 11_0 + * 18 -> 11_1 + * 19 -> 12_0 + * 20 -> 12_1 + * 21 -> 12_x + * + * "z" is the subversion number. + * + * "w" is the vendor specific driver build number. + * + * In practice the reported version is tied to the driver, not the actual + * Windows version or feature level. E.g. NVIDIA driver 445.87 advertises the + * exact same version 26.21.14.4587 on Windows 7 as it does on Windows 10 + * (it's in fact the same driver). Similarly for driver 310.90 that advertises + * itself as 9.18.13.1090 on Windows Vista with a GeForce 9600M. */ + +struct driver_version_information +{ + enum wined3d_display_driver driver; + enum wined3d_driver_model driver_model; + const char *driver_name; /* name of Windows driver */ + WORD subversion; /* subversion word ('z'), contained in high word of DriverVersion.LowPart */ + WORD build; /* build number ('w'), contained in low word of DriverVersion.LowPart */ +}; + +/* The driver version table contains driver information for different devices on several OS versions. */ +static const struct driver_version_information driver_version_table[] = +{ + /* AMD + * - Radeon HD2x00 (R600) and up supported by current drivers. + * - Radeon 9500 (R300) - X1*00 (R5xx) supported up to Catalyst 9.3 (Linux) and 10.2 (XP/Vista/Win7) + * - Radeon 7xxx (R100) - 9250 (RV250) supported up to Catalyst 6.11 (XP) + * - Rage 128 supported up to XP, latest official build 6.13.3279 dated October 2001 */ + {DRIVER_AMD_RAGE_128PRO, DRIVER_MODEL_NT5X, "ati2dvaa.dll", 3279, 0}, + {DRIVER_AMD_R100, DRIVER_MODEL_NT5X, "ati2dvag.dll", 10, 6614}, + {DRIVER_AMD_R300, DRIVER_MODEL_NT5X, "ati2dvag.dll", 10, 6764}, + {DRIVER_AMD_R600, DRIVER_MODEL_NT5X, "ati2dvag.dll", 10, 1280}, + {DRIVER_AMD_R300, DRIVER_MODEL_NT6X, "atiumdag.dll", 10, 741 }, + {DRIVER_AMD_R600, DRIVER_MODEL_NT6X, "atiumdag.dll", 10, 1280}, + {DRIVER_AMD_RX, DRIVER_MODEL_NT6X, "aticfx32.dll", 15002, 61}, + + /* Intel + * The drivers are unified but not all versions support all GPUs. At some point the 2k/xp + * drivers used ialmrnt5.dll for GMA800/GMA900 but at some point the file was renamed to + * igxprd32.dll but the GMA800 driver was never updated. */ + {DRIVER_INTEL_GMA800, DRIVER_MODEL_NT5X, "ialmrnt5.dll", 10, 3889}, + {DRIVER_INTEL_GMA900, DRIVER_MODEL_NT5X, "igxprd32.dll", 10, 4764}, + {DRIVER_INTEL_GMA950, DRIVER_MODEL_NT5X, "igxprd32.dll", 10, 4926}, + {DRIVER_INTEL_GMA3000, DRIVER_MODEL_NT5X, "igxprd32.dll", 10, 5218}, + {DRIVER_INTEL_GMA950, DRIVER_MODEL_NT6X, "igdumd32.dll", 10, 1504}, + {DRIVER_INTEL_GMA3000, DRIVER_MODEL_NT6X, "igdumd32.dll", 10, 1666}, + {DRIVER_INTEL_HD4000, DRIVER_MODEL_NT6X, "igdumdim32.dll", 15, 4352}, + + /* Nvidia + * - Geforce8 and newer is supported by the current 340.52 driver on XP-Win8 + * - Geforce6 and 7 support is up to 307.83 on XP-Win8 + * - GeforceFX support is up to 173.x on <= XP + * - Geforce2MX/3/4 up to 96.x on <= XP + * - TNT/Geforce1/2 up to 71.x on <= XP + * All version numbers used below are from the Linux nvidia drivers. */ + {DRIVER_NVIDIA_TNT, DRIVER_MODEL_NT5X, "nv4_disp.dll", 10, 7186}, + {DRIVER_NVIDIA_GEFORCE2MX, DRIVER_MODEL_NT5X, "nv4_disp.dll", 10, 9371}, + {DRIVER_NVIDIA_GEFORCEFX, DRIVER_MODEL_NT5X, "nv4_disp.dll", 11, 7516}, + {DRIVER_NVIDIA_GEFORCE6, DRIVER_MODEL_NT5X, "nv4_disp.dll", 13, 783}, + {DRIVER_NVIDIA_GEFORCE8, DRIVER_MODEL_NT5X, "nv4_disp.dll", 13, 4052}, + {DRIVER_NVIDIA_GEFORCE6, DRIVER_MODEL_NT6X, "nvd3dum.dll", 13, 783}, + {DRIVER_NVIDIA_GEFORCE8, DRIVER_MODEL_NT6X, "nvd3dum.dll", 13, 4052}, + {DRIVER_NVIDIA_FERMI, DRIVER_MODEL_NT6X, "nvd3dum.dll", 13, 9135}, + {DRIVER_NVIDIA_KEPLER, DRIVER_MODEL_NT6X, "nvd3dum.dll", 14, 4587}, + + /* Red Hat */ + {DRIVER_REDHAT_VIRGL, DRIVER_MODEL_GENERIC, "virgl.dll", 0, 0}, + + /* VMware */ + {DRIVER_VMWARE, DRIVER_MODEL_NT5X, "vm3dum.dll", 1, 1134}, + + /* Wine */ + {DRIVER_WINE, DRIVER_MODEL_GENERIC, "wined3d.dll", 0, 0}, +}; + +/* The amount of video memory stored in the gpu description table is the minimum amount of video memory + * found on a board containing a specific GPU. */ +static const struct wined3d_gpu_description gpu_description_table[] = +{ + /* Nvidia cards */ + {HW_VENDOR_NVIDIA, CARD_NVIDIA_RIVA_128, "NVIDIA RIVA 128", DRIVER_NVIDIA_TNT, 4 }, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_RIVA_TNT, "NVIDIA RIVA TNT", DRIVER_NVIDIA_TNT, 16 }, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_RIVA_TNT2, "NVIDIA RIVA TNT2/TNT2 Pro", DRIVER_NVIDIA_TNT, 32 }, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE, "NVIDIA GeForce 256", DRIVER_NVIDIA_TNT, 32 }, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE2, "NVIDIA GeForce2 GTS/GeForce2 Pro", DRIVER_NVIDIA_TNT, 32 }, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE2_MX, "NVIDIA GeForce2 MX/MX 400", DRIVER_NVIDIA_GEFORCE2MX,32 }, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE3, "NVIDIA GeForce3", DRIVER_NVIDIA_GEFORCE2MX,64 }, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE4_MX, "NVIDIA GeForce4 MX 460", DRIVER_NVIDIA_GEFORCE2MX,64 }, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE4_TI4200, "NVIDIA GeForce4 Ti 4200", DRIVER_NVIDIA_GEFORCE2MX,64, }, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCEFX_5200, "NVIDIA GeForce FX 5200", DRIVER_NVIDIA_GEFORCEFX, 64 }, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCEFX_5600, "NVIDIA GeForce FX 5600", DRIVER_NVIDIA_GEFORCEFX, 128 }, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCEFX_5800, "NVIDIA GeForce FX 5800", DRIVER_NVIDIA_GEFORCEFX, 256 }, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_6200, "NVIDIA GeForce 6200", DRIVER_NVIDIA_GEFORCE6, 64 }, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_6600GT, "NVIDIA GeForce 6600 GT", DRIVER_NVIDIA_GEFORCE6, 128 }, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_6800, "NVIDIA GeForce 6800", DRIVER_NVIDIA_GEFORCE6, 128 }, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_7300, "NVIDIA GeForce Go 7300", DRIVER_NVIDIA_GEFORCE6, 256 }, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_7400, "NVIDIA GeForce Go 7400", DRIVER_NVIDIA_GEFORCE6, 256 }, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_7600, "NVIDIA GeForce 7600 GT", DRIVER_NVIDIA_GEFORCE6, 256 }, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_7800GT, "NVIDIA GeForce 7800 GT", DRIVER_NVIDIA_GEFORCE6, 256 }, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_8200, "NVIDIA GeForce 8200", DRIVER_NVIDIA_GEFORCE8, 512 }, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_8300GS, "NVIDIA GeForce 8300 GS", DRIVER_NVIDIA_GEFORCE8, 128 }, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_8400GS, "NVIDIA GeForce 8400 GS", DRIVER_NVIDIA_GEFORCE8, 128 }, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_8500GT, "NVIDIA GeForce 8500 GT", DRIVER_NVIDIA_GEFORCE8, 256 }, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_8600GT, "NVIDIA GeForce 8600 GT", DRIVER_NVIDIA_GEFORCE8, 256 }, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_8600MGT, "NVIDIA GeForce 8600M GT", DRIVER_NVIDIA_GEFORCE8, 512 }, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_8800GTS, "NVIDIA GeForce 8800 GTS", DRIVER_NVIDIA_GEFORCE8, 320 }, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_8800GTX, "NVIDIA GeForce 8800 GTX", DRIVER_NVIDIA_GEFORCE8, 768 }, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_9200, "NVIDIA GeForce 9200", DRIVER_NVIDIA_GEFORCE8, 256 }, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_9300, "NVIDIA GeForce 9300", DRIVER_NVIDIA_GEFORCE8, 256 }, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_9400M, "NVIDIA GeForce 9400M", DRIVER_NVIDIA_GEFORCE8, 256 }, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_9400GT, "NVIDIA GeForce 9400 GT", DRIVER_NVIDIA_GEFORCE8, 256 }, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_9500GT, "NVIDIA GeForce 9500 GT", DRIVER_NVIDIA_GEFORCE8, 256 }, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_9600GT, "NVIDIA GeForce 9600 GT", DRIVER_NVIDIA_GEFORCE8, 512 }, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_9700MGT, "NVIDIA GeForce 9700M GT", DRIVER_NVIDIA_GEFORCE8, 512 }, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_9800GT, "NVIDIA GeForce 9800 GT", DRIVER_NVIDIA_GEFORCE8, 512 }, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_210, "NVIDIA GeForce 210", DRIVER_NVIDIA_GEFORCE8, 512 }, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GT220, "NVIDIA GeForce GT 220", DRIVER_NVIDIA_GEFORCE8, 512 }, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GT240, "NVIDIA GeForce GT 240", DRIVER_NVIDIA_GEFORCE8, 512 }, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTS250, "NVIDIA GeForce GTS 250", DRIVER_NVIDIA_GEFORCE8, 1024}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX260, "NVIDIA GeForce GTX 260", DRIVER_NVIDIA_GEFORCE8, 1024}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX275, "NVIDIA GeForce GTX 275", DRIVER_NVIDIA_GEFORCE8, 896 }, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX280, "NVIDIA GeForce GTX 280", DRIVER_NVIDIA_GEFORCE8, 1024}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_315M, "NVIDIA GeForce 315M", DRIVER_NVIDIA_GEFORCE8, 512 }, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_320M, "NVIDIA GeForce 320M", DRIVER_NVIDIA_GEFORCE8, 256}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GT320M, "NVIDIA GeForce GT 320M", DRIVER_NVIDIA_GEFORCE8, 1024}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GT325M, "NVIDIA GeForce GT 325M", DRIVER_NVIDIA_GEFORCE8, 1024}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GT330, "NVIDIA GeForce GT 330", DRIVER_NVIDIA_GEFORCE8, 1024}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTS350M, "NVIDIA GeForce GTS 350M", DRIVER_NVIDIA_GEFORCE8, 1024}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_410M, "NVIDIA GeForce 410M", DRIVER_NVIDIA_FERMI, 512}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GT420, "NVIDIA GeForce GT 420", DRIVER_NVIDIA_FERMI, 2048}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GT425M, "NVIDIA GeForce GT 425M", DRIVER_NVIDIA_FERMI, 1024}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GT430, "NVIDIA GeForce GT 430", DRIVER_NVIDIA_FERMI, 1024}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GT440, "NVIDIA GeForce GT 440", DRIVER_NVIDIA_FERMI, 1024}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTS450, "NVIDIA GeForce GTS 450", DRIVER_NVIDIA_FERMI, 1024}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX460, "NVIDIA GeForce GTX 460", DRIVER_NVIDIA_FERMI, 768 }, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX460M, "NVIDIA GeForce GTX 460M", DRIVER_NVIDIA_FERMI, 1536}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX465, "NVIDIA GeForce GTX 465", DRIVER_NVIDIA_FERMI, 1024}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX470, "NVIDIA GeForce GTX 470", DRIVER_NVIDIA_FERMI, 1280}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX480, "NVIDIA GeForce GTX 480", DRIVER_NVIDIA_FERMI, 1536}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GT520, "NVIDIA GeForce GT 520", DRIVER_NVIDIA_FERMI, 1024}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GT525M, "NVIDIA GeForce GT 525M", DRIVER_NVIDIA_FERMI, 1024}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GT540M, "NVIDIA GeForce GT 540M", DRIVER_NVIDIA_FERMI, 1024}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX550, "NVIDIA GeForce GTX 550 Ti", DRIVER_NVIDIA_FERMI, 1024}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GT555M, "NVIDIA GeForce GT 555M", DRIVER_NVIDIA_FERMI, 1024}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX560TI, "NVIDIA GeForce GTX 560 Ti", DRIVER_NVIDIA_FERMI, 1024}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX560M, "NVIDIA GeForce GTX 560M", DRIVER_NVIDIA_FERMI, 3072}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX560, "NVIDIA GeForce GTX 560", DRIVER_NVIDIA_FERMI, 1024}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX570, "NVIDIA GeForce GTX 570", DRIVER_NVIDIA_FERMI, 1280}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX580, "NVIDIA GeForce GTX 580", DRIVER_NVIDIA_FERMI, 1536}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GT610, "NVIDIA GeForce GT 610", DRIVER_NVIDIA_FERMI, 1024}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GT630, "NVIDIA GeForce GT 630", DRIVER_NVIDIA_KEPLER, 1024}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GT630M, "NVIDIA GeForce GT 630M", DRIVER_NVIDIA_FERMI, 1024}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GT640, "NVIDIA GeForce GT 640", DRIVER_NVIDIA_KEPLER, 1024}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GT640M, "NVIDIA GeForce GT 640M", DRIVER_NVIDIA_KEPLER, 1024}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GT650M, "NVIDIA GeForce GT 650M", DRIVER_NVIDIA_KEPLER, 2048}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX650, "NVIDIA GeForce GTX 650", DRIVER_NVIDIA_KEPLER, 1024}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX650TI, "NVIDIA GeForce GTX 650 Ti", DRIVER_NVIDIA_KEPLER, 1024}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX660, "NVIDIA GeForce GTX 660", DRIVER_NVIDIA_KEPLER, 2048}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX660M, "NVIDIA GeForce GTX 660M", DRIVER_NVIDIA_KEPLER, 2048}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX660TI, "NVIDIA GeForce GTX 660 Ti", DRIVER_NVIDIA_KEPLER, 2048}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX670, "NVIDIA GeForce GTX 670", DRIVER_NVIDIA_KEPLER, 2048}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX670MX, "NVIDIA GeForce GTX 670MX", DRIVER_NVIDIA_KEPLER, 3072}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX675MX_1, "NVIDIA GeForce GTX 675MX", DRIVER_NVIDIA_KEPLER, 4096}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX675MX_2, "NVIDIA GeForce GTX 675MX", DRIVER_NVIDIA_KEPLER, 2048}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX680, "NVIDIA GeForce GTX 680", DRIVER_NVIDIA_KEPLER, 2048}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX690, "NVIDIA GeForce GTX 690", DRIVER_NVIDIA_KEPLER, 2048}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GT720, "NVIDIA GeForce GT 720", DRIVER_NVIDIA_KEPLER, 2048}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GT730, "NVIDIA GeForce GT 730", DRIVER_NVIDIA_KEPLER, 2048}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GT730M, "NVIDIA GeForce GT 730M", DRIVER_NVIDIA_KEPLER, 1024}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GT740M, "NVIDIA GeForce GT 740M", DRIVER_NVIDIA_KEPLER, 2048}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GT750M, "NVIDIA GeForce GT 750M", DRIVER_NVIDIA_KEPLER, 1024}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GT755M, "NVIDIA GeForce GT 755M", DRIVER_NVIDIA_KEPLER, 1024}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX750, "NVIDIA GeForce GTX 750", DRIVER_NVIDIA_KEPLER, 1024}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX750TI, "NVIDIA GeForce GTX 750 Ti", DRIVER_NVIDIA_KEPLER, 2048}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX760, "NVIDIA GeForce GTX 760", DRIVER_NVIDIA_KEPLER, 2048}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX760TI, "NVIDIA GeForce GTX 760 Ti", DRIVER_NVIDIA_KEPLER, 2048}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX765M, "NVIDIA GeForce GTX 765M", DRIVER_NVIDIA_KEPLER, 2048}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX770M, "NVIDIA GeForce GTX 770M", DRIVER_NVIDIA_KEPLER, 3072}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX770, "NVIDIA GeForce GTX 770", DRIVER_NVIDIA_KEPLER, 2048}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX775M, "NVIDIA GeForce GTX 775M", DRIVER_NVIDIA_KEPLER, 3072}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX780, "NVIDIA GeForce GTX 780", DRIVER_NVIDIA_KEPLER, 3072}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX780M, "NVIDIA GeForce GTX 780M", DRIVER_NVIDIA_KEPLER, 4096}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX780TI, "NVIDIA GeForce GTX 780 Ti", DRIVER_NVIDIA_KEPLER, 3072}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTXTITAN, "NVIDIA GeForce GTX TITAN", DRIVER_NVIDIA_KEPLER, 6144}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTXTITANB, "NVIDIA GeForce GTX TITAN Black", DRIVER_NVIDIA_KEPLER, 6144}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTXTITANX, "NVIDIA GeForce GTX TITAN X", DRIVER_NVIDIA_KEPLER, 12288}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTXTITANZ, "NVIDIA GeForce GTX TITAN Z", DRIVER_NVIDIA_KEPLER, 12288}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_820M, "NVIDIA GeForce 820M", DRIVER_NVIDIA_FERMI, 2048}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_830M, "NVIDIA GeForce 830M", DRIVER_NVIDIA_KEPLER, 2048}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_840M, "NVIDIA GeForce 840M", DRIVER_NVIDIA_KEPLER, 2048}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_845M, "NVIDIA GeForce 845M", DRIVER_NVIDIA_KEPLER, 2048}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX850M, "NVIDIA GeForce GTX 850M", DRIVER_NVIDIA_KEPLER, 2048}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX860M, "NVIDIA GeForce GTX 860M", DRIVER_NVIDIA_KEPLER, 2048}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX870M, "NVIDIA GeForce GTX 870M", DRIVER_NVIDIA_KEPLER, 3072}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX880M, "NVIDIA GeForce GTX 880M", DRIVER_NVIDIA_KEPLER, 4096}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_940M, "NVIDIA GeForce 940M", DRIVER_NVIDIA_KEPLER, 4096}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX950, "NVIDIA GeForce GTX 950", DRIVER_NVIDIA_KEPLER, 2048}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX950M, "NVIDIA GeForce GTX 950M", DRIVER_NVIDIA_KEPLER, 4096}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX960, "NVIDIA GeForce GTX 960", DRIVER_NVIDIA_KEPLER, 4096}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX960M, "NVIDIA GeForce GTX 960M", DRIVER_NVIDIA_KEPLER, 2048}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX970, "NVIDIA GeForce GTX 970", DRIVER_NVIDIA_KEPLER, 4096}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX970M, "NVIDIA GeForce GTX 970M", DRIVER_NVIDIA_KEPLER, 3072}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX980, "NVIDIA GeForce GTX 980", DRIVER_NVIDIA_KEPLER, 4096}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX980TI, "NVIDIA GeForce GTX 980 Ti", DRIVER_NVIDIA_KEPLER, 6144}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX1050, "NVIDIA GeForce GTX 1050", DRIVER_NVIDIA_KEPLER, 2048}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX1050TI, "NVIDIA GeForce GTX 1050 Ti", DRIVER_NVIDIA_KEPLER, 4096}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX1060_3GB,"NVIDIA GeForce GTX 1060 3GB", DRIVER_NVIDIA_KEPLER, 3072}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX1060, "NVIDIA GeForce GTX 1060", DRIVER_NVIDIA_KEPLER, 6144}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX1060M, "NVIDIA GeForce GTX 1060M", DRIVER_NVIDIA_KEPLER, 6144}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX1070, "NVIDIA GeForce GTX 1070", DRIVER_NVIDIA_KEPLER, 8192}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX1080, "NVIDIA GeForce GTX 1080", DRIVER_NVIDIA_KEPLER, 8192}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX1080M, "NVIDIA GeForce GTX 1080M", DRIVER_NVIDIA_KEPLER, 8192}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX1080TI, "NVIDIA GeForce GTX 1080 Ti", DRIVER_NVIDIA_KEPLER, 11264}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_TITANX_PASCAL, "NVIDIA TITAN X (Pascal)", DRIVER_NVIDIA_KEPLER, 12288}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_TITANV, "NVIDIA TITAN V", DRIVER_NVIDIA_KEPLER, 12288}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX1650SUPER,"NVIDIA GeForce GTX 1650 SUPER", DRIVER_NVIDIA_KEPLER, 4096}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX1660SUPER,"NVIDIA GeForce GTX 1660 SUPER", DRIVER_NVIDIA_KEPLER, 6144}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_GTX1660TI, "NVIDIA GeForce GTX 1660 Ti", DRIVER_NVIDIA_KEPLER, 6144}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_RTX2060, "NVIDIA GeForce RTX 2060", DRIVER_NVIDIA_KEPLER, 6144}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_RTX2070, "NVIDIA GeForce RTX 2070", DRIVER_NVIDIA_KEPLER, 8192}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_RTX2080, "NVIDIA GeForce RTX 2080", DRIVER_NVIDIA_KEPLER, 8192}, + {HW_VENDOR_NVIDIA, CARD_NVIDIA_GEFORCE_RTX2080TI, "NVIDIA GeForce RTX 2080 Ti", DRIVER_NVIDIA_KEPLER, 11264}, + + /* AMD cards */ + {HW_VENDOR_AMD, CARD_AMD_RAGE_128PRO, "ATI Rage Fury", DRIVER_AMD_RAGE_128PRO, 16 }, + {HW_VENDOR_AMD, CARD_AMD_RADEON_7200, "ATI RADEON 7200 SERIES", DRIVER_AMD_R100, 32 }, + {HW_VENDOR_AMD, CARD_AMD_RADEON_8500, "ATI RADEON 8500 SERIES", DRIVER_AMD_R100, 64 }, + {HW_VENDOR_AMD, CARD_AMD_RADEON_9500, "ATI Radeon 9500", DRIVER_AMD_R300, 64 }, + {HW_VENDOR_AMD, CARD_AMD_RADEON_XPRESS_200M, "ATI RADEON XPRESS 200M Series", DRIVER_AMD_R300, 64 }, + {HW_VENDOR_AMD, CARD_AMD_RADEON_X700, "ATI Radeon X700 SE", DRIVER_AMD_R300, 128 }, + {HW_VENDOR_AMD, CARD_AMD_RADEON_X1600, "ATI Radeon X1600 Series", DRIVER_AMD_R300, 128 }, + {HW_VENDOR_AMD, CARD_AMD_RADEON_HD2350, "ATI Mobility Radeon HD 2350", DRIVER_AMD_R600, 256 }, + {HW_VENDOR_AMD, CARD_AMD_RADEON_HD2600, "ATI Mobility Radeon HD 2600", DRIVER_AMD_R600, 256 }, + {HW_VENDOR_AMD, CARD_AMD_RADEON_HD2900, "ATI Radeon HD 2900 XT", DRIVER_AMD_R600, 512 }, + {HW_VENDOR_AMD, CARD_AMD_RADEON_HD3200, "ATI Radeon HD 3200 Graphics", DRIVER_AMD_R600, 128 }, + {HW_VENDOR_AMD, CARD_AMD_RADEON_HD3850, "ATI Radeon HD 3850 AGP", DRIVER_AMD_R600, 512 }, + {HW_VENDOR_AMD, CARD_AMD_RADEON_HD4200M, "ATI Mobility Radeon HD 4200", DRIVER_AMD_R600, 256 }, + {HW_VENDOR_AMD, CARD_AMD_RADEON_HD4350, "ATI Radeon HD 4350", DRIVER_AMD_R600, 256 }, + {HW_VENDOR_AMD, CARD_AMD_RADEON_HD4600, "ATI Radeon HD 4600 Series", DRIVER_AMD_R600, 512 }, + {HW_VENDOR_AMD, CARD_AMD_RADEON_HD4700, "ATI Radeon HD 4700 Series", DRIVER_AMD_R600, 512 }, + {HW_VENDOR_AMD, CARD_AMD_RADEON_HD4800, "ATI Radeon HD 4800 Series", DRIVER_AMD_R600, 512 }, + {HW_VENDOR_AMD, CARD_AMD_RADEON_HD5400, "ATI Radeon HD 5400 Series", DRIVER_AMD_R600, 512 }, + {HW_VENDOR_AMD, CARD_AMD_RADEON_HD5600, "ATI Radeon HD 5600 Series", DRIVER_AMD_R600, 512 }, + {HW_VENDOR_AMD, CARD_AMD_RADEON_HD5700, "ATI Radeon HD 5700 Series", DRIVER_AMD_R600, 512 }, + {HW_VENDOR_AMD, CARD_AMD_RADEON_HD5800, "ATI Radeon HD 5800 Series", DRIVER_AMD_R600, 1024}, + {HW_VENDOR_AMD, CARD_AMD_RADEON_HD5900, "ATI Radeon HD 5900 Series", DRIVER_AMD_R600, 1024}, + {HW_VENDOR_AMD, CARD_AMD_RADEON_HD6300, "AMD Radeon HD 6300 series Graphics", DRIVER_AMD_R600, 1024}, + {HW_VENDOR_AMD, CARD_AMD_RADEON_HD6400, "AMD Radeon HD 6400 Series", DRIVER_AMD_R600, 1024}, + {HW_VENDOR_AMD, CARD_AMD_RADEON_HD6410D, "AMD Radeon HD 6410D", DRIVER_AMD_R600, 1024}, + {HW_VENDOR_AMD, CARD_AMD_RADEON_HD6480G, "AMD Radeon HD 6480G", DRIVER_AMD_R600, 512 }, + {HW_VENDOR_AMD, CARD_AMD_RADEON_HD6490M, "AMD Radeon HD 6490M", DRIVER_AMD_R600, 1024}, + {HW_VENDOR_AMD, CARD_AMD_RADEON_HD6550D, "AMD Radeon HD 6550D", DRIVER_AMD_R600, 1024}, + {HW_VENDOR_AMD, CARD_AMD_RADEON_HD6600, "AMD Radeon HD 6600 Series", DRIVER_AMD_R600, 1024}, + {HW_VENDOR_AMD, CARD_AMD_RADEON_HD6600M, "AMD Radeon HD 6600M Series", DRIVER_AMD_R600, 512 }, + {HW_VENDOR_AMD, CARD_AMD_RADEON_HD6700, "AMD Radeon HD 6700 Series", DRIVER_AMD_R600, 1024}, + {HW_VENDOR_AMD, CARD_AMD_RADEON_HD6800, "AMD Radeon HD 6800 Series", DRIVER_AMD_R600, 1024}, + {HW_VENDOR_AMD, CARD_AMD_RADEON_HD6900, "AMD Radeon HD 6900 Series", DRIVER_AMD_R600, 2048}, + {HW_VENDOR_AMD, CARD_AMD_RADEON_HD7660D, "AMD Radeon HD 7660D", DRIVER_AMD_R600, 2048}, + {HW_VENDOR_AMD, CARD_AMD_RADEON_HD7700, "AMD Radeon HD 7700 Series", DRIVER_AMD_R600, 1024}, + {HW_VENDOR_AMD, CARD_AMD_RADEON_HD7800, "AMD Radeon HD 7800 Series", DRIVER_AMD_R600, 2048}, + {HW_VENDOR_AMD, CARD_AMD_RADEON_HD7870, "AMD Radeon HD 7870 Series", DRIVER_AMD_R600, 2048}, + {HW_VENDOR_AMD, CARD_AMD_RADEON_HD7900, "AMD Radeon HD 7900 Series", DRIVER_AMD_R600, 2048}, + {HW_VENDOR_AMD, CARD_AMD_RADEON_HD8600M, "AMD Radeon HD 8600M Series", DRIVER_AMD_R600, 1024}, + {HW_VENDOR_AMD, CARD_AMD_RADEON_HD8670, "AMD Radeon HD 8670", DRIVER_AMD_R600, 2048}, + {HW_VENDOR_AMD, CARD_AMD_RADEON_HD8770, "AMD Radeon HD 8770", DRIVER_AMD_R600, 2048}, + {HW_VENDOR_AMD, CARD_AMD_RADEON_R3, "AMD Radeon HD 8400 / R3 Series", DRIVER_AMD_R600, 2048}, + {HW_VENDOR_AMD, CARD_AMD_RADEON_R7, "AMD Radeon(TM) R7 Graphics", DRIVER_AMD_R600, 2048}, + {HW_VENDOR_AMD, CARD_AMD_RADEON_R9_285, "AMD Radeon R9 285", DRIVER_AMD_RX, 2048}, + {HW_VENDOR_AMD, CARD_AMD_RADEON_R9_290, "AMD Radeon R9 290", DRIVER_AMD_RX, 4096}, + {HW_VENDOR_AMD, CARD_AMD_RADEON_R9_290X, "AMD Radeon R9 290X", DRIVER_AMD_RX, 4096}, + {HW_VENDOR_AMD, CARD_AMD_RADEON_R9_FURY, "AMD Radeon (TM) R9 Fury Series", DRIVER_AMD_RX, 4096}, + {HW_VENDOR_AMD, CARD_AMD_RADEON_R9_M370X, "AMD Radeon R9 M370X", DRIVER_AMD_RX, 2048}, + {HW_VENDOR_AMD, CARD_AMD_RADEON_R9_M380, "AMD Radeon R9 M380", DRIVER_AMD_RX, 2048}, + {HW_VENDOR_AMD, CARD_AMD_RADEON_R9_M395X, "AMD Radeon R9 M395X", DRIVER_AMD_RX, 4096}, + {HW_VENDOR_AMD, CARD_AMD_RADEON_RX_460, "Radeon(TM) RX 460 Graphics", DRIVER_AMD_RX, 4096}, + {HW_VENDOR_AMD, CARD_AMD_RADEON_RX_480, "Radeon (TM) RX 480 Graphics", DRIVER_AMD_RX, 4096}, + {HW_VENDOR_AMD, CARD_AMD_RADEON_RX_VEGA_10, "Radeon RX Vega", DRIVER_AMD_RX, 8192}, + {HW_VENDOR_AMD, CARD_AMD_RADEON_RX_VEGA_12, "Radeon Pro Vega 20", DRIVER_AMD_RX, 4096}, + {HW_VENDOR_AMD, CARD_AMD_RADEON_RAVEN, "AMD Radeon(TM) Vega 10 Mobile Graphics", DRIVER_AMD_RX, 1024}, + {HW_VENDOR_AMD, CARD_AMD_RADEON_RX_VEGA_20, "Radeon RX Vega 20", DRIVER_AMD_RX, 4096}, + {HW_VENDOR_AMD, CARD_AMD_RADEON_RX_NAVI_10, "Radeon RX 5700 / 5700 XT", DRIVER_AMD_RX, 8192}, + {HW_VENDOR_AMD, CARD_AMD_RADEON_RX_NAVI_14, "Radeon RX 5500M", DRIVER_AMD_RX, 4096}, + + /* Red Hat */ + {HW_VENDOR_REDHAT, CARD_REDHAT_VIRGL, "Red Hat VirtIO GPU", DRIVER_REDHAT_VIRGL, 1024}, + + /* VMware */ + {HW_VENDOR_VMWARE, CARD_VMWARE_SVGA3D, "VMware SVGA 3D (Microsoft Corporation - WDDM)", DRIVER_VMWARE, 1024}, + + /* Intel cards */ + {HW_VENDOR_INTEL, CARD_INTEL_830M, "Intel(R) 82830M Graphics Controller", DRIVER_INTEL_GMA800, 32 }, + {HW_VENDOR_INTEL, CARD_INTEL_855GM, "Intel(R) 82852/82855 GM/GME Graphics Controller", DRIVER_INTEL_GMA800, 32 }, + {HW_VENDOR_INTEL, CARD_INTEL_845G, "Intel(R) 845G", DRIVER_INTEL_GMA800, 32 }, + {HW_VENDOR_INTEL, CARD_INTEL_865G, "Intel(R) 82865G Graphics Controller", DRIVER_INTEL_GMA800, 32 }, + {HW_VENDOR_INTEL, CARD_INTEL_915G, "Intel(R) 82915G/GV/910GL Express Chipset Family", DRIVER_INTEL_GMA900, 64 }, + {HW_VENDOR_INTEL, CARD_INTEL_E7221G, "Intel(R) E7221G", DRIVER_INTEL_GMA900, 64 }, + {HW_VENDOR_INTEL, CARD_INTEL_915GM, "Mobile Intel(R) 915GM/GMS,910GML Express Chipset Family", DRIVER_INTEL_GMA900, 64 }, + {HW_VENDOR_INTEL, CARD_INTEL_945G, "Intel(R) 945G", DRIVER_INTEL_GMA950, 64 }, + {HW_VENDOR_INTEL, CARD_INTEL_945GM, "Mobile Intel(R) 945GM Express Chipset Family", DRIVER_INTEL_GMA950, 64 }, + {HW_VENDOR_INTEL, CARD_INTEL_945GME, "Intel(R) 945GME", DRIVER_INTEL_GMA950, 64 }, + {HW_VENDOR_INTEL, CARD_INTEL_Q35, "Intel(R) Q35", DRIVER_INTEL_GMA950, 64 }, + {HW_VENDOR_INTEL, CARD_INTEL_G33, "Intel(R) G33", DRIVER_INTEL_GMA950, 64 }, + {HW_VENDOR_INTEL, CARD_INTEL_Q33, "Intel(R) Q33", DRIVER_INTEL_GMA950, 64 }, + {HW_VENDOR_INTEL, CARD_INTEL_PNVG, "Intel(R) IGD", DRIVER_INTEL_GMA950, 64 }, + {HW_VENDOR_INTEL, CARD_INTEL_PNVM, "Intel(R) IGD", DRIVER_INTEL_GMA950, 64 }, + {HW_VENDOR_INTEL, CARD_INTEL_965Q, "Intel(R) 965Q", DRIVER_INTEL_GMA3000, 128}, + {HW_VENDOR_INTEL, CARD_INTEL_965G, "Intel(R) 965G", DRIVER_INTEL_GMA3000, 128}, + {HW_VENDOR_INTEL, CARD_INTEL_946GZ, "Intel(R) 946GZ", DRIVER_INTEL_GMA3000, 128}, + {HW_VENDOR_INTEL, CARD_INTEL_965GM, "Mobile Intel(R) 965 Express Chipset Family", DRIVER_INTEL_GMA3000, 128}, + {HW_VENDOR_INTEL, CARD_INTEL_965GME, "Intel(R) 965GME", DRIVER_INTEL_GMA3000, 128}, + {HW_VENDOR_INTEL, CARD_INTEL_GM45, "Mobile Intel(R) GM45 Express Chipset Family", DRIVER_INTEL_GMA3000, 512}, + {HW_VENDOR_INTEL, CARD_INTEL_IGD, "Intel(R) Integrated Graphics Device", DRIVER_INTEL_GMA3000, 512}, + {HW_VENDOR_INTEL, CARD_INTEL_G45, "Intel(R) G45/G43", DRIVER_INTEL_GMA3000, 512}, + {HW_VENDOR_INTEL, CARD_INTEL_Q45, "Intel(R) Q45/Q43", DRIVER_INTEL_GMA3000, 512}, + {HW_VENDOR_INTEL, CARD_INTEL_G41, "Intel(R) G41", DRIVER_INTEL_GMA3000, 512}, + {HW_VENDOR_INTEL, CARD_INTEL_B43, "Intel(R) B43", DRIVER_INTEL_GMA3000, 512}, + {HW_VENDOR_INTEL, CARD_INTEL_ILKD, "Intel(R) HD Graphics", DRIVER_INTEL_GMA3000, 1536}, + {HW_VENDOR_INTEL, CARD_INTEL_ILKM, "Intel(R) HD Graphics", DRIVER_INTEL_GMA3000, 1536}, + {HW_VENDOR_INTEL, CARD_INTEL_SNBD, "Intel(R) HD Graphics 3000", DRIVER_INTEL_GMA3000, 1536}, + {HW_VENDOR_INTEL, CARD_INTEL_SNBM, "Intel(R) HD Graphics 3000", DRIVER_INTEL_GMA3000, 1536}, + {HW_VENDOR_INTEL, CARD_INTEL_SNBS, "Intel(R) HD Graphics Family", DRIVER_INTEL_GMA3000, 1536}, + {HW_VENDOR_INTEL, CARD_INTEL_IVBD, "Intel(R) HD Graphics 4000", DRIVER_INTEL_HD4000, 1536}, + {HW_VENDOR_INTEL, CARD_INTEL_IVBM, "Intel(R) HD Graphics 4000", DRIVER_INTEL_HD4000, 1536}, + {HW_VENDOR_INTEL, CARD_INTEL_IVBS, "Intel(R) HD Graphics Family", DRIVER_INTEL_HD4000, 1536}, + {HW_VENDOR_INTEL, CARD_INTEL_HWD, "Intel(R) HD Graphics 4600", DRIVER_INTEL_HD4000, 1536}, + {HW_VENDOR_INTEL, CARD_INTEL_HWM, "Intel(R) HD Graphics 4600", DRIVER_INTEL_HD4000, 1536}, + {HW_VENDOR_INTEL, CARD_INTEL_HD5000_1, "Intel(R) HD Graphics 5000", DRIVER_INTEL_HD4000, 1536}, + {HW_VENDOR_INTEL, CARD_INTEL_HD5000_2, "Intel(R) HD Graphics 5000", DRIVER_INTEL_HD4000, 1536}, + {HW_VENDOR_INTEL, CARD_INTEL_I5100_1, "Intel(R) Iris(TM) Graphics 5100", DRIVER_INTEL_HD4000, 1536}, + {HW_VENDOR_INTEL, CARD_INTEL_I5100_2, "Intel(R) Iris(TM) Graphics 5100", DRIVER_INTEL_HD4000, 1536}, + {HW_VENDOR_INTEL, CARD_INTEL_I5100_3, "Intel(R) Iris(TM) Graphics 5100", DRIVER_INTEL_HD4000, 1536}, + {HW_VENDOR_INTEL, CARD_INTEL_I5100_4, "Intel(R) Iris(TM) Graphics 5100", DRIVER_INTEL_HD4000, 1536}, + {HW_VENDOR_INTEL, CARD_INTEL_IP5200_1, "Intel(R) Iris(TM) Pro Graphics 5200", DRIVER_INTEL_HD4000, 1536}, + {HW_VENDOR_INTEL, CARD_INTEL_IP5200_2, "Intel(R) Iris(TM) Pro Graphics 5200", DRIVER_INTEL_HD4000, 1536}, + {HW_VENDOR_INTEL, CARD_INTEL_IP5200_3, "Intel(R) Iris(TM) Pro Graphics 5200", DRIVER_INTEL_HD4000, 1536}, + {HW_VENDOR_INTEL, CARD_INTEL_IP5200_4, "Intel(R) Iris(TM) Pro Graphics 5200", DRIVER_INTEL_HD4000, 1536}, + {HW_VENDOR_INTEL, CARD_INTEL_IP5200_5, "Intel(R) Iris(TM) Pro Graphics 5200", DRIVER_INTEL_HD4000, 1536}, + {HW_VENDOR_INTEL, CARD_INTEL_IP5200_6, "Intel(R) Iris(TM) Pro Graphics 5200", DRIVER_INTEL_HD4000, 2048}, + {HW_VENDOR_INTEL, CARD_INTEL_HD5300, "Intel(R) HD Graphics 5300", DRIVER_INTEL_HD4000, 2048}, + {HW_VENDOR_INTEL, CARD_INTEL_HD5500, "Intel(R) HD Graphics 5500", DRIVER_INTEL_HD4000, 2048}, + {HW_VENDOR_INTEL, CARD_INTEL_HD5600, "Intel(R) HD Graphics 5600", DRIVER_INTEL_HD4000, 2048}, + {HW_VENDOR_INTEL, CARD_INTEL_HD6000, "Intel(R) HD Graphics 6000", DRIVER_INTEL_HD4000, 2048}, + {HW_VENDOR_INTEL, CARD_INTEL_I6100, "Intel(R) Iris(TM) Graphics 6100", DRIVER_INTEL_HD4000, 2048}, + {HW_VENDOR_INTEL, CARD_INTEL_IP6200, "Intel(R) Iris(TM) Pro Graphics 6200", DRIVER_INTEL_HD4000, 2048}, + {HW_VENDOR_INTEL, CARD_INTEL_IPP6300, "Intel(R) Iris(TM) Pro Graphics P6300", DRIVER_INTEL_HD4000, 2048}, + {HW_VENDOR_INTEL, CARD_INTEL_HD510_1, "Intel(R) HD Graphics 510", DRIVER_INTEL_HD4000, 2048}, + {HW_VENDOR_INTEL, CARD_INTEL_HD510_2, "Intel(R) HD Graphics 510", DRIVER_INTEL_HD4000, 2048}, + {HW_VENDOR_INTEL, CARD_INTEL_HD510_3, "Intel(R) HD Graphics 510", DRIVER_INTEL_HD4000, 2048}, + {HW_VENDOR_INTEL, CARD_INTEL_HD515, "Intel(R) HD Graphics 515", DRIVER_INTEL_HD4000, 2048}, + {HW_VENDOR_INTEL, CARD_INTEL_HD520_1, "Intel(R) HD Graphics 520", DRIVER_INTEL_HD4000, 2048}, + {HW_VENDOR_INTEL, CARD_INTEL_HD520_2, "Intel(R) HD Graphics 520", DRIVER_INTEL_HD4000, 2048}, + {HW_VENDOR_INTEL, CARD_INTEL_HD530_1, "Intel(R) HD Graphics 530", DRIVER_INTEL_HD4000, 2048}, + {HW_VENDOR_INTEL, CARD_INTEL_HD530_2, "Intel(R) HD Graphics 530", DRIVER_INTEL_HD4000, 2048}, + {HW_VENDOR_INTEL, CARD_INTEL_HDP530, "Intel(R) HD Graphics P530", DRIVER_INTEL_HD4000, 2048}, + {HW_VENDOR_INTEL, CARD_INTEL_I540, "Intel(R) Iris(TM) Graphics 540", DRIVER_INTEL_HD4000, 2048}, + {HW_VENDOR_INTEL, CARD_INTEL_I550, "Intel(R) Iris(TM) Graphics 550", DRIVER_INTEL_HD4000, 2048}, + {HW_VENDOR_INTEL, CARD_INTEL_I555, "Intel(R) Iris(TM) Graphics 555", DRIVER_INTEL_HD4000, 2048}, + {HW_VENDOR_INTEL, CARD_INTEL_IP555, "Intel(R) Iris(TM) Graphics P555", DRIVER_INTEL_HD4000, 2048}, + {HW_VENDOR_INTEL, CARD_INTEL_IP580_1, "Intel(R) Iris(TM) Pro Graphics 580", DRIVER_INTEL_HD4000, 2048}, + {HW_VENDOR_INTEL, CARD_INTEL_IP580_2, "Intel(R) Iris(TM) Pro Graphics 580", DRIVER_INTEL_HD4000, 2048}, + {HW_VENDOR_INTEL, CARD_INTEL_IPP580_1, "Intel(R) Iris(TM) Pro Graphics P580", DRIVER_INTEL_HD4000, 2048}, + {HW_VENDOR_INTEL, CARD_INTEL_IPP580_2, "Intel(R) Iris(TM) Pro Graphics P580", DRIVER_INTEL_HD4000, 2048}, + {HW_VENDOR_INTEL, CARD_INTEL_UHD617, "Intel(R) UHD Graphics 617", DRIVER_INTEL_HD4000, 2048}, + {HW_VENDOR_INTEL, CARD_INTEL_UHD620, "Intel(R) UHD Graphics 620", DRIVER_INTEL_HD4000, 3072}, + {HW_VENDOR_INTEL, CARD_INTEL_HD615, "Intel(R) HD Graphics 615", DRIVER_INTEL_HD4000, 2048}, + {HW_VENDOR_INTEL, CARD_INTEL_HD620, "Intel(R) HD Graphics 620", DRIVER_INTEL_HD4000, 3072}, + {HW_VENDOR_INTEL, CARD_INTEL_HD630_1, "Intel(R) HD Graphics 630", DRIVER_INTEL_HD4000, 3072}, + {HW_VENDOR_INTEL, CARD_INTEL_HD630_2, "Intel(R) HD Graphics 630", DRIVER_INTEL_HD4000, 3072}, +}; + +static const struct driver_version_information *get_driver_version_info(enum wined3d_display_driver driver, + enum wined3d_driver_model driver_model) +{ + unsigned int i; + + TRACE("Looking up version info for driver %#x, driver_model %#x.\n", driver, driver_model); + + for (i = 0; i < ARRAY_SIZE(driver_version_table); ++i) + { + const struct driver_version_information *entry = &driver_version_table[i]; + + if (entry->driver == driver && (driver_model == DRIVER_MODEL_GENERIC + || entry->driver_model == driver_model)) + { + TRACE("Found driver \"%s\", subversion %u, build %u.\n", + entry->driver_name, entry->subversion, entry->build); + return entry; + } + } + return NULL; +} + +const struct wined3d_gpu_description *wined3d_get_gpu_description(enum wined3d_pci_vendor vendor, + enum wined3d_pci_device device) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(gpu_description_table); ++i) + { + if (vendor == gpu_description_table[i].vendor && device == gpu_description_table[i].device) + return &gpu_description_table[i]; + } + + return NULL; +} + +const struct wined3d_gpu_description *wined3d_get_user_override_gpu_description(enum wined3d_pci_vendor vendor, + enum wined3d_pci_device device) +{ + const struct wined3d_gpu_description *gpu_desc; + static unsigned int once; + + if (wined3d_settings.pci_vendor_id == PCI_VENDOR_NONE && wined3d_settings.pci_device_id == PCI_DEVICE_NONE) + return NULL; + + if (wined3d_settings.pci_vendor_id != PCI_VENDOR_NONE) + { + vendor = wined3d_settings.pci_vendor_id; + TRACE("Overriding vendor PCI ID with 0x%04x.\n", vendor); + } + if (wined3d_settings.pci_device_id != PCI_DEVICE_NONE) + { + device = wined3d_settings.pci_device_id; + TRACE("Overriding device PCI ID with 0x%04x.\n", device); + } + + if (!(gpu_desc = wined3d_get_gpu_description(vendor, device)) && !once++) + ERR_(winediag)("Invalid GPU override %04x:%04x specified, ignoring.\n", vendor, device); + + return gpu_desc; +} + +static void wined3d_copy_name(char *dst, const char *src, unsigned int dst_size) +{ + size_t len; + + if (dst_size) + { + len = min(strlen(src), dst_size - 1); + memcpy(dst, src, len); + memset(&dst[len], 0, dst_size - len); + } +} + +bool wined3d_driver_info_init(struct wined3d_driver_info *driver_info, + const struct wined3d_gpu_description *gpu_desc, enum wined3d_feature_level feature_level, + UINT64 vram_bytes, UINT64 sysmem_bytes) +{ + const struct driver_version_information *version_info; + WORD driver_os_version, driver_feature_level = 10; + enum wined3d_driver_model driver_model; + enum wined3d_display_driver driver; + MEMORYSTATUSEX memory_status; + OSVERSIONINFOW os_version; + + memset(&os_version, 0, sizeof(os_version)); + os_version.dwOSVersionInfoSize = sizeof(os_version); + if (!GetVersionExW(&os_version)) + { + ERR("Failed to get OS version, reporting 2000/XP.\n"); + driver_os_version = 6; + driver_model = DRIVER_MODEL_NT5X; + } + else + { + TRACE("OS version %u.%u.\n", os_version.dwMajorVersion, os_version.dwMinorVersion); + switch (os_version.dwMajorVersion) + { + case 2: + case 3: + case 4: + /* If needed we could distinguish between 9x and NT4, but this code won't make + * sense for NT4 since it had no way to obtain this info through DirectDraw 3.0. + */ + driver_os_version = 4; + driver_model = DRIVER_MODEL_WIN9X; + break; + + case 5: + driver_os_version = 6; + driver_model = DRIVER_MODEL_NT5X; + break; + + case 6: + if (os_version.dwMinorVersion == 0) + { + driver_os_version = 7; + driver_model = DRIVER_MODEL_NT6X; + } + else if (os_version.dwMinorVersion == 1) + { + driver_os_version = 8; + driver_model = DRIVER_MODEL_NT6X; + } + else if (os_version.dwMinorVersion == 2) + { + driver_os_version = 9; + driver_model = DRIVER_MODEL_NT6X; + } + else + { + if (os_version.dwMinorVersion > 3) + { + FIXME("Unhandled OS version %u.%u, reporting Windows 8.1.\n", + os_version.dwMajorVersion, os_version.dwMinorVersion); + } + driver_os_version = 10; + driver_model = DRIVER_MODEL_NT6X; + } + break; + + case 10: + driver_os_version = 26; + driver_model = DRIVER_MODEL_NT6X; + break; + + default: + FIXME("Unhandled OS version %u.%u, reporting Windows 7.\n", + os_version.dwMajorVersion, os_version.dwMinorVersion); + driver_os_version = 8; + driver_model = DRIVER_MODEL_NT6X; + break; + } + } + + TRACE("GPU maximum feature level %#x.\n", feature_level); + switch (feature_level) + { + case WINED3D_FEATURE_LEVEL_NONE: + case WINED3D_FEATURE_LEVEL_5: + if (driver_model == DRIVER_MODEL_WIN9X) + driver_feature_level = 5; + else + driver_feature_level = 10; + break; + case WINED3D_FEATURE_LEVEL_6: + driver_feature_level = 11; + break; + case WINED3D_FEATURE_LEVEL_7: + driver_feature_level = 12; + break; + case WINED3D_FEATURE_LEVEL_8: + driver_feature_level = 13; + break; + case WINED3D_FEATURE_LEVEL_9_1: + case WINED3D_FEATURE_LEVEL_9_2: + case WINED3D_FEATURE_LEVEL_9_3: + driver_feature_level = 14; + break; + case WINED3D_FEATURE_LEVEL_10: + driver_feature_level = 15; + break; + case WINED3D_FEATURE_LEVEL_10_1: + driver_feature_level = 16; + break; + case WINED3D_FEATURE_LEVEL_11: + driver_feature_level = 17; + break; + case WINED3D_FEATURE_LEVEL_11_1: + /* Advertise support for everything up to FL 12_x. */ + driver_feature_level = 21; + break; + } + if (os_version.dwMajorVersion == 6 && !os_version.dwMinorVersion) + driver_feature_level = min(driver_feature_level, 18); + else if (os_version.dwMajorVersion < 6) + driver_feature_level = min(driver_feature_level, 14); + + driver_info->vendor = gpu_desc->vendor; + driver_info->device = gpu_desc->device; + wined3d_copy_name(driver_info->description, gpu_desc->description, ARRAY_SIZE(driver_info->description)); + driver_info->vram_bytes = vram_bytes ? vram_bytes : (UINT64)gpu_desc->vidmem * 1024 * 1024; + driver = gpu_desc->driver; + + if (wined3d_settings.emulated_textureram) + { + driver_info->vram_bytes = wined3d_settings.emulated_textureram; + TRACE("Overriding amount of video memory with 0x%s bytes.\n", + wine_dbgstr_longlong(driver_info->vram_bytes)); + } + + /** + * Diablo 2 crashes when the amount of video memory is greater than 0x7fffffff. + * In order to avoid this application bug we limit the amount of video memory + * to LONG_MAX for older Windows versions. + */ + if (driver_model < DRIVER_MODEL_NT6X && driver_info->vram_bytes > LONG_MAX) + { + TRACE("Limiting amount of video memory to %#lx bytes for OS version older than Vista.\n", LONG_MAX); + driver_info->vram_bytes = LONG_MAX; + } + + if (!(driver_info->sysmem_bytes = sysmem_bytes)) + { + driver_info->sysmem_bytes = 64 * 1024 * 1024; + memory_status.dwLength = sizeof(memory_status); + if (GlobalMemoryStatusEx(&memory_status)) + driver_info->sysmem_bytes = max(memory_status.ullTotalPhys / 2, driver_info->sysmem_bytes); + else + ERR("Failed to get global memory status.\n"); + } + + /* Try to obtain driver version information for the current Windows version. This fails in + * some cases: + * - the gpu is not available on the currently selected OS version: + * - Geforce GTX480 on Win98. When running applications in compatibility mode on Windows, + * version information for the current Windows version is returned instead of faked info. + * We do the same and assume the default Windows version to emulate is WinXP. + * + * - Videocard is a Riva TNT but winver is set to win7 (there are no drivers for this beast) + * For now return the XP driver info. Perhaps later on we should return VESA. + * + * - the gpu is not in our database (can happen when the user overrides the vendor_id / device_id) + * This could be an indication that our database is not up to date, so this should be fixed. + */ + if ((version_info = get_driver_version_info(driver, driver_model)) + || (version_info = get_driver_version_info(driver, DRIVER_MODEL_GENERIC))) + { + driver_info->name = version_info->driver_name; + driver_info->version_high = MAKEDWORD_VERSION(driver_os_version, driver_feature_level); + driver_info->version_low = MAKEDWORD_VERSION(version_info->subversion, version_info->build); + + return true; + } + + ERR("No driver version info found for device %04x:%04x, driver model %#x.\n", + driver_info->vendor, driver_info->device, driver_model); + return false; +} + +enum wined3d_pci_device wined3d_gpu_from_feature_level(enum wined3d_pci_vendor *vendor, + enum wined3d_feature_level feature_level) +{ + static const struct wined3d_fallback_card + { + enum wined3d_feature_level feature_level; + enum wined3d_pci_device device_id; + } + card_fallback_nvidia[] = + { + {WINED3D_FEATURE_LEVEL_5, CARD_NVIDIA_RIVA_128}, + {WINED3D_FEATURE_LEVEL_6, CARD_NVIDIA_RIVA_TNT}, + {WINED3D_FEATURE_LEVEL_7, CARD_NVIDIA_GEFORCE}, + {WINED3D_FEATURE_LEVEL_8, CARD_NVIDIA_GEFORCE3}, + {WINED3D_FEATURE_LEVEL_9_2, CARD_NVIDIA_GEFORCEFX_5800}, + {WINED3D_FEATURE_LEVEL_9_3, CARD_NVIDIA_GEFORCE_6800}, + {WINED3D_FEATURE_LEVEL_10, CARD_NVIDIA_GEFORCE_8800GTX}, + {WINED3D_FEATURE_LEVEL_11, CARD_NVIDIA_GEFORCE_GTX470}, + {WINED3D_FEATURE_LEVEL_NONE}, + }, + card_fallback_amd[] = + { + {WINED3D_FEATURE_LEVEL_5, CARD_AMD_RAGE_128PRO}, + {WINED3D_FEATURE_LEVEL_7, CARD_AMD_RADEON_7200}, + {WINED3D_FEATURE_LEVEL_8, CARD_AMD_RADEON_8500}, + {WINED3D_FEATURE_LEVEL_9_1, CARD_AMD_RADEON_9500}, + {WINED3D_FEATURE_LEVEL_9_3, CARD_AMD_RADEON_X1600}, + {WINED3D_FEATURE_LEVEL_10, CARD_AMD_RADEON_HD2900}, + {WINED3D_FEATURE_LEVEL_11, CARD_AMD_RADEON_HD5600}, + {WINED3D_FEATURE_LEVEL_NONE}, + }, + card_fallback_intel[] = + { + {WINED3D_FEATURE_LEVEL_5, CARD_INTEL_845G}, + {WINED3D_FEATURE_LEVEL_8, CARD_INTEL_915G}, + {WINED3D_FEATURE_LEVEL_9_3, CARD_INTEL_945G}, + {WINED3D_FEATURE_LEVEL_10, CARD_INTEL_G45}, + {WINED3D_FEATURE_LEVEL_11, CARD_INTEL_IVBD}, + {WINED3D_FEATURE_LEVEL_NONE}, + }; + + static const struct + { + enum wined3d_pci_vendor vendor; + const struct wined3d_fallback_card *cards; + } + fallbacks[] = + { + {HW_VENDOR_AMD, card_fallback_amd}, + {HW_VENDOR_NVIDIA, card_fallback_nvidia}, + {HW_VENDOR_VMWARE, card_fallback_amd}, + {HW_VENDOR_INTEL, card_fallback_intel}, + }; + + const struct wined3d_fallback_card *cards; + enum wined3d_pci_device device_id; + unsigned int i; + + cards = NULL; + for (i = 0; i < ARRAY_SIZE(fallbacks); ++i) + { + if (*vendor == fallbacks[i].vendor) + cards = fallbacks[i].cards; + } + if (!cards) + { + *vendor = HW_VENDOR_NVIDIA; + cards = card_fallback_nvidia; + } + + device_id = cards->device_id; + for (i = 0; cards[i].feature_level; ++i) + { + if (feature_level >= cards[i].feature_level) + device_id = cards[i].device_id; + } + return device_id; +} + +struct wined3d_adapter * CDECL wined3d_get_adapter(const struct wined3d *wined3d, unsigned int idx) +{ + TRACE("wined3d %p, idx %u.\n", wined3d, idx); + + if (idx >= wined3d->adapter_count) + return NULL; + + return wined3d->adapters[idx]; +} + +UINT CDECL wined3d_get_adapter_count(const struct wined3d *wined3d) +{ + TRACE("wined3d %p, reporting %u adapters.\n", + wined3d, wined3d->adapter_count); + + return wined3d->adapter_count; +} + +struct wined3d_output * CDECL wined3d_adapter_get_output(const struct wined3d_adapter *adapter, + unsigned int idx) +{ + TRACE("adapter %p, idx %u.\n", adapter, idx); + + if (idx >= adapter->output_count) + return NULL; + + return &adapter->outputs[idx]; +} + +unsigned int CDECL wined3d_adapter_get_output_count(const struct wined3d_adapter *adapter) +{ + TRACE("adapter %p, reporting %u outputs.\n", adapter, adapter->output_count); + + return adapter->output_count; +} + +HRESULT CDECL wined3d_register_software_device(struct wined3d *wined3d, void *init_function) +{ + FIXME("wined3d %p, init_function %p stub!\n", wined3d, init_function); + + return WINED3D_OK; +} + +static BOOL CALLBACK enum_monitor_proc(HMONITOR monitor, HDC hdc, RECT *rect, LPARAM lparam) +{ + struct wined3d_output_desc *desc = (struct wined3d_output_desc *)lparam; + MONITORINFOEXW monitor_info; + + monitor_info.cbSize = sizeof(monitor_info); + if (GetMonitorInfoW(monitor, (MONITORINFO *)&monitor_info) && + !lstrcmpiW(desc->device_name, monitor_info.szDevice)) + { + desc->monitor = monitor; + desc->desktop_rect = monitor_info.rcMonitor; + desc->attached_to_desktop = TRUE; + return FALSE; + } + + return TRUE; +} + +HRESULT CDECL wined3d_output_get_desc(const struct wined3d_output *output, + struct wined3d_output_desc *desc) +{ + TRACE("output %p, desc %p.\n", output, desc); + + memset(desc, 0, sizeof(*desc)); + desc->ordinal = output->ordinal; + lstrcpyW(desc->device_name, output->device_name); + EnumDisplayMonitors(NULL, NULL, enum_monitor_proc, (LPARAM)desc); + return WINED3D_OK; +} + +/* FIXME: GetAdapterModeCount and EnumAdapterModes currently only returns modes + of the same bpp but different resolutions */ + +/* Note: dx9 supplies a format. Calls from d3d8 supply WINED3DFMT_UNKNOWN */ +unsigned int CDECL wined3d_output_get_mode_count(const struct wined3d_output *output, + enum wined3d_format_id format_id, enum wined3d_scanline_ordering scanline_ordering) +{ + const struct wined3d_adapter *adapter; + const struct wined3d_format *format; + unsigned int i = 0; + unsigned int j = 0; + UINT format_bits; + DEVMODEW mode; + + TRACE("output %p, format %s, scanline_ordering %#x.\n", + output, debug_d3dformat(format_id), scanline_ordering); + + adapter = output->adapter; + format = wined3d_get_format(adapter, format_id, WINED3D_BIND_RENDER_TARGET); + format_bits = format->byte_count * CHAR_BIT; + + memset(&mode, 0, sizeof(mode)); + mode.dmSize = sizeof(mode); + + while (EnumDisplaySettingsExW(output->device_name, j++, &mode, 0)) + { + if (mode.dmFields & DM_DISPLAYFLAGS) + { + if (scanline_ordering == WINED3D_SCANLINE_ORDERING_PROGRESSIVE + && (mode.u2.dmDisplayFlags & DM_INTERLACED)) + continue; + + if (scanline_ordering == WINED3D_SCANLINE_ORDERING_INTERLACED + && !(mode.u2.dmDisplayFlags & DM_INTERLACED)) + continue; + } + + if (format_id == WINED3DFMT_UNKNOWN) + { + /* This is for d3d8, do not enumerate P8 here. */ + if (mode.dmBitsPerPel == 32 || mode.dmBitsPerPel == 16) ++i; + } + else if (mode.dmBitsPerPel == format_bits) + { + ++i; + } + } + + TRACE("Returning %u matching modes (out of %u total) for output %p.\n", i, j, output); + + return i; +} + +/* Note: dx9 supplies a format. Calls from d3d8 supply WINED3DFMT_UNKNOWN */ +HRESULT CDECL wined3d_output_get_mode(const struct wined3d_output *output, + enum wined3d_format_id format_id, enum wined3d_scanline_ordering scanline_ordering, + unsigned int mode_idx, struct wined3d_display_mode *mode) +{ + const struct wined3d_adapter *adapter; + const struct wined3d_format *format; + UINT format_bits; + DEVMODEW m; + UINT i = 0; + int j = 0; + + TRACE("output %p, format %s, scanline_ordering %#x, mode_idx %u, mode %p.\n", + output, debug_d3dformat(format_id), scanline_ordering, mode_idx, mode); + + if (!mode) + return WINED3DERR_INVALIDCALL; + + adapter = output->adapter; + format = wined3d_get_format(adapter, format_id, WINED3D_BIND_RENDER_TARGET); + format_bits = format->byte_count * CHAR_BIT; + + memset(&m, 0, sizeof(m)); + m.dmSize = sizeof(m); + + while (i <= mode_idx) + { + if (!EnumDisplaySettingsExW(output->device_name, j++, &m, 0)) + { + WARN("Invalid mode_idx %u.\n", mode_idx); + return WINED3DERR_INVALIDCALL; + } + + if (m.dmFields & DM_DISPLAYFLAGS) + { + if (scanline_ordering == WINED3D_SCANLINE_ORDERING_PROGRESSIVE + && (m.u2.dmDisplayFlags & DM_INTERLACED)) + continue; + + if (scanline_ordering == WINED3D_SCANLINE_ORDERING_INTERLACED + && !(m.u2.dmDisplayFlags & DM_INTERLACED)) + continue; + } + + if (format_id == WINED3DFMT_UNKNOWN) + { + /* This is for d3d8, do not enumerate P8 here. */ + if (m.dmBitsPerPel == 32 || m.dmBitsPerPel == 16) ++i; + } + else if (m.dmBitsPerPel == format_bits) + { + ++i; + } + } + + mode->width = m.dmPelsWidth; + mode->height = m.dmPelsHeight; + mode->refresh_rate = DEFAULT_REFRESH_RATE; + if (m.dmFields & DM_DISPLAYFREQUENCY) + mode->refresh_rate = m.dmDisplayFrequency; + + if (format_id == WINED3DFMT_UNKNOWN) + mode->format_id = pixelformat_for_depth(m.dmBitsPerPel); + else + mode->format_id = format_id; + + if (!(m.dmFields & DM_DISPLAYFLAGS)) + mode->scanline_ordering = WINED3D_SCANLINE_ORDERING_UNKNOWN; + else if (m.u2.dmDisplayFlags & DM_INTERLACED) + mode->scanline_ordering = WINED3D_SCANLINE_ORDERING_INTERLACED; + else + mode->scanline_ordering = WINED3D_SCANLINE_ORDERING_PROGRESSIVE; + + TRACE("%ux%u@%u %u bpp, %s %#x.\n", mode->width, mode->height, mode->refresh_rate, + m.dmBitsPerPel, debug_d3dformat(mode->format_id), mode->scanline_ordering); + + return WINED3D_OK; +} + +HRESULT CDECL wined3d_output_find_closest_matching_mode(const struct wined3d_output *output, + struct wined3d_display_mode *mode) +{ + unsigned int i, j, mode_count, matching_mode_count, closest; + struct wined3d_display_mode **matching_modes; + struct wined3d_display_mode *modes; + HRESULT hr; + + TRACE("output %p, mode %p.\n", output, mode); + + if (!(mode_count = wined3d_output_get_mode_count(output, mode->format_id, + WINED3D_SCANLINE_ORDERING_UNKNOWN))) + { + WARN("Output has 0 matching modes.\n"); + return E_FAIL; + } + + if (!(modes = heap_calloc(mode_count, sizeof(*modes)))) + return E_OUTOFMEMORY; + if (!(matching_modes = heap_calloc(mode_count, sizeof(*matching_modes)))) + { + heap_free(modes); + return E_OUTOFMEMORY; + } + + for (i = 0; i < mode_count; ++i) + { + if (FAILED(hr = wined3d_output_get_mode(output, mode->format_id, + WINED3D_SCANLINE_ORDERING_UNKNOWN, i, &modes[i]))) + { + heap_free(matching_modes); + heap_free(modes); + return hr; + } + matching_modes[i] = &modes[i]; + } + + matching_mode_count = mode_count; + + if (mode->scanline_ordering != WINED3D_SCANLINE_ORDERING_UNKNOWN) + { + for (i = 0, j = 0; i < matching_mode_count; ++i) + { + if (matching_modes[i]->scanline_ordering == mode->scanline_ordering) + matching_modes[j++] = matching_modes[i]; + } + if (j > 0) + matching_mode_count = j; + } + + if (mode->refresh_rate) + { + for (i = 0, j = 0; i < matching_mode_count; ++i) + { + if (matching_modes[i]->refresh_rate == mode->refresh_rate) + matching_modes[j++] = matching_modes[i]; + } + if (j > 0) + matching_mode_count = j; + } + + if (!mode->width || !mode->height) + { + struct wined3d_display_mode current_mode; + if (FAILED(hr = wined3d_output_get_display_mode(output, ¤t_mode, NULL))) + { + heap_free(matching_modes); + heap_free(modes); + return hr; + } + mode->width = current_mode.width; + mode->height = current_mode.height; + } + + closest = ~0u; + for (i = 0, j = 0; i < matching_mode_count; ++i) + { + unsigned int d = abs(mode->width - matching_modes[i]->width) + + abs(mode->height - matching_modes[i]->height); + + if (closest > d) + { + closest = d; + j = i; + } + } + + *mode = *matching_modes[j]; + + heap_free(matching_modes); + heap_free(modes); + + TRACE("Returning %ux%u@%u %s %#x.\n", mode->width, mode->height, + mode->refresh_rate, debug_d3dformat(mode->format_id), + mode->scanline_ordering); + + return WINED3D_OK; +} + +struct wined3d_adapter * CDECL wined3d_output_get_adapter(const struct wined3d_output *output) +{ + TRACE("output %p.\n", output); + + return output->adapter; +} + +HRESULT CDECL wined3d_output_get_display_mode(const struct wined3d_output *output, + struct wined3d_display_mode *mode, enum wined3d_display_rotation *rotation) +{ + DEVMODEW m; + + TRACE("output %p, display_mode %p, rotation %p.\n", output, mode, rotation); + + if (!mode) + return WINED3DERR_INVALIDCALL; + + memset(&m, 0, sizeof(m)); + m.dmSize = sizeof(m); + + EnumDisplaySettingsExW(output->device_name, ENUM_CURRENT_SETTINGS, &m, 0); + mode->width = m.dmPelsWidth; + mode->height = m.dmPelsHeight; + mode->refresh_rate = DEFAULT_REFRESH_RATE; + if (m.dmFields & DM_DISPLAYFREQUENCY) + mode->refresh_rate = m.dmDisplayFrequency; + mode->format_id = pixelformat_for_depth(m.dmBitsPerPel); + + /* Lie about the format. X11 can't change the color depth, and some apps + * are pretty angry if they SetDisplayMode from 24 to 16 bpp and find out + * that GetDisplayMode still returns 24 bpp. This should probably be + * handled in winex11 instead. */ + if (output->screen_format && output->screen_format != mode->format_id) + { + WARN("Overriding format %s with stored format %s.\n", + debug_d3dformat(mode->format_id), + debug_d3dformat(output->screen_format)); + mode->format_id = output->screen_format; + } + + if (!(m.dmFields & DM_DISPLAYFLAGS)) + mode->scanline_ordering = WINED3D_SCANLINE_ORDERING_UNKNOWN; + else if (m.u2.dmDisplayFlags & DM_INTERLACED) + mode->scanline_ordering = WINED3D_SCANLINE_ORDERING_INTERLACED; + else + mode->scanline_ordering = WINED3D_SCANLINE_ORDERING_PROGRESSIVE; + + if (rotation) + { + switch (m.u1.s2.dmDisplayOrientation) + { + case DMDO_DEFAULT: + *rotation = WINED3D_DISPLAY_ROTATION_0; + break; + case DMDO_90: + *rotation = WINED3D_DISPLAY_ROTATION_90; + break; + case DMDO_180: + *rotation = WINED3D_DISPLAY_ROTATION_180; + break; + case DMDO_270: + *rotation = WINED3D_DISPLAY_ROTATION_270; + break; + default: + FIXME("Unhandled display rotation %#x.\n", m.u1.s2.dmDisplayOrientation); + *rotation = WINED3D_DISPLAY_ROTATION_UNSPECIFIED; + break; + } + } + + TRACE("Returning %ux%u@%u %s %#x.\n", mode->width, mode->height, + mode->refresh_rate, debug_d3dformat(mode->format_id), + mode->scanline_ordering); + return WINED3D_OK; +} + +static BOOL equal_display_mode(const DEVMODEW *mode1, const DEVMODEW *mode2) +{ + if (mode1->dmFields & mode2->dmFields & DM_PELSWIDTH + && mode1->dmPelsWidth != mode2->dmPelsWidth) + return FALSE; + + if (mode1->dmFields & mode2->dmFields & DM_PELSHEIGHT + && mode1->dmPelsHeight != mode2->dmPelsHeight) + return FALSE; + + if (mode1->dmFields & mode2->dmFields & DM_BITSPERPEL + && mode1->dmBitsPerPel != mode2->dmBitsPerPel) + return FALSE; + + if (mode1->dmFields & mode2->dmFields & DM_DISPLAYFLAGS + && mode1->u2.dmDisplayFlags != mode2->u2.dmDisplayFlags) + return FALSE; + + if (mode1->dmFields & mode2->dmFields & DM_DISPLAYFREQUENCY + && mode1->dmDisplayFrequency != mode2->dmDisplayFrequency) + return FALSE; + + if (mode1->dmFields & mode2->dmFields & DM_DISPLAYORIENTATION + && mode1->u1.s2.dmDisplayOrientation != mode2->u1.s2.dmDisplayOrientation) + return FALSE; + + if (mode1->dmFields & mode2->dmFields & DM_POSITION + && (mode1->u1.s2.dmPosition.x != mode2->u1.s2.dmPosition.x + || mode1->u1.s2.dmPosition.y != mode2->u1.s2.dmPosition.y)) + return FALSE; + + return TRUE; +} + +HRESULT CDECL wined3d_restore_display_modes(struct wined3d *wined3d) +{ + unsigned int adapter_idx, output_idx = 0; + DEVMODEW current_mode, registry_mode; + struct wined3d_adapter *adapter; + DISPLAY_DEVICEW display_device; + struct wined3d_output *output; + BOOL do_mode_change = FALSE; + LONG ret; + + TRACE("wined3d %p.\n", wined3d); + + memset(¤t_mode, 0, sizeof(current_mode)); + memset(®istry_mode, 0, sizeof(registry_mode)); + current_mode.dmSize = sizeof(current_mode); + registry_mode.dmSize = sizeof(registry_mode); + display_device.cb = sizeof(display_device); + while (EnumDisplayDevicesW(NULL, output_idx++, &display_device, 0)) + { + if (!EnumDisplaySettingsExW(display_device.DeviceName, ENUM_CURRENT_SETTINGS, ¤t_mode, 0)) + { + ERR("Failed to read the current display mode for %s.\n", + wine_dbgstr_w(display_device.DeviceName)); + return WINED3DERR_NOTAVAILABLE; + } + + if (!EnumDisplaySettingsExW(display_device.DeviceName, ENUM_REGISTRY_SETTINGS, ®istry_mode, 0)) + { + ERR("Failed to read the registry display mode for %s.\n", + wine_dbgstr_w(display_device.DeviceName)); + return WINED3DERR_NOTAVAILABLE; + } + + if (!equal_display_mode(¤t_mode, ®istry_mode)) + { + do_mode_change = TRUE; + break; + } + } + + if (do_mode_change) + { + ret = ChangeDisplaySettingsExW(NULL, NULL, NULL, 0, NULL); + if (ret != DISP_CHANGE_SUCCESSFUL) + { + ERR("Failed to restore all outputs to their registry display settings, error %d.\n", ret); + return WINED3DERR_NOTAVAILABLE; + } + } + else + { + TRACE("Skipping redundant mode setting call.\n"); + } + + for (adapter_idx = 0; adapter_idx < wined3d->adapter_count; ++adapter_idx) + { + adapter = wined3d->adapters[adapter_idx]; + for (output_idx = 0; output_idx < adapter->output_count; ++output_idx) + { + output = &adapter->outputs[output_idx]; + + if (!EnumDisplaySettingsExW(output->device_name, ENUM_CURRENT_SETTINGS, ¤t_mode, 0)) + { + ERR("Failed to read the current display mode for %s.\n", + wine_dbgstr_w(output->device_name)); + return WINED3DERR_NOTAVAILABLE; + } + + output->screen_format = pixelformat_for_depth(current_mode.dmBitsPerPel); + } + } + + return WINED3D_OK; +} + +HRESULT CDECL wined3d_output_set_display_mode(struct wined3d_output *output, + const struct wined3d_display_mode *mode) +{ + enum wined3d_format_id new_format_id; + const struct wined3d_format *format; + DEVMODEW new_mode, current_mode; + LONG ret; + + TRACE("output %p, mode %p.\n", output, mode); + TRACE("mode %ux%u@%u %s %#x.\n", mode->width, mode->height, mode->refresh_rate, + debug_d3dformat(mode->format_id), mode->scanline_ordering); + + memset(&new_mode, 0, sizeof(new_mode)); + new_mode.dmSize = sizeof(new_mode); + memset(¤t_mode, 0, sizeof(current_mode)); + current_mode.dmSize = sizeof(current_mode); + + format = wined3d_get_format(output->adapter, mode->format_id, WINED3D_BIND_RENDER_TARGET); + + new_mode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; + new_mode.dmBitsPerPel = format->byte_count * CHAR_BIT; + new_mode.dmPelsWidth = mode->width; + new_mode.dmPelsHeight = mode->height; + new_mode.dmDisplayFrequency = mode->refresh_rate; + if (mode->refresh_rate) + new_mode.dmFields |= DM_DISPLAYFREQUENCY; + if (mode->scanline_ordering != WINED3D_SCANLINE_ORDERING_UNKNOWN) + { + new_mode.dmFields |= DM_DISPLAYFLAGS; + if (mode->scanline_ordering == WINED3D_SCANLINE_ORDERING_INTERLACED) + new_mode.u2.dmDisplayFlags |= DM_INTERLACED; + } + new_format_id = mode->format_id; + + /* Only change the mode if necessary. */ + if (!EnumDisplaySettingsW(output->device_name, ENUM_CURRENT_SETTINGS, ¤t_mode)) + { + ERR("Failed to get current display mode.\n"); + } + else if (equal_display_mode(¤t_mode, &new_mode)) + { + TRACE("Skipping redundant mode setting call.\n"); + output->screen_format = new_format_id; + return WINED3D_OK; + } + + ret = ChangeDisplaySettingsExW(output->device_name, &new_mode, NULL, CDS_FULLSCREEN, NULL); + if (ret != DISP_CHANGE_SUCCESSFUL) + { + if (new_mode.dmFields & DM_DISPLAYFREQUENCY) + { + WARN("ChangeDisplaySettingsExW failed, trying without the refresh rate.\n"); + new_mode.dmFields &= ~DM_DISPLAYFREQUENCY; + new_mode.dmDisplayFrequency = 0; + ret = ChangeDisplaySettingsExW(output->device_name, &new_mode, NULL, CDS_FULLSCREEN, NULL); + } + if (ret != DISP_CHANGE_SUCCESSFUL) + return WINED3DERR_NOTAVAILABLE; + } + + /* Store the new values. */ + output->screen_format = new_format_id; + + return WINED3D_OK; +} + +HRESULT CDECL wined3d_adapter_get_identifier(const struct wined3d_adapter *adapter, + DWORD flags, struct wined3d_adapter_identifier *identifier) +{ + TRACE("adapter %p, flags %#x, identifier %p.\n", adapter, flags, identifier); + + wined3d_mutex_lock(); + + wined3d_copy_name(identifier->driver, adapter->driver_info.name, identifier->driver_size); + wined3d_copy_name(identifier->description, adapter->driver_info.description, identifier->description_size); + + identifier->driver_version.u.HighPart = adapter->driver_info.version_high; + identifier->driver_version.u.LowPart = adapter->driver_info.version_low; + identifier->vendor_id = adapter->driver_info.vendor; + identifier->device_id = adapter->driver_info.device; + identifier->subsystem_id = 0; + identifier->revision = 0; + identifier->device_identifier = IID_D3DDEVICE_D3DUID; + identifier->driver_uuid = adapter->driver_uuid; + identifier->device_uuid = adapter->device_uuid; + identifier->whql_level = (flags & WINED3DENUM_WHQL_LEVEL) ? 1 : 0; + identifier->adapter_luid = adapter->luid; + identifier->video_memory = min(~(SIZE_T)0, adapter->driver_info.vram_bytes); + identifier->shared_system_memory = min(~(SIZE_T)0, adapter->driver_info.sysmem_bytes); + + wined3d_mutex_unlock(); + + return WINED3D_OK; +} + +HRESULT CDECL wined3d_output_get_raster_status(const struct wined3d_output *output, + struct wined3d_raster_status *raster_status) +{ + LONGLONG freq_per_frame, freq_per_line; + LARGE_INTEGER counter, freq_per_sec; + struct wined3d_display_mode mode; + static UINT once; + + if (!once++) + FIXME("output %p, raster_status %p semi-stub!\n", output, raster_status); + else + WARN("output %p, raster_status %p semi-stub!\n", output, raster_status); + + /* Obtaining the raster status is a widely implemented but optional + * feature. When this method returns OK StarCraft 2 expects the + * raster_status->InVBlank value to actually change over time. + * And Endless Alice Crysis doesn't care even if this method fails. + * Thus this method returns OK and fakes raster_status by + * QueryPerformanceCounter. */ + + if (!QueryPerformanceCounter(&counter) || !QueryPerformanceFrequency(&freq_per_sec)) + return WINED3DERR_INVALIDCALL; + if (FAILED(wined3d_output_get_display_mode(output, &mode, NULL))) + return WINED3DERR_INVALIDCALL; + if (mode.refresh_rate == DEFAULT_REFRESH_RATE) + mode.refresh_rate = 60; + + freq_per_frame = freq_per_sec.QuadPart / mode.refresh_rate; + /* Assume 20 scan lines in the vertical blank. */ + freq_per_line = freq_per_frame / (mode.height + 20); + raster_status->scan_line = (counter.QuadPart % freq_per_frame) / freq_per_line; + if (raster_status->scan_line < mode.height) + raster_status->in_vblank = FALSE; + else + { + raster_status->scan_line = 0; + raster_status->in_vblank = TRUE; + } + + TRACE("Returning fake value, in_vblank %u, scan_line %u.\n", + raster_status->in_vblank, raster_status->scan_line); + + return WINED3D_OK; +} + +HRESULT CDECL wined3d_check_depth_stencil_match(const struct wined3d_adapter *adapter, + enum wined3d_device_type device_type, enum wined3d_format_id adapter_format_id, + enum wined3d_format_id render_target_format_id, enum wined3d_format_id depth_stencil_format_id) +{ + const struct wined3d_format *rt_format; + const struct wined3d_format *ds_format; + + TRACE("adapter %p, device_type %s, adapter_format %s, render_target_format %s, " + "depth_stencil_format %s.\n", + adapter, debug_d3ddevicetype(device_type), debug_d3dformat(adapter_format_id), + debug_d3dformat(render_target_format_id), debug_d3dformat(depth_stencil_format_id)); + + rt_format = wined3d_get_format(adapter, render_target_format_id, WINED3D_BIND_RENDER_TARGET); + ds_format = wined3d_get_format(adapter, depth_stencil_format_id, WINED3D_BIND_DEPTH_STENCIL); + + if (!(rt_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_RENDERTARGET)) + { + WARN("Format %s is not render target format.\n", debug_d3dformat(rt_format->id)); + return WINED3DERR_NOTAVAILABLE; + } + if (!(ds_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_DEPTH_STENCIL)) + { + WARN("Format %s is not depth/stencil format.\n", debug_d3dformat(ds_format->id)); + return WINED3DERR_NOTAVAILABLE; + } + + if (adapter->adapter_ops->adapter_check_format(adapter, NULL, rt_format, ds_format)) + { + TRACE("Formats match.\n"); + return WINED3D_OK; + } + + TRACE("Unsupported format pair: %s and %s.\n", + debug_d3dformat(render_target_format_id), + debug_d3dformat(depth_stencil_format_id)); + + return WINED3DERR_NOTAVAILABLE; +} + +HRESULT CDECL wined3d_check_device_multisample_type(const struct wined3d_adapter *adapter, + enum wined3d_device_type device_type, enum wined3d_format_id surface_format_id, BOOL windowed, + enum wined3d_multisample_type multisample_type, DWORD *quality_levels) +{ + const struct wined3d_format *format; + HRESULT hr = WINED3D_OK; + + TRACE("adapter %p, device_type %s, surface_format %s, " + "windowed %#x, multisample_type %#x, quality_levels %p.\n", + adapter, debug_d3ddevicetype(device_type), debug_d3dformat(surface_format_id), + windowed, multisample_type, quality_levels); + + if (surface_format_id == WINED3DFMT_UNKNOWN) + return WINED3DERR_INVALIDCALL; + if (multisample_type < WINED3D_MULTISAMPLE_NONE) + return WINED3DERR_INVALIDCALL; + if (multisample_type > WINED3D_MULTISAMPLE_16_SAMPLES) + { + FIXME("multisample_type %u not handled yet.\n", multisample_type); + return WINED3DERR_NOTAVAILABLE; + } + + format = wined3d_get_format(adapter, surface_format_id, 0); + + if (multisample_type && !(format->multisample_types & 1u << (multisample_type - 1))) + hr = WINED3DERR_NOTAVAILABLE; + + if (SUCCEEDED(hr) || (multisample_type == WINED3D_MULTISAMPLE_NON_MASKABLE && format->multisample_types)) + { + if (quality_levels) + { + if (multisample_type == WINED3D_MULTISAMPLE_NON_MASKABLE) + *quality_levels = wined3d_popcount(format->multisample_types); + else + *quality_levels = 1; + } + return WINED3D_OK; + } + + TRACE("Returning not supported.\n"); + return hr; +} + +static BOOL wined3d_check_depth_stencil_format(const struct wined3d_adapter *adapter, + const struct wined3d_format *adapter_format, const struct wined3d_format *ds_format, + enum wined3d_gl_resource_type gl_type) +{ + if (!ds_format->depth_size && !ds_format->stencil_size) + return FALSE; + + return adapter->adapter_ops->adapter_check_format(adapter, adapter_format, NULL, ds_format); +} + +static BOOL wined3d_check_surface_format(const struct wined3d_format *format) +{ + if ((format->flags[WINED3D_GL_RES_TYPE_TEX_2D] | format->flags[WINED3D_GL_RES_TYPE_RB]) & WINED3DFMT_FLAG_BLIT) + return TRUE; + + if ((format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & (WINED3DFMT_FLAG_EXTENSION | WINED3DFMT_FLAG_TEXTURE)) + == (WINED3DFMT_FLAG_EXTENSION | WINED3DFMT_FLAG_TEXTURE)) + return TRUE; + + return FALSE; +} + +/* OpenGL supports mipmapping on all formats. Wrapping is unsupported, but we + * have to report mipmapping so we cannot reject WRAPANDMIP. Tests show that + * Windows reports WRAPANDMIP on unfilterable surfaces as well, apparently to + * show that wrapping is supported. The lack of filtering will sort out the + * mipmapping capability anyway. + * + * For now lets report this on all formats, but in the future we may want to + * restrict it to some should applications need that. */ +HRESULT CDECL wined3d_check_device_format(const struct wined3d *wined3d, + const struct wined3d_adapter *adapter, enum wined3d_device_type device_type, + enum wined3d_format_id adapter_format_id, DWORD usage, unsigned int bind_flags, + enum wined3d_resource_type resource_type, enum wined3d_format_id check_format_id) +{ + const struct wined3d_format *adapter_format, *format; + enum wined3d_gl_resource_type gl_type, gl_type_end; + BOOL mipmap_gen_supported = TRUE; + unsigned int allowed_bind_flags; + DWORD format_flags = 0; + DWORD allowed_usage; + + TRACE("wined3d %p, adapter %p, device_type %s, adapter_format %s, usage %s, " + "bind_flags %s, resource_type %s, check_format %s.\n", + wined3d, adapter, debug_d3ddevicetype(device_type), debug_d3dformat(adapter_format_id), + debug_d3dusage(usage), wined3d_debug_bind_flags(bind_flags), + debug_d3dresourcetype(resource_type), debug_d3dformat(check_format_id)); + + adapter_format = wined3d_get_format(adapter, adapter_format_id, WINED3D_BIND_RENDER_TARGET); + format = wined3d_get_format(adapter, check_format_id, bind_flags); + + switch (resource_type) + { + case WINED3D_RTYPE_NONE: + allowed_usage = 0; + allowed_bind_flags = WINED3D_BIND_RENDER_TARGET + | WINED3D_BIND_DEPTH_STENCIL + | WINED3D_BIND_UNORDERED_ACCESS; + gl_type = WINED3D_GL_RES_TYPE_TEX_2D; + gl_type_end = WINED3D_GL_RES_TYPE_TEX_3D; + break; + + case WINED3D_RTYPE_TEXTURE_1D: + allowed_usage = WINED3DUSAGE_DYNAMIC + | WINED3DUSAGE_SOFTWAREPROCESSING + | WINED3DUSAGE_QUERY_FILTER + | WINED3DUSAGE_QUERY_GENMIPMAP + | WINED3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING + | WINED3DUSAGE_QUERY_SRGBREAD + | WINED3DUSAGE_QUERY_SRGBWRITE + | WINED3DUSAGE_QUERY_VERTEXTEXTURE + | WINED3DUSAGE_QUERY_WRAPANDMIP; + allowed_bind_flags = WINED3D_BIND_SHADER_RESOURCE + | WINED3D_BIND_UNORDERED_ACCESS; + gl_type = gl_type_end = WINED3D_GL_RES_TYPE_TEX_1D; + break; + + case WINED3D_RTYPE_TEXTURE_2D: + allowed_usage = WINED3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING; + if (bind_flags & WINED3D_BIND_RENDER_TARGET) + allowed_usage |= WINED3DUSAGE_QUERY_SRGBWRITE; + allowed_bind_flags = WINED3D_BIND_RENDER_TARGET + | WINED3D_BIND_DEPTH_STENCIL + | WINED3D_BIND_UNORDERED_ACCESS; + if (!(bind_flags & WINED3D_BIND_SHADER_RESOURCE)) + { + if (!wined3d_check_surface_format(format)) + { + TRACE("%s is not supported for plain surfaces.\n", debug_d3dformat(format->id)); + return WINED3DERR_NOTAVAILABLE; + } + + gl_type = gl_type_end = WINED3D_GL_RES_TYPE_RB; + break; + } + allowed_usage |= WINED3DUSAGE_DYNAMIC + | WINED3DUSAGE_LEGACY_CUBEMAP + | WINED3DUSAGE_SOFTWAREPROCESSING + | WINED3DUSAGE_QUERY_FILTER + | WINED3DUSAGE_QUERY_GENMIPMAP + | WINED3DUSAGE_QUERY_LEGACYBUMPMAP + | WINED3DUSAGE_QUERY_SRGBREAD + | WINED3DUSAGE_QUERY_SRGBWRITE + | WINED3DUSAGE_QUERY_VERTEXTEXTURE + | WINED3DUSAGE_QUERY_WRAPANDMIP; + allowed_bind_flags |= WINED3D_BIND_SHADER_RESOURCE; + gl_type = gl_type_end = WINED3D_GL_RES_TYPE_TEX_2D; + if (usage & WINED3DUSAGE_LEGACY_CUBEMAP) + { + allowed_usage &= ~WINED3DUSAGE_QUERY_LEGACYBUMPMAP; + allowed_bind_flags &= ~WINED3D_BIND_DEPTH_STENCIL; + gl_type = gl_type_end = WINED3D_GL_RES_TYPE_TEX_CUBE; + } + break; + + case WINED3D_RTYPE_TEXTURE_3D: + allowed_usage = WINED3DUSAGE_DYNAMIC + | WINED3DUSAGE_SOFTWAREPROCESSING + | WINED3DUSAGE_QUERY_FILTER + | WINED3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING + | WINED3DUSAGE_QUERY_SRGBREAD + | WINED3DUSAGE_QUERY_SRGBWRITE + | WINED3DUSAGE_QUERY_VERTEXTEXTURE + | WINED3DUSAGE_QUERY_WRAPANDMIP; + allowed_bind_flags = WINED3D_BIND_SHADER_RESOURCE + | WINED3D_BIND_UNORDERED_ACCESS; + gl_type = gl_type_end = WINED3D_GL_RES_TYPE_TEX_3D; + break; + + case WINED3D_RTYPE_BUFFER: + if (wined3d_format_is_typeless(format)) + { + TRACE("Requested WINED3D_RTYPE_BUFFER, but format %s is typeless.\n", debug_d3dformat(check_format_id)); + return WINED3DERR_NOTAVAILABLE; + } + + allowed_usage = WINED3DUSAGE_DYNAMIC; + allowed_bind_flags = WINED3D_BIND_SHADER_RESOURCE + | WINED3D_BIND_UNORDERED_ACCESS; + gl_type = gl_type_end = WINED3D_GL_RES_TYPE_BUFFER; + break; + + default: + FIXME("Unhandled resource type %s.\n", debug_d3dresourcetype(resource_type)); + return WINED3DERR_NOTAVAILABLE; + } + + if ((usage & allowed_usage) != usage) + { + TRACE("Requested usage %#x, but resource type %s only allows %#x.\n", + usage, debug_d3dresourcetype(resource_type), allowed_usage); + return WINED3DERR_NOTAVAILABLE; + } + + if ((bind_flags & allowed_bind_flags) != bind_flags) + { + TRACE("Requested bind flags %s, but resource type %s only allows %s.\n", + wined3d_debug_bind_flags(bind_flags), debug_d3dresourcetype(resource_type), + wined3d_debug_bind_flags(allowed_bind_flags)); + return WINED3DERR_NOTAVAILABLE; + } + + if (bind_flags & WINED3D_BIND_SHADER_RESOURCE) + format_flags |= WINED3DFMT_FLAG_TEXTURE; + if (bind_flags & WINED3D_BIND_RENDER_TARGET) + format_flags |= WINED3DFMT_FLAG_RENDERTARGET; + if (bind_flags & WINED3D_BIND_DEPTH_STENCIL) + format_flags |= WINED3DFMT_FLAG_DEPTH_STENCIL; + if (bind_flags & WINED3D_BIND_UNORDERED_ACCESS) + format_flags |= WINED3DFMT_FLAG_UNORDERED_ACCESS; + if (usage & WINED3DUSAGE_QUERY_FILTER) + format_flags |= WINED3DFMT_FLAG_FILTERING; + if (usage & WINED3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING) + format_flags |= WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING; + if (usage & WINED3DUSAGE_QUERY_SRGBREAD) + format_flags |= WINED3DFMT_FLAG_SRGB_READ; + if (usage & WINED3DUSAGE_QUERY_SRGBWRITE) + format_flags |= WINED3DFMT_FLAG_SRGB_WRITE; + if (usage & WINED3DUSAGE_QUERY_VERTEXTEXTURE) + format_flags |= WINED3DFMT_FLAG_VTF; + if (usage & WINED3DUSAGE_QUERY_LEGACYBUMPMAP) + format_flags |= WINED3DFMT_FLAG_BUMPMAP; + + if ((format_flags & WINED3DFMT_FLAG_TEXTURE) && (wined3d->flags & WINED3D_NO3D)) + { + TRACE("Requested texturing support, but wined3d was created with WINED3D_NO3D.\n"); + return WINED3DERR_NOTAVAILABLE; + } + + for (; gl_type <= gl_type_end; ++gl_type) + { + if ((bind_flags & WINED3D_BIND_RENDER_TARGET) + && !adapter->adapter_ops->adapter_check_format(adapter, adapter_format, format, NULL)) + { + TRACE("Requested WINED3D_BIND_RENDER_TARGET, but format %s is not supported for render targets.\n", + debug_d3dformat(check_format_id)); + return WINED3DERR_NOTAVAILABLE; + } + + /* 3D depth / stencil textures are never supported. */ + if (bind_flags == WINED3D_BIND_DEPTH_STENCIL && gl_type == WINED3D_GL_RES_TYPE_TEX_3D) + continue; + + if ((bind_flags & WINED3D_BIND_DEPTH_STENCIL) + && !wined3d_check_depth_stencil_format(adapter, adapter_format, format, gl_type)) + { + TRACE("Requested WINED3D_BIND_DEPTH_STENCIL, but format %s is not supported for depth/stencil buffers.\n", + debug_d3dformat(check_format_id)); + return WINED3DERR_NOTAVAILABLE; + } + + if ((bind_flags & WINED3D_BIND_UNORDERED_ACCESS) && wined3d_format_is_typeless(format)) + { + TRACE("Requested WINED3D_BIND_UNORDERED_ACCESS, but format %s is typeless.\n", + debug_d3dformat(check_format_id)); + return WINED3DERR_NOTAVAILABLE; + } + + if ((format->flags[gl_type] & format_flags) != format_flags) + { + TRACE("Requested format flags %#x, but format %s only has %#x.\n", + format_flags, debug_d3dformat(check_format_id), format->flags[gl_type]); + return WINED3DERR_NOTAVAILABLE; + } + + if (!(format->flags[gl_type] & WINED3DFMT_FLAG_GEN_MIPMAP)) + mipmap_gen_supported = FALSE; + } + + if ((usage & WINED3DUSAGE_QUERY_GENMIPMAP) && !mipmap_gen_supported) + { + TRACE("No WINED3DUSAGE_AUTOGENMIPMAP support, returning WINED3DOK_NOAUTOGEN.\n"); + return WINED3DOK_NOMIPGEN; + } + + return WINED3D_OK; +} + +unsigned int CDECL wined3d_calculate_format_pitch(const struct wined3d_adapter *adapter, + enum wined3d_format_id format_id, unsigned int width) +{ + unsigned int row_pitch, slice_pitch; + + TRACE("adapter %p, format_id %s, width %u.\n", adapter, debug_d3dformat(format_id), width); + + wined3d_format_calculate_pitch(wined3d_get_format(adapter, format_id, 0), + 1, width, 1, &row_pitch, &slice_pitch); + + return row_pitch; +} + +HRESULT CDECL wined3d_check_device_format_conversion(const struct wined3d_output *output, + enum wined3d_device_type device_type, enum wined3d_format_id src_format, + enum wined3d_format_id dst_format) +{ + FIXME("output %p, device_type %s, src_format %s, dst_format %s stub!\n", + output, debug_d3ddevicetype(device_type), debug_d3dformat(src_format), + debug_d3dformat(dst_format)); + + return WINED3D_OK; +} + +HRESULT CDECL wined3d_check_device_type(const struct wined3d *wined3d, + const struct wined3d_output *output, enum wined3d_device_type device_type, + enum wined3d_format_id display_format, enum wined3d_format_id backbuffer_format, + BOOL windowed) +{ + BOOL present_conversion = wined3d->flags & WINED3D_PRESENT_CONVERSION; + + TRACE("wined3d %p, output %p, device_type %s, display_format %s, backbuffer_format %s, windowed %#x.\n", + wined3d, output, debug_d3ddevicetype(device_type), debug_d3dformat(display_format), + debug_d3dformat(backbuffer_format), windowed); + + /* The task of this function is to check whether a certain display / backbuffer format + * combination is available on the given output. In fullscreen mode microsoft specified + * that the display format shouldn't provide alpha and that ignoring alpha the backbuffer + * and display format should match exactly. + * In windowed mode format conversion can occur and this depends on the driver. */ + + /* There are only 4 display formats. */ + if (!(display_format == WINED3DFMT_B5G6R5_UNORM + || display_format == WINED3DFMT_B5G5R5X1_UNORM + || display_format == WINED3DFMT_B8G8R8X8_UNORM + || display_format == WINED3DFMT_B10G10R10A2_UNORM)) + { + TRACE("Format %s is not supported as display format.\n", debug_d3dformat(display_format)); + return WINED3DERR_NOTAVAILABLE; + } + + if (!windowed) + { + /* If the requested display format is not available, don't continue. */ + if (!wined3d_output_get_mode_count(output, display_format, + WINED3D_SCANLINE_ORDERING_UNKNOWN)) + { + TRACE("No available modes for display format %s.\n", debug_d3dformat(display_format)); + return WINED3DERR_NOTAVAILABLE; + } + + present_conversion = FALSE; + } + else if (display_format == WINED3DFMT_B10G10R10A2_UNORM) + { + /* WINED3DFMT_B10G10R10A2_UNORM is only allowed in fullscreen mode. */ + TRACE("Unsupported format combination %s / %s in windowed mode.\n", + debug_d3dformat(display_format), debug_d3dformat(backbuffer_format)); + return WINED3DERR_NOTAVAILABLE; + } + + if (present_conversion) + { + /* Use the display format as back buffer format if the latter is + * WINED3DFMT_UNKNOWN. */ + if (backbuffer_format == WINED3DFMT_UNKNOWN) + backbuffer_format = display_format; + + if (FAILED(wined3d_check_device_format_conversion(output, device_type, backbuffer_format, + display_format))) + { + TRACE("Format conversion from %s to %s not supported.\n", + debug_d3dformat(backbuffer_format), debug_d3dformat(display_format)); + return WINED3DERR_NOTAVAILABLE; + } + } + else + { + /* When format conversion from the back buffer format to the display + * format is not allowed, only a limited number of combinations are + * valid. */ + + if (display_format == WINED3DFMT_B5G6R5_UNORM && backbuffer_format != WINED3DFMT_B5G6R5_UNORM) + { + TRACE("Unsupported format combination %s / %s.\n", + debug_d3dformat(display_format), debug_d3dformat(backbuffer_format)); + return WINED3DERR_NOTAVAILABLE; + } + + if (display_format == WINED3DFMT_B5G5R5X1_UNORM + && !(backbuffer_format == WINED3DFMT_B5G5R5X1_UNORM || backbuffer_format == WINED3DFMT_B5G5R5A1_UNORM)) + { + TRACE("Unsupported format combination %s / %s.\n", + debug_d3dformat(display_format), debug_d3dformat(backbuffer_format)); + return WINED3DERR_NOTAVAILABLE; + } + + if (display_format == WINED3DFMT_B8G8R8X8_UNORM + && !(backbuffer_format == WINED3DFMT_B8G8R8X8_UNORM || backbuffer_format == WINED3DFMT_B8G8R8A8_UNORM)) + { + TRACE("Unsupported format combination %s / %s.\n", + debug_d3dformat(display_format), debug_d3dformat(backbuffer_format)); + return WINED3DERR_NOTAVAILABLE; + } + + if (display_format == WINED3DFMT_B10G10R10A2_UNORM + && backbuffer_format != WINED3DFMT_B10G10R10A2_UNORM) + { + TRACE("Unsupported format combination %s / %s.\n", + debug_d3dformat(display_format), debug_d3dformat(backbuffer_format)); + return WINED3DERR_NOTAVAILABLE; + } + } + + /* Validate that the back buffer format is usable for render targets. */ + if (FAILED(wined3d_check_device_format(wined3d, output->adapter, device_type, + display_format, 0, WINED3D_BIND_RENDER_TARGET, WINED3D_RTYPE_TEXTURE_2D, + backbuffer_format))) + { + TRACE("Format %s not allowed for render targets.\n", debug_d3dformat(backbuffer_format)); + return WINED3DERR_NOTAVAILABLE; + } + + return WINED3D_OK; +} + +HRESULT CDECL wined3d_get_device_caps(const struct wined3d_adapter *adapter, + enum wined3d_device_type device_type, struct wined3d_caps *caps) +{ + const struct wined3d_d3d_info *d3d_info; + struct wined3d_vertex_caps vertex_caps; + DWORD ckey_caps, blit_caps, fx_caps; + struct fragment_caps fragment_caps; + struct shader_caps shader_caps; + + TRACE("adapter %p, device_type %s, caps %p.\n", + adapter, debug_d3ddevicetype(device_type), caps); + + d3d_info = &adapter->d3d_info; + + caps->DeviceType = (device_type == WINED3D_DEVICE_TYPE_HAL) ? WINED3D_DEVICE_TYPE_HAL : WINED3D_DEVICE_TYPE_REF; + + caps->Caps = 0; + caps->Caps2 = WINED3DCAPS2_CANRENDERWINDOWED | + WINED3DCAPS2_FULLSCREENGAMMA | + WINED3DCAPS2_DYNAMICTEXTURES; + + caps->Caps3 = WINED3DCAPS3_ALPHA_FULLSCREEN_FLIP_OR_DISCARD | + WINED3DCAPS3_COPY_TO_VIDMEM | + WINED3DCAPS3_COPY_TO_SYSTEMMEM; + + caps->CursorCaps = WINED3DCURSORCAPS_COLOR | + WINED3DCURSORCAPS_LOWRES; + + caps->DevCaps = WINED3DDEVCAPS_FLOATTLVERTEX | + WINED3DDEVCAPS_EXECUTESYSTEMMEMORY | + WINED3DDEVCAPS_TLVERTEXSYSTEMMEMORY| + WINED3DDEVCAPS_TLVERTEXVIDEOMEMORY | + WINED3DDEVCAPS_DRAWPRIMTLVERTEX | + WINED3DDEVCAPS_HWTRANSFORMANDLIGHT | + WINED3DDEVCAPS_EXECUTEVIDEOMEMORY | + WINED3DDEVCAPS_PUREDEVICE | + WINED3DDEVCAPS_HWRASTERIZATION | + WINED3DDEVCAPS_TEXTUREVIDEOMEMORY | + WINED3DDEVCAPS_TEXTURESYSTEMMEMORY | + WINED3DDEVCAPS_CANRENDERAFTERFLIP | + WINED3DDEVCAPS_DRAWPRIMITIVES2 | + WINED3DDEVCAPS_DRAWPRIMITIVES2EX; + + caps->PrimitiveMiscCaps = WINED3DPMISCCAPS_CULLNONE | + WINED3DPMISCCAPS_CULLCCW | + WINED3DPMISCCAPS_CULLCW | + WINED3DPMISCCAPS_COLORWRITEENABLE | + WINED3DPMISCCAPS_CLIPTLVERTS | + WINED3DPMISCCAPS_CLIPPLANESCALEDPOINTS | + WINED3DPMISCCAPS_MASKZ | + WINED3DPMISCCAPS_MRTPOSTPIXELSHADERBLENDING; + /* TODO: + WINED3DPMISCCAPS_NULLREFERENCE + WINED3DPMISCCAPS_FOGANDSPECULARALPHA + WINED3DPMISCCAPS_FOGVERTEXCLAMPED */ + + caps->RasterCaps = WINED3DPRASTERCAPS_DITHER | + WINED3DPRASTERCAPS_PAT | + WINED3DPRASTERCAPS_WFOG | + WINED3DPRASTERCAPS_ZFOG | + WINED3DPRASTERCAPS_FOGVERTEX | + WINED3DPRASTERCAPS_FOGTABLE | + WINED3DPRASTERCAPS_STIPPLE | + WINED3DPRASTERCAPS_SUBPIXEL | + WINED3DPRASTERCAPS_ZTEST | + WINED3DPRASTERCAPS_SCISSORTEST | + WINED3DPRASTERCAPS_SLOPESCALEDEPTHBIAS | + WINED3DPRASTERCAPS_DEPTHBIAS; + + caps->ZCmpCaps = WINED3DPCMPCAPS_ALWAYS | + WINED3DPCMPCAPS_EQUAL | + WINED3DPCMPCAPS_GREATER | + WINED3DPCMPCAPS_GREATEREQUAL | + WINED3DPCMPCAPS_LESS | + WINED3DPCMPCAPS_LESSEQUAL | + WINED3DPCMPCAPS_NEVER | + WINED3DPCMPCAPS_NOTEQUAL; + + /* WINED3DPBLENDCAPS_BOTHINVSRCALPHA and WINED3DPBLENDCAPS_BOTHSRCALPHA + * are legacy settings for srcblend only. */ + caps->SrcBlendCaps = WINED3DPBLENDCAPS_BOTHINVSRCALPHA | + WINED3DPBLENDCAPS_BOTHSRCALPHA | + WINED3DPBLENDCAPS_DESTALPHA | + WINED3DPBLENDCAPS_DESTCOLOR | + WINED3DPBLENDCAPS_INVDESTALPHA | + WINED3DPBLENDCAPS_INVDESTCOLOR | + WINED3DPBLENDCAPS_INVSRCALPHA | + WINED3DPBLENDCAPS_INVSRCCOLOR | + WINED3DPBLENDCAPS_ONE | + WINED3DPBLENDCAPS_SRCALPHA | + WINED3DPBLENDCAPS_SRCALPHASAT | + WINED3DPBLENDCAPS_SRCCOLOR | + WINED3DPBLENDCAPS_ZERO; + + caps->DestBlendCaps = WINED3DPBLENDCAPS_DESTALPHA | + WINED3DPBLENDCAPS_DESTCOLOR | + WINED3DPBLENDCAPS_INVDESTALPHA | + WINED3DPBLENDCAPS_INVDESTCOLOR | + WINED3DPBLENDCAPS_INVSRCALPHA | + WINED3DPBLENDCAPS_INVSRCCOLOR | + WINED3DPBLENDCAPS_ONE | + WINED3DPBLENDCAPS_SRCALPHA | + WINED3DPBLENDCAPS_SRCCOLOR | + WINED3DPBLENDCAPS_ZERO; + + caps->AlphaCmpCaps = WINED3DPCMPCAPS_ALWAYS | + WINED3DPCMPCAPS_EQUAL | + WINED3DPCMPCAPS_GREATER | + WINED3DPCMPCAPS_GREATEREQUAL | + WINED3DPCMPCAPS_LESS | + WINED3DPCMPCAPS_LESSEQUAL | + WINED3DPCMPCAPS_NEVER | + WINED3DPCMPCAPS_NOTEQUAL; + + caps->ShadeCaps = WINED3DPSHADECAPS_SPECULARGOURAUDRGB | + WINED3DPSHADECAPS_COLORGOURAUDRGB | + WINED3DPSHADECAPS_ALPHAFLATBLEND | + WINED3DPSHADECAPS_ALPHAGOURAUDBLEND | + WINED3DPSHADECAPS_COLORFLATRGB | + WINED3DPSHADECAPS_FOGFLAT | + WINED3DPSHADECAPS_FOGGOURAUD | + WINED3DPSHADECAPS_SPECULARFLATRGB; + + caps->TextureCaps = WINED3DPTEXTURECAPS_ALPHA | + WINED3DPTEXTURECAPS_ALPHAPALETTE | + WINED3DPTEXTURECAPS_TRANSPARENCY | + WINED3DPTEXTURECAPS_BORDER | + WINED3DPTEXTURECAPS_MIPMAP | + WINED3DPTEXTURECAPS_PROJECTED | + WINED3DPTEXTURECAPS_PERSPECTIVE; + + if (!d3d_info->texture_npot) + { + caps->TextureCaps |= WINED3DPTEXTURECAPS_POW2; + if (d3d_info->texture_npot_conditional) + caps->TextureCaps |= WINED3DPTEXTURECAPS_NONPOW2CONDITIONAL; + } + + caps->TextureFilterCaps = WINED3DPTFILTERCAPS_MAGFLINEAR | + WINED3DPTFILTERCAPS_MAGFPOINT | + WINED3DPTFILTERCAPS_MINFLINEAR | + WINED3DPTFILTERCAPS_MINFPOINT | + WINED3DPTFILTERCAPS_MIPFLINEAR | + WINED3DPTFILTERCAPS_MIPFPOINT | + WINED3DPTFILTERCAPS_LINEAR | + WINED3DPTFILTERCAPS_LINEARMIPLINEAR | + WINED3DPTFILTERCAPS_LINEARMIPNEAREST | + WINED3DPTFILTERCAPS_MIPLINEAR | + WINED3DPTFILTERCAPS_MIPNEAREST | + WINED3DPTFILTERCAPS_NEAREST; + + caps->CubeTextureFilterCaps = 0; + caps->VolumeTextureFilterCaps = 0; + + caps->TextureAddressCaps = WINED3DPTADDRESSCAPS_INDEPENDENTUV | + WINED3DPTADDRESSCAPS_CLAMP | + WINED3DPTADDRESSCAPS_WRAP; + + caps->VolumeTextureAddressCaps = 0; + + caps->LineCaps = WINED3DLINECAPS_TEXTURE | + WINED3DLINECAPS_ZTEST | + WINED3DLINECAPS_BLEND | + WINED3DLINECAPS_ALPHACMP | + WINED3DLINECAPS_FOG; + /* WINED3DLINECAPS_ANTIALIAS is not supported on Windows, and dx and gl seem to have a different + * idea how generating the smoothing alpha values works; the result is different + */ + + caps->MaxTextureWidth = d3d_info->limits.texture_size; + caps->MaxTextureHeight = d3d_info->limits.texture_size; + + caps->MaxVolumeExtent = 0; + + caps->MaxTextureRepeat = 32768; + caps->MaxTextureAspectRatio = d3d_info->limits.texture_size; + caps->MaxVertexW = 1e10f; + + caps->GuardBandLeft = -32768.0f; + caps->GuardBandTop = -32768.0f; + caps->GuardBandRight = 32768.0f; + caps->GuardBandBottom = 32768.0f; + + caps->ExtentsAdjust = 0.0f; + + caps->StencilCaps = WINED3DSTENCILCAPS_DECRSAT | + WINED3DSTENCILCAPS_INCRSAT | + WINED3DSTENCILCAPS_INVERT | + WINED3DSTENCILCAPS_KEEP | + WINED3DSTENCILCAPS_REPLACE | + WINED3DSTENCILCAPS_ZERO; + + caps->MaxAnisotropy = 0; + caps->MaxPointSize = d3d_info->limits.pointsize_max; + + caps->MaxPrimitiveCount = 0x555555; /* Taken from an AMD Radeon HD 5700 (Evergreen) GPU. */ + caps->MaxVertexIndex = 0xffffff; /* Taken from an AMD Radeon HD 5700 (Evergreen) GPU. */ + caps->MaxStreams = WINED3D_MAX_STREAMS; + caps->MaxStreamStride = 1024; + + /* d3d9.dll sets D3DDEVCAPS2_CAN_STRETCHRECT_FROM_TEXTURES here because StretchRects is implemented in d3d9 */ + caps->DevCaps2 = WINED3DDEVCAPS2_STREAMOFFSET | + WINED3DDEVCAPS2_VERTEXELEMENTSCANSHARESTREAMOFFSET; + caps->MaxNpatchTessellationLevel = 0; + + caps->NumSimultaneousRTs = d3d_info->limits.max_rt_count; + + caps->StretchRectFilterCaps = WINED3DPTFILTERCAPS_MINFPOINT | + WINED3DPTFILTERCAPS_MAGFPOINT | + WINED3DPTFILTERCAPS_MINFLINEAR | + WINED3DPTFILTERCAPS_MAGFLINEAR; + caps->VertexTextureFilterCaps = 0; + + adapter->shader_backend->shader_get_caps(adapter, &shader_caps); + adapter->fragment_pipe->get_caps(adapter, &fragment_caps); + adapter->vertex_pipe->vp_get_caps(adapter, &vertex_caps); + + /* Add shader misc caps. Only some of them belong to the shader parts of the pipeline */ + caps->PrimitiveMiscCaps |= fragment_caps.PrimitiveMiscCaps; + + caps->VertexShaderVersion = shader_caps.vs_version; + caps->MaxVertexShaderConst = shader_caps.vs_uniform_count; + + caps->PixelShaderVersion = shader_caps.ps_version; + caps->PixelShader1xMaxValue = shader_caps.ps_1x_max_value; + + caps->TextureOpCaps = fragment_caps.TextureOpCaps; + caps->MaxTextureBlendStages = fragment_caps.MaxTextureBlendStages; + caps->MaxSimultaneousTextures = fragment_caps.MaxSimultaneousTextures; + + caps->MaxUserClipPlanes = vertex_caps.max_user_clip_planes; + caps->MaxActiveLights = vertex_caps.max_active_lights; + caps->MaxVertexBlendMatrices = vertex_caps.max_vertex_blend_matrices; + caps->MaxVertexBlendMatrixIndex = vertex_caps.max_vertex_blend_matrix_index; + caps->VertexProcessingCaps = vertex_caps.vertex_processing_caps; + caps->FVFCaps = vertex_caps.fvf_caps; + caps->RasterCaps |= vertex_caps.raster_caps; + + /* The following caps are shader specific, but they are things we cannot detect, or which + * are the same among all shader models. So to avoid code duplication set the shader version + * specific, but otherwise constant caps here + */ + if (caps->VertexShaderVersion >= 3) + { + /* Where possible set the caps based on OpenGL extensions and if they + * aren't set (in case of software rendering) use the VS 3.0 from + * MSDN or else if there's OpenGL spec use a hardcoded value minimum + * VS3.0 value. */ + caps->VS20Caps.caps = WINED3DVS20CAPS_PREDICATION; + /* VS 3.0 requires MAX_DYNAMICFLOWCONTROLDEPTH (24) */ + caps->VS20Caps.dynamic_flow_control_depth = WINED3DVS20_MAX_DYNAMICFLOWCONTROLDEPTH; + caps->VS20Caps.temp_count = 32; + /* level of nesting in loops / if-statements; VS 3.0 requires MAX (4) */ + caps->VS20Caps.static_flow_control_depth = WINED3DVS20_MAX_STATICFLOWCONTROLDEPTH; + + caps->MaxVShaderInstructionsExecuted = 65535; /* VS 3.0 needs at least 65535, some cards even use 2^32-1 */ + caps->MaxVertexShader30InstructionSlots = WINED3DMIN30SHADERINSTRUCTIONS; + caps->VertexTextureFilterCaps = WINED3DPTFILTERCAPS_MINFPOINT | WINED3DPTFILTERCAPS_MAGFPOINT; + } + else if (caps->VertexShaderVersion == 2) + { + caps->VS20Caps.caps = 0; + caps->VS20Caps.dynamic_flow_control_depth = WINED3DVS20_MIN_DYNAMICFLOWCONTROLDEPTH; + caps->VS20Caps.temp_count = WINED3DVS20_MIN_NUMTEMPS; + caps->VS20Caps.static_flow_control_depth = 1; + + caps->MaxVShaderInstructionsExecuted = 65535; + caps->MaxVertexShader30InstructionSlots = 0; + } + else /* VS 1.x */ + { + caps->VS20Caps.caps = 0; + caps->VS20Caps.dynamic_flow_control_depth = 0; + caps->VS20Caps.temp_count = 0; + caps->VS20Caps.static_flow_control_depth = 0; + + caps->MaxVShaderInstructionsExecuted = 0; + caps->MaxVertexShader30InstructionSlots = 0; + } + + if (caps->PixelShaderVersion >= 3) + { + /* Where possible set the caps based on OpenGL extensions and if they + * aren't set (in case of software rendering) use the PS 3.0 from + * MSDN or else if there's OpenGL spec use a hardcoded value minimum + * PS 3.0 value. */ + + /* Caps is more or less undocumented on MSDN but it appears to be + * used for PS20Caps based on results from R9600/FX5900/Geforce6800 + * cards from Windows */ + caps->PS20Caps.caps = WINED3DPS20CAPS_ARBITRARYSWIZZLE | + WINED3DPS20CAPS_GRADIENTINSTRUCTIONS | + WINED3DPS20CAPS_PREDICATION | + WINED3DPS20CAPS_NODEPENDENTREADLIMIT | + WINED3DPS20CAPS_NOTEXINSTRUCTIONLIMIT; + /* PS 3.0 requires MAX_DYNAMICFLOWCONTROLDEPTH (24) */ + caps->PS20Caps.dynamic_flow_control_depth = WINED3DPS20_MAX_DYNAMICFLOWCONTROLDEPTH; + caps->PS20Caps.temp_count = 32; + /* PS 3.0 requires MAX_STATICFLOWCONTROLDEPTH (4) */ + caps->PS20Caps.static_flow_control_depth = WINED3DPS20_MAX_STATICFLOWCONTROLDEPTH; + /* PS 3.0 requires MAX_NUMINSTRUCTIONSLOTS (512) */ + caps->PS20Caps.instruction_slot_count = WINED3DPS20_MAX_NUMINSTRUCTIONSLOTS; + + caps->MaxPShaderInstructionsExecuted = 65535; + caps->MaxPixelShader30InstructionSlots = WINED3DMIN30SHADERINSTRUCTIONS; + } + else if (caps->PixelShaderVersion == 2) + { + /* Below we assume PS2.0 specs, not extended 2.0a(GeforceFX)/2.0b(Radeon R3xx) ones */ + caps->PS20Caps.caps = 0; + caps->PS20Caps.dynamic_flow_control_depth = 0; /* WINED3DVS20_MIN_DYNAMICFLOWCONTROLDEPTH = 0 */ + caps->PS20Caps.temp_count = WINED3DPS20_MIN_NUMTEMPS; + caps->PS20Caps.static_flow_control_depth = WINED3DPS20_MIN_STATICFLOWCONTROLDEPTH; /* Minimum: 1 */ + /* Minimum number (64 ALU + 32 Texture), a GeforceFX uses 512 */ + caps->PS20Caps.instruction_slot_count = WINED3DPS20_MIN_NUMINSTRUCTIONSLOTS; + + caps->MaxPShaderInstructionsExecuted = 512; /* Minimum value, a GeforceFX uses 1024 */ + caps->MaxPixelShader30InstructionSlots = 0; + } + else /* PS 1.x */ + { + caps->PS20Caps.caps = 0; + caps->PS20Caps.dynamic_flow_control_depth = 0; + caps->PS20Caps.temp_count = 0; + caps->PS20Caps.static_flow_control_depth = 0; + caps->PS20Caps.instruction_slot_count = 0; + + caps->MaxPShaderInstructionsExecuted = 0; + caps->MaxPixelShader30InstructionSlots = 0; + } + + if (caps->VertexShaderVersion >= 2) + { + /* OpenGL supports all the formats below, perhaps not always + * without conversion, but it supports them. + * Further GLSL doesn't seem to have an official unsigned type so + * don't advertise it yet as I'm not sure how we handle it. + * We might need to add some clamping in the shader engine to + * support it. + * TODO: WINED3DDTCAPS_USHORT2N, WINED3DDTCAPS_USHORT4N, WINED3DDTCAPS_UDEC3, WINED3DDTCAPS_DEC3N */ + caps->DeclTypes = WINED3DDTCAPS_UBYTE4 | + WINED3DDTCAPS_UBYTE4N | + WINED3DDTCAPS_SHORT2N | + WINED3DDTCAPS_SHORT4N; + } + else + { + caps->DeclTypes = 0; + } + + /* Set DirectDraw helper Caps */ + ckey_caps = WINEDDCKEYCAPS_DESTBLT | + WINEDDCKEYCAPS_SRCBLT; + fx_caps = WINEDDFXCAPS_BLTALPHA | + WINEDDFXCAPS_BLTMIRRORLEFTRIGHT | + WINEDDFXCAPS_BLTMIRRORUPDOWN | + WINEDDFXCAPS_BLTROTATION90 | + WINEDDFXCAPS_BLTSHRINKX | + WINEDDFXCAPS_BLTSHRINKXN | + WINEDDFXCAPS_BLTSHRINKY | + WINEDDFXCAPS_BLTSHRINKYN | + WINEDDFXCAPS_BLTSTRETCHX | + WINEDDFXCAPS_BLTSTRETCHXN | + WINEDDFXCAPS_BLTSTRETCHY | + WINEDDFXCAPS_BLTSTRETCHYN; + blit_caps = WINEDDCAPS_BLT | + WINEDDCAPS_BLTCOLORFILL | + WINEDDCAPS_BLTDEPTHFILL | + WINEDDCAPS_BLTSTRETCH | + WINEDDCAPS_CANBLTSYSMEM | + WINEDDCAPS_CANCLIP | + WINEDDCAPS_CANCLIPSTRETCHED | + WINEDDCAPS_COLORKEY | + WINEDDCAPS_COLORKEYHWASSIST; + + /* Fill the ddraw caps structure */ + caps->ddraw_caps.caps = WINEDDCAPS_GDI | + WINEDDCAPS_PALETTE | + blit_caps; + caps->ddraw_caps.caps2 = WINEDDCAPS2_CERTIFIED | + WINEDDCAPS2_NOPAGELOCKREQUIRED | + WINEDDCAPS2_PRIMARYGAMMA | + WINEDDCAPS2_WIDESURFACES | + WINEDDCAPS2_CANRENDERWINDOWED; + caps->ddraw_caps.color_key_caps = ckey_caps; + caps->ddraw_caps.fx_caps = fx_caps; + caps->ddraw_caps.svb_caps = blit_caps; + caps->ddraw_caps.svb_color_key_caps = ckey_caps; + caps->ddraw_caps.svb_fx_caps = fx_caps; + caps->ddraw_caps.vsb_caps = blit_caps; + caps->ddraw_caps.vsb_color_key_caps = ckey_caps; + caps->ddraw_caps.vsb_fx_caps = fx_caps; + caps->ddraw_caps.ssb_caps = blit_caps; + caps->ddraw_caps.ssb_color_key_caps = ckey_caps; + caps->ddraw_caps.ssb_fx_caps = fx_caps; + + caps->ddraw_caps.dds_caps = WINEDDSCAPS_FLIP + | WINEDDSCAPS_OFFSCREENPLAIN + | WINEDDSCAPS_PALETTE + | WINEDDSCAPS_PRIMARYSURFACE + | WINEDDSCAPS_TEXTURE + | WINEDDSCAPS_ZBUFFER + | WINEDDSCAPS_MIPMAP; + + caps->shader_double_precision = d3d_info->shader_double_precision; + caps->viewport_array_index_any_shader = d3d_info->viewport_array_index_any_shader; + + caps->max_feature_level = d3d_info->feature_level; + + adapter->adapter_ops->adapter_get_wined3d_caps(adapter, caps); + + return WINED3D_OK; +} + +HRESULT CDECL wined3d_device_create(struct wined3d *wined3d, struct wined3d_adapter *adapter, + enum wined3d_device_type device_type, HWND focus_window, DWORD flags, BYTE surface_alignment, + const enum wined3d_feature_level *feature_levels, unsigned int feature_level_count, + struct wined3d_device_parent *device_parent, struct wined3d_device **device) +{ + struct wined3d_device *object; + HRESULT hr; + + TRACE("wined3d %p, adapter %p, device_type %#x, focus_window %p, flags %#x, " + "surface_alignment %u, feature_levels %p, feature_level_count %u, device_parent %p, device %p.\n", + wined3d, adapter, device_type, focus_window, flags, surface_alignment, + feature_levels, feature_level_count, device_parent, device); + + if (FAILED(hr = adapter->adapter_ops->adapter_create_device(wined3d, adapter, + device_type, focus_window, flags, surface_alignment, + feature_levels, feature_level_count, device_parent, &object))) + return hr; + + TRACE("Created device %p.\n", object); + *device = object; + + device_parent->ops->wined3d_device_created(device_parent, *device); + + return WINED3D_OK; +} + +static const struct wined3d_state_entry_template misc_state_template_no3d[] = +{ + {STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_VERTEX), {STATE_VDECL}}, + {STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_HULL), {STATE_VDECL}}, + {STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_DOMAIN), {STATE_VDECL}}, + {STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_GEOMETRY), {STATE_VDECL}}, + {STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_PIXEL), {STATE_VDECL}}, + {STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_COMPUTE), {STATE_VDECL}}, + {STATE_GRAPHICS_SHADER_RESOURCE_BINDING, {STATE_VDECL}}, + {STATE_GRAPHICS_UNORDERED_ACCESS_VIEW_BINDING, {STATE_VDECL}}, + {STATE_COMPUTE_SHADER_RESOURCE_BINDING, {STATE_VDECL}}, + {STATE_COMPUTE_UNORDERED_ACCESS_VIEW_BINDING, {STATE_VDECL}}, + {STATE_STREAM_OUTPUT, {STATE_VDECL}}, + {STATE_BLEND, {STATE_VDECL}}, + {STATE_BLEND_FACTOR, {STATE_VDECL}}, + {STATE_SAMPLE_MASK, {STATE_VDECL}}, + {STATE_DEPTH_STENCIL, {STATE_VDECL}}, + {STATE_STENCIL_REF, {STATE_VDECL}}, + {STATE_STREAMSRC, {STATE_VDECL}}, + {STATE_VDECL, {STATE_VDECL, state_nop}}, + {STATE_RASTERIZER, {STATE_VDECL}}, + {STATE_SCISSORRECT, {STATE_VDECL}}, + {STATE_POINTSPRITECOORDORIGIN, {STATE_VDECL}}, + + {STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_MAT00), {STATE_VDECL}}, + {STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_MAT01), {STATE_VDECL}}, + {STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_MAT10), {STATE_VDECL}}, + {STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_MAT11), {STATE_VDECL}}, + {STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_MAT00), {STATE_VDECL}}, + {STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_MAT01), {STATE_VDECL}}, + {STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_MAT10), {STATE_VDECL}}, + {STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_MAT11), {STATE_VDECL}}, + {STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_MAT00), {STATE_VDECL}}, + {STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_MAT01), {STATE_VDECL}}, + {STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_MAT10), {STATE_VDECL}}, + {STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_MAT11), {STATE_VDECL}}, + {STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_MAT00), {STATE_VDECL}}, + {STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_MAT01), {STATE_VDECL}}, + {STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_MAT10), {STATE_VDECL}}, + {STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_MAT11), {STATE_VDECL}}, + {STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_MAT00), {STATE_VDECL}}, + {STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_MAT01), {STATE_VDECL}}, + {STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_MAT10), {STATE_VDECL}}, + {STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_MAT11), {STATE_VDECL}}, + {STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_MAT00), {STATE_VDECL}}, + {STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_MAT01), {STATE_VDECL}}, + {STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_MAT10), {STATE_VDECL}}, + {STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_MAT11), {STATE_VDECL}}, + {STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_MAT00), {STATE_VDECL}}, + {STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_MAT01), {STATE_VDECL}}, + {STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_MAT10), {STATE_VDECL}}, + {STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_MAT11), {STATE_VDECL}}, + {STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_MAT00), {STATE_VDECL}}, + {STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_MAT01), {STATE_VDECL}}, + {STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_MAT10), {STATE_VDECL}}, + {STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_MAT11), {STATE_VDECL}}, + {STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_LSCALE), {STATE_VDECL}}, + {STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_LOFFSET), {STATE_VDECL}}, + {STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_LSCALE), {STATE_VDECL}}, + {STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_LOFFSET), {STATE_VDECL}}, + {STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_LSCALE), {STATE_VDECL}}, + {STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_LOFFSET), {STATE_VDECL}}, + {STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_LSCALE), {STATE_VDECL}}, + {STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_LOFFSET), {STATE_VDECL}}, + {STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_LSCALE), {STATE_VDECL}}, + {STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_LOFFSET), {STATE_VDECL}}, + {STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_LSCALE), {STATE_VDECL}}, + {STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_LOFFSET), {STATE_VDECL}}, + {STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_LSCALE), {STATE_VDECL}}, + {STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_LOFFSET), {STATE_VDECL}}, + {STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_LSCALE), {STATE_VDECL}}, + {STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_LOFFSET), {STATE_VDECL}}, + + {STATE_VIEWPORT, {STATE_VDECL}}, + {STATE_INDEXBUFFER, {STATE_VDECL}}, + {STATE_RENDER(WINED3D_RS_ANTIALIAS), {STATE_VDECL}}, + {STATE_RENDER(WINED3D_RS_TEXTUREPERSPECTIVE), {STATE_VDECL}}, + {STATE_RENDER(WINED3D_RS_WRAPU), {STATE_VDECL}}, + {STATE_RENDER(WINED3D_RS_WRAPV), {STATE_VDECL}}, + {STATE_RENDER(WINED3D_RS_LINEPATTERN), {STATE_VDECL}}, + {STATE_RENDER(WINED3D_RS_MONOENABLE), {STATE_VDECL}}, + {STATE_RENDER(WINED3D_RS_ROP2), {STATE_VDECL}}, + {STATE_RENDER(WINED3D_RS_PLANEMASK), {STATE_VDECL}}, + {STATE_RENDER(WINED3D_RS_LASTPIXEL), {STATE_VDECL}}, + {STATE_RENDER(WINED3D_RS_ZFUNC), {STATE_VDECL}}, + {STATE_RENDER(WINED3D_RS_DITHERENABLE), {STATE_VDECL}}, + {STATE_RENDER(WINED3D_RS_SUBPIXEL), {STATE_VDECL}}, + {STATE_RENDER(WINED3D_RS_SUBPIXELX), {STATE_VDECL}}, + {STATE_RENDER(WINED3D_RS_STIPPLEDALPHA), {STATE_VDECL}}, + {STATE_RENDER(WINED3D_RS_STIPPLEENABLE), {STATE_VDECL}}, + {STATE_RENDER(WINED3D_RS_MIPMAPLODBIAS), {STATE_VDECL}}, + {STATE_RENDER(WINED3D_RS_ANISOTROPY), {STATE_VDECL}}, + {STATE_RENDER(WINED3D_RS_FLUSHBATCH), {STATE_VDECL}}, + {STATE_RENDER(WINED3D_RS_TRANSLUCENTSORTINDEPENDENT), {STATE_VDECL}}, + {STATE_RENDER(WINED3D_RS_WRAP0), {STATE_VDECL}}, + {STATE_RENDER(WINED3D_RS_WRAP1), {STATE_VDECL}}, + {STATE_RENDER(WINED3D_RS_WRAP2), {STATE_VDECL}}, + {STATE_RENDER(WINED3D_RS_WRAP3), {STATE_VDECL}}, + {STATE_RENDER(WINED3D_RS_WRAP4), {STATE_VDECL}}, + {STATE_RENDER(WINED3D_RS_WRAP5), {STATE_VDECL}}, + {STATE_RENDER(WINED3D_RS_WRAP6), {STATE_VDECL}}, + {STATE_RENDER(WINED3D_RS_WRAP7), {STATE_VDECL}}, + {STATE_RENDER(WINED3D_RS_WRAP8), {STATE_VDECL}}, + {STATE_RENDER(WINED3D_RS_WRAP9), {STATE_VDECL}}, + {STATE_RENDER(WINED3D_RS_WRAP10), {STATE_VDECL}}, + {STATE_RENDER(WINED3D_RS_WRAP11), {STATE_VDECL}}, + {STATE_RENDER(WINED3D_RS_WRAP12), {STATE_VDECL}}, + {STATE_RENDER(WINED3D_RS_WRAP13), {STATE_VDECL}}, + {STATE_RENDER(WINED3D_RS_WRAP14), {STATE_VDECL}}, + {STATE_RENDER(WINED3D_RS_WRAP15), {STATE_VDECL}}, + {STATE_RENDER(WINED3D_RS_EXTENTS), {STATE_VDECL}}, + {STATE_RENDER(WINED3D_RS_COLORKEYBLENDENABLE), {STATE_VDECL}}, + {STATE_RENDER(WINED3D_RS_SOFTWAREVERTEXPROCESSING), {STATE_VDECL}}, + {STATE_RENDER(WINED3D_RS_PATCHEDGESTYLE), {STATE_VDECL}}, + {STATE_RENDER(WINED3D_RS_PATCHSEGMENTS), {STATE_VDECL}}, + {STATE_RENDER(WINED3D_RS_POSITIONDEGREE), {STATE_VDECL}}, + {STATE_RENDER(WINED3D_RS_NORMALDEGREE), {STATE_VDECL}}, + {STATE_RENDER(WINED3D_RS_MINTESSELLATIONLEVEL), {STATE_VDECL}}, + {STATE_RENDER(WINED3D_RS_MAXTESSELLATIONLEVEL), {STATE_VDECL}}, + {STATE_RENDER(WINED3D_RS_ADAPTIVETESS_X), {STATE_VDECL}}, + {STATE_RENDER(WINED3D_RS_ADAPTIVETESS_Y), {STATE_VDECL}}, + {STATE_RENDER(WINED3D_RS_ADAPTIVETESS_Z), {STATE_VDECL}}, + {STATE_RENDER(WINED3D_RS_ADAPTIVETESS_W), {STATE_VDECL}}, + {STATE_RENDER(WINED3D_RS_ENABLEADAPTIVETESSELLATION), {STATE_VDECL}}, + {STATE_RENDER(WINED3D_RS_MULTISAMPLEANTIALIAS), {STATE_VDECL}}, + {STATE_RENDER(WINED3D_RS_DEBUGMONITORTOKEN), {STATE_VDECL}}, + {STATE_RENDER(WINED3D_RS_ZVISIBLE), {STATE_VDECL}}, + /* Samplers */ + {STATE_SAMPLER(0), {STATE_VDECL}}, + {STATE_SAMPLER(1), {STATE_VDECL}}, + {STATE_SAMPLER(2), {STATE_VDECL}}, + {STATE_SAMPLER(3), {STATE_VDECL}}, + {STATE_SAMPLER(4), {STATE_VDECL}}, + {STATE_SAMPLER(5), {STATE_VDECL}}, + {STATE_SAMPLER(6), {STATE_VDECL}}, + {STATE_SAMPLER(7), {STATE_VDECL}}, + {STATE_SAMPLER(8), {STATE_VDECL}}, + {STATE_SAMPLER(9), {STATE_VDECL}}, + {STATE_SAMPLER(10), {STATE_VDECL}}, + {STATE_SAMPLER(11), {STATE_VDECL}}, + {STATE_SAMPLER(12), {STATE_VDECL}}, + {STATE_SAMPLER(13), {STATE_VDECL}}, + {STATE_SAMPLER(14), {STATE_VDECL}}, + {STATE_SAMPLER(15), {STATE_VDECL}}, + {STATE_SAMPLER(16), /* Vertex sampler 0 */ {STATE_VDECL}}, + {STATE_SAMPLER(17), /* Vertex sampler 1 */ {STATE_VDECL}}, + {STATE_SAMPLER(18), /* Vertex sampler 2 */ {STATE_VDECL}}, + {STATE_SAMPLER(19), /* Vertex sampler 3 */ {STATE_VDECL}}, + {STATE_BASEVERTEXINDEX, {STATE_VDECL}}, + {STATE_FRAMEBUFFER, {STATE_VDECL}}, + {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), {STATE_VDECL}}, + {STATE_SHADER(WINED3D_SHADER_TYPE_HULL), {STATE_VDECL}}, + {STATE_SHADER(WINED3D_SHADER_TYPE_DOMAIN), {STATE_VDECL}}, + {STATE_SHADER(WINED3D_SHADER_TYPE_GEOMETRY), {STATE_VDECL}}, + {STATE_SHADER(WINED3D_SHADER_TYPE_COMPUTE), {STATE_VDECL}}, + {0}, /* Terminate */ +}; + +static void adapter_no3d_destroy(struct wined3d_adapter *adapter) +{ + wined3d_adapter_cleanup(adapter); + heap_free(adapter); +} + +static HRESULT adapter_no3d_create_device(struct wined3d *wined3d, const struct wined3d_adapter *adapter, + enum wined3d_device_type device_type, HWND focus_window, unsigned int flags, BYTE surface_alignment, + const enum wined3d_feature_level *levels, unsigned int level_count, + struct wined3d_device_parent *device_parent, struct wined3d_device **device) +{ + struct wined3d_device_no3d *device_no3d; + HRESULT hr; + + if (!(device_no3d = heap_alloc_zero(sizeof(*device_no3d)))) + return E_OUTOFMEMORY; + + if (FAILED(hr = wined3d_device_init(&device_no3d->d, wined3d, adapter->ordinal, device_type, focus_window, + flags, surface_alignment, levels, level_count, adapter->gl_info.supported, device_parent))) + { + WARN("Failed to initialize device, hr %#x.\n", hr); + heap_free(device_no3d); + return hr; + } + + *device = &device_no3d->d; + + return WINED3D_OK; +} + +static void adapter_no3d_destroy_device(struct wined3d_device *device) +{ + wined3d_device_cleanup(device); + heap_free(device); +} + +struct wined3d_context *adapter_no3d_acquire_context(struct wined3d_device *device, + struct wined3d_texture *texture, unsigned int sub_resource_idx) +{ + TRACE("device %p, texture %p, sub_resource_idx %u.\n", device, texture, sub_resource_idx); + + wined3d_from_cs(device->cs); + + if (!device->context_count) + return NULL; + + return &wined3d_device_no3d(device)->context_no3d; +} + +void adapter_no3d_release_context(struct wined3d_context *context) +{ + TRACE("context %p.\n", context); +} + +static void adapter_no3d_get_wined3d_caps(const struct wined3d_adapter *adapter, struct wined3d_caps *caps) +{ +} + +static BOOL adapter_no3d_check_format(const struct wined3d_adapter *adapter, + const struct wined3d_format *adapter_format, const struct wined3d_format *rt_format, + const struct wined3d_format *ds_format) +{ + return TRUE; +} + +static HRESULT adapter_no3d_init_3d(struct wined3d_device *device) +{ + struct wined3d_context *context_no3d; + HRESULT hr; + + TRACE("device %p.\n", device); + + context_no3d = &wined3d_device_no3d(device)->context_no3d; + if (FAILED(hr = wined3d_context_no3d_init(context_no3d, device->swapchains[0]))) + { + WARN("Failed to initialise context.\n"); + return hr; + } + + if (!device_context_add(device, context_no3d)) + { + ERR("Failed to add the newly created context to the context list.\n"); + wined3d_context_cleanup(context_no3d); + return E_FAIL; + } + + TRACE("Initialised context %p.\n", context_no3d); + + if (!(device->blitter = wined3d_cpu_blitter_create())) + { + ERR("Failed to create CPU blitter.\n"); + device_context_remove(device, context_no3d); + wined3d_context_cleanup(context_no3d); + return E_FAIL; + } + + return WINED3D_OK; +} + +static void adapter_no3d_uninit_3d(struct wined3d_device *device) +{ + struct wined3d_context *context_no3d; + + TRACE("device %p.\n", device); + + context_no3d = &wined3d_device_no3d(device)->context_no3d; + device->blitter->ops->blitter_destroy(device->blitter, NULL); + wined3d_cs_finish(device->cs, WINED3D_CS_QUEUE_DEFAULT); + + device_context_remove(device, context_no3d); + wined3d_context_cleanup(context_no3d); +} + +static void *adapter_no3d_map_bo_address(struct wined3d_context *context, + const struct wined3d_bo_address *data, size_t size, uint32_t map_flags) +{ + if (data->buffer_object) + { + ERR("Unsupported buffer object %#lx.\n", data->buffer_object); + return NULL; + } + + return data->addr; +} + +static void adapter_no3d_unmap_bo_address(struct wined3d_context *context, + const struct wined3d_bo_address *data, unsigned int range_count, const struct wined3d_range *ranges) +{ + if (data->buffer_object) + ERR("Unsupported buffer object %#lx.\n", data->buffer_object); +} + +static void adapter_no3d_copy_bo_address(struct wined3d_context *context, + const struct wined3d_bo_address *dst, const struct wined3d_bo_address *src, size_t size) +{ + if (dst->buffer_object) + ERR("Unsupported dst buffer object %#lx.\n", dst->buffer_object); + if (src->buffer_object) + ERR("Unsupported src buffer object %#lx.\n", src->buffer_object); + if (dst->buffer_object || src->buffer_object) + return; + memcpy(dst->addr, src->addr, size); +} + +static HRESULT adapter_no3d_create_swapchain(struct wined3d_device *device, + struct wined3d_swapchain_desc *desc, struct wined3d_swapchain_state_parent *state_parent, + void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_swapchain **swapchain) +{ + struct wined3d_swapchain *swapchain_no3d; + HRESULT hr; + + TRACE("device %p, desc %p, state_parent %p, parent %p, parent_ops %p, swapchain %p.\n", + device, desc, state_parent, parent, parent_ops, swapchain); + + if (!(swapchain_no3d = heap_alloc_zero(sizeof(*swapchain_no3d)))) + return E_OUTOFMEMORY; + + if (FAILED(hr = wined3d_swapchain_no3d_init(swapchain_no3d, device, desc, state_parent, parent, + parent_ops))) + { + WARN("Failed to initialise swapchain, hr %#x.\n", hr); + heap_free(swapchain_no3d); + return hr; + } + + TRACE("Created swapchain %p.\n", swapchain_no3d); + *swapchain = swapchain_no3d; + + return hr; +} + +static void adapter_no3d_destroy_swapchain(struct wined3d_swapchain *swapchain) +{ + wined3d_swapchain_cleanup(swapchain); + heap_free(swapchain); +} + +static HRESULT adapter_no3d_create_buffer(struct wined3d_device *device, + const struct wined3d_buffer_desc *desc, const struct wined3d_sub_resource_data *data, + void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_buffer **buffer) +{ + struct wined3d_buffer *buffer_no3d; + HRESULT hr; + + TRACE("device %p, desc %p, data %p, parent %p, parent_ops %p, buffer %p.\n", + device, desc, data, parent, parent_ops, buffer); + + if (!(buffer_no3d = heap_alloc_zero(sizeof(*buffer_no3d)))) + return E_OUTOFMEMORY; + + if (FAILED(hr = wined3d_buffer_no3d_init(buffer_no3d, device, desc, data, parent, parent_ops))) + { + WARN("Failed to initialise buffer, hr %#x.\n", hr); + heap_free(buffer_no3d); + return hr; + } + + TRACE("Created buffer %p.\n", buffer_no3d); + *buffer = buffer_no3d; + + return hr; +} + +static void adapter_no3d_destroy_buffer(struct wined3d_buffer *buffer) +{ + struct wined3d_device *device = buffer->resource.device; + unsigned int swapchain_count = device->swapchain_count; + + TRACE("buffer %p.\n", buffer); + + /* Take a reference to the device, in case releasing the buffer would + * cause the device to be destroyed. However, swapchain resources don't + * take a reference to the device, and we wouldn't want to increment the + * refcount on a device that's in the process of being destroyed. */ + if (swapchain_count) + wined3d_device_incref(device); + wined3d_buffer_cleanup(buffer); + wined3d_cs_destroy_object(device->cs, heap_free, buffer); + if (swapchain_count) + wined3d_device_decref(device); +} + +static HRESULT adapter_no3d_create_texture(struct wined3d_device *device, + const struct wined3d_resource_desc *desc, unsigned int layer_count, unsigned int level_count, + uint32_t flags, void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_texture **texture) +{ + struct wined3d_texture *texture_no3d; + HRESULT hr; + + TRACE("device %p, desc %p, layer_count %u, level_count %u, flags %#x, parent %p, parent_ops %p, texture %p.\n", + device, desc, layer_count, level_count, flags, parent, parent_ops, texture); + + if (!(texture_no3d = wined3d_texture_allocate_object_memory(sizeof(*texture_no3d), level_count, layer_count))) + return E_OUTOFMEMORY; + + if (FAILED(hr = wined3d_texture_no3d_init(texture_no3d, device, desc, + layer_count, level_count, flags, parent, parent_ops))) + { + WARN("Failed to initialise texture, hr %#x.\n", hr); + heap_free(texture_no3d); + return hr; + } + + TRACE("Created texture %p.\n", texture_no3d); + *texture = texture_no3d; + + return hr; +} + +static void adapter_no3d_destroy_texture(struct wined3d_texture *texture) +{ + struct wined3d_device *device = texture->resource.device; + unsigned int swapchain_count = device->swapchain_count; + + TRACE("texture %p.\n", texture); + + /* Take a reference to the device, in case releasing the texture would + * cause the device to be destroyed. However, swapchain resources don't + * take a reference to the device, and we wouldn't want to increment the + * refcount on a device that's in the process of being destroyed. */ + if (swapchain_count) + wined3d_device_incref(device); + + wined3d_texture_sub_resources_destroyed(texture); + texture->resource.parent_ops->wined3d_object_destroyed(texture->resource.parent); + + wined3d_texture_cleanup(texture); + wined3d_cs_destroy_object(device->cs, heap_free, texture); + + if (swapchain_count) + wined3d_device_decref(device); +} + +static HRESULT adapter_no3d_create_rendertarget_view(const struct wined3d_view_desc *desc, + struct wined3d_resource *resource, void *parent, const struct wined3d_parent_ops *parent_ops, + struct wined3d_rendertarget_view **view) +{ + struct wined3d_rendertarget_view *view_no3d; + HRESULT hr; + + TRACE("desc %s, resource %p, parent %p, parent_ops %p, view %p.\n", + wined3d_debug_view_desc(desc, resource), resource, parent, parent_ops, view); + + if (!(view_no3d = heap_alloc_zero(sizeof(*view_no3d)))) + return E_OUTOFMEMORY; + + if (FAILED(hr = wined3d_rendertarget_view_no3d_init(view_no3d, desc, resource, parent, parent_ops))) + { + WARN("Failed to initialise view, hr %#x.\n", hr); + heap_free(view_no3d); + return hr; + } + + TRACE("Created render target view %p.\n", view_no3d); + *view = view_no3d; + + return hr; +} + +static void adapter_no3d_destroy_rendertarget_view(struct wined3d_rendertarget_view *view) +{ + struct wined3d_device *device = view->resource->device; + unsigned int swapchain_count = device->swapchain_count; + + TRACE("view %p.\n", view); + + /* Take a reference to the device, in case releasing the view's resource + * would cause the device to be destroyed. However, swapchain resources + * don't take a reference to the device, and we wouldn't want to increment + * the refcount on a device that's in the process of being destroyed. */ + if (swapchain_count) + wined3d_device_incref(device); + wined3d_rendertarget_view_cleanup(view); + wined3d_cs_destroy_object(device->cs, heap_free, view); + if (swapchain_count) + wined3d_device_decref(device); +} + +static HRESULT adapter_no3d_create_shader_resource_view(const struct wined3d_view_desc *desc, + struct wined3d_resource *resource, void *parent, const struct wined3d_parent_ops *parent_ops, + struct wined3d_shader_resource_view **view) +{ + TRACE("desc %s, resource %p, parent %p, parent_ops %p, view %p.\n", + wined3d_debug_view_desc(desc, resource), resource, parent, parent_ops, view); + + return E_NOTIMPL; +} + +static void adapter_no3d_destroy_shader_resource_view(struct wined3d_shader_resource_view *view) +{ + TRACE("view %p.\n", view); +} + +static HRESULT adapter_no3d_create_unordered_access_view(const struct wined3d_view_desc *desc, + struct wined3d_resource *resource, void *parent, const struct wined3d_parent_ops *parent_ops, + struct wined3d_unordered_access_view **view) +{ + TRACE("desc %s, resource %p, parent %p, parent_ops %p, view %p.\n", + wined3d_debug_view_desc(desc, resource), resource, parent, parent_ops, view); + + return E_NOTIMPL; +} + +static void adapter_no3d_destroy_unordered_access_view(struct wined3d_unordered_access_view *view) +{ + TRACE("view %p.\n", view); +} + +static HRESULT adapter_no3d_create_sampler(struct wined3d_device *device, const struct wined3d_sampler_desc *desc, + void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_sampler **sampler) +{ + TRACE("device %p, desc %p, parent %p, parent_ops %p, sampler %p.\n", + device, desc, parent, parent_ops, sampler); + + return E_NOTIMPL; +} + +static void adapter_no3d_destroy_sampler(struct wined3d_sampler *sampler) +{ + TRACE("sampler %p.\n", sampler); +} + +static HRESULT adapter_no3d_create_query(struct wined3d_device *device, enum wined3d_query_type type, + void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_query **query) +{ + TRACE("device %p, type %#x, parent %p, parent_ops %p, query %p.\n", + device, type, parent, parent_ops, query); + + return WINED3DERR_NOTAVAILABLE; +} + +static void adapter_no3d_destroy_query(struct wined3d_query *query) +{ + TRACE("query %p.\n", query); +} + +static void adapter_no3d_flush_context(struct wined3d_context *context) +{ + TRACE("context %p.\n", context); +} + +static void adapter_no3d_draw_primitive(struct wined3d_device *device, + const struct wined3d_state *state, const struct wined3d_draw_parameters *parameters) +{ + ERR("device %p, state %p, parameters %p.\n", device, state, parameters); +} + +static void adapter_no3d_dispatch_compute(struct wined3d_device *device, + const struct wined3d_state *state, const struct wined3d_dispatch_parameters *parameters) +{ + ERR("device %p, state %p, parameters %p.\n", device, state, parameters); +} + +void adapter_no3d_clear_uav(struct wined3d_context *context, + struct wined3d_unordered_access_view *view, const struct wined3d_uvec4 *clear_value) +{ + ERR("context %p, view %p, clear_value %s.\n", context, view, debug_uvec4(clear_value)); +} + +static const struct wined3d_adapter_ops wined3d_adapter_no3d_ops = +{ + .adapter_destroy = adapter_no3d_destroy, + .adapter_create_device = adapter_no3d_create_device, + .adapter_destroy_device = adapter_no3d_destroy_device, + .adapter_acquire_context = adapter_no3d_acquire_context, + .adapter_release_context = adapter_no3d_release_context, + .adapter_get_wined3d_caps = adapter_no3d_get_wined3d_caps, + .adapter_check_format = adapter_no3d_check_format, + .adapter_init_3d = adapter_no3d_init_3d, + .adapter_uninit_3d = adapter_no3d_uninit_3d, + .adapter_map_bo_address = adapter_no3d_map_bo_address, + .adapter_unmap_bo_address = adapter_no3d_unmap_bo_address, + .adapter_copy_bo_address = adapter_no3d_copy_bo_address, + .adapter_create_swapchain = adapter_no3d_create_swapchain, + .adapter_destroy_swapchain = adapter_no3d_destroy_swapchain, + .adapter_create_buffer = adapter_no3d_create_buffer, + .adapter_destroy_buffer = adapter_no3d_destroy_buffer, + .adapter_create_texture = adapter_no3d_create_texture, + .adapter_destroy_texture = adapter_no3d_destroy_texture, + .adapter_create_rendertarget_view = adapter_no3d_create_rendertarget_view, + .adapter_destroy_rendertarget_view = adapter_no3d_destroy_rendertarget_view, + .adapter_create_shader_resource_view = adapter_no3d_create_shader_resource_view, + .adapter_destroy_shader_resource_view = adapter_no3d_destroy_shader_resource_view, + .adapter_create_unordered_access_view = adapter_no3d_create_unordered_access_view, + .adapter_destroy_unordered_access_view = adapter_no3d_destroy_unordered_access_view, + .adapter_create_sampler = adapter_no3d_create_sampler, + .adapter_destroy_sampler = adapter_no3d_destroy_sampler, + .adapter_create_query = adapter_no3d_create_query, + .adapter_destroy_query = adapter_no3d_destroy_query, + .adapter_flush_context = adapter_no3d_flush_context, + .adapter_draw_primitive = adapter_no3d_draw_primitive, + .adapter_dispatch_compute = adapter_no3d_dispatch_compute, + .adapter_clear_uav = adapter_no3d_clear_uav, +}; + +static void wined3d_adapter_no3d_init_d3d_info(struct wined3d_adapter *adapter, unsigned int wined3d_creation_flags) +{ + struct wined3d_d3d_info *d3d_info = &adapter->d3d_info; + + d3d_info->wined3d_creation_flags = wined3d_creation_flags; + d3d_info->texture_npot = TRUE; + d3d_info->feature_level = WINED3D_FEATURE_LEVEL_5; +} + +static struct wined3d_adapter *wined3d_adapter_no3d_create(unsigned int ordinal, unsigned int wined3d_creation_flags) +{ + struct wined3d_adapter *adapter; + LUID primary_luid, *luid = NULL; + + static const struct wined3d_gpu_description gpu_description = + { + HW_VENDOR_SOFTWARE, CARD_WINE, "WineD3D DirectDraw Emulation", DRIVER_WINE, 128, + }; + + TRACE("ordinal %u, wined3d_creation_flags %#x.\n", ordinal, wined3d_creation_flags); + + if (!(adapter = heap_alloc_zero(sizeof(*adapter)))) + return NULL; + + if (ordinal == 0 && wined3d_get_primary_adapter_luid(&primary_luid)) + luid = &primary_luid; + + if (!wined3d_adapter_init(adapter, ordinal, luid, &wined3d_adapter_no3d_ops)) + { + heap_free(adapter); + return NULL; + } + + if (!wined3d_adapter_no3d_init_format_info(adapter)) + { + wined3d_adapter_cleanup(adapter); + heap_free(adapter); + return NULL; + } + + if (!wined3d_driver_info_init(&adapter->driver_info, &gpu_description, WINED3D_FEATURE_LEVEL_NONE, 0, 0)) + { + wined3d_adapter_cleanup(adapter); + heap_free(adapter); + return NULL; + } + adapter->vram_bytes_used = 0; + TRACE("Emulating 0x%s bytes of video ram.\n", wine_dbgstr_longlong(adapter->driver_info.vram_bytes)); + + adapter->vertex_pipe = &none_vertex_pipe; + adapter->fragment_pipe = &none_fragment_pipe; + adapter->misc_state_template = misc_state_template_no3d; + adapter->shader_backend = &none_shader_backend; + + wined3d_adapter_no3d_init_d3d_info(adapter, wined3d_creation_flags); + + TRACE("Created adapter %p.\n", adapter); + + return adapter; +} + +static BOOL wined3d_adapter_create_output(struct wined3d_adapter *adapter, const WCHAR *output_name) +{ + struct wined3d_output *outputs; + HRESULT hr; + + if (!adapter->outputs && !(adapter->outputs = heap_calloc(1, sizeof(*adapter->outputs)))) + { + return FALSE; + } + else + { + if (!(outputs = heap_realloc(adapter->outputs, + sizeof(*adapter->outputs) * (adapter->output_count + 1)))) + return FALSE; + + adapter->outputs = outputs; + } + + if (FAILED(hr = wined3d_output_init(&adapter->outputs[adapter->output_count], + adapter->output_count, adapter, output_name))) + { + ERR("Failed to initialise output %s, hr %#x.\n", wine_dbgstr_w(output_name), hr); + return FALSE; + } + + ++adapter->output_count; + TRACE("Initialised output %s.\n", wine_dbgstr_w(output_name)); + return TRUE; +} + +BOOL wined3d_adapter_init(struct wined3d_adapter *adapter, unsigned int ordinal, const LUID *luid, + const struct wined3d_adapter_ops *adapter_ops) +{ + unsigned int output_idx = 0, primary_idx = 0; + DISPLAY_DEVICEW display_device; + BOOL ret = FALSE; + + adapter->ordinal = ordinal; + adapter->output_count = 0; + adapter->outputs = NULL; + + if (luid) + { + adapter->luid = *luid; + } + else + { + WARN("Allocating a random LUID.\n"); + if (!AllocateLocallyUniqueId(&adapter->luid)) + { + ERR("Failed to allocate a LUID, error %#x.\n", GetLastError()); + return FALSE; + } + } + TRACE("adapter %p LUID %08x:%08x.\n", adapter, adapter->luid.HighPart, adapter->luid.LowPart); + + display_device.cb = sizeof(display_device); + while (EnumDisplayDevicesW(NULL, output_idx++, &display_device, 0)) + { + /* Detached outputs are not enumerated */ + if (!(display_device.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP)) + continue; + + if (display_device.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) + primary_idx = adapter->output_count; + + if (!wined3d_adapter_create_output(adapter, display_device.DeviceName)) + goto done; + } + TRACE("Initialised %d outputs for adapter %p.\n", adapter->output_count, adapter); + + /* Make the primary output first */ + if (primary_idx) + { + struct wined3d_output tmp = adapter->outputs[0]; + adapter->outputs[0] = adapter->outputs[primary_idx]; + adapter->outputs[0].ordinal = 0; + adapter->outputs[primary_idx] = tmp; + adapter->outputs[primary_idx].ordinal = primary_idx; + } + + memset(&adapter->driver_uuid, 0, sizeof(adapter->driver_uuid)); + memset(&adapter->device_uuid, 0, sizeof(adapter->device_uuid)); + + adapter->formats = NULL; + adapter->adapter_ops = adapter_ops; + ret = TRUE; +done: + if (!ret) + { + for (output_idx = 0; output_idx < adapter->output_count; ++output_idx) + wined3d_output_cleanup(&adapter->outputs[output_idx]); + heap_free(adapter->outputs); + } + return ret; +} + +static struct wined3d_adapter *wined3d_adapter_create(unsigned int ordinal, DWORD wined3d_creation_flags) +{ + if (wined3d_creation_flags & WINED3D_NO3D) + return wined3d_adapter_no3d_create(ordinal, wined3d_creation_flags); + + if (wined3d_settings.renderer == WINED3D_RENDERER_VULKAN) + return wined3d_adapter_vk_create(ordinal, wined3d_creation_flags); + + return wined3d_adapter_gl_create(ordinal, wined3d_creation_flags); +} + +static void STDMETHODCALLTYPE wined3d_null_wined3d_object_destroyed(void *parent) {} + +const struct wined3d_parent_ops wined3d_null_parent_ops = +{ + wined3d_null_wined3d_object_destroyed, +}; + +HRESULT wined3d_init(struct wined3d *wined3d, DWORD flags) +{ + wined3d->ref = 1; + wined3d->flags = flags; + + TRACE("Initialising adapters.\n"); + + if (!(wined3d->adapters[0] = wined3d_adapter_create(0, flags))) + { + WARN("Failed to create adapter.\n"); + return E_FAIL; + } + wined3d->adapter_count = 1; + + return WINED3D_OK; +} diff --git a/wrappers/directx/d3dwine_wrapper/dxtn.c b/wrappers/directx/d3dwine_wrapper/dxtn.c new file mode 100644 index 00000000000..4e24fb56419 --- /dev/null +++ b/wrappers/directx/d3dwine_wrapper/dxtn.c @@ -0,0 +1,435 @@ +/* + * Copyright 2014 Michael Müller + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "config.h" +#include "wine/port.h" +#include "wined3d_private.h" +#include "wine/library.h" +#include "dxtn.h" + +WINE_DEFAULT_DEBUG_CHANNEL(d3d); + +static inline BOOL dxt1_to_x8r8g8b8(const BYTE *src, BYTE *dst, DWORD pitch_in, + DWORD pitch_out, unsigned int w, unsigned int h, BOOL alpha) +{ + unsigned int x, y; + DWORD color; + + TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out); + + for (y = 0; y < h; ++y) + { + DWORD *dst_line = (DWORD *)(dst + y * pitch_out); + for (x = 0; x < w; ++x) + { + /* fetch_2d_texel_rgba_dxt1 doesn't correctly handle pitch */ + fetch_2d_texel_rgba_dxt1(0, src + (y / 4) * pitch_in + (x / 4) * 8, + x & 3, y & 3, &color); + if (alpha) + { + dst_line[x] = (color & 0xff00ff00) | ((color & 0xff) << 16) | + ((color & 0xff0000) >> 16); + } + else + { + dst_line[x] = 0xff000000 | ((color & 0xff) << 16) | + (color & 0xff00) | ((color & 0xff0000) >> 16); + } + } + } + + return TRUE; +} + +static inline BOOL dxt1_to_x4r4g4b4(const BYTE *src, BYTE *dst, DWORD pitch_in, + DWORD pitch_out, unsigned int w, unsigned int h, BOOL alpha) +{ + unsigned int x, y; + DWORD color; + + TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out); + + for (y = 0; y < h; ++y) + { + WORD *dst_line = (WORD *)(dst + y * pitch_out); + for (x = 0; x < w; ++x) + { + /* fetch_2d_texel_rgba_dxt1 doesn't correctly handle pitch */ + fetch_2d_texel_rgba_dxt1(0, src + (y / 4) * pitch_in + (x / 4) * 16, + x & 3, y & 3, &color); + if (alpha) + { + dst_line[x] = ((color & 0xf0000000) >> 16) | ((color & 0xf00000) >> 20) | + ((color & 0xf000) >> 8) | ((color & 0xf0) << 4); + } + else + { + dst_line[x] = 0xf000 | ((color & 0xf00000) >> 20) | + ((color & 0xf000) >> 8) | ((color & 0xf0) << 4); + } + } + } + + return TRUE; +} + +static inline BOOL dxt1_to_x1r5g5b5(const BYTE *src, BYTE *dst, DWORD pitch_in, + DWORD pitch_out, unsigned int w, unsigned int h, BOOL alpha) +{ + unsigned int x, y; + DWORD color; + + TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out); + + for (y = 0; y < h; ++y) + { + WORD *dst_line = (WORD *)(dst + y * pitch_out); + for (x = 0; x < w; ++x) + { + /* fetch_2d_texel_rgba_dxt1 doesn't correctly handle pitch */ + fetch_2d_texel_rgba_dxt1(0, src + (y / 4) * pitch_in + (x / 4) * 16, + x & 3, y & 3, &color); + if (alpha) + { + dst_line[x] = ((color & 0x80000000) >> 16) | ((color & 0xf80000) >> 19) | + ((color & 0xf800) >> 6) | ((color & 0xf8) << 7); + } + else + { + dst_line[x] = 0x8000 | ((color & 0xf80000) >> 19) | + ((color & 0xf800) >> 6) | ((color & 0xf8) << 7); + } + } + } + + return TRUE; +} + +static inline BOOL dxt3_to_x8r8g8b8(const BYTE *src, BYTE *dst, DWORD pitch_in, + DWORD pitch_out, unsigned int w, unsigned int h, BOOL alpha) +{ + unsigned int x, y; + DWORD color; + + TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out); + + for (y = 0; y < h; ++y) + { + DWORD *dst_line = (DWORD *)(dst + y * pitch_out); + for (x = 0; x < w; ++x) + { + /* fetch_2d_texel_rgba_dxt3 doesn't correctly handle pitch */ + fetch_2d_texel_rgba_dxt3(0, src + (y / 4) * pitch_in + (x / 4) * 16, + x & 3, y & 3, &color); + if (alpha) + { + dst_line[x] = (color & 0xff00ff00) | ((color & 0xff) << 16) | + ((color & 0xff0000) >> 16); + } + else + { + dst_line[x] = 0xff000000 | ((color & 0xff) << 16) | + (color & 0xff00) | ((color & 0xff0000) >> 16); + } + } + } + + return TRUE; +} + +static inline BOOL dxt3_to_x4r4g4b4(const BYTE *src, BYTE *dst, DWORD pitch_in, + DWORD pitch_out, unsigned int w, unsigned int h, BOOL alpha) +{ + unsigned int x, y; + DWORD color; + + TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out); + + for (y = 0; y < h; ++y) + { + WORD *dst_line = (WORD *)(dst + y * pitch_out); + for (x = 0; x < w; ++x) + { + /* fetch_2d_texel_rgba_dxt3 doesn't correctly handle pitch */ + fetch_2d_texel_rgba_dxt3(0, src + (y / 4) * pitch_in + (x / 4) * 16, + x & 3, y & 3, &color); + if (alpha) + { + dst_line[x] = ((color & 0xf0000000) >> 16) | ((color & 0xf00000) >> 20) | + ((color & 0xf000) >> 8) | ((color & 0xf0) << 4); + } + else + { + dst_line[x] = 0xf000 | ((color & 0xf00000) >> 20) | + ((color & 0xf000) >> 8) | ((color & 0xf0) << 4); + } + } + } + + return TRUE; +} + +static inline BOOL dxt5_to_x8r8g8b8(const BYTE *src, BYTE *dst, DWORD pitch_in, + DWORD pitch_out, unsigned int w, unsigned int h, BOOL alpha) +{ + unsigned int x, y; + DWORD color; + + TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out); + + for (y = 0; y < h; ++y) + { + DWORD *dst_line = (DWORD *)(dst + y * pitch_out); + for (x = 0; x < w; ++x) + { + /* fetch_2d_texel_rgba_dxt5 doesn't correctly handle pitch */ + fetch_2d_texel_rgba_dxt5(0, src + (y / 4) * pitch_in + (x / 4) * 16, + x & 3, y & 3, &color); + if (alpha) + { + dst_line[x] = (color & 0xff00ff00) | ((color & 0xff) << 16) | + ((color & 0xff0000) >> 16); + } + else + { + dst_line[x] = 0xff000000 | ((color & 0xff) << 16) | + (color & 0xff00) | ((color & 0xff0000) >> 16); + } + } + } + + return TRUE; +} + +static inline BOOL x8r8g8b8_to_dxtn(const BYTE *src, BYTE *dst, DWORD pitch_in, + DWORD pitch_out, unsigned int w, unsigned int h, GLenum destformat, BOOL alpha) +{ + unsigned int x, y; + DWORD color, *tmp; + + TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out); + + tmp = HeapAlloc(GetProcessHeap(), 0, h * w * sizeof(DWORD)); + if (!tmp) + { + ERR("Failed to allocate memory for conversion\n"); + return FALSE; + } + + for (y = 0; y < h; ++y) + { + const DWORD *src_line = (const DWORD *)(src + y * pitch_in); + DWORD *dst_line = tmp + y * w; + for (x = 0; x < w; ++x) + { + color = src_line[x]; + if (alpha) + { + dst_line[x] = (color & 0xff00ff00) | ((color & 0xff) << 16) | + ((color & 0xff0000) >> 16); + } + else + { + dst_line[x] = 0xff000000 | ((color & 0xff) << 16) | + (color & 0xff00) | ((color & 0xff0000) >> 16); + } + } + } + + tx_compress_dxtn(4, w, h, (BYTE *)tmp, destformat, dst, pitch_out); + HeapFree(GetProcessHeap(), 0, tmp); + return TRUE; +} + +static inline BOOL x1r5g5b5_to_dxtn(const BYTE *src, BYTE *dst, DWORD pitch_in, + DWORD pitch_out, unsigned int w, unsigned int h, GLenum destformat, BOOL alpha) +{ + static const unsigned char convert_5to8[] = + { + 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a, + 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b, + 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd, + 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff, + }; + unsigned int x, y; + DWORD *tmp; + WORD color; + + TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out); + + tmp = HeapAlloc(GetProcessHeap(), 0, h * w * sizeof(DWORD)); + if (!tmp) + { + ERR("Failed to allocate memory for conversion\n"); + return FALSE; + } + + for (y = 0; y < h; ++y) + { + const WORD *src_line = (const WORD *)(src + y * pitch_in); + DWORD *dst_line = tmp + y * w; + for (x = 0; x < w; ++x) + { + color = src_line[x]; + if (alpha) + { + dst_line[x] = ((color & 0x8000) ? 0xff000000 : 0) | + convert_5to8[(color & 0x001f)] << 16 | + convert_5to8[(color & 0x03e0) >> 5] << 8 | + convert_5to8[(color & 0x7c00) >> 10]; + } + else + { + dst_line[x] = 0xff000000 | + convert_5to8[(color & 0x001f)] << 16 | + convert_5to8[(color & 0x03e0) >> 5] << 8 | + convert_5to8[(color & 0x7c00) >> 10]; + } + } + } + + tx_compress_dxtn(4, w, h, (BYTE *)tmp, destformat, dst, pitch_out); + HeapFree(GetProcessHeap(), 0, tmp); + return TRUE; +} + +BOOL wined3d_dxt1_decode(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, + enum wined3d_format_id format, unsigned int w, unsigned int h) +{ + switch (format) + { + case WINED3DFMT_B8G8R8A8_UNORM: + return dxt1_to_x8r8g8b8(src, dst, pitch_in, pitch_out, w, h, TRUE); + case WINED3DFMT_B8G8R8X8_UNORM: + return dxt1_to_x8r8g8b8(src, dst, pitch_in, pitch_out, w, h, FALSE); + case WINED3DFMT_B4G4R4A4_UNORM: + return dxt1_to_x4r4g4b4(src, dst, pitch_in, pitch_out, w, h, TRUE); + case WINED3DFMT_B4G4R4X4_UNORM: + return dxt1_to_x4r4g4b4(src, dst, pitch_in, pitch_out, w, h, FALSE); + case WINED3DFMT_B5G5R5A1_UNORM: + return dxt1_to_x1r5g5b5(src, dst, pitch_in, pitch_out, w, h, TRUE); + case WINED3DFMT_B5G5R5X1_UNORM: + return dxt1_to_x1r5g5b5(src, dst, pitch_in, pitch_out, w, h, FALSE); + default: + break; + } + + FIXME("Cannot find a conversion function from format DXT1 to %s.\n", debug_d3dformat(format)); + return FALSE; +} + +BOOL wined3d_dxt3_decode(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, + enum wined3d_format_id format, unsigned int w, unsigned int h) +{ + switch (format) + { + case WINED3DFMT_B8G8R8A8_UNORM: + return dxt3_to_x8r8g8b8(src, dst, pitch_in, pitch_out, w, h, TRUE); + case WINED3DFMT_B8G8R8X8_UNORM: + return dxt3_to_x8r8g8b8(src, dst, pitch_in, pitch_out, w, h, FALSE); + case WINED3DFMT_B4G4R4A4_UNORM: + return dxt3_to_x4r4g4b4(src, dst, pitch_in, pitch_out, w, h, TRUE); + case WINED3DFMT_B4G4R4X4_UNORM: + return dxt3_to_x4r4g4b4(src, dst, pitch_in, pitch_out, w, h, FALSE); + default: + break; + } + + FIXME("Cannot find a conversion function from format DXT3 to %s.\n", debug_d3dformat(format)); + return FALSE; +} + +BOOL wined3d_dxt5_decode(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, + enum wined3d_format_id format, unsigned int w, unsigned int h) +{ + switch (format) + { + case WINED3DFMT_B8G8R8A8_UNORM: + return dxt5_to_x8r8g8b8(src, dst, pitch_in, pitch_out, w, h, TRUE); + case WINED3DFMT_B8G8R8X8_UNORM: + return dxt5_to_x8r8g8b8(src, dst, pitch_in, pitch_out, w, h, FALSE); + default: + break; + } + + FIXME("Cannot find a conversion function from format DXT5 to %s.\n", debug_d3dformat(format)); + return FALSE; +} + +BOOL wined3d_dxt1_encode(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, + enum wined3d_format_id format, unsigned int w, unsigned int h) +{ + switch (format) + { + case WINED3DFMT_B8G8R8A8_UNORM: + return x8r8g8b8_to_dxtn(src, dst, pitch_in, pitch_out, w, h, + GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, TRUE); + case WINED3DFMT_B8G8R8X8_UNORM: + return x8r8g8b8_to_dxtn(src, dst, pitch_in, pitch_out, w, h, + GL_COMPRESSED_RGB_S3TC_DXT1_EXT, FALSE); + case WINED3DFMT_B5G5R5A1_UNORM: + return x1r5g5b5_to_dxtn(src, dst, pitch_in, pitch_out, w, h, + GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, TRUE); + case WINED3DFMT_B5G5R5X1_UNORM: + return x1r5g5b5_to_dxtn(src, dst, pitch_in, pitch_out, w, h, + GL_COMPRESSED_RGB_S3TC_DXT1_EXT, FALSE); + default: + break; + } + + FIXME("Cannot find a conversion function from format %s to DXT1.\n", debug_d3dformat(format)); + return FALSE; +} + +BOOL wined3d_dxt3_encode(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, + enum wined3d_format_id format, unsigned int w, unsigned int h) +{ + switch (format) + { + case WINED3DFMT_B8G8R8A8_UNORM: + return x8r8g8b8_to_dxtn(src, dst, pitch_in, pitch_out, w, h, + GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, TRUE); + case WINED3DFMT_B8G8R8X8_UNORM: + return x8r8g8b8_to_dxtn(src, dst, pitch_in, pitch_out, w, h, + GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, FALSE); + default: + break; + } + + FIXME("Cannot find a conversion function from format %s to DXT3.\n", debug_d3dformat(format)); + return FALSE; +} + +BOOL wined3d_dxt5_encode(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, + enum wined3d_format_id format, unsigned int w, unsigned int h) +{ + switch (format) + { + case WINED3DFMT_B8G8R8A8_UNORM: + return x8r8g8b8_to_dxtn(src, dst, pitch_in, pitch_out, w, h, + GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, TRUE); + case WINED3DFMT_B8G8R8X8_UNORM: + return x8r8g8b8_to_dxtn(src, dst, pitch_in, pitch_out, w, h, + GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, FALSE); + default: + break; + } + + FIXME("Cannot find a conversion function from format %s to DXT5.\n", debug_d3dformat(format)); + return FALSE; +} diff --git a/wrappers/directx/d3dwine_wrapper/dxtn.h b/wrappers/directx/d3dwine_wrapper/dxtn.h new file mode 100644 index 00000000000..5f0826ee1eb --- /dev/null +++ b/wrappers/directx/d3dwine_wrapper/dxtn.h @@ -0,0 +1,987 @@ +/* + * libtxc_dxtn + * Version: 1.0 + * + * Copyright (C) 2004 Roland Scheidegger All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include "wined3d_gl.h" + +typedef GLubyte GLchan; +#define UBYTE_TO_CHAN(b) (b) +#define CHAN_MAX 255 +#define RCOMP 0 +#define GCOMP 1 +#define BCOMP 2 +#define ACOMP 3 + +#define EXP5TO8R(packedcol) \ + ((((packedcol) >> 8) & 0xf8) | (((packedcol) >> 13) & 0x7)) + +#define EXP6TO8G(packedcol) \ + ((((packedcol) >> 3) & 0xfc) | (((packedcol) >> 9) & 0x3)) + +#define EXP5TO8B(packedcol) \ + ((((packedcol) << 3) & 0xf8) | (((packedcol) >> 2) & 0x7)) + +#define EXP4TO8(col) \ + ((col) | ((col) << 4)) + +/* inefficient. To be efficient, it would be necessary to decode 16 pixels at once */ + +static void dxt135_decode_imageblock ( const GLubyte *img_block_src, + GLint i, GLint j, GLuint dxt_type, GLvoid *texel ) { + GLchan *rgba = (GLchan *) texel; + const GLushort color0 = img_block_src[0] | (img_block_src[1] << 8); + const GLushort color1 = img_block_src[2] | (img_block_src[3] << 8); + const GLuint bits = img_block_src[4] | (img_block_src[5] << 8) | + (img_block_src[6] << 16) | (img_block_src[7] << 24); + /* What about big/little endian? */ + GLubyte bit_pos = 2 * (j * 4 + i) ; + GLubyte code = (GLubyte) ((bits >> bit_pos) & 3); + + rgba[ACOMP] = CHAN_MAX; + switch (code) { + case 0: + rgba[RCOMP] = UBYTE_TO_CHAN( EXP5TO8R(color0) ); + rgba[GCOMP] = UBYTE_TO_CHAN( EXP6TO8G(color0) ); + rgba[BCOMP] = UBYTE_TO_CHAN( EXP5TO8B(color0) ); + break; + case 1: + rgba[RCOMP] = UBYTE_TO_CHAN( EXP5TO8R(color1) ); + rgba[GCOMP] = UBYTE_TO_CHAN( EXP6TO8G(color1) ); + rgba[BCOMP] = UBYTE_TO_CHAN( EXP5TO8B(color1) ); + break; + case 2: + if ((dxt_type > 1) || (color0 > color1)) { + rgba[RCOMP] = UBYTE_TO_CHAN( ((EXP5TO8R(color0) * 2 + EXP5TO8R(color1)) / 3) ); + rgba[GCOMP] = UBYTE_TO_CHAN( ((EXP6TO8G(color0) * 2 + EXP6TO8G(color1)) / 3) ); + rgba[BCOMP] = UBYTE_TO_CHAN( ((EXP5TO8B(color0) * 2 + EXP5TO8B(color1)) / 3) ); + } + else { + rgba[RCOMP] = UBYTE_TO_CHAN( ((EXP5TO8R(color0) + EXP5TO8R(color1)) / 2) ); + rgba[GCOMP] = UBYTE_TO_CHAN( ((EXP6TO8G(color0) + EXP6TO8G(color1)) / 2) ); + rgba[BCOMP] = UBYTE_TO_CHAN( ((EXP5TO8B(color0) + EXP5TO8B(color1)) / 2) ); + } + break; + case 3: + if ((dxt_type > 1) || (color0 > color1)) { + rgba[RCOMP] = UBYTE_TO_CHAN( ((EXP5TO8R(color0) + EXP5TO8R(color1) * 2) / 3) ); + rgba[GCOMP] = UBYTE_TO_CHAN( ((EXP6TO8G(color0) + EXP6TO8G(color1) * 2) / 3) ); + rgba[BCOMP] = UBYTE_TO_CHAN( ((EXP5TO8B(color0) + EXP5TO8B(color1) * 2) / 3) ); + } + else { + rgba[RCOMP] = 0; + rgba[GCOMP] = 0; + rgba[BCOMP] = 0; + if (dxt_type == 1) rgba[ACOMP] = UBYTE_TO_CHAN(0); + } + break; + default: + /* CANNOT happen (I hope) */ + break; + } +} + + +// static void fetch_2d_texel_rgb_dxt1(GLint srcRowStride, const GLubyte *pixdata, + // GLint i, GLint j, GLvoid *texel) +// { + // /* Extract the (i,j) pixel from pixdata and return it + // * in texel[RCOMP], texel[GCOMP], texel[BCOMP], texel[ACOMP]. + // */ + + // const GLubyte *blksrc = (pixdata + ((srcRowStride + 3) / 4 * (j / 4) + (i / 4)) * 8); + // dxt135_decode_imageblock(blksrc, (i&3), (j&3), 0, texel); +// } + + +static void fetch_2d_texel_rgba_dxt1(GLint srcRowStride, const GLubyte *pixdata, + GLint i, GLint j, GLvoid *texel) +{ + /* Extract the (i,j) pixel from pixdata and return it + * in texel[RCOMP], texel[GCOMP], texel[BCOMP], texel[ACOMP]. + */ + + const GLubyte *blksrc = (pixdata + ((srcRowStride + 3) / 4 * (j / 4) + (i / 4)) * 8); + dxt135_decode_imageblock(blksrc, (i&3), (j&3), 1, texel); +} + +static void fetch_2d_texel_rgba_dxt3(GLint srcRowStride, const GLubyte *pixdata, + GLint i, GLint j, GLvoid *texel) { + + /* Extract the (i,j) pixel from pixdata and return it + * in texel[RCOMP], texel[GCOMP], texel[BCOMP], texel[ACOMP]. + */ + + GLchan *rgba = (GLchan *) texel; + const GLubyte *blksrc = (pixdata + ((srcRowStride + 3) / 4 * (j / 4) + (i / 4)) * 16); + const GLubyte anibble = (blksrc[((j&3) * 4 + (i&3)) / 2] >> (4 * (i&1))) & 0xf; + dxt135_decode_imageblock(blksrc + 8, (i&3), (j&3), 2, texel); + rgba[ACOMP] = UBYTE_TO_CHAN( (GLubyte)(EXP4TO8(anibble)) ); +} + +static void fetch_2d_texel_rgba_dxt5(GLint srcRowStride, const GLubyte *pixdata, + GLint i, GLint j, GLvoid *texel) { + + /* Extract the (i,j) pixel from pixdata and return it + * in texel[RCOMP], texel[GCOMP], texel[BCOMP], texel[ACOMP]. + */ + + GLchan *rgba = (GLchan *) texel; + const GLubyte *blksrc = (pixdata + ((srcRowStride + 3) / 4 * (j / 4) + (i / 4)) * 16); + const GLubyte alpha0 = blksrc[0]; + const GLubyte alpha1 = blksrc[1]; + const GLubyte bit_pos = ((j&3) * 4 + (i&3)) * 3; + const GLubyte acodelow = blksrc[2 + bit_pos / 8]; + const GLubyte acodehigh = blksrc[3 + bit_pos / 8]; + const GLubyte code = (acodelow >> (bit_pos & 0x7) | + (acodehigh << (8 - (bit_pos & 0x7)))) & 0x7; + dxt135_decode_imageblock(blksrc + 8, (i&3), (j&3), 2, texel); + if (code == 0) + rgba[ACOMP] = UBYTE_TO_CHAN( alpha0 ); + else if (code == 1) + rgba[ACOMP] = UBYTE_TO_CHAN( alpha1 ); + else if (alpha0 > alpha1) + rgba[ACOMP] = UBYTE_TO_CHAN( ((alpha0 * (8 - code) + (alpha1 * (code - 1))) / 7) ); + else if (code < 6) + rgba[ACOMP] = UBYTE_TO_CHAN( ((alpha0 * (6 - code) + (alpha1 * (code - 1))) / 5) ); + else if (code == 6) + rgba[ACOMP] = 0; + else + rgba[ACOMP] = CHAN_MAX; +} + + +/* weights used for error function, basically weights (unsquared 2/4/1) according to rgb->luminance conversion + not sure if this really reflects visual perception */ +#define REDWEIGHT 4 +#define GREENWEIGHT 16 +#define BLUEWEIGHT 1 + +#define ALPHACUT 127 + +static void fancybasecolorsearch( GLubyte *blkaddr, GLubyte srccolors[4][4][4], GLubyte *bestcolor[2], + GLint numxpixels, GLint numypixels, GLint type, GLboolean haveAlpha) +{ + /* use same luminance-weighted distance metric to determine encoding as for finding the base colors */ + + /* TODO could also try to find a better encoding for the 3-color-encoding type, this really should be done + if it's rgba_dxt1 and we have alpha in the block, currently even values which will be mapped to black + due to their alpha value will influence the result */ + GLint i, j, colors, z; + GLuint pixerror, pixerrorred, pixerrorgreen, pixerrorblue, pixerrorbest; + GLint colordist, blockerrlin[2][3]; + GLubyte nrcolor[2]; + GLint pixerrorcolorbest[3]; + GLubyte enc = 0; + GLubyte cv[4][4]; + GLubyte testcolor[2][3]; + +/* fprintf(stderr, "color begin 0 r/g/b %d/%d/%d, 1 r/g/b %d/%d/%d\n", + bestcolor[0][0], bestcolor[0][1], bestcolor[0][2], bestcolor[1][0], bestcolor[1][1], bestcolor[1][2]);*/ + if (((bestcolor[0][0] & 0xf8) << 8 | (bestcolor[0][1] & 0xfc) << 3 | bestcolor[0][2] >> 3) < + ((bestcolor[1][0] & 0xf8) << 8 | (bestcolor[1][1] & 0xfc) << 3 | bestcolor[1][2] >> 3)) { + testcolor[0][0] = bestcolor[0][0]; + testcolor[0][1] = bestcolor[0][1]; + testcolor[0][2] = bestcolor[0][2]; + testcolor[1][0] = bestcolor[1][0]; + testcolor[1][1] = bestcolor[1][1]; + testcolor[1][2] = bestcolor[1][2]; + } + else { + testcolor[1][0] = bestcolor[0][0]; + testcolor[1][1] = bestcolor[0][1]; + testcolor[1][2] = bestcolor[0][2]; + testcolor[0][0] = bestcolor[1][0]; + testcolor[0][1] = bestcolor[1][1]; + testcolor[0][2] = bestcolor[1][2]; + } + + for (i = 0; i < 3; i ++) { + cv[0][i] = testcolor[0][i]; + cv[1][i] = testcolor[1][i]; + cv[2][i] = (testcolor[0][i] * 2 + testcolor[1][i]) / 3; + cv[3][i] = (testcolor[0][i] + testcolor[1][i] * 2) / 3; + } + + blockerrlin[0][0] = 0; + blockerrlin[0][1] = 0; + blockerrlin[0][2] = 0; + blockerrlin[1][0] = 0; + blockerrlin[1][1] = 0; + blockerrlin[1][2] = 0; + + nrcolor[0] = 0; + nrcolor[1] = 0; + + for (j = 0; j < numypixels; j++) { + for (i = 0; i < numxpixels; i++) { + pixerrorbest = 0xffffffff; + for (colors = 0; colors < 4; colors++) { + colordist = srccolors[j][i][0] - (cv[colors][0]); + pixerror = colordist * colordist * REDWEIGHT; + pixerrorred = colordist; + colordist = srccolors[j][i][1] - (cv[colors][1]); + pixerror += colordist * colordist * GREENWEIGHT; + pixerrorgreen = colordist; + colordist = srccolors[j][i][2] - (cv[colors][2]); + pixerror += colordist * colordist * BLUEWEIGHT; + pixerrorblue = colordist; + if (pixerror < pixerrorbest) { + enc = colors; + pixerrorbest = pixerror; + pixerrorcolorbest[0] = pixerrorred; + pixerrorcolorbest[1] = pixerrorgreen; + pixerrorcolorbest[2] = pixerrorblue; + } + } + if (enc == 0) { + for (z = 0; z < 3; z++) { + blockerrlin[0][z] += 3 * pixerrorcolorbest[z]; + } + nrcolor[0] += 3; + } + else if (enc == 2) { + for (z = 0; z < 3; z++) { + blockerrlin[0][z] += 2 * pixerrorcolorbest[z]; + } + nrcolor[0] += 2; + for (z = 0; z < 3; z++) { + blockerrlin[1][z] += 1 * pixerrorcolorbest[z]; + } + nrcolor[1] += 1; + } + else if (enc == 3) { + for (z = 0; z < 3; z++) { + blockerrlin[0][z] += 1 * pixerrorcolorbest[z]; + } + nrcolor[0] += 1; + for (z = 0; z < 3; z++) { + blockerrlin[1][z] += 2 * pixerrorcolorbest[z]; + } + nrcolor[1] += 2; + } + else if (enc == 1) { + for (z = 0; z < 3; z++) { + blockerrlin[1][z] += 3 * pixerrorcolorbest[z]; + } + nrcolor[1] += 3; + } + } + } + if (nrcolor[0] == 0) nrcolor[0] = 1; + if (nrcolor[1] == 0) nrcolor[1] = 1; + for (j = 0; j < 2; j++) { + for (i = 0; i < 3; i++) { + GLint newvalue = testcolor[j][i] + blockerrlin[j][i] / nrcolor[j]; + if (newvalue <= 0) + testcolor[j][i] = 0; + else if (newvalue >= 255) + testcolor[j][i] = 255; + else testcolor[j][i] = newvalue; + } + } + + if ((abs(testcolor[0][0] - testcolor[1][0]) < 8) && + (abs(testcolor[0][1] - testcolor[1][1]) < 4) && + (abs(testcolor[0][2] - testcolor[1][2]) < 8)) { + /* both colors are so close they might get encoded as the same 16bit values */ + GLubyte coldiffred, coldiffgreen, coldiffblue, coldiffmax, factor, ind0, ind1; + + coldiffred = abs(testcolor[0][0] - testcolor[1][0]); + coldiffgreen = 2 * abs(testcolor[0][1] - testcolor[1][1]); + coldiffblue = abs(testcolor[0][2] - testcolor[1][2]); + coldiffmax = coldiffred; + if (coldiffmax < coldiffgreen) coldiffmax = coldiffgreen; + if (coldiffmax < coldiffblue) coldiffmax = coldiffblue; + if (coldiffmax > 0) { + if (coldiffmax > 4) factor = 2; + else if (coldiffmax > 2) factor = 3; + else factor = 4; + /* Won't do much if the color value is near 255... */ + /* argh so many ifs */ + if (testcolor[1][1] >= testcolor[0][1]) { + ind1 = 1; ind0 = 0; + } + else { + ind1 = 0; ind0 = 1; + } + if ((testcolor[ind1][1] + factor * coldiffgreen) <= 255) + testcolor[ind1][1] += factor * coldiffgreen; + else testcolor[ind1][1] = 255; + if ((testcolor[ind1][0] - testcolor[ind0][1]) > 0) { + if ((testcolor[ind1][0] + factor * coldiffred) <= 255) + testcolor[ind1][0] += factor * coldiffred; + else testcolor[ind1][0] = 255; + } + else { + if ((testcolor[ind0][0] + factor * coldiffred) <= 255) + testcolor[ind0][0] += factor * coldiffred; + else testcolor[ind0][0] = 255; + } + if ((testcolor[ind1][2] - testcolor[ind0][2]) > 0) { + if ((testcolor[ind1][2] + factor * coldiffblue) <= 255) + testcolor[ind1][2] += factor * coldiffblue; + else testcolor[ind1][2] = 255; + } + else { + if ((testcolor[ind0][2] + factor * coldiffblue) <= 255) + testcolor[ind0][2] += factor * coldiffblue; + else testcolor[ind0][2] = 255; + } + } + } + + if (((testcolor[0][0] & 0xf8) << 8 | (testcolor[0][1] & 0xfc) << 3 | testcolor[0][2] >> 3) < + ((testcolor[1][0] & 0xf8) << 8 | (testcolor[1][1] & 0xfc) << 3 | testcolor[1][2]) >> 3) { + for (i = 0; i < 3; i++) { + bestcolor[0][i] = testcolor[0][i]; + bestcolor[1][i] = testcolor[1][i]; + } + } + else { + for (i = 0; i < 3; i++) { + bestcolor[0][i] = testcolor[1][i]; + bestcolor[1][i] = testcolor[0][i]; + } + } + +/* fprintf(stderr, "color end 0 r/g/b %d/%d/%d, 1 r/g/b %d/%d/%d\n", + bestcolor[0][0], bestcolor[0][1], bestcolor[0][2], bestcolor[1][0], bestcolor[1][1], bestcolor[1][2]);*/ +} + + + +static void storedxtencodedblock( GLubyte *blkaddr, GLubyte srccolors[4][4][4], GLubyte *bestcolor[2], + GLint numxpixels, GLint numypixels, GLuint type, GLboolean haveAlpha) +{ + /* use same luminance-weighted distance metric to determine encoding as for finding the base colors */ + + GLint i, j, colors; + GLuint testerror, testerror2, pixerror, pixerrorbest; + GLint colordist; + GLushort color0, color1, tempcolor; + GLuint bits = 0, bits2 = 0; + GLubyte *colorptr; + GLubyte enc = 0; + GLubyte cv[4][4]; + + bestcolor[0][0] = bestcolor[0][0] & 0xf8; + bestcolor[0][1] = bestcolor[0][1] & 0xfc; + bestcolor[0][2] = bestcolor[0][2] & 0xf8; + bestcolor[1][0] = bestcolor[1][0] & 0xf8; + bestcolor[1][1] = bestcolor[1][1] & 0xfc; + bestcolor[1][2] = bestcolor[1][2] & 0xf8; + + color0 = bestcolor[0][0] << 8 | bestcolor[0][1] << 3 | bestcolor[0][2] >> 3; + color1 = bestcolor[1][0] << 8 | bestcolor[1][1] << 3 | bestcolor[1][2] >> 3; + if (color0 < color1) { + tempcolor = color0; color0 = color1; color1 = tempcolor; + colorptr = bestcolor[0]; bestcolor[0] = bestcolor[1]; bestcolor[1] = colorptr; + } + + + for (i = 0; i < 3; i++) { + cv[0][i] = bestcolor[0][i]; + cv[1][i] = bestcolor[1][i]; + cv[2][i] = (bestcolor[0][i] * 2 + bestcolor[1][i]) / 3; + cv[3][i] = (bestcolor[0][i] + bestcolor[1][i] * 2) / 3; + } + + testerror = 0; + for (j = 0; j < numypixels; j++) { + for (i = 0; i < numxpixels; i++) { + pixerrorbest = 0xffffffff; + for (colors = 0; colors < 4; colors++) { + colordist = srccolors[j][i][0] - cv[colors][0]; + pixerror = colordist * colordist * REDWEIGHT; + colordist = srccolors[j][i][1] - cv[colors][1]; + pixerror += colordist * colordist * GREENWEIGHT; + colordist = srccolors[j][i][2] - cv[colors][2]; + pixerror += colordist * colordist * BLUEWEIGHT; + if (pixerror < pixerrorbest) { + pixerrorbest = pixerror; + enc = colors; + } + } + testerror += pixerrorbest; + bits |= enc << (2 * (j * 4 + i)); + } + } + /* some hw might disagree but actually decoding should always use 4-color encoding + for non-dxt1 formats */ + if (type == GL_COMPRESSED_RGB_S3TC_DXT1_EXT || type == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) { + for (i = 0; i < 3; i++) { + cv[2][i] = (bestcolor[0][i] + bestcolor[1][i]) / 2; + /* this isn't used. Looks like the black color constant can only be used + with RGB_DXT1 if I read the spec correctly (note though that the radeon gpu disagrees, + it will decode 3 to black even with DXT3/5), and due to how the color searching works + it won't get used even then */ + cv[3][i] = 0; + } + testerror2 = 0; + for (j = 0; j < numypixels; j++) { + for (i = 0; i < numxpixels; i++) { + pixerrorbest = 0xffffffff; + if ((type == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) && (srccolors[j][i][3] <= ALPHACUT)) { + enc = 3; + pixerrorbest = 0; /* don't calculate error */ + } + else { + /* we're calculating the same what we have done already for colors 0-1 above... */ + for (colors = 0; colors < 3; colors++) { + colordist = srccolors[j][i][0] - cv[colors][0]; + pixerror = colordist * colordist * REDWEIGHT; + colordist = srccolors[j][i][1] - cv[colors][1]; + pixerror += colordist * colordist * GREENWEIGHT; + colordist = srccolors[j][i][2] - cv[colors][2]; + pixerror += colordist * colordist * BLUEWEIGHT; + if (pixerror < pixerrorbest) { + pixerrorbest = pixerror; + /* need to exchange colors later */ + if (colors > 1) enc = colors; + else enc = colors ^ 1; + } + } + } + testerror2 += pixerrorbest; + bits2 |= enc << (2 * (j * 4 + i)); + } + } + } else { + testerror2 = 0xffffffff; + } + + /* finally we're finished, write back colors and bits */ + if ((testerror > testerror2) || (haveAlpha)) { + *blkaddr++ = color1 & 0xff; + *blkaddr++ = color1 >> 8; + *blkaddr++ = color0 & 0xff; + *blkaddr++ = color0 >> 8; + *blkaddr++ = bits2 & 0xff; + *blkaddr++ = ( bits2 >> 8) & 0xff; + *blkaddr++ = ( bits2 >> 16) & 0xff; + *blkaddr = bits2 >> 24; + } + else { + *blkaddr++ = color0 & 0xff; + *blkaddr++ = color0 >> 8; + *blkaddr++ = color1 & 0xff; + *blkaddr++ = color1 >> 8; + *blkaddr++ = bits & 0xff; + *blkaddr++ = ( bits >> 8) & 0xff; + *blkaddr++ = ( bits >> 16) & 0xff; + *blkaddr = bits >> 24; + } +} + +static void encodedxtcolorblockfaster( GLubyte *blkaddr, GLubyte srccolors[4][4][4], + GLint numxpixels, GLint numypixels, GLuint type ) +{ +/* simplistic approach. We need two base colors, simply use the "highest" and the "lowest" color + present in the picture as base colors */ + + /* define lowest and highest color as shortest and longest vector to 0/0/0, though the + vectors are weighted similar to their importance in rgb-luminance conversion + doesn't work too well though... + This seems to be a rather difficult problem */ + + GLubyte *bestcolor[2]; + GLubyte basecolors[2][3]; + GLubyte i, j; + GLuint lowcv, highcv, testcv; + GLboolean haveAlpha = GL_FALSE; + + lowcv = highcv = srccolors[0][0][0] * srccolors[0][0][0] * REDWEIGHT + + srccolors[0][0][1] * srccolors[0][0][1] * GREENWEIGHT + + srccolors[0][0][2] * srccolors[0][0][2] * BLUEWEIGHT; + bestcolor[0] = bestcolor[1] = srccolors[0][0]; + for (j = 0; j < numypixels; j++) { + for (i = 0; i < numxpixels; i++) { + /* don't use this as a base color if the pixel will get black/transparent anyway */ + if ((type != GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) || (srccolors[j][i][3] > ALPHACUT)) { + testcv = srccolors[j][i][0] * srccolors[j][i][0] * REDWEIGHT + + srccolors[j][i][1] * srccolors[j][i][1] * GREENWEIGHT + + srccolors[j][i][2] * srccolors[j][i][2] * BLUEWEIGHT; + if (testcv > highcv) { + highcv = testcv; + bestcolor[1] = srccolors[j][i]; + } + else if (testcv < lowcv) { + lowcv = testcv; + bestcolor[0] = srccolors[j][i]; + } + } + else haveAlpha = GL_TRUE; + } + } + /* make sure the original color values won't get touched... */ + for (j = 0; j < 2; j++) { + for (i = 0; i < 3; i++) { + basecolors[j][i] = bestcolor[j][i]; + } + } + bestcolor[0] = basecolors[0]; + bestcolor[1] = basecolors[1]; + + /* try to find better base colors */ + fancybasecolorsearch(blkaddr, srccolors, bestcolor, numxpixels, numypixels, type, haveAlpha); + /* find the best encoding for these colors, and store the result */ + storedxtencodedblock(blkaddr, srccolors, bestcolor, numxpixels, numypixels, type, haveAlpha); +} + +static void writedxt5encodedalphablock( GLubyte *blkaddr, GLubyte alphabase1, GLubyte alphabase2, + GLubyte alphaenc[16]) +{ + *blkaddr++ = alphabase1; + *blkaddr++ = alphabase2; + *blkaddr++ = alphaenc[0] | (alphaenc[1] << 3) | ((alphaenc[2] & 3) << 6); + *blkaddr++ = (alphaenc[2] >> 2) | (alphaenc[3] << 1) | (alphaenc[4] << 4) | ((alphaenc[5] & 1) << 7); + *blkaddr++ = (alphaenc[5] >> 1) | (alphaenc[6] << 2) | (alphaenc[7] << 5); + *blkaddr++ = alphaenc[8] | (alphaenc[9] << 3) | ((alphaenc[10] & 3) << 6); + *blkaddr++ = (alphaenc[10] >> 2) | (alphaenc[11] << 1) | (alphaenc[12] << 4) | ((alphaenc[13] & 1) << 7); + *blkaddr++ = (alphaenc[13] >> 1) | (alphaenc[14] << 2) | (alphaenc[15] << 5); +} + +static void encodedxt5alpha(GLubyte *blkaddr, GLubyte srccolors[4][4][4], + GLint numxpixels, GLint numypixels) +{ + GLubyte alphabase[2], alphause[2]; + GLshort alphatest[2]; + GLuint alphablockerror1, alphablockerror2, alphablockerror3; + GLubyte i, j, aindex, acutValues[7]; + GLubyte alphaenc1[16], alphaenc2[16], alphaenc3[16]; + GLboolean alphaabsmin = GL_FALSE; + GLboolean alphaabsmax = GL_FALSE; + GLshort alphadist; + + /* find lowest and highest alpha value in block, alphabase[0] lowest, alphabase[1] highest */ + alphabase[0] = 0xff; alphabase[1] = 0x0; + for (j = 0; j < numypixels; j++) { + for (i = 0; i < numxpixels; i++) { + if (srccolors[j][i][3] == 0) + alphaabsmin = GL_TRUE; + else if (srccolors[j][i][3] == 255) + alphaabsmax = GL_TRUE; + else { + if (srccolors[j][i][3] > alphabase[1]) + alphabase[1] = srccolors[j][i][3]; + if (srccolors[j][i][3] < alphabase[0]) + alphabase[0] = srccolors[j][i][3]; + } + } + } + + + if ((alphabase[0] > alphabase[1]) && !(alphaabsmin && alphaabsmax)) { /* one color, either max or min */ + /* shortcut here since it is a very common case (and also avoids later problems) */ + /* || (alphabase[0] == alphabase[1] && !alphaabsmin && !alphaabsmax) */ + /* could also thest for alpha0 == alpha1 (and not min/max), but probably not common, so don't bother */ + + *blkaddr++ = srccolors[0][0][3]; + blkaddr++; + *blkaddr++ = 0; + *blkaddr++ = 0; + *blkaddr++ = 0; + *blkaddr++ = 0; + *blkaddr++ = 0; + *blkaddr++ = 0; +/* fprintf(stderr, "enc0 used\n");*/ + return; + } + + /* find best encoding for alpha0 > alpha1 */ + /* it's possible this encoding is better even if both alphaabsmin and alphaabsmax are true */ + alphablockerror1 = 0x0; + alphablockerror2 = 0xffffffff; + alphablockerror3 = 0xffffffff; + if (alphaabsmin) alphause[0] = 0; + else alphause[0] = alphabase[0]; + if (alphaabsmax) alphause[1] = 255; + else alphause[1] = alphabase[1]; + /* calculate the 7 cut values, just the middle between 2 of the computed alpha values */ + for (aindex = 0; aindex < 7; aindex++) { + /* don't forget here is always rounded down */ + acutValues[aindex] = (alphause[0] * (2*aindex + 1) + alphause[1] * (14 - (2*aindex + 1))) / 14; + } + + for (j = 0; j < numypixels; j++) { + for (i = 0; i < numxpixels; i++) { + /* maybe it's overkill to have the most complicated calculation just for the error + calculation which we only need to figure out if encoding1 or encoding2 is better... */ + if (srccolors[j][i][3] > acutValues[0]) { + alphaenc1[4*j + i] = 0; + alphadist = srccolors[j][i][3] - alphause[1]; + } + else if (srccolors[j][i][3] > acutValues[1]) { + alphaenc1[4*j + i] = 2; + alphadist = srccolors[j][i][3] - (alphause[1] * 6 + alphause[0] * 1) / 7; + } + else if (srccolors[j][i][3] > acutValues[2]) { + alphaenc1[4*j + i] = 3; + alphadist = srccolors[j][i][3] - (alphause[1] * 5 + alphause[0] * 2) / 7; + } + else if (srccolors[j][i][3] > acutValues[3]) { + alphaenc1[4*j + i] = 4; + alphadist = srccolors[j][i][3] - (alphause[1] * 4 + alphause[0] * 3) / 7; + } + else if (srccolors[j][i][3] > acutValues[4]) { + alphaenc1[4*j + i] = 5; + alphadist = srccolors[j][i][3] - (alphause[1] * 3 + alphause[0] * 4) / 7; + } + else if (srccolors[j][i][3] > acutValues[5]) { + alphaenc1[4*j + i] = 6; + alphadist = srccolors[j][i][3] - (alphause[1] * 2 + alphause[0] * 5) / 7; + } + else if (srccolors[j][i][3] > acutValues[6]) { + alphaenc1[4*j + i] = 7; + alphadist = srccolors[j][i][3] - (alphause[1] * 1 + alphause[0] * 6) / 7; + } + else { + alphaenc1[4*j + i] = 1; + alphadist = srccolors[j][i][3] - alphause[0]; + } + alphablockerror1 += alphadist * alphadist; + } + } +/* for (i = 0; i < 16; i++) { + fprintf(stderr, "%d ", alphaenc1[i]); + } + fprintf(stderr, "cutVals "); + for (i = 0; i < 8; i++) { + fprintf(stderr, "%d ", acutValues[i]); + } + fprintf(stderr, "srcVals "); + for (j = 0; j < numypixels; j++) + for (i = 0; i < numxpixels; i++) { + fprintf(stderr, "%d ", srccolors[j][i][3]); + } + + fprintf(stderr, "\n"); + }*/ + /* it's not very likely this encoding is better if both alphaabsmin and alphaabsmax + are false but try it anyway */ + if (alphablockerror1 >= 32) { + + /* don't bother if encoding is already very good, this condition should also imply + we have valid alphabase colors which we absolutely need (alphabase[0] <= alphabase[1]) */ + alphablockerror2 = 0; + for (aindex = 0; aindex < 5; aindex++) { + /* don't forget here is always rounded down */ + acutValues[aindex] = (alphabase[0] * (10 - (2*aindex + 1)) + alphabase[1] * (2*aindex + 1)) / 10; + } + for (j = 0; j < numypixels; j++) { + for (i = 0; i < numxpixels; i++) { + /* maybe it's overkill to have the most complicated calculation just for the error + calculation which we only need to figure out if encoding1 or encoding2 is better... */ + if (srccolors[j][i][3] == 0) { + alphaenc2[4*j + i] = 6; + alphadist = 0; + } + else if (srccolors[j][i][3] == 255) { + alphaenc2[4*j + i] = 7; + alphadist = 0; + } + else if (srccolors[j][i][3] <= acutValues[0]) { + alphaenc2[4*j + i] = 0; + alphadist = srccolors[j][i][3] - alphabase[0]; + } + else if (srccolors[j][i][3] <= acutValues[1]) { + alphaenc2[4*j + i] = 2; + alphadist = srccolors[j][i][3] - (alphabase[0] * 4 + alphabase[1] * 1) / 5; + } + else if (srccolors[j][i][3] <= acutValues[2]) { + alphaenc2[4*j + i] = 3; + alphadist = srccolors[j][i][3] - (alphabase[0] * 3 + alphabase[1] * 2) / 5; + } + else if (srccolors[j][i][3] <= acutValues[3]) { + alphaenc2[4*j + i] = 4; + alphadist = srccolors[j][i][3] - (alphabase[0] * 2 + alphabase[1] * 3) / 5; + } + else if (srccolors[j][i][3] <= acutValues[4]) { + alphaenc2[4*j + i] = 5; + alphadist = srccolors[j][i][3] - (alphabase[0] * 1 + alphabase[1] * 4) / 5; + } + else { + alphaenc2[4*j + i] = 1; + alphadist = srccolors[j][i][3] - alphabase[1]; + } + alphablockerror2 += alphadist * alphadist; + } + } + + + /* skip this if the error is already very small + this encoding is MUCH better on average than #2 though, but expensive! */ + if ((alphablockerror2 > 96) && (alphablockerror1 > 96)) { + GLshort blockerrlin1 = 0; + GLshort blockerrlin2 = 0; + GLubyte nralphainrangelow = 0; + GLubyte nralphainrangehigh = 0; + alphatest[0] = 0xff; + alphatest[1] = 0x0; + /* if we have large range it's likely there are values close to 0/255, try to map them to 0/255 */ + for (j = 0; j < numypixels; j++) { + for (i = 0; i < numxpixels; i++) { + if ((srccolors[j][i][3] > alphatest[1]) && (srccolors[j][i][3] < (255 -(alphabase[1] - alphabase[0]) / 28))) + alphatest[1] = srccolors[j][i][3]; + if ((srccolors[j][i][3] < alphatest[0]) && (srccolors[j][i][3] > (alphabase[1] - alphabase[0]) / 28)) + alphatest[0] = srccolors[j][i][3]; + } + } + /* shouldn't happen too often, don't really care about those degenerated cases */ + if (alphatest[1] <= alphatest[0]) { + alphatest[0] = 1; + alphatest[1] = 254; +/* fprintf(stderr, "only 1 or 0 colors for encoding!\n");*/ + } + for (aindex = 0; aindex < 5; aindex++) { + /* don't forget here is always rounded down */ + acutValues[aindex] = (alphatest[0] * (10 - (2*aindex + 1)) + alphatest[1] * (2*aindex + 1)) / 10; + } + + /* find the "average" difference between the alpha values and the next encoded value. + This is then used to calculate new base values. + Should there be some weighting, i.e. those values closer to alphatest[x] have more weight, + since they will see more improvement, and also because the values in the middle are somewhat + likely to get no improvement at all (because the base values might move in different directions)? + OTOH it would mean the values in the middle are even less likely to get an improvement + */ + for (j = 0; j < numypixels; j++) { + for (i = 0; i < numxpixels; i++) { + if (srccolors[j][i][3] <= alphatest[0] / 2) { + } + else if (srccolors[j][i][3] > ((255 + alphatest[1]) / 2)) { + } + else if (srccolors[j][i][3] <= acutValues[0]) { + blockerrlin1 += (srccolors[j][i][3] - alphatest[0]); + nralphainrangelow += 1; + } + else if (srccolors[j][i][3] <= acutValues[1]) { + blockerrlin1 += (srccolors[j][i][3] - (alphatest[0] * 4 + alphatest[1] * 1) / 5); + blockerrlin2 += (srccolors[j][i][3] - (alphatest[0] * 4 + alphatest[1] * 1) / 5); + nralphainrangelow += 1; + nralphainrangehigh += 1; + } + else if (srccolors[j][i][3] <= acutValues[2]) { + blockerrlin1 += (srccolors[j][i][3] - (alphatest[0] * 3 + alphatest[1] * 2) / 5); + blockerrlin2 += (srccolors[j][i][3] - (alphatest[0] * 3 + alphatest[1] * 2) / 5); + nralphainrangelow += 1; + nralphainrangehigh += 1; + } + else if (srccolors[j][i][3] <= acutValues[3]) { + blockerrlin1 += (srccolors[j][i][3] - (alphatest[0] * 2 + alphatest[1] * 3) / 5); + blockerrlin2 += (srccolors[j][i][3] - (alphatest[0] * 2 + alphatest[1] * 3) / 5); + nralphainrangelow += 1; + nralphainrangehigh += 1; + } + else if (srccolors[j][i][3] <= acutValues[4]) { + blockerrlin1 += (srccolors[j][i][3] - (alphatest[0] * 1 + alphatest[1] * 4) / 5); + blockerrlin2 += (srccolors[j][i][3] - (alphatest[0] * 1 + alphatest[1] * 4) / 5); + nralphainrangelow += 1; + nralphainrangehigh += 1; + } + else { + blockerrlin2 += (srccolors[j][i][3] - alphatest[1]); + nralphainrangehigh += 1; + } + } + } + /* shouldn't happen often, needed to avoid div by zero */ + if (nralphainrangelow == 0) nralphainrangelow = 1; + if (nralphainrangehigh == 0) nralphainrangehigh = 1; + alphatest[0] = alphatest[0] + (blockerrlin1 / nralphainrangelow); +/* fprintf(stderr, "block err lin low %d, nr %d\n", blockerrlin1, nralphainrangelow); + fprintf(stderr, "block err lin high %d, nr %d\n", blockerrlin2, nralphainrangehigh);*/ + /* again shouldn't really happen often... */ + if (alphatest[0] < 0) { + alphatest[0] = 0; +/* fprintf(stderr, "adj alpha base val to 0\n");*/ + } + alphatest[1] = alphatest[1] + (blockerrlin2 / nralphainrangehigh); + if (alphatest[1] > 255) { + alphatest[1] = 255; +/* fprintf(stderr, "adj alpha base val to 255\n");*/ + } + + alphablockerror3 = 0; + for (aindex = 0; aindex < 5; aindex++) { + /* don't forget here is always rounded down */ + acutValues[aindex] = (alphatest[0] * (10 - (2*aindex + 1)) + alphatest[1] * (2*aindex + 1)) / 10; + } + for (j = 0; j < numypixels; j++) { + for (i = 0; i < numxpixels; i++) { + /* maybe it's overkill to have the most complicated calculation just for the error + calculation which we only need to figure out if encoding1 or encoding2 is better... */ + if (srccolors[j][i][3] <= alphatest[0] / 2) { + alphaenc3[4*j + i] = 6; + alphadist = srccolors[j][i][3]; + } + else if (srccolors[j][i][3] > ((255 + alphatest[1]) / 2)) { + alphaenc3[4*j + i] = 7; + alphadist = 255 - srccolors[j][i][3]; + } + else if (srccolors[j][i][3] <= acutValues[0]) { + alphaenc3[4*j + i] = 0; + alphadist = srccolors[j][i][3] - alphatest[0]; + } + else if (srccolors[j][i][3] <= acutValues[1]) { + alphaenc3[4*j + i] = 2; + alphadist = srccolors[j][i][3] - (alphatest[0] * 4 + alphatest[1] * 1) / 5; + } + else if (srccolors[j][i][3] <= acutValues[2]) { + alphaenc3[4*j + i] = 3; + alphadist = srccolors[j][i][3] - (alphatest[0] * 3 + alphatest[1] * 2) / 5; + } + else if (srccolors[j][i][3] <= acutValues[3]) { + alphaenc3[4*j + i] = 4; + alphadist = srccolors[j][i][3] - (alphatest[0] * 2 + alphatest[1] * 3) / 5; + } + else if (srccolors[j][i][3] <= acutValues[4]) { + alphaenc3[4*j + i] = 5; + alphadist = srccolors[j][i][3] - (alphatest[0] * 1 + alphatest[1] * 4) / 5; + } + else { + alphaenc3[4*j + i] = 1; + alphadist = srccolors[j][i][3] - alphatest[1]; + } + alphablockerror3 += alphadist * alphadist; + } + } + } + } + /* write the alpha values and encoding back. */ + if ((alphablockerror1 <= alphablockerror2) && (alphablockerror1 <= alphablockerror3)) { +/* if (alphablockerror1 > 96) fprintf(stderr, "enc1 used, error %d\n", alphablockerror1);*/ + writedxt5encodedalphablock( blkaddr, alphause[1], alphause[0], alphaenc1 ); + } + else if (alphablockerror2 <= alphablockerror3) { +/* if (alphablockerror2 > 96) fprintf(stderr, "enc2 used, error %d\n", alphablockerror2);*/ + writedxt5encodedalphablock( blkaddr, alphabase[0], alphabase[1], alphaenc2 ); + } + else { +/* fprintf(stderr, "enc3 used, error %d\n", alphablockerror3);*/ + writedxt5encodedalphablock( blkaddr, (GLubyte)alphatest[0], (GLubyte)alphatest[1], alphaenc3 ); + } +} + +static void extractsrccolors( GLubyte srcpixels[4][4][4], const GLchan *srcaddr, + GLint srcRowStride, GLint numxpixels, GLint numypixels, GLint comps) +{ + GLubyte i, j, c; + const GLchan *curaddr; + for (j = 0; j < numypixels; j++) { + curaddr = srcaddr + j * srcRowStride * comps; + for (i = 0; i < numxpixels; i++) { + for (c = 0; c < comps; c++) { + srcpixels[j][i][c] = *curaddr++ / (CHAN_MAX / 255); + } + } + } +} + + +static void tx_compress_dxtn(GLint srccomps, GLint width, GLint height, const GLubyte *srcPixData, + GLenum destFormat, GLubyte *dest, GLint dstRowStride) +{ + GLubyte *blkaddr = dest; + GLubyte srcpixels[4][4][4]; + const GLchan *srcaddr = srcPixData; + GLint numxpixels, numypixels; + GLint i, j; + GLint dstRowDiff; + + switch (destFormat) { + case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: + case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: + /* hmm we used to get called without dstRowStride... */ + dstRowDiff = dstRowStride >= (width * 2) ? dstRowStride - (((width + 3) & ~3) * 2) : 0; +/* fprintf(stderr, "dxt1 tex width %d tex height %d dstRowStride %d\n", + width, height, dstRowStride); */ + for (j = 0; j < height; j += 4) { + if (height > j + 3) numypixels = 4; + else numypixels = height - j; + srcaddr = srcPixData + j * width * srccomps; + for (i = 0; i < width; i += 4) { + if (width > i + 3) numxpixels = 4; + else numxpixels = width - i; + extractsrccolors(srcpixels, srcaddr, width, numxpixels, numypixels, srccomps); + encodedxtcolorblockfaster(blkaddr, srcpixels, numxpixels, numypixels, destFormat); + srcaddr += srccomps * numxpixels; + blkaddr += 8; + } + blkaddr += dstRowDiff; + } + break; + case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: + dstRowDiff = dstRowStride >= (width * 4) ? dstRowStride - (((width + 3) & ~3) * 4) : 0; +/* fprintf(stderr, "dxt3 tex width %d tex height %d dstRowStride %d\n", + width, height, dstRowStride); */ + for (j = 0; j < height; j += 4) { + if (height > j + 3) numypixels = 4; + else numypixels = height - j; + srcaddr = srcPixData + j * width * srccomps; + for (i = 0; i < width; i += 4) { + if (width > i + 3) numxpixels = 4; + else numxpixels = width - i; + extractsrccolors(srcpixels, srcaddr, width, numxpixels, numypixels, srccomps); + *blkaddr++ = (srcpixels[0][0][3] >> 4) | (srcpixels[0][1][3] & 0xf0); + *blkaddr++ = (srcpixels[0][2][3] >> 4) | (srcpixels[0][3][3] & 0xf0); + *blkaddr++ = (srcpixels[1][0][3] >> 4) | (srcpixels[1][1][3] & 0xf0); + *blkaddr++ = (srcpixels[1][2][3] >> 4) | (srcpixels[1][3][3] & 0xf0); + *blkaddr++ = (srcpixels[2][0][3] >> 4) | (srcpixels[2][1][3] & 0xf0); + *blkaddr++ = (srcpixels[2][2][3] >> 4) | (srcpixels[2][3][3] & 0xf0); + *blkaddr++ = (srcpixels[3][0][3] >> 4) | (srcpixels[3][1][3] & 0xf0); + *blkaddr++ = (srcpixels[3][2][3] >> 4) | (srcpixels[3][3][3] & 0xf0); + encodedxtcolorblockfaster(blkaddr, srcpixels, numxpixels, numypixels, destFormat); + srcaddr += srccomps * numxpixels; + blkaddr += 8; + } + blkaddr += dstRowDiff; + } + break; + case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: + dstRowDiff = dstRowStride >= (width * 4) ? dstRowStride - (((width + 3) & ~3) * 4) : 0; +/* fprintf(stderr, "dxt5 tex width %d tex height %d dstRowStride %d\n", + width, height, dstRowStride); */ + for (j = 0; j < height; j += 4) { + if (height > j + 3) numypixels = 4; + else numypixels = height - j; + srcaddr = srcPixData + j * width * srccomps; + for (i = 0; i < width; i += 4) { + if (width > i + 3) numxpixels = 4; + else numxpixels = width - i; + extractsrccolors(srcpixels, srcaddr, width, numxpixels, numypixels, srccomps); + encodedxt5alpha(blkaddr, srcpixels, numxpixels, numypixels); + encodedxtcolorblockfaster(blkaddr + 8, srcpixels, numxpixels, numypixels, destFormat); + srcaddr += srccomps * numxpixels; + blkaddr += 16; + } + blkaddr += dstRowDiff; + } + break; + default: + fprintf(stderr, "libdxtn: Bad dstFormat %d in tx_compress_dxtn\n", destFormat); + return; + } +} diff --git a/wrappers/directx/d3dwine_wrapper/gl_compat.c b/wrappers/directx/d3dwine_wrapper/gl_compat.c new file mode 100644 index 00000000000..b8323b7e226 --- /dev/null +++ b/wrappers/directx/d3dwine_wrapper/gl_compat.c @@ -0,0 +1,541 @@ +/* + * Compatibility functions for older GL implementations + * + * Copyright 2008 Stefan Dösinger for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "config.h" +#include "wine/port.h" + +#include +#ifdef HAVE_FLOAT_H +# include +#endif + +#include "wined3d_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(gl_compat); +WINE_DECLARE_DEBUG_CHANNEL(d3d_perf); + +/* Start GL_ARB_multitexture emulation */ +static void WINE_GLAPI wine_glMultiTexCoord1fARB(GLenum target, GLfloat s) +{ + if (target != GL_TEXTURE0) + { + ERR("Texture unit > 0 used, but GL_ARB_multitexture is not supported.\n"); + return; + } + wined3d_context_gl_get_current()->gl_info->gl_ops.gl.p_glTexCoord1f(s); +} + +static void WINE_GLAPI wine_glMultiTexCoord1fvARB(GLenum target, const GLfloat *v) +{ + if (target != GL_TEXTURE0) + { + ERR("Texture unit > 0 used, but GL_ARB_multitexture is not supported.\n"); + return; + } + wined3d_context_gl_get_current()->gl_info->gl_ops.gl.p_glTexCoord1fv(v); +} + +static void WINE_GLAPI wine_glMultiTexCoord2fARB(GLenum target, GLfloat s, GLfloat t) +{ + if (target != GL_TEXTURE0) + { + ERR("Texture unit > 0 used, but GL_ARB_multitexture is not supported.\n"); + return; + } + wined3d_context_gl_get_current()->gl_info->gl_ops.gl.p_glTexCoord2f(s, t); +} + +static void WINE_GLAPI wine_glMultiTexCoord2fvARB(GLenum target, const GLfloat *v) +{ + if (target != GL_TEXTURE0) + { + ERR("Texture unit > 0 used, but GL_ARB_multitexture is not supported.\n"); + return; + } + wined3d_context_gl_get_current()->gl_info->gl_ops.gl.p_glTexCoord2fv(v); +} + +static void WINE_GLAPI wine_glMultiTexCoord3fARB(GLenum target, GLfloat s, GLfloat t, GLfloat r) +{ + if (target != GL_TEXTURE0) + { + ERR("Texture unit > 0 used, but GL_ARB_multitexture is not supported.\n"); + return; + } + wined3d_context_gl_get_current()->gl_info->gl_ops.gl.p_glTexCoord3f(s, t, r); +} + +static void WINE_GLAPI wine_glMultiTexCoord3fvARB(GLenum target, const GLfloat *v) +{ + if (target != GL_TEXTURE0) + { + ERR("Texture unit > 0 used, but GL_ARB_multitexture is not supported.\n"); + return; + } + wined3d_context_gl_get_current()->gl_info->gl_ops.gl.p_glTexCoord3fv(v); +} + +static void WINE_GLAPI wine_glMultiTexCoord4fARB(GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q) +{ + if (target != GL_TEXTURE0) + { + ERR("Texture unit > 0 used, but GL_ARB_multitexture is not supported.\n"); + return; + } + wined3d_context_gl_get_current()->gl_info->gl_ops.gl.p_glTexCoord4f(s, t, r, q); +} + +static void WINE_GLAPI wine_glMultiTexCoord4fvARB(GLenum target, const GLfloat *v) +{ + if (target != GL_TEXTURE0) + { + ERR("Texture unit > 0 used, but GL_ARB_multitexture is not supported.\n"); + return; + } + wined3d_context_gl_get_current()->gl_info->gl_ops.gl.p_glTexCoord4fv(v); +} + +static void WINE_GLAPI wine_glMultiTexCoord2svARB(GLenum target, const GLshort *v) +{ + if (target != GL_TEXTURE0) + { + ERR("Texture unit > 0 used, but GL_ARB_multitexture is not supported.\n"); + return; + } + wined3d_context_gl_get_current()->gl_info->gl_ops.gl.p_glTexCoord2sv(v); +} + +static void WINE_GLAPI wine_glMultiTexCoord4svARB(GLenum target, const GLshort *v) +{ + if (target != GL_TEXTURE0) + { + ERR("Texture unit > 0 used, but GL_ARB_multitexture is not supported.\n"); + return; + } + wined3d_context_gl_get_current()->gl_info->gl_ops.gl.p_glTexCoord4sv(v); +} + +static void WINE_GLAPI wine_glActiveTexture(GLenum texture) +{ + if(texture != GL_TEXTURE0) { + ERR("Texture unit > 0 used, but GL_ARB_multitexture is not supported\n"); + return; + } +} + +static void WINE_GLAPI wine_glClientActiveTextureARB(GLenum texture) { + if(texture != GL_TEXTURE0) { + ERR("Texture unit > 0 used, but GL_ARB_multitexture is not supported\n"); + return; + } +} + +static void (WINE_GLAPI *old_multitex_glGetIntegerv) (GLenum pname, GLint* params) = NULL; +static void WINE_GLAPI wine_glGetIntegerv(GLenum pname, GLint* params) { + switch(pname) { + case GL_ACTIVE_TEXTURE: *params = 0; break; + case GL_MAX_TEXTURE_UNITS_ARB: *params = 1; break; + default: old_multitex_glGetIntegerv(pname, params); + } +} + +static void (WINE_GLAPI *old_multitex_glGetFloatv) (GLenum pname, GLfloat* params) = NULL; +static void WINE_GLAPI wine_glGetFloatv(GLenum pname, GLfloat* params) { + if (pname == GL_ACTIVE_TEXTURE) *params = 0.0f; + else old_multitex_glGetFloatv(pname, params); +} + +static void (WINE_GLAPI *old_multitex_glGetDoublev) (GLenum pname, GLdouble* params) = NULL; +static void WINE_GLAPI wine_glGetDoublev(GLenum pname, GLdouble* params) { + if(pname == GL_ACTIVE_TEXTURE) *params = 0.0; + else old_multitex_glGetDoublev(pname, params); +} + +/* Start GL_EXT_fogcoord emulation */ +static void (WINE_GLAPI *old_fogcoord_glEnable)(GLenum cap); +static void WINE_GLAPI wine_glEnable(GLenum cap) +{ + if (cap == GL_FOG) + { + struct wined3d_context_gl *ctx = wined3d_context_gl_get_current(); + + ctx->fog_enabled = 1; + if (ctx->gl_fog_source != GL_FRAGMENT_DEPTH_EXT) + return; + } + old_fogcoord_glEnable(cap); +} + +static void (WINE_GLAPI *old_fogcoord_glDisable)(GLenum cap); +static void WINE_GLAPI wine_glDisable(GLenum cap) +{ + if (cap == GL_FOG) + { + struct wined3d_context_gl *ctx = wined3d_context_gl_get_current(); + + ctx->fog_enabled = 0; + if (ctx->gl_fog_source != GL_FRAGMENT_DEPTH_EXT) + return; + } + old_fogcoord_glDisable(cap); +} + +static void (WINE_GLAPI *old_fogcoord_glFogi)(GLenum pname, GLint param); +static void WINE_GLAPI wine_glFogi(GLenum pname, GLint param) +{ + struct wined3d_context_gl *ctx = wined3d_context_gl_get_current(); + + if (pname == GL_FOG_COORDINATE_SOURCE_EXT) + { + ctx->gl_fog_source = param; + if (param == GL_FRAGMENT_DEPTH_EXT) + { + if (ctx->fog_enabled) + old_fogcoord_glEnable(GL_FOG); + } + else + { + WARN_(d3d_perf)("Fog coordinates activated, but not supported. Using slow emulation.\n"); + old_fogcoord_glDisable(GL_FOG); + } + } + else + { + if (pname == GL_FOG_START) + ctx->fog_start = (float)param; + else if (pname == GL_FOG_END) + ctx->fog_end = (float)param; + old_fogcoord_glFogi(pname, param); + } +} + +static void (WINE_GLAPI *old_fogcoord_glFogiv)(GLenum pname, const GLint *param); +static void WINE_GLAPI wine_glFogiv(GLenum pname, const GLint *param) +{ + struct wined3d_context_gl *ctx = wined3d_context_gl_get_current(); + + if (pname == GL_FOG_COORDINATE_SOURCE_EXT) + { + ctx->gl_fog_source = *param; + if (*param == GL_FRAGMENT_DEPTH_EXT) + { + if (ctx->fog_enabled) + old_fogcoord_glEnable(GL_FOG); + } + else + { + WARN_(d3d_perf)("Fog coordinates activated, but not supported. Using slow emulation.\n"); + old_fogcoord_glDisable(GL_FOG); + } + } + else + { + if (pname == GL_FOG_START) + ctx->fog_start = (float)*param; + else if (pname == GL_FOG_END) + ctx->fog_end = (float)*param; + old_fogcoord_glFogiv(pname, param); + } +} + +static void (WINE_GLAPI *old_fogcoord_glFogf)(GLenum pname, GLfloat param); +static void WINE_GLAPI wine_glFogf(GLenum pname, GLfloat param) +{ + struct wined3d_context_gl *ctx = wined3d_context_gl_get_current(); + + if (pname == GL_FOG_COORDINATE_SOURCE_EXT) + { + ctx->gl_fog_source = (GLint)param; + if (param == GL_FRAGMENT_DEPTH_EXT) + { + if (ctx->fog_enabled) + old_fogcoord_glEnable(GL_FOG); + } + else + { + WARN_(d3d_perf)("Fog coordinates activated, but not supported. Using slow emulation.\n"); + old_fogcoord_glDisable(GL_FOG); + } + } + else + { + if (pname == GL_FOG_START) + ctx->fog_start = param; + else if (pname == GL_FOG_END) + ctx->fog_end = param; + old_fogcoord_glFogf(pname, param); + } +} + +static void (WINE_GLAPI *old_fogcoord_glFogfv)(GLenum pname, const GLfloat *param); +static void WINE_GLAPI wine_glFogfv(GLenum pname, const GLfloat *param) +{ + struct wined3d_context_gl *ctx = wined3d_context_gl_get_current(); + + if (pname == GL_FOG_COORDINATE_SOURCE_EXT) + { + ctx->gl_fog_source = (GLint)*param; + if (*param == GL_FRAGMENT_DEPTH_EXT) + { + if (ctx->fog_enabled) + old_fogcoord_glEnable(GL_FOG); + } + else + { + WARN_(d3d_perf)("Fog coordinates activated, but not supported. Using slow emulation.\n"); + old_fogcoord_glDisable(GL_FOG); + } + } + else + { + if (pname == GL_FOG_COLOR) + { + ctx->fog_colour[0] = param[0]; + ctx->fog_colour[1] = param[1]; + ctx->fog_colour[2] = param[2]; + ctx->fog_colour[3] = param[3]; + } + else if (pname == GL_FOG_START) + { + ctx->fog_start = *param; + } + else if (pname == GL_FOG_END) + { + ctx->fog_end = *param; + } + old_fogcoord_glFogfv(pname, param); + } +} + +static void (WINE_GLAPI *old_fogcoord_glVertex4f)(GLfloat x, GLfloat y, GLfloat z, GLfloat w); +static void (WINE_GLAPI *old_fogcoord_glColor4f)(GLfloat r, GLfloat g, GLfloat b, GLfloat a); + +static void WINE_GLAPI wine_glVertex4f(GLfloat x, GLfloat y, GLfloat z, GLfloat w) +{ + const struct wined3d_context_gl *ctx_gl = wined3d_context_gl_get_current(); + + /* This can be called from draw_test_quad() and at that point there is no + * wined3d_context current. */ + if (!ctx_gl) + { + old_fogcoord_glVertex4f(x, y, z, w); + return; + } + + if (ctx_gl->gl_fog_source == GL_FOG_COORDINATE_EXT && ctx_gl->fog_enabled) + { + GLfloat c[4] = {ctx_gl->colour[0], ctx_gl->colour[1], ctx_gl->colour[2], ctx_gl->colour[3]}; + GLfloat i; + + i = (ctx_gl->fog_end - ctx_gl->fog_coord_value) / (ctx_gl->fog_end - ctx_gl->fog_start); + c[0] = i * c[0] + (1.0f - i) * ctx_gl->fog_colour[0]; + c[1] = i * c[1] + (1.0f - i) * ctx_gl->fog_colour[1]; + c[2] = i * c[2] + (1.0f - i) * ctx_gl->fog_colour[2]; + + old_fogcoord_glColor4f(c[0], c[1], c[2], c[3]); + old_fogcoord_glVertex4f(x, y, z, w); + } + else + { + old_fogcoord_glVertex4f(x, y, z, w); + } +} + +static void WINE_GLAPI wine_glVertex4fv(const GLfloat *pos) { + wine_glVertex4f(pos[0], pos[1], pos[2], pos[3]); +} + +static void WINE_GLAPI wine_glVertex3f(GLfloat x, GLfloat y, GLfloat z) { + wine_glVertex4f(x, y, z, 1.0f); +} + +static void WINE_GLAPI wine_glVertex3fv(const GLfloat *pos) { + wine_glVertex4f(pos[0], pos[1], pos[2], 1.0f); +} + +static void WINE_GLAPI wine_glColor4f(GLfloat r, GLfloat g, GLfloat b, GLfloat a) +{ + struct wined3d_context_gl *ctx_gl = wined3d_context_gl_get_current(); + + /* This can be called from draw_test_quad() and at that point there is no + * wined3d_context current. */ + if (!ctx_gl) + { + old_fogcoord_glColor4f(r, g, b, a); + return; + } + + ctx_gl->colour[0] = r; + ctx_gl->colour[1] = g; + ctx_gl->colour[2] = b; + ctx_gl->colour[3] = a; + old_fogcoord_glColor4f(r, g, b, a); +} + +static void WINE_GLAPI wine_glColor4fv(const GLfloat *c) { + wine_glColor4f(c[0], c[1], c[2], c[3]); +} + +static void WINE_GLAPI wine_glColor3f(GLfloat r, GLfloat g, GLfloat b) { + wine_glColor4f(r, g, b, 1.0f); +} + +static void WINE_GLAPI wine_glColor3fv(const GLfloat *c) { + wine_glColor4f(c[0], c[1], c[2], 1.0f); +} + +static void WINE_GLAPI wine_glColor4ub(GLubyte r, GLubyte g, GLubyte b, GLubyte a) { + wine_glColor4f(r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f); +} + +/* In D3D the fog coord is a UBYTE, so there's no problem with using the + * single precision function. */ +static void WINE_GLAPI wine_glFogCoordfEXT(GLfloat f) +{ + struct wined3d_context_gl *ctx = wined3d_context_gl_get_current(); + + ctx->fog_coord_value = f; +} +static void WINE_GLAPI wine_glFogCoorddEXT(GLdouble f) { + wine_glFogCoordfEXT((GLfloat) f); +} +static void WINE_GLAPI wine_glFogCoordfvEXT(const GLfloat *f) { + wine_glFogCoordfEXT(*f); +} +static void WINE_GLAPI wine_glFogCoorddvEXT(const GLdouble *f) { + wine_glFogCoordfEXT((GLfloat) *f); +} + +/* End GL_EXT_fog_coord emulation */ + +void install_gl_compat_wrapper(struct wined3d_gl_info *gl_info, enum wined3d_gl_extension ext) +{ + switch (ext) + { + case ARB_MULTITEXTURE: + if (gl_info->supported[ARB_MULTITEXTURE]) + return; + if (gl_info->gl_ops.ext.p_glActiveTexture == wine_glActiveTexture) + { + FIXME("ARB_multitexture emulation hooks already applied.\n"); + return; + } + TRACE("Applying GL_ARB_multitexture emulation hooks.\n"); + gl_info->gl_ops.ext.p_glActiveTexture = wine_glActiveTexture; + gl_info->gl_ops.ext.p_glClientActiveTextureARB = wine_glClientActiveTextureARB; + gl_info->gl_ops.ext.p_glMultiTexCoord1fARB = wine_glMultiTexCoord1fARB; + gl_info->gl_ops.ext.p_glMultiTexCoord1fvARB = wine_glMultiTexCoord1fvARB; + gl_info->gl_ops.ext.p_glMultiTexCoord2fARB = wine_glMultiTexCoord2fARB; + gl_info->gl_ops.ext.p_glMultiTexCoord2fvARB = wine_glMultiTexCoord2fvARB; + gl_info->gl_ops.ext.p_glMultiTexCoord3fARB = wine_glMultiTexCoord3fARB; + gl_info->gl_ops.ext.p_glMultiTexCoord3fvARB = wine_glMultiTexCoord3fvARB; + gl_info->gl_ops.ext.p_glMultiTexCoord4fARB = wine_glMultiTexCoord4fARB; + gl_info->gl_ops.ext.p_glMultiTexCoord4fvARB = wine_glMultiTexCoord4fvARB; + gl_info->gl_ops.ext.p_glMultiTexCoord2svARB = wine_glMultiTexCoord2svARB; + gl_info->gl_ops.ext.p_glMultiTexCoord4svARB = wine_glMultiTexCoord4svARB; + old_multitex_glGetIntegerv = gl_info->gl_ops.gl.p_glGetIntegerv; + gl_info->gl_ops.gl.p_glGetIntegerv = wine_glGetIntegerv; + old_multitex_glGetFloatv = gl_info->gl_ops.gl.p_glGetFloatv; + gl_info->gl_ops.gl.p_glGetFloatv = wine_glGetFloatv; + old_multitex_glGetDoublev = gl_info->gl_ops.gl.p_glGetDoublev; + gl_info->gl_ops.gl.p_glGetDoublev = wine_glGetDoublev; + gl_info->supported[ARB_MULTITEXTURE] = TRUE; + return; + + case EXT_FOG_COORD: + /* This emulation isn't perfect. There are a number of potential problems, but they should + * not matter in practise: + * + * Fog vs fragment shader: If we are using GL_ARB_fragment_program with the fog option, the + * glDisable(GL_FOG) here won't matter. However, if we have GL_ARB_fragment_program, it is pretty + * unlikely that we don't have GL_EXT_fog_coord. Besides, we probably have GL_ARB_vertex_program + * too, which would allow fog coord emulation in a fixed function vertex pipeline replacement. + * + * Fog vs texture: We apply the fog in the vertex color. An app could set up texturing settings which + * ignore the vertex color, thus effectively disabling our fog. However, in D3D this type of fog is + * a per-vertex fog too, so the apps shouldn't do that. + * + * Fog vs lighting: The app could in theory use D3DFOG_NONE table and D3DFOG_NONE vertex fog with + * untransformed vertices. That enables lighting and fog coords at the same time, and the lighting + * calculations could affect the already blended in fog color. There's nothing we can do against that, + * but most apps using fog color do their own lighting too and often even use RHW vertices. So live + * with it. + */ + if (gl_info->supported[EXT_FOG_COORD]) + return; + if (gl_info->gl_ops.gl.p_glFogi == wine_glFogi) + { + FIXME("EXT_fog_coord emulation hooks already applied.\n"); + return; + } + TRACE("Applying GL_ARB_fog_coord emulation hooks\n"); + + /* This probably means that the implementation doesn't advertise the extension, but implicitly supports + * it via the GL core version, or someone messed around in the extension table in directx.c. Add version- + * dependent loading for this extension if we ever hit this situation + */ + if (gl_info->supported[ARB_FRAGMENT_PROGRAM]) + { + FIXME("GL implementation supports GL_ARB_fragment_program but not GL_EXT_fog_coord\n"); + FIXME("The fog coord emulation will most likely fail\n"); + } + else if (gl_info->supported[ARB_FRAGMENT_SHADER]) + { + FIXME("GL implementation supports GL_ARB_fragment_shader but not GL_EXT_fog_coord\n"); + FIXME("The fog coord emulation will most likely fail\n"); + } + + old_fogcoord_glFogi = gl_info->gl_ops.gl.p_glFogi; + gl_info->gl_ops.gl.p_glFogi = wine_glFogi; + old_fogcoord_glFogiv = gl_info->gl_ops.gl.p_glFogiv; + gl_info->gl_ops.gl.p_glFogiv = wine_glFogiv; + old_fogcoord_glFogf = gl_info->gl_ops.gl.p_glFogf; + gl_info->gl_ops.gl.p_glFogf = wine_glFogf; + old_fogcoord_glFogfv = gl_info->gl_ops.gl.p_glFogfv; + gl_info->gl_ops.gl.p_glFogfv = wine_glFogfv; + old_fogcoord_glEnable = gl_info->p_glEnableWINE; + gl_info->p_glEnableWINE = wine_glEnable; + old_fogcoord_glDisable = gl_info->p_glDisableWINE; + gl_info->p_glDisableWINE = wine_glDisable; + + old_fogcoord_glVertex4f = gl_info->gl_ops.gl.p_glVertex4f; + gl_info->gl_ops.gl.p_glVertex4f = wine_glVertex4f; + gl_info->gl_ops.gl.p_glVertex4fv = wine_glVertex4fv; + gl_info->gl_ops.gl.p_glVertex3f = wine_glVertex3f; + gl_info->gl_ops.gl.p_glVertex3fv = wine_glVertex3fv; + + old_fogcoord_glColor4f = gl_info->gl_ops.gl.p_glColor4f; + gl_info->gl_ops.gl.p_glColor4f = wine_glColor4f; + gl_info->gl_ops.gl.p_glColor4fv = wine_glColor4fv; + gl_info->gl_ops.gl.p_glColor3f = wine_glColor3f; + gl_info->gl_ops.gl.p_glColor3fv = wine_glColor3fv; + gl_info->gl_ops.gl.p_glColor4ub = wine_glColor4ub; + + gl_info->gl_ops.ext.p_glFogCoordfEXT = wine_glFogCoordfEXT; + gl_info->gl_ops.ext.p_glFogCoordfvEXT = wine_glFogCoordfvEXT; + gl_info->gl_ops.ext.p_glFogCoorddEXT = wine_glFogCoorddEXT; + gl_info->gl_ops.ext.p_glFogCoorddvEXT = wine_glFogCoorddvEXT; + gl_info->supported[EXT_FOG_COORD] = TRUE; + return; + + default: + FIXME("Extension %u emulation not supported.\n", ext); + } +} diff --git a/wrappers/directx/d3dwine_wrapper/glsl_shader.c b/wrappers/directx/d3dwine_wrapper/glsl_shader.c new file mode 100644 index 00000000000..45eebcc2c20 --- /dev/null +++ b/wrappers/directx/d3dwine_wrapper/glsl_shader.c @@ -0,0 +1,13303 @@ +/* + * GLSL pixel and vertex shader implementation + * + * Copyright 2006 Jason Green + * Copyright 2006-2007 Henri Verbeet + * Copyright 2007-2009, 2013 Stefan Dösinger for CodeWeavers + * Copyright 2009-2011 Henri Verbeet for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/* + * D3D shader asm has swizzles on source parameters, and write masks for + * destination parameters. GLSL uses swizzles for both. The result of this is + * that for example "mov dst.xw, src.zyxw" becomes "dst.xw = src.zw" in GLSL. + * Ie, to generate a proper GLSL source swizzle, we need to take the D3D write + * mask for the destination parameter into account. + */ + +#include "config.h" +#include "wine/port.h" + +#include +#include + +#include "wined3d_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(d3d_shader); +WINE_DECLARE_DEBUG_CHANNEL(d3d); +WINE_DECLARE_DEBUG_CHANNEL(winediag); + +#define WINED3D_GLSL_SAMPLE_PROJECTED 0x01 +#define WINED3D_GLSL_SAMPLE_LOD 0x02 +#define WINED3D_GLSL_SAMPLE_GRAD 0x04 +#define WINED3D_GLSL_SAMPLE_LOAD 0x08 +#define WINED3D_GLSL_SAMPLE_OFFSET 0x10 + +static const struct +{ + unsigned int coord_size; + unsigned int resinfo_size; + const char *type_part; +} +resource_type_info[] = +{ + {0, 0, ""}, /* WINED3D_SHADER_RESOURCE_NONE */ + {1, 1, "Buffer"}, /* WINED3D_SHADER_RESOURCE_BUFFER */ + {1, 1, "1D"}, /* WINED3D_SHADER_RESOURCE_TEXTURE_1D */ + {2, 2, "2D"}, /* WINED3D_SHADER_RESOURCE_TEXTURE_2D */ + {2, 2, ""}, /* WINED3D_SHADER_RESOURCE_TEXTURE_2DMS */ + {3, 3, "3D"}, /* WINED3D_SHADER_RESOURCE_TEXTURE_3D */ + {3, 2, "Cube"}, /* WINED3D_SHADER_RESOURCE_TEXTURE_CUBE */ + {2, 2, ""}, /* WINED3D_SHADER_RESOURCE_TEXTURE_1DARRAY */ + {3, 3, "2DArray"}, /* WINED3D_SHADER_RESOURCE_TEXTURE_2DARRAY */ + {3, 3, ""}, /* WINED3D_SHADER_RESOURCE_TEXTURE_2DMSARRAY */ + {4, 3, ""}, /* WINED3D_SHADER_RESOURCE_TEXTURE_CUBEARRAY */ +}; + +static const struct +{ + enum wined3d_data_type data_type; + const char *glsl_scalar_type; + const char *glsl_vector_type; +} +component_type_info[] = +{ + {WINED3D_DATA_FLOAT, "float", "vec"}, /* WINED3D_TYPE_UNKNOWN */ + {WINED3D_DATA_UINT, "uint", "uvec"}, /* WINED3D_TYPE_UINT */ + {WINED3D_DATA_INT, "int", "ivec"}, /* WINED3D_TYPE_INT */ + {WINED3D_DATA_FLOAT, "float", "vec"}, /* WINED3D_TYPE_FLOAT */ +}; + +struct glsl_dst_param +{ + char reg_name[150]; + char mask_str[6]; +}; + +struct glsl_src_param +{ + char param_str[200]; +}; + +struct glsl_sample_function +{ + struct wined3d_string_buffer *name; + unsigned int coord_mask; + unsigned int deriv_mask; + enum wined3d_data_type data_type; + BOOL output_single_component; + unsigned int offset_size; +}; + +enum heap_node_op +{ + HEAP_NODE_TRAVERSE_LEFT, + HEAP_NODE_TRAVERSE_RIGHT, + HEAP_NODE_POP, +}; + +struct constant_entry +{ + unsigned int idx; + unsigned int version; +}; + +struct constant_heap +{ + struct constant_entry *entries; + BOOL *contained; + unsigned int *positions; + unsigned int size; +}; + +/* GLSL shader private data */ +struct shader_glsl_priv +{ + struct wined3d_string_buffer shader_buffer; + struct wined3d_string_buffer_list string_buffers; + struct wine_rb_tree program_lookup; + struct constant_heap vconst_heap; + struct constant_heap pconst_heap; + unsigned char *stack; + UINT next_constant_version; + + const struct wined3d_vertex_pipe_ops *vertex_pipe; + const struct wined3d_fragment_pipe_ops *fragment_pipe; + struct wine_rb_tree ffp_vertex_shaders; + struct wine_rb_tree ffp_fragment_shaders; + BOOL ffp_proj_control; + BOOL legacy_lighting; +}; + +struct glsl_vs_program +{ + struct list shader_entry; + GLuint id; + GLenum vertex_color_clamp; + GLint uniform_f_locations[WINED3D_MAX_VS_CONSTS_F]; + GLint uniform_i_locations[WINED3D_MAX_CONSTS_I]; + GLint uniform_b_locations[WINED3D_MAX_CONSTS_B]; + GLint pos_fixup_location; + GLint base_vertex_id_location; + + GLint modelview_matrix_location[MAX_VERTEX_BLENDS]; + GLint projection_matrix_location; + GLint normal_matrix_location; + GLint texture_matrix_location[WINED3D_MAX_TEXTURES]; + GLint material_ambient_location; + GLint material_diffuse_location; + GLint material_specular_location; + GLint material_emissive_location; + GLint material_shininess_location; + GLint light_ambient_location; + struct + { + GLint diffuse; + GLint specular; + GLint ambient; + GLint position; + GLint direction; + GLint range; + GLint falloff; + GLint c_att; + GLint l_att; + GLint q_att; + GLint cos_htheta; + GLint cos_hphi; + } light_location[WINED3D_MAX_ACTIVE_LIGHTS]; + GLint pointsize_location; + GLint pointsize_min_location; + GLint pointsize_max_location; + GLint pointsize_c_att_location; + GLint pointsize_l_att_location; + GLint pointsize_q_att_location; + GLint clip_planes_location; +}; + +struct glsl_hs_program +{ + struct list shader_entry; + GLuint id; +}; + +struct glsl_ds_program +{ + struct list shader_entry; + GLuint id; + + GLint pos_fixup_location; +}; + +struct glsl_gs_program +{ + struct list shader_entry; + GLuint id; + + GLint pos_fixup_location; +}; + +struct glsl_ps_program +{ + struct list shader_entry; + GLuint id; + GLint uniform_f_locations[WINED3D_MAX_PS_CONSTS_F]; + GLint uniform_i_locations[WINED3D_MAX_CONSTS_I]; + GLint uniform_b_locations[WINED3D_MAX_CONSTS_B]; + GLint bumpenv_mat_location[WINED3D_MAX_TEXTURES]; + GLint bumpenv_lum_scale_location[WINED3D_MAX_TEXTURES]; + GLint bumpenv_lum_offset_location[WINED3D_MAX_TEXTURES]; + GLint tss_constant_location[WINED3D_MAX_TEXTURES]; + GLint tex_factor_location; + GLint specular_enable_location; + GLint fog_color_location; + GLint fog_density_location; + GLint fog_end_location; + GLint fog_scale_location; + GLint alpha_test_ref_location; + GLint ycorrection_location; + GLint np2_fixup_location; + GLint color_key_location; + const struct ps_np2fixup_info *np2_fixup_info; +}; + +struct glsl_cs_program +{ + struct list shader_entry; + GLuint id; +}; + +/* Struct to maintain data about a linked GLSL program */ +struct glsl_shader_prog_link +{ + struct wine_rb_entry program_lookup_entry; + struct glsl_vs_program vs; + struct glsl_hs_program hs; + struct glsl_ds_program ds; + struct glsl_gs_program gs; + struct glsl_ps_program ps; + struct glsl_cs_program cs; + GLuint id; + DWORD constant_update_mask; + unsigned int constant_version; + DWORD shader_controlled_clip_distances : 1; + DWORD clip_distance_mask : 8; /* WINED3D_MAX_CLIP_DISTANCES, 8 */ + DWORD padding : 23; +}; + +struct glsl_program_key +{ + GLuint vs_id; + GLuint hs_id; + GLuint ds_id; + GLuint gs_id; + GLuint ps_id; + GLuint cs_id; +}; + +struct shader_glsl_ctx_priv +{ + const struct wined3d_gl_info *gl_info; + const struct vs_compile_args *cur_vs_args; + const struct ds_compile_args *cur_ds_args; + const struct ps_compile_args *cur_ps_args; + struct ps_np2fixup_info *cur_np2fixup_info; + struct wined3d_string_buffer_list *string_buffers; +}; + +struct glsl_context_data +{ + struct glsl_shader_prog_link *glsl_program; + GLenum vertex_color_clamp; + BOOL rasterization_disabled; +}; + +struct glsl_ps_compiled_shader +{ + struct ps_compile_args args; + struct ps_np2fixup_info np2fixup; + GLuint id; +}; + +struct glsl_vs_compiled_shader +{ + struct vs_compile_args args; + GLuint id; +}; + +struct glsl_hs_compiled_shader +{ + GLuint id; +}; + +struct glsl_ds_compiled_shader +{ + struct ds_compile_args args; + GLuint id; +}; + +struct glsl_gs_compiled_shader +{ + struct gs_compile_args args; + GLuint id; +}; + +struct glsl_cs_compiled_shader +{ + GLuint id; +}; + +struct glsl_shader_private +{ + union + { + struct glsl_vs_compiled_shader *vs; + struct glsl_hs_compiled_shader *hs; + struct glsl_ds_compiled_shader *ds; + struct glsl_gs_compiled_shader *gs; + struct glsl_ps_compiled_shader *ps; + struct glsl_cs_compiled_shader *cs; + } gl_shaders; + unsigned int num_gl_shaders, shader_array_size; +}; + +struct glsl_ffp_vertex_shader +{ + struct wined3d_ffp_vs_desc desc; + GLuint id; + struct list linked_programs; +}; + +struct glsl_ffp_fragment_shader +{ + struct ffp_frag_desc entry; + GLuint id; + struct list linked_programs; +}; + +struct glsl_ffp_destroy_ctx +{ + struct shader_glsl_priv *priv; + const struct wined3d_context_gl *context_gl; +}; + +static void shader_glsl_generate_shader_epilogue(const struct wined3d_shader_context *ctx); + +static const char *debug_gl_shader_type(GLenum type) +{ + switch (type) + { +#define WINED3D_TO_STR(u) case u: return #u + WINED3D_TO_STR(GL_VERTEX_SHADER); + WINED3D_TO_STR(GL_TESS_CONTROL_SHADER); + WINED3D_TO_STR(GL_TESS_EVALUATION_SHADER); + WINED3D_TO_STR(GL_GEOMETRY_SHADER); + WINED3D_TO_STR(GL_FRAGMENT_SHADER); + WINED3D_TO_STR(GL_COMPUTE_SHADER); +#undef WINED3D_TO_STR + default: + return wine_dbg_sprintf("UNKNOWN(%#x)", type); + } +} + +static const char *shader_glsl_get_prefix(enum wined3d_shader_type type) +{ + switch (type) + { + case WINED3D_SHADER_TYPE_VERTEX: + return "vs"; + + case WINED3D_SHADER_TYPE_HULL: + return "hs"; + + case WINED3D_SHADER_TYPE_DOMAIN: + return "ds"; + + case WINED3D_SHADER_TYPE_GEOMETRY: + return "gs"; + + case WINED3D_SHADER_TYPE_PIXEL: + return "ps"; + + case WINED3D_SHADER_TYPE_COMPUTE: + return "cs"; + + default: + FIXME("Unhandled shader type %#x.\n", type); + return "unknown"; + } +} + +static unsigned int shader_glsl_get_version(const struct wined3d_gl_info *gl_info) +{ + if (gl_info->glsl_version >= MAKEDWORD_VERSION(4, 40)) + return 440; + else if (gl_info->glsl_version >= MAKEDWORD_VERSION(1, 50)) + return 150; + else if (gl_info->glsl_version >= MAKEDWORD_VERSION(1, 30)) + return 130; + else + return 120; +} + +static void shader_glsl_add_version_declaration(struct wined3d_string_buffer *buffer, + const struct wined3d_gl_info *gl_info) +{ + shader_addline(buffer, "#version %u\n", shader_glsl_get_version(gl_info)); +} + +unsigned int shader_glsl_full_ffp_varyings(const struct wined3d_gl_info *gl_info) +{ + /* On core profile we have to also count diffuse and specular colours and + * the fog coordinate. */ + return gl_info->limits.glsl_varyings >= (gl_info->supported[WINED3D_GL_LEGACY_CONTEXT] + ? WINED3D_MAX_TEXTURES * 4 : (WINED3D_MAX_TEXTURES + 2) * 4 + 1); +} + +static void shader_glsl_append_imm_vec(struct wined3d_string_buffer *buffer, + const float *values, unsigned int size, const struct wined3d_gl_info *gl_info) +{ + const int *int_values = (const int *)values; + unsigned int i; + char str[17]; + + if (!gl_info->supported[ARB_SHADER_BIT_ENCODING]) + { + if (size > 1) + shader_addline(buffer, "vec%u(", size); + + for (i = 0; i < size; ++i) + { + wined3d_ftoa(values[i], str); + shader_addline(buffer, i ? ", %s" : "%s", str); + } + + if (size > 1) + shader_addline(buffer, ")"); + + return; + } + + shader_addline(buffer, "intBitsToFloat("); + if (size > 1) + shader_addline(buffer, "ivec%u(", size); + + for (i = 0; i < size; ++i) + { + wined3d_ftoa(values[i], str); + shader_addline(buffer, i ? ", %#x /* %s */" : "%#x /* %s */", int_values[i], str); + } + + if (size > 1) + shader_addline(buffer, ")"); + shader_addline(buffer, ")"); +} + +static void shader_glsl_append_imm_ivec(struct wined3d_string_buffer *buffer, + const int *values, unsigned int size) +{ + int i; + + if (!size || size > 4) + { + ERR("Invalid vector size %u.\n", size); + return; + } + + if (size > 1) + shader_addline(buffer, "ivec%u(", size); + + for (i = 0; i < size; ++i) + shader_addline(buffer, i ? ", %#x" : "%#x", values[i]); + + if (size > 1) + shader_addline(buffer, ")"); +} + +static const char *get_info_log_line(const char **ptr) +{ + const char *p, *q; + + p = *ptr; + if (!(q = strstr(p, "\n"))) + { + if (!*p) return NULL; + *ptr += strlen(p); + return p; + } + *ptr = q + 1; + + return p; +} + +/* Context activation is done by the caller. */ +void print_glsl_info_log(const struct wined3d_gl_info *gl_info, GLuint id, BOOL program) +{ + int length = 0; + char *log; + + if (!WARN_ON(d3d_shader) && !FIXME_ON(d3d_shader)) + return; + + if (program) + GL_EXTCALL(glGetProgramiv(id, GL_INFO_LOG_LENGTH, &length)); + else + GL_EXTCALL(glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length)); + + /* A size of 1 is just a null-terminated string, so the log should be bigger than + * that if there are errors. */ + if (length > 1) + { + const char *ptr, *line; + + log = heap_alloc(length); + /* The info log is supposed to be zero-terminated, but at least some + * versions of fglrx don't terminate the string properly. The reported + * length does include the terminator, so explicitly set it to zero + * here. */ + log[length - 1] = 0; + if (program) + GL_EXTCALL(glGetProgramInfoLog(id, length, NULL, log)); + else + GL_EXTCALL(glGetShaderInfoLog(id, length, NULL, log)); + + ptr = log; + if (gl_info->quirks & WINED3D_QUIRK_INFO_LOG_SPAM) + { + WARN("Info log received from GLSL shader #%u:\n", id); + while ((line = get_info_log_line(&ptr))) WARN(" %.*s", (int)(ptr - line), line); + } + else + { + FIXME("Info log received from GLSL shader #%u:\n", id); + while ((line = get_info_log_line(&ptr))) FIXME(" %.*s", (int)(ptr - line), line); + } + heap_free(log); + } +} + +/* Context activation is done by the caller. */ +static void shader_glsl_compile(const struct wined3d_gl_info *gl_info, GLuint shader, const char *src) +{ + const char *ptr, *line; + + TRACE("Compiling shader object %u.\n", shader); + + if (TRACE_ON(d3d_shader)) + { + ptr = src; + while ((line = get_info_log_line(&ptr))) TRACE_(d3d_shader)(" %.*s", (int)(ptr - line), line); + } + + GL_EXTCALL(glShaderSource(shader, 1, &src, NULL)); + checkGLcall("glShaderSource"); + GL_EXTCALL(glCompileShader(shader)); + checkGLcall("glCompileShader"); + print_glsl_info_log(gl_info, shader, FALSE); +} + +/* Context activation is done by the caller. */ +static void shader_glsl_dump_program_source(const struct wined3d_gl_info *gl_info, GLuint program) +{ + GLint i, shader_count, source_size = -1; + GLuint *shaders; + char *source = NULL; + + GL_EXTCALL(glGetProgramiv(program, GL_ATTACHED_SHADERS, &shader_count)); + if (!(shaders = heap_calloc(shader_count, sizeof(*shaders)))) + { + ERR("Failed to allocate shader array memory.\n"); + return; + } + + GL_EXTCALL(glGetAttachedShaders(program, shader_count, NULL, shaders)); + for (i = 0; i < shader_count; ++i) + { + const char *ptr, *line; + GLint tmp; + + GL_EXTCALL(glGetShaderiv(shaders[i], GL_SHADER_SOURCE_LENGTH, &tmp)); + + if (source_size < tmp) + { + heap_free(source); + + if (!(source = heap_alloc_zero(tmp))) + { + ERR("Failed to allocate %d bytes for shader source.\n", tmp); + heap_free(shaders); + return; + } + source_size = tmp; + } + + FIXME("Shader %u:\n", shaders[i]); + GL_EXTCALL(glGetShaderiv(shaders[i], GL_SHADER_TYPE, &tmp)); + FIXME(" GL_SHADER_TYPE: %s.\n", debug_gl_shader_type(tmp)); + GL_EXTCALL(glGetShaderiv(shaders[i], GL_COMPILE_STATUS, &tmp)); + FIXME(" GL_COMPILE_STATUS: %d.\n", tmp); + FIXME("\n"); + + ptr = source; + GL_EXTCALL(glGetShaderSource(shaders[i], source_size, NULL, source)); + while ((line = get_info_log_line(&ptr))) FIXME(" %.*s", (int)(ptr - line), line); + FIXME("\n"); + } + + heap_free(source); + heap_free(shaders); +} + +/* Context activation is done by the caller. */ +void shader_glsl_validate_link(const struct wined3d_gl_info *gl_info, GLuint program) +{ + GLint tmp; + + if (!TRACE_ON(d3d_shader) && !FIXME_ON(d3d_shader)) + return; + + GL_EXTCALL(glGetProgramiv(program, GL_LINK_STATUS, &tmp)); + if (!tmp) + { + FIXME("Program %u link status invalid.\n", program); + shader_glsl_dump_program_source(gl_info, program); + } + + print_glsl_info_log(gl_info, program, TRUE); +} + +static BOOL shader_glsl_use_layout_qualifier(const struct wined3d_gl_info *gl_info) +{ + /* Layout qualifiers were introduced in GLSL 1.40. The Nvidia Legacy GPU + * driver (series 340.xx) doesn't parse layout qualifiers in older GLSL + * versions. */ + return shader_glsl_get_version(gl_info) >= 140; +} + +static BOOL shader_glsl_use_layout_binding_qualifier(const struct wined3d_gl_info *gl_info) +{ + return gl_info->supported[ARB_SHADING_LANGUAGE_420PACK] && shader_glsl_use_layout_qualifier(gl_info); +} + +static void shader_glsl_init_uniform_block_bindings(const struct wined3d_gl_info *gl_info, + struct shader_glsl_priv *priv, GLuint program_id, + const struct wined3d_shader_reg_maps *reg_maps) +{ + const char *prefix = shader_glsl_get_prefix(reg_maps->shader_version.type); + struct wined3d_string_buffer *name; + unsigned int i, base, count; + GLuint block_idx; + + if (shader_glsl_use_layout_binding_qualifier(gl_info)) + return; + + name = string_buffer_get(&priv->string_buffers); + wined3d_gl_limits_get_uniform_block_range(&gl_info->limits, reg_maps->shader_version.type, &base, &count); + for (i = 0; i < count; ++i) + { + if (!reg_maps->cb_sizes[i]) + continue; + + string_buffer_sprintf(name, "block_%s_cb%u", prefix, i); + block_idx = GL_EXTCALL(glGetUniformBlockIndex(program_id, name->buffer)); + GL_EXTCALL(glUniformBlockBinding(program_id, block_idx, base + i)); + } + checkGLcall("glUniformBlockBinding"); + string_buffer_release(&priv->string_buffers, name); +} + +/* Context activation is done by the caller. */ +static void shader_glsl_load_samplers_range(const struct wined3d_gl_info *gl_info, + struct shader_glsl_priv *priv, GLuint program_id, const char *prefix, + unsigned int base, unsigned int count, const DWORD *tex_unit_map) +{ + struct wined3d_string_buffer *sampler_name = string_buffer_get(&priv->string_buffers); + unsigned int i, mapped_unit; + GLint name_loc; + + for (i = 0; i < count; ++i) + { + string_buffer_sprintf(sampler_name, "%s_sampler%u", prefix, i); + name_loc = GL_EXTCALL(glGetUniformLocation(program_id, sampler_name->buffer)); + if (name_loc == -1) + continue; + + mapped_unit = tex_unit_map ? tex_unit_map[base + i] : base + i; + if (mapped_unit == WINED3D_UNMAPPED_STAGE || mapped_unit >= gl_info->limits.combined_samplers) + { + ERR("Trying to load sampler %s on unsupported unit %u.\n", sampler_name->buffer, mapped_unit); + continue; + } + + TRACE("Loading sampler %s on unit %u.\n", sampler_name->buffer, mapped_unit); + GL_EXTCALL(glUniform1i(name_loc, mapped_unit)); + } + checkGLcall("Load sampler bindings"); + string_buffer_release(&priv->string_buffers, sampler_name); +} + +static unsigned int shader_glsl_map_tex_unit(const struct wined3d_context *context, + const struct wined3d_shader_version *shader_version, unsigned int sampler_idx) +{ + const struct wined3d_context_gl *context_gl = wined3d_context_gl_const(context); + const unsigned int *tex_unit_map; + unsigned int base, count; + + tex_unit_map = wined3d_context_gl_get_tex_unit_mapping(context_gl, shader_version, &base, &count); + if (sampler_idx >= count) + return WINED3D_UNMAPPED_STAGE; + if (!tex_unit_map) + return base + sampler_idx; + return tex_unit_map[base + sampler_idx]; +} + +static void shader_glsl_append_sampler_binding_qualifier(struct wined3d_string_buffer *buffer, + const struct wined3d_context *context, const struct wined3d_shader_version *shader_version, + unsigned int sampler_idx) +{ + unsigned int mapped_unit = shader_glsl_map_tex_unit(context, shader_version, sampler_idx); + if (mapped_unit != WINED3D_UNMAPPED_STAGE) + shader_addline(buffer, "layout(binding = %u)\n", mapped_unit); + else + ERR("Unmapped sampler %u.\n", sampler_idx); +} + +/* Context activation is done by the caller. */ +static void shader_glsl_load_samplers(const struct wined3d_context *context, + struct shader_glsl_priv *priv, GLuint program_id, const struct wined3d_shader_reg_maps *reg_maps) +{ + const struct wined3d_context_gl *context_gl = wined3d_context_gl_const(context); + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + const struct wined3d_shader_version *shader_version; + const unsigned int *tex_unit_map; + unsigned int base, count; + const char *prefix; + + if (shader_glsl_use_layout_binding_qualifier(gl_info)) + return; + + shader_version = reg_maps ? ®_maps->shader_version : NULL; + prefix = shader_glsl_get_prefix(shader_version ? shader_version->type : WINED3D_SHADER_TYPE_PIXEL); + tex_unit_map = wined3d_context_gl_get_tex_unit_mapping(context_gl, shader_version, &base, &count); + shader_glsl_load_samplers_range(gl_info, priv, program_id, prefix, base, count, tex_unit_map); +} + +static void shader_glsl_load_icb(const struct wined3d_gl_info *gl_info, struct shader_glsl_priv *priv, + GLuint program_id, const struct wined3d_shader_reg_maps *reg_maps) +{ + const struct wined3d_shader_immediate_constant_buffer *icb = reg_maps->icb; + + if (icb) + { + struct wined3d_string_buffer *icb_name = string_buffer_get(&priv->string_buffers); + const char *prefix = shader_glsl_get_prefix(reg_maps->shader_version.type); + GLint icb_location; + + string_buffer_sprintf(icb_name, "%s_icb", prefix); + icb_location = GL_EXTCALL(glGetUniformLocation(program_id, icb_name->buffer)); + GL_EXTCALL(glUniform4fv(icb_location, icb->vec4_count, (const GLfloat *)icb->data)); + checkGLcall("Load immediate constant buffer"); + + string_buffer_release(&priv->string_buffers, icb_name); + } +} + +/* Context activation is done by the caller. */ +static void shader_glsl_load_images(const struct wined3d_gl_info *gl_info, struct shader_glsl_priv *priv, + GLuint program_id, const struct wined3d_shader_reg_maps *reg_maps) +{ + const char *prefix = shader_glsl_get_prefix(reg_maps->shader_version.type); + struct wined3d_string_buffer *name; + GLint location; + unsigned int i; + + if (shader_glsl_use_layout_binding_qualifier(gl_info)) + return; + + name = string_buffer_get(&priv->string_buffers); + for (i = 0; i < MAX_UNORDERED_ACCESS_VIEWS; ++i) + { + if (!reg_maps->uav_resource_info[i].type) + continue; + + string_buffer_sprintf(name, "%s_image%u", prefix, i); + location = GL_EXTCALL(glGetUniformLocation(program_id, name->buffer)); + if (location == -1) + continue; + + TRACE("Loading image %s on unit %u.\n", name->buffer, i); + GL_EXTCALL(glUniform1i(location, i)); + } + checkGLcall("Load image bindings"); + string_buffer_release(&priv->string_buffers, name); +} + +/* Context activation is done by the caller. */ +static void shader_glsl_load_program_resources(const struct wined3d_context_gl *context_gl, + struct shader_glsl_priv *priv, GLuint program_id, const struct wined3d_shader *shader) +{ + const struct wined3d_shader_reg_maps *reg_maps = &shader->reg_maps; + + shader_glsl_init_uniform_block_bindings(context_gl->gl_info, priv, program_id, reg_maps); + shader_glsl_load_icb(context_gl->gl_info, priv, program_id, reg_maps); + /* Texture unit mapping is set up to be the same each time the shader + * program is used so we can hardcode the sampler uniform values. */ + shader_glsl_load_samplers(&context_gl->c, priv, program_id, reg_maps); +} + +static void append_transform_feedback_varying(const char **varyings, unsigned int *varying_count, + char **strings, unsigned int *strings_length, struct wined3d_string_buffer *buffer) +{ + if (varyings && *strings) + { + char *ptr = *strings; + + varyings[*varying_count] = ptr; + + memcpy(ptr, buffer->buffer, buffer->content_size + 1); + ptr += buffer->content_size + 1; + + *strings = ptr; + } + + *strings_length += buffer->content_size + 1; + ++(*varying_count); +} + +static void append_transform_feedback_skip_components(const char **varyings, + unsigned int *varying_count, char **strings, unsigned int *strings_length, + struct wined3d_string_buffer *buffer, unsigned int component_count) +{ + unsigned int j; + + for (j = 0; j < component_count / 4; ++j) + { + string_buffer_sprintf(buffer, "gl_SkipComponents4"); + append_transform_feedback_varying(varyings, varying_count, strings, strings_length, buffer); + } + if (component_count % 4) + { + string_buffer_sprintf(buffer, "gl_SkipComponents%u", component_count % 4); + append_transform_feedback_varying(varyings, varying_count, strings, strings_length, buffer); + } +} + +static BOOL shader_glsl_generate_transform_feedback_varyings(struct wined3d_string_buffer *buffer, + const char **varyings, unsigned int *varying_count, char *strings, unsigned int *strings_length, + GLenum buffer_mode, struct wined3d_shader *shader) +{ + const struct wined3d_stream_output_desc *so_desc = shader->u.gs.so_desc; + unsigned int buffer_idx, count, length, highest_output_slot, stride; + unsigned int i, register_idx, component_idx; + BOOL have_varyings_to_record = FALSE; + + count = length = 0; + highest_output_slot = 0; + for (buffer_idx = 0; buffer_idx < WINED3D_MAX_STREAM_OUTPUT_BUFFERS; ++buffer_idx) + { + stride = 0; + + for (i = 0; i < so_desc->element_count; ++i) + { + const struct wined3d_stream_output_element *e = &so_desc->elements[i]; + + highest_output_slot = max(highest_output_slot, e->output_slot); + if (e->output_slot != buffer_idx) + continue; + + if (e->stream_idx) + { + FIXME("Unhandled stream %u.\n", e->stream_idx); + continue; + } + + stride += e->component_count; + + if (!e->semantic_name) + { + append_transform_feedback_skip_components(varyings, &count, + &strings, &length, buffer, e->component_count); + continue; + } + + if (!shader_get_stream_output_register_info(shader, e, ®ister_idx, &component_idx)) + continue; + + if (component_idx || e->component_count != 4) + { + if (so_desc->rasterizer_stream_idx != WINED3D_NO_RASTERIZER_STREAM) + { + FIXME("Unsupported component range %u-%u.\n", component_idx, e->component_count); + append_transform_feedback_skip_components(varyings, &count, + &strings, &length, buffer, e->component_count); + continue; + } + + string_buffer_sprintf(buffer, "shader_in_out.reg%u_%u_%u", + register_idx, component_idx, component_idx + e->component_count - 1); + append_transform_feedback_varying(varyings, &count, &strings, &length, buffer); + } + else + { + string_buffer_sprintf(buffer, "shader_in_out.reg%u", register_idx); + append_transform_feedback_varying(varyings, &count, &strings, &length, buffer); + } + + have_varyings_to_record = TRUE; + } + + if (buffer_idx < so_desc->buffer_stride_count + && stride < so_desc->buffer_strides[buffer_idx] / 4) + { + unsigned int component_count = so_desc->buffer_strides[buffer_idx] / 4 - stride; + append_transform_feedback_skip_components(varyings, &count, + &strings, &length, buffer, component_count); + } + + if (highest_output_slot <= buffer_idx) + break; + + if (buffer_mode == GL_INTERLEAVED_ATTRIBS) + { + string_buffer_sprintf(buffer, "gl_NextBuffer"); + append_transform_feedback_varying(varyings, &count, &strings, &length, buffer); + } + } + + if (varying_count) + *varying_count = count; + if (strings_length) + *strings_length = length; + + return have_varyings_to_record; +} + +static void shader_glsl_init_transform_feedback(const struct wined3d_context_gl *context_gl, + struct shader_glsl_priv *priv, GLuint program_id, struct wined3d_shader *shader) +{ + const struct wined3d_stream_output_desc *so_desc = shader->u.gs.so_desc; + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + struct wined3d_string_buffer *buffer; + unsigned int i, count, length; + const char **varyings; + char *strings; + GLenum mode; + + if (!so_desc) + return; + + if (gl_info->supported[ARB_TRANSFORM_FEEDBACK3]) + { + mode = GL_INTERLEAVED_ATTRIBS; + } + else + { + unsigned int element_count[WINED3D_MAX_STREAM_OUTPUT_BUFFERS] = {0}; + + for (i = 0; i < so_desc->element_count; ++i) + { + if (!so_desc->elements[i].semantic_name) + { + FIXME("ARB_transform_feedback3 is needed for stream output gaps.\n"); + return; + } + ++element_count[so_desc->elements[i].output_slot]; + } + + if (element_count[0] == so_desc->element_count) + { + mode = GL_INTERLEAVED_ATTRIBS; + } + else + { + mode = GL_SEPARATE_ATTRIBS; + for (i = 0; i < ARRAY_SIZE(element_count); ++i) + { + if (element_count[i] != 1) + break; + } + for (; i < ARRAY_SIZE(element_count); ++i) + { + if (element_count[i]) + { + FIXME("Only single element per buffer is allowed in separate mode.\n"); + return; + } + } + } + } + + buffer = string_buffer_get(&priv->string_buffers); + + if (!shader_glsl_generate_transform_feedback_varyings(buffer, NULL, &count, NULL, &length, mode, shader)) + { + FIXME("No varyings to record, disabling transform feedback.\n"); + shader->u.gs.so_desc = NULL; + string_buffer_release(&priv->string_buffers, buffer); + return; + } + + if (!(varyings = heap_calloc(count, sizeof(*varyings)))) + { + ERR("Out of memory.\n"); + string_buffer_release(&priv->string_buffers, buffer); + return; + } + if (!(strings = heap_calloc(length, sizeof(*strings)))) + { + ERR("Out of memory.\n"); + heap_free(varyings); + string_buffer_release(&priv->string_buffers, buffer); + return; + } + + shader_glsl_generate_transform_feedback_varyings(buffer, varyings, NULL, strings, NULL, mode, shader); + GL_EXTCALL(glTransformFeedbackVaryings(program_id, count, varyings, mode)); + checkGLcall("glTransformFeedbackVaryings"); + + heap_free(varyings); + heap_free(strings); + string_buffer_release(&priv->string_buffers, buffer); +} + +/* Context activation is done by the caller. */ +static void walk_constant_heap(const struct wined3d_gl_info *gl_info, const struct wined3d_vec4 *constants, + const GLint *constant_locations, const struct constant_heap *heap, unsigned char *stack, DWORD version) +{ + unsigned int start = ~0U, end = 0; + int stack_idx = 0; + unsigned int heap_idx = 1; + unsigned int idx; + + if (heap->entries[heap_idx].version <= version) return; + + idx = heap->entries[heap_idx].idx; + if (constant_locations[idx] != -1) + start = end = idx; + stack[stack_idx] = HEAP_NODE_TRAVERSE_LEFT; + + while (stack_idx >= 0) + { + /* Note that we fall through to the next case statement. */ + switch(stack[stack_idx]) + { + case HEAP_NODE_TRAVERSE_LEFT: + { + unsigned int left_idx = heap_idx << 1; + if (left_idx < heap->size && heap->entries[left_idx].version > version) + { + heap_idx = left_idx; + idx = heap->entries[heap_idx].idx; + if (constant_locations[idx] != -1) + { + if (start > idx) + start = idx; + if (end < idx) + end = idx; + } + + stack[stack_idx++] = HEAP_NODE_TRAVERSE_RIGHT; + stack[stack_idx] = HEAP_NODE_TRAVERSE_LEFT; + break; + } + } + + case HEAP_NODE_TRAVERSE_RIGHT: + { + unsigned int right_idx = (heap_idx << 1) + 1; + if (right_idx < heap->size && heap->entries[right_idx].version > version) + { + heap_idx = right_idx; + idx = heap->entries[heap_idx].idx; + if (constant_locations[idx] != -1) + { + if (start > idx) + start = idx; + if (end < idx) + end = idx; + } + + stack[stack_idx++] = HEAP_NODE_POP; + stack[stack_idx] = HEAP_NODE_TRAVERSE_LEFT; + break; + } + } + + case HEAP_NODE_POP: + heap_idx >>= 1; + --stack_idx; + break; + } + } + if (start <= end) + GL_EXTCALL(glUniform4fv(constant_locations[start], end - start + 1, &constants[start].x)); + checkGLcall("walk_constant_heap()"); +} + +/* Context activation is done by the caller. */ +static void apply_clamped_constant(const struct wined3d_gl_info *gl_info, + GLint location, const struct wined3d_vec4 *data) +{ + GLfloat clamped_constant[4]; + + if (location == -1) return; + + clamped_constant[0] = data->x < -1.0f ? -1.0f : data->x > 1.0f ? 1.0f : data->x; + clamped_constant[1] = data->y < -1.0f ? -1.0f : data->y > 1.0f ? 1.0f : data->y; + clamped_constant[2] = data->z < -1.0f ? -1.0f : data->z > 1.0f ? 1.0f : data->z; + clamped_constant[3] = data->w < -1.0f ? -1.0f : data->w > 1.0f ? 1.0f : data->w; + + GL_EXTCALL(glUniform4fv(location, 1, clamped_constant)); +} + +/* Context activation is done by the caller. */ +static void walk_constant_heap_clamped(const struct wined3d_gl_info *gl_info, + const struct wined3d_vec4 *constants, const GLint *constant_locations, + const struct constant_heap *heap, unsigned char *stack, DWORD version) +{ + int stack_idx = 0; + unsigned int heap_idx = 1; + unsigned int idx; + + if (heap->entries[heap_idx].version <= version) return; + + idx = heap->entries[heap_idx].idx; + apply_clamped_constant(gl_info, constant_locations[idx], &constants[idx]); + stack[stack_idx] = HEAP_NODE_TRAVERSE_LEFT; + + while (stack_idx >= 0) + { + /* Note that we fall through to the next case statement. */ + switch(stack[stack_idx]) + { + case HEAP_NODE_TRAVERSE_LEFT: + { + unsigned int left_idx = heap_idx << 1; + if (left_idx < heap->size && heap->entries[left_idx].version > version) + { + heap_idx = left_idx; + idx = heap->entries[heap_idx].idx; + apply_clamped_constant(gl_info, constant_locations[idx], &constants[idx]); + + stack[stack_idx++] = HEAP_NODE_TRAVERSE_RIGHT; + stack[stack_idx] = HEAP_NODE_TRAVERSE_LEFT; + break; + } + } + + case HEAP_NODE_TRAVERSE_RIGHT: + { + unsigned int right_idx = (heap_idx << 1) + 1; + if (right_idx < heap->size && heap->entries[right_idx].version > version) + { + heap_idx = right_idx; + idx = heap->entries[heap_idx].idx; + apply_clamped_constant(gl_info, constant_locations[idx], &constants[idx]); + + stack[stack_idx++] = HEAP_NODE_POP; + stack[stack_idx] = HEAP_NODE_TRAVERSE_LEFT; + break; + } + } + + case HEAP_NODE_POP: + heap_idx >>= 1; + --stack_idx; + break; + } + } + checkGLcall("walk_constant_heap_clamped()"); +} + +/* Context activation is done by the caller. */ +static void shader_glsl_load_constants_f(const struct wined3d_shader *shader, const struct wined3d_gl_info *gl_info, + const struct wined3d_vec4 *constants, const GLint *constant_locations, const struct constant_heap *heap, + unsigned char *stack, unsigned int version) +{ + const struct wined3d_shader_lconst *lconst; + + /* 1.X pshaders have the constants clamped to [-1;1] implicitly. */ + if (shader->reg_maps.shader_version.major == 1 + && shader->reg_maps.shader_version.type == WINED3D_SHADER_TYPE_PIXEL) + walk_constant_heap_clamped(gl_info, constants, constant_locations, heap, stack, version); + else + walk_constant_heap(gl_info, constants, constant_locations, heap, stack, version); + + if (!shader->load_local_constsF) + { + TRACE("No need to load local float constants for this shader.\n"); + return; + } + + /* Immediate constants are clamped to [-1;1] at shader creation time if needed */ + LIST_FOR_EACH_ENTRY(lconst, &shader->constantsF, struct wined3d_shader_lconst, entry) + { + GL_EXTCALL(glUniform4fv(constant_locations[lconst->idx], 1, (const GLfloat *)lconst->value)); + } + checkGLcall("glUniform4fv()"); +} + +/* Context activation is done by the caller. */ +static void shader_glsl_load_constants_i(const struct wined3d_shader *shader, const struct wined3d_gl_info *gl_info, + const struct wined3d_ivec4 *constants, const GLint locations[WINED3D_MAX_CONSTS_I], WORD constants_set) +{ + unsigned int i; + struct list* ptr; + + for (i = 0; constants_set; constants_set >>= 1, ++i) + { + if (!(constants_set & 1)) continue; + + /* We found this uniform name in the program - go ahead and send the data */ + GL_EXTCALL(glUniform4iv(locations[i], 1, &constants[i].x)); + } + + /* Load immediate constants */ + ptr = list_head(&shader->constantsI); + while (ptr) + { + const struct wined3d_shader_lconst *lconst = LIST_ENTRY(ptr, const struct wined3d_shader_lconst, entry); + unsigned int idx = lconst->idx; + const GLint *values = (const GLint *)lconst->value; + + /* We found this uniform name in the program - go ahead and send the data */ + GL_EXTCALL(glUniform4iv(locations[idx], 1, values)); + ptr = list_next(&shader->constantsI, ptr); + } + checkGLcall("glUniform4iv()"); +} + +/* Context activation is done by the caller. */ +static void shader_glsl_load_constantsB(const struct wined3d_shader *shader, const struct wined3d_gl_info *gl_info, + const GLint locations[WINED3D_MAX_CONSTS_B], const BOOL *constants, WORD constants_set) +{ + unsigned int i; + struct list* ptr; + + for (i = 0; constants_set; constants_set >>= 1, ++i) + { + if (!(constants_set & 1)) continue; + + GL_EXTCALL(glUniform1iv(locations[i], 1, &constants[i])); + } + + /* Load immediate constants */ + ptr = list_head(&shader->constantsB); + while (ptr) + { + const struct wined3d_shader_lconst *lconst = LIST_ENTRY(ptr, const struct wined3d_shader_lconst, entry); + unsigned int idx = lconst->idx; + const GLint *values = (const GLint *)lconst->value; + + GL_EXTCALL(glUniform1iv(locations[idx], 1, values)); + ptr = list_next(&shader->constantsB, ptr); + } + checkGLcall("glUniform1iv()"); +} + +static void reset_program_constant_version(struct wine_rb_entry *entry, void *context) +{ + WINE_RB_ENTRY_VALUE(entry, struct glsl_shader_prog_link, program_lookup_entry)->constant_version = 0; +} + +/* Context activation is done by the caller (state handler). */ +static void shader_glsl_load_np2fixup_constants(const struct glsl_ps_program *ps, + const struct wined3d_gl_info *gl_info, const struct wined3d_state *state) +{ + struct + { + float sx, sy; + } + np2fixup_constants[WINED3D_MAX_FRAGMENT_SAMPLERS]; + UINT fixup = ps->np2_fixup_info->active; + UINT i; + + for (i = 0; fixup; fixup >>= 1, ++i) + { + const struct wined3d_texture *tex = state->textures[i]; + unsigned char idx = ps->np2_fixup_info->idx[i]; + + if (!tex) + { + ERR("Nonexistent texture is flagged for NP2 texcoord fixup.\n"); + continue; + } + + np2fixup_constants[idx].sx = tex->pow2_matrix[0]; + np2fixup_constants[idx].sy = tex->pow2_matrix[5]; + } + + GL_EXTCALL(glUniform4fv(ps->np2_fixup_location, ps->np2_fixup_info->num_consts, &np2fixup_constants[0].sx)); +} + +static void transpose_matrix(struct wined3d_matrix *out, const struct wined3d_matrix *m) +{ + struct wined3d_matrix temp; + unsigned int i, j; + + for (i = 0; i < 4; ++i) + for (j = 0; j < 4; ++j) + (&temp._11)[4 * j + i] = (&m->_11)[4 * i + j]; + + *out = temp; +} + +static void shader_glsl_ffp_vertex_normalmatrix_uniform(const struct wined3d_context_gl *context_gl, + const struct wined3d_state *state, struct glsl_shader_prog_link *prog) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + struct wined3d_matrix mv; + float mat[3 * 3]; + + if (prog->vs.normal_matrix_location == -1) + return; + + get_modelview_matrix(&context_gl->c, state, 0, &mv); + compute_normal_matrix(mat, context_gl->c.d3d_info->wined3d_creation_flags & WINED3D_LEGACY_FFP_LIGHTING, &mv); + + GL_EXTCALL(glUniformMatrix3fv(prog->vs.normal_matrix_location, 1, FALSE, mat)); + checkGLcall("glUniformMatrix3fv"); +} + +static void shader_glsl_ffp_vertex_texmatrix_uniform(const struct wined3d_context_gl *context_gl, + const struct wined3d_state *state, unsigned int tex, struct glsl_shader_prog_link *prog) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + struct wined3d_matrix mat; + + if (tex >= WINED3D_MAX_TEXTURES) + return; + if (prog->vs.texture_matrix_location[tex] == -1) + return; + + get_texture_matrix(&context_gl->c, state, tex, &mat); + GL_EXTCALL(glUniformMatrix4fv(prog->vs.texture_matrix_location[tex], 1, FALSE, &mat._11)); + checkGLcall("glUniformMatrix4fv"); +} + +static void shader_glsl_ffp_vertex_material_uniform(const struct wined3d_context_gl *context_gl, + const struct wined3d_state *state, struct glsl_shader_prog_link *prog) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + + if (state->render_states[WINED3D_RS_SPECULARENABLE]) + { + GL_EXTCALL(glUniform4fv(prog->vs.material_specular_location, 1, &state->material.specular.r)); + GL_EXTCALL(glUniform1f(prog->vs.material_shininess_location, state->material.power)); + } + else + { + static const float black[] = {0.0f, 0.0f, 0.0f, 0.0f}; + + GL_EXTCALL(glUniform4fv(prog->vs.material_specular_location, 1, black)); + } + GL_EXTCALL(glUniform4fv(prog->vs.material_ambient_location, 1, &state->material.ambient.r)); + GL_EXTCALL(glUniform4fv(prog->vs.material_diffuse_location, 1, &state->material.diffuse.r)); + GL_EXTCALL(glUniform4fv(prog->vs.material_emissive_location, 1, &state->material.emissive.r)); + checkGLcall("setting FFP material uniforms"); +} + +static void shader_glsl_ffp_vertex_lightambient_uniform(const struct wined3d_context_gl *context_gl, + const struct wined3d_state *state, struct glsl_shader_prog_link *prog) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + struct wined3d_color color; + + wined3d_color_from_d3dcolor(&color, state->render_states[WINED3D_RS_AMBIENT]); + GL_EXTCALL(glUniform3fv(prog->vs.light_ambient_location, 1, &color.r)); + checkGLcall("glUniform3fv"); +} + +static void shader_glsl_ffp_vertex_light_uniform(const struct wined3d_context_gl *context_gl, + const struct wined3d_state *state, unsigned int light, const struct wined3d_light_info *light_info, + struct glsl_shader_prog_link *prog) +{ + const struct wined3d_matrix *view = &state->transforms[WINED3D_TS_VIEW]; + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + struct wined3d_vec4 vec4; + + GL_EXTCALL(glUniform4fv(prog->vs.light_location[light].diffuse, 1, &light_info->OriginalParms.diffuse.r)); + GL_EXTCALL(glUniform4fv(prog->vs.light_location[light].specular, 1, &light_info->OriginalParms.specular.r)); + GL_EXTCALL(glUniform4fv(prog->vs.light_location[light].ambient, 1, &light_info->OriginalParms.ambient.r)); + + switch (light_info->OriginalParms.type) + { + case WINED3D_LIGHT_POINT: + wined3d_vec4_transform(&vec4, &light_info->position, view); + GL_EXTCALL(glUniform4fv(prog->vs.light_location[light].position, 1, &vec4.x)); + GL_EXTCALL(glUniform1f(prog->vs.light_location[light].range, light_info->OriginalParms.range)); + GL_EXTCALL(glUniform1f(prog->vs.light_location[light].c_att, light_info->OriginalParms.attenuation0)); + GL_EXTCALL(glUniform1f(prog->vs.light_location[light].l_att, light_info->OriginalParms.attenuation1)); + GL_EXTCALL(glUniform1f(prog->vs.light_location[light].q_att, light_info->OriginalParms.attenuation2)); + break; + + case WINED3D_LIGHT_SPOT: + wined3d_vec4_transform(&vec4, &light_info->position, view); + GL_EXTCALL(glUniform4fv(prog->vs.light_location[light].position, 1, &vec4.x)); + + wined3d_vec4_transform(&vec4, &light_info->direction, view); + GL_EXTCALL(glUniform3fv(prog->vs.light_location[light].direction, 1, &vec4.x)); + + GL_EXTCALL(glUniform1f(prog->vs.light_location[light].range, light_info->OriginalParms.range)); + GL_EXTCALL(glUniform1f(prog->vs.light_location[light].falloff, light_info->OriginalParms.falloff)); + GL_EXTCALL(glUniform1f(prog->vs.light_location[light].c_att, light_info->OriginalParms.attenuation0)); + GL_EXTCALL(glUniform1f(prog->vs.light_location[light].l_att, light_info->OriginalParms.attenuation1)); + GL_EXTCALL(glUniform1f(prog->vs.light_location[light].q_att, light_info->OriginalParms.attenuation2)); + GL_EXTCALL(glUniform1f(prog->vs.light_location[light].cos_htheta, cosf(light_info->OriginalParms.theta / 2.0f))); + GL_EXTCALL(glUniform1f(prog->vs.light_location[light].cos_hphi, cosf(light_info->OriginalParms.phi / 2.0f))); + break; + + case WINED3D_LIGHT_DIRECTIONAL: + wined3d_vec4_transform(&vec4, &light_info->direction, view); + GL_EXTCALL(glUniform3fv(prog->vs.light_location[light].direction, 1, &vec4.x)); + break; + + case WINED3D_LIGHT_PARALLELPOINT: + wined3d_vec4_transform(&vec4, &light_info->position, view); + GL_EXTCALL(glUniform4fv(prog->vs.light_location[light].position, 1, &vec4.x)); + break; + + default: + FIXME("Unrecognized light type %#x.\n", light_info->OriginalParms.type); + } + checkGLcall("setting FFP lights uniforms"); +} + +static void shader_glsl_pointsize_uniform(const struct wined3d_context_gl *context_gl, + const struct wined3d_state *state, struct glsl_shader_prog_link *prog) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + float size, att[3]; + float min, max; + + get_pointsize_minmax(&context_gl->c, state, &min, &max); + + GL_EXTCALL(glUniform1f(prog->vs.pointsize_min_location, min)); + checkGLcall("glUniform1f"); + GL_EXTCALL(glUniform1f(prog->vs.pointsize_max_location, max)); + checkGLcall("glUniform1f"); + + get_pointsize(&context_gl->c, state, &size, att); + + GL_EXTCALL(glUniform1f(prog->vs.pointsize_location, size)); + checkGLcall("glUniform1f"); + GL_EXTCALL(glUniform1f(prog->vs.pointsize_c_att_location, att[0])); + checkGLcall("glUniform1f"); + GL_EXTCALL(glUniform1f(prog->vs.pointsize_l_att_location, att[1])); + checkGLcall("glUniform1f"); + GL_EXTCALL(glUniform1f(prog->vs.pointsize_q_att_location, att[2])); + checkGLcall("glUniform1f"); +} + +static void shader_glsl_load_fog_uniform(const struct wined3d_context_gl *context_gl, + const struct wined3d_state *state, struct glsl_shader_prog_link *prog) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + struct wined3d_color color; + float start, end, scale; + union + { + DWORD d; + float f; + } tmpvalue; + + wined3d_color_from_d3dcolor(&color, state->render_states[WINED3D_RS_FOGCOLOR]); + GL_EXTCALL(glUniform4fv(prog->ps.fog_color_location, 1, &color.r)); + tmpvalue.d = state->render_states[WINED3D_RS_FOGDENSITY]; + GL_EXTCALL(glUniform1f(prog->ps.fog_density_location, tmpvalue.f)); + get_fog_start_end(&context_gl->c, state, &start, &end); + scale = 1.0f / (end - start); + GL_EXTCALL(glUniform1f(prog->ps.fog_end_location, end)); + GL_EXTCALL(glUniform1f(prog->ps.fog_scale_location, scale)); + checkGLcall("fog emulation uniforms"); +} + +static void shader_glsl_clip_plane_uniform(const struct wined3d_context_gl *context_gl, + const struct wined3d_state *state, unsigned int index, struct glsl_shader_prog_link *prog) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + struct wined3d_matrix matrix; + struct wined3d_vec4 plane; + + plane = state->clip_planes[index]; + + /* Clip planes are affected by the view transform in d3d for FFP draws. */ + if (!use_vs(state)) + { + invert_matrix(&matrix, &state->transforms[WINED3D_TS_VIEW]); + transpose_matrix(&matrix, &matrix); + wined3d_vec4_transform(&plane, &plane, &matrix); + } + + GL_EXTCALL(glUniform4fv(prog->vs.clip_planes_location + index, 1, &plane.x)); +} + +/* Context activation is done by the caller (state handler). */ +static void shader_glsl_load_color_key_constant(const struct glsl_ps_program *ps, + const struct wined3d_gl_info *gl_info, const struct wined3d_state *state) +{ + struct wined3d_color float_key[2]; + const struct wined3d_texture *texture = state->textures[0]; + + wined3d_format_get_float_color_key(texture->resource.format, &texture->async.src_blt_color_key, float_key); + GL_EXTCALL(glUniform4fv(ps->color_key_location, 2, &float_key[0].r)); +} + +/* Context activation is done by the caller (state handler). */ +static void shader_glsl_load_constants(void *shader_priv, struct wined3d_context *context, + const struct wined3d_state *state) +{ + const struct wined3d_shader *vshader = state->shader[WINED3D_SHADER_TYPE_VERTEX]; + const struct wined3d_shader *pshader = state->shader[WINED3D_SHADER_TYPE_PIXEL]; + const struct glsl_context_data *ctx_data = context->shader_backend_data; + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + struct glsl_shader_prog_link *prog = ctx_data->glsl_program; + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + float position_fixup[4 * WINED3D_MAX_VIEWPORTS]; + struct shader_glsl_priv *priv = shader_priv; + unsigned int constant_version; + DWORD update_mask; + int i; + + /* No GLSL program set - nothing to do. */ + if (!prog) + return; + + constant_version = prog->constant_version; + update_mask = context->constant_update_mask & prog->constant_update_mask; + + if (update_mask & WINED3D_SHADER_CONST_VS_F) + shader_glsl_load_constants_f(vshader, gl_info, state->vs_consts_f, + prog->vs.uniform_f_locations, &priv->vconst_heap, priv->stack, constant_version); + + if (update_mask & WINED3D_SHADER_CONST_VS_I) + shader_glsl_load_constants_i(vshader, gl_info, state->vs_consts_i, + prog->vs.uniform_i_locations, vshader->reg_maps.integer_constants); + + if (update_mask & WINED3D_SHADER_CONST_VS_B) + shader_glsl_load_constantsB(vshader, gl_info, prog->vs.uniform_b_locations, state->vs_consts_b, + vshader->reg_maps.boolean_constants); + + if (update_mask & WINED3D_SHADER_CONST_VS_CLIP_PLANES) + { + for (i = 0; i < gl_info->limits.user_clip_distances; ++i) + shader_glsl_clip_plane_uniform(context_gl, state, i, prog); + } + + if (update_mask & WINED3D_SHADER_CONST_VS_POINTSIZE) + shader_glsl_pointsize_uniform(context_gl, state, prog); + + if (update_mask & WINED3D_SHADER_CONST_POS_FIXUP) + { + unsigned int fixup_count = state->shader[WINED3D_SHADER_TYPE_GEOMETRY] ? + max(state->viewport_count, 1) : 1; + shader_get_position_fixup(context, state, fixup_count, position_fixup); + if (state->shader[WINED3D_SHADER_TYPE_GEOMETRY]) + GL_EXTCALL(glUniform4fv(prog->gs.pos_fixup_location, fixup_count, position_fixup)); + else if (state->shader[WINED3D_SHADER_TYPE_DOMAIN]) + GL_EXTCALL(glUniform4fv(prog->ds.pos_fixup_location, 1, position_fixup)); + else + GL_EXTCALL(glUniform4fv(prog->vs.pos_fixup_location, 1, position_fixup)); + checkGLcall("glUniform4fv"); + } + + if (update_mask & WINED3D_SHADER_CONST_BASE_VERTEX_ID) + { + GL_EXTCALL(glUniform1i(prog->vs.base_vertex_id_location, state->base_vertex_index)); + checkGLcall("base vertex id"); + } + + if (update_mask & WINED3D_SHADER_CONST_FFP_MODELVIEW) + { + struct wined3d_matrix mat; + + get_modelview_matrix(context, state, 0, &mat); + GL_EXTCALL(glUniformMatrix4fv(prog->vs.modelview_matrix_location[0], 1, FALSE, &mat._11)); + checkGLcall("glUniformMatrix4fv"); + + shader_glsl_ffp_vertex_normalmatrix_uniform(context_gl, state, prog); + } + + if (update_mask & WINED3D_SHADER_CONST_FFP_VERTEXBLEND) + { + struct wined3d_matrix mat; + + for (i = 1; i < MAX_VERTEX_BLENDS; ++i) + { + if (prog->vs.modelview_matrix_location[i] == -1) + break; + + get_modelview_matrix(context, state, i, &mat); + GL_EXTCALL(glUniformMatrix4fv(prog->vs.modelview_matrix_location[i], 1, FALSE, &mat._11)); + checkGLcall("glUniformMatrix4fv"); + } + } + + if (update_mask & WINED3D_SHADER_CONST_FFP_PROJ) + { + struct wined3d_matrix projection; + + get_projection_matrix(context, state, &projection); + GL_EXTCALL(glUniformMatrix4fv(prog->vs.projection_matrix_location, 1, FALSE, &projection._11)); + checkGLcall("glUniformMatrix4fv"); + } + + if (update_mask & WINED3D_SHADER_CONST_FFP_TEXMATRIX) + { + for (i = 0; i < WINED3D_MAX_TEXTURES; ++i) + shader_glsl_ffp_vertex_texmatrix_uniform(context_gl, state, i, prog); + } + + if (update_mask & WINED3D_SHADER_CONST_FFP_MATERIAL) + shader_glsl_ffp_vertex_material_uniform(context_gl, state, prog); + + if (update_mask & WINED3D_SHADER_CONST_FFP_LIGHTS) + { + unsigned int point_idx, spot_idx, directional_idx, parallel_point_idx; + DWORD point_count = 0; + DWORD spot_count = 0; + DWORD directional_count = 0; + DWORD parallel_point_count = 0; + + for (i = 0; i < WINED3D_MAX_ACTIVE_LIGHTS; ++i) + { + if (!state->light_state.lights[i]) + continue; + + switch (state->light_state.lights[i]->OriginalParms.type) + { + case WINED3D_LIGHT_POINT: + ++point_count; + break; + case WINED3D_LIGHT_SPOT: + ++spot_count; + break; + case WINED3D_LIGHT_DIRECTIONAL: + ++directional_count; + break; + case WINED3D_LIGHT_PARALLELPOINT: + ++parallel_point_count; + break; + default: + FIXME("Unhandled light type %#x.\n", state->light_state.lights[i]->OriginalParms.type); + break; + } + } + point_idx = 0; + spot_idx = point_idx + point_count; + directional_idx = spot_idx + spot_count; + parallel_point_idx = directional_idx + directional_count; + + shader_glsl_ffp_vertex_lightambient_uniform(context_gl, state, prog); + for (i = 0; i < WINED3D_MAX_ACTIVE_LIGHTS; ++i) + { + const struct wined3d_light_info *light_info = state->light_state.lights[i]; + unsigned int idx; + + if (!light_info) + continue; + + switch (light_info->OriginalParms.type) + { + case WINED3D_LIGHT_POINT: + idx = point_idx++; + break; + case WINED3D_LIGHT_SPOT: + idx = spot_idx++; + break; + case WINED3D_LIGHT_DIRECTIONAL: + idx = directional_idx++; + break; + case WINED3D_LIGHT_PARALLELPOINT: + idx = parallel_point_idx++; + break; + default: + FIXME("Unhandled light type %#x.\n", light_info->OriginalParms.type); + continue; + } + shader_glsl_ffp_vertex_light_uniform(context_gl, state, idx, light_info, prog); + } + } + + if (update_mask & WINED3D_SHADER_CONST_PS_F) + shader_glsl_load_constants_f(pshader, gl_info, state->ps_consts_f, + prog->ps.uniform_f_locations, &priv->pconst_heap, priv->stack, constant_version); + + if (update_mask & WINED3D_SHADER_CONST_PS_I) + shader_glsl_load_constants_i(pshader, gl_info, state->ps_consts_i, + prog->ps.uniform_i_locations, pshader->reg_maps.integer_constants); + + if (update_mask & WINED3D_SHADER_CONST_PS_B) + shader_glsl_load_constantsB(pshader, gl_info, prog->ps.uniform_b_locations, state->ps_consts_b, + pshader->reg_maps.boolean_constants); + + if (update_mask & WINED3D_SHADER_CONST_PS_BUMP_ENV) + { + for (i = 0; i < WINED3D_MAX_TEXTURES; ++i) + { + if (prog->ps.bumpenv_mat_location[i] == -1) + continue; + + GL_EXTCALL(glUniformMatrix2fv(prog->ps.bumpenv_mat_location[i], 1, 0, + (const GLfloat *)&state->texture_states[i][WINED3D_TSS_BUMPENV_MAT00])); + + if (prog->ps.bumpenv_lum_scale_location[i] != -1) + { + GL_EXTCALL(glUniform1fv(prog->ps.bumpenv_lum_scale_location[i], 1, + (const GLfloat *)&state->texture_states[i][WINED3D_TSS_BUMPENV_LSCALE])); + GL_EXTCALL(glUniform1fv(prog->ps.bumpenv_lum_offset_location[i], 1, + (const GLfloat *)&state->texture_states[i][WINED3D_TSS_BUMPENV_LOFFSET])); + } + } + + checkGLcall("bump env uniforms"); + } + + if (update_mask & WINED3D_SHADER_CONST_PS_Y_CORR) + { + const struct wined3d_vec4 correction_params = + { + /* Position is relative to the framebuffer, not the viewport. */ + context->render_offscreen ? 0.0f : (float)state->fb.render_targets[0]->height, + context->render_offscreen ? 1.0f : -1.0f, + 0.0f, + 0.0f, + }; + + GL_EXTCALL(glUniform4fv(prog->ps.ycorrection_location, 1, &correction_params.x)); + } + + if (update_mask & WINED3D_SHADER_CONST_PS_NP2_FIXUP) + shader_glsl_load_np2fixup_constants(&prog->ps, gl_info, state); + if (update_mask & WINED3D_SHADER_CONST_FFP_COLOR_KEY) + shader_glsl_load_color_key_constant(&prog->ps, gl_info, state); + + if (update_mask & WINED3D_SHADER_CONST_FFP_PS) + { + struct wined3d_color color; + + if (prog->ps.tex_factor_location != -1) + { + wined3d_color_from_d3dcolor(&color, state->render_states[WINED3D_RS_TEXTUREFACTOR]); + GL_EXTCALL(glUniform4fv(prog->ps.tex_factor_location, 1, &color.r)); + } + + if (state->render_states[WINED3D_RS_SPECULARENABLE]) + GL_EXTCALL(glUniform4f(prog->ps.specular_enable_location, 1.0f, 1.0f, 1.0f, 0.0f)); + else + GL_EXTCALL(glUniform4f(prog->ps.specular_enable_location, 0.0f, 0.0f, 0.0f, 0.0f)); + + for (i = 0; i < WINED3D_MAX_TEXTURES; ++i) + { + if (prog->ps.tss_constant_location[i] == -1) + continue; + + wined3d_color_from_d3dcolor(&color, state->texture_states[i][WINED3D_TSS_CONSTANT]); + GL_EXTCALL(glUniform4fv(prog->ps.tss_constant_location[i], 1, &color.r)); + } + + checkGLcall("fixed function uniforms"); + } + + if (update_mask & WINED3D_SHADER_CONST_PS_FOG) + shader_glsl_load_fog_uniform(context_gl, state, prog); + + if (update_mask & WINED3D_SHADER_CONST_PS_ALPHA_TEST) + { + float ref = wined3d_alpha_ref(state); + + GL_EXTCALL(glUniform1f(prog->ps.alpha_test_ref_location, ref)); + checkGLcall("alpha test emulation uniform"); + } + + if (priv->next_constant_version == UINT_MAX) + { + TRACE("Max constant version reached, resetting to 0.\n"); + wine_rb_for_each_entry(&priv->program_lookup, reset_program_constant_version, NULL); + priv->next_constant_version = 1; + } + else + { + prog->constant_version = priv->next_constant_version++; + } +} + +static void update_heap_entry(struct constant_heap *heap, unsigned int idx, DWORD new_version) +{ + struct constant_entry *entries = heap->entries; + unsigned int *positions = heap->positions; + unsigned int heap_idx, parent_idx; + + if (!heap->contained[idx]) + { + heap_idx = heap->size++; + heap->contained[idx] = TRUE; + } + else + { + heap_idx = positions[idx]; + } + + while (heap_idx > 1) + { + parent_idx = heap_idx >> 1; + + if (new_version <= entries[parent_idx].version) break; + + entries[heap_idx] = entries[parent_idx]; + positions[entries[parent_idx].idx] = heap_idx; + heap_idx = parent_idx; + } + + entries[heap_idx].version = new_version; + entries[heap_idx].idx = idx; + positions[idx] = heap_idx; +} + +static void shader_glsl_update_float_vertex_constants(struct wined3d_device *device, UINT start, UINT count) +{ + struct shader_glsl_priv *priv = device->shader_priv; + struct constant_heap *heap = &priv->vconst_heap; + UINT i; + + for (i = start; i < count + start; ++i) + { + update_heap_entry(heap, i, priv->next_constant_version); + } +} + +static void shader_glsl_update_float_pixel_constants(struct wined3d_device *device, UINT start, UINT count) +{ + struct shader_glsl_priv *priv = device->shader_priv; + struct constant_heap *heap = &priv->pconst_heap; + UINT i; + + for (i = start; i < count + start; ++i) + { + update_heap_entry(heap, i, priv->next_constant_version); + } +} + +static unsigned int vec4_varyings(DWORD shader_major, const struct wined3d_gl_info *gl_info) +{ + unsigned int ret = gl_info->limits.glsl_varyings / 4; + /* 4.0 shaders do not write clip coords because d3d10 does not support user clipplanes */ + if(shader_major > 3) return ret; + + /* 3.0 shaders may need an extra varying for the clip coord on some cards(mostly dx10 ones) */ + if (gl_info->quirks & WINED3D_QUIRK_GLSL_CLIP_VARYING) ret -= 1; + return ret; +} + +static BOOL needs_legacy_glsl_syntax(const struct wined3d_gl_info *gl_info) +{ + return gl_info->glsl_version < MAKEDWORD_VERSION(1, 30); +} + +static BOOL shader_glsl_use_explicit_attrib_location(const struct wined3d_gl_info *gl_info) +{ + return gl_info->supported[ARB_EXPLICIT_ATTRIB_LOCATION] + && shader_glsl_use_layout_qualifier(gl_info) && !needs_legacy_glsl_syntax(gl_info); +} + +static BOOL shader_glsl_use_interface_blocks(const struct wined3d_gl_info *gl_info) +{ + return shader_glsl_get_version(gl_info) >= 150; +} + +static const char *get_attribute_keyword(const struct wined3d_gl_info *gl_info) +{ + return needs_legacy_glsl_syntax(gl_info) ? "attribute" : "in"; +} + +static void PRINTF_ATTR(4, 5) declare_in_varying(const struct wined3d_gl_info *gl_info, + struct wined3d_string_buffer *buffer, BOOL flat, const char *format, ...) +{ + va_list args; + int ret; + + shader_addline(buffer, "%s%s ", flat ? "flat " : "", + needs_legacy_glsl_syntax(gl_info) ? "varying" : "in"); + for (;;) + { + va_start(args, format); + ret = shader_vaddline(buffer, format, args); + va_end(args); + if (!ret) + return; + if (!string_buffer_resize(buffer, ret)) + return; + } +} + +static void PRINTF_ATTR(4, 5) declare_out_varying(const struct wined3d_gl_info *gl_info, + struct wined3d_string_buffer *buffer, BOOL flat, const char *format, ...) +{ + va_list args; + int ret; + + shader_addline(buffer, "%s%s ", flat ? "flat " : "", + needs_legacy_glsl_syntax(gl_info) ? "varying" : "out"); + for (;;) + { + va_start(args, format); + ret = shader_vaddline(buffer, format, args); + va_end(args); + if (!ret) + return; + if (!string_buffer_resize(buffer, ret)) + return; + } +} + +static const char *shader_glsl_shader_input_name(const struct wined3d_gl_info *gl_info) +{ + return shader_glsl_use_interface_blocks(gl_info) ? "shader_in.reg" : "ps_link"; +} + +static const char *shader_glsl_shader_output_name(const struct wined3d_gl_info *gl_info) +{ + return shader_glsl_use_interface_blocks(gl_info) ? "shader_out.reg" : "ps_link"; +} + +static const char *shader_glsl_interpolation_qualifiers(enum wined3d_shader_interpolation_mode mode) +{ + switch (mode) + { + case WINED3DSIM_CONSTANT: + return "flat "; + case WINED3DSIM_LINEAR_NOPERSPECTIVE: + return "noperspective "; + default: + FIXME("Unhandled interpolation mode %#x.\n", mode); + case WINED3DSIM_NONE: + case WINED3DSIM_LINEAR: + return ""; + } +} + +static enum wined3d_shader_interpolation_mode wined3d_extract_interpolation_mode( + const DWORD *packed_interpolation_mode, unsigned int register_idx) +{ + return wined3d_extract_bits(packed_interpolation_mode, + register_idx * WINED3D_PACKED_INTERPOLATION_BIT_COUNT, WINED3D_PACKED_INTERPOLATION_BIT_COUNT); +} + +static void shader_glsl_declare_shader_inputs(const struct wined3d_gl_info *gl_info, + struct wined3d_string_buffer *buffer, unsigned int element_count, + const DWORD *interpolation_mode, BOOL unroll) +{ + enum wined3d_shader_interpolation_mode mode; + unsigned int i; + + if (shader_glsl_use_interface_blocks(gl_info)) + { + if (unroll) + { + shader_addline(buffer, "in shader_in_out {\n"); + for (i = 0; i < element_count; ++i) + { + mode = wined3d_extract_interpolation_mode(interpolation_mode, i); + shader_addline(buffer, " %svec4 reg%u;\n", shader_glsl_interpolation_qualifiers(mode), i); + } + shader_addline(buffer, "} shader_in;\n"); + } + else + { + shader_addline(buffer, "in shader_in_out { vec4 reg[%u]; } shader_in;\n", element_count); + } + } + else + { + declare_in_varying(gl_info, buffer, FALSE, "vec4 ps_link[%u];\n", element_count); + } +} + +static BOOL needs_interpolation_qualifiers_for_shader_outputs(const struct wined3d_gl_info *gl_info) +{ + /* In GLSL 4.40+ it is fine to specify interpolation qualifiers only in + * fragment shaders. In older GLSL versions interpolation qualifiers must + * match between shader stages. */ + return gl_info->glsl_version < MAKEDWORD_VERSION(4, 40); +} + +static void shader_glsl_declare_shader_outputs(const struct wined3d_gl_info *gl_info, + struct wined3d_string_buffer *buffer, unsigned int element_count, BOOL rasterizer_setup, + const DWORD *interpolation_mode) +{ + enum wined3d_shader_interpolation_mode mode; + unsigned int i; + + if (shader_glsl_use_interface_blocks(gl_info)) + { + if (rasterizer_setup) + { + shader_addline(buffer, "out shader_in_out {\n"); + for (i = 0; i < element_count; ++i) + { + const char *interpolation_qualifiers = ""; + if (needs_interpolation_qualifiers_for_shader_outputs(gl_info)) + { + mode = wined3d_extract_interpolation_mode(interpolation_mode, i); + interpolation_qualifiers = shader_glsl_interpolation_qualifiers(mode); + } + shader_addline(buffer, " %svec4 reg%u;\n", interpolation_qualifiers, i); + } + shader_addline(buffer, "} shader_out;\n"); + } + else + { + shader_addline(buffer, "out shader_in_out { vec4 reg[%u]; } shader_out;\n", element_count); + } + } + else + { + declare_out_varying(gl_info, buffer, FALSE, "vec4 ps_link[%u];\n", element_count); + } +} + +static BOOL use_legacy_fragment_output(const struct wined3d_gl_info *gl_info) +{ + /* Technically 1.30 does support user-defined fragment shader outputs but + * we might not have glBindFragDataLocation() available (i.e. GL version + * might be < 3.0). */ + return gl_info->glsl_version <= MAKEDWORD_VERSION(1, 30); +} + +static const char *get_fragment_output(const struct wined3d_gl_info *gl_info) +{ + return use_legacy_fragment_output(gl_info) ? "gl_FragData" : "ps_out"; +} + +static const char *glsl_primitive_type_from_d3d(enum wined3d_primitive_type primitive_type) +{ + switch (primitive_type) + { + case WINED3D_PT_POINTLIST: + return "points"; + + case WINED3D_PT_LINELIST: + return "lines"; + + case WINED3D_PT_LINESTRIP: + return "line_strip"; + + case WINED3D_PT_TRIANGLELIST: + return "triangles"; + + case WINED3D_PT_TRIANGLESTRIP: + return "triangle_strip"; + + case WINED3D_PT_LINELIST_ADJ: + return "lines_adjacency"; + + case WINED3D_PT_TRIANGLELIST_ADJ: + return "triangles_adjacency"; + + default: + FIXME("Unhandled primitive type %s.\n", debug_d3dprimitivetype(primitive_type)); + return ""; + } +} + +static BOOL glsl_is_color_reg_read(const struct wined3d_shader *shader, unsigned int idx) +{ + const struct wined3d_shader_signature *input_signature = &shader->input_signature; + const struct wined3d_shader_reg_maps *reg_maps = &shader->reg_maps; + DWORD input_reg_used = shader->u.ps.input_reg_used; + unsigned int i; + + if (reg_maps->shader_version.major < 3) + return input_reg_used & (1u << idx); + + for (i = 0; i < input_signature->element_count; ++i) + { + const struct wined3d_shader_signature_element *input = &input_signature->elements[i]; + + if (!(reg_maps->input_registers & (1u << input->register_idx))) + continue; + + if (shader_match_semantic(input->semantic_name, WINED3D_DECL_USAGE_COLOR) + && input->semantic_idx == idx) + return input_reg_used & (1u << input->register_idx); + } + return FALSE; +} + +static BOOL glsl_is_shadow_sampler(const struct wined3d_shader *shader, + const struct ps_compile_args *ps_args, unsigned int resource_idx, unsigned int sampler_idx) +{ + const struct wined3d_shader_version *version = &shader->reg_maps.shader_version; + + if (version->major >= 4) + return shader->reg_maps.sampler_comparison_mode & (1u << sampler_idx); + else + return version->type == WINED3D_SHADER_TYPE_PIXEL && (ps_args->shadow & (1u << resource_idx)); +} + +static void shader_glsl_declare_typed_vertex_attribute(struct wined3d_string_buffer *buffer, + const struct wined3d_gl_info *gl_info, const char *vector_type, const char *scalar_type, + unsigned int index) +{ + shader_addline(buffer, "%s %s4 vs_in_%s%u;\n", + get_attribute_keyword(gl_info), vector_type, scalar_type, index); + shader_addline(buffer, "vec4 vs_in%u = %sBitsToFloat(vs_in_%s%u);\n", + index, scalar_type, scalar_type, index); +} + +static void shader_glsl_declare_generic_vertex_attribute(struct wined3d_string_buffer *buffer, + const struct wined3d_gl_info *gl_info, const struct wined3d_shader_signature_element *e) +{ + unsigned int index = e->register_idx; + enum wined3d_component_type type; + + if (e->sysval_semantic == WINED3D_SV_VERTEX_ID) + { + shader_addline(buffer, "uniform int base_vertex_id;\n"); + shader_addline(buffer, "vec4 vs_in%u = vec4(intBitsToFloat(gl_VertexID - base_vertex_id), 0.0, 0.0, 0.0);\n", index); + return; + } + if (e->sysval_semantic == WINED3D_SV_INSTANCE_ID) + { + shader_addline(buffer, "vec4 vs_in%u = vec4(intBitsToFloat(gl_InstanceID), 0.0, 0.0, 0.0);\n", + index); + return; + } + if (e->sysval_semantic && e->sysval_semantic != WINED3D_SV_POSITION) + FIXME("Unhandled sysval semantic %#x.\n", e->sysval_semantic); + + if (shader_glsl_use_explicit_attrib_location(gl_info)) + shader_addline(buffer, "layout(location = %u) ", index); + + type = e->component_type; + if ((unsigned int)type >= ARRAY_SIZE(component_type_info)) + { + FIXME("Unhandled type %#x.\n", type); + type = WINED3D_TYPE_FLOAT; + } + if (type == WINED3D_TYPE_FLOAT || type == WINED3D_TYPE_UNKNOWN) + shader_addline(buffer, "%s vec4 vs_in%u;\n", get_attribute_keyword(gl_info), index); + else + shader_glsl_declare_typed_vertex_attribute(buffer, gl_info, + component_type_info[type].glsl_vector_type, + component_type_info[type].glsl_scalar_type, index); +} + +/** Generate the variable & register declarations for the GLSL output target */ +static void shader_generate_glsl_declarations(const struct wined3d_context_gl *context_gl, + struct wined3d_string_buffer *buffer, const struct wined3d_shader *shader, + const struct wined3d_shader_reg_maps *reg_maps, const struct shader_glsl_ctx_priv *ctx_priv) +{ + const struct wined3d_shader_version *version = ®_maps->shader_version; + const struct vs_compile_args *vs_args = ctx_priv->cur_vs_args; + const struct ps_compile_args *ps_args = ctx_priv->cur_ps_args; + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + const struct wined3d_shader_indexable_temp *idx_temp_reg; + unsigned int uniform_block_base, uniform_block_count; + enum wined3d_shader_resource_type resource_type; + const struct wined3d_shader_lconst *lconst; + const char *prefix; + unsigned int i; + DWORD map; + + if (wined3d_settings.strict_shader_math) + shader_addline(buffer, "#pragma optionNV(fastmath off)\n"); + + prefix = shader_glsl_get_prefix(version->type); + + /* Prototype the subroutines */ + for (i = 0, map = reg_maps->labels; map; map >>= 1, ++i) + { + if (map & 1) shader_addline(buffer, "void subroutine%u();\n", i); + } + + /* Declare the constants (aka uniforms) */ + if (shader->limits->constant_float > 0) + { + unsigned max_constantsF; + + /* Unless the shader uses indirect addressing, always declare the + * maximum array size and ignore that we need some uniforms privately. + * E.g. if GL supports 256 uniforms, and we need 2 for the pos fixup + * and immediate values, still declare VC[256]. If the shader needs + * more uniforms than we have it won't work in any case. If it uses + * less, the compiler will figure out which uniforms are really used + * and strip them out. This allows a shader to use c255 on a dx9 card, + * as long as it doesn't also use all the other constants. + * + * If the shader uses indirect addressing the compiler must assume + * that all declared uniforms are used. In this case, declare only the + * amount that we're assured to have. + * + * Thus we run into problems in these two cases: + * 1) The shader really uses more uniforms than supported. + * 2) The shader uses indirect addressing, less constants than + * supported, but uses a constant index > #supported consts. */ + if (version->type == WINED3D_SHADER_TYPE_PIXEL) + { + /* No indirect addressing here. */ + max_constantsF = gl_info->limits.glsl_ps_float_constants; + } + else + { + if (reg_maps->usesrelconstF) + { + /* Subtract the other potential uniforms from the max + * available (bools, ints, and 1 row of projection matrix). + * Subtract another uniform for immediate values, which have + * to be loaded via uniform by the driver as well. The shader + * code only uses 0.5, 2.0, 1.0, 128 and -128 in vertex + * shader code, so one vec4 should be enough. (Unfortunately + * the Nvidia driver doesn't store 128 and -128 in one float). + * + * Writing gl_ClipVertex requires one uniform for each + * clipplane as well. */ + max_constantsF = gl_info->limits.glsl_vs_float_constants - 3; + if (vs_args->clip_enabled) + max_constantsF -= gl_info->limits.user_clip_distances; + max_constantsF -= wined3d_popcount(reg_maps->integer_constants); + /* Strictly speaking a bool only uses one scalar, but the nvidia(Linux) compiler doesn't pack them properly, + * so each scalar requires a full vec4. We could work around this by packing the booleans ourselves, but + * for now take this into account when calculating the number of available constants + */ + max_constantsF -= wined3d_popcount(reg_maps->boolean_constants); + /* Set by driver quirks in directx.c */ + max_constantsF -= gl_info->reserved_glsl_constants; + + if (max_constantsF < shader->limits->constant_float) + { + static unsigned int once; + + if (!once++) + ERR_(winediag)("The hardware does not support enough uniform components to run this shader," + " it may not render correctly.\n"); + else + WARN("The hardware does not support enough uniform components to run this shader.\n"); + } + } + else + { + max_constantsF = gl_info->limits.glsl_vs_float_constants; + } + } + max_constantsF = min(shader->limits->constant_float, max_constantsF); + shader_addline(buffer, "uniform vec4 %s_c[%u];\n", prefix, max_constantsF); + } + + /* Always declare the full set of constants, the compiler can remove the + * unused ones because d3d doesn't (yet) support indirect int and bool + * constant addressing. This avoids problems if the app uses e.g. i0 and i9. */ + if (shader->limits->constant_int > 0 && reg_maps->integer_constants) + shader_addline(buffer, "uniform ivec4 %s_i[%u];\n", prefix, shader->limits->constant_int); + + if (shader->limits->constant_bool > 0 && reg_maps->boolean_constants) + shader_addline(buffer, "uniform bool %s_b[%u];\n", prefix, shader->limits->constant_bool); + + /* Declare immediate constant buffer */ + if (reg_maps->icb) + shader_addline(buffer, "uniform vec4 %s_icb[%u];\n", prefix, reg_maps->icb->vec4_count); + + /* Declare constant buffers */ + wined3d_gl_limits_get_uniform_block_range(&gl_info->limits, version->type, + &uniform_block_base, &uniform_block_count); + for (i = 0; i < min(uniform_block_count, WINED3D_MAX_CBS); ++i) + { + if (reg_maps->cb_sizes[i]) + { + shader_addline(buffer, "layout(std140"); + if (shader_glsl_use_layout_binding_qualifier(gl_info)) + shader_addline(buffer, ", binding = %u", uniform_block_base + i); + shader_addline(buffer, ") uniform block_%s_cb%u { vec4 %s_cb%u[%u]; };\n", + prefix, i, prefix, i, reg_maps->cb_sizes[i]); + } + } + + /* Declare texture samplers */ + for (i = 0; i < reg_maps->sampler_map.count; ++i) + { + struct wined3d_shader_sampler_map_entry *entry; + const char *sampler_type_prefix, *sampler_type; + BOOL shadow_sampler, tex_rect; + + entry = ®_maps->sampler_map.entries[i]; + + if (entry->resource_idx >= ARRAY_SIZE(reg_maps->resource_info)) + { + ERR("Invalid resource index %u.\n", entry->resource_idx); + continue; + } + + switch (reg_maps->resource_info[entry->resource_idx].data_type) + { + case WINED3D_DATA_FLOAT: + case WINED3D_DATA_UNORM: + case WINED3D_DATA_SNORM: + sampler_type_prefix = ""; + break; + + case WINED3D_DATA_INT: + sampler_type_prefix = "i"; + break; + + case WINED3D_DATA_UINT: + sampler_type_prefix = "u"; + break; + + default: + sampler_type_prefix = ""; + ERR("Unhandled resource data type %#x.\n", reg_maps->resource_info[i].data_type); + break; + } + + shadow_sampler = glsl_is_shadow_sampler(shader, ps_args, entry->resource_idx, entry->sampler_idx); + resource_type = version->type == WINED3D_SHADER_TYPE_PIXEL + ? pixelshader_get_resource_type(reg_maps, entry->resource_idx, ps_args->tex_types) + : reg_maps->resource_info[entry->resource_idx].type; + + switch (resource_type) + { + case WINED3D_SHADER_RESOURCE_BUFFER: + sampler_type = "samplerBuffer"; + break; + + case WINED3D_SHADER_RESOURCE_TEXTURE_1D: + if (shadow_sampler) + sampler_type = "sampler1DShadow"; + else + sampler_type = "sampler1D"; + break; + + case WINED3D_SHADER_RESOURCE_TEXTURE_2D: + tex_rect = version->type == WINED3D_SHADER_TYPE_PIXEL + && (ps_args->np2_fixup & (1u << entry->resource_idx)) + && gl_info->supported[ARB_TEXTURE_RECTANGLE]; + if (shadow_sampler) + { + if (tex_rect) + sampler_type = "sampler2DRectShadow"; + else + sampler_type = "sampler2DShadow"; + } + else + { + if (tex_rect) + sampler_type = "sampler2DRect"; + else + sampler_type = "sampler2D"; + } + break; + + case WINED3D_SHADER_RESOURCE_TEXTURE_3D: + if (shadow_sampler) + FIXME("Unsupported 3D shadow sampler.\n"); + sampler_type = "sampler3D"; + break; + + case WINED3D_SHADER_RESOURCE_TEXTURE_CUBE: + if (shadow_sampler) + sampler_type = "samplerCubeShadow"; + else + sampler_type = "samplerCube"; + break; + + case WINED3D_SHADER_RESOURCE_TEXTURE_1DARRAY: + if (shadow_sampler) + sampler_type = "sampler1DArrayShadow"; + else + sampler_type = "sampler1DArray"; + break; + + case WINED3D_SHADER_RESOURCE_TEXTURE_2DARRAY: + if (shadow_sampler) + sampler_type = "sampler2DArrayShadow"; + else + sampler_type = "sampler2DArray"; + break; + + case WINED3D_SHADER_RESOURCE_TEXTURE_CUBEARRAY: + if (shadow_sampler) + sampler_type = "samplerCubeArrayShadow"; + else + sampler_type = "samplerCubeArray"; + break; + + case WINED3D_SHADER_RESOURCE_TEXTURE_2DMS: + sampler_type = "sampler2DMS"; + break; + + case WINED3D_SHADER_RESOURCE_TEXTURE_2DMSARRAY: + sampler_type = "sampler2DMSArray"; + break; + + default: + sampler_type = "unsupported_sampler"; + FIXME("Unhandled resource type %#x.\n", resource_type); + break; + } + + if (shader_glsl_use_layout_binding_qualifier(gl_info)) + shader_glsl_append_sampler_binding_qualifier(buffer, &context_gl->c, version, entry->bind_idx); + shader_addline(buffer, "uniform %s%s %s_sampler%u;\n", + sampler_type_prefix, sampler_type, prefix, entry->bind_idx); + } + + /* Declare images */ + for (i = 0; i < ARRAY_SIZE(reg_maps->uav_resource_info); ++i) + { + const char *image_type_prefix, *image_type, *read_format; + + if (!reg_maps->uav_resource_info[i].type) + continue; + + switch (reg_maps->uav_resource_info[i].data_type) + { + case WINED3D_DATA_FLOAT: + case WINED3D_DATA_UNORM: + case WINED3D_DATA_SNORM: + image_type_prefix = ""; + read_format = "r32f"; + break; + + case WINED3D_DATA_INT: + image_type_prefix = "i"; + read_format = "r32i"; + break; + + case WINED3D_DATA_UINT: + image_type_prefix = "u"; + read_format = "r32ui"; + break; + + default: + image_type_prefix = ""; + read_format = ""; + ERR("Unhandled resource data type %#x.\n", reg_maps->uav_resource_info[i].data_type); + break; + } + + switch (reg_maps->uav_resource_info[i].type) + { + case WINED3D_SHADER_RESOURCE_BUFFER: + image_type = "imageBuffer"; + break; + + case WINED3D_SHADER_RESOURCE_TEXTURE_1D: + image_type = "image1D"; + break; + + case WINED3D_SHADER_RESOURCE_TEXTURE_2D: + image_type = "image2D"; + break; + + case WINED3D_SHADER_RESOURCE_TEXTURE_3D: + image_type = "image3D"; + break; + + case WINED3D_SHADER_RESOURCE_TEXTURE_1DARRAY: + image_type = "image1DArray"; + break; + + case WINED3D_SHADER_RESOURCE_TEXTURE_2DARRAY: + image_type = "image2DArray"; + break; + + default: + image_type = "unsupported_image"; + FIXME("Unhandled resource type %#x.\n", reg_maps->uav_resource_info[i].type); + break; + } + + if (shader_glsl_use_layout_binding_qualifier(gl_info)) + shader_addline(buffer, "layout(binding = %u)\n", i); + if (reg_maps->uav_read_mask & (1u << i)) + shader_addline(buffer, "layout(%s) uniform %s%s %s_image%u;\n", + read_format, image_type_prefix, image_type, prefix, i); + else + shader_addline(buffer, "writeonly uniform %s%s %s_image%u;\n", + image_type_prefix, image_type, prefix, i); + + if (reg_maps->uav_counter_mask & (1u << i)) + shader_addline(buffer, "layout(binding = %u) uniform atomic_uint %s_counter%u;\n", + i, prefix, i); + } + + /* Declare address variables */ + for (i = 0, map = reg_maps->address; map; map >>= 1, ++i) + { + if (map & 1) shader_addline(buffer, "ivec4 A%u;\n", i); + } + + /* Declare output register temporaries */ + if (shader->limits->packed_output) + shader_addline(buffer, "vec4 %s_out[%u];\n", prefix, shader->limits->packed_output); + + /* Declare temporary variables */ + if (reg_maps->temporary_count) + { + for (i = 0; i < reg_maps->temporary_count; ++i) + shader_addline(buffer, "vec4 R%u;\n", i); + } + else if (version->major < 4) + { + for (i = 0, map = reg_maps->temporary; map; map >>= 1, ++i) + { + if (map & 1) + shader_addline(buffer, "vec4 R%u;\n", i); + } + } + + /* Declare indexable temporary variables */ + LIST_FOR_EACH_ENTRY(idx_temp_reg, ®_maps->indexable_temps, struct wined3d_shader_indexable_temp, entry) + { + if (idx_temp_reg->component_count != 4) + FIXME("Ignoring component count %u.\n", idx_temp_reg->component_count); + shader_addline(buffer, "vec4 X%u[%u];\n", idx_temp_reg->register_idx, idx_temp_reg->register_size); + } + + /* Declare loop registers aLx */ + if (version->major < 4) + { + for (i = 0; i < reg_maps->loop_depth; ++i) + { + shader_addline(buffer, "int aL%u;\n", i); + shader_addline(buffer, "int tmpInt%u;\n", i); + } + } + + /* Temporary variables for matrix operations */ + shader_addline(buffer, "vec4 tmp0;\n"); + shader_addline(buffer, "vec4 tmp1;\n"); + if (gl_info->supported[ARB_GPU_SHADER5]) + shader_addline(buffer, "precise vec4 tmp_precise;\n"); + else + shader_addline(buffer, "/* precise */ vec4 tmp_precise;\n"); + + if (!shader->load_local_constsF) + { + LIST_FOR_EACH_ENTRY(lconst, &shader->constantsF, struct wined3d_shader_lconst, entry) + { + shader_addline(buffer, "const vec4 %s_lc%u = ", prefix, lconst->idx); + shader_glsl_append_imm_vec(buffer, (const float *)lconst->value, ARRAY_SIZE(lconst->value), gl_info); + shader_addline(buffer, ";\n"); + } + } +} + +/* Prototypes */ +static void shader_glsl_add_src_param_ext(const struct wined3d_shader_context *ctx, + const struct wined3d_shader_src_param *wined3d_src, DWORD mask, struct glsl_src_param *glsl_src, + enum wined3d_data_type data_type); + +/** Used for opcode modifiers - They multiply the result by the specified amount */ +static const char * const shift_glsl_tab[] = { + "", /* 0 (none) */ + "2.0 * ", /* 1 (x2) */ + "4.0 * ", /* 2 (x4) */ + "8.0 * ", /* 3 (x8) */ + "16.0 * ", /* 4 (x16) */ + "32.0 * ", /* 5 (x32) */ + "", /* 6 (x64) */ + "", /* 7 (x128) */ + "", /* 8 (d256) */ + "", /* 9 (d128) */ + "", /* 10 (d64) */ + "", /* 11 (d32) */ + "0.0625 * ", /* 12 (d16) */ + "0.125 * ", /* 13 (d8) */ + "0.25 * ", /* 14 (d4) */ + "0.5 * " /* 15 (d2) */ +}; + +/* Generate a GLSL parameter that does the input modifier computation and return the input register/mask to use */ +static void shader_glsl_gen_modifier(enum wined3d_shader_src_modifier src_modifier, + const char *in_reg, const char *in_regswizzle, char *out_str) +{ + switch (src_modifier) + { + case WINED3DSPSM_DZ: /* Need to handle this in the instructions itself (texld & texcrd). */ + case WINED3DSPSM_DW: + case WINED3DSPSM_NONE: + sprintf(out_str, "%s%s", in_reg, in_regswizzle); + break; + case WINED3DSPSM_NEG: + sprintf(out_str, "-%s%s", in_reg, in_regswizzle); + break; + case WINED3DSPSM_NOT: + sprintf(out_str, "!%s%s", in_reg, in_regswizzle); + break; + case WINED3DSPSM_BIAS: + sprintf(out_str, "(%s%s - vec4(0.5)%s)", in_reg, in_regswizzle, in_regswizzle); + break; + case WINED3DSPSM_BIASNEG: + sprintf(out_str, "-(%s%s - vec4(0.5)%s)", in_reg, in_regswizzle, in_regswizzle); + break; + case WINED3DSPSM_SIGN: + sprintf(out_str, "(2.0 * (%s%s - 0.5))", in_reg, in_regswizzle); + break; + case WINED3DSPSM_SIGNNEG: + sprintf(out_str, "-(2.0 * (%s%s - 0.5))", in_reg, in_regswizzle); + break; + case WINED3DSPSM_COMP: + sprintf(out_str, "(1.0 - %s%s)", in_reg, in_regswizzle); + break; + case WINED3DSPSM_X2: + sprintf(out_str, "(2.0 * %s%s)", in_reg, in_regswizzle); + break; + case WINED3DSPSM_X2NEG: + sprintf(out_str, "-(2.0 * %s%s)", in_reg, in_regswizzle); + break; + case WINED3DSPSM_ABS: + sprintf(out_str, "abs(%s%s)", in_reg, in_regswizzle); + break; + case WINED3DSPSM_ABSNEG: + sprintf(out_str, "-abs(%s%s)", in_reg, in_regswizzle); + break; + default: + FIXME("Unhandled modifier %u\n", src_modifier); + sprintf(out_str, "%s%s", in_reg, in_regswizzle); + } +} + +static void shader_glsl_fixup_scalar_register_variable(struct wined3d_string_buffer *register_name, + const char *glsl_variable, const struct wined3d_gl_info *gl_info) +{ + /* The ARB_shading_language_420pack extension allows swizzle operations on + * scalars. */ + if (gl_info->supported[ARB_SHADING_LANGUAGE_420PACK]) + string_buffer_sprintf(register_name, "%s", glsl_variable); + else + string_buffer_sprintf(register_name, "ivec2(%s, 0)", glsl_variable); +} + +/** Writes the GLSL variable name that corresponds to the register that the + * DX opcode parameter is trying to access */ +static void shader_glsl_get_register_name(const struct wined3d_shader_register *reg, + enum wined3d_data_type data_type, struct wined3d_string_buffer *register_name, + BOOL *is_swizzled, const struct wined3d_shader_context *ctx) +{ + /* oPos, oFog and oPts in D3D */ + static const char * const hwrastout_reg_names[] = {"vs_out[10]", "vs_out[11].x", "vs_out[11].y"}; + + const struct wined3d_shader *shader = ctx->shader; + const struct wined3d_shader_reg_maps *reg_maps = ctx->reg_maps; + const struct wined3d_shader_version *version = ®_maps->shader_version; + const struct shader_glsl_ctx_priv *priv = ctx->backend_data; + const char *prefix = shader_glsl_get_prefix(version->type); + const struct wined3d_gl_info *gl_info = priv->gl_info; + struct glsl_src_param rel_param0, rel_param1; + + if (reg->idx[0].offset != ~0u && reg->idx[0].rel_addr) + shader_glsl_add_src_param_ext(ctx, reg->idx[0].rel_addr, WINED3DSP_WRITEMASK_0, + &rel_param0, reg->idx[0].rel_addr->reg.data_type); + if (reg->idx[1].offset != ~0u && reg->idx[1].rel_addr) + shader_glsl_add_src_param_ext(ctx, reg->idx[1].rel_addr, WINED3DSP_WRITEMASK_0, + &rel_param1, reg->idx[1].rel_addr->reg.data_type); + if (is_swizzled) + *is_swizzled = FALSE; + + switch (reg->type) + { + case WINED3DSPR_TEMP: + string_buffer_sprintf(register_name, "R%u", reg->idx[0].offset); + break; + + case WINED3DSPR_INPUT: + case WINED3DSPR_INCONTROLPOINT: + if (version->type == WINED3D_SHADER_TYPE_VERTEX) + { + if (reg->idx[0].rel_addr) + FIXME("VS3 input registers relative addressing.\n"); + if (is_swizzled && priv->cur_vs_args->swizzle_map & (1u << reg->idx[0].offset)) + *is_swizzled = TRUE; + if (reg->idx[0].rel_addr) + { + string_buffer_sprintf(register_name, "%s_in[%s + %u]", + prefix, rel_param0.param_str, reg->idx[0].offset); + } + else + { + string_buffer_sprintf(register_name, "%s_in%u", prefix, reg->idx[0].offset); + } + break; + } + + if (version->type == WINED3D_SHADER_TYPE_HULL + || version->type == WINED3D_SHADER_TYPE_DOMAIN + || version->type == WINED3D_SHADER_TYPE_GEOMETRY) + { + if (reg->idx[0].rel_addr) + { + if (reg->idx[1].rel_addr) + string_buffer_sprintf(register_name, "shader_in[%s + %u].reg[%s + %u]", + rel_param0.param_str, reg->idx[0].offset, + rel_param1.param_str, reg->idx[1].offset); + else + string_buffer_sprintf(register_name, "shader_in[%s + %u].reg[%u]", + rel_param0.param_str, reg->idx[0].offset, + reg->idx[1].offset); + } + else if (reg->idx[1].rel_addr) + string_buffer_sprintf(register_name, "shader_in[%u].reg[%s + %u]", reg->idx[0].offset, + rel_param1.param_str, reg->idx[1].offset); + else + string_buffer_sprintf(register_name, "shader_in[%u].reg[%u]", + reg->idx[0].offset, reg->idx[1].offset); + break; + } + + /* pixel shaders >= 3.0 */ + if (version->major >= 3) + { + DWORD idx = shader->u.ps.input_reg_map[reg->idx[0].offset]; + unsigned int in_count = vec4_varyings(version->major, gl_info); + + if (reg->idx[0].rel_addr) + { + /* Removing a + 0 would be an obvious optimization, but + * OS X doesn't see the NOP operation there. */ + if (idx) + { + if (needs_legacy_glsl_syntax(gl_info) + && shader->u.ps.declared_in_count > in_count) + { + string_buffer_sprintf(register_name, + "((%s + %u) > %u ? (%s + %u) > %u ? gl_SecondaryColor : gl_Color : %s_in[%s + %u])", + rel_param0.param_str, idx, in_count - 1, rel_param0.param_str, idx, in_count, + prefix, rel_param0.param_str, idx); + } + else + { + string_buffer_sprintf(register_name, "%s_in[%s + %u]", prefix, rel_param0.param_str, idx); + } + } + else + { + if (needs_legacy_glsl_syntax(gl_info) + && shader->u.ps.declared_in_count > in_count) + { + string_buffer_sprintf(register_name, + "((%s) > %u ? (%s) > %u ? gl_SecondaryColor : gl_Color : %s_in[%s])", + rel_param0.param_str, in_count - 1, rel_param0.param_str, in_count, + prefix, rel_param0.param_str); + } + else + { + string_buffer_sprintf(register_name, "%s_in[%s]", prefix, rel_param0.param_str); + } + } + } + else + { + if (idx == in_count) + string_buffer_sprintf(register_name, "gl_Color"); + else if (idx == in_count + 1) + string_buffer_sprintf(register_name, "gl_SecondaryColor"); + else + string_buffer_sprintf(register_name, "%s_in[%u]", prefix, idx); + } + } + else + { + if (!reg->idx[0].offset) + string_buffer_sprintf(register_name, "ffp_varying_diffuse"); + else + string_buffer_sprintf(register_name, "ffp_varying_specular"); + break; + } + break; + + case WINED3DSPR_CONST: + { + /* Relative addressing */ + if (reg->idx[0].rel_addr) + { + if (wined3d_settings.check_float_constants) + string_buffer_sprintf(register_name, + "(%s + %u >= 0 && %s + %u < %u ? %s_c[%s + %u] : vec4(0.0))", + rel_param0.param_str, reg->idx[0].offset, + rel_param0.param_str, reg->idx[0].offset, shader->limits->constant_float, + prefix, rel_param0.param_str, reg->idx[0].offset); + else if (reg->idx[0].offset) + string_buffer_sprintf(register_name, "%s_c[%s + %u]", + prefix, rel_param0.param_str, reg->idx[0].offset); + else + string_buffer_sprintf(register_name, "%s_c[%s]", prefix, rel_param0.param_str); + } + else + { + if (shader_constant_is_local(shader, reg->idx[0].offset)) + string_buffer_sprintf(register_name, "%s_lc%u", prefix, reg->idx[0].offset); + else + string_buffer_sprintf(register_name, "%s_c[%u]", prefix, reg->idx[0].offset); + } + } + break; + + case WINED3DSPR_CONSTINT: + string_buffer_sprintf(register_name, "%s_i[%u]", prefix, reg->idx[0].offset); + break; + + case WINED3DSPR_CONSTBOOL: + string_buffer_sprintf(register_name, "%s_b[%u]", prefix, reg->idx[0].offset); + break; + + case WINED3DSPR_TEXTURE: /* case WINED3DSPR_ADDR: */ + if (version->type == WINED3D_SHADER_TYPE_PIXEL) + string_buffer_sprintf(register_name, "T%u", reg->idx[0].offset); + else + string_buffer_sprintf(register_name, "A%u", reg->idx[0].offset); + break; + + case WINED3DSPR_LOOP: + string_buffer_sprintf(register_name, "aL%u", ctx->state->current_loop_reg - 1); + break; + + case WINED3DSPR_SAMPLER: + string_buffer_sprintf(register_name, "%s_sampler%u", prefix, reg->idx[0].offset); + break; + + case WINED3DSPR_COLOROUT: + if (reg->idx[0].offset >= gl_info->limits.buffers) + WARN("Write to render target %u, only %d supported.\n", + reg->idx[0].offset, gl_info->limits.buffers); + + string_buffer_sprintf(register_name, "%s[%u]", get_fragment_output(gl_info), reg->idx[0].offset); + break; + + case WINED3DSPR_RASTOUT: + string_buffer_sprintf(register_name, "%s", hwrastout_reg_names[reg->idx[0].offset]); + break; + + case WINED3DSPR_DEPTHOUT: + case WINED3DSPR_DEPTHOUTGE: + case WINED3DSPR_DEPTHOUTLE: + string_buffer_sprintf(register_name, "gl_FragDepth"); + break; + + case WINED3DSPR_ATTROUT: + if (!reg->idx[0].offset) + string_buffer_sprintf(register_name, "%s_out[8]", prefix); + else + string_buffer_sprintf(register_name, "%s_out[9]", prefix); + break; + + case WINED3DSPR_TEXCRDOUT: + /* Vertex shaders >= 3.0: WINED3DSPR_OUTPUT */ + if (reg->idx[0].rel_addr) + string_buffer_sprintf(register_name, "%s_out[%s + %u]", + prefix, rel_param0.param_str, reg->idx[0].offset); + else + string_buffer_sprintf(register_name, "%s_out[%u]", prefix, reg->idx[0].offset); + break; + + case WINED3DSPR_MISCTYPE: + if (!reg->idx[0].offset) + { + /* vPos */ + string_buffer_sprintf(register_name, "vpos"); + } + else if (reg->idx[0].offset == 1) + { + /* Note that gl_FrontFacing is a bool, while vFace is + * a float for which the sign determines front/back */ + string_buffer_sprintf(register_name, "(gl_FrontFacing ? 1.0 : -1.0)"); + } + else + { + FIXME("Unhandled misctype register %u.\n", reg->idx[0].offset); + string_buffer_sprintf(register_name, "unrecognized_register"); + } + break; + + case WINED3DSPR_IMMCONST: + switch (reg->immconst_type) + { + case WINED3D_IMMCONST_SCALAR: + switch (data_type) + { + case WINED3D_DATA_UNORM: + case WINED3D_DATA_SNORM: + case WINED3D_DATA_FLOAT: + string_buffer_clear(register_name); + shader_glsl_append_imm_vec(register_name, (const float *)reg->u.immconst_data, 1, gl_info); + break; + case WINED3D_DATA_INT: + string_buffer_sprintf(register_name, "%#x", reg->u.immconst_data[0]); + break; + case WINED3D_DATA_RESOURCE: + case WINED3D_DATA_SAMPLER: + case WINED3D_DATA_UINT: + string_buffer_sprintf(register_name, "%#xu", reg->u.immconst_data[0]); + break; + default: + string_buffer_sprintf(register_name, "", data_type); + break; + } + break; + + case WINED3D_IMMCONST_VEC4: + switch (data_type) + { + case WINED3D_DATA_UNORM: + case WINED3D_DATA_SNORM: + case WINED3D_DATA_FLOAT: + string_buffer_clear(register_name); + shader_glsl_append_imm_vec(register_name, (const float *)reg->u.immconst_data, 4, gl_info); + break; + case WINED3D_DATA_INT: + string_buffer_sprintf(register_name, "ivec4(%#x, %#x, %#x, %#x)", + reg->u.immconst_data[0], reg->u.immconst_data[1], + reg->u.immconst_data[2], reg->u.immconst_data[3]); + break; + case WINED3D_DATA_RESOURCE: + case WINED3D_DATA_SAMPLER: + case WINED3D_DATA_UINT: + string_buffer_sprintf(register_name, "uvec4(%#xu, %#xu, %#xu, %#xu)", + reg->u.immconst_data[0], reg->u.immconst_data[1], + reg->u.immconst_data[2], reg->u.immconst_data[3]); + break; + default: + string_buffer_sprintf(register_name, "", data_type); + break; + } + break; + + default: + FIXME("Unhandled immconst type %#x\n", reg->immconst_type); + string_buffer_sprintf(register_name, "", reg->immconst_type); + } + break; + + case WINED3DSPR_CONSTBUFFER: + if (reg->idx[1].rel_addr) + string_buffer_sprintf(register_name, "%s_cb%u[%s + %u]", + prefix, reg->idx[0].offset, rel_param1.param_str, reg->idx[1].offset); + else + string_buffer_sprintf(register_name, "%s_cb%u[%u]", prefix, reg->idx[0].offset, reg->idx[1].offset); + break; + + case WINED3DSPR_IMMCONSTBUFFER: + if (reg->idx[0].rel_addr) + string_buffer_sprintf(register_name, "%s_icb[%s + %u]", + prefix, rel_param0.param_str, reg->idx[0].offset); + else + string_buffer_sprintf(register_name, "%s_icb[%u]", prefix, reg->idx[0].offset); + break; + + case WINED3DSPR_PRIMID: + if (version->type == WINED3D_SHADER_TYPE_GEOMETRY) + string_buffer_sprintf(register_name, "gl_PrimitiveIDIn"); + else + string_buffer_sprintf(register_name, "gl_PrimitiveID"); + break; + + case WINED3DSPR_IDXTEMP: + if (reg->idx[1].rel_addr) + string_buffer_sprintf(register_name, "X%u[%s + %u]", + reg->idx[0].offset, rel_param1.param_str, reg->idx[1].offset); + else + string_buffer_sprintf(register_name, "X%u[%u]", reg->idx[0].offset, reg->idx[1].offset); + break; + + case WINED3DSPR_LOCALTHREADINDEX: + shader_glsl_fixup_scalar_register_variable(register_name, + "int(gl_LocalInvocationIndex)", gl_info); + break; + + case WINED3DSPR_GSINSTID: + case WINED3DSPR_OUTPOINTID: + shader_glsl_fixup_scalar_register_variable(register_name, + "gl_InvocationID", gl_info); + break; + + case WINED3DSPR_THREADID: + string_buffer_sprintf(register_name, "ivec3(gl_GlobalInvocationID)"); + break; + + case WINED3DSPR_THREADGROUPID: + string_buffer_sprintf(register_name, "ivec3(gl_WorkGroupID)"); + break; + + case WINED3DSPR_LOCALTHREADID: + string_buffer_sprintf(register_name, "ivec3(gl_LocalInvocationID)"); + break; + + case WINED3DSPR_FORKINSTID: + case WINED3DSPR_JOININSTID: + shader_glsl_fixup_scalar_register_variable(register_name, + "phase_instance_id", gl_info); + break; + + case WINED3DSPR_TESSCOORD: + string_buffer_sprintf(register_name, "gl_TessCoord"); + break; + + case WINED3DSPR_OUTCONTROLPOINT: + if (reg->idx[0].rel_addr) + { + if (reg->idx[1].rel_addr) + string_buffer_sprintf(register_name, "shader_out[%s + %u].reg[%s + %u]", + rel_param0.param_str, reg->idx[0].offset, + rel_param1.param_str, reg->idx[1].offset); + else + string_buffer_sprintf(register_name, "shader_out[%s + %u].reg[%u]", + rel_param0.param_str, reg->idx[0].offset, + reg->idx[1].offset); + } + else if (reg->idx[1].rel_addr) + { + string_buffer_sprintf(register_name, "shader_out[%u].reg[%s + %u]", + reg->idx[0].offset, rel_param1.param_str, + reg->idx[1].offset); + } + else + { + string_buffer_sprintf(register_name, "shader_out[%u].reg[%u]", + reg->idx[0].offset, reg->idx[1].offset); + } + break; + + case WINED3DSPR_PATCHCONST: + if (version->type == WINED3D_SHADER_TYPE_HULL) + string_buffer_sprintf(register_name, "hs_out[%u]", reg->idx[0].offset); + else + string_buffer_sprintf(register_name, "vpc[%u]", reg->idx[0].offset); + break; + + case WINED3DSPR_COVERAGE: + string_buffer_sprintf(register_name, "gl_SampleMaskIn[0]"); + break; + + case WINED3DSPR_SAMPLEMASK: + string_buffer_sprintf(register_name, "sample_mask"); + break; + + default: + FIXME("Unhandled register type %#x.\n", reg->type); + string_buffer_sprintf(register_name, "unrecognised_register"); + break; + } +} + +static void shader_glsl_write_mask_to_str(DWORD write_mask, char *str) +{ + *str++ = '.'; + if (write_mask & WINED3DSP_WRITEMASK_0) *str++ = 'x'; + if (write_mask & WINED3DSP_WRITEMASK_1) *str++ = 'y'; + if (write_mask & WINED3DSP_WRITEMASK_2) *str++ = 'z'; + if (write_mask & WINED3DSP_WRITEMASK_3) *str++ = 'w'; + *str = '\0'; +} + +/* Get the GLSL write mask for the destination register */ +static DWORD shader_glsl_get_write_mask(const struct wined3d_shader_dst_param *param, char *write_mask) +{ + DWORD mask = param->write_mask; + + if (shader_is_scalar(¶m->reg)) + { + mask = WINED3DSP_WRITEMASK_0; + *write_mask = '\0'; + } + else + { + shader_glsl_write_mask_to_str(mask, write_mask); + } + + return mask; +} + +static unsigned int shader_glsl_get_write_mask_size(DWORD write_mask) +{ + unsigned int size = 0; + + if (write_mask & WINED3DSP_WRITEMASK_0) ++size; + if (write_mask & WINED3DSP_WRITEMASK_1) ++size; + if (write_mask & WINED3DSP_WRITEMASK_2) ++size; + if (write_mask & WINED3DSP_WRITEMASK_3) ++size; + + return size; +} + +static unsigned int shader_glsl_swizzle_get_component(DWORD swizzle, + unsigned int component_idx) +{ + /* swizzle bits fields: wwzzyyxx */ + return (swizzle >> (2 * component_idx)) & 0x3; +} + +static void shader_glsl_swizzle_to_str(DWORD swizzle, BOOL fixup, DWORD mask, char *str) +{ + /* For registers of type WINED3DDECLTYPE_D3DCOLOR, data is stored as "bgra", + * but addressed as "rgba". To fix this we need to swap the register's x + * and z components. */ + const char *swizzle_chars = fixup ? "zyxw" : "xyzw"; + unsigned int i; + + *str++ = '.'; + for (i = 0; i < 4; ++i) + { + if (mask & (WINED3DSP_WRITEMASK_0 << i)) + *str++ = swizzle_chars[shader_glsl_swizzle_get_component(swizzle, i)]; + } + *str = '\0'; +} + +static void shader_glsl_get_swizzle(const struct wined3d_shader_src_param *param, + BOOL fixup, DWORD mask, char *swizzle_str) +{ + if (shader_is_scalar(¶m->reg)) + *swizzle_str = '\0'; + else + shader_glsl_swizzle_to_str(param->swizzle, fixup, mask, swizzle_str); +} + +static void shader_glsl_sprintf_cast(struct wined3d_string_buffer *dst_param, const char *src_param, + enum wined3d_data_type dst_data_type, enum wined3d_data_type src_data_type, unsigned int size) +{ + if (dst_data_type == WINED3D_DATA_UNORM || dst_data_type == WINED3D_DATA_SNORM) + dst_data_type = WINED3D_DATA_FLOAT; + if (src_data_type == WINED3D_DATA_UNORM || src_data_type == WINED3D_DATA_SNORM) + src_data_type = WINED3D_DATA_FLOAT; + + if (dst_data_type == src_data_type) + { + string_buffer_sprintf(dst_param, "%s", src_param); + return; + } + + if (src_data_type == WINED3D_DATA_FLOAT) + { + switch (dst_data_type) + { + case WINED3D_DATA_INT: + string_buffer_sprintf(dst_param, "floatBitsToInt(%s)", src_param); + return; + case WINED3D_DATA_RESOURCE: + case WINED3D_DATA_SAMPLER: + case WINED3D_DATA_UINT: + string_buffer_sprintf(dst_param, "floatBitsToUint(%s)", src_param); + return; + default: + break; + } + } + + if (src_data_type == WINED3D_DATA_UINT && dst_data_type == WINED3D_DATA_FLOAT) + { + string_buffer_sprintf(dst_param, "uintBitsToFloat(%s)", src_param); + return; + } + + if (src_data_type == WINED3D_DATA_INT) + { + switch (dst_data_type) + { + case WINED3D_DATA_FLOAT: + string_buffer_sprintf(dst_param, "intBitsToFloat(%s)", src_param); + return; + case WINED3D_DATA_UINT: + if (size == 1) + string_buffer_sprintf(dst_param, "uint(%s)", src_param); + else + string_buffer_sprintf(dst_param, "uvec%u(%s)", size, src_param); + return; + default: + break; + } + } + + FIXME("Unhandled cast from %#x to %#x.\n", src_data_type, dst_data_type); + string_buffer_sprintf(dst_param, "%s", src_param); +} + +/* From a given parameter token, generate the corresponding GLSL string. + * Also, return the actual register name and swizzle in case the + * caller needs this information as well. */ +static void shader_glsl_add_src_param_ext(const struct wined3d_shader_context *ctx, + const struct wined3d_shader_src_param *wined3d_src, DWORD mask, struct glsl_src_param *glsl_src, + enum wined3d_data_type data_type) +{ + struct shader_glsl_ctx_priv *priv = ctx->backend_data; + struct wined3d_string_buffer *param_str = string_buffer_get(priv->string_buffers); + struct wined3d_string_buffer *reg_name = string_buffer_get(priv->string_buffers); + enum wined3d_data_type param_data_type; + BOOL is_color = FALSE; + char swizzle_str[6]; + unsigned int size; + + glsl_src->param_str[0] = '\0'; + swizzle_str[0] = '\0'; + + shader_glsl_get_register_name(&wined3d_src->reg, data_type, reg_name, &is_color, ctx); + shader_glsl_get_swizzle(wined3d_src, is_color, mask, swizzle_str); + + switch (wined3d_src->reg.type) + { + case WINED3DSPR_IMMCONST: + param_data_type = data_type; + size = wined3d_src->reg.immconst_type == WINED3D_IMMCONST_SCALAR ? 1 : 4; + break; + case WINED3DSPR_FORKINSTID: + case WINED3DSPR_GSINSTID: + case WINED3DSPR_JOININSTID: + case WINED3DSPR_LOCALTHREADINDEX: + case WINED3DSPR_OUTPOINTID: + case WINED3DSPR_PRIMID: + param_data_type = WINED3D_DATA_INT; + size = 1; + break; + case WINED3DSPR_LOCALTHREADID: + case WINED3DSPR_THREADGROUPID: + case WINED3DSPR_THREADID: + param_data_type = WINED3D_DATA_INT; + size = 3; + break; + default: + param_data_type = WINED3D_DATA_FLOAT; + size = 4; + break; + } + + shader_glsl_sprintf_cast(param_str, reg_name->buffer, data_type, param_data_type, size); + shader_glsl_gen_modifier(wined3d_src->modifiers, param_str->buffer, swizzle_str, glsl_src->param_str); + + string_buffer_release(priv->string_buffers, reg_name); + string_buffer_release(priv->string_buffers, param_str); +} + +static void shader_glsl_add_src_param(const struct wined3d_shader_instruction *ins, + const struct wined3d_shader_src_param *wined3d_src, DWORD mask, struct glsl_src_param *glsl_src) +{ + shader_glsl_add_src_param_ext(ins->ctx, wined3d_src, mask, glsl_src, wined3d_src->reg.data_type); +} + +/* From a given parameter token, generate the corresponding GLSL string. + * Also, return the actual register name and swizzle in case the + * caller needs this information as well. */ +static DWORD shader_glsl_add_dst_param(const struct wined3d_shader_instruction *ins, + const struct wined3d_shader_dst_param *wined3d_dst, struct glsl_dst_param *glsl_dst) +{ + struct shader_glsl_ctx_priv *priv = ins->ctx->backend_data; + struct wined3d_string_buffer *reg_name; + size_t len; + + glsl_dst->mask_str[0] = '\0'; + + reg_name = string_buffer_get(priv->string_buffers); + shader_glsl_get_register_name(&wined3d_dst->reg, wined3d_dst->reg.data_type, reg_name, NULL, ins->ctx); + len = min(reg_name->content_size, ARRAY_SIZE(glsl_dst->reg_name) - 1); + memcpy(glsl_dst->reg_name, reg_name->buffer, len); + glsl_dst->reg_name[len] = '\0'; + string_buffer_release(priv->string_buffers, reg_name); + + return shader_glsl_get_write_mask(wined3d_dst, glsl_dst->mask_str); +} + +/* Append the destination part of the instruction to the buffer, return the effective write mask */ +static DWORD shader_glsl_append_dst_ext(struct wined3d_string_buffer *buffer, + const struct wined3d_shader_instruction *ins, const struct wined3d_shader_dst_param *dst, + enum wined3d_data_type data_type) +{ + struct glsl_dst_param glsl_dst; + DWORD mask; + + if ((mask = shader_glsl_add_dst_param(ins, dst, &glsl_dst))) + { + if (ins->flags & WINED3DSI_PRECISE_XYZW) + sprintf(glsl_dst.reg_name, "tmp_precise"); + + switch (data_type) + { + case WINED3D_DATA_FLOAT: + case WINED3D_DATA_UNORM: + case WINED3D_DATA_SNORM: + shader_addline(buffer, "%s%s = %s(", + glsl_dst.reg_name, glsl_dst.mask_str, shift_glsl_tab[dst->shift]); + break; + case WINED3D_DATA_INT: + shader_addline(buffer, "%s%s = %sintBitsToFloat(", + glsl_dst.reg_name, glsl_dst.mask_str, shift_glsl_tab[dst->shift]); + break; + case WINED3D_DATA_RESOURCE: + case WINED3D_DATA_SAMPLER: + case WINED3D_DATA_UINT: + shader_addline(buffer, "%s%s = %suintBitsToFloat(", + glsl_dst.reg_name, glsl_dst.mask_str, shift_glsl_tab[dst->shift]); + break; + default: + FIXME("Unhandled data type %#x.\n", data_type); + shader_addline(buffer, "%s%s = %s(", + glsl_dst.reg_name, glsl_dst.mask_str, shift_glsl_tab[dst->shift]); + break; + } + } + + return mask; +} + +/* Append the destination part of the instruction to the buffer, return the effective write mask */ +static DWORD shader_glsl_append_dst(struct wined3d_string_buffer *buffer, const struct wined3d_shader_instruction *ins) +{ + return shader_glsl_append_dst_ext(buffer, ins, &ins->dst[0], ins->dst[0].reg.data_type); +} + +/** Process GLSL instruction modifiers */ +static void shader_glsl_add_instruction_modifiers(const struct wined3d_shader_instruction *ins) +{ + struct glsl_dst_param dst_param; + DWORD modifiers; + + if (!ins->dst_count) return; + + if (ins->flags & WINED3DSI_PRECISE_XYZW) + { + shader_glsl_add_dst_param(ins, &ins->dst[0], &dst_param); + shader_addline(ins->ctx->buffer, "%s%s = tmp_precise%s;\n", + dst_param.reg_name, dst_param.mask_str, dst_param.mask_str); + } + + modifiers = ins->dst[0].modifiers; + if (!modifiers) return; + + shader_glsl_add_dst_param(ins, &ins->dst[0], &dst_param); + + if (modifiers & WINED3DSPDM_SATURATE) + { + /* _SAT means to clamp the value of the register to between 0 and 1 */ + shader_addline(ins->ctx->buffer, "%s%s = clamp(%s%s, 0.0, 1.0);\n", dst_param.reg_name, + dst_param.mask_str, dst_param.reg_name, dst_param.mask_str); + } + + if (modifiers & WINED3DSPDM_MSAMPCENTROID) + { + FIXME("_centroid modifier not handled\n"); + } + + if (modifiers & WINED3DSPDM_PARTIALPRECISION) + { + /* MSDN says this modifier can be safely ignored, so that's what we'll do. */ + } +} + +static const char *shader_glsl_get_rel_op(enum wined3d_shader_rel_op op) +{ + switch (op) + { + case WINED3D_SHADER_REL_OP_GT: return ">"; + case WINED3D_SHADER_REL_OP_EQ: return "=="; + case WINED3D_SHADER_REL_OP_GE: return ">="; + case WINED3D_SHADER_REL_OP_LT: return "<"; + case WINED3D_SHADER_REL_OP_NE: return "!="; + case WINED3D_SHADER_REL_OP_LE: return "<="; + default: + FIXME("Unrecognized operator %#x.\n", op); + return "(\?\?)"; + } +} + +static BOOL shader_glsl_has_core_grad(const struct wined3d_gl_info *gl_info) +{ + return shader_glsl_get_version(gl_info) >= 130 || gl_info->supported[EXT_GPU_SHADER4]; +} + +static void shader_glsl_get_coord_size(enum wined3d_shader_resource_type resource_type, + unsigned int *coord_size, unsigned int *deriv_size) +{ + const BOOL is_array = resource_type == WINED3D_SHADER_RESOURCE_TEXTURE_1DARRAY + || resource_type == WINED3D_SHADER_RESOURCE_TEXTURE_2DARRAY; + + *coord_size = resource_type_info[resource_type].coord_size; + *deriv_size = *coord_size; + if (is_array) + --(*deriv_size); +} + +static void shader_glsl_get_sample_function(const struct wined3d_shader_context *ctx, + DWORD resource_idx, DWORD sampler_idx, DWORD flags, struct glsl_sample_function *sample_function) +{ + enum wined3d_shader_resource_type resource_type; + struct shader_glsl_ctx_priv *priv = ctx->backend_data; + const struct wined3d_gl_info *gl_info = priv->gl_info; + BOOL shadow = glsl_is_shadow_sampler(ctx->shader, priv->cur_ps_args, resource_idx, sampler_idx); + BOOL projected = flags & WINED3D_GLSL_SAMPLE_PROJECTED; + BOOL texrect = ctx->reg_maps->shader_version.type == WINED3D_SHADER_TYPE_PIXEL + && priv->cur_ps_args->np2_fixup & (1u << resource_idx) + && gl_info->supported[ARB_TEXTURE_RECTANGLE]; + BOOL lod = flags & WINED3D_GLSL_SAMPLE_LOD; + BOOL grad = flags & WINED3D_GLSL_SAMPLE_GRAD; + BOOL offset = flags & WINED3D_GLSL_SAMPLE_OFFSET; + const char *base = "texture", *type_part = "", *suffix = ""; + unsigned int coord_size, deriv_size; + + resource_type = ctx->reg_maps->shader_version.type == WINED3D_SHADER_TYPE_PIXEL + ? pixelshader_get_resource_type(ctx->reg_maps, resource_idx, priv->cur_ps_args->tex_types) + : ctx->reg_maps->resource_info[resource_idx].type; + + sample_function->data_type = ctx->reg_maps->resource_info[resource_idx].data_type; + + if (resource_type >= ARRAY_SIZE(resource_type_info)) + { + ERR("Unexpected resource type %#x.\n", resource_type); + resource_type = WINED3D_SHADER_RESOURCE_TEXTURE_2D; + } + + /* Note that there's no such thing as a projected cube texture. */ + if (resource_type == WINED3D_SHADER_RESOURCE_TEXTURE_CUBE) + projected = FALSE; + + if (needs_legacy_glsl_syntax(gl_info)) + { + if (shadow) + base = "shadow"; + + type_part = resource_type_info[resource_type].type_part; + if (resource_type == WINED3D_SHADER_RESOURCE_TEXTURE_2D && texrect) + type_part = "2DRect"; + if (!type_part[0] && resource_type != WINED3D_SHADER_RESOURCE_TEXTURE_CUBEARRAY) + FIXME("Unhandled resource type %#x.\n", resource_type); + + if (!lod && grad && !shader_glsl_has_core_grad(gl_info)) + { + if (gl_info->supported[ARB_SHADER_TEXTURE_LOD]) + suffix = "ARB"; + else + FIXME("Unsupported grad function.\n"); + } + } + + if (flags & WINED3D_GLSL_SAMPLE_LOAD) + { + static const DWORD texel_fetch_flags = WINED3D_GLSL_SAMPLE_LOAD | WINED3D_GLSL_SAMPLE_OFFSET; + if (flags & ~texel_fetch_flags) + ERR("Unexpected flags %#x for texelFetch.\n", flags & ~texel_fetch_flags); + + base = "texelFetch"; + type_part = ""; + } + + sample_function->name = string_buffer_get(priv->string_buffers); + string_buffer_sprintf(sample_function->name, "%s%s%s%s%s%s", base, type_part, projected ? "Proj" : "", + lod ? "Lod" : grad ? "Grad" : "", offset ? "Offset" : "", suffix); + + shader_glsl_get_coord_size(resource_type, &coord_size, &deriv_size); + if (shadow) + ++coord_size; + sample_function->offset_size = offset ? deriv_size : 0; + sample_function->coord_mask = (1u << coord_size) - 1; + sample_function->deriv_mask = (1u << deriv_size) - 1; + sample_function->output_single_component = shadow && !needs_legacy_glsl_syntax(gl_info); +} + +static void shader_glsl_release_sample_function(const struct wined3d_shader_context *ctx, + struct glsl_sample_function *sample_function) +{ + const struct shader_glsl_ctx_priv *priv = ctx->backend_data; + + string_buffer_release(priv->string_buffers, sample_function->name); +} + +static void shader_glsl_append_fixup_arg(char *arguments, const char *reg_name, + BOOL sign_fixup, enum fixup_channel_source channel_source) +{ + switch(channel_source) + { + case CHANNEL_SOURCE_ZERO: + strcat(arguments, "0.0"); + break; + + case CHANNEL_SOURCE_ONE: + strcat(arguments, "1.0"); + break; + + case CHANNEL_SOURCE_X: + strcat(arguments, reg_name); + strcat(arguments, ".x"); + break; + + case CHANNEL_SOURCE_Y: + strcat(arguments, reg_name); + strcat(arguments, ".y"); + break; + + case CHANNEL_SOURCE_Z: + strcat(arguments, reg_name); + strcat(arguments, ".z"); + break; + + case CHANNEL_SOURCE_W: + strcat(arguments, reg_name); + strcat(arguments, ".w"); + break; + + default: + FIXME("Unhandled channel source %#x\n", channel_source); + strcat(arguments, "undefined"); + break; + } + + if (sign_fixup) strcat(arguments, " * 2.0 - 1.0"); +} + +static void shader_glsl_color_correction_ext(struct wined3d_string_buffer *buffer, + const char *reg_name, DWORD mask, struct color_fixup_desc fixup) +{ + unsigned int mask_size, remaining; + DWORD fixup_mask = 0; + char arguments[256]; + char mask_str[6]; + + if (fixup.x_sign_fixup || fixup.x_source != CHANNEL_SOURCE_X) fixup_mask |= WINED3DSP_WRITEMASK_0; + if (fixup.y_sign_fixup || fixup.y_source != CHANNEL_SOURCE_Y) fixup_mask |= WINED3DSP_WRITEMASK_1; + if (fixup.z_sign_fixup || fixup.z_source != CHANNEL_SOURCE_Z) fixup_mask |= WINED3DSP_WRITEMASK_2; + if (fixup.w_sign_fixup || fixup.w_source != CHANNEL_SOURCE_W) fixup_mask |= WINED3DSP_WRITEMASK_3; + if (!(mask &= fixup_mask)) + return; + + if (is_complex_fixup(fixup)) + { + enum complex_fixup complex_fixup = get_complex_fixup(fixup); + FIXME("Complex fixup (%#x) not supported\n",complex_fixup); + return; + } + + shader_glsl_write_mask_to_str(mask, mask_str); + mask_size = shader_glsl_get_write_mask_size(mask); + + arguments[0] = '\0'; + remaining = mask_size; + if (mask & WINED3DSP_WRITEMASK_0) + { + shader_glsl_append_fixup_arg(arguments, reg_name, fixup.x_sign_fixup, fixup.x_source); + if (--remaining) strcat(arguments, ", "); + } + if (mask & WINED3DSP_WRITEMASK_1) + { + shader_glsl_append_fixup_arg(arguments, reg_name, fixup.y_sign_fixup, fixup.y_source); + if (--remaining) strcat(arguments, ", "); + } + if (mask & WINED3DSP_WRITEMASK_2) + { + shader_glsl_append_fixup_arg(arguments, reg_name, fixup.z_sign_fixup, fixup.z_source); + if (--remaining) strcat(arguments, ", "); + } + if (mask & WINED3DSP_WRITEMASK_3) + { + shader_glsl_append_fixup_arg(arguments, reg_name, fixup.w_sign_fixup, fixup.w_source); + if (--remaining) strcat(arguments, ", "); + } + + if (mask_size > 1) + shader_addline(buffer, "%s%s = vec%u(%s);\n", reg_name, mask_str, mask_size, arguments); + else + shader_addline(buffer, "%s%s = %s;\n", reg_name, mask_str, arguments); +} + +static void shader_glsl_color_correction(const struct wined3d_shader_instruction *ins, struct color_fixup_desc fixup) +{ + struct shader_glsl_ctx_priv *priv = ins->ctx->backend_data; + struct wined3d_string_buffer *reg_name; + + reg_name = string_buffer_get(priv->string_buffers); + shader_glsl_get_register_name(&ins->dst[0].reg, ins->dst[0].reg.data_type, reg_name, NULL, ins->ctx); + shader_glsl_color_correction_ext(ins->ctx->buffer, reg_name->buffer, ins->dst[0].write_mask, fixup); + string_buffer_release(priv->string_buffers, reg_name); +} + +static void PRINTF_ATTR(9, 10) shader_glsl_gen_sample_code(const struct wined3d_shader_instruction *ins, + unsigned int sampler_bind_idx, const struct glsl_sample_function *sample_function, DWORD swizzle, + const char *dx, const char *dy, const char *bias, const struct wined3d_shader_texel_offset *offset, + const char *coord_reg_fmt, ...) +{ + const struct wined3d_shader_version *version = &ins->ctx->reg_maps->shader_version; + char dst_swizzle[6]; + struct color_fixup_desc fixup; + BOOL np2_fixup = FALSE; + va_list args; + int ret; + + shader_glsl_swizzle_to_str(swizzle, FALSE, ins->dst[0].write_mask, dst_swizzle); + + /* If ARB_texture_swizzle is supported we don't need to do anything here. + * We actually rely on it for vertex shaders and SM4+. */ + if (version->type == WINED3D_SHADER_TYPE_PIXEL && version->major < 4) + { + const struct shader_glsl_ctx_priv *priv = ins->ctx->backend_data; + fixup = priv->cur_ps_args->color_fixup[sampler_bind_idx]; + + if (priv->cur_ps_args->np2_fixup & (1u << sampler_bind_idx)) + np2_fixup = TRUE; + } + else + { + fixup = COLOR_FIXUP_IDENTITY; + } + + shader_glsl_append_dst_ext(ins->ctx->buffer, ins, &ins->dst[0], sample_function->data_type); + + if (sample_function->output_single_component) + shader_addline(ins->ctx->buffer, "vec4("); + + shader_addline(ins->ctx->buffer, "%s(%s_sampler%u, ", + sample_function->name->buffer, shader_glsl_get_prefix(version->type), sampler_bind_idx); + + for (;;) + { + va_start(args, coord_reg_fmt); + ret = shader_vaddline(ins->ctx->buffer, coord_reg_fmt, args); + va_end(args); + if (!ret) + break; + if (!string_buffer_resize(ins->ctx->buffer, ret)) + break; + } + + if (np2_fixup) + { + const struct shader_glsl_ctx_priv *priv = ins->ctx->backend_data; + const unsigned char idx = priv->cur_np2fixup_info->idx[sampler_bind_idx]; + + switch (shader_glsl_get_write_mask_size(sample_function->coord_mask)) + { + case 1: + shader_addline(ins->ctx->buffer, " * ps_samplerNP2Fixup[%u].%s", + idx >> 1, (idx % 2) ? "z" : "x"); + break; + case 2: + shader_addline(ins->ctx->buffer, " * ps_samplerNP2Fixup[%u].%s", + idx >> 1, (idx % 2) ? "zw" : "xy"); + break; + case 3: + shader_addline(ins->ctx->buffer, " * vec3(ps_samplerNP2Fixup[%u].%s, 1.0)", + idx >> 1, (idx % 2) ? "zw" : "xy"); + break; + case 4: + shader_addline(ins->ctx->buffer, " * vec4(ps_samplerNP2Fixup[%u].%s, 1.0, 1.0)", + idx >> 1, (idx % 2) ? "zw" : "xy"); + break; + } + } + if (dx && dy) + shader_addline(ins->ctx->buffer, ", %s, %s", dx, dy); + else if (bias) + shader_addline(ins->ctx->buffer, ", %s", bias); + if (sample_function->offset_size) + { + int offset_immdata[4] = {offset->u, offset->v, offset->w}; + shader_addline(ins->ctx->buffer, ", "); + shader_glsl_append_imm_ivec(ins->ctx->buffer, offset_immdata, sample_function->offset_size); + } + shader_addline(ins->ctx->buffer, ")"); + + if (sample_function->output_single_component) + shader_addline(ins->ctx->buffer, ")"); + + shader_addline(ins->ctx->buffer, "%s);\n", dst_swizzle); + + if (!is_identity_fixup(fixup)) + shader_glsl_color_correction(ins, fixup); +} + +static void shader_glsl_fixup_position(struct wined3d_string_buffer *buffer, BOOL use_viewport_index) +{ + /* Write the final position. + * + * OpenGL coordinates specify the center of the pixel while D3D coords + * specify the corner. The offsets are stored in z and w in + * pos_fixup. pos_fixup.y contains 1.0 or -1.0 to turn the rendering + * upside down for offscreen rendering. pos_fixup.x contains 1.0 to allow + * a MAD. */ + if (use_viewport_index) + { + shader_addline(buffer, "gl_Position.y = gl_Position.y * pos_fixup[gl_ViewportIndex].y;\n"); + shader_addline(buffer, "gl_Position.xy += pos_fixup[gl_ViewportIndex].zw * gl_Position.ww;\n"); + } + else + { + shader_addline(buffer, "gl_Position.y = gl_Position.y * pos_fixup.y;\n"); + shader_addline(buffer, "gl_Position.xy += pos_fixup.zw * gl_Position.ww;\n"); + } + + /* Z coord [0;1]->[-1;1] mapping, see comment in get_projection_matrix() + * in utils.c + * + * Basically we want (in homogeneous coordinates) z = z * 2 - 1. However, + * shaders are run before the homogeneous divide, so we have to take the w + * into account: z = ((z / w) * 2 - 1) * w, which is the same as + * z = z * 2 - w. */ + shader_addline(buffer, "gl_Position.z = gl_Position.z * 2.0 - gl_Position.w;\n"); +} + +/***************************************************************************** + * Begin processing individual instruction opcodes + ****************************************************************************/ + +static void shader_glsl_binop(const struct wined3d_shader_instruction *ins) +{ + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + struct glsl_src_param src0_param; + struct glsl_src_param src1_param; + DWORD write_mask; + const char *op; + + /* Determine the GLSL operator to use based on the opcode */ + switch (ins->handler_idx) + { + case WINED3DSIH_ADD: op = "+"; break; + case WINED3DSIH_AND: op = "&"; break; + case WINED3DSIH_DIV: op = "/"; break; + case WINED3DSIH_IADD: op = "+"; break; + case WINED3DSIH_ISHL: op = "<<"; break; + case WINED3DSIH_ISHR: op = ">>"; break; + case WINED3DSIH_MUL: op = "*"; break; + case WINED3DSIH_OR: op = "|"; break; + case WINED3DSIH_SUB: op = "-"; break; + case WINED3DSIH_USHR: op = ">>"; break; + case WINED3DSIH_XOR: op = "^"; break; + default: + op = ""; + FIXME("Opcode %s not yet handled in GLSL.\n", debug_d3dshaderinstructionhandler(ins->handler_idx)); + break; + } + + write_mask = shader_glsl_append_dst(buffer, ins); + shader_glsl_add_src_param(ins, &ins->src[0], write_mask, &src0_param); + shader_glsl_add_src_param(ins, &ins->src[1], write_mask, &src1_param); + shader_addline(buffer, "%s %s %s);\n", src0_param.param_str, op, src1_param.param_str); +} + +static void shader_glsl_relop(const struct wined3d_shader_instruction *ins) +{ + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + struct glsl_src_param src0_param; + struct glsl_src_param src1_param; + unsigned int mask_size; + DWORD write_mask; + const char *op; + + write_mask = shader_glsl_append_dst(buffer, ins); + mask_size = shader_glsl_get_write_mask_size(write_mask); + shader_glsl_add_src_param(ins, &ins->src[0], write_mask, &src0_param); + shader_glsl_add_src_param(ins, &ins->src[1], write_mask, &src1_param); + + if (mask_size > 1) + { + switch (ins->handler_idx) + { + case WINED3DSIH_EQ: op = "equal"; break; + case WINED3DSIH_IEQ: op = "equal"; break; + case WINED3DSIH_GE: op = "greaterThanEqual"; break; + case WINED3DSIH_IGE: op = "greaterThanEqual"; break; + case WINED3DSIH_UGE: op = "greaterThanEqual"; break; + case WINED3DSIH_LT: op = "lessThan"; break; + case WINED3DSIH_ILT: op = "lessThan"; break; + case WINED3DSIH_ULT: op = "lessThan"; break; + case WINED3DSIH_NE: op = "notEqual"; break; + case WINED3DSIH_INE: op = "notEqual"; break; + default: + op = ""; + ERR("Unhandled opcode %#x.\n", ins->handler_idx); + break; + } + + shader_addline(buffer, "uvec%u(%s(%s, %s)) * 0xffffffffu);\n", + mask_size, op, src0_param.param_str, src1_param.param_str); + } + else + { + switch (ins->handler_idx) + { + case WINED3DSIH_EQ: op = "=="; break; + case WINED3DSIH_IEQ: op = "=="; break; + case WINED3DSIH_GE: op = ">="; break; + case WINED3DSIH_IGE: op = ">="; break; + case WINED3DSIH_UGE: op = ">="; break; + case WINED3DSIH_LT: op = "<"; break; + case WINED3DSIH_ILT: op = "<"; break; + case WINED3DSIH_ULT: op = "<"; break; + case WINED3DSIH_NE: op = "!="; break; + case WINED3DSIH_INE: op = "!="; break; + default: + op = ""; + ERR("Unhandled opcode %#x.\n", ins->handler_idx); + break; + } + + shader_addline(buffer, "%s %s %s ? 0xffffffffu : 0u);\n", + src0_param.param_str, op, src1_param.param_str); + } +} + +static void shader_glsl_unary_op(const struct wined3d_shader_instruction *ins) +{ + struct glsl_src_param src_param; + DWORD write_mask; + const char *op; + + switch (ins->handler_idx) + { + case WINED3DSIH_INEG: op = "-"; break; + case WINED3DSIH_NOT: op = "~"; break; + default: + op = ""; + ERR("Unhandled opcode %s.\n", + debug_d3dshaderinstructionhandler(ins->handler_idx)); + break; + } + + write_mask = shader_glsl_append_dst(ins->ctx->buffer, ins); + shader_glsl_add_src_param(ins, &ins->src[0], write_mask, &src_param); + shader_addline(ins->ctx->buffer, "%s%s);\n", op, src_param.param_str); +} + +static void shader_glsl_mul_extended(const struct wined3d_shader_instruction *ins) +{ + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + struct glsl_src_param src0_param; + struct glsl_src_param src1_param; + DWORD write_mask; + + /* If we have ARB_gpu_shader5, we can use imulExtended() / umulExtended(). + * If not, we can emulate it. */ + if (ins->dst[0].reg.type != WINED3DSPR_NULL) + FIXME("64-bit integer multiplies not implemented.\n"); + + if (ins->dst[1].reg.type != WINED3DSPR_NULL) + { + write_mask = shader_glsl_append_dst_ext(buffer, ins, &ins->dst[1], ins->dst[1].reg.data_type); + shader_glsl_add_src_param(ins, &ins->src[0], write_mask, &src0_param); + shader_glsl_add_src_param(ins, &ins->src[1], write_mask, &src1_param); + + shader_addline(ins->ctx->buffer, "%s * %s);\n", + src0_param.param_str, src1_param.param_str); + } +} + +static void shader_glsl_udiv(const struct wined3d_shader_instruction *ins) +{ + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + struct glsl_src_param src0_param, src1_param; + DWORD write_mask; + + if (ins->dst[0].reg.type != WINED3DSPR_NULL) + { + if (ins->dst[1].reg.type != WINED3DSPR_NULL) + { + char dst_mask[6]; + + write_mask = shader_glsl_get_write_mask(&ins->dst[0], dst_mask); + shader_glsl_add_src_param(ins, &ins->src[0], write_mask, &src0_param); + shader_glsl_add_src_param(ins, &ins->src[1], write_mask, &src1_param); + shader_addline(buffer, "tmp0%s = uintBitsToFloat(%s / %s);\n", + dst_mask, src0_param.param_str, src1_param.param_str); + + write_mask = shader_glsl_append_dst_ext(buffer, ins, &ins->dst[1], ins->dst[1].reg.data_type); + shader_glsl_add_src_param(ins, &ins->src[0], write_mask, &src0_param); + shader_glsl_add_src_param(ins, &ins->src[1], write_mask, &src1_param); + shader_addline(buffer, "%s %% %s);\n", src0_param.param_str, src1_param.param_str); + + shader_glsl_append_dst_ext(buffer, ins, &ins->dst[0], WINED3D_DATA_FLOAT); + shader_addline(buffer, "tmp0%s);\n", dst_mask); + } + else + { + write_mask = shader_glsl_append_dst_ext(buffer, ins, &ins->dst[0], ins->dst[0].reg.data_type); + shader_glsl_add_src_param(ins, &ins->src[0], write_mask, &src0_param); + shader_glsl_add_src_param(ins, &ins->src[1], write_mask, &src1_param); + shader_addline(buffer, "%s / %s);\n", src0_param.param_str, src1_param.param_str); + } + } + else if (ins->dst[1].reg.type != WINED3DSPR_NULL) + { + write_mask = shader_glsl_append_dst_ext(buffer, ins, &ins->dst[1], ins->dst[1].reg.data_type); + shader_glsl_add_src_param(ins, &ins->src[0], write_mask, &src0_param); + shader_glsl_add_src_param(ins, &ins->src[1], write_mask, &src1_param); + shader_addline(buffer, "%s %% %s);\n", src0_param.param_str, src1_param.param_str); + } +} + +/* Process the WINED3DSIO_MOV opcode using GLSL (dst = src) */ +static void shader_glsl_mov(const struct wined3d_shader_instruction *ins) +{ + const struct shader_glsl_ctx_priv *priv = ins->ctx->backend_data; + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + const struct wined3d_gl_info *gl_info = priv->gl_info; + struct glsl_src_param src0_param; + DWORD write_mask; + + write_mask = shader_glsl_append_dst(buffer, ins); + shader_glsl_add_src_param(ins, &ins->src[0], write_mask, &src0_param); + + /* In vs_1_1 WINED3DSIO_MOV can write to the address register. In later + * shader versions WINED3DSIO_MOVA is used for this. */ + if (ins->ctx->reg_maps->shader_version.major == 1 + && ins->ctx->reg_maps->shader_version.type == WINED3D_SHADER_TYPE_VERTEX + && ins->dst[0].reg.type == WINED3DSPR_ADDR) + { + /* This is a simple floor() */ + unsigned int mask_size = shader_glsl_get_write_mask_size(write_mask); + if (mask_size > 1) { + shader_addline(buffer, "ivec%d(floor(%s)));\n", mask_size, src0_param.param_str); + } else { + shader_addline(buffer, "int(floor(%s)));\n", src0_param.param_str); + } + } + else if (ins->handler_idx == WINED3DSIH_MOVA) + { + unsigned int mask_size = shader_glsl_get_write_mask_size(write_mask); + + if (shader_glsl_get_version(gl_info) >= 130 || gl_info->supported[EXT_GPU_SHADER4]) + { + if (mask_size > 1) + shader_addline(buffer, "ivec%d(round(%s)));\n", mask_size, src0_param.param_str); + else + shader_addline(buffer, "int(round(%s)));\n", src0_param.param_str); + } + else + { + if (mask_size > 1) + shader_addline(buffer, "ivec%d(floor(abs(%s) + vec%d(0.5)) * sign(%s)));\n", + mask_size, src0_param.param_str, mask_size, src0_param.param_str); + else + shader_addline(buffer, "int(floor(abs(%s) + 0.5) * sign(%s)));\n", + src0_param.param_str, src0_param.param_str); + } + } + else + { + shader_addline(buffer, "%s);\n", src0_param.param_str); + } +} + +/* Process the dot product operators DP3 and DP4 in GLSL (dst = dot(src0, src1)) */ +static void shader_glsl_dot(const struct wined3d_shader_instruction *ins) +{ + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + struct glsl_src_param src0_param; + struct glsl_src_param src1_param; + DWORD dst_write_mask, src_write_mask; + unsigned int dst_size; + + dst_write_mask = shader_glsl_append_dst(buffer, ins); + dst_size = shader_glsl_get_write_mask_size(dst_write_mask); + + /* dp4 works on vec4, dp3 on vec3, etc. */ + if (ins->handler_idx == WINED3DSIH_DP4) + src_write_mask = WINED3DSP_WRITEMASK_ALL; + else if (ins->handler_idx == WINED3DSIH_DP3) + src_write_mask = WINED3DSP_WRITEMASK_0 | WINED3DSP_WRITEMASK_1 | WINED3DSP_WRITEMASK_2; + else + src_write_mask = WINED3DSP_WRITEMASK_0 | WINED3DSP_WRITEMASK_1; + + shader_glsl_add_src_param(ins, &ins->src[0], src_write_mask, &src0_param); + shader_glsl_add_src_param(ins, &ins->src[1], src_write_mask, &src1_param); + + if (dst_size > 1) { + shader_addline(buffer, "vec%d(dot(%s, %s)));\n", dst_size, src0_param.param_str, src1_param.param_str); + } else { + shader_addline(buffer, "dot(%s, %s));\n", src0_param.param_str, src1_param.param_str); + } +} + +/* Note that this instruction has some restrictions. The destination write mask + * can't contain the w component, and the source swizzles have to be .xyzw */ +static void shader_glsl_cross(const struct wined3d_shader_instruction *ins) +{ + DWORD src_mask = WINED3DSP_WRITEMASK_0 | WINED3DSP_WRITEMASK_1 | WINED3DSP_WRITEMASK_2; + struct glsl_src_param src0_param; + struct glsl_src_param src1_param; + char dst_mask[6]; + + shader_glsl_get_write_mask(&ins->dst[0], dst_mask); + shader_glsl_append_dst(ins->ctx->buffer, ins); + shader_glsl_add_src_param(ins, &ins->src[0], src_mask, &src0_param); + shader_glsl_add_src_param(ins, &ins->src[1], src_mask, &src1_param); + shader_addline(ins->ctx->buffer, "cross(%s, %s)%s);\n", src0_param.param_str, src1_param.param_str, dst_mask); +} + +static void shader_glsl_cut(const struct wined3d_shader_instruction *ins) +{ + unsigned int stream = ins->handler_idx == WINED3DSIH_CUT ? 0 : ins->src[0].reg.idx[0].offset; + + if (!stream) + shader_addline(ins->ctx->buffer, "EndPrimitive();\n"); + else + FIXME("Unhandled primitive stream %u.\n", stream); +} + +/* Process the WINED3DSIO_POW instruction in GLSL (dst = |src0|^src1) + * Src0 and src1 are scalars. Note that D3D uses the absolute of src0, while + * GLSL uses the value as-is. */ +static void shader_glsl_pow(const struct wined3d_shader_instruction *ins) +{ + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + struct glsl_src_param src0_param; + struct glsl_src_param src1_param; + DWORD dst_write_mask; + unsigned int dst_size; + + dst_write_mask = shader_glsl_append_dst(buffer, ins); + dst_size = shader_glsl_get_write_mask_size(dst_write_mask); + + shader_glsl_add_src_param(ins, &ins->src[0], WINED3DSP_WRITEMASK_0, &src0_param); + shader_glsl_add_src_param(ins, &ins->src[1], WINED3DSP_WRITEMASK_0, &src1_param); + + if (dst_size > 1) + { + shader_addline(buffer, "vec%u(%s == 0.0 ? 1.0 : pow(abs(%s), %s)));\n", + dst_size, src1_param.param_str, src0_param.param_str, src1_param.param_str); + } + else + { + shader_addline(buffer, "%s == 0.0 ? 1.0 : pow(abs(%s), %s));\n", + src1_param.param_str, src0_param.param_str, src1_param.param_str); + } +} + +/* Map the opcode 1-to-1 to the GL code (arg->dst = instruction(src0, src1, ...) */ +static void shader_glsl_map2gl(const struct wined3d_shader_instruction *ins) +{ + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + struct glsl_src_param src_param; + const char *instruction; + DWORD write_mask; + unsigned i; + + /* Determine the GLSL function to use based on the opcode */ + /* TODO: Possibly make this a table for faster lookups */ + switch (ins->handler_idx) + { + case WINED3DSIH_ABS: instruction = "abs"; break; + case WINED3DSIH_BFREV: instruction = "bitfieldReverse"; break; + case WINED3DSIH_COUNTBITS: instruction = "bitCount"; break; + case WINED3DSIH_DSX: instruction = "dFdx"; break; + case WINED3DSIH_DSX_COARSE: instruction = "dFdxCoarse"; break; + case WINED3DSIH_DSX_FINE: instruction = "dFdxFine"; break; + case WINED3DSIH_DSY: instruction = "ycorrection.y * dFdy"; break; + case WINED3DSIH_DSY_COARSE: instruction = "ycorrection.y * dFdyCoarse"; break; + case WINED3DSIH_DSY_FINE: instruction = "ycorrection.y * dFdyFine"; break; + case WINED3DSIH_FIRSTBIT_HI: instruction = "findMSB"; break; + case WINED3DSIH_FIRSTBIT_LO: instruction = "findLSB"; break; + case WINED3DSIH_FIRSTBIT_SHI: instruction = "findMSB"; break; + case WINED3DSIH_FRC: instruction = "fract"; break; + case WINED3DSIH_IMAX: instruction = "max"; break; + case WINED3DSIH_IMIN: instruction = "min"; break; + case WINED3DSIH_MAX: instruction = "max"; break; + case WINED3DSIH_MIN: instruction = "min"; break; + case WINED3DSIH_ROUND_NE: instruction = "roundEven"; break; + case WINED3DSIH_ROUND_NI: instruction = "floor"; break; + case WINED3DSIH_ROUND_PI: instruction = "ceil"; break; + case WINED3DSIH_ROUND_Z: instruction = "trunc"; break; + case WINED3DSIH_SQRT: instruction = "sqrt"; break; + case WINED3DSIH_UMAX: instruction = "max"; break; + case WINED3DSIH_UMIN: instruction = "min"; break; + default: instruction = ""; + ERR("Opcode %s not yet handled in GLSL.\n", debug_d3dshaderinstructionhandler(ins->handler_idx)); + break; + } + + write_mask = shader_glsl_append_dst(buffer, ins); + + /* In D3D bits are numbered from the most significant bit. */ + if (ins->handler_idx == WINED3DSIH_FIRSTBIT_HI || ins->handler_idx == WINED3DSIH_FIRSTBIT_SHI) + shader_addline(buffer, "31 - "); + shader_addline(buffer, "%s(", instruction); + + if (ins->src_count) + { + shader_glsl_add_src_param(ins, &ins->src[0], write_mask, &src_param); + shader_addline(buffer, "%s", src_param.param_str); + for (i = 1; i < ins->src_count; ++i) + { + shader_glsl_add_src_param(ins, &ins->src[i], write_mask, &src_param); + shader_addline(buffer, ", %s", src_param.param_str); + } + } + + shader_addline(buffer, "));\n"); +} + +static void shader_glsl_float16(const struct wined3d_shader_instruction *ins) +{ + struct wined3d_shader_dst_param dst; + struct glsl_src_param src; + DWORD write_mask; + const char *fmt; + unsigned int i; + + fmt = ins->handler_idx == WINED3DSIH_F16TOF32 + ? "unpackHalf2x16(%s).x);\n" : "packHalf2x16(vec2(%s, 0.0)));\n"; + + dst = ins->dst[0]; + for (i = 0; i < 4; ++i) + { + dst.write_mask = ins->dst[0].write_mask & (WINED3DSP_WRITEMASK_0 << i); + if (!(write_mask = shader_glsl_append_dst_ext(ins->ctx->buffer, ins, + &dst, dst.reg.data_type))) + continue; + + shader_glsl_add_src_param(ins, &ins->src[0], write_mask, &src); + shader_addline(ins->ctx->buffer, fmt, src.param_str); + } +} + +static void shader_glsl_bitwise_op(const struct wined3d_shader_instruction *ins) +{ + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + struct wined3d_shader_dst_param dst; + struct glsl_src_param src[4]; + const char *instruction; + BOOL tmp_dst = FALSE; + char mask_char[6]; + unsigned int i, j; + DWORD write_mask; + + switch (ins->handler_idx) + { + case WINED3DSIH_BFI: instruction = "bitfieldInsert"; break; + case WINED3DSIH_IBFE: instruction = "bitfieldExtract"; break; + case WINED3DSIH_UBFE: instruction = "bitfieldExtract"; break; + default: + ERR("Unhandled opcode %#x.\n", ins->handler_idx); + return; + } + + for (i = 0; i < ins->src_count; ++i) + { + if (ins->dst[0].reg.idx[0].offset == ins->src[i].reg.idx[0].offset + && ins->dst[0].reg.type == ins->src[i].reg.type) + tmp_dst = TRUE; + } + + dst = ins->dst[0]; + for (i = 0; i < 4; ++i) + { + dst.write_mask = ins->dst[0].write_mask & (WINED3DSP_WRITEMASK_0 << i); + if (tmp_dst && (write_mask = shader_glsl_get_write_mask(&dst, mask_char))) + shader_addline(buffer, "tmp0%s = %sBitsToFloat(", mask_char, + dst.reg.data_type == WINED3D_DATA_INT ? "int" : "uint"); + else if (!(write_mask = shader_glsl_append_dst_ext(buffer, ins, &dst, dst.reg.data_type))) + continue; + + for (j = 0; j < ins->src_count; ++j) + shader_glsl_add_src_param(ins, &ins->src[j], write_mask, &src[j]); + shader_addline(buffer, "%s(", instruction); + for (j = 0; j < ins->src_count - 2; ++j) + shader_addline(buffer, "%s, ", src[ins->src_count - j - 1].param_str); + shader_addline(buffer, "%s & 0x1f, %s & 0x1f));\n", src[1].param_str, src[0].param_str); + } + + if (tmp_dst) + { + shader_glsl_append_dst_ext(buffer, ins, &ins->dst[0], WINED3D_DATA_FLOAT); + shader_glsl_get_write_mask(&ins->dst[0], mask_char); + shader_addline(buffer, "tmp0%s);\n", mask_char); + } +} + +static void shader_glsl_nop(const struct wined3d_shader_instruction *ins) {} + +static void shader_glsl_nrm(const struct wined3d_shader_instruction *ins) +{ + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + struct glsl_src_param src_param; + unsigned int mask_size; + DWORD write_mask; + char dst_mask[6]; + + write_mask = shader_glsl_get_write_mask(ins->dst, dst_mask); + mask_size = shader_glsl_get_write_mask_size(write_mask); + shader_glsl_add_src_param(ins, &ins->src[0], write_mask, &src_param); + + if (mask_size > 3) + shader_addline(buffer, "tmp0.x = dot(vec3(%s), vec3(%s));\n", + src_param.param_str, src_param.param_str); + else + shader_addline(buffer, "tmp0.x = dot(%s, %s);\n", + src_param.param_str, src_param.param_str); + shader_glsl_append_dst(buffer, ins); + + shader_addline(buffer, "tmp0.x == 0.0 ? %s : (%s * inversesqrt(tmp0.x)));\n", + src_param.param_str, src_param.param_str); +} + +static void shader_glsl_scalar_op(const struct wined3d_shader_instruction *ins) +{ + DWORD shader_version = WINED3D_SHADER_VERSION(ins->ctx->reg_maps->shader_version.major, + ins->ctx->reg_maps->shader_version.minor); + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + struct glsl_src_param src0_param; + const char *prefix, *suffix; + unsigned int dst_size; + DWORD dst_write_mask; + + dst_write_mask = shader_glsl_append_dst(buffer, ins); + dst_size = shader_glsl_get_write_mask_size(dst_write_mask); + + if (shader_version < WINED3D_SHADER_VERSION(4, 0)) + dst_write_mask = WINED3DSP_WRITEMASK_3; + + shader_glsl_add_src_param(ins, &ins->src[0], dst_write_mask, &src0_param); + + switch (ins->handler_idx) + { + case WINED3DSIH_EXP: + case WINED3DSIH_EXPP: + prefix = "exp2("; + suffix = ")"; + break; + + case WINED3DSIH_LOG: + case WINED3DSIH_LOGP: + prefix = "log2(abs("; + suffix = "))"; + break; + + case WINED3DSIH_RCP: + prefix = "1.0 / "; + suffix = ""; + break; + + case WINED3DSIH_RSQ: + prefix = "inversesqrt(abs("; + suffix = "))"; + break; + + default: + prefix = ""; + suffix = ""; + FIXME("Unhandled instruction %#x.\n", ins->handler_idx); + break; + } + + if (dst_size > 1 && shader_version < WINED3D_SHADER_VERSION(4, 0)) + shader_addline(buffer, "vec%u(%s%s%s));\n", dst_size, prefix, src0_param.param_str, suffix); + else + shader_addline(buffer, "%s%s%s);\n", prefix, src0_param.param_str, suffix); +} + +/** Process the WINED3DSIO_EXPP instruction in GLSL: + * For shader model 1.x, do the following (and honor the writemask, so use a temporary variable): + * dst.x = 2^(floor(src)) + * dst.y = src - floor(src) + * dst.z = 2^src (partial precision is allowed, but optional) + * dst.w = 1.0; + * For 2.0 shaders, just do this (honoring writemask and swizzle): + * dst = 2^src; (partial precision is allowed, but optional) + */ +static void shader_glsl_expp(const struct wined3d_shader_instruction *ins) +{ + if (ins->ctx->reg_maps->shader_version.major < 2) + { + struct glsl_src_param src_param; + char dst_mask[6]; + + shader_glsl_add_src_param(ins, &ins->src[0], WINED3DSP_WRITEMASK_3, &src_param); + + shader_addline(ins->ctx->buffer, "tmp0.x = exp2(floor(%s));\n", src_param.param_str); + shader_addline(ins->ctx->buffer, "tmp0.y = %s - floor(%s);\n", src_param.param_str, src_param.param_str); + shader_addline(ins->ctx->buffer, "tmp0.z = exp2(%s);\n", src_param.param_str); + shader_addline(ins->ctx->buffer, "tmp0.w = 1.0;\n"); + + shader_glsl_append_dst(ins->ctx->buffer, ins); + shader_glsl_get_write_mask(&ins->dst[0], dst_mask); + shader_addline(ins->ctx->buffer, "tmp0%s);\n", dst_mask); + return; + } + + shader_glsl_scalar_op(ins); +} + +static void shader_glsl_cast(const struct wined3d_shader_instruction *ins, + const char *vector_constructor, const char *scalar_constructor) +{ + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + struct glsl_src_param src_param; + unsigned int mask_size; + DWORD write_mask; + + write_mask = shader_glsl_append_dst(buffer, ins); + mask_size = shader_glsl_get_write_mask_size(write_mask); + shader_glsl_add_src_param(ins, &ins->src[0], write_mask, &src_param); + + if (mask_size > 1) + shader_addline(buffer, "%s%u(%s));\n", vector_constructor, mask_size, src_param.param_str); + else + shader_addline(buffer, "%s(%s));\n", scalar_constructor, src_param.param_str); +} + +static void shader_glsl_to_int(const struct wined3d_shader_instruction *ins) +{ + shader_glsl_cast(ins, "ivec", "int"); +} + +static void shader_glsl_to_uint(const struct wined3d_shader_instruction *ins) +{ + shader_glsl_cast(ins, "uvec", "uint"); +} + +static void shader_glsl_to_float(const struct wined3d_shader_instruction *ins) +{ + shader_glsl_cast(ins, "vec", "float"); +} + +/** Process signed comparison opcodes in GLSL. */ +static void shader_glsl_compare(const struct wined3d_shader_instruction *ins) +{ + struct glsl_src_param src0_param; + struct glsl_src_param src1_param; + DWORD write_mask; + unsigned int mask_size; + + write_mask = shader_glsl_append_dst(ins->ctx->buffer, ins); + mask_size = shader_glsl_get_write_mask_size(write_mask); + shader_glsl_add_src_param(ins, &ins->src[0], write_mask, &src0_param); + shader_glsl_add_src_param(ins, &ins->src[1], write_mask, &src1_param); + + if (mask_size > 1) { + const char *compare; + + switch(ins->handler_idx) + { + case WINED3DSIH_SLT: compare = "lessThan"; break; + case WINED3DSIH_SGE: compare = "greaterThanEqual"; break; + default: compare = ""; + FIXME("Can't handle opcode %s.\n", debug_d3dshaderinstructionhandler(ins->handler_idx)); + } + + shader_addline(ins->ctx->buffer, "vec%d(%s(%s, %s)));\n", mask_size, compare, + src0_param.param_str, src1_param.param_str); + } else { + switch(ins->handler_idx) + { + case WINED3DSIH_SLT: + /* Step(src0, src1) is not suitable here because if src0 == src1 SLT is supposed, + * to return 0.0 but step returns 1.0 because step is not < x + * An alternative is a bvec compare padded with an unused second component. + * step(src1 * -1.0, src0 * -1.0) is not an option because it suffers from the same + * issue. Playing with not() is not possible either because not() does not accept + * a scalar. + */ + shader_addline(ins->ctx->buffer, "(%s < %s) ? 1.0 : 0.0);\n", + src0_param.param_str, src1_param.param_str); + break; + case WINED3DSIH_SGE: + /* Here we can use the step() function and safe a conditional */ + shader_addline(ins->ctx->buffer, "step(%s, %s));\n", src1_param.param_str, src0_param.param_str); + break; + default: + FIXME("Can't handle opcode %s.\n", debug_d3dshaderinstructionhandler(ins->handler_idx)); + } + + } +} + +static void shader_glsl_swapc(const struct wined3d_shader_instruction *ins) +{ + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + struct wined3d_shader_dst_param dst[2]; + struct glsl_src_param src[3]; + unsigned int i, j, k; + char mask_char[6]; + DWORD write_mask; + BOOL tmp_dst[2]; + + for (i = 0; i < ins->dst_count; ++i) + { + tmp_dst[i] = FALSE; + for (j = 0; j < ins->src_count; ++j) + { + if (ins->dst[i].reg.idx[0].offset == ins->src[j].reg.idx[0].offset + && ins->dst[i].reg.type == ins->src[j].reg.type) + tmp_dst[i] = TRUE; + } + } + + dst[0] = ins->dst[0]; + dst[1] = ins->dst[1]; + for (i = 0; i < 4; ++i) + { + for (j = 0; j < ARRAY_SIZE(dst); ++j) + { + dst[j].write_mask = ins->dst[j].write_mask & (WINED3DSP_WRITEMASK_0 << i); + if (tmp_dst[j] && (write_mask = shader_glsl_get_write_mask(&dst[j], mask_char))) + shader_addline(buffer, "tmp%u%s = (", j, mask_char); + else if (!(write_mask = shader_glsl_append_dst_ext(buffer, ins, &dst[j], dst[j].reg.data_type))) + continue; + + for (k = 0; k < ARRAY_SIZE(src); ++k) + shader_glsl_add_src_param(ins, &ins->src[k], write_mask, &src[k]); + + shader_addline(buffer, "%sbool(%s) ? %s : %s);\n", !j ? "!" : "", + src[0].param_str, src[1].param_str, src[2].param_str); + } + } + + for (i = 0; i < ARRAY_SIZE(tmp_dst); ++i) + { + if (tmp_dst[i]) + { + shader_glsl_get_write_mask(&ins->dst[i], mask_char); + shader_glsl_append_dst_ext(buffer, ins, &ins->dst[i], ins->dst[i].reg.data_type); + shader_addline(buffer, "tmp%u%s);\n", i, mask_char); + } + } +} + +static void shader_glsl_conditional_move(const struct wined3d_shader_instruction *ins) +{ + const char *condition_prefix, *condition_suffix; + struct wined3d_shader_dst_param dst; + struct glsl_src_param src0_param; + struct glsl_src_param src1_param; + struct glsl_src_param src2_param; + BOOL temp_destination = FALSE; + DWORD cmp_channel = 0; + unsigned int i, j; + char mask_char[6]; + DWORD write_mask; + + switch (ins->handler_idx) + { + case WINED3DSIH_CMP: + condition_prefix = ""; + condition_suffix = " >= 0.0"; + break; + + case WINED3DSIH_CND: + condition_prefix = ""; + condition_suffix = " > 0.5"; + break; + + case WINED3DSIH_MOVC: + condition_prefix = "bool("; + condition_suffix = ")"; + break; + + default: + FIXME("Unhandled instruction %#x.\n", ins->handler_idx); + condition_prefix = ""; + condition_suffix = ""; + break; + } + + if (shader_is_scalar(&ins->dst[0].reg) || shader_is_scalar(&ins->src[0].reg)) + { + write_mask = shader_glsl_append_dst(ins->ctx->buffer, ins); + shader_glsl_add_src_param(ins, &ins->src[0], write_mask, &src0_param); + shader_glsl_add_src_param(ins, &ins->src[1], write_mask, &src1_param); + shader_glsl_add_src_param(ins, &ins->src[2], write_mask, &src2_param); + + shader_addline(ins->ctx->buffer, "%s%s%s ? %s : %s);\n", + condition_prefix, src0_param.param_str, condition_suffix, + src1_param.param_str, src2_param.param_str); + return; + } + + dst = ins->dst[0]; + + /* Splitting the instruction up in multiple lines imposes a problem: + * The first lines may overwrite source parameters of the following lines. + * Deal with that by using a temporary destination register if needed. */ + if ((ins->src[0].reg.idx[0].offset == dst.reg.idx[0].offset + && ins->src[0].reg.type == dst.reg.type) + || (ins->src[1].reg.idx[0].offset == dst.reg.idx[0].offset + && ins->src[1].reg.type == dst.reg.type) + || (ins->src[2].reg.idx[0].offset == dst.reg.idx[0].offset + && ins->src[2].reg.type == dst.reg.type)) + temp_destination = TRUE; + + /* Cycle through all source0 channels. */ + for (i = 0; i < 4; ++i) + { + write_mask = 0; + /* Find the destination channels which use the current source0 channel. */ + for (j = 0; j < 4; ++j) + { + if (shader_glsl_swizzle_get_component(ins->src[0].swizzle, j) == i) + { + write_mask |= WINED3DSP_WRITEMASK_0 << j; + cmp_channel = WINED3DSP_WRITEMASK_0 << j; + } + } + dst.write_mask = ins->dst[0].write_mask & write_mask; + + if (temp_destination) + { + if (!(write_mask = shader_glsl_get_write_mask(&dst, mask_char))) + continue; + shader_addline(ins->ctx->buffer, "tmp0%s = (", mask_char); + } + else if (!(write_mask = shader_glsl_append_dst_ext(ins->ctx->buffer, ins, &dst, dst.reg.data_type))) + continue; + + shader_glsl_add_src_param(ins, &ins->src[0], cmp_channel, &src0_param); + shader_glsl_add_src_param(ins, &ins->src[1], write_mask, &src1_param); + shader_glsl_add_src_param(ins, &ins->src[2], write_mask, &src2_param); + + shader_addline(ins->ctx->buffer, "%s%s%s ? %s : %s);\n", + condition_prefix, src0_param.param_str, condition_suffix, + src1_param.param_str, src2_param.param_str); + } + + if (temp_destination) + { + shader_glsl_get_write_mask(&ins->dst[0], mask_char); + shader_glsl_append_dst(ins->ctx->buffer, ins); + shader_addline(ins->ctx->buffer, "tmp0%s);\n", mask_char); + } +} + +/** Process the CND opcode in GLSL (dst = (src0 > 0.5) ? src1 : src2) */ +/* For ps 1.1-1.3, only a single component of src0 is used. For ps 1.4 + * the compare is done per component of src0. */ +static void shader_glsl_cnd(const struct wined3d_shader_instruction *ins) +{ + struct glsl_src_param src0_param; + struct glsl_src_param src1_param; + struct glsl_src_param src2_param; + DWORD write_mask; + DWORD shader_version = WINED3D_SHADER_VERSION(ins->ctx->reg_maps->shader_version.major, + ins->ctx->reg_maps->shader_version.minor); + + if (shader_version < WINED3D_SHADER_VERSION(1, 4)) + { + write_mask = shader_glsl_append_dst(ins->ctx->buffer, ins); + shader_glsl_add_src_param(ins, &ins->src[0], WINED3DSP_WRITEMASK_0, &src0_param); + shader_glsl_add_src_param(ins, &ins->src[1], write_mask, &src1_param); + shader_glsl_add_src_param(ins, &ins->src[2], write_mask, &src2_param); + + if (ins->coissue && ins->dst->write_mask != WINED3DSP_WRITEMASK_3) + shader_addline(ins->ctx->buffer, "%s /* COISSUE! */);\n", src1_param.param_str); + else + shader_addline(ins->ctx->buffer, "%s > 0.5 ? %s : %s);\n", + src0_param.param_str, src1_param.param_str, src2_param.param_str); + return; + } + + shader_glsl_conditional_move(ins); +} + +/** GLSL code generation for WINED3DSIO_MAD: Multiply the first 2 opcodes, then add the last */ +static void shader_glsl_mad(const struct wined3d_shader_instruction *ins) +{ + struct glsl_src_param src0_param; + struct glsl_src_param src1_param; + struct glsl_src_param src2_param; + DWORD write_mask; + + write_mask = shader_glsl_append_dst(ins->ctx->buffer, ins); + shader_glsl_add_src_param(ins, &ins->src[0], write_mask, &src0_param); + shader_glsl_add_src_param(ins, &ins->src[1], write_mask, &src1_param); + shader_glsl_add_src_param(ins, &ins->src[2], write_mask, &src2_param); + shader_addline(ins->ctx->buffer, "(%s * %s) + %s);\n", + src0_param.param_str, src1_param.param_str, src2_param.param_str); +} + +/* Handles transforming all WINED3DSIO_M?x? opcodes for + Vertex shaders to GLSL codes */ +static void shader_glsl_mnxn(const struct wined3d_shader_instruction *ins) +{ + int i; + int nComponents = 0; + struct wined3d_shader_dst_param tmp_dst = {{0}}; + struct wined3d_shader_src_param tmp_src[2] = {{{0}}}; + struct wined3d_shader_instruction tmp_ins; + + memset(&tmp_ins, 0, sizeof(tmp_ins)); + + /* Set constants for the temporary argument */ + tmp_ins.ctx = ins->ctx; + tmp_ins.dst_count = 1; + tmp_ins.dst = &tmp_dst; + tmp_ins.src_count = 2; + tmp_ins.src = tmp_src; + + switch(ins->handler_idx) + { + case WINED3DSIH_M4x4: + nComponents = 4; + tmp_ins.handler_idx = WINED3DSIH_DP4; + break; + case WINED3DSIH_M4x3: + nComponents = 3; + tmp_ins.handler_idx = WINED3DSIH_DP4; + break; + case WINED3DSIH_M3x4: + nComponents = 4; + tmp_ins.handler_idx = WINED3DSIH_DP3; + break; + case WINED3DSIH_M3x3: + nComponents = 3; + tmp_ins.handler_idx = WINED3DSIH_DP3; + break; + case WINED3DSIH_M3x2: + nComponents = 2; + tmp_ins.handler_idx = WINED3DSIH_DP3; + break; + default: + break; + } + + tmp_dst = ins->dst[0]; + tmp_src[0] = ins->src[0]; + tmp_src[1] = ins->src[1]; + for (i = 0; i < nComponents; ++i) + { + tmp_dst.write_mask = WINED3DSP_WRITEMASK_0 << i; + shader_glsl_dot(&tmp_ins); + ++tmp_src[1].reg.idx[0].offset; + } +} + +/** + The LRP instruction performs a component-wise linear interpolation + between the second and third operands using the first operand as the + blend factor. Equation: (dst = src2 + src0 * (src1 - src2)) + This is equivalent to mix(src2, src1, src0); +*/ +static void shader_glsl_lrp(const struct wined3d_shader_instruction *ins) +{ + struct glsl_src_param src0_param; + struct glsl_src_param src1_param; + struct glsl_src_param src2_param; + DWORD write_mask; + + write_mask = shader_glsl_append_dst(ins->ctx->buffer, ins); + + shader_glsl_add_src_param(ins, &ins->src[0], write_mask, &src0_param); + shader_glsl_add_src_param(ins, &ins->src[1], write_mask, &src1_param); + shader_glsl_add_src_param(ins, &ins->src[2], write_mask, &src2_param); + + shader_addline(ins->ctx->buffer, "mix(%s, %s, %s));\n", + src2_param.param_str, src1_param.param_str, src0_param.param_str); +} + +/** Process the WINED3DSIO_LIT instruction in GLSL: + * dst.x = dst.w = 1.0 + * dst.y = (src0.x > 0) ? src0.x + * dst.z = (src0.x > 0) ? ((src0.y > 0) ? pow(src0.y, src.w) : 0) : 0 + * where src.w is clamped at +- 128 + */ +static void shader_glsl_lit(const struct wined3d_shader_instruction *ins) +{ + struct glsl_src_param src0_param; + struct glsl_src_param src1_param; + struct glsl_src_param src3_param; + char dst_mask[6]; + + shader_glsl_append_dst(ins->ctx->buffer, ins); + shader_glsl_get_write_mask(&ins->dst[0], dst_mask); + + shader_glsl_add_src_param(ins, &ins->src[0], WINED3DSP_WRITEMASK_0, &src0_param); + shader_glsl_add_src_param(ins, &ins->src[0], WINED3DSP_WRITEMASK_1, &src1_param); + shader_glsl_add_src_param(ins, &ins->src[0], WINED3DSP_WRITEMASK_3, &src3_param); + + /* The sdk specifies the instruction like this + * dst.x = 1.0; + * if(src.x > 0.0) dst.y = src.x + * else dst.y = 0.0. + * if(src.x > 0.0 && src.y > 0.0) dst.z = pow(src.y, power); + * else dst.z = 0.0; + * dst.w = 1.0; + * (where power = src.w clamped between -128 and 128) + * + * Obviously that has quite a few conditionals in it which we don't like. So the first step is this: + * dst.x = 1.0 ... No further explanation needed + * dst.y = max(src.y, 0.0); ... If x < 0.0, use 0.0, otherwise x. Same as the conditional + * dst.z = x > 0.0 ? pow(max(y, 0.0), p) : 0; ... 0 ^ power is 0, and otherwise we use y anyway + * dst.w = 1.0. ... Nothing fancy. + * + * So we still have one conditional in there. So do this: + * dst.z = pow(max(0.0, src.y) * step(0.0, src.x), power); + * + * step(0.0, x) will return 1 if src.x > 0.0, and 0 otherwise. So if y is 0 we get pow(0.0 * 1.0, power), + * which sets dst.z to 0. If y > 0, but x = 0.0, we get pow(y * 0.0, power), which results in 0 too. + * if both x and y are > 0, we get pow(y * 1.0, power), as it is supposed to. + * + * Unfortunately pow(0.0 ^ 0.0) returns NaN on most GPUs, but lit with src.y = 0 and src.w = 0 returns + * a non-NaN value in dst.z. What we return doesn't matter, as long as it is not NaN. Return 0, which is + * what all Windows HW drivers and GL_ARB_vertex_program's LIT do. + */ + shader_addline(ins->ctx->buffer, + "vec4(1.0, max(%s, 0.0), %s == 0.0 ? 0.0 : " + "pow(max(0.0, %s) * step(0.0, %s), clamp(%s, -128.0, 128.0)), 1.0)%s);\n", + src0_param.param_str, src3_param.param_str, src1_param.param_str, + src0_param.param_str, src3_param.param_str, dst_mask); +} + +/** Process the WINED3DSIO_DST instruction in GLSL: + * dst.x = 1.0 + * dst.y = src0.x * src0.y + * dst.z = src0.z + * dst.w = src1.w + */ +static void shader_glsl_dst(const struct wined3d_shader_instruction *ins) +{ + struct glsl_src_param src0y_param; + struct glsl_src_param src0z_param; + struct glsl_src_param src1y_param; + struct glsl_src_param src1w_param; + char dst_mask[6]; + + shader_glsl_append_dst(ins->ctx->buffer, ins); + shader_glsl_get_write_mask(&ins->dst[0], dst_mask); + + shader_glsl_add_src_param(ins, &ins->src[0], WINED3DSP_WRITEMASK_1, &src0y_param); + shader_glsl_add_src_param(ins, &ins->src[0], WINED3DSP_WRITEMASK_2, &src0z_param); + shader_glsl_add_src_param(ins, &ins->src[1], WINED3DSP_WRITEMASK_1, &src1y_param); + shader_glsl_add_src_param(ins, &ins->src[1], WINED3DSP_WRITEMASK_3, &src1w_param); + + shader_addline(ins->ctx->buffer, "vec4(1.0, %s * %s, %s, %s))%s;\n", + src0y_param.param_str, src1y_param.param_str, src0z_param.param_str, src1w_param.param_str, dst_mask); +} + +/** Process the WINED3DSIO_SINCOS instruction in GLSL: + * VS 2.0 requires that specific cosine and sine constants be passed to this instruction so the hardware + * can handle it. But, these functions are built-in for GLSL, so we can just ignore the last 2 params. + * + * dst.x = cos(src0.?) + * dst.y = sin(src0.?) + * dst.z = dst.z + * dst.w = dst.w + */ +static void shader_glsl_sincos(const struct wined3d_shader_instruction *ins) +{ + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + struct glsl_src_param src0_param; + DWORD write_mask; + + if (ins->ctx->reg_maps->shader_version.major < 4) + { + shader_glsl_add_src_param(ins, &ins->src[0], WINED3DSP_WRITEMASK_0, &src0_param); + + write_mask = shader_glsl_append_dst(buffer, ins); + switch (write_mask) + { + case WINED3DSP_WRITEMASK_0: + shader_addline(buffer, "cos(%s));\n", src0_param.param_str); + break; + + case WINED3DSP_WRITEMASK_1: + shader_addline(buffer, "sin(%s));\n", src0_param.param_str); + break; + + case (WINED3DSP_WRITEMASK_0 | WINED3DSP_WRITEMASK_1): + shader_addline(buffer, "vec2(cos(%s), sin(%s)));\n", + src0_param.param_str, src0_param.param_str); + break; + + default: + ERR("Write mask should be .x, .y or .xy\n"); + break; + } + + return; + } + + if (ins->dst[0].reg.type != WINED3DSPR_NULL) + { + + if (ins->dst[1].reg.type != WINED3DSPR_NULL) + { + char dst_mask[6]; + + write_mask = shader_glsl_get_write_mask(&ins->dst[0], dst_mask); + shader_glsl_add_src_param(ins, &ins->src[0], write_mask, &src0_param); + shader_addline(buffer, "tmp0%s = sin(%s);\n", dst_mask, src0_param.param_str); + + write_mask = shader_glsl_append_dst_ext(buffer, ins, &ins->dst[1], ins->dst[1].reg.data_type); + shader_glsl_add_src_param(ins, &ins->src[0], write_mask, &src0_param); + shader_addline(buffer, "cos(%s));\n", src0_param.param_str); + + shader_glsl_append_dst_ext(buffer, ins, &ins->dst[0], ins->dst[0].reg.data_type); + shader_addline(buffer, "tmp0%s);\n", dst_mask); + } + else + { + write_mask = shader_glsl_append_dst_ext(buffer, ins, &ins->dst[0], ins->dst[0].reg.data_type); + shader_glsl_add_src_param(ins, &ins->src[0], write_mask, &src0_param); + shader_addline(buffer, "sin(%s));\n", src0_param.param_str); + } + } + else if (ins->dst[1].reg.type != WINED3DSPR_NULL) + { + write_mask = shader_glsl_append_dst_ext(buffer, ins, &ins->dst[1], ins->dst[1].reg.data_type); + shader_glsl_add_src_param(ins, &ins->src[0], write_mask, &src0_param); + shader_addline(buffer, "cos(%s));\n", src0_param.param_str); + } +} + +/* sgn in vs_2_0 has 2 extra parameters(registers for temporary storage) which we don't use + * here. But those extra parameters require a dedicated function for sgn, since map2gl would + * generate invalid code + */ +static void shader_glsl_sgn(const struct wined3d_shader_instruction *ins) +{ + struct glsl_src_param src0_param; + DWORD write_mask; + + write_mask = shader_glsl_append_dst(ins->ctx->buffer, ins); + shader_glsl_add_src_param(ins, &ins->src[0], write_mask, &src0_param); + + shader_addline(ins->ctx->buffer, "sign(%s));\n", src0_param.param_str); +} + +/** Process the WINED3DSIO_LOOP instruction in GLSL: + * Start a for() loop where src1.y is the initial value of aL, + * increment aL by src1.z for a total of src1.x iterations. + * Need to use a temporary variable for this operation. + */ +/* FIXME: I don't think nested loops will work correctly this way. */ +static void shader_glsl_loop(const struct wined3d_shader_instruction *ins) +{ + struct wined3d_shader_parser_state *state = ins->ctx->state; + struct shader_glsl_ctx_priv *priv = ins->ctx->backend_data; + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + const struct wined3d_shader *shader = ins->ctx->shader; + const struct wined3d_shader_lconst *constant; + struct wined3d_string_buffer *reg_name; + const DWORD *control_values = NULL; + + if (ins->ctx->reg_maps->shader_version.major < 4) + { + /* Try to hardcode the loop control parameters if possible. Direct3D 9 + * class hardware doesn't support real varying indexing, but Microsoft + * designed this feature for Shader model 2.x+. If the loop control is + * known at compile time, the GLSL compiler can unroll the loop, and + * replace indirect addressing with direct addressing. */ + if (ins->src[1].reg.type == WINED3DSPR_CONSTINT) + { + LIST_FOR_EACH_ENTRY(constant, &shader->constantsI, struct wined3d_shader_lconst, entry) + { + if (constant->idx == ins->src[1].reg.idx[0].offset) + { + control_values = constant->value; + break; + } + } + } + + if (control_values) + { + struct wined3d_shader_loop_control loop_control; + loop_control.count = control_values[0]; + loop_control.start = control_values[1]; + loop_control.step = (int)control_values[2]; + + if (loop_control.step > 0) + { + shader_addline(buffer, "for (aL%u = %u; aL%u < (%u * %d + %u); aL%u += %d)\n{\n", + state->current_loop_depth, loop_control.start, + state->current_loop_depth, loop_control.count, loop_control.step, loop_control.start, + state->current_loop_depth, loop_control.step); + } + else if (loop_control.step < 0) + { + shader_addline(buffer, "for (aL%u = %u; aL%u > (%u * %d + %u); aL%u += %d)\n{\n", + state->current_loop_depth, loop_control.start, + state->current_loop_depth, loop_control.count, loop_control.step, loop_control.start, + state->current_loop_depth, loop_control.step); + } + else + { + shader_addline(buffer, "for (aL%u = %u, tmpInt%u = 0; tmpInt%u < %u; tmpInt%u++)\n{\n", + state->current_loop_depth, loop_control.start, state->current_loop_depth, + state->current_loop_depth, loop_control.count, + state->current_loop_depth); + } + } + else + { + reg_name = string_buffer_get(priv->string_buffers); + shader_glsl_get_register_name(&ins->src[1].reg, ins->src[1].reg.data_type, reg_name, NULL, ins->ctx); + + shader_addline(buffer, "for (tmpInt%u = 0, aL%u = %s.y; tmpInt%u < %s.x; tmpInt%u++, aL%u += %s.z)\n{\n", + state->current_loop_depth, state->current_loop_reg, reg_name->buffer, + state->current_loop_depth, reg_name->buffer, + state->current_loop_depth, state->current_loop_reg, reg_name->buffer); + + string_buffer_release(priv->string_buffers, reg_name); + + } + + ++state->current_loop_reg; + } + else + { + shader_addline(buffer, "for (;;)\n{\n"); + } + + ++state->current_loop_depth; +} + +static void shader_glsl_end(const struct wined3d_shader_instruction *ins) +{ + struct wined3d_shader_parser_state *state = ins->ctx->state; + + shader_addline(ins->ctx->buffer, "}\n"); + + if (ins->handler_idx == WINED3DSIH_ENDLOOP) + { + --state->current_loop_depth; + --state->current_loop_reg; + } + + if (ins->handler_idx == WINED3DSIH_ENDREP) + { + --state->current_loop_depth; + } +} + +static void shader_glsl_rep(const struct wined3d_shader_instruction *ins) +{ + struct wined3d_shader_parser_state *state = ins->ctx->state; + const struct wined3d_shader *shader = ins->ctx->shader; + const struct wined3d_shader_lconst *constant; + struct glsl_src_param src0_param; + const DWORD *control_values = NULL; + + /* Try to hardcode local values to help the GLSL compiler to unroll and optimize the loop */ + if (ins->src[0].reg.type == WINED3DSPR_CONSTINT) + { + LIST_FOR_EACH_ENTRY(constant, &shader->constantsI, struct wined3d_shader_lconst, entry) + { + if (constant->idx == ins->src[0].reg.idx[0].offset) + { + control_values = constant->value; + break; + } + } + } + + if (control_values) + { + shader_addline(ins->ctx->buffer, "for (tmpInt%d = 0; tmpInt%d < %d; tmpInt%d++) {\n", + state->current_loop_depth, state->current_loop_depth, + control_values[0], state->current_loop_depth); + } + else + { + shader_glsl_add_src_param(ins, &ins->src[0], WINED3DSP_WRITEMASK_0, &src0_param); + shader_addline(ins->ctx->buffer, "for (tmpInt%d = 0; tmpInt%d < %s; tmpInt%d++) {\n", + state->current_loop_depth, state->current_loop_depth, + src0_param.param_str, state->current_loop_depth); + } + + ++state->current_loop_depth; +} + +static void shader_glsl_switch(const struct wined3d_shader_instruction *ins) +{ + struct glsl_src_param src0_param; + + shader_glsl_add_src_param(ins, &ins->src[0], WINED3DSP_WRITEMASK_0, &src0_param); + shader_addline(ins->ctx->buffer, "switch (%s)\n{\n", src0_param.param_str); +} + +static void shader_glsl_case(const struct wined3d_shader_instruction *ins) +{ + struct glsl_src_param src0_param; + + shader_glsl_add_src_param(ins, &ins->src[0], WINED3DSP_WRITEMASK_0, &src0_param); + shader_addline(ins->ctx->buffer, "case %s:\n", src0_param.param_str); +} + +static void shader_glsl_default(const struct wined3d_shader_instruction *ins) +{ + shader_addline(ins->ctx->buffer, "default:\n"); +} + +static void shader_glsl_generate_condition(const struct wined3d_shader_instruction *ins) +{ + struct glsl_src_param src_param; + const char *condition; + + condition = ins->flags == WINED3D_SHADER_CONDITIONAL_OP_NZ ? "bool" : "!bool"; + shader_glsl_add_src_param(ins, &ins->src[0], WINED3DSP_WRITEMASK_0, &src_param); + shader_addline(ins->ctx->buffer, "if (%s(%s))\n", condition, src_param.param_str); +} + +static void shader_glsl_if(const struct wined3d_shader_instruction *ins) +{ + shader_glsl_generate_condition(ins); + shader_addline(ins->ctx->buffer, "{\n"); +} + +static void shader_glsl_ifc(const struct wined3d_shader_instruction *ins) +{ + struct glsl_src_param src0_param; + struct glsl_src_param src1_param; + + shader_glsl_add_src_param(ins, &ins->src[0], WINED3DSP_WRITEMASK_0, &src0_param); + shader_glsl_add_src_param(ins, &ins->src[1], WINED3DSP_WRITEMASK_0, &src1_param); + + shader_addline(ins->ctx->buffer, "if (%s %s %s) {\n", + src0_param.param_str, shader_glsl_get_rel_op(ins->flags), src1_param.param_str); +} + +static void shader_glsl_else(const struct wined3d_shader_instruction *ins) +{ + shader_addline(ins->ctx->buffer, "} else {\n"); +} + +static void shader_glsl_emit(const struct wined3d_shader_instruction *ins) +{ + unsigned int stream = ins->handler_idx == WINED3DSIH_EMIT ? 0 : ins->src[0].reg.idx[0].offset; + const struct wined3d_shader_reg_maps *reg_maps = ins->ctx->reg_maps; + const struct shader_glsl_ctx_priv *priv = ins->ctx->backend_data; + + shader_addline(ins->ctx->buffer, "setup_gs_output(gs_out);\n"); + if (!priv->gl_info->supported[ARB_CLIP_CONTROL]) + shader_glsl_fixup_position(ins->ctx->buffer, reg_maps->viewport_array); + + if (!stream) + shader_addline(ins->ctx->buffer, "EmitVertex();\n"); + else + FIXME("Unhandled primitive stream %u.\n", stream); +} + +static void shader_glsl_break(const struct wined3d_shader_instruction *ins) +{ + shader_addline(ins->ctx->buffer, "break;\n"); +} + +/* FIXME: According to MSDN the compare is done per component. */ +static void shader_glsl_breakc(const struct wined3d_shader_instruction *ins) +{ + struct glsl_src_param src0_param; + struct glsl_src_param src1_param; + + shader_glsl_add_src_param(ins, &ins->src[0], WINED3DSP_WRITEMASK_0, &src0_param); + shader_glsl_add_src_param(ins, &ins->src[1], WINED3DSP_WRITEMASK_0, &src1_param); + + shader_addline(ins->ctx->buffer, "if (%s %s %s) break;\n", + src0_param.param_str, shader_glsl_get_rel_op(ins->flags), src1_param.param_str); +} + +static void shader_glsl_conditional_op(const struct wined3d_shader_instruction *ins) +{ + const char *op; + + switch (ins->handler_idx) + { + case WINED3DSIH_BREAKP: + op = "break;"; + break; + case WINED3DSIH_CONTINUEP: + op = "continue;"; + break; + case WINED3DSIH_RETP: + op = "return;"; + break; + default: + ERR("Unhandled opcode %#x.\n", ins->handler_idx); + return; + } + + shader_glsl_generate_condition(ins); + if (ins->handler_idx == WINED3DSIH_RETP) + { + shader_addline(ins->ctx->buffer, "{\n"); + shader_glsl_generate_shader_epilogue(ins->ctx); + } + shader_addline(ins->ctx->buffer, " %s\n", op); + if (ins->handler_idx == WINED3DSIH_RETP) + shader_addline(ins->ctx->buffer, "}\n"); +} + +static void shader_glsl_continue(const struct wined3d_shader_instruction *ins) +{ + shader_addline(ins->ctx->buffer, "continue;\n"); +} + +static void shader_glsl_label(const struct wined3d_shader_instruction *ins) +{ + shader_addline(ins->ctx->buffer, "}\n"); + shader_addline(ins->ctx->buffer, "void subroutine%u()\n{\n", ins->src[0].reg.idx[0].offset); + + /* Subroutines appear at the end of the shader. */ + ins->ctx->state->in_subroutine = TRUE; +} + +static void shader_glsl_call(const struct wined3d_shader_instruction *ins) +{ + shader_addline(ins->ctx->buffer, "subroutine%u();\n", ins->src[0].reg.idx[0].offset); +} + +static void shader_glsl_callnz(const struct wined3d_shader_instruction *ins) +{ + struct glsl_src_param src1_param; + + shader_glsl_add_src_param(ins, &ins->src[1], WINED3DSP_WRITEMASK_0, &src1_param); + shader_addline(ins->ctx->buffer, "if (%s) subroutine%u();\n", + src1_param.param_str, ins->src[0].reg.idx[0].offset); +} + +static void shader_glsl_ret(const struct wined3d_shader_instruction *ins) +{ + const struct wined3d_shader_version *version = &ins->ctx->shader->reg_maps.shader_version; + + if (version->major >= 4 && !ins->ctx->state->in_subroutine) + { + shader_glsl_generate_shader_epilogue(ins->ctx); + shader_addline(ins->ctx->buffer, "return;\n"); + } +} + +static void shader_glsl_tex(const struct wined3d_shader_instruction *ins) +{ + DWORD shader_version = WINED3D_SHADER_VERSION(ins->ctx->reg_maps->shader_version.major, + ins->ctx->reg_maps->shader_version.minor); + struct glsl_sample_function sample_function; + DWORD sample_flags = 0; + DWORD resource_idx; + DWORD mask = 0, swizzle; + const struct shader_glsl_ctx_priv *priv = ins->ctx->backend_data; + enum wined3d_shader_resource_type resource_type; + + /* 1.0-1.4: Use destination register as sampler source. + * 2.0+: Use provided sampler source. */ + if (shader_version < WINED3D_SHADER_VERSION(2,0)) + resource_idx = ins->dst[0].reg.idx[0].offset; + else + resource_idx = ins->src[1].reg.idx[0].offset; + + resource_type = ins->ctx->reg_maps->shader_version.type == WINED3D_SHADER_TYPE_PIXEL + ? pixelshader_get_resource_type(ins->ctx->reg_maps, resource_idx, priv->cur_ps_args->tex_types) + : ins->ctx->reg_maps->resource_info[resource_idx].type; + + if (shader_version < WINED3D_SHADER_VERSION(1,4)) + { + DWORD flags = (priv->cur_ps_args->tex_transform >> resource_idx * WINED3D_PSARGS_TEXTRANSFORM_SHIFT) + & WINED3D_PSARGS_TEXTRANSFORM_MASK; + + /* Projected cube textures don't make a lot of sense, the resulting coordinates stay the same. */ + if (flags & WINED3D_PSARGS_PROJECTED && resource_type != WINED3D_SHADER_RESOURCE_TEXTURE_CUBE) + { + sample_flags |= WINED3D_GLSL_SAMPLE_PROJECTED; + switch (flags & ~WINED3D_PSARGS_PROJECTED) + { + case WINED3D_TTFF_COUNT1: + FIXME("WINED3D_TTFF_PROJECTED with WINED3D_TTFF_COUNT1?\n"); + break; + case WINED3D_TTFF_COUNT2: + mask = WINED3DSP_WRITEMASK_1; + break; + case WINED3D_TTFF_COUNT3: + mask = WINED3DSP_WRITEMASK_2; + break; + case WINED3D_TTFF_COUNT4: + case WINED3D_TTFF_DISABLE: + mask = WINED3DSP_WRITEMASK_3; + break; + } + } + } + else if (shader_version < WINED3D_SHADER_VERSION(2,0)) + { + enum wined3d_shader_src_modifier src_mod = ins->src[0].modifiers; + + if (src_mod == WINED3DSPSM_DZ) { + sample_flags |= WINED3D_GLSL_SAMPLE_PROJECTED; + mask = WINED3DSP_WRITEMASK_2; + } else if (src_mod == WINED3DSPSM_DW) { + sample_flags |= WINED3D_GLSL_SAMPLE_PROJECTED; + mask = WINED3DSP_WRITEMASK_3; + } + } + else + { + if ((ins->flags & WINED3DSI_TEXLD_PROJECT) + && resource_type != WINED3D_SHADER_RESOURCE_TEXTURE_CUBE) + { + /* ps 2.0 texldp instruction always divides by the fourth component. */ + sample_flags |= WINED3D_GLSL_SAMPLE_PROJECTED; + mask = WINED3DSP_WRITEMASK_3; + } + } + + shader_glsl_get_sample_function(ins->ctx, resource_idx, resource_idx, sample_flags, &sample_function); + mask |= sample_function.coord_mask; + sample_function.coord_mask = mask; + + if (shader_version < WINED3D_SHADER_VERSION(2,0)) swizzle = WINED3DSP_NOSWIZZLE; + else swizzle = ins->src[1].swizzle; + + /* 1.0-1.3: Use destination register as coordinate source. + 1.4+: Use provided coordinate source register. */ + if (shader_version < WINED3D_SHADER_VERSION(1,4)) + { + char coord_mask[6]; + shader_glsl_write_mask_to_str(mask, coord_mask); + shader_glsl_gen_sample_code(ins, resource_idx, &sample_function, swizzle, NULL, NULL, NULL, NULL, + "T%u%s", resource_idx, coord_mask); + } + else + { + struct glsl_src_param coord_param; + shader_glsl_add_src_param(ins, &ins->src[0], mask, &coord_param); + if (ins->flags & WINED3DSI_TEXLD_BIAS) + { + struct glsl_src_param bias; + shader_glsl_add_src_param(ins, &ins->src[0], WINED3DSP_WRITEMASK_3, &bias); + shader_glsl_gen_sample_code(ins, resource_idx, &sample_function, swizzle, NULL, NULL, bias.param_str, + NULL, "%s", coord_param.param_str); + } else { + shader_glsl_gen_sample_code(ins, resource_idx, &sample_function, swizzle, NULL, NULL, NULL, NULL, + "%s", coord_param.param_str); + } + } + shader_glsl_release_sample_function(ins->ctx, &sample_function); +} + +static void shader_glsl_texldd(const struct wined3d_shader_instruction *ins) +{ + const struct shader_glsl_ctx_priv *priv = ins->ctx->backend_data; + const struct wined3d_gl_info *gl_info = priv->gl_info; + struct glsl_src_param coord_param, dx_param, dy_param; + struct glsl_sample_function sample_function; + DWORD sampler_idx; + DWORD swizzle = ins->src[1].swizzle; + + if (!shader_glsl_has_core_grad(gl_info) && !gl_info->supported[ARB_SHADER_TEXTURE_LOD]) + { + FIXME("texldd used, but not supported by hardware. Falling back to regular tex.\n"); + shader_glsl_tex(ins); + return; + } + + sampler_idx = ins->src[1].reg.idx[0].offset; + + shader_glsl_get_sample_function(ins->ctx, sampler_idx, sampler_idx, WINED3D_GLSL_SAMPLE_GRAD, &sample_function); + shader_glsl_add_src_param(ins, &ins->src[0], sample_function.coord_mask, &coord_param); + shader_glsl_add_src_param(ins, &ins->src[2], sample_function.deriv_mask, &dx_param); + shader_glsl_add_src_param(ins, &ins->src[3], sample_function.deriv_mask, &dy_param); + + shader_glsl_gen_sample_code(ins, sampler_idx, &sample_function, swizzle, dx_param.param_str, dy_param.param_str, + NULL, NULL, "%s", coord_param.param_str); + shader_glsl_release_sample_function(ins->ctx, &sample_function); +} + +static void shader_glsl_texldl(const struct wined3d_shader_instruction *ins) +{ + const struct wined3d_shader_version *shader_version = &ins->ctx->reg_maps->shader_version; + const struct shader_glsl_ctx_priv *priv = ins->ctx->backend_data; + const struct wined3d_gl_info *gl_info = priv->gl_info; + struct glsl_src_param coord_param, lod_param; + struct glsl_sample_function sample_function; + DWORD swizzle = ins->src[1].swizzle; + DWORD sampler_idx; + + sampler_idx = ins->src[1].reg.idx[0].offset; + + shader_glsl_get_sample_function(ins->ctx, sampler_idx, sampler_idx, WINED3D_GLSL_SAMPLE_LOD, &sample_function); + shader_glsl_add_src_param(ins, &ins->src[0], sample_function.coord_mask, &coord_param); + + shader_glsl_add_src_param(ins, &ins->src[0], WINED3DSP_WRITEMASK_3, &lod_param); + + if (shader_version->type == WINED3D_SHADER_TYPE_PIXEL && !shader_glsl_has_core_grad(gl_info) + && !gl_info->supported[ARB_SHADER_TEXTURE_LOD]) + { + /* Plain GLSL only supports Lod sampling functions in vertex shaders. + * However, the NVIDIA drivers allow them in fragment shaders as well, + * even without the appropriate extension. */ + WARN("Using %s in fragment shader.\n", sample_function.name->buffer); + } + shader_glsl_gen_sample_code(ins, sampler_idx, &sample_function, swizzle, NULL, NULL, lod_param.param_str, NULL, + "%s", coord_param.param_str); + shader_glsl_release_sample_function(ins->ctx, &sample_function); +} + +static unsigned int shader_glsl_find_sampler(const struct wined3d_shader_sampler_map *sampler_map, + unsigned int resource_idx, unsigned int sampler_idx) +{ + struct wined3d_shader_sampler_map_entry *entries = sampler_map->entries; + unsigned int i; + + for (i = 0; i < sampler_map->count; ++i) + { + if (entries[i].resource_idx == resource_idx && entries[i].sampler_idx == sampler_idx) + return entries[i].bind_idx; + } + + ERR("No GLSL sampler found for resource %u / sampler %u.\n", resource_idx, sampler_idx); + + return ~0u; +} + +static void shader_glsl_atomic(const struct wined3d_shader_instruction *ins) +{ + const BOOL is_imm_instruction = WINED3DSIH_IMM_ATOMIC_AND <= ins->handler_idx + && ins->handler_idx <= WINED3DSIH_IMM_ATOMIC_XOR; + const struct wined3d_shader_reg_maps *reg_maps = ins->ctx->reg_maps; + const struct wined3d_shader_version *version = ®_maps->shader_version; + struct shader_glsl_ctx_priv *priv = ins->ctx->backend_data; + struct glsl_src_param structure_idx, offset, data, data2; + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + enum wined3d_shader_resource_type resource_type; + struct wined3d_string_buffer *address; + enum wined3d_data_type data_type; + unsigned int resource_idx, stride; + const char *op, *resource; + DWORD coord_mask; + BOOL is_tgsm; + + resource_idx = ins->dst[is_imm_instruction].reg.idx[0].offset; + is_tgsm = ins->dst[is_imm_instruction].reg.type == WINED3DSPR_GROUPSHAREDMEM; + if (is_tgsm) + { + if (resource_idx >= reg_maps->tgsm_count) + { + ERR("Invalid TGSM index %u.\n", resource_idx); + return; + } + resource = "g"; + data_type = WINED3D_DATA_UINT; + coord_mask = 1; + stride = reg_maps->tgsm[resource_idx].stride; + } + else + { + if (resource_idx >= ARRAY_SIZE(reg_maps->uav_resource_info)) + { + ERR("Invalid UAV index %u.\n", resource_idx); + return; + } + resource_type = reg_maps->uav_resource_info[resource_idx].type; + if (resource_type >= ARRAY_SIZE(resource_type_info)) + { + ERR("Unexpected resource type %#x.\n", resource_type); + return; + } + resource = "image"; + data_type = reg_maps->uav_resource_info[resource_idx].data_type; + coord_mask = (1u << resource_type_info[resource_type].coord_size) - 1; + stride = reg_maps->uav_resource_info[resource_idx].stride; + } + + switch (ins->handler_idx) + { + case WINED3DSIH_ATOMIC_AND: + case WINED3DSIH_IMM_ATOMIC_AND: + if (is_tgsm) + op = "atomicAnd"; + else + op = "imageAtomicAnd"; + break; + case WINED3DSIH_ATOMIC_CMP_STORE: + case WINED3DSIH_IMM_ATOMIC_CMP_EXCH: + if (is_tgsm) + op = "atomicCompSwap"; + else + op = "imageAtomicCompSwap"; + break; + case WINED3DSIH_ATOMIC_IADD: + case WINED3DSIH_IMM_ATOMIC_IADD: + if (is_tgsm) + op = "atomicAdd"; + else + op = "imageAtomicAdd"; + break; + case WINED3DSIH_ATOMIC_IMAX: + case WINED3DSIH_IMM_ATOMIC_IMAX: + if (is_tgsm) + op = "atomicMax"; + else + op = "imageAtomicMax"; + if (data_type != WINED3D_DATA_INT) + { + FIXME("Unhandled opcode %#x for unsigned integers.\n", ins->handler_idx); + return; + } + break; + case WINED3DSIH_ATOMIC_IMIN: + case WINED3DSIH_IMM_ATOMIC_IMIN: + if (is_tgsm) + op = "atomicMin"; + else + op = "imageAtomicMin"; + if (data_type != WINED3D_DATA_INT) + { + FIXME("Unhandled opcode %#x for unsigned integers.\n", ins->handler_idx); + return; + } + break; + case WINED3DSIH_ATOMIC_OR: + case WINED3DSIH_IMM_ATOMIC_OR: + if (is_tgsm) + op = "atomicOr"; + else + op = "imageAtomicOr"; + break; + case WINED3DSIH_ATOMIC_UMAX: + case WINED3DSIH_IMM_ATOMIC_UMAX: + if (is_tgsm) + op = "atomicMax"; + else + op = "imageAtomicMax"; + if (data_type != WINED3D_DATA_UINT) + { + FIXME("Unhandled opcode %#x for signed integers.\n", ins->handler_idx); + return; + } + break; + case WINED3DSIH_ATOMIC_UMIN: + case WINED3DSIH_IMM_ATOMIC_UMIN: + if (is_tgsm) + op = "atomicMin"; + else + op = "imageAtomicMin"; + if (data_type != WINED3D_DATA_UINT) + { + FIXME("Unhandled opcode %#x for signed integers.\n", ins->handler_idx); + return; + } + break; + case WINED3DSIH_ATOMIC_XOR: + case WINED3DSIH_IMM_ATOMIC_XOR: + if (is_tgsm) + op = "atomicXor"; + else + op = "imageAtomicXor"; + break; + case WINED3DSIH_IMM_ATOMIC_EXCH: + if (is_tgsm) + op = "atomicExchange"; + else + op = "imageAtomicExchange"; + break; + default: + ERR("Unhandled opcode %#x.\n", ins->handler_idx); + return; + } + + address = string_buffer_get(priv->string_buffers); + if (stride) + { + shader_glsl_add_src_param(ins, &ins->src[0], WINED3DSP_WRITEMASK_0, &structure_idx); + shader_glsl_add_src_param(ins, &ins->src[0], WINED3DSP_WRITEMASK_1, &offset); + string_buffer_sprintf(address, "%s * %u + %s / 4", structure_idx.param_str, stride, offset.param_str); + } + else + { + shader_glsl_add_src_param(ins, &ins->src[0], coord_mask, &offset); + string_buffer_sprintf(address, "%s", offset.param_str); + if (is_tgsm || (reg_maps->uav_resource_info[resource_idx].flags & WINED3D_VIEW_BUFFER_RAW)) + shader_addline(address, "/ 4"); + } + + if (is_imm_instruction) + shader_glsl_append_dst_ext(ins->ctx->buffer, ins, &ins->dst[0], data_type); + + if (is_tgsm) + shader_addline(buffer, "%s(%s_%s%u[%s], ", + op, shader_glsl_get_prefix(version->type), resource, resource_idx, address->buffer); + else + shader_addline(buffer, "%s(%s_%s%u, %s, ", + op, shader_glsl_get_prefix(version->type), resource, resource_idx, address->buffer); + + shader_glsl_add_src_param_ext(ins->ctx, &ins->src[1], WINED3DSP_WRITEMASK_0, &data, data_type); + shader_addline(buffer, "%s", data.param_str); + if (ins->src_count >= 3) + { + shader_glsl_add_src_param_ext(ins->ctx, &ins->src[2], WINED3DSP_WRITEMASK_0, &data2, data_type); + shader_addline(buffer, ", %s", data2.param_str); + } + + if (is_imm_instruction) + shader_addline(buffer, ")"); + shader_addline(buffer, ");\n"); + + string_buffer_release(priv->string_buffers, address); +} + +static void shader_glsl_uav_counter(const struct wined3d_shader_instruction *ins) +{ + const char *prefix = shader_glsl_get_prefix(ins->ctx->reg_maps->shader_version.type); + const char *op; + + if (ins->handler_idx == WINED3DSIH_IMM_ATOMIC_ALLOC) + op = "atomicCounterIncrement"; + else + op = "atomicCounterDecrement"; + + shader_glsl_append_dst(ins->ctx->buffer, ins); + shader_addline(ins->ctx->buffer, "%s(%s_counter%u));\n", op, prefix, ins->src[0].reg.idx[0].offset); +} + +static void shader_glsl_ld_uav(const struct wined3d_shader_instruction *ins) +{ + const struct wined3d_shader_reg_maps *reg_maps = ins->ctx->reg_maps; + const struct wined3d_shader_version *version = ®_maps->shader_version; + enum wined3d_shader_resource_type resource_type; + struct glsl_src_param image_coord_param; + enum wined3d_data_type data_type; + DWORD coord_mask, write_mask; + unsigned int uav_idx; + char dst_swizzle[6]; + + uav_idx = ins->src[1].reg.idx[0].offset; + if (uav_idx >= ARRAY_SIZE(reg_maps->uav_resource_info)) + { + ERR("Invalid UAV index %u.\n", uav_idx); + return; + } + resource_type = reg_maps->uav_resource_info[uav_idx].type; + if (resource_type >= ARRAY_SIZE(resource_type_info)) + { + ERR("Unexpected resource type %#x.\n", resource_type); + resource_type = WINED3D_SHADER_RESOURCE_TEXTURE_2D; + } + data_type = reg_maps->uav_resource_info[uav_idx].data_type; + coord_mask = (1u << resource_type_info[resource_type].coord_size) - 1; + + write_mask = shader_glsl_append_dst_ext(ins->ctx->buffer, ins, &ins->dst[0], data_type); + shader_glsl_get_swizzle(&ins->src[1], FALSE, write_mask, dst_swizzle); + + shader_glsl_add_src_param(ins, &ins->src[0], coord_mask, &image_coord_param); + shader_addline(ins->ctx->buffer, "imageLoad(%s_image%u, %s)%s);\n", + shader_glsl_get_prefix(version->type), uav_idx, image_coord_param.param_str, dst_swizzle); +} + +static void shader_glsl_ld_raw_structured(const struct wined3d_shader_instruction *ins) +{ + const char *prefix = shader_glsl_get_prefix(ins->ctx->reg_maps->shader_version.type); + const struct wined3d_shader_src_param *src = &ins->src[ins->src_count - 1]; + unsigned int i, swizzle, resource_idx, bind_idx, stride, src_idx = 0; + const struct wined3d_shader_reg_maps *reg_maps = ins->ctx->reg_maps; + struct shader_glsl_ctx_priv *priv = ins->ctx->backend_data; + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + struct glsl_src_param structure_idx, offset; + struct wined3d_string_buffer *address; + struct wined3d_shader_dst_param dst; + const char *function, *resource; + + resource_idx = src->reg.idx[0].offset; + if (src->reg.type == WINED3DSPR_RESOURCE) + { + if (resource_idx >= ARRAY_SIZE(reg_maps->resource_info)) + { + ERR("Invalid resource index %u.\n", resource_idx); + return; + } + stride = reg_maps->resource_info[resource_idx].stride; + bind_idx = shader_glsl_find_sampler(®_maps->sampler_map, resource_idx, WINED3D_SAMPLER_DEFAULT); + function = "texelFetch"; + resource = "sampler"; + } + else if (src->reg.type == WINED3DSPR_UAV) + { + if (resource_idx >= ARRAY_SIZE(reg_maps->uav_resource_info)) + { + ERR("Invalid UAV index %u.\n", resource_idx); + return; + } + stride = reg_maps->uav_resource_info[resource_idx].stride; + bind_idx = resource_idx; + function = "imageLoad"; + resource = "image"; + } + else + { + if (resource_idx >= reg_maps->tgsm_count) + { + ERR("Invalid TGSM index %u.\n", resource_idx); + return; + } + stride = reg_maps->tgsm[resource_idx].stride; + bind_idx = resource_idx; + function = NULL; + resource = "g"; + } + + address = string_buffer_get(priv->string_buffers); + if (ins->handler_idx == WINED3DSIH_LD_STRUCTURED) + { + shader_glsl_add_src_param(ins, &ins->src[src_idx++], WINED3DSP_WRITEMASK_0, &structure_idx); + shader_addline(address, "%s * %u + ", structure_idx.param_str, stride); + } + shader_glsl_add_src_param(ins, &ins->src[src_idx++], WINED3DSP_WRITEMASK_0, &offset); + shader_addline(address, "%s / 4", offset.param_str); + + dst = ins->dst[0]; + if (shader_glsl_get_write_mask_size(dst.write_mask) > 1) + { + /* The instruction is split into multiple lines. The first lines may + * overwrite source parameters of the following lines. */ + shader_addline(buffer, "tmp0.x = intBitsToFloat(%s);\n", address->buffer); + string_buffer_sprintf(address, "floatBitsToInt(tmp0.x)"); + } + + for (i = 0; i < 4; ++i) + { + dst.write_mask = ins->dst[0].write_mask & (WINED3DSP_WRITEMASK_0 << i); + if (!shader_glsl_append_dst_ext(ins->ctx->buffer, ins, &dst, dst.reg.data_type)) + continue; + + swizzle = shader_glsl_swizzle_get_component(src->swizzle, i); + if (function) + shader_addline(buffer, "%s(%s_%s%u, %s + %u).x);\n", + function, prefix, resource, bind_idx, address->buffer, swizzle); + else + shader_addline(buffer, "%s_%s%u[%s + %u]);\n", + prefix, resource, bind_idx, address->buffer, swizzle); + } + + string_buffer_release(priv->string_buffers, address); +} + +static void shader_glsl_store_uav(const struct wined3d_shader_instruction *ins) +{ + const struct wined3d_shader_reg_maps *reg_maps = ins->ctx->reg_maps; + const struct wined3d_shader_version *version = ®_maps->shader_version; + struct glsl_src_param image_coord_param, image_data_param; + enum wined3d_shader_resource_type resource_type; + enum wined3d_data_type data_type; + unsigned int uav_idx; + DWORD coord_mask; + + uav_idx = ins->dst[0].reg.idx[0].offset; + if (uav_idx >= ARRAY_SIZE(reg_maps->uav_resource_info)) + { + ERR("Invalid UAV index %u.\n", uav_idx); + return; + } + resource_type = reg_maps->uav_resource_info[uav_idx].type; + if (resource_type >= ARRAY_SIZE(resource_type_info)) + { + ERR("Unexpected resource type %#x.\n", resource_type); + return; + } + data_type = reg_maps->uav_resource_info[uav_idx].data_type; + coord_mask = (1u << resource_type_info[resource_type].coord_size) - 1; + + shader_glsl_add_src_param(ins, &ins->src[0], coord_mask, &image_coord_param); + shader_glsl_add_src_param_ext(ins->ctx, &ins->src[1], WINED3DSP_WRITEMASK_ALL, &image_data_param, data_type); + shader_addline(ins->ctx->buffer, "imageStore(%s_image%u, %s, %s);\n", + shader_glsl_get_prefix(version->type), uav_idx, + image_coord_param.param_str, image_data_param.param_str); +} + +static void shader_glsl_store_raw_structured(const struct wined3d_shader_instruction *ins) +{ + const char *prefix = shader_glsl_get_prefix(ins->ctx->reg_maps->shader_version.type); + const struct wined3d_shader_reg_maps *reg_maps = ins->ctx->reg_maps; + struct shader_glsl_ctx_priv *priv = ins->ctx->backend_data; + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + struct glsl_src_param structure_idx, offset, data; + unsigned int i, resource_idx, stride, src_idx = 0; + struct wined3d_string_buffer *address; + DWORD write_mask; + BOOL is_tgsm; + + resource_idx = ins->dst[0].reg.idx[0].offset; + is_tgsm = ins->dst[0].reg.type == WINED3DSPR_GROUPSHAREDMEM; + if (is_tgsm) + { + if (resource_idx >= reg_maps->tgsm_count) + { + ERR("Invalid TGSM index %u.\n", resource_idx); + return; + } + stride = reg_maps->tgsm[resource_idx].stride; + } + else + { + if (resource_idx >= ARRAY_SIZE(reg_maps->uav_resource_info)) + { + ERR("Invalid UAV index %u.\n", resource_idx); + return; + } + stride = reg_maps->uav_resource_info[resource_idx].stride; + } + + address = string_buffer_get(priv->string_buffers); + if (ins->handler_idx == WINED3DSIH_STORE_STRUCTURED) + { + shader_glsl_add_src_param(ins, &ins->src[src_idx++], WINED3DSP_WRITEMASK_0, &structure_idx); + shader_addline(address, "%s * %u + ", structure_idx.param_str, stride); + } + shader_glsl_add_src_param(ins, &ins->src[src_idx++], WINED3DSP_WRITEMASK_0, &offset); + shader_addline(address, "%s / 4", offset.param_str); + + for (i = 0; i < 4; ++i) + { + if (!(write_mask = ins->dst[0].write_mask & (WINED3DSP_WRITEMASK_0 << i))) + continue; + + shader_glsl_add_src_param(ins, &ins->src[src_idx], write_mask, &data); + + if (is_tgsm) + shader_addline(buffer, "%s_g%u[%s + %u] = %s;\n", + prefix, resource_idx, address->buffer, i, data.param_str); + else + shader_addline(buffer, "imageStore(%s_image%u, %s + %u, uvec4(%s, 0, 0, 0));\n", + prefix, resource_idx, address->buffer, i, data.param_str); + } + + string_buffer_release(priv->string_buffers, address); +} + +static void shader_glsl_sync(const struct wined3d_shader_instruction *ins) +{ + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + unsigned int sync_flags = ins->flags; + + if (sync_flags & WINED3DSSF_THREAD_GROUP) + { + shader_addline(buffer, "barrier();\n"); + sync_flags &= ~(WINED3DSSF_THREAD_GROUP | WINED3DSSF_GROUP_SHARED_MEMORY); + } + + if (sync_flags & WINED3DSSF_GROUP_SHARED_MEMORY) + { + shader_addline(buffer, "memoryBarrierShared();\n"); + sync_flags &= ~WINED3DSSF_GROUP_SHARED_MEMORY; + } + + if (sync_flags & WINED3DSSF_GLOBAL_UAV) + { + shader_addline(buffer, "memoryBarrier();\n"); + sync_flags &= ~WINED3DSSF_GLOBAL_UAV; + } + + if (sync_flags) + FIXME("Unhandled sync flags %#x.\n", sync_flags); +} + +static const struct wined3d_shader_resource_info *shader_glsl_get_resource_info( + const struct wined3d_shader_instruction *ins, const struct wined3d_shader_register *reg) +{ + const struct wined3d_shader_reg_maps *reg_maps = ins->ctx->reg_maps; + unsigned int idx = reg->idx[0].offset; + + if (reg->type == WINED3DSPR_RESOURCE) + { + if (idx >= ARRAY_SIZE(reg_maps->resource_info)) + { + ERR("Invalid resource index %u.\n", idx); + return NULL; + } + return ®_maps->resource_info[idx]; + } + + if (reg->type == WINED3DSPR_UAV) + { + if (idx >= ARRAY_SIZE(reg_maps->uav_resource_info)) + { + ERR("Invalid UAV index %u.\n", idx); + return NULL; + } + return ®_maps->uav_resource_info[idx]; + } + + FIXME("Unhandled register type %#x.\n", reg->type); + return NULL; +} + +static void shader_glsl_bufinfo(const struct wined3d_shader_instruction *ins) +{ + const char *prefix = shader_glsl_get_prefix(ins->ctx->reg_maps->shader_version.type); + const struct wined3d_shader_resource_info *resource_info; + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + unsigned int resource_idx; + char dst_swizzle[6]; + DWORD write_mask; + + write_mask = shader_glsl_append_dst(buffer, ins); + shader_glsl_get_swizzle(&ins->src[0], FALSE, write_mask, dst_swizzle); + + if (!(resource_info = shader_glsl_get_resource_info(ins, &ins->src[0].reg))) + return; + resource_idx = ins->src[0].reg.idx[0].offset; + + shader_addline(buffer, "ivec2("); + if (ins->src[0].reg.type == WINED3DSPR_RESOURCE) + { + unsigned int bind_idx = shader_glsl_find_sampler(&ins->ctx->reg_maps->sampler_map, + resource_idx, WINED3D_SAMPLER_DEFAULT); + shader_addline(buffer, "textureSize(%s_sampler%u)", prefix, bind_idx); + } + else + { + shader_addline(buffer, "imageSize(%s_image%u)", prefix, resource_idx); + } + if (resource_info->stride) + shader_addline(buffer, " / %u", resource_info->stride); + else if (resource_info->flags & WINED3D_VIEW_BUFFER_RAW) + shader_addline(buffer, " * 4"); + shader_addline(buffer, ", %u)%s);\n", resource_info->stride, dst_swizzle); +} + +static BOOL is_multisampled(enum wined3d_shader_resource_type resource_type) +{ + return resource_type == WINED3D_SHADER_RESOURCE_TEXTURE_2DMS + || resource_type == WINED3D_SHADER_RESOURCE_TEXTURE_2DMSARRAY; +} + +static BOOL is_mipmapped(enum wined3d_shader_resource_type resource_type) +{ + return resource_type != WINED3D_SHADER_RESOURCE_BUFFER && !is_multisampled(resource_type); +} + +static void shader_glsl_resinfo(const struct wined3d_shader_instruction *ins) +{ + const struct wined3d_shader_version *version = &ins->ctx->reg_maps->shader_version; + const struct shader_glsl_ctx_priv *priv = ins->ctx->backend_data; + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + const struct wined3d_gl_info *gl_info = priv->gl_info; + enum wined3d_shader_resource_type resource_type; + enum wined3d_shader_register_type reg_type; + unsigned int resource_idx, bind_idx, i; + enum wined3d_data_type dst_data_type; + struct glsl_src_param lod_param; + BOOL supports_mipmaps; + char dst_swizzle[6]; + DWORD write_mask; + + dst_data_type = ins->dst[0].reg.data_type; + if (ins->flags == WINED3DSI_RESINFO_UINT) + dst_data_type = WINED3D_DATA_UINT; + else if (ins->flags) + FIXME("Unhandled flags %#x.\n", ins->flags); + + reg_type = ins->src[1].reg.type; + resource_idx = ins->src[1].reg.idx[0].offset; + shader_glsl_add_src_param(ins, &ins->src[0], WINED3DSP_WRITEMASK_0, &lod_param); + if (reg_type == WINED3DSPR_RESOURCE) + { + resource_type = ins->ctx->reg_maps->resource_info[resource_idx].type; + bind_idx = shader_glsl_find_sampler(&ins->ctx->reg_maps->sampler_map, + resource_idx, WINED3D_SAMPLER_DEFAULT); + } + else + { + resource_type = ins->ctx->reg_maps->uav_resource_info[resource_idx].type; + bind_idx = resource_idx; + } + + if (resource_type >= ARRAY_SIZE(resource_type_info)) + { + ERR("Unexpected resource type %#x.\n", resource_type); + return; + } + + write_mask = shader_glsl_append_dst_ext(buffer, ins, &ins->dst[0], dst_data_type); + shader_glsl_get_swizzle(&ins->src[1], FALSE, write_mask, dst_swizzle); + + if (dst_data_type == WINED3D_DATA_UINT) + shader_addline(buffer, "uvec4("); + else + shader_addline(buffer, "vec4("); + + if (reg_type == WINED3DSPR_RESOURCE) + { + shader_addline(buffer, "textureSize(%s_sampler%u", + shader_glsl_get_prefix(version->type), bind_idx); + } + else + { + shader_addline(buffer, "imageSize(%s_image%u", + shader_glsl_get_prefix(version->type), bind_idx); + } + + supports_mipmaps = is_mipmapped(resource_type) && reg_type != WINED3DSPR_UAV; + if (supports_mipmaps) + shader_addline(buffer, ", %s", lod_param.param_str); + shader_addline(buffer, "), "); + + for (i = 0; i < 3 - resource_type_info[resource_type].resinfo_size; ++i) + shader_addline(buffer, "0, "); + + if (supports_mipmaps) + { + if (gl_info->supported[ARB_TEXTURE_QUERY_LEVELS]) + { + shader_addline(buffer, "textureQueryLevels(%s_sampler%u)", + shader_glsl_get_prefix(version->type), bind_idx); + } + else + { + FIXME("textureQueryLevels is not supported, returning 1 level.\n"); + shader_addline(buffer, "1"); + } + } + else + { + shader_addline(buffer, "1"); + } + + shader_addline(buffer, ")%s);\n", dst_swizzle); +} + +static void shader_glsl_sample_info(const struct wined3d_shader_instruction *ins) +{ + const struct wined3d_shader_reg_maps *reg_maps = ins->ctx->reg_maps; + const struct shader_glsl_ctx_priv *priv = ins->ctx->backend_data; + const struct wined3d_gl_info *gl_info = priv->gl_info; + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + const struct wined3d_shader_dst_param *dst = ins->dst; + const struct wined3d_shader_src_param *src = ins->src; + enum wined3d_shader_resource_type resource_type; + enum wined3d_data_type dst_data_type; + unsigned int resource_idx, bind_idx; + char dst_swizzle[6]; + DWORD write_mask; + + dst_data_type = dst->reg.data_type; + if (ins->flags == WINED3DSI_SAMPLE_INFO_UINT) + dst_data_type = WINED3D_DATA_UINT; + else if (ins->flags) + FIXME("Unhandled flags %#x.\n", ins->flags); + + write_mask = shader_glsl_append_dst_ext(buffer, ins, dst, dst_data_type); + shader_glsl_get_swizzle(src, FALSE, write_mask, dst_swizzle); + + if (dst_data_type == WINED3D_DATA_UINT) + shader_addline(buffer, "uvec4("); + else + shader_addline(buffer, "vec4("); + + if (src->reg.type == WINED3DSPR_RASTERIZER) + { + if (gl_info->supported[ARB_SAMPLE_SHADING]) + { + shader_addline(buffer, "gl_NumSamples"); + } + else + { + FIXME("OpenGL implementation does not support ARB_sample_shading.\n"); + shader_addline(buffer, "1"); + } + } + else + { + resource_idx = src->reg.idx[0].offset; + resource_type = reg_maps->resource_info[resource_idx].type; + if (resource_type >= ARRAY_SIZE(resource_type_info)) + { + ERR("Unexpected resource type %#x.\n", resource_type); + return; + } + bind_idx = shader_glsl_find_sampler(®_maps->sampler_map, resource_idx, WINED3D_SAMPLER_DEFAULT); + + if (gl_info->supported[ARB_SHADER_TEXTURE_IMAGE_SAMPLES]) + { + shader_addline(buffer, "textureSamples(%s_sampler%u)", + shader_glsl_get_prefix(reg_maps->shader_version.type), bind_idx); + } + else + { + FIXME("textureSamples() is not supported.\n"); + shader_addline(buffer, "1"); + } + } + + shader_addline(buffer, ", 0, 0, 0)%s);\n", dst_swizzle); +} + +static void shader_glsl_interpolate(const struct wined3d_shader_instruction *ins) +{ + const struct wined3d_shader_src_param *input = &ins->src[0]; + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + struct glsl_src_param sample_param; + char dst_swizzle[6]; + DWORD write_mask; + + write_mask = shader_glsl_append_dst(buffer, ins); + shader_glsl_get_swizzle(input, FALSE, write_mask, dst_swizzle); + + shader_glsl_add_src_param(ins, &ins->src[1], WINED3DSP_WRITEMASK_0, &sample_param); + shader_addline(buffer, "interpolateAtSample(shader_in.reg%u, %s)%s);\n", + input->reg.idx[0].offset, sample_param.param_str, dst_swizzle); +} + +static void shader_glsl_ld(const struct wined3d_shader_instruction *ins) +{ + const struct wined3d_shader_reg_maps *reg_maps = ins->ctx->reg_maps; + struct glsl_src_param coord_param, lod_param, sample_param; + unsigned int resource_idx, sampler_idx, sampler_bind_idx; + struct glsl_sample_function sample_function; + DWORD flags = WINED3D_GLSL_SAMPLE_LOAD; + BOOL has_lod_param; + + if (wined3d_shader_instruction_has_texel_offset(ins)) + flags |= WINED3D_GLSL_SAMPLE_OFFSET; + + resource_idx = ins->src[1].reg.idx[0].offset; + sampler_idx = WINED3D_SAMPLER_DEFAULT; + + if (resource_idx >= ARRAY_SIZE(reg_maps->resource_info)) + { + ERR("Invalid resource index %u.\n", resource_idx); + return; + } + has_lod_param = is_mipmapped(reg_maps->resource_info[resource_idx].type); + + shader_glsl_get_sample_function(ins->ctx, resource_idx, sampler_idx, flags, &sample_function); + shader_glsl_add_src_param(ins, &ins->src[0], sample_function.coord_mask, &coord_param); + shader_glsl_add_src_param(ins, &ins->src[0], WINED3DSP_WRITEMASK_3, &lod_param); + sampler_bind_idx = shader_glsl_find_sampler(®_maps->sampler_map, resource_idx, sampler_idx); + if (is_multisampled(reg_maps->resource_info[resource_idx].type)) + { + shader_glsl_add_src_param(ins, &ins->src[2], WINED3DSP_WRITEMASK_0, &sample_param); + shader_glsl_gen_sample_code(ins, sampler_bind_idx, &sample_function, ins->src[1].swizzle, + NULL, NULL, NULL, &ins->texel_offset, "%s, %s", coord_param.param_str, sample_param.param_str); + } + else + { + shader_glsl_gen_sample_code(ins, sampler_bind_idx, &sample_function, ins->src[1].swizzle, + NULL, NULL, has_lod_param ? lod_param.param_str : NULL, &ins->texel_offset, + "%s", coord_param.param_str); + } + shader_glsl_release_sample_function(ins->ctx, &sample_function); +} + +static void shader_glsl_sample(const struct wined3d_shader_instruction *ins) +{ + const char *lod_param_str = NULL, *dx_param_str = NULL, *dy_param_str = NULL; + struct glsl_src_param coord_param, lod_param, dx_param, dy_param; + unsigned int resource_idx, sampler_idx, sampler_bind_idx; + struct glsl_sample_function sample_function; + DWORD flags = 0; + + if (ins->handler_idx == WINED3DSIH_SAMPLE_GRAD) + flags |= WINED3D_GLSL_SAMPLE_GRAD; + if (ins->handler_idx == WINED3DSIH_SAMPLE_LOD) + flags |= WINED3D_GLSL_SAMPLE_LOD; + if (wined3d_shader_instruction_has_texel_offset(ins)) + flags |= WINED3D_GLSL_SAMPLE_OFFSET; + + resource_idx = ins->src[1].reg.idx[0].offset; + sampler_idx = ins->src[2].reg.idx[0].offset; + + shader_glsl_get_sample_function(ins->ctx, resource_idx, sampler_idx, flags, &sample_function); + shader_glsl_add_src_param(ins, &ins->src[0], sample_function.coord_mask, &coord_param); + + switch (ins->handler_idx) + { + case WINED3DSIH_SAMPLE: + break; + case WINED3DSIH_SAMPLE_B: + shader_glsl_add_src_param(ins, &ins->src[3], WINED3DSP_WRITEMASK_0, &lod_param); + lod_param_str = lod_param.param_str; + break; + case WINED3DSIH_SAMPLE_GRAD: + shader_glsl_add_src_param(ins, &ins->src[3], sample_function.deriv_mask, &dx_param); + shader_glsl_add_src_param(ins, &ins->src[4], sample_function.deriv_mask, &dy_param); + dx_param_str = dx_param.param_str; + dy_param_str = dy_param.param_str; + break; + case WINED3DSIH_SAMPLE_LOD: + shader_glsl_add_src_param(ins, &ins->src[3], WINED3DSP_WRITEMASK_0, &lod_param); + lod_param_str = lod_param.param_str; + break; + default: + ERR("Unhandled opcode %s.\n", debug_d3dshaderinstructionhandler(ins->handler_idx)); + break; + } + + sampler_bind_idx = shader_glsl_find_sampler(&ins->ctx->reg_maps->sampler_map, resource_idx, sampler_idx); + shader_glsl_gen_sample_code(ins, sampler_bind_idx, &sample_function, ins->src[1].swizzle, + dx_param_str, dy_param_str, lod_param_str, &ins->texel_offset, "%s", coord_param.param_str); + shader_glsl_release_sample_function(ins->ctx, &sample_function); +} + +/* Unless EXT_texture_shadow_lod is available, GLSL doesn't provide a function + * to sample from level zero with depth comparison for array textures and cube + * textures. We use textureGrad*() to implement sample_c_lz in that case. */ +static void shader_glsl_gen_sample_c_lz_emulation(const struct wined3d_shader_instruction *ins, + unsigned int sampler_bind_idx, const struct glsl_sample_function *sample_function, + unsigned int coord_size, const char *coord_param, const char *ref_param) +{ + const struct wined3d_shader_version *version = &ins->ctx->reg_maps->shader_version; + unsigned int deriv_size = wined3d_popcount(sample_function->deriv_mask); + const struct wined3d_shader_texel_offset *offset = &ins->texel_offset; + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + char dst_swizzle[6]; + + WARN("Emitting textureGrad() for sample_c_lz.\n"); + + shader_glsl_swizzle_to_str(WINED3DSP_NOSWIZZLE, FALSE, ins->dst[0].write_mask, dst_swizzle); + shader_glsl_append_dst_ext(buffer, ins, &ins->dst[0], sample_function->data_type); + shader_addline(buffer, "vec4(textureGrad%s(%s_sampler%u, vec%u(%s, %s), vec%u(0.0), vec%u(0.0)", + sample_function->offset_size ? "Offset" : "", + shader_glsl_get_prefix(version->type), sampler_bind_idx, + coord_size, coord_param, ref_param, deriv_size, deriv_size); + if (sample_function->offset_size) + { + int offset_immdata[4] = {offset->u, offset->v, offset->w}; + shader_addline(buffer, ", "); + shader_glsl_append_imm_ivec(buffer, offset_immdata, sample_function->offset_size); + } + shader_addline(buffer, "))%s);\n", dst_swizzle); +} + +static void shader_glsl_sample_c(const struct wined3d_shader_instruction *ins) +{ + const struct shader_glsl_ctx_priv *priv = ins->ctx->backend_data; + unsigned int resource_idx, sampler_idx, sampler_bind_idx; + const struct wined3d_shader_resource_info *resource_info; + const struct wined3d_gl_info *gl_info = priv->gl_info; + struct glsl_src_param coord_param, compare_param; + struct glsl_sample_function sample_function; + const char *lod_param = NULL; + unsigned int coord_size; + DWORD flags = 0; + + if (ins->handler_idx == WINED3DSIH_SAMPLE_C_LZ) + { + lod_param = "0"; + flags |= WINED3D_GLSL_SAMPLE_LOD; + } + + if (wined3d_shader_instruction_has_texel_offset(ins)) + flags |= WINED3D_GLSL_SAMPLE_OFFSET; + + if (!(resource_info = shader_glsl_get_resource_info(ins, &ins->src[1].reg))) + return; + resource_idx = ins->src[1].reg.idx[0].offset; + sampler_idx = ins->src[2].reg.idx[0].offset; + + shader_glsl_get_sample_function(ins->ctx, resource_idx, sampler_idx, flags, &sample_function); + coord_size = shader_glsl_get_write_mask_size(sample_function.coord_mask); + shader_glsl_add_src_param(ins, &ins->src[0], sample_function.coord_mask >> 1, &coord_param); + shader_glsl_add_src_param(ins, &ins->src[3], WINED3DSP_WRITEMASK_0, &compare_param); + sampler_bind_idx = shader_glsl_find_sampler(&ins->ctx->reg_maps->sampler_map, resource_idx, sampler_idx); + if (ins->handler_idx == WINED3DSIH_SAMPLE_C_LZ + && !gl_info->supported[EXT_TEXTURE_SHADOW_LOD] + && (resource_info->type == WINED3D_SHADER_RESOURCE_TEXTURE_2DARRAY + || resource_info->type == WINED3D_SHADER_RESOURCE_TEXTURE_CUBE)) + { + shader_glsl_gen_sample_c_lz_emulation(ins, sampler_bind_idx, &sample_function, + coord_size, coord_param.param_str, compare_param.param_str); + } + else + { + shader_glsl_gen_sample_code(ins, sampler_bind_idx, &sample_function, WINED3DSP_NOSWIZZLE, + NULL, NULL, lod_param, &ins->texel_offset, "vec%u(%s, %s)", + coord_size, coord_param.param_str, compare_param.param_str); + } + shader_glsl_release_sample_function(ins->ctx, &sample_function); +} + +static void shader_glsl_gather4(const struct wined3d_shader_instruction *ins) +{ + unsigned int resource_param_idx, resource_idx, sampler_idx, sampler_bind_idx, component_idx; + const struct wined3d_shader_reg_maps *reg_maps = ins->ctx->reg_maps; + const char *prefix = shader_glsl_get_prefix(reg_maps->shader_version.type); + const struct shader_glsl_ctx_priv *priv = ins->ctx->backend_data; + struct glsl_src_param coord_param, compare_param, offset_param; + const struct wined3d_gl_info *gl_info = priv->gl_info; + const struct wined3d_shader_resource_info *resource_info; + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + unsigned int coord_size, offset_size; + char dst_swizzle[6]; + BOOL has_offset; + + if (!gl_info->supported[ARB_TEXTURE_GATHER]) + { + FIXME("OpenGL implementation does not support textureGather.\n"); + return; + } + + has_offset = ins->handler_idx == WINED3DSIH_GATHER4_PO + || ins->handler_idx == WINED3DSIH_GATHER4_PO_C + || wined3d_shader_instruction_has_texel_offset(ins); + + resource_param_idx = + (ins->handler_idx == WINED3DSIH_GATHER4_PO || ins->handler_idx == WINED3DSIH_GATHER4_PO_C) ? 2 : 1; + resource_idx = ins->src[resource_param_idx].reg.idx[0].offset; + sampler_idx = ins->src[resource_param_idx + 1].reg.idx[0].offset; + component_idx = shader_glsl_swizzle_get_component(ins->src[resource_param_idx + 1].swizzle, 0); + sampler_bind_idx = shader_glsl_find_sampler(®_maps->sampler_map, resource_idx, sampler_idx); + + if (!(resource_info = shader_glsl_get_resource_info(ins, &ins->src[resource_param_idx].reg))) + return; + + if (resource_info->type >= ARRAY_SIZE(resource_type_info)) + { + ERR("Unexpected resource type %#x.\n", resource_info->type); + return; + } + shader_glsl_get_coord_size(resource_info->type, &coord_size, &offset_size); + + shader_glsl_swizzle_to_str(ins->src[resource_param_idx].swizzle, FALSE, ins->dst[0].write_mask, dst_swizzle); + shader_glsl_append_dst_ext(buffer, ins, &ins->dst[0], resource_info->data_type); + + shader_glsl_add_src_param(ins, &ins->src[0], (1u << coord_size) - 1, &coord_param); + + shader_addline(buffer, "textureGather%s(%s_sampler%u, %s", + has_offset ? "Offset" : "", prefix, sampler_bind_idx, coord_param.param_str); + if (ins->handler_idx == WINED3DSIH_GATHER4_C || ins->handler_idx == WINED3DSIH_GATHER4_PO_C) + { + shader_glsl_add_src_param(ins, &ins->src[resource_param_idx + 2], WINED3DSP_WRITEMASK_0, &compare_param); + shader_addline(buffer, ", %s", compare_param.param_str); + } + if (ins->handler_idx == WINED3DSIH_GATHER4_PO || ins->handler_idx == WINED3DSIH_GATHER4_PO_C) + { + shader_glsl_add_src_param(ins, &ins->src[1], (1u << offset_size) - 1, &offset_param); + shader_addline(buffer, ", %s", offset_param.param_str); + } + else if (has_offset) + { + int offset_immdata[4] = {ins->texel_offset.u, ins->texel_offset.v, ins->texel_offset.w}; + shader_addline(buffer, ", "); + shader_glsl_append_imm_ivec(buffer, offset_immdata, offset_size); + } + if (component_idx) + shader_addline(buffer, ", %u", component_idx); + + shader_addline(buffer, ")%s);\n", dst_swizzle); +} + +static void shader_glsl_texcoord(const struct wined3d_shader_instruction *ins) +{ + /* FIXME: Make this work for more than just 2D textures */ + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + DWORD write_mask = shader_glsl_append_dst(ins->ctx->buffer, ins); + + if (!(ins->ctx->reg_maps->shader_version.major == 1 && ins->ctx->reg_maps->shader_version.minor == 4)) + { + char dst_mask[6]; + + shader_glsl_get_write_mask(&ins->dst[0], dst_mask); + shader_addline(buffer, "clamp(ffp_texcoord[%u], 0.0, 1.0)%s);\n", + ins->dst[0].reg.idx[0].offset, dst_mask); + } + else + { + enum wined3d_shader_src_modifier src_mod = ins->src[0].modifiers; + DWORD reg = ins->src[0].reg.idx[0].offset; + char dst_swizzle[6]; + + shader_glsl_get_swizzle(&ins->src[0], FALSE, write_mask, dst_swizzle); + + if (src_mod == WINED3DSPSM_DZ || src_mod == WINED3DSPSM_DW) + { + unsigned int mask_size = shader_glsl_get_write_mask_size(write_mask); + struct glsl_src_param div_param; + DWORD src_writemask = src_mod == WINED3DSPSM_DZ ? WINED3DSP_WRITEMASK_2 : WINED3DSP_WRITEMASK_3; + + shader_glsl_add_src_param(ins, &ins->src[0], src_writemask, &div_param); + + if (mask_size > 1) + shader_addline(buffer, "ffp_texcoord[%u]%s / vec%d(%s));\n", reg, dst_swizzle, mask_size, div_param.param_str); + else + shader_addline(buffer, "ffp_texcoord[%u]%s / %s);\n", reg, dst_swizzle, div_param.param_str); + } + else + { + shader_addline(buffer, "ffp_texcoord[%u]%s);\n", reg, dst_swizzle); + } + } +} + +/** Process the WINED3DSIO_TEXDP3TEX instruction in GLSL: + * Take a 3-component dot product of the TexCoord[dstreg] and src, + * then perform a 1D texture lookup from stage dstregnum, place into dst. */ +static void shader_glsl_texdp3tex(const struct wined3d_shader_instruction *ins) +{ + DWORD src_mask = WINED3DSP_WRITEMASK_0 | WINED3DSP_WRITEMASK_1 | WINED3DSP_WRITEMASK_2; + DWORD sampler_idx = ins->dst[0].reg.idx[0].offset; + struct glsl_sample_function sample_function; + struct glsl_src_param src0_param; + UINT mask_size; + + shader_glsl_add_src_param(ins, &ins->src[0], src_mask, &src0_param); + + /* Do I have to take care about the projected bit? I don't think so, since the dp3 returns only one + * scalar, and projected sampling would require 4. + * + * It is a dependent read - not valid with conditional NP2 textures + */ + shader_glsl_get_sample_function(ins->ctx, sampler_idx, sampler_idx, 0, &sample_function); + mask_size = shader_glsl_get_write_mask_size(sample_function.coord_mask); + + switch(mask_size) + { + case 1: + shader_glsl_gen_sample_code(ins, sampler_idx, &sample_function, WINED3DSP_NOSWIZZLE, NULL, NULL, NULL, + NULL, "dot(ffp_texcoord[%u].xyz, %s)", sampler_idx, src0_param.param_str); + break; + + case 2: + shader_glsl_gen_sample_code(ins, sampler_idx, &sample_function, WINED3DSP_NOSWIZZLE, NULL, NULL, NULL, + NULL, "vec2(dot(ffp_texcoord[%u].xyz, %s), 0.0)", sampler_idx, src0_param.param_str); + break; + + case 3: + shader_glsl_gen_sample_code(ins, sampler_idx, &sample_function, WINED3DSP_NOSWIZZLE, NULL, NULL, NULL, + NULL, "vec3(dot(ffp_texcoord[%u].xyz, %s), 0.0, 0.0)", sampler_idx, src0_param.param_str); + break; + + default: + FIXME("Unexpected mask size %u\n", mask_size); + break; + } + shader_glsl_release_sample_function(ins->ctx, &sample_function); +} + +/** Process the WINED3DSIO_TEXDP3 instruction in GLSL: + * Take a 3-component dot product of the TexCoord[dstreg] and src. */ +static void shader_glsl_texdp3(const struct wined3d_shader_instruction *ins) +{ + DWORD src_mask = WINED3DSP_WRITEMASK_0 | WINED3DSP_WRITEMASK_1 | WINED3DSP_WRITEMASK_2; + DWORD dstreg = ins->dst[0].reg.idx[0].offset; + struct glsl_src_param src0_param; + DWORD dst_mask; + unsigned int mask_size; + + dst_mask = shader_glsl_append_dst(ins->ctx->buffer, ins); + mask_size = shader_glsl_get_write_mask_size(dst_mask); + shader_glsl_add_src_param(ins, &ins->src[0], src_mask, &src0_param); + + if (mask_size > 1) { + shader_addline(ins->ctx->buffer, "vec%d(dot(T%u.xyz, %s)));\n", mask_size, dstreg, src0_param.param_str); + } else { + shader_addline(ins->ctx->buffer, "dot(T%u.xyz, %s));\n", dstreg, src0_param.param_str); + } +} + +/** Process the WINED3DSIO_TEXDEPTH instruction in GLSL: + * Calculate the depth as dst.x / dst.y */ +static void shader_glsl_texdepth(const struct wined3d_shader_instruction *ins) +{ + struct glsl_dst_param dst_param; + + shader_glsl_add_dst_param(ins, &ins->dst[0], &dst_param); + + /* Tests show that texdepth never returns anything below 0.0, and that r5.y is clamped to 1.0. + * Negative input is accepted, -0.25 / -0.5 returns 0.5. GL should clamp gl_FragDepth to [0;1], but + * this doesn't always work, so clamp the results manually. Whether or not the x value is clamped at 1 + * too is irrelevant, since if x = 0, any y value < 1.0 (and > 1.0 is not allowed) results in a result + * >= 1.0 or < 0.0 + */ + shader_addline(ins->ctx->buffer, "gl_FragDepth = clamp((%s.x / min(%s.y, 1.0)), 0.0, 1.0);\n", + dst_param.reg_name, dst_param.reg_name); +} + +/** Process the WINED3DSIO_TEXM3X2DEPTH instruction in GLSL: + * Last row of a 3x2 matrix multiply, use the result to calculate the depth: + * Calculate tmp0.y = TexCoord[dstreg] . src.xyz; (tmp0.x has already been calculated) + * depth = (tmp0.y == 0.0) ? 1.0 : tmp0.x / tmp0.y + */ +static void shader_glsl_texm3x2depth(const struct wined3d_shader_instruction *ins) +{ + DWORD src_mask = WINED3DSP_WRITEMASK_0 | WINED3DSP_WRITEMASK_1 | WINED3DSP_WRITEMASK_2; + DWORD dstreg = ins->dst[0].reg.idx[0].offset; + struct glsl_src_param src0_param; + + shader_glsl_add_src_param(ins, &ins->src[0], src_mask, &src0_param); + + shader_addline(ins->ctx->buffer, "tmp0.y = dot(T%u.xyz, %s);\n", dstreg, src0_param.param_str); + shader_addline(ins->ctx->buffer, "gl_FragDepth = (tmp0.y == 0.0) ? 1.0 : clamp(tmp0.x / tmp0.y, 0.0, 1.0);\n"); +} + +/** Process the WINED3DSIO_TEXM3X2PAD instruction in GLSL + * Calculate the 1st of a 2-row matrix multiplication. */ +static void shader_glsl_texm3x2pad(const struct wined3d_shader_instruction *ins) +{ + DWORD src_mask = WINED3DSP_WRITEMASK_0 | WINED3DSP_WRITEMASK_1 | WINED3DSP_WRITEMASK_2; + DWORD reg = ins->dst[0].reg.idx[0].offset; + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + struct glsl_src_param src0_param; + + shader_glsl_add_src_param(ins, &ins->src[0], src_mask, &src0_param); + shader_addline(buffer, "tmp0.x = dot(T%u.xyz, %s);\n", reg, src0_param.param_str); +} + +/** Process the WINED3DSIO_TEXM3X3PAD instruction in GLSL + * Calculate the 1st or 2nd row of a 3-row matrix multiplication. */ +static void shader_glsl_texm3x3pad(const struct wined3d_shader_instruction *ins) +{ + DWORD src_mask = WINED3DSP_WRITEMASK_0 | WINED3DSP_WRITEMASK_1 | WINED3DSP_WRITEMASK_2; + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + struct wined3d_shader_tex_mx *tex_mx = ins->ctx->tex_mx; + DWORD reg = ins->dst[0].reg.idx[0].offset; + struct glsl_src_param src0_param; + + shader_glsl_add_src_param(ins, &ins->src[0], src_mask, &src0_param); + shader_addline(buffer, "tmp0.%c = dot(T%u.xyz, %s);\n", 'x' + tex_mx->current_row, reg, src0_param.param_str); + tex_mx->texcoord_w[tex_mx->current_row++] = reg; +} + +static void shader_glsl_texm3x2tex(const struct wined3d_shader_instruction *ins) +{ + DWORD src_mask = WINED3DSP_WRITEMASK_0 | WINED3DSP_WRITEMASK_1 | WINED3DSP_WRITEMASK_2; + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + struct glsl_sample_function sample_function; + DWORD reg = ins->dst[0].reg.idx[0].offset; + struct glsl_src_param src0_param; + + shader_glsl_add_src_param(ins, &ins->src[0], src_mask, &src0_param); + shader_addline(buffer, "tmp0.y = dot(T%u.xyz, %s);\n", reg, src0_param.param_str); + + shader_glsl_get_sample_function(ins->ctx, reg, reg, 0, &sample_function); + + /* Sample the texture using the calculated coordinates */ + shader_glsl_gen_sample_code(ins, reg, &sample_function, WINED3DSP_NOSWIZZLE, NULL, NULL, NULL, NULL, "tmp0.xy"); + shader_glsl_release_sample_function(ins->ctx, &sample_function); +} + +/** Process the WINED3DSIO_TEXM3X3TEX instruction in GLSL + * Perform the 3rd row of a 3x3 matrix multiply, then sample the texture using the calculated coordinates */ +static void shader_glsl_texm3x3tex(const struct wined3d_shader_instruction *ins) +{ + DWORD src_mask = WINED3DSP_WRITEMASK_0 | WINED3DSP_WRITEMASK_1 | WINED3DSP_WRITEMASK_2; + struct wined3d_shader_tex_mx *tex_mx = ins->ctx->tex_mx; + struct glsl_sample_function sample_function; + DWORD reg = ins->dst[0].reg.idx[0].offset; + struct glsl_src_param src0_param; + + shader_glsl_add_src_param(ins, &ins->src[0], src_mask, &src0_param); + shader_addline(ins->ctx->buffer, "tmp0.z = dot(T%u.xyz, %s);\n", reg, src0_param.param_str); + + /* Dependent read, not valid with conditional NP2 */ + shader_glsl_get_sample_function(ins->ctx, reg, reg, 0, &sample_function); + + /* Sample the texture using the calculated coordinates */ + shader_glsl_gen_sample_code(ins, reg, &sample_function, WINED3DSP_NOSWIZZLE, NULL, NULL, NULL, NULL, "tmp0.xyz"); + shader_glsl_release_sample_function(ins->ctx, &sample_function); + + tex_mx->current_row = 0; +} + +/** Process the WINED3DSIO_TEXM3X3 instruction in GLSL + * Perform the 3rd row of a 3x3 matrix multiply */ +static void shader_glsl_texm3x3(const struct wined3d_shader_instruction *ins) +{ + DWORD src_mask = WINED3DSP_WRITEMASK_0 | WINED3DSP_WRITEMASK_1 | WINED3DSP_WRITEMASK_2; + struct wined3d_shader_tex_mx *tex_mx = ins->ctx->tex_mx; + DWORD reg = ins->dst[0].reg.idx[0].offset; + struct glsl_src_param src0_param; + char dst_mask[6]; + + shader_glsl_add_src_param(ins, &ins->src[0], src_mask, &src0_param); + + shader_glsl_append_dst(ins->ctx->buffer, ins); + shader_glsl_get_write_mask(&ins->dst[0], dst_mask); + shader_addline(ins->ctx->buffer, "vec4(tmp0.xy, dot(T%u.xyz, %s), 1.0)%s);\n", reg, src0_param.param_str, dst_mask); + + tex_mx->current_row = 0; +} + +/* Process the WINED3DSIO_TEXM3X3SPEC instruction in GLSL + * Perform the final texture lookup based on the previous 2 3x3 matrix multiplies */ +static void shader_glsl_texm3x3spec(const struct wined3d_shader_instruction *ins) +{ + struct glsl_src_param src0_param; + struct glsl_src_param src1_param; + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + struct wined3d_shader_tex_mx *tex_mx = ins->ctx->tex_mx; + DWORD src_mask = WINED3DSP_WRITEMASK_0 | WINED3DSP_WRITEMASK_1 | WINED3DSP_WRITEMASK_2; + struct glsl_sample_function sample_function; + DWORD reg = ins->dst[0].reg.idx[0].offset; + char coord_mask[6]; + + shader_glsl_add_src_param(ins, &ins->src[0], src_mask, &src0_param); + shader_glsl_add_src_param(ins, &ins->src[1], src_mask, &src1_param); + + /* Perform the last matrix multiply operation */ + shader_addline(buffer, "tmp0.z = dot(T%u.xyz, %s);\n", reg, src0_param.param_str); + /* Reflection calculation */ + shader_addline(buffer, "tmp0.xyz = -reflect((%s), normalize(tmp0.xyz));\n", src1_param.param_str); + + /* Dependent read, not valid with conditional NP2 */ + shader_glsl_get_sample_function(ins->ctx, reg, reg, 0, &sample_function); + shader_glsl_write_mask_to_str(sample_function.coord_mask, coord_mask); + + /* Sample the texture */ + shader_glsl_gen_sample_code(ins, reg, &sample_function, WINED3DSP_NOSWIZZLE, + NULL, NULL, NULL, NULL, "tmp0%s", coord_mask); + shader_glsl_release_sample_function(ins->ctx, &sample_function); + + tex_mx->current_row = 0; +} + +/* Process the WINED3DSIO_TEXM3X3VSPEC instruction in GLSL + * Perform the final texture lookup based on the previous 2 3x3 matrix multiplies */ +static void shader_glsl_texm3x3vspec(const struct wined3d_shader_instruction *ins) +{ + struct wined3d_string_buffer *buffer = ins->ctx->buffer; + struct wined3d_shader_tex_mx *tex_mx = ins->ctx->tex_mx; + DWORD src_mask = WINED3DSP_WRITEMASK_0 | WINED3DSP_WRITEMASK_1 | WINED3DSP_WRITEMASK_2; + struct glsl_sample_function sample_function; + DWORD reg = ins->dst[0].reg.idx[0].offset; + struct glsl_src_param src0_param; + char coord_mask[6]; + + shader_glsl_add_src_param(ins, &ins->src[0], src_mask, &src0_param); + + /* Perform the last matrix multiply operation */ + shader_addline(buffer, "tmp0.z = dot(vec3(T%u), vec3(%s));\n", reg, src0_param.param_str); + + /* Construct the eye-ray vector from w coordinates */ + shader_addline(buffer, "tmp1.xyz = normalize(vec3(ffp_texcoord[%u].w, ffp_texcoord[%u].w, ffp_texcoord[%u].w));\n", + tex_mx->texcoord_w[0], tex_mx->texcoord_w[1], reg); + shader_addline(buffer, "tmp0.xyz = -reflect(tmp1.xyz, normalize(tmp0.xyz));\n"); + + /* Dependent read, not valid with conditional NP2 */ + shader_glsl_get_sample_function(ins->ctx, reg, reg, 0, &sample_function); + shader_glsl_write_mask_to_str(sample_function.coord_mask, coord_mask); + + /* Sample the texture using the calculated coordinates */ + shader_glsl_gen_sample_code(ins, reg, &sample_function, WINED3DSP_NOSWIZZLE, + NULL, NULL, NULL, NULL, "tmp0%s", coord_mask); + shader_glsl_release_sample_function(ins->ctx, &sample_function); + + tex_mx->current_row = 0; +} + +/** Process the WINED3DSIO_TEXBEM instruction in GLSL. + * Apply a fake bump map transform. + * texbem is pshader <= 1.3 only, this saves a few version checks + */ +static void shader_glsl_texbem(const struct wined3d_shader_instruction *ins) +{ + const struct shader_glsl_ctx_priv *priv = ins->ctx->backend_data; + struct glsl_sample_function sample_function; + struct glsl_src_param coord_param; + DWORD sampler_idx; + DWORD mask; + DWORD flags; + char coord_mask[6]; + + sampler_idx = ins->dst[0].reg.idx[0].offset; + flags = (priv->cur_ps_args->tex_transform >> sampler_idx * WINED3D_PSARGS_TEXTRANSFORM_SHIFT) + & WINED3D_PSARGS_TEXTRANSFORM_MASK; + + /* Dependent read, not valid with conditional NP2 */ + shader_glsl_get_sample_function(ins->ctx, sampler_idx, sampler_idx, 0, &sample_function); + mask = sample_function.coord_mask; + + shader_glsl_write_mask_to_str(mask, coord_mask); + + /* With projected textures, texbem only divides the static texture coord, + * not the displacement, so we can't let GL handle this. */ + if (flags & WINED3D_PSARGS_PROJECTED) + { + DWORD div_mask=0; + char coord_div_mask[3]; + switch (flags & ~WINED3D_PSARGS_PROJECTED) + { + case WINED3D_TTFF_COUNT1: + FIXME("WINED3D_TTFF_PROJECTED with WINED3D_TTFF_COUNT1?\n"); + break; + case WINED3D_TTFF_COUNT2: + div_mask = WINED3DSP_WRITEMASK_1; + break; + case WINED3D_TTFF_COUNT3: + div_mask = WINED3DSP_WRITEMASK_2; + break; + case WINED3D_TTFF_COUNT4: + case WINED3D_TTFF_DISABLE: + div_mask = WINED3DSP_WRITEMASK_3; + break; + } + shader_glsl_write_mask_to_str(div_mask, coord_div_mask); + shader_addline(ins->ctx->buffer, "T%u%s /= T%u%s;\n", sampler_idx, coord_mask, sampler_idx, coord_div_mask); + } + + shader_glsl_add_src_param(ins, &ins->src[0], WINED3DSP_WRITEMASK_0 | WINED3DSP_WRITEMASK_1, &coord_param); + + shader_glsl_gen_sample_code(ins, sampler_idx, &sample_function, WINED3DSP_NOSWIZZLE, NULL, NULL, NULL, NULL, + "T%u%s + vec4(bumpenv_mat%u * %s, 0.0, 0.0)%s", sampler_idx, coord_mask, sampler_idx, + coord_param.param_str, coord_mask); + + if (ins->handler_idx == WINED3DSIH_TEXBEML) + { + struct glsl_src_param luminance_param; + struct glsl_dst_param dst_param; + + shader_glsl_add_src_param(ins, &ins->src[0], WINED3DSP_WRITEMASK_2, &luminance_param); + shader_glsl_add_dst_param(ins, &ins->dst[0], &dst_param); + + shader_addline(ins->ctx->buffer, "%s%s *= (%s * bumpenv_lum_scale%u + bumpenv_lum_offset%u);\n", + dst_param.reg_name, dst_param.mask_str, + luminance_param.param_str, sampler_idx, sampler_idx); + } + shader_glsl_release_sample_function(ins->ctx, &sample_function); +} + +static void shader_glsl_bem(const struct wined3d_shader_instruction *ins) +{ + DWORD sampler_idx = ins->dst[0].reg.idx[0].offset; + struct glsl_src_param src0_param, src1_param; + + shader_glsl_add_src_param(ins, &ins->src[0], WINED3DSP_WRITEMASK_0 | WINED3DSP_WRITEMASK_1, &src0_param); + shader_glsl_add_src_param(ins, &ins->src[1], WINED3DSP_WRITEMASK_0 | WINED3DSP_WRITEMASK_1, &src1_param); + + shader_glsl_append_dst(ins->ctx->buffer, ins); + shader_addline(ins->ctx->buffer, "%s + bumpenv_mat%u * %s);\n", + src0_param.param_str, sampler_idx, src1_param.param_str); +} + +/** Process the WINED3DSIO_TEXREG2AR instruction in GLSL + * Sample 2D texture at dst using the alpha & red (wx) components of src as texture coordinates */ +static void shader_glsl_texreg2ar(const struct wined3d_shader_instruction *ins) +{ + struct shader_glsl_ctx_priv *priv = ins->ctx->backend_data; + DWORD sampler_idx = ins->dst[0].reg.idx[0].offset; + struct glsl_sample_function sample_function; + struct wined3d_string_buffer *reg_name; + + reg_name = string_buffer_get(priv->string_buffers); + shader_glsl_get_register_name(&ins->src[0].reg, ins->src[0].reg.data_type, reg_name, NULL, ins->ctx); + + shader_glsl_get_sample_function(ins->ctx, sampler_idx, sampler_idx, 0, &sample_function); + shader_glsl_gen_sample_code(ins, sampler_idx, &sample_function, WINED3DSP_NOSWIZZLE, NULL, NULL, NULL, NULL, + "%s.wx", reg_name->buffer); + shader_glsl_release_sample_function(ins->ctx, &sample_function); + + string_buffer_release(priv->string_buffers, reg_name); +} + +/** Process the WINED3DSIO_TEXREG2GB instruction in GLSL + * Sample 2D texture at dst using the green & blue (yz) components of src as texture coordinates */ +static void shader_glsl_texreg2gb(const struct wined3d_shader_instruction *ins) +{ + struct shader_glsl_ctx_priv *priv = ins->ctx->backend_data; + DWORD sampler_idx = ins->dst[0].reg.idx[0].offset; + struct glsl_sample_function sample_function; + struct wined3d_string_buffer *reg_name; + + reg_name = string_buffer_get(priv->string_buffers); + shader_glsl_get_register_name(&ins->src[0].reg, ins->src[0].reg.data_type, reg_name, NULL, ins->ctx); + + shader_glsl_get_sample_function(ins->ctx, sampler_idx, sampler_idx, 0, &sample_function); + shader_glsl_gen_sample_code(ins, sampler_idx, &sample_function, WINED3DSP_NOSWIZZLE, NULL, NULL, NULL, NULL, + "%s.yz", reg_name->buffer); + shader_glsl_release_sample_function(ins->ctx, &sample_function); + + string_buffer_release(priv->string_buffers, reg_name); +} + +/** Process the WINED3DSIO_TEXREG2RGB instruction in GLSL + * Sample texture at dst using the rgb (xyz) components of src as texture coordinates */ +static void shader_glsl_texreg2rgb(const struct wined3d_shader_instruction *ins) +{ + DWORD sampler_idx = ins->dst[0].reg.idx[0].offset; + struct glsl_sample_function sample_function; + struct glsl_src_param src0_param; + + /* Dependent read, not valid with conditional NP2 */ + shader_glsl_get_sample_function(ins->ctx, sampler_idx, sampler_idx, 0, &sample_function); + shader_glsl_add_src_param(ins, &ins->src[0], sample_function.coord_mask, &src0_param); + + shader_glsl_gen_sample_code(ins, sampler_idx, &sample_function, WINED3DSP_NOSWIZZLE, NULL, NULL, NULL, NULL, + "%s", src0_param.param_str); + shader_glsl_release_sample_function(ins->ctx, &sample_function); +} + +/** Process the WINED3DSIO_TEXKILL instruction in GLSL. + * If any of the first 3 components are < 0, discard this pixel */ +static void shader_glsl_texkill(const struct wined3d_shader_instruction *ins) +{ + if (ins->ctx->reg_maps->shader_version.major >= 4) + { + shader_glsl_generate_condition(ins); + shader_addline(ins->ctx->buffer, " discard;\n"); + } + else + { + struct glsl_dst_param dst_param; + + /* The argument is a destination parameter, and no writemasks are allowed */ + shader_glsl_add_dst_param(ins, &ins->dst[0], &dst_param); + + /* 2.0 shaders compare all 4 components in texkill. */ + if (ins->ctx->reg_maps->shader_version.major >= 2) + shader_addline(ins->ctx->buffer, "if (any(lessThan(%s.xyzw, vec4(0.0)))) discard;\n", dst_param.reg_name); + /* 1.x shaders only compare the first 3 components, probably due to + * the nature of the texkill instruction as a tex* instruction, and + * phase, which kills all .w components. Even if all 4 components are + * defined, only the first 3 are used. */ + else + shader_addline(ins->ctx->buffer, "if (any(lessThan(%s.xyz, vec3(0.0)))) discard;\n", dst_param.reg_name); + } +} + +/** Process the WINED3DSIO_DP2ADD instruction in GLSL. + * dst = dot2(src0, src1) + src2 */ +static void shader_glsl_dp2add(const struct wined3d_shader_instruction *ins) +{ + struct glsl_src_param src0_param; + struct glsl_src_param src1_param; + struct glsl_src_param src2_param; + DWORD write_mask; + unsigned int mask_size; + + write_mask = shader_glsl_append_dst(ins->ctx->buffer, ins); + mask_size = shader_glsl_get_write_mask_size(write_mask); + + shader_glsl_add_src_param(ins, &ins->src[0], WINED3DSP_WRITEMASK_0 | WINED3DSP_WRITEMASK_1, &src0_param); + shader_glsl_add_src_param(ins, &ins->src[1], WINED3DSP_WRITEMASK_0 | WINED3DSP_WRITEMASK_1, &src1_param); + shader_glsl_add_src_param(ins, &ins->src[2], WINED3DSP_WRITEMASK_0, &src2_param); + + if (mask_size > 1) { + shader_addline(ins->ctx->buffer, "vec%d(dot(%s, %s) + %s));\n", + mask_size, src0_param.param_str, src1_param.param_str, src2_param.param_str); + } else { + shader_addline(ins->ctx->buffer, "dot(%s, %s) + %s);\n", + src0_param.param_str, src1_param.param_str, src2_param.param_str); + } +} + +static void shader_glsl_input_pack(const struct wined3d_shader *shader, struct wined3d_string_buffer *buffer, + const struct wined3d_shader_signature *input_signature, + const struct wined3d_shader_reg_maps *reg_maps, + const struct ps_compile_args *args, const struct wined3d_gl_info *gl_info, BOOL unroll) +{ + unsigned int i; + + for (i = 0; i < input_signature->element_count; ++i) + { + const struct wined3d_shader_signature_element *input = &input_signature->elements[i]; + const char *semantic_name; + UINT semantic_idx; + char reg_mask[6]; + + /* Unused */ + if (!(reg_maps->input_registers & (1u << input->register_idx))) + continue; + + semantic_name = input->semantic_name; + semantic_idx = input->semantic_idx; + shader_glsl_write_mask_to_str(input->mask, reg_mask); + + if (args->vp_mode == WINED3D_VP_MODE_SHADER) + { + if (input->sysval_semantic == WINED3D_SV_POSITION && !semantic_idx) + { + shader_addline(buffer, "ps_in[%u]%s = vpos%s;\n", + shader->u.ps.input_reg_map[input->register_idx], reg_mask, reg_mask); + } + else if (args->pointsprite && shader_match_semantic(semantic_name, WINED3D_DECL_USAGE_TEXCOORD)) + { + shader_addline(buffer, "ps_in[%u] = vec4(gl_PointCoord.xy, 0.0, 0.0);\n", input->register_idx); + } + else if (input->sysval_semantic == WINED3D_SV_IS_FRONT_FACE) + { + shader_addline(buffer, "ps_in[%u]%s = uintBitsToFloat(gl_FrontFacing ? 0xffffffffu : 0u);\n", + input->register_idx, reg_mask); + } + else if (input->sysval_semantic == WINED3D_SV_SAMPLE_INDEX) + { + if (gl_info->supported[ARB_SAMPLE_SHADING]) + shader_addline(buffer, "ps_in[%u]%s = intBitsToFloat(gl_SampleID);\n", + input->register_idx, reg_mask); + else + FIXME("ARB_sample_shading is not supported.\n"); + } + else if (input->sysval_semantic == WINED3D_SV_RENDER_TARGET_ARRAY_INDEX && !semantic_idx) + { + if (gl_info->supported[ARB_FRAGMENT_LAYER_VIEWPORT]) + shader_addline(buffer, "ps_in[%u]%s = intBitsToFloat(gl_Layer);\n", + input->register_idx, reg_mask); + else + FIXME("ARB_fragment_layer_viewport is not supported.\n"); + } + else if (input->sysval_semantic == WINED3D_SV_VIEWPORT_ARRAY_INDEX && !semantic_idx) + { + if (gl_info->supported[ARB_VIEWPORT_ARRAY]) + shader_addline(buffer, "ps_in[%u]%s = intBitsToFloat(gl_ViewportIndex);\n", + input->register_idx, reg_mask); + else + FIXME("ARB_viewport_array is not supported.\n"); + } + else + { + if (input->sysval_semantic) + FIXME("Unhandled sysval semantic %#x.\n", input->sysval_semantic); + shader_addline(buffer, unroll ? "ps_in[%u]%s = %s%u%s;\n" : "ps_in[%u]%s = %s[%u]%s;\n", + shader->u.ps.input_reg_map[input->register_idx], reg_mask, + shader_glsl_shader_input_name(gl_info), + shader->u.ps.input_reg_map[input->register_idx], reg_mask); + } + } + else if (shader_match_semantic(semantic_name, WINED3D_DECL_USAGE_TEXCOORD)) + { + if (args->pointsprite) + shader_addline(buffer, "ps_in[%u] = vec4(gl_PointCoord.xy, 0.0, 0.0);\n", + shader->u.ps.input_reg_map[input->register_idx]); + else if (args->vp_mode == WINED3D_VP_MODE_NONE && args->texcoords_initialized & (1u << semantic_idx)) + shader_addline(buffer, "ps_in[%u]%s = %s[%u]%s;\n", + shader->u.ps.input_reg_map[input->register_idx], reg_mask, + needs_legacy_glsl_syntax(gl_info) + ? "gl_TexCoord" : "ffp_varying_texcoord", semantic_idx, reg_mask); + else + shader_addline(buffer, "ps_in[%u]%s = vec4(0.0, 0.0, 0.0, 0.0)%s;\n", + shader->u.ps.input_reg_map[input->register_idx], reg_mask, reg_mask); + } + else if (shader_match_semantic(semantic_name, WINED3D_DECL_USAGE_COLOR)) + { + if (!semantic_idx) + shader_addline(buffer, "ps_in[%u]%s = vec4(ffp_varying_diffuse)%s;\n", + shader->u.ps.input_reg_map[input->register_idx], reg_mask, reg_mask); + else if (semantic_idx == 1) + shader_addline(buffer, "ps_in[%u]%s = vec4(ffp_varying_specular)%s;\n", + shader->u.ps.input_reg_map[input->register_idx], reg_mask, reg_mask); + else + shader_addline(buffer, "ps_in[%u]%s = vec4(0.0, 0.0, 0.0, 0.0)%s;\n", + shader->u.ps.input_reg_map[input->register_idx], reg_mask, reg_mask); + } + else + { + shader_addline(buffer, "ps_in[%u]%s = vec4(0.0, 0.0, 0.0, 0.0)%s;\n", + shader->u.ps.input_reg_map[input->register_idx], reg_mask, reg_mask); + } + } +} + +static void add_glsl_program_entry(struct shader_glsl_priv *priv, struct glsl_shader_prog_link *entry) +{ + struct glsl_program_key key; + + key.vs_id = entry->vs.id; + key.hs_id = entry->hs.id; + key.ds_id = entry->ds.id; + key.gs_id = entry->gs.id; + key.ps_id = entry->ps.id; + key.cs_id = entry->cs.id; + + if (wine_rb_put(&priv->program_lookup, &key, &entry->program_lookup_entry) == -1) + { + ERR("Failed to insert program entry.\n"); + } +} + +static struct glsl_shader_prog_link *get_glsl_program_entry(const struct shader_glsl_priv *priv, + const struct glsl_program_key *key) +{ + struct wine_rb_entry *entry; + + entry = wine_rb_get(&priv->program_lookup, key); + return entry ? WINE_RB_ENTRY_VALUE(entry, struct glsl_shader_prog_link, program_lookup_entry) : NULL; +} + +/* Context activation is done by the caller. */ +static void delete_glsl_program_entry(struct shader_glsl_priv *priv, const struct wined3d_gl_info *gl_info, + struct glsl_shader_prog_link *entry) +{ + wine_rb_remove(&priv->program_lookup, &entry->program_lookup_entry); + + GL_EXTCALL(glDeleteProgram(entry->id)); + if (entry->vs.id) + list_remove(&entry->vs.shader_entry); + if (entry->hs.id) + list_remove(&entry->hs.shader_entry); + if (entry->ds.id) + list_remove(&entry->ds.shader_entry); + if (entry->gs.id) + list_remove(&entry->gs.shader_entry); + if (entry->ps.id) + list_remove(&entry->ps.shader_entry); + if (entry->cs.id) + list_remove(&entry->cs.shader_entry); + heap_free(entry); +} + +static void shader_glsl_setup_vs3_output(struct shader_glsl_priv *priv, + const struct wined3d_gl_info *gl_info, const DWORD *map, + const struct wined3d_shader_signature *input_signature, + const struct wined3d_shader_reg_maps *reg_maps_in, + const struct wined3d_shader_signature *output_signature, + const struct wined3d_shader_reg_maps *reg_maps_out) +{ + struct wined3d_string_buffer *destination = string_buffer_get(&priv->string_buffers); + const char *out_array_name = shader_glsl_shader_output_name(gl_info); + struct wined3d_string_buffer *buffer = &priv->shader_buffer; + unsigned int in_count = vec4_varyings(3, gl_info); + unsigned int max_varyings = needs_legacy_glsl_syntax(gl_info) ? in_count + 2 : in_count; + DWORD in_idx, *set = NULL; + unsigned int i, j; + char reg_mask[6]; + + set = heap_calloc(max_varyings, sizeof(*set)); + + for (i = 0; i < input_signature->element_count; ++i) + { + const struct wined3d_shader_signature_element *input = &input_signature->elements[i]; + + if (!(reg_maps_in->input_registers & (1u << input->register_idx))) + continue; + + in_idx = map[input->register_idx]; + /* Declared, but not read register */ + if (in_idx == ~0u) + continue; + if (in_idx >= max_varyings) + { + FIXME("More input varyings declared than supported, expect issues.\n"); + continue; + } + + if (in_idx == in_count) + string_buffer_sprintf(destination, "gl_FrontColor"); + else if (in_idx == in_count + 1) + string_buffer_sprintf(destination, "gl_FrontSecondaryColor"); + else + string_buffer_sprintf(destination, "%s[%u]", out_array_name, in_idx); + + if (!set[in_idx]) + set[in_idx] = ~0u; + + for (j = 0; j < output_signature->element_count; ++j) + { + const struct wined3d_shader_signature_element *output = &output_signature->elements[j]; + DWORD mask; + + if (!(reg_maps_out->output_registers & (1u << output->register_idx)) + || input->semantic_idx != output->semantic_idx + || strcmp(input->semantic_name, output->semantic_name) + || !(mask = input->mask & output->mask)) + continue; + + if (set[in_idx] == ~0u) + set[in_idx] = 0; + set[in_idx] |= mask & reg_maps_out->u.output_registers_mask[output->register_idx]; + shader_glsl_write_mask_to_str(mask, reg_mask); + + shader_addline(buffer, "%s%s = outputs[%u]%s;\n", + destination->buffer, reg_mask, output->register_idx, reg_mask); + } + } + + for (i = 0; i < max_varyings; ++i) + { + unsigned int size; + + if (!set[i] || set[i] == WINED3DSP_WRITEMASK_ALL) + continue; + + if (set[i] == ~0u) + set[i] = 0; + + size = 0; + if (!(set[i] & WINED3DSP_WRITEMASK_0)) + reg_mask[size++] = 'x'; + if (!(set[i] & WINED3DSP_WRITEMASK_1)) + reg_mask[size++] = 'y'; + if (!(set[i] & WINED3DSP_WRITEMASK_2)) + reg_mask[size++] = 'z'; + if (!(set[i] & WINED3DSP_WRITEMASK_3)) + reg_mask[size++] = 'w'; + reg_mask[size] = '\0'; + + if (i == in_count) + string_buffer_sprintf(destination, "gl_FrontColor"); + else if (i == in_count + 1) + string_buffer_sprintf(destination, "gl_FrontSecondaryColor"); + else + string_buffer_sprintf(destination, "%s[%u]", out_array_name, i); + + if (size == 1) + shader_addline(buffer, "%s.%s = 0.0;\n", destination->buffer, reg_mask); + else + shader_addline(buffer, "%s.%s = vec%u(0.0);\n", destination->buffer, reg_mask, size); + } + + heap_free(set); + string_buffer_release(&priv->string_buffers, destination); +} + +static void shader_glsl_setup_sm4_shader_output(struct shader_glsl_priv *priv, + unsigned int input_count, const struct wined3d_shader_signature *output_signature, + const struct wined3d_shader_reg_maps *reg_maps_out, const char *output_variable_name, + BOOL rasterizer_setup) +{ + struct wined3d_string_buffer *buffer = &priv->shader_buffer; + char reg_mask[6]; + unsigned int i; + + for (i = 0; i < output_signature->element_count; ++i) + { + const struct wined3d_shader_signature_element *output = &output_signature->elements[i]; + + if (!(reg_maps_out->output_registers & (1u << output->register_idx))) + continue; + + if (output->stream_idx) + continue; + + if (output->register_idx >= input_count) + continue; + + shader_glsl_write_mask_to_str(output->mask, reg_mask); + + shader_addline(buffer, + rasterizer_setup ? "%s.reg%u%s = outputs[%u]%s;\n" : "%s.reg[%u]%s = outputs[%u]%s;\n", + output_variable_name, output->register_idx, reg_mask, output->register_idx, reg_mask); + } +} + +static void shader_glsl_generate_clip_or_cull_distances(struct wined3d_string_buffer *buffer, + const struct wined3d_shader_signature_element *element, DWORD clip_or_cull_distance_mask) +{ + unsigned int i, clip_or_cull_index; + const char *name; + char reg_mask[6]; + + name = element->sysval_semantic == WINED3D_SV_CLIP_DISTANCE ? "Clip" : "Cull"; + /* Assign consecutive indices starting from 0. */ + clip_or_cull_index = element->semantic_idx ? wined3d_popcount(clip_or_cull_distance_mask & 0xf) : 0; + for (i = 0; i < 4; ++i) + { + if (!(element->mask & (WINED3DSP_WRITEMASK_0 << i))) + continue; + + shader_glsl_write_mask_to_str(WINED3DSP_WRITEMASK_0 << i, reg_mask); + shader_addline(buffer, "gl_%sDistance[%u] = outputs[%u]%s;\n", + name, clip_or_cull_index, element->register_idx, reg_mask); + ++clip_or_cull_index; + } +} + +static void shader_glsl_setup_sm3_rasterizer_input(struct shader_glsl_priv *priv, + const struct wined3d_gl_info *gl_info, const DWORD *map, + const struct wined3d_shader_signature *input_signature, + const struct wined3d_shader_reg_maps *reg_maps_in, unsigned int input_count, + const struct wined3d_shader_signature *output_signature, + const struct wined3d_shader_reg_maps *reg_maps_out, BOOL per_vertex_point_size) +{ + struct wined3d_string_buffer *buffer = &priv->shader_buffer; + const char *semantic_name; + unsigned int semantic_idx; + char reg_mask[6]; + unsigned int i; + + /* First, sort out position and point size system values. */ + for (i = 0; i < output_signature->element_count; ++i) + { + const struct wined3d_shader_signature_element *output = &output_signature->elements[i]; + + if (!(reg_maps_out->output_registers & (1u << output->register_idx))) + continue; + + if (output->stream_idx) + continue; + + semantic_name = output->semantic_name; + semantic_idx = output->semantic_idx; + shader_glsl_write_mask_to_str(output->mask, reg_mask); + + if (output->sysval_semantic == WINED3D_SV_POSITION && !semantic_idx) + { + shader_addline(buffer, "gl_Position%s = outputs[%u]%s;\n", + reg_mask, output->register_idx, reg_mask); + } + else if (shader_match_semantic(semantic_name, WINED3D_DECL_USAGE_PSIZE) && per_vertex_point_size) + { + shader_addline(buffer, "gl_PointSize = clamp(outputs[%u].%c, " + "ffp_point.size_min, ffp_point.size_max);\n", output->register_idx, reg_mask[1]); + } + else if (output->sysval_semantic == WINED3D_SV_RENDER_TARGET_ARRAY_INDEX && !semantic_idx) + { + shader_addline(buffer, "gl_Layer = floatBitsToInt(outputs[%u])%s;\n", + output->register_idx, reg_mask); + } + else if (output->sysval_semantic == WINED3D_SV_VIEWPORT_ARRAY_INDEX && !semantic_idx) + { + shader_addline(buffer, "gl_ViewportIndex = floatBitsToInt(outputs[%u])%s;\n", + output->register_idx, reg_mask); + } + else if (output->sysval_semantic == WINED3D_SV_CLIP_DISTANCE) + { + shader_glsl_generate_clip_or_cull_distances(buffer, output, reg_maps_out->clip_distance_mask); + } + else if (output->sysval_semantic == WINED3D_SV_CULL_DISTANCE) + { + shader_glsl_generate_clip_or_cull_distances(buffer, output, reg_maps_out->cull_distance_mask); + } + else if (output->sysval_semantic) + { + FIXME("Unhandled sysval semantic %#x.\n", output->sysval_semantic); + } + } + + /* Then, setup the pixel shader input. */ + if (reg_maps_out->shader_version.major < 4) + shader_glsl_setup_vs3_output(priv, gl_info, map, input_signature, reg_maps_in, + output_signature, reg_maps_out); + else + shader_glsl_setup_sm4_shader_output(priv, input_count, output_signature, reg_maps_out, "shader_out", TRUE); +} + +/* Context activation is done by the caller. */ +static GLuint shader_glsl_generate_vs3_rasterizer_input_setup(struct shader_glsl_priv *priv, + const struct wined3d_shader *vs, const struct wined3d_shader *ps, + BOOL per_vertex_point_size, BOOL flatshading, const struct wined3d_gl_info *gl_info) +{ + const BOOL legacy_syntax = needs_legacy_glsl_syntax(gl_info); + DWORD ps_major = ps ? ps->reg_maps.shader_version.major : 0; + struct wined3d_string_buffer *buffer = &priv->shader_buffer; + const char *semantic_name; + UINT semantic_idx; + char reg_mask[6]; + unsigned int i; + GLuint ret; + + string_buffer_clear(buffer); + + shader_glsl_add_version_declaration(buffer, gl_info); + + if (per_vertex_point_size) + { + shader_addline(buffer, "uniform struct\n{\n"); + shader_addline(buffer, " float size_min;\n"); + shader_addline(buffer, " float size_max;\n"); + shader_addline(buffer, "} ffp_point;\n"); + } + + if (ps_major < 3) + { + DWORD colors_written_mask[2] = {0}; + DWORD texcoords_written_mask[WINED3D_MAX_TEXTURES] = {0}; + + if (!legacy_syntax) + { + declare_out_varying(gl_info, buffer, flatshading, "vec4 ffp_varying_diffuse;\n"); + declare_out_varying(gl_info, buffer, flatshading, "vec4 ffp_varying_specular;\n"); + declare_out_varying(gl_info, buffer, FALSE, "vec4 ffp_varying_texcoord[%u];\n", WINED3D_MAX_TEXTURES); + declare_out_varying(gl_info, buffer, FALSE, "float ffp_varying_fogcoord;\n"); + } + + shader_addline(buffer, "void setup_vs_output(in vec4 outputs[%u])\n{\n", vs->limits->packed_output); + + for (i = 0; i < vs->output_signature.element_count; ++i) + { + const struct wined3d_shader_signature_element *output = &vs->output_signature.elements[i]; + DWORD write_mask; + + if (!(vs->reg_maps.output_registers & (1u << output->register_idx))) + continue; + + semantic_name = output->semantic_name; + semantic_idx = output->semantic_idx; + write_mask = output->mask; + shader_glsl_write_mask_to_str(write_mask, reg_mask); + + if (shader_match_semantic(semantic_name, WINED3D_DECL_USAGE_COLOR) && semantic_idx < 2) + { + if (legacy_syntax) + shader_addline(buffer, "gl_Front%sColor%s = outputs[%u]%s;\n", + semantic_idx ? "Secondary" : "", reg_mask, output->register_idx, reg_mask); + else + shader_addline(buffer, "ffp_varying_%s%s = clamp(outputs[%u]%s, 0.0, 1.0);\n", + semantic_idx ? "specular" : "diffuse", reg_mask, output->register_idx, reg_mask); + + colors_written_mask[semantic_idx] = write_mask; + } + else if (shader_match_semantic(semantic_name, WINED3D_DECL_USAGE_POSITION) && !semantic_idx) + { + shader_addline(buffer, "gl_Position%s = outputs[%u]%s;\n", + reg_mask, output->register_idx, reg_mask); + } + else if (shader_match_semantic(semantic_name, WINED3D_DECL_USAGE_TEXCOORD)) + { + if (semantic_idx < WINED3D_MAX_TEXTURES) + { + shader_addline(buffer, "%s[%u]%s = outputs[%u]%s;\n", + legacy_syntax ? "gl_TexCoord" : "ffp_varying_texcoord", + semantic_idx, reg_mask, output->register_idx, reg_mask); + texcoords_written_mask[semantic_idx] = write_mask; + } + } + else if (shader_match_semantic(semantic_name, WINED3D_DECL_USAGE_PSIZE) && per_vertex_point_size) + { + shader_addline(buffer, "gl_PointSize = clamp(outputs[%u].%c, " + "ffp_point.size_min, ffp_point.size_max);\n", output->register_idx, reg_mask[1]); + } + else if (shader_match_semantic(semantic_name, WINED3D_DECL_USAGE_FOG)) + { + shader_addline(buffer, "%s = clamp(outputs[%u].%c, 0.0, 1.0);\n", + legacy_syntax ? "gl_FogFragCoord" : "ffp_varying_fogcoord", + output->register_idx, reg_mask[1]); + } + } + + for (i = 0; i < 2; ++i) + { + if (colors_written_mask[i] != WINED3DSP_WRITEMASK_ALL) + { + shader_glsl_write_mask_to_str(~colors_written_mask[i] & WINED3DSP_WRITEMASK_ALL, reg_mask); + if (!i) + shader_addline(buffer, "%s%s = vec4(1.0)%s;\n", + legacy_syntax ? "gl_FrontColor" : "ffp_varying_diffuse", + reg_mask, reg_mask); + else + shader_addline(buffer, "%s%s = vec4(0.0)%s;\n", + legacy_syntax ? "gl_FrontSecondaryColor" : "ffp_varying_specular", + reg_mask, reg_mask); + } + } + for (i = 0; i < WINED3D_MAX_TEXTURES; ++i) + { + if (ps && !(ps->reg_maps.texcoord & (1u << i))) + continue; + + if (texcoords_written_mask[i] != WINED3DSP_WRITEMASK_ALL) + { + if (!shader_glsl_full_ffp_varyings(gl_info) && !texcoords_written_mask[i]) + continue; + + shader_glsl_write_mask_to_str(~texcoords_written_mask[i] & WINED3DSP_WRITEMASK_ALL, reg_mask); + shader_addline(buffer, "%s[%u]%s = vec4(0.0)%s;\n", + legacy_syntax ? "gl_TexCoord" : "ffp_varying_texcoord", i, reg_mask, reg_mask); + } + } + } + else + { + unsigned int in_count = min(vec4_varyings(ps_major, gl_info), ps->limits->packed_input); + + shader_glsl_declare_shader_outputs(gl_info, buffer, in_count, FALSE, NULL); + shader_addline(buffer, "void setup_vs_output(in vec4 outputs[%u])\n{\n", vs->limits->packed_output); + shader_glsl_setup_sm3_rasterizer_input(priv, gl_info, ps->u.ps.input_reg_map, &ps->input_signature, + &ps->reg_maps, 0, &vs->output_signature, &vs->reg_maps, per_vertex_point_size); + } + + shader_addline(buffer, "}\n"); + + ret = GL_EXTCALL(glCreateShader(GL_VERTEX_SHADER)); + checkGLcall("glCreateShader(GL_VERTEX_SHADER)"); + shader_glsl_compile(gl_info, ret, buffer->buffer); + + return ret; +} + +static void shader_glsl_generate_stream_output_setup(struct wined3d_string_buffer *buffer, + const struct wined3d_shader *shader) +{ + const struct wined3d_stream_output_desc *so_desc = shader->u.gs.so_desc; + unsigned int i, register_idx, component_idx; + + shader_addline(buffer, "out shader_in_out\n{\n"); + for (i = 0; i < so_desc->element_count; ++i) + { + const struct wined3d_stream_output_element *e = &so_desc->elements[i]; + + if (e->stream_idx) + { + FIXME("Unhandled stream %u.\n", e->stream_idx); + continue; + } + if (!e->semantic_name) + continue; + if (!shader_get_stream_output_register_info(shader, e, ®ister_idx, &component_idx)) + continue; + + if (component_idx || e->component_count != 4) + { + if (e->component_count == 1) + shader_addline(buffer, "float"); + else + shader_addline(buffer, "vec%u", e->component_count); + + shader_addline(buffer, " reg%u_%u_%u;\n", + register_idx, component_idx, component_idx + e->component_count - 1); + } + else + { + shader_addline(buffer, "vec4 reg%u;\n", register_idx); + } + } + shader_addline(buffer, "} shader_out;\n"); + + shader_addline(buffer, "void setup_gs_output(in vec4 outputs[%u])\n{\n", + shader->limits->packed_output); + for (i = 0; i < so_desc->element_count; ++i) + { + const struct wined3d_stream_output_element *e = &so_desc->elements[i]; + + if (e->stream_idx) + { + FIXME("Unhandled stream %u.\n", e->stream_idx); + continue; + } + if (!e->semantic_name) + continue; + if (!shader_get_stream_output_register_info(shader, e, ®ister_idx, &component_idx)) + continue; + + if (component_idx || e->component_count != 4) + { + DWORD write_mask; + char str_mask[6]; + + write_mask = ((1u << e->component_count) - 1) << component_idx; + shader_glsl_write_mask_to_str(write_mask, str_mask); + shader_addline(buffer, "shader_out.reg%u_%u_%u = outputs[%u]%s;\n", + register_idx, component_idx, component_idx + e->component_count - 1, + register_idx, str_mask); + } + else + { + shader_addline(buffer, "shader_out.reg%u = outputs[%u];\n", register_idx, register_idx); + } + } + shader_addline(buffer, "}\n"); +} + +static void shader_glsl_generate_sm4_output_setup(struct shader_glsl_priv *priv, + const struct wined3d_shader *shader, unsigned int input_count, + const struct wined3d_gl_info *gl_info, BOOL rasterizer_setup, const DWORD *interpolation_mode) +{ + const char *prefix = shader_glsl_get_prefix(shader->reg_maps.shader_version.type); + struct wined3d_string_buffer *buffer = &priv->shader_buffer; + + if (rasterizer_setup) + input_count = min(vec4_varyings(4, gl_info), input_count); + + if (input_count) + shader_glsl_declare_shader_outputs(gl_info, buffer, input_count, rasterizer_setup, interpolation_mode); + + shader_addline(buffer, "void setup_%s_output(in vec4 outputs[%u])\n{\n", + prefix, shader->limits->packed_output); + + if (rasterizer_setup) + shader_glsl_setup_sm3_rasterizer_input(priv, gl_info, NULL, NULL, + NULL, input_count, &shader->output_signature, &shader->reg_maps, FALSE); + else + shader_glsl_setup_sm4_shader_output(priv, input_count, &shader->output_signature, + &shader->reg_maps, "shader_out", rasterizer_setup); + + shader_addline(buffer, "}\n"); +} + +static void shader_glsl_generate_patch_constant_name(struct wined3d_string_buffer *buffer, + const struct wined3d_shader_signature_element *constant, unsigned int *user_constant_idx, + const char *reg_mask) +{ + if (!constant->sysval_semantic) + { + shader_addline(buffer, "user_patch_constant[%u]%s", (*user_constant_idx)++, reg_mask); + return; + } + + switch (constant->sysval_semantic) + { + case WINED3D_SV_TESS_FACTOR_QUADEDGE: + case WINED3D_SV_TESS_FACTOR_TRIEDGE: + case WINED3D_SV_TESS_FACTOR_LINEDET: + case WINED3D_SV_TESS_FACTOR_LINEDEN: + shader_addline(buffer, "gl_TessLevelOuter[%u]", constant->semantic_idx); + break; + case WINED3D_SV_TESS_FACTOR_QUADINT: + case WINED3D_SV_TESS_FACTOR_TRIINT: + shader_addline(buffer, "gl_TessLevelInner[%u]", constant->semantic_idx); + break; + default: + FIXME("Unhandled sysval semantic %#x.\n", constant->sysval_semantic); + shader_addline(buffer, "vec4(0.0)%s", reg_mask); + } +} + +static void shader_glsl_generate_patch_constant_setup(struct wined3d_string_buffer *buffer, + const struct wined3d_shader_signature *signature, BOOL input_setup) +{ + unsigned int i, register_count, user_constant_index, user_constant_count; + + register_count = user_constant_count = 0; + for (i = 0; i < signature->element_count; ++i) + { + const struct wined3d_shader_signature_element *constant = &signature->elements[i]; + register_count = max(constant->register_idx + 1, register_count); + if (!constant->sysval_semantic) + ++user_constant_count; + } + + if (user_constant_count) + shader_addline(buffer, "patch %s vec4 user_patch_constant[%u];\n", + input_setup ? "in" : "out", user_constant_count); + if (input_setup) + shader_addline(buffer, "vec4 vpc[%u];\n", register_count); + + shader_addline(buffer, "void setup_patch_constant_%s()\n{\n", input_setup ? "input" : "output"); + for (i = 0, user_constant_index = 0; i < signature->element_count; ++i) + { + const struct wined3d_shader_signature_element *constant = &signature->elements[i]; + char reg_mask[6]; + + shader_glsl_write_mask_to_str(constant->mask, reg_mask); + + if (input_setup) + shader_addline(buffer, "vpc[%u]%s", constant->register_idx, reg_mask); + else + shader_glsl_generate_patch_constant_name(buffer, constant, &user_constant_index, reg_mask); + + shader_addline(buffer, " = "); + + if (input_setup) + shader_glsl_generate_patch_constant_name(buffer, constant, &user_constant_index, reg_mask); + else + shader_addline(buffer, "hs_out[%u]%s", constant->register_idx, reg_mask); + + shader_addline(buffer, ";\n"); + } + shader_addline(buffer, "}\n"); +} + +static void shader_glsl_generate_srgb_write_correction(struct wined3d_string_buffer *buffer, + const struct wined3d_gl_info *gl_info) +{ + const char *output = get_fragment_output(gl_info); + + shader_addline(buffer, "tmp0.xyz = pow(%s[0].xyz, vec3(srgb_const0.x));\n", output); + shader_addline(buffer, "tmp0.xyz = tmp0.xyz * vec3(srgb_const0.y) - vec3(srgb_const0.z);\n"); + shader_addline(buffer, "tmp1.xyz = %s[0].xyz * vec3(srgb_const0.w);\n", output); + shader_addline(buffer, "bvec3 srgb_compare = lessThan(%s[0].xyz, vec3(srgb_const1.x));\n", output); + shader_addline(buffer, "%s[0].xyz = mix(tmp0.xyz, tmp1.xyz, vec3(srgb_compare));\n", output); + shader_addline(buffer, "%s[0] = clamp(%s[0], 0.0, 1.0);\n", output, output); +} + +static void shader_glsl_generate_fog_code(struct wined3d_string_buffer *buffer, + const struct wined3d_gl_info *gl_info, enum wined3d_ffp_ps_fog_mode mode) +{ + const char *output = get_fragment_output(gl_info); + + switch (mode) + { + case WINED3D_FFP_PS_FOG_OFF: + return; + + case WINED3D_FFP_PS_FOG_LINEAR: + shader_addline(buffer, "float fog = (ffp_fog.end - ffp_varying_fogcoord) * ffp_fog.scale;\n"); + break; + + case WINED3D_FFP_PS_FOG_EXP: + shader_addline(buffer, "float fog = exp(-ffp_fog.density * ffp_varying_fogcoord);\n"); + break; + + case WINED3D_FFP_PS_FOG_EXP2: + shader_addline(buffer, "float fog = exp(-ffp_fog.density * ffp_fog.density" + " * ffp_varying_fogcoord * ffp_varying_fogcoord);\n"); + break; + + default: + ERR("Invalid fog mode %#x.\n", mode); + return; + } + + shader_addline(buffer, "%s[0].xyz = mix(ffp_fog.color.xyz, %s[0].xyz, clamp(fog, 0.0, 1.0));\n", + output, output); +} + +static void shader_glsl_generate_alpha_test(struct wined3d_string_buffer *buffer, + const struct wined3d_gl_info *gl_info, enum wined3d_cmp_func alpha_func) +{ + /* alpha_func is the PASS condition, not the DISCARD condition. Instead of + * flipping all the operators here, just negate the comparison below. */ + static const char * const comparison_operator[] = + { + "", /* WINED3D_CMP_NEVER */ + "<", /* WINED3D_CMP_LESS */ + "==", /* WINED3D_CMP_EQUAL */ + "<=", /* WINED3D_CMP_LESSEQUAL */ + ">", /* WINED3D_CMP_GREATER */ + "!=", /* WINED3D_CMP_NOTEQUAL */ + ">=", /* WINED3D_CMP_GREATEREQUAL */ + "" /* WINED3D_CMP_ALWAYS */ + }; + + if (alpha_func == WINED3D_CMP_ALWAYS) + return; + + if (alpha_func != WINED3D_CMP_NEVER) + shader_addline(buffer, "if (!(%s[0].a %s alpha_test_ref))\n", + get_fragment_output(gl_info), comparison_operator[alpha_func - WINED3D_CMP_NEVER]); + shader_addline(buffer, " discard;\n"); +} + +static void shader_glsl_generate_colour_key_test(struct wined3d_string_buffer *buffer, + const char *colour, const char *colour_key_low, const char *colour_key_high) +{ + shader_addline(buffer, "if (all(greaterThanEqual(%s, %s)) && all(lessThan(%s, %s)))\n", + colour, colour_key_low, colour, colour_key_high); + shader_addline(buffer, " discard;\n"); +} + +static void shader_glsl_enable_extensions(struct wined3d_string_buffer *buffer, + const struct wined3d_gl_info *gl_info) +{ + if (gl_info->supported[ARB_CULL_DISTANCE]) + shader_addline(buffer, "#extension GL_ARB_cull_distance : enable\n"); + if (gl_info->supported[ARB_GPU_SHADER5]) + shader_addline(buffer, "#extension GL_ARB_gpu_shader5 : enable\n"); + if (gl_info->supported[ARB_SHADER_ATOMIC_COUNTERS]) + shader_addline(buffer, "#extension GL_ARB_shader_atomic_counters : enable\n"); + if (gl_info->supported[ARB_SHADER_BIT_ENCODING]) + shader_addline(buffer, "#extension GL_ARB_shader_bit_encoding : enable\n"); + if (gl_info->supported[ARB_SHADER_IMAGE_LOAD_STORE]) + shader_addline(buffer, "#extension GL_ARB_shader_image_load_store : enable\n"); + if (gl_info->supported[ARB_SHADER_IMAGE_SIZE]) + shader_addline(buffer, "#extension GL_ARB_shader_image_size : enable\n"); + if (gl_info->supported[ARB_SHADER_STORAGE_BUFFER_OBJECT]) + shader_addline(buffer, "#extension GL_ARB_shader_storage_buffer_object : enable\n"); + if (gl_info->supported[ARB_SHADER_TEXTURE_IMAGE_SAMPLES]) + shader_addline(buffer, "#extension GL_ARB_shader_texture_image_samples : enable\n"); + if (gl_info->supported[ARB_SHADING_LANGUAGE_420PACK]) + shader_addline(buffer, "#extension GL_ARB_shading_language_420pack : enable\n"); + if (gl_info->supported[ARB_SHADING_LANGUAGE_PACKING]) + shader_addline(buffer, "#extension GL_ARB_shading_language_packing : enable\n"); + if (gl_info->supported[ARB_TEXTURE_CUBE_MAP_ARRAY]) + shader_addline(buffer, "#extension GL_ARB_texture_cube_map_array : enable\n"); + if (gl_info->supported[ARB_TEXTURE_GATHER]) + shader_addline(buffer, "#extension GL_ARB_texture_gather : enable\n"); + if (gl_info->supported[ARB_TEXTURE_QUERY_LEVELS]) + shader_addline(buffer, "#extension GL_ARB_texture_query_levels : enable\n"); + if (gl_info->supported[ARB_UNIFORM_BUFFER_OBJECT]) + shader_addline(buffer, "#extension GL_ARB_uniform_buffer_object : enable\n"); + if (gl_info->supported[ARB_VIEWPORT_ARRAY]) + shader_addline(buffer, "#extension GL_ARB_viewport_array : enable\n"); + if (gl_info->supported[EXT_GPU_SHADER4]) + shader_addline(buffer, "#extension GL_EXT_gpu_shader4 : enable\n"); + if (gl_info->supported[EXT_TEXTURE_ARRAY]) + shader_addline(buffer, "#extension GL_EXT_texture_array : enable\n"); + if (gl_info->supported[EXT_TEXTURE_SHADOW_LOD]) + shader_addline(buffer, "#extension GL_EXT_texture_shadow_lod : enable\n"); +} + +static void shader_glsl_generate_color_output(struct wined3d_string_buffer *buffer, + const struct wined3d_gl_info *gl_info, const struct wined3d_shader *shader, + const struct ps_compile_args *args, struct wined3d_string_buffer_list *string_buffers) +{ + const struct wined3d_shader_signature *output_signature = &shader->output_signature; + struct wined3d_string_buffer *src, *assignment; + enum wined3d_data_type dst_data_type; + const char *swizzle; + unsigned int i; + + if (output_signature->element_count) + { + src = string_buffer_get(string_buffers); + assignment = string_buffer_get(string_buffers); + for (i = 0; i < output_signature->element_count; ++i) + { + const struct wined3d_shader_signature_element *output = &output_signature->elements[i]; + + /* register_idx is set to ~0u for non-color outputs. */ + if (output->register_idx == ~0u) + continue; + if ((unsigned int)output->component_type >= ARRAY_SIZE(component_type_info)) + { + FIXME("Unhandled component type %#x.\n", output->component_type); + continue; + } + dst_data_type = component_type_info[output->component_type].data_type; + shader_addline(buffer, "color_out%u = ", output->semantic_idx); + string_buffer_sprintf(src, "ps_out[%u]", output->semantic_idx); + shader_glsl_sprintf_cast(assignment, src->buffer, dst_data_type, WINED3D_DATA_FLOAT, 4); + swizzle = args->rt_alpha_swizzle & (1u << output->semantic_idx) ? ".argb" : ""; + shader_addline(buffer, "%s%s;\n", assignment->buffer, swizzle); + } + string_buffer_release(string_buffers, src); + string_buffer_release(string_buffers, assignment); + } + else + { + DWORD mask = shader->reg_maps.rt_mask; + + while (mask) + { + i = wined3d_bit_scan(&mask); + swizzle = args->rt_alpha_swizzle & (1u << i) ? ".argb" : ""; + shader_addline(buffer, "color_out%u = ps_out[%u]%s;\n", i, i, swizzle); + } + } +} + +static void shader_glsl_generate_ps_epilogue(const struct wined3d_gl_info *gl_info, + struct wined3d_string_buffer *buffer, const struct wined3d_shader *shader, + const struct ps_compile_args *args, struct wined3d_string_buffer_list *string_buffers) +{ + const struct wined3d_shader_reg_maps *reg_maps = &shader->reg_maps; + + /* Pixel shaders < 2.0 place the resulting color in R0 implicitly. */ + if (reg_maps->shader_version.major < 2) + shader_addline(buffer, "%s[0] = R0;\n", get_fragment_output(gl_info)); + + if (args->srgb_correction) + shader_glsl_generate_srgb_write_correction(buffer, gl_info); + + /* SM < 3 does not replace the fog stage. */ + if (reg_maps->shader_version.major < 3) + shader_glsl_generate_fog_code(buffer, gl_info, args->fog); + + shader_glsl_generate_alpha_test(buffer, gl_info, args->alpha_test_func + 1); + + if (reg_maps->sample_mask) + shader_addline(buffer, "gl_SampleMask[0] = floatBitsToInt(sample_mask);\n"); + + if (!use_legacy_fragment_output(gl_info)) + shader_glsl_generate_color_output(buffer, gl_info, shader, args, string_buffers); +} + +/* Context activation is done by the caller. */ +static GLuint shader_glsl_generate_fragment_shader(const struct wined3d_context_gl *context_gl, + struct wined3d_string_buffer *buffer, struct wined3d_string_buffer_list *string_buffers, + const struct wined3d_shader *shader, const struct ps_compile_args *args, + struct ps_np2fixup_info *np2fixup_info) +{ + const struct wined3d_shader_reg_maps *reg_maps = &shader->reg_maps; + const struct wined3d_shader_version *version = ®_maps->shader_version; + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + const char *prefix = shader_glsl_get_prefix(version->type); + BOOL legacy_syntax = needs_legacy_glsl_syntax(gl_info); + unsigned int i, extra_constants_needed = 0; + struct shader_glsl_ctx_priv priv_ctx; + GLuint shader_id; + DWORD map; + + memset(&priv_ctx, 0, sizeof(priv_ctx)); + priv_ctx.gl_info = gl_info; + priv_ctx.cur_ps_args = args; + priv_ctx.cur_np2fixup_info = np2fixup_info; + priv_ctx.string_buffers = string_buffers; + + shader_glsl_add_version_declaration(buffer, gl_info); + + shader_glsl_enable_extensions(buffer, gl_info); + if (gl_info->supported[ARB_CONSERVATIVE_DEPTH]) + shader_addline(buffer, "#extension GL_ARB_conservative_depth : enable\n"); + if (gl_info->supported[ARB_DERIVATIVE_CONTROL]) + shader_addline(buffer, "#extension GL_ARB_derivative_control : enable\n"); + if (shader_glsl_use_explicit_attrib_location(gl_info)) + shader_addline(buffer, "#extension GL_ARB_explicit_attrib_location : enable\n"); + if (gl_info->supported[ARB_FRAGMENT_COORD_CONVENTIONS]) + shader_addline(buffer, "#extension GL_ARB_fragment_coord_conventions : enable\n"); + if (gl_info->supported[ARB_FRAGMENT_LAYER_VIEWPORT]) + shader_addline(buffer, "#extension GL_ARB_fragment_layer_viewport : enable\n"); + if (gl_info->supported[ARB_SAMPLE_SHADING]) + shader_addline(buffer, "#extension GL_ARB_sample_shading : enable\n"); + if (gl_info->supported[ARB_SHADER_TEXTURE_LOD]) + shader_addline(buffer, "#extension GL_ARB_shader_texture_lod : enable\n"); + /* The spec says that it doesn't have to be explicitly enabled, but the + * nvidia drivers write a warning if we don't do so. */ + if (gl_info->supported[ARB_TEXTURE_RECTANGLE]) + shader_addline(buffer, "#extension GL_ARB_texture_rectangle : enable\n"); + + /* Base Declarations */ + shader_generate_glsl_declarations(context_gl, buffer, shader, reg_maps, &priv_ctx); + + if (gl_info->supported[ARB_CONSERVATIVE_DEPTH]) + { + if (shader->u.ps.depth_output == WINED3DSPR_DEPTHOUTGE) + shader_addline(buffer, "layout (depth_greater) out float gl_FragDepth;\n"); + else if (shader->u.ps.depth_output == WINED3DSPR_DEPTHOUTLE) + shader_addline(buffer, "layout (depth_less) out float gl_FragDepth;\n"); + } + + /* Declare uniforms for NP2 texcoord fixup: + * This is NOT done inside the loop that declares the texture samplers + * since the NP2 fixup code is currently only used for the GeforceFX + * series and when forcing the ARB_npot extension off. Modern cards just + * skip the code anyway, so put it inside a separate loop. */ + if (args->np2_fixup) + { + struct ps_np2fixup_info *fixup = priv_ctx.cur_np2fixup_info; + unsigned int cur = 0; + + /* NP2/RECT textures in OpenGL use texcoords in the range [0,width]x[0,height] + * while D3D has them in the (normalized) [0,1]x[0,1] range. + * samplerNP2Fixup stores texture dimensions and is updated through + * shader_glsl_load_np2fixup_constants when the sampler changes. */ + + for (i = 0; i < shader->limits->sampler; ++i) + { + enum wined3d_shader_resource_type resource_type; + + resource_type = pixelshader_get_resource_type(reg_maps, i, args->tex_types); + + if (!resource_type || !(args->np2_fixup & (1u << i))) + continue; + + if (resource_type != WINED3D_SHADER_RESOURCE_TEXTURE_2D) + { + FIXME("Non-2D texture is flagged for NP2 texcoord fixup.\n"); + continue; + } + + fixup->idx[i] = cur++; + } + + fixup->num_consts = (cur + 1) >> 1; + fixup->active = args->np2_fixup; + shader_addline(buffer, "uniform vec4 %s_samplerNP2Fixup[%u];\n", prefix, fixup->num_consts); + } + + if (version->major < 3 || args->vp_mode != WINED3D_VP_MODE_SHADER) + { + shader_addline(buffer, "uniform struct\n{\n"); + shader_addline(buffer, " vec4 color;\n"); + shader_addline(buffer, " float density;\n"); + shader_addline(buffer, " float end;\n"); + shader_addline(buffer, " float scale;\n"); + shader_addline(buffer, "} ffp_fog;\n"); + + if (legacy_syntax) + { + if (glsl_is_color_reg_read(shader, 0)) + shader_addline(buffer, "vec4 ffp_varying_diffuse;\n"); + if (glsl_is_color_reg_read(shader, 1)) + shader_addline(buffer, "vec4 ffp_varying_specular;\n"); + shader_addline(buffer, "vec4 ffp_texcoord[%u];\n", WINED3D_MAX_TEXTURES); + shader_addline(buffer, "float ffp_varying_fogcoord;\n"); + } + else + { + if (glsl_is_color_reg_read(shader, 0)) + declare_in_varying(gl_info, buffer, args->flatshading, "vec4 ffp_varying_diffuse;\n"); + if (glsl_is_color_reg_read(shader, 1)) + declare_in_varying(gl_info, buffer, args->flatshading, "vec4 ffp_varying_specular;\n"); + declare_in_varying(gl_info, buffer, FALSE, "vec4 ffp_varying_texcoord[%u];\n", WINED3D_MAX_TEXTURES); + shader_addline(buffer, "vec4 ffp_texcoord[%u];\n", WINED3D_MAX_TEXTURES); + declare_in_varying(gl_info, buffer, FALSE, "float ffp_varying_fogcoord;\n"); + } + } + + if (version->major >= 3) + { + unsigned int in_count = min(vec4_varyings(version->major, gl_info), shader->limits->packed_input); + + if (args->vp_mode == WINED3D_VP_MODE_SHADER && reg_maps->input_registers) + shader_glsl_declare_shader_inputs(gl_info, buffer, in_count, + shader->u.ps.interpolation_mode, version->major >= 4); + shader_addline(buffer, "vec4 %s_in[%u];\n", prefix, in_count); + } + + for (i = 0, map = reg_maps->bumpmat; map; map >>= 1, ++i) + { + if (!(map & 1)) + continue; + + shader_addline(buffer, "uniform mat2 bumpenv_mat%u;\n", i); + + if (reg_maps->luminanceparams & (1u << i)) + { + shader_addline(buffer, "uniform float bumpenv_lum_scale%u;\n", i); + shader_addline(buffer, "uniform float bumpenv_lum_offset%u;\n", i); + extra_constants_needed++; + } + + extra_constants_needed++; + } + + if (args->srgb_correction) + { + shader_addline(buffer, "const vec4 srgb_const0 = "); + shader_glsl_append_imm_vec(buffer, &wined3d_srgb_const[0].x, 4, gl_info); + shader_addline(buffer, ";\n"); + shader_addline(buffer, "const vec4 srgb_const1 = "); + shader_glsl_append_imm_vec(buffer, &wined3d_srgb_const[1].x, 4, gl_info); + shader_addline(buffer, ";\n"); + } + if (reg_maps->vpos || reg_maps->usesdsy) + { + if (reg_maps->usesdsy || !gl_info->supported[ARB_FRAGMENT_COORD_CONVENTIONS]) + { + ++extra_constants_needed; + shader_addline(buffer, "uniform vec4 ycorrection;\n"); + } + if (reg_maps->vpos) + { + if (gl_info->supported[ARB_FRAGMENT_COORD_CONVENTIONS]) + { + if (context_gl->c.d3d_info->wined3d_creation_flags & WINED3D_PIXEL_CENTER_INTEGER) + shader_addline(buffer, "layout(%spixel_center_integer) in vec4 gl_FragCoord;\n", + args->render_offscreen ? "" : "origin_upper_left, "); + else if (!args->render_offscreen) + shader_addline(buffer, "layout(origin_upper_left) in vec4 gl_FragCoord;\n"); + } + shader_addline(buffer, "vec4 vpos;\n"); + } + } + + if (args->alpha_test_func + 1 != WINED3D_CMP_ALWAYS) + shader_addline(buffer, "uniform float alpha_test_ref;\n"); + + if (!use_legacy_fragment_output(gl_info)) + { + const struct wined3d_shader_signature *output_signature = &shader->output_signature; + + if (args->dual_source_blend) + shader_addline(buffer, "vec4 ps_out[2];\n"); + else + shader_addline(buffer, "vec4 ps_out[%u];\n", gl_info->limits.buffers); + if (output_signature->element_count) + { + for (i = 0; i < output_signature->element_count; ++i) + { + const struct wined3d_shader_signature_element *output = &output_signature->elements[i]; + + if (output->register_idx == ~0u) + continue; + if ((unsigned int)output->component_type >= ARRAY_SIZE(component_type_info)) + { + FIXME("Unhandled component type %#x.\n", output->component_type); + continue; + } + if (shader_glsl_use_explicit_attrib_location(gl_info)) + { + if (args->dual_source_blend) + shader_addline(buffer, "layout(location = 0, index = %u) ", output->semantic_idx); + else + shader_addline(buffer, "layout(location = %u) ", output->semantic_idx); + } + shader_addline(buffer, "out %s4 color_out%u;\n", + component_type_info[output->component_type].glsl_vector_type, output->semantic_idx); + } + } + else + { + DWORD mask = reg_maps->rt_mask; + + while (mask) + { + i = wined3d_bit_scan(&mask); + if (shader_glsl_use_explicit_attrib_location(gl_info)) + { + if (args->dual_source_blend) + shader_addline(buffer, "layout(location = 0, index = %u) ", i); + else + shader_addline(buffer, "layout(location = %u) ", i); + } + shader_addline(buffer, "out vec4 color_out%u;\n", i); + } + } + } + + if (shader->limits->constant_float + extra_constants_needed >= gl_info->limits.glsl_ps_float_constants) + FIXME("Insufficient uniforms to run this shader.\n"); + + if (shader->u.ps.force_early_depth_stencil) + shader_addline(buffer, "layout(early_fragment_tests) in;\n"); + + shader_addline(buffer, "void main()\n{\n"); + + if (reg_maps->sample_mask) + shader_addline(buffer, "float sample_mask = uintBitsToFloat(0xffffffffu);\n"); + + /* Direct3D applications expect integer vPos values, while OpenGL drivers + * add approximately 0.5. This causes off-by-one problems as spotted by + * the vPos d3d9 visual test. Unfortunately ATI cards do not add exactly + * 0.5, but rather something like 0.49999999 or 0.50000001, which still + * causes precision troubles when we just subtract 0.5. + * + * To deal with that, just floor() the position. This will eliminate the + * fraction on all cards. + * + * TODO: Test how this behaves with multisampling. + * + * An advantage of floor is that it works even if the driver doesn't add + * 0.5. It is somewhat questionable if 1.5, 2.5, ... are the proper values + * to return in gl_FragCoord, even though coordinates specify the pixel + * centers instead of the pixel corners. This code will behave correctly + * on drivers that returns integer values. */ + if (reg_maps->vpos) + { + if (gl_info->supported[ARB_FRAGMENT_COORD_CONVENTIONS]) + shader_addline(buffer, "vpos = gl_FragCoord;\n"); + else if (context_gl->c.d3d_info->wined3d_creation_flags & WINED3D_PIXEL_CENTER_INTEGER) + shader_addline(buffer, + "vpos = floor(vec4(0, ycorrection[0], 0, 0) + gl_FragCoord * vec4(1, ycorrection[1], 1, 1));\n"); + else + shader_addline(buffer, + "vpos = vec4(0, ycorrection[0], 0, 0) + gl_FragCoord * vec4(1, ycorrection[1], 1, 1);\n"); + } + + if (reg_maps->shader_version.major < 3 || args->vp_mode != WINED3D_VP_MODE_SHADER) + { + unsigned int i; + WORD map = reg_maps->texcoord; + + if (legacy_syntax) + { + if (glsl_is_color_reg_read(shader, 0)) + shader_addline(buffer, "ffp_varying_diffuse = gl_Color;\n"); + if (glsl_is_color_reg_read(shader, 1)) + shader_addline(buffer, "ffp_varying_specular = gl_SecondaryColor;\n"); + } + + for (i = 0; map; map >>= 1, ++i) + { + if (map & 1) + { + if (args->pointsprite) + shader_addline(buffer, "ffp_texcoord[%u] = vec4(gl_PointCoord.xy, 0.0, 0.0);\n", i); + else if (args->texcoords_initialized & (1u << i)) + shader_addline(buffer, "ffp_texcoord[%u] = %s[%u];\n", i, + legacy_syntax ? "gl_TexCoord" : "ffp_varying_texcoord", i); + else + shader_addline(buffer, "ffp_texcoord[%u] = vec4(0.0);\n", i); + shader_addline(buffer, "vec4 T%u = ffp_texcoord[%u];\n", i, i); + } + } + + if (legacy_syntax) + shader_addline(buffer, "ffp_varying_fogcoord = gl_FogFragCoord;\n"); + } + + /* Pack 3.0 inputs */ + if (reg_maps->shader_version.major >= 3) + shader_glsl_input_pack(shader, buffer, &shader->input_signature, reg_maps, args, gl_info, + reg_maps->shader_version.major >= 4); + + /* Base Shader Body */ + if (FAILED(shader_generate_code(shader, buffer, reg_maps, &priv_ctx, NULL, NULL))) + return 0; + + /* In SM4+ the shader epilogue is generated by the "ret" instruction. */ + if (reg_maps->shader_version.major < 4) + shader_glsl_generate_ps_epilogue(gl_info, buffer, shader, args, string_buffers); + + shader_addline(buffer, "}\n"); + + shader_id = GL_EXTCALL(glCreateShader(GL_FRAGMENT_SHADER)); + TRACE("Compiling shader object %u.\n", shader_id); + shader_glsl_compile(gl_info, shader_id, buffer->buffer); + + return shader_id; +} + +static void shader_glsl_generate_vs_epilogue(const struct wined3d_gl_info *gl_info, + struct wined3d_string_buffer *buffer, const struct wined3d_shader *shader, + const struct vs_compile_args *args) +{ + const struct wined3d_shader_reg_maps *reg_maps = &shader->reg_maps; + const BOOL legacy_syntax = needs_legacy_glsl_syntax(gl_info); + unsigned int i; + + /* Unpack outputs. */ + shader_addline(buffer, "setup_vs_output(vs_out);\n"); + + /* The D3DRS_FOGTABLEMODE render state defines if the shader-generated fog coord is used + * or if the fragment depth is used. If the fragment depth is used(FOGTABLEMODE != NONE), + * the fog frag coord is thrown away. If the fog frag coord is used, but not written by + * the shader, it is set to 0.0(fully fogged, since start = 1.0, end = 0.0). + */ + if (reg_maps->shader_version.major < 3) + { + if (args->fog_src == VS_FOG_Z) + shader_addline(buffer, "%s = gl_Position.z;\n", + legacy_syntax ? "gl_FogFragCoord" : "ffp_varying_fogcoord"); + else if (!reg_maps->fog) + shader_addline(buffer, "%s = 0.0;\n", + legacy_syntax ? "gl_FogFragCoord" : "ffp_varying_fogcoord"); + } + + /* We always store the clipplanes without y inversion. */ + if (args->clip_enabled) + { + if (legacy_syntax) + shader_addline(buffer, "gl_ClipVertex = gl_Position;\n"); + else + for (i = 0; i < gl_info->limits.user_clip_distances; ++i) + shader_addline(buffer, "gl_ClipDistance[%u] = dot(gl_Position, clip_planes[%u]);\n", i, i); + } + + if (args->point_size && !args->per_vertex_point_size) + shader_addline(buffer, "gl_PointSize = clamp(ffp_point.size, ffp_point.size_min, ffp_point.size_max);\n"); + + if (args->next_shader_type == WINED3D_SHADER_TYPE_PIXEL && !gl_info->supported[ARB_CLIP_CONTROL]) + shader_glsl_fixup_position(buffer, FALSE); +} + +/* Context activation is done by the caller. */ +static GLuint shader_glsl_generate_vertex_shader(const struct wined3d_context_gl *context_gl, + struct shader_glsl_priv *priv, const struct wined3d_shader *shader, const struct vs_compile_args *args) +{ + struct wined3d_string_buffer_list *string_buffers = &priv->string_buffers; + const struct wined3d_shader_reg_maps *reg_maps = &shader->reg_maps; + const struct wined3d_shader_version *version = ®_maps->shader_version; + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + struct wined3d_string_buffer *buffer = &priv->shader_buffer; + struct shader_glsl_ctx_priv priv_ctx; + GLuint shader_id; + unsigned int i; + + memset(&priv_ctx, 0, sizeof(priv_ctx)); + priv_ctx.gl_info = gl_info; + priv_ctx.cur_vs_args = args; + priv_ctx.string_buffers = string_buffers; + + shader_glsl_add_version_declaration(buffer, gl_info); + + shader_glsl_enable_extensions(buffer, gl_info); + if (gl_info->supported[ARB_DRAW_INSTANCED]) + shader_addline(buffer, "#extension GL_ARB_draw_instanced : enable\n"); + if (shader_glsl_use_explicit_attrib_location(gl_info)) + shader_addline(buffer, "#extension GL_ARB_explicit_attrib_location : enable\n"); + if (gl_info->supported[ARB_SHADER_VIEWPORT_LAYER_ARRAY]) + shader_addline(buffer, "#extension GL_ARB_shader_viewport_layer_array : enable\n"); + + /* Base Declarations */ + shader_generate_glsl_declarations(context_gl, buffer, shader, reg_maps, &priv_ctx); + + for (i = 0; i < shader->input_signature.element_count; ++i) + shader_glsl_declare_generic_vertex_attribute(buffer, gl_info, &shader->input_signature.elements[i]); + + if (args->point_size && !args->per_vertex_point_size) + { + shader_addline(buffer, "uniform struct\n{\n"); + shader_addline(buffer, " float size;\n"); + shader_addline(buffer, " float size_min;\n"); + shader_addline(buffer, " float size_max;\n"); + shader_addline(buffer, "} ffp_point;\n"); + } + + if (!needs_legacy_glsl_syntax(gl_info)) + { + if (args->clip_enabled) + shader_addline(buffer, "uniform vec4 clip_planes[%u];\n", gl_info->limits.user_clip_distances); + + if (version->major < 3) + { + declare_out_varying(gl_info, buffer, args->flatshading, "vec4 ffp_varying_diffuse;\n"); + declare_out_varying(gl_info, buffer, args->flatshading, "vec4 ffp_varying_specular;\n"); + declare_out_varying(gl_info, buffer, FALSE, "vec4 ffp_varying_texcoord[%u];\n", WINED3D_MAX_TEXTURES); + declare_out_varying(gl_info, buffer, FALSE, "float ffp_varying_fogcoord;\n"); + } + } + + if (version->major < 4) + shader_addline(buffer, "void setup_vs_output(in vec4[%u]);\n", shader->limits->packed_output); + + if (args->next_shader_type == WINED3D_SHADER_TYPE_PIXEL && !gl_info->supported[ARB_CLIP_CONTROL]) + shader_addline(buffer, "uniform vec4 pos_fixup;\n"); + + if (reg_maps->shader_version.major >= 4) + shader_glsl_generate_sm4_output_setup(priv, shader, args->next_shader_input_count, + gl_info, args->next_shader_type == WINED3D_SHADER_TYPE_PIXEL, args->interpolation_mode); + + shader_addline(buffer, "void main()\n{\n"); + + if (reg_maps->input_rel_addressing) + { + unsigned int highest_input_register = wined3d_log2i(reg_maps->input_registers); + shader_addline(buffer, "vec4 vs_in[%u];\n", highest_input_register + 1); + for (i = 0; i < shader->input_signature.element_count; ++i) + { + const struct wined3d_shader_signature_element *e = &shader->input_signature.elements[i]; + shader_addline(buffer, "vs_in[%u] = vs_in%u;\n", e->register_idx, e->register_idx); + } + } + + if (FAILED(shader_generate_code(shader, buffer, reg_maps, &priv_ctx, NULL, NULL))) + return 0; + + /* In SM4+ the shader epilogue is generated by the "ret" instruction. */ + if (reg_maps->shader_version.major < 4) + shader_glsl_generate_vs_epilogue(gl_info, buffer, shader, args); + + shader_addline(buffer, "}\n"); + + shader_id = GL_EXTCALL(glCreateShader(GL_VERTEX_SHADER)); + TRACE("Compiling shader object %u.\n", shader_id); + shader_glsl_compile(gl_info, shader_id, buffer->buffer); + + return shader_id; +} + +static void shader_glsl_generate_default_control_point_phase(const struct wined3d_shader *shader, + struct wined3d_string_buffer *buffer, const struct wined3d_shader_reg_maps *reg_maps) +{ + const struct wined3d_shader_signature *output_signature = &shader->output_signature; + char reg_mask[6]; + unsigned int i; + + for (i = 0; i < output_signature->element_count; ++i) + { + const struct wined3d_shader_signature_element *output = &output_signature->elements[i]; + + shader_glsl_write_mask_to_str(output->mask, reg_mask); + shader_addline(buffer, "shader_out[gl_InvocationID].reg[%u]%s = shader_in[gl_InvocationID].reg[%u]%s;\n", + output->register_idx, reg_mask, output->register_idx, reg_mask); + } +} + +static HRESULT shader_glsl_generate_shader_phase(const struct wined3d_shader *shader, + struct wined3d_string_buffer *buffer, const struct wined3d_shader_reg_maps *reg_maps, + struct shader_glsl_ctx_priv *priv_ctx, const struct wined3d_shader_phase *phase, + const char *phase_name, unsigned phase_idx) +{ + unsigned int i; + HRESULT hr; + + shader_addline(buffer, "void hs_%s_phase%u(%s)\n{\n", + phase_name, phase_idx, phase->instance_count ? "int phase_instance_id" : ""); + for (i = 0; i < phase->temporary_count; ++i) + shader_addline(buffer, "vec4 R%u;\n", i); + hr = shader_generate_code(shader, buffer, reg_maps, priv_ctx, phase->start, phase->end); + shader_addline(buffer, "}\n"); + return hr; +} + +static void shader_glsl_generate_shader_phase_invocation(struct wined3d_string_buffer *buffer, + const struct wined3d_shader_phase *phase, const char *phase_name, unsigned int phase_idx) +{ + if (phase->instance_count) + { + shader_addline(buffer, "for (int i = 0; i < %u; ++i)\n{\n", phase->instance_count); + shader_addline(buffer, "hs_%s_phase%u(i);\n", phase_name, phase_idx); + shader_addline(buffer, "}\n"); + } + else + { + shader_addline(buffer, "hs_%s_phase%u();\n", phase_name, phase_idx); + } +} + +static GLuint shader_glsl_generate_hull_shader(const struct wined3d_context_gl *context_gl, + struct shader_glsl_priv *priv, const struct wined3d_shader *shader) +{ + struct wined3d_string_buffer_list *string_buffers = &priv->string_buffers; + const struct wined3d_shader_reg_maps *reg_maps = &shader->reg_maps; + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + struct wined3d_string_buffer *buffer = &priv->shader_buffer; + const struct wined3d_hull_shader *hs = &shader->u.hs; + const struct wined3d_shader_phase *phase; + struct shader_glsl_ctx_priv priv_ctx; + GLuint shader_id; + unsigned int i; + + memset(&priv_ctx, 0, sizeof(priv_ctx)); + priv_ctx.gl_info = gl_info; + priv_ctx.string_buffers = string_buffers; + + shader_glsl_add_version_declaration(buffer, gl_info); + + shader_glsl_enable_extensions(buffer, gl_info); + shader_addline(buffer, "#extension GL_ARB_tessellation_shader : enable\n"); + + shader_generate_glsl_declarations(context_gl, buffer, shader, reg_maps, &priv_ctx); + + shader_addline(buffer, "layout(vertices = %u) out;\n", hs->output_vertex_count); + + shader_addline(buffer, "in shader_in_out { vec4 reg[%u]; } shader_in[];\n", shader->limits->packed_input); + shader_addline(buffer, "out shader_in_out { vec4 reg[%u]; } shader_out[];\n", shader->limits->packed_output); + + shader_glsl_generate_patch_constant_setup(buffer, &shader->patch_constant_signature, FALSE); + + if (hs->phases.control_point) + { + shader_addline(buffer, "void setup_hs_output(in vec4 outputs[%u])\n{\n", + shader->limits->packed_output); + shader_glsl_setup_sm4_shader_output(priv, shader->limits->packed_output, &shader->output_signature, + &shader->reg_maps, "shader_out[gl_InvocationID]", FALSE); + shader_addline(buffer, "}\n"); + } + + shader_addline(buffer, "void hs_control_point_phase()\n{\n"); + if ((phase = hs->phases.control_point)) + { + for (i = 0; i < phase->temporary_count; ++i) + shader_addline(buffer, "vec4 R%u;\n", i); + if (FAILED(shader_generate_code(shader, buffer, reg_maps, &priv_ctx, phase->start, phase->end))) + return 0; + shader_addline(buffer, "setup_hs_output(hs_out);\n"); + } + else + { + shader_glsl_generate_default_control_point_phase(shader, buffer, reg_maps); + } + shader_addline(buffer, "}\n"); + + for (i = 0; i < hs->phases.fork_count; ++i) + { + if (FAILED(shader_glsl_generate_shader_phase(shader, buffer, reg_maps, &priv_ctx, + &hs->phases.fork[i], "fork", i))) + return 0; + } + + for (i = 0; i < hs->phases.join_count; ++i) + { + if (FAILED(shader_glsl_generate_shader_phase(shader, buffer, reg_maps, &priv_ctx, + &hs->phases.join[i], "join", i))) + return 0; + } + + shader_addline(buffer, "void main()\n{\n"); + shader_addline(buffer, "hs_control_point_phase();\n"); + if (reg_maps->vocp) + shader_addline(buffer, "barrier();\n"); + for (i = 0; i < hs->phases.fork_count; ++i) + shader_glsl_generate_shader_phase_invocation(buffer, &hs->phases.fork[i], "fork", i); + for (i = 0; i < hs->phases.join_count; ++i) + shader_glsl_generate_shader_phase_invocation(buffer, &hs->phases.join[i], "join", i); + shader_addline(buffer, "setup_patch_constant_output();\n"); + shader_addline(buffer, "}\n"); + + shader_id = GL_EXTCALL(glCreateShader(GL_TESS_CONTROL_SHADER)); + TRACE("Compiling shader object %u.\n", shader_id); + shader_glsl_compile(gl_info, shader_id, buffer->buffer); + + return shader_id; +} + +static void shader_glsl_generate_ds_epilogue(const struct wined3d_gl_info *gl_info, + struct wined3d_string_buffer *buffer, const struct wined3d_shader *shader, + const struct ds_compile_args *args) +{ + shader_addline(buffer, "setup_ds_output(ds_out);\n"); + + if (args->next_shader_type == WINED3D_SHADER_TYPE_PIXEL && !gl_info->supported[ARB_CLIP_CONTROL]) + shader_glsl_fixup_position(buffer, FALSE); +} + +static GLuint shader_glsl_generate_domain_shader(const struct wined3d_context_gl *context_gl, + struct shader_glsl_priv *priv, const struct wined3d_shader *shader, const struct ds_compile_args *args) +{ + struct wined3d_string_buffer_list *string_buffers = &priv->string_buffers; + const struct wined3d_shader_reg_maps *reg_maps = &shader->reg_maps; + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + struct wined3d_string_buffer *buffer = &priv->shader_buffer; + struct shader_glsl_ctx_priv priv_ctx; + GLuint shader_id; + + memset(&priv_ctx, 0, sizeof(priv_ctx)); + priv_ctx.gl_info = gl_info; + priv_ctx.cur_ds_args = args; + priv_ctx.string_buffers = string_buffers; + + shader_glsl_add_version_declaration(buffer, gl_info); + + shader_glsl_enable_extensions(buffer, gl_info); + shader_addline(buffer, "#extension GL_ARB_tessellation_shader : enable\n"); + + shader_generate_glsl_declarations(context_gl, buffer, shader, reg_maps, &priv_ctx); + + shader_addline(buffer, "layout("); + switch (shader->u.ds.tessellator_domain) + { + case WINED3D_TESSELLATOR_DOMAIN_LINE: + shader_addline(buffer, "isolines"); + break; + case WINED3D_TESSELLATOR_DOMAIN_QUAD: + shader_addline(buffer, "quads"); + break; + case WINED3D_TESSELLATOR_DOMAIN_TRIANGLE: + shader_addline(buffer, "triangles"); + break; + } + switch (args->tessellator_output_primitive) + { + case WINED3D_TESSELLATOR_OUTPUT_TRIANGLE_CW: + if (args->render_offscreen) + shader_addline(buffer, ", ccw"); + else + shader_addline(buffer, ", cw"); + break; + case WINED3D_TESSELLATOR_OUTPUT_TRIANGLE_CCW: + if (args->render_offscreen) + shader_addline(buffer, ", cw"); + else + shader_addline(buffer, ", ccw"); + break; + case WINED3D_TESSELLATOR_OUTPUT_POINT: + shader_addline(buffer, ", point_mode"); + break; + case WINED3D_TESSELLATOR_OUTPUT_LINE: + break; + } + switch (args->tessellator_partitioning) + { + case WINED3D_TESSELLATOR_PARTITIONING_FRACTIONAL_ODD: + shader_addline(buffer, ", fractional_odd_spacing"); + break; + case WINED3D_TESSELLATOR_PARTITIONING_FRACTIONAL_EVEN: + shader_addline(buffer, ", fractional_even_spacing"); + break; + case WINED3D_TESSELLATOR_PARTITIONING_INTEGER: + case WINED3D_TESSELLATOR_PARTITIONING_POW2: + shader_addline(buffer, ", equal_spacing"); + break; + } + shader_addline(buffer, ") in;\n"); + + shader_addline(buffer, "in shader_in_out { vec4 reg[%u]; } shader_in[];\n", shader->limits->packed_input); + + if (args->next_shader_type == WINED3D_SHADER_TYPE_PIXEL && !gl_info->supported[ARB_CLIP_CONTROL]) + shader_addline(buffer, "uniform vec4 pos_fixup;\n"); + + shader_glsl_generate_sm4_output_setup(priv, shader, args->output_count, gl_info, + args->next_shader_type == WINED3D_SHADER_TYPE_PIXEL, args->interpolation_mode); + shader_glsl_generate_patch_constant_setup(buffer, &shader->patch_constant_signature, TRUE); + + shader_addline(buffer, "void main()\n{\n"); + shader_addline(buffer, "setup_patch_constant_input();\n"); + + if (FAILED(shader_generate_code(shader, buffer, reg_maps, &priv_ctx, NULL, NULL))) + return 0; + + shader_addline(buffer, "}\n"); + + shader_id = GL_EXTCALL(glCreateShader(GL_TESS_EVALUATION_SHADER)); + TRACE("Compiling shader object %u.\n", shader_id); + shader_glsl_compile(gl_info, shader_id, buffer->buffer); + + return shader_id; +} + +/* Context activation is done by the caller. */ +static GLuint shader_glsl_generate_geometry_shader(const struct wined3d_context_gl *context_gl, + struct shader_glsl_priv *priv, const struct wined3d_shader *shader, const struct gs_compile_args *args) +{ + struct wined3d_string_buffer_list *string_buffers = &priv->string_buffers; + const struct wined3d_shader_reg_maps *reg_maps = &shader->reg_maps; + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + struct wined3d_string_buffer *buffer = &priv->shader_buffer; + const struct wined3d_shader_signature_element *output; + enum wined3d_primitive_type primitive_type; + struct shader_glsl_ctx_priv priv_ctx; + unsigned int max_vertices; + unsigned int i, j; + GLuint shader_id; + + memset(&priv_ctx, 0, sizeof(priv_ctx)); + priv_ctx.gl_info = gl_info; + priv_ctx.string_buffers = string_buffers; + + shader_glsl_add_version_declaration(buffer, gl_info); + + shader_glsl_enable_extensions(buffer, gl_info); + + shader_generate_glsl_declarations(context_gl, buffer, shader, reg_maps, &priv_ctx); + + primitive_type = shader->u.gs.input_type ? shader->u.gs.input_type : args->primitive_type; + shader_addline(buffer, "layout(%s", glsl_primitive_type_from_d3d(primitive_type)); + if (shader->u.gs.instance_count > 1) + shader_addline(buffer, ", invocations = %u", shader->u.gs.instance_count); + shader_addline(buffer, ") in;\n"); + + primitive_type = shader->u.gs.output_type ? shader->u.gs.output_type : args->primitive_type; + if (!(max_vertices = shader->u.gs.vertices_out)) + { + switch (args->primitive_type) + { + case WINED3D_PT_POINTLIST: + max_vertices = 1; + break; + case WINED3D_PT_LINELIST: + max_vertices = 2; + break; + case WINED3D_PT_TRIANGLELIST: + max_vertices = 3; + break; + default: + FIXME("Unhandled primitive type %s.\n", debug_d3dprimitivetype(args->primitive_type)); + break; + } + } + shader_addline(buffer, "layout(%s, max_vertices = %u) out;\n", + glsl_primitive_type_from_d3d(primitive_type), max_vertices); + shader_addline(buffer, "in shader_in_out { vec4 reg[%u]; } shader_in[];\n", shader->limits->packed_input); + + if (!gl_info->supported[ARB_CLIP_CONTROL]) + { + shader_addline(buffer, "uniform vec4 pos_fixup"); + if (reg_maps->viewport_array) + shader_addline(buffer, "[%u]", WINED3D_MAX_VIEWPORTS); + shader_addline(buffer, ";\n"); + } + + if (is_rasterization_disabled(shader)) + { + shader_glsl_generate_stream_output_setup(buffer, shader); + } + else + { + shader_glsl_generate_sm4_output_setup(priv, shader, args->output_count, + gl_info, TRUE, args->interpolation_mode); + } + + shader_addline(buffer, "void main()\n{\n"); + if (shader->function) + { + if (FAILED(shader_generate_code(shader, buffer, reg_maps, &priv_ctx, NULL, NULL))) + return 0; + } + else + { + for (i = 0; i < max_vertices; ++i) + { + for (j = 0; j < shader->output_signature.element_count; ++j) + { + output = &shader->output_signature.elements[j]; + shader_addline(buffer, "gs_out[%u] = shader_in[%u].reg[%u];\n", + output->register_idx, i, output->register_idx); + } + shader_addline(buffer, "setup_gs_output(gs_out);\n"); + if (!gl_info->supported[ARB_CLIP_CONTROL]) + shader_glsl_fixup_position(buffer, FALSE); + shader_addline(buffer, "EmitVertex();\n"); + } + } + shader_addline(buffer, "}\n"); + + shader_id = GL_EXTCALL(glCreateShader(GL_GEOMETRY_SHADER)); + TRACE("Compiling shader object %u.\n", shader_id); + shader_glsl_compile(gl_info, shader_id, buffer->buffer); + + return shader_id; +} + +static void shader_glsl_generate_shader_epilogue(const struct wined3d_shader_context *ctx) +{ + const struct shader_glsl_ctx_priv *priv = ctx->backend_data; + const struct wined3d_gl_info *gl_info = priv->gl_info; + struct wined3d_string_buffer *buffer = ctx->buffer; + const struct wined3d_shader *shader = ctx->shader; + + switch (shader->reg_maps.shader_version.type) + { + case WINED3D_SHADER_TYPE_PIXEL: + shader_glsl_generate_ps_epilogue(gl_info, buffer, shader, priv->cur_ps_args, priv->string_buffers); + break; + case WINED3D_SHADER_TYPE_VERTEX: + shader_glsl_generate_vs_epilogue(gl_info, buffer, shader, priv->cur_vs_args); + break; + case WINED3D_SHADER_TYPE_DOMAIN: + shader_glsl_generate_ds_epilogue(gl_info, buffer, shader, priv->cur_ds_args); + break; + case WINED3D_SHADER_TYPE_GEOMETRY: + case WINED3D_SHADER_TYPE_COMPUTE: + break; + default: + FIXME("Unhandled shader type %#x.\n", shader->reg_maps.shader_version.type); + break; + } +} + +/* Context activation is done by the caller. */ +static GLuint shader_glsl_generate_compute_shader(const struct wined3d_context_gl *context_gl, + struct wined3d_string_buffer *buffer, struct wined3d_string_buffer_list *string_buffers, + const struct wined3d_shader *shader) +{ + const struct wined3d_shader_thread_group_size *thread_group_size = &shader->u.cs.thread_group_size; + const struct wined3d_shader_reg_maps *reg_maps = &shader->reg_maps; + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + struct shader_glsl_ctx_priv priv_ctx; + GLuint shader_id; + unsigned int i; + + memset(&priv_ctx, 0, sizeof(priv_ctx)); + priv_ctx.gl_info = gl_info; + priv_ctx.string_buffers = string_buffers; + + shader_glsl_add_version_declaration(buffer, gl_info); + + shader_glsl_enable_extensions(buffer, gl_info); + shader_addline(buffer, "#extension GL_ARB_compute_shader : enable\n"); + + shader_generate_glsl_declarations(context_gl, buffer, shader, reg_maps, &priv_ctx); + + for (i = 0; i < reg_maps->tgsm_count; ++i) + { + if (reg_maps->tgsm[i].size) + shader_addline(buffer, "shared uint cs_g%u[%u];\n", i, reg_maps->tgsm[i].size); + } + + shader_addline(buffer, "layout(local_size_x = %u, local_size_y = %u, local_size_z = %u) in;\n", + thread_group_size->x, thread_group_size->y, thread_group_size->z); + + shader_addline(buffer, "void main()\n{\n"); + shader_generate_code(shader, buffer, reg_maps, &priv_ctx, NULL, NULL); + shader_addline(buffer, "}\n"); + + shader_id = GL_EXTCALL(glCreateShader(GL_COMPUTE_SHADER)); + TRACE("Compiling shader object %u.\n", shader_id); + shader_glsl_compile(gl_info, shader_id, buffer->buffer); + + return shader_id; +} + +static GLuint find_glsl_fragment_shader(const struct wined3d_context_gl *context_gl, + struct wined3d_string_buffer *buffer, struct wined3d_string_buffer_list *string_buffers, + struct wined3d_shader *shader, + const struct ps_compile_args *args, const struct ps_np2fixup_info **np2fixup_info) +{ + struct glsl_ps_compiled_shader *gl_shaders, *new_array; + struct glsl_shader_private *shader_data; + struct ps_np2fixup_info *np2fixup; + UINT i; + DWORD new_size; + GLuint ret; + + if (!shader->backend_data) + { + if (!(shader->backend_data = heap_alloc_zero(sizeof(*shader_data)))) + { + ERR("Failed to allocate backend data.\n"); + return 0; + } + } + shader_data = shader->backend_data; + gl_shaders = shader_data->gl_shaders.ps; + + /* Usually we have very few GL shaders for each d3d shader(just 1 or maybe 2), + * so a linear search is more performant than a hashmap or a binary search + * (cache coherency etc) + */ + for (i = 0; i < shader_data->num_gl_shaders; ++i) + { + if (!memcmp(&gl_shaders[i].args, args, sizeof(*args))) + { + if (args->np2_fixup) + *np2fixup_info = &gl_shaders[i].np2fixup; + return gl_shaders[i].id; + } + } + + TRACE("No matching GL shader found for shader %p, compiling a new shader.\n", shader); + if (shader_data->shader_array_size == shader_data->num_gl_shaders) + { + if (shader_data->num_gl_shaders) + { + new_size = shader_data->shader_array_size + max(1, shader_data->shader_array_size / 2); + new_array = heap_realloc(shader_data->gl_shaders.ps, new_size * sizeof(*gl_shaders)); + } + else + { + new_array = heap_alloc(sizeof(*gl_shaders)); + new_size = 1; + } + + if(!new_array) { + ERR("Out of memory\n"); + return 0; + } + shader_data->gl_shaders.ps = new_array; + shader_data->shader_array_size = new_size; + gl_shaders = new_array; + } + + gl_shaders[shader_data->num_gl_shaders].args = *args; + + np2fixup = &gl_shaders[shader_data->num_gl_shaders].np2fixup; + memset(np2fixup, 0, sizeof(*np2fixup)); + *np2fixup_info = args->np2_fixup ? np2fixup : NULL; + + string_buffer_clear(buffer); + ret = shader_glsl_generate_fragment_shader(context_gl, buffer, string_buffers, shader, args, np2fixup); + gl_shaders[shader_data->num_gl_shaders++].id = ret; + + return ret; +} + +static BOOL vs_args_equal(const struct vs_compile_args *stored, const struct vs_compile_args *new, + const DWORD use_map) +{ + if ((stored->swizzle_map & use_map) != new->swizzle_map) + return FALSE; + if ((stored->clip_enabled) != new->clip_enabled) + return FALSE; + if (stored->point_size != new->point_size) + return FALSE; + if (stored->per_vertex_point_size != new->per_vertex_point_size) + return FALSE; + if (stored->flatshading != new->flatshading) + return FALSE; + if (stored->next_shader_type != new->next_shader_type) + return FALSE; + if (stored->next_shader_input_count != new->next_shader_input_count) + return FALSE; + if (stored->fog_src != new->fog_src) + return FALSE; + return !memcmp(stored->interpolation_mode, new->interpolation_mode, sizeof(new->interpolation_mode)); +} + +static GLuint find_glsl_vertex_shader(const struct wined3d_context_gl *context_gl, + struct shader_glsl_priv *priv, struct wined3d_shader *shader, const struct vs_compile_args *args) +{ + struct glsl_vs_compiled_shader *gl_shaders, *new_array; + uint32_t use_map = context_gl->c.stream_info.use_map; + struct glsl_shader_private *shader_data; + unsigned int i, new_size; + GLuint ret; + + if (!shader->backend_data) + { + if (!(shader->backend_data = heap_alloc_zero(sizeof(*shader_data)))) + { + ERR("Failed to allocate backend data.\n"); + return 0; + } + } + shader_data = shader->backend_data; + gl_shaders = shader_data->gl_shaders.vs; + + /* Usually we have very few GL shaders for each d3d shader(just 1 or maybe 2), + * so a linear search is more performant than a hashmap or a binary search + * (cache coherency etc) + */ + for (i = 0; i < shader_data->num_gl_shaders; ++i) + { + if (vs_args_equal(&gl_shaders[i].args, args, use_map)) + return gl_shaders[i].id; + } + + TRACE("No matching GL shader found for shader %p, compiling a new shader.\n", shader); + + if (shader_data->shader_array_size == shader_data->num_gl_shaders) + { + if (shader_data->num_gl_shaders) + { + new_size = shader_data->shader_array_size + max(1, shader_data->shader_array_size / 2); + new_array = heap_realloc(shader_data->gl_shaders.vs, new_size * sizeof(*gl_shaders)); + } + else + { + new_array = heap_alloc(sizeof(*gl_shaders)); + new_size = 1; + } + + if(!new_array) { + ERR("Out of memory\n"); + return 0; + } + shader_data->gl_shaders.vs = new_array; + shader_data->shader_array_size = new_size; + gl_shaders = new_array; + } + + gl_shaders[shader_data->num_gl_shaders].args = *args; + + string_buffer_clear(&priv->shader_buffer); + ret = shader_glsl_generate_vertex_shader(context_gl, priv, shader, args); + gl_shaders[shader_data->num_gl_shaders++].id = ret; + + return ret; +} + +static GLuint find_glsl_hull_shader(const struct wined3d_context_gl *context_gl, + struct shader_glsl_priv *priv, struct wined3d_shader *shader) +{ + struct glsl_hs_compiled_shader *gl_shaders, *new_array; + struct glsl_shader_private *shader_data; + unsigned int new_size; + GLuint ret; + + if (!shader->backend_data) + { + if (!(shader->backend_data = heap_alloc_zero(sizeof(*shader_data)))) + { + ERR("Failed to allocate backend data.\n"); + return 0; + } + } + shader_data = shader->backend_data; + gl_shaders = shader_data->gl_shaders.hs; + + if (shader_data->num_gl_shaders > 0) + { + assert(shader_data->num_gl_shaders == 1); + return gl_shaders[0].id; + } + + TRACE("No matching GL shader found for shader %p, compiling a new shader.\n", shader); + + assert(!shader_data->gl_shaders.hs); + new_size = 1; + if (!(new_array = heap_alloc(sizeof(*new_array)))) + { + ERR("Failed to allocate GL shaders array.\n"); + return 0; + } + shader_data->gl_shaders.hs = new_array; + shader_data->shader_array_size = new_size; + gl_shaders = new_array; + + string_buffer_clear(&priv->shader_buffer); + ret = shader_glsl_generate_hull_shader(context_gl, priv, shader); + gl_shaders[shader_data->num_gl_shaders++].id = ret; + + return ret; +} + +static GLuint find_glsl_domain_shader(const struct wined3d_context_gl *context_gl, + struct shader_glsl_priv *priv, struct wined3d_shader *shader, const struct ds_compile_args *args) +{ + struct glsl_ds_compiled_shader *gl_shaders, *new_array; + struct glsl_shader_private *shader_data; + unsigned int i, new_size; + GLuint ret; + + if (!shader->backend_data) + { + if (!(shader->backend_data = heap_alloc_zero(sizeof(*shader_data)))) + { + ERR("Failed to allocate backend data.\n"); + return 0; + } + } + shader_data = shader->backend_data; + gl_shaders = shader_data->gl_shaders.ds; + + for (i = 0; i < shader_data->num_gl_shaders; ++i) + { + if (!memcmp(&gl_shaders[i].args, args, sizeof(*args))) + return gl_shaders[i].id; + } + + TRACE("No matching GL shader found for shader %p, compiling a new shader.\n", shader); + + if (shader_data->num_gl_shaders) + { + new_size = shader_data->shader_array_size + 1; + new_array = heap_realloc(shader_data->gl_shaders.ds, new_size * sizeof(*new_array)); + } + else + { + new_array = heap_alloc(sizeof(*new_array)); + new_size = 1; + } + + if (!new_array) + { + ERR("Failed to allocate GL shaders array.\n"); + return 0; + } + shader_data->gl_shaders.ds = new_array; + shader_data->shader_array_size = new_size; + gl_shaders = new_array; + + string_buffer_clear(&priv->shader_buffer); + ret = shader_glsl_generate_domain_shader(context_gl, priv, shader, args); + gl_shaders[shader_data->num_gl_shaders].args = *args; + gl_shaders[shader_data->num_gl_shaders++].id = ret; + + return ret; +} + +static GLuint find_glsl_geometry_shader(const struct wined3d_context_gl *context_gl, + struct shader_glsl_priv *priv, struct wined3d_shader *shader, const struct gs_compile_args *args) +{ + struct glsl_gs_compiled_shader *gl_shaders, *new_array; + struct glsl_shader_private *shader_data; + unsigned int i, new_size; + GLuint ret; + + if (!shader->backend_data) + { + if (!(shader->backend_data = heap_alloc_zero(sizeof(*shader_data)))) + { + ERR("Failed to allocate backend data.\n"); + return 0; + } + } + shader_data = shader->backend_data; + gl_shaders = shader_data->gl_shaders.gs; + + for (i = 0; i < shader_data->num_gl_shaders; ++i) + { + if (!memcmp(&gl_shaders[i].args, args, sizeof(*args))) + return gl_shaders[i].id; + } + + TRACE("No matching GL shader found for shader %p, compiling a new shader.\n", shader); + + if (shader_data->num_gl_shaders) + { + new_size = shader_data->shader_array_size + 1; + new_array = heap_realloc(shader_data->gl_shaders.gs, new_size * sizeof(*new_array)); + } + else + { + new_array = heap_alloc(sizeof(*new_array)); + new_size = 1; + } + + if (!new_array) + { + ERR("Failed to allocate GL shaders array.\n"); + return 0; + } + shader_data->gl_shaders.gs = new_array; + shader_data->shader_array_size = new_size; + gl_shaders = new_array; + + string_buffer_clear(&priv->shader_buffer); + ret = shader_glsl_generate_geometry_shader(context_gl, priv, shader, args); + gl_shaders[shader_data->num_gl_shaders].args = *args; + gl_shaders[shader_data->num_gl_shaders++].id = ret; + + return ret; +} + +static const char *shader_glsl_ffp_mcs(enum wined3d_material_color_source mcs, const char *material) +{ + switch (mcs) + { + case WINED3D_MCS_MATERIAL: + return material; + case WINED3D_MCS_COLOR1: + return "ffp_attrib_diffuse"; + case WINED3D_MCS_COLOR2: + return "ffp_attrib_specular"; + default: + ERR("Invalid material color source %#x.\n", mcs); + return ""; + } +} + +static void shader_glsl_ffp_vertex_lighting_footer(struct wined3d_string_buffer *buffer, + const struct wined3d_ffp_vs_settings *settings, unsigned int idx, BOOL legacy_lighting) +{ + shader_addline(buffer, "diffuse += clamp(dot(dir, normal), 0.0, 1.0)" + " * ffp_light[%u].diffuse.xyz * att;\n", idx); + if (settings->localviewer) + shader_addline(buffer, "t = dot(normal, normalize(dir - normalize(ec_pos.xyz)));\n"); + else + shader_addline(buffer, "t = dot(normal, normalize(dir + vec3(0.0, 0.0, -1.0)));\n"); + shader_addline(buffer, "if (dot(dir, normal) > 0.0 && t > 0.0%s) specular +=" + " pow(t, ffp_material.shininess) * ffp_light[%u].specular * att;\n", + legacy_lighting ? " && ffp_material.shininess > 0.0" : "", idx); +} + +static void shader_glsl_ffp_vertex_lighting(struct wined3d_string_buffer *buffer, + const struct wined3d_ffp_vs_settings *settings, BOOL legacy_lighting) +{ + const char *diffuse, *specular, *emissive, *ambient; + unsigned int i, idx; + + if (!settings->lighting) + { + shader_addline(buffer, "ffp_varying_diffuse = ffp_attrib_diffuse;\n"); + shader_addline(buffer, "ffp_varying_specular = ffp_attrib_specular;\n"); + return; + } + + shader_addline(buffer, "vec3 ambient = ffp_light_ambient;\n"); + shader_addline(buffer, "vec3 diffuse = vec3(0.0);\n"); + shader_addline(buffer, "vec4 specular = vec4(0.0);\n"); + shader_addline(buffer, "vec3 dir, dst;\n"); + shader_addline(buffer, "float att, t;\n"); + + ambient = shader_glsl_ffp_mcs(settings->ambient_source, "ffp_material.ambient"); + diffuse = shader_glsl_ffp_mcs(settings->diffuse_source, "ffp_material.diffuse"); + specular = shader_glsl_ffp_mcs(settings->specular_source, "ffp_material.specular"); + emissive = shader_glsl_ffp_mcs(settings->emissive_source, "ffp_material.emissive"); + + idx = 0; + for (i = 0; i < settings->point_light_count; ++i, ++idx) + { + shader_addline(buffer, "dir = ffp_light[%u].position.xyz - ec_pos.xyz;\n", idx); + shader_addline(buffer, "dst.z = dot(dir, dir);\n"); + shader_addline(buffer, "dst.y = sqrt(dst.z);\n"); + shader_addline(buffer, "dst.x = 1.0;\n"); + if (legacy_lighting) + { + shader_addline(buffer, "dst.y = (ffp_light[%u].range - dst.y) / ffp_light[%u].range;\n", idx, idx); + shader_addline(buffer, "dst.z = dst.y * dst.y;\n"); + shader_addline(buffer, "if (dst.y > 0.0)\n{\n"); + } + else + { + shader_addline(buffer, "if (dst.y <= ffp_light[%u].range)\n{\n", idx); + } + shader_addline(buffer, "att = dot(dst.xyz, vec3(ffp_light[%u].c_att," + " ffp_light[%u].l_att, ffp_light[%u].q_att));\n", idx, idx, idx); + if (!legacy_lighting) + shader_addline(buffer, "att = 1.0 / att;\n"); + shader_addline(buffer, "ambient += ffp_light[%u].ambient.xyz * att;\n", idx); + if (!settings->normal) + { + shader_addline(buffer, "}\n"); + continue; + } + shader_addline(buffer, "dir = normalize(dir);\n"); + shader_glsl_ffp_vertex_lighting_footer(buffer, settings, idx, legacy_lighting); + shader_addline(buffer, "}\n"); + } + + for (i = 0; i < settings->spot_light_count; ++i, ++idx) + { + shader_addline(buffer, "dir = ffp_light[%u].position.xyz - ec_pos.xyz;\n", idx); + shader_addline(buffer, "dst.z = dot(dir, dir);\n"); + shader_addline(buffer, "dst.y = sqrt(dst.z);\n"); + shader_addline(buffer, "dst.x = 1.0;\n"); + if (legacy_lighting) + { + shader_addline(buffer, "dst.y = (ffp_light[%u].range - dst.y) / ffp_light[%u].range;\n", idx, idx); + shader_addline(buffer, "dst.z = dst.y * dst.y;\n"); + shader_addline(buffer, "if (dst.y > 0.0)\n{\n"); + } + else + { + shader_addline(buffer, "if (dst.y <= ffp_light[%u].range)\n{\n", idx); + } + shader_addline(buffer, "dir = normalize(dir);\n"); + shader_addline(buffer, "t = dot(-dir, normalize(ffp_light[%u].direction));\n", idx); + shader_addline(buffer, "if (t > ffp_light[%u].cos_htheta) att = 1.0;\n", idx); + shader_addline(buffer, "else if (t <= ffp_light[%u].cos_hphi) att = 0.0;\n", idx); + shader_addline(buffer, "else att = pow((t - ffp_light[%u].cos_hphi)" + " / (ffp_light[%u].cos_htheta - ffp_light[%u].cos_hphi), ffp_light[%u].falloff);\n", + idx, idx, idx, idx); + if (legacy_lighting) + shader_addline(buffer, "att *= dot(dst.xyz, vec3(ffp_light[%u].c_att," + " ffp_light[%u].l_att, ffp_light[%u].q_att));\n", + idx, idx, idx); + else + shader_addline(buffer, "att /= dot(dst.xyz, vec3(ffp_light[%u].c_att," + " ffp_light[%u].l_att, ffp_light[%u].q_att));\n", + idx, idx, idx); + shader_addline(buffer, "ambient += ffp_light[%u].ambient.xyz * att;\n", idx); + if (!settings->normal) + { + shader_addline(buffer, "}\n"); + continue; + } + shader_glsl_ffp_vertex_lighting_footer(buffer, settings, idx, legacy_lighting); + shader_addline(buffer, "}\n"); + } + + for (i = 0; i < settings->directional_light_count; ++i, ++idx) + { + shader_addline(buffer, "ambient += ffp_light[%u].ambient.xyz;\n", idx); + if (!settings->normal) + continue; + shader_addline(buffer, "att = 1.0;\n"); + shader_addline(buffer, "dir = normalize(ffp_light[%u].direction.xyz);\n", idx); + shader_glsl_ffp_vertex_lighting_footer(buffer, settings, idx, legacy_lighting); + } + + for (i = 0; i < settings->parallel_point_light_count; ++i, ++idx) + { + shader_addline(buffer, "ambient += ffp_light[%u].ambient.xyz;\n", idx); + if (!settings->normal) + continue; + shader_addline(buffer, "att = 1.0;\n"); + shader_addline(buffer, "dir = normalize(ffp_light[%u].position.xyz);\n", idx); + shader_glsl_ffp_vertex_lighting_footer(buffer, settings, idx, legacy_lighting); + } + + shader_addline(buffer, "ffp_varying_diffuse.xyz = %s.xyz * ambient + %s.xyz * diffuse + %s.xyz;\n", + ambient, diffuse, emissive); + shader_addline(buffer, "ffp_varying_diffuse.w = %s.w;\n", diffuse); + shader_addline(buffer, "ffp_varying_specular = %s * specular;\n", specular); +} + +/* Context activation is done by the caller. */ +static GLuint shader_glsl_generate_ffp_vertex_shader(struct shader_glsl_priv *priv, + const struct wined3d_ffp_vs_settings *settings, const struct wined3d_gl_info *gl_info) +{ + static const struct attrib_info + { + const char type[6]; + const char name[24]; + } + attrib_info[] = + { + {"vec4", "ffp_attrib_position"}, /* WINED3D_FFP_POSITION */ + {"vec4", "ffp_attrib_blendweight"}, /* WINED3D_FFP_BLENDWEIGHT */ + /* TODO: Indexed vertex blending */ + {"float", ""}, /* WINED3D_FFP_BLENDINDICES */ + {"vec3", "ffp_attrib_normal"}, /* WINED3D_FFP_NORMAL */ + {"float", "ffp_attrib_psize"}, /* WINED3D_FFP_PSIZE */ + {"vec4", "ffp_attrib_diffuse"}, /* WINED3D_FFP_DIFFUSE */ + {"vec4", "ffp_attrib_specular"}, /* WINED3D_FFP_SPECULAR */ + }; + const BOOL legacy_syntax = needs_legacy_glsl_syntax(gl_info); + struct wined3d_string_buffer *buffer = &priv->shader_buffer; + BOOL output_legacy_fogcoord = legacy_syntax; + BOOL legacy_lighting = priv->legacy_lighting; + GLuint shader_obj; + unsigned int i; + + string_buffer_clear(buffer); + + shader_glsl_add_version_declaration(buffer, gl_info); + + if (shader_glsl_use_explicit_attrib_location(gl_info)) + shader_addline(buffer, "#extension GL_ARB_explicit_attrib_location : enable\n"); + + for (i = 0; i < WINED3D_FFP_ATTRIBS_COUNT; ++i) + { + const char *type = i < ARRAY_SIZE(attrib_info) ? attrib_info[i].type : "vec4"; + + if (shader_glsl_use_explicit_attrib_location(gl_info)) + shader_addline(buffer, "layout(location = %u) ", i); + shader_addline(buffer, "%s %s vs_in%u;\n", get_attribute_keyword(gl_info), type, i); + } + shader_addline(buffer, "\n"); + + shader_addline(buffer, "uniform mat4 ffp_modelview_matrix[%u];\n", MAX_VERTEX_BLENDS); + shader_addline(buffer, "uniform mat4 ffp_projection_matrix;\n"); + shader_addline(buffer, "uniform mat3 ffp_normal_matrix;\n"); + shader_addline(buffer, "uniform mat4 ffp_texture_matrix[%u];\n", WINED3D_MAX_TEXTURES); + + shader_addline(buffer, "uniform struct\n{\n"); + shader_addline(buffer, " vec4 emissive;\n"); + shader_addline(buffer, " vec4 ambient;\n"); + shader_addline(buffer, " vec4 diffuse;\n"); + shader_addline(buffer, " vec4 specular;\n"); + shader_addline(buffer, " float shininess;\n"); + shader_addline(buffer, "} ffp_material;\n"); + + shader_addline(buffer, "uniform vec3 ffp_light_ambient;\n"); + shader_addline(buffer, "uniform struct\n{\n"); + shader_addline(buffer, " vec4 diffuse;\n"); + shader_addline(buffer, " vec4 specular;\n"); + shader_addline(buffer, " vec4 ambient;\n"); + shader_addline(buffer, " vec4 position;\n"); + shader_addline(buffer, " vec3 direction;\n"); + shader_addline(buffer, " float range;\n"); + shader_addline(buffer, " float falloff;\n"); + shader_addline(buffer, " float c_att;\n"); + shader_addline(buffer, " float l_att;\n"); + shader_addline(buffer, " float q_att;\n"); + shader_addline(buffer, " float cos_htheta;\n"); + shader_addline(buffer, " float cos_hphi;\n"); + shader_addline(buffer, "} ffp_light[%u];\n", WINED3D_MAX_ACTIVE_LIGHTS); + + if (settings->point_size) + { + shader_addline(buffer, "uniform struct\n{\n"); + shader_addline(buffer, " float size;\n"); + shader_addline(buffer, " float size_min;\n"); + shader_addline(buffer, " float size_max;\n"); + shader_addline(buffer, " float c_att;\n"); + shader_addline(buffer, " float l_att;\n"); + shader_addline(buffer, " float q_att;\n"); + shader_addline(buffer, "} ffp_point;\n"); + } + + if (legacy_syntax) + { + shader_addline(buffer, "vec4 ffp_varying_diffuse;\n"); + shader_addline(buffer, "vec4 ffp_varying_specular;\n"); + shader_addline(buffer, "vec4 ffp_varying_texcoord[%u];\n", WINED3D_MAX_TEXTURES); + shader_addline(buffer, "float ffp_varying_fogcoord;\n"); + } + else + { + if (settings->clipping) + shader_addline(buffer, "uniform vec4 clip_planes[%u];\n", gl_info->limits.user_clip_distances); + + declare_out_varying(gl_info, buffer, settings->flatshading, "vec4 ffp_varying_diffuse;\n"); + declare_out_varying(gl_info, buffer, settings->flatshading, "vec4 ffp_varying_specular;\n"); + declare_out_varying(gl_info, buffer, FALSE, "vec4 ffp_varying_texcoord[%u];\n", WINED3D_MAX_TEXTURES); + declare_out_varying(gl_info, buffer, FALSE, "float ffp_varying_fogcoord;\n"); + } + + shader_addline(buffer, "\nvoid main()\n{\n"); + shader_addline(buffer, "float m;\n"); + shader_addline(buffer, "vec3 r;\n"); + + for (i = 0; i < ARRAY_SIZE(attrib_info); ++i) + { + if (attrib_info[i].name[0]) + shader_addline(buffer, "%s %s = vs_in%u%s;\n", attrib_info[i].type, attrib_info[i].name, + i, settings->swizzle_map & (1u << i) ? ".zyxw" : ""); + } + for (i = 0; i < WINED3D_MAX_TEXTURES; ++i) + { + unsigned int coord_idx = settings->texgen[i] & 0x0000ffff; + if ((settings->texgen[i] & 0xffff0000) == WINED3DTSS_TCI_PASSTHRU + && settings->texcoords & (1u << i)) + shader_addline(buffer, "vec4 ffp_attrib_texcoord%u = vs_in%u;\n", i, coord_idx + WINED3D_FFP_TEXCOORD0); + } + + shader_addline(buffer, "ffp_attrib_blendweight[%u] = 1.0;\n", settings->vertexblends); + + if (settings->transformed) + { + shader_addline(buffer, "vec4 ec_pos = vec4(ffp_attrib_position.xyz, 1.0);\n"); + shader_addline(buffer, "gl_Position = ffp_projection_matrix * ec_pos;\n"); + shader_addline(buffer, "if (ffp_attrib_position.w != 0.0) gl_Position /= ffp_attrib_position.w;\n"); + } + else + { + for (i = 0; i < settings->vertexblends; ++i) + shader_addline(buffer, "ffp_attrib_blendweight[%u] -= ffp_attrib_blendweight[%u];\n", settings->vertexblends, i); + + shader_addline(buffer, "vec4 ec_pos = vec4(0.0);\n"); + for (i = 0; i < settings->vertexblends + 1; ++i) + shader_addline(buffer, "ec_pos += ffp_attrib_blendweight[%u] * (ffp_modelview_matrix[%u] * ffp_attrib_position);\n", i, i); + + shader_addline(buffer, "gl_Position = ffp_projection_matrix * ec_pos;\n"); + if (settings->clipping) + { + if (legacy_syntax) + shader_addline(buffer, "gl_ClipVertex = ec_pos;\n"); + else + for (i = 0; i < gl_info->limits.user_clip_distances; ++i) + shader_addline(buffer, "gl_ClipDistance[%u] = dot(ec_pos, clip_planes[%u]);\n", i, i); + } + shader_addline(buffer, "ec_pos /= ec_pos.w;\n"); + } + + shader_addline(buffer, "vec3 normal = vec3(0.0);\n"); + if (settings->normal) + { + if (!settings->vertexblends) + { + shader_addline(buffer, "normal = ffp_normal_matrix * ffp_attrib_normal;\n"); + } + else + { + for (i = 0; i < settings->vertexblends + 1; ++i) + shader_addline(buffer, "normal += ffp_attrib_blendweight[%u] * (mat3(ffp_modelview_matrix[%u]) * ffp_attrib_normal);\n", i, i); + } + + if (settings->normalize) + shader_addline(buffer, "normal = normalize(normal);\n"); + } + + shader_glsl_ffp_vertex_lighting(buffer, settings, legacy_lighting); + if (legacy_syntax) + { + shader_addline(buffer, "gl_FrontColor = ffp_varying_diffuse;\n"); + shader_addline(buffer, "gl_FrontSecondaryColor = ffp_varying_specular;\n"); + } + else + { + shader_addline(buffer, "ffp_varying_diffuse = clamp(ffp_varying_diffuse, 0.0, 1.0);\n"); + shader_addline(buffer, "ffp_varying_specular = clamp(ffp_varying_specular, 0.0, 1.0);\n"); + } + + for (i = 0; i < WINED3D_MAX_TEXTURES; ++i) + { + BOOL output_legacy_texcoord = legacy_syntax; + + switch (settings->texgen[i] & 0xffff0000) + { + case WINED3DTSS_TCI_PASSTHRU: + if (settings->texcoords & (1u << i)) + shader_addline(buffer, "ffp_varying_texcoord[%u] = ffp_texture_matrix[%u] * ffp_attrib_texcoord%u;\n", + i, i, i); + else if (shader_glsl_full_ffp_varyings(gl_info)) + shader_addline(buffer, "ffp_varying_texcoord[%u] = vec4(0.0);\n", i); + else + output_legacy_texcoord = FALSE; + break; + + case WINED3DTSS_TCI_CAMERASPACENORMAL: + shader_addline(buffer, "ffp_varying_texcoord[%u] = ffp_texture_matrix[%u] * vec4(normal, 1.0);\n", i, i); + break; + + case WINED3DTSS_TCI_CAMERASPACEPOSITION: + shader_addline(buffer, "ffp_varying_texcoord[%u] = ffp_texture_matrix[%u] * ec_pos;\n", i, i); + break; + + case WINED3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR: + shader_addline(buffer, "ffp_varying_texcoord[%u] = ffp_texture_matrix[%u]" + " * vec4(reflect(normalize(ec_pos.xyz), normal), 1.0);\n", i, i); + break; + + case WINED3DTSS_TCI_SPHEREMAP: + shader_addline(buffer, "r = reflect(normalize(ec_pos.xyz), normal);\n"); + shader_addline(buffer, "m = 2.0 * length(vec3(r.x, r.y, r.z + 1.0));\n"); + shader_addline(buffer, "ffp_varying_texcoord[%u] = ffp_texture_matrix[%u]" + " * vec4(r.x / m + 0.5, r.y / m + 0.5, 0.0, 1.0);\n", i, i); + break; + + default: + ERR("Unhandled texgen %#x.\n", settings->texgen[i]); + break; + } + if (output_legacy_texcoord) + shader_addline(buffer, "gl_TexCoord[%u] = ffp_varying_texcoord[%u];\n", i, i); + } + + switch (settings->fog_mode) + { + case WINED3D_FFP_VS_FOG_OFF: + output_legacy_fogcoord = FALSE; + break; + + case WINED3D_FFP_VS_FOG_FOGCOORD: + shader_addline(buffer, "ffp_varying_fogcoord = ffp_attrib_specular.w * 255.0;\n"); + break; + + case WINED3D_FFP_VS_FOG_RANGE: + shader_addline(buffer, "ffp_varying_fogcoord = length(ec_pos.xyz);\n"); + break; + + case WINED3D_FFP_VS_FOG_DEPTH: + if (settings->ortho_fog) + { + if (gl_info->supported[ARB_CLIP_CONTROL]) + shader_addline(buffer, "ffp_varying_fogcoord = gl_Position.z;\n"); + else + /* Need to undo the [0.0 - 1.0] -> [-1.0 - 1.0] transformation from D3D to GL coordinates. */ + shader_addline(buffer, "ffp_varying_fogcoord = gl_Position.z * 0.5 + 0.5;\n"); + } + else if (settings->transformed) + { + shader_addline(buffer, "ffp_varying_fogcoord = ec_pos.z;\n"); + } + else + { + shader_addline(buffer, "ffp_varying_fogcoord = abs(ec_pos.z);\n"); + } + break; + + default: + ERR("Unhandled fog mode %#x.\n", settings->fog_mode); + break; + } + if (output_legacy_fogcoord) + shader_addline(buffer, "gl_FogFragCoord = ffp_varying_fogcoord;\n"); + + if (settings->point_size) + { + shader_addline(buffer, "gl_PointSize = %s / sqrt(ffp_point.c_att" + " + ffp_point.l_att * length(ec_pos.xyz)" + " + ffp_point.q_att * dot(ec_pos.xyz, ec_pos.xyz));\n", + settings->per_vertex_point_size ? "ffp_attrib_psize" : "ffp_point.size"); + shader_addline(buffer, "gl_PointSize = clamp(gl_PointSize, ffp_point.size_min, ffp_point.size_max);\n"); + } + + shader_addline(buffer, "}\n"); + + shader_obj = GL_EXTCALL(glCreateShader(GL_VERTEX_SHADER)); + shader_glsl_compile(gl_info, shader_obj, buffer->buffer); + + return shader_obj; +} + +static const char *shader_glsl_get_ffp_fragment_op_arg(struct wined3d_string_buffer *buffer, + DWORD argnum, unsigned int stage, DWORD arg) +{ + const char *ret; + + if (arg == ARG_UNUSED) + return ""; + + switch (arg & WINED3DTA_SELECTMASK) + { + case WINED3DTA_DIFFUSE: + ret = "ffp_varying_diffuse"; + break; + + case WINED3DTA_CURRENT: + ret = "ret"; + break; + + case WINED3DTA_TEXTURE: + switch (stage) + { + case 0: ret = "tex0"; break; + case 1: ret = "tex1"; break; + case 2: ret = "tex2"; break; + case 3: ret = "tex3"; break; + case 4: ret = "tex4"; break; + case 5: ret = "tex5"; break; + case 6: ret = "tex6"; break; + case 7: ret = "tex7"; break; + default: + ret = ""; + break; + } + break; + + case WINED3DTA_TFACTOR: + ret = "tex_factor"; + break; + + case WINED3DTA_SPECULAR: + ret = "ffp_varying_specular"; + break; + + case WINED3DTA_TEMP: + ret = "temp_reg"; + break; + + case WINED3DTA_CONSTANT: + switch (stage) + { + case 0: ret = "tss_const0"; break; + case 1: ret = "tss_const1"; break; + case 2: ret = "tss_const2"; break; + case 3: ret = "tss_const3"; break; + case 4: ret = "tss_const4"; break; + case 5: ret = "tss_const5"; break; + case 6: ret = "tss_const6"; break; + case 7: ret = "tss_const7"; break; + default: + ret = ""; + break; + } + break; + + default: + return ""; + } + + if (arg & WINED3DTA_COMPLEMENT) + { + shader_addline(buffer, "arg%u = vec4(1.0) - %s;\n", argnum, ret); + if (argnum == 0) + ret = "arg0"; + else if (argnum == 1) + ret = "arg1"; + else if (argnum == 2) + ret = "arg2"; + } + + if (arg & WINED3DTA_ALPHAREPLICATE) + { + shader_addline(buffer, "arg%u = vec4(%s.w);\n", argnum, ret); + if (argnum == 0) + ret = "arg0"; + else if (argnum == 1) + ret = "arg1"; + else if (argnum == 2) + ret = "arg2"; + } + + return ret; +} + +static void shader_glsl_ffp_fragment_op(struct wined3d_string_buffer *buffer, unsigned int stage, BOOL color, + BOOL alpha, BOOL tmp_dst, DWORD op, DWORD dw_arg0, DWORD dw_arg1, DWORD dw_arg2) +{ + const char *dstmask, *dstreg, *arg0, *arg1, *arg2; + + if (color && alpha) + dstmask = ""; + else if (color) + dstmask = ".xyz"; + else + dstmask = ".w"; + + dstreg = tmp_dst ? "temp_reg" : "ret"; + + arg0 = shader_glsl_get_ffp_fragment_op_arg(buffer, 0, stage, dw_arg0); + arg1 = shader_glsl_get_ffp_fragment_op_arg(buffer, 1, stage, dw_arg1); + arg2 = shader_glsl_get_ffp_fragment_op_arg(buffer, 2, stage, dw_arg2); + + switch (op) + { + case WINED3D_TOP_DISABLE: + break; + + case WINED3D_TOP_SELECT_ARG1: + shader_addline(buffer, "%s%s = %s%s;\n", dstreg, dstmask, arg1, dstmask); + break; + + case WINED3D_TOP_SELECT_ARG2: + shader_addline(buffer, "%s%s = %s%s;\n", dstreg, dstmask, arg2, dstmask); + break; + + case WINED3D_TOP_MODULATE: + shader_addline(buffer, "%s%s = %s%s * %s%s;\n", dstreg, dstmask, arg1, dstmask, arg2, dstmask); + break; + + case WINED3D_TOP_MODULATE_4X: + shader_addline(buffer, "%s%s = clamp(%s%s * %s%s * 4.0, 0.0, 1.0);\n", + dstreg, dstmask, arg1, dstmask, arg2, dstmask); + break; + + case WINED3D_TOP_MODULATE_2X: + shader_addline(buffer, "%s%s = clamp(%s%s * %s%s * 2.0, 0.0, 1.0);\n", + dstreg, dstmask, arg1, dstmask, arg2, dstmask); + break; + + case WINED3D_TOP_ADD: + shader_addline(buffer, "%s%s = clamp(%s%s + %s%s, 0.0, 1.0);\n", + dstreg, dstmask, arg1, dstmask, arg2, dstmask); + break; + + case WINED3D_TOP_ADD_SIGNED: + shader_addline(buffer, "%s%s = clamp(%s%s + (%s - vec4(0.5))%s, 0.0, 1.0);\n", + dstreg, dstmask, arg1, dstmask, arg2, dstmask); + break; + + case WINED3D_TOP_ADD_SIGNED_2X: + shader_addline(buffer, "%s%s = clamp((%s%s + (%s - vec4(0.5))%s) * 2.0, 0.0, 1.0);\n", + dstreg, dstmask, arg1, dstmask, arg2, dstmask); + break; + + case WINED3D_TOP_SUBTRACT: + shader_addline(buffer, "%s%s = clamp(%s%s - %s%s, 0.0, 1.0);\n", + dstreg, dstmask, arg1, dstmask, arg2, dstmask); + break; + + case WINED3D_TOP_ADD_SMOOTH: + shader_addline(buffer, "%s%s = clamp((vec4(1.0) - %s)%s * %s%s + %s%s, 0.0, 1.0);\n", + dstreg, dstmask, arg1, dstmask, arg2, dstmask, arg1, dstmask); + break; + + case WINED3D_TOP_BLEND_DIFFUSE_ALPHA: + arg0 = shader_glsl_get_ffp_fragment_op_arg(buffer, 0, stage, WINED3DTA_DIFFUSE); + shader_addline(buffer, "%s%s = mix(%s%s, %s%s, %s.w);\n", + dstreg, dstmask, arg2, dstmask, arg1, dstmask, arg0); + break; + + case WINED3D_TOP_BLEND_TEXTURE_ALPHA: + arg0 = shader_glsl_get_ffp_fragment_op_arg(buffer, 0, stage, WINED3DTA_TEXTURE); + shader_addline(buffer, "%s%s = mix(%s%s, %s%s, %s.w);\n", + dstreg, dstmask, arg2, dstmask, arg1, dstmask, arg0); + break; + + case WINED3D_TOP_BLEND_FACTOR_ALPHA: + arg0 = shader_glsl_get_ffp_fragment_op_arg(buffer, 0, stage, WINED3DTA_TFACTOR); + shader_addline(buffer, "%s%s = mix(%s%s, %s%s, %s.w);\n", + dstreg, dstmask, arg2, dstmask, arg1, dstmask, arg0); + break; + + case WINED3D_TOP_BLEND_TEXTURE_ALPHA_PM: + arg0 = shader_glsl_get_ffp_fragment_op_arg(buffer, 0, stage, WINED3DTA_TEXTURE); + shader_addline(buffer, "%s%s = clamp(%s%s * (1.0 - %s.w) + %s%s, 0.0, 1.0);\n", + dstreg, dstmask, arg2, dstmask, arg0, arg1, dstmask); + break; + + case WINED3D_TOP_BLEND_CURRENT_ALPHA: + arg0 = shader_glsl_get_ffp_fragment_op_arg(buffer, 0, stage, WINED3DTA_CURRENT); + shader_addline(buffer, "%s%s = mix(%s%s, %s%s, %s.w);\n", + dstreg, dstmask, arg2, dstmask, arg1, dstmask, arg0); + break; + + case WINED3D_TOP_MODULATE_ALPHA_ADD_COLOR: + shader_addline(buffer, "%s%s = clamp(%s%s * %s.w + %s%s, 0.0, 1.0);\n", + dstreg, dstmask, arg2, dstmask, arg1, arg1, dstmask); + break; + + case WINED3D_TOP_MODULATE_COLOR_ADD_ALPHA: + shader_addline(buffer, "%s%s = clamp(%s%s * %s%s + %s.w, 0.0, 1.0);\n", + dstreg, dstmask, arg1, dstmask, arg2, dstmask, arg1); + break; + + case WINED3D_TOP_MODULATE_INVALPHA_ADD_COLOR: + shader_addline(buffer, "%s%s = clamp(%s%s * (1.0 - %s.w) + %s%s, 0.0, 1.0);\n", + dstreg, dstmask, arg2, dstmask, arg1, arg1, dstmask); + break; + case WINED3D_TOP_MODULATE_INVCOLOR_ADD_ALPHA: + shader_addline(buffer, "%s%s = clamp((vec4(1.0) - %s)%s * %s%s + %s.w, 0.0, 1.0);\n", + dstreg, dstmask, arg1, dstmask, arg2, dstmask, arg1); + break; + + case WINED3D_TOP_BUMPENVMAP: + case WINED3D_TOP_BUMPENVMAP_LUMINANCE: + /* These are handled in the first pass, nothing to do. */ + break; + + case WINED3D_TOP_DOTPRODUCT3: + shader_addline(buffer, "%s%s = vec4(clamp(dot(%s.xyz - 0.5, %s.xyz - 0.5) * 4.0, 0.0, 1.0))%s;\n", + dstreg, dstmask, arg1, arg2, dstmask); + break; + + case WINED3D_TOP_MULTIPLY_ADD: + shader_addline(buffer, "%s%s = clamp(%s%s * %s%s + %s%s, 0.0, 1.0);\n", + dstreg, dstmask, arg1, dstmask, arg2, dstmask, arg0, dstmask); + break; + + case WINED3D_TOP_LERP: + /* MSDN isn't quite right here. */ + shader_addline(buffer, "%s%s = mix(%s%s, %s%s, %s%s);\n", + dstreg, dstmask, arg2, dstmask, arg1, dstmask, arg0, dstmask); + break; + + default: + FIXME("Unhandled operation %#x.\n", op); + break; + } +} + +/* Context activation is done by the caller. */ +static GLuint shader_glsl_generate_ffp_fragment_shader(struct shader_glsl_priv *priv, + const struct ffp_frag_settings *settings, const struct wined3d_context_gl *context_gl) +{ + struct wined3d_string_buffer *tex_reg_name = string_buffer_get(&priv->string_buffers); + enum wined3d_cmp_func alpha_test_func = settings->alpha_test_func + 1; + struct wined3d_string_buffer *buffer = &priv->shader_buffer; + BYTE lum_map = 0, bump_map = 0, tex_map = 0, tss_const_map = 0; + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + const BOOL legacy_syntax = needs_legacy_glsl_syntax(gl_info); + BOOL tempreg_used = FALSE, tfactor_used = FALSE; + UINT lowest_disabled_stage; + GLuint shader_id; + DWORD arg0, arg1, arg2; + unsigned int stage; + + string_buffer_clear(buffer); + + /* Find out which textures are read */ + for (stage = 0; stage < WINED3D_MAX_TEXTURES; ++stage) + { + if (settings->op[stage].cop == WINED3D_TOP_DISABLE) + break; + + arg0 = settings->op[stage].carg0 & WINED3DTA_SELECTMASK; + arg1 = settings->op[stage].carg1 & WINED3DTA_SELECTMASK; + arg2 = settings->op[stage].carg2 & WINED3DTA_SELECTMASK; + + if (arg0 == WINED3DTA_TEXTURE || arg1 == WINED3DTA_TEXTURE || arg2 == WINED3DTA_TEXTURE + || (stage == 0 && settings->color_key_enabled)) + tex_map |= 1u << stage; + if (arg0 == WINED3DTA_TFACTOR || arg1 == WINED3DTA_TFACTOR || arg2 == WINED3DTA_TFACTOR) + tfactor_used = TRUE; + if (arg0 == WINED3DTA_TEMP || arg1 == WINED3DTA_TEMP || arg2 == WINED3DTA_TEMP) + tempreg_used = TRUE; + if (settings->op[stage].tmp_dst) + tempreg_used = TRUE; + if (arg0 == WINED3DTA_CONSTANT || arg1 == WINED3DTA_CONSTANT || arg2 == WINED3DTA_CONSTANT) + tss_const_map |= 1u << stage; + + switch (settings->op[stage].cop) + { + case WINED3D_TOP_BUMPENVMAP_LUMINANCE: + lum_map |= 1u << stage; + /* fall through */ + case WINED3D_TOP_BUMPENVMAP: + bump_map |= 1u << stage; + /* fall through */ + case WINED3D_TOP_BLEND_TEXTURE_ALPHA: + case WINED3D_TOP_BLEND_TEXTURE_ALPHA_PM: + tex_map |= 1u << stage; + break; + + case WINED3D_TOP_BLEND_FACTOR_ALPHA: + tfactor_used = TRUE; + break; + + default: + break; + } + + if (settings->op[stage].aop == WINED3D_TOP_DISABLE) + continue; + + arg0 = settings->op[stage].aarg0 & WINED3DTA_SELECTMASK; + arg1 = settings->op[stage].aarg1 & WINED3DTA_SELECTMASK; + arg2 = settings->op[stage].aarg2 & WINED3DTA_SELECTMASK; + + if (arg0 == WINED3DTA_TEXTURE || arg1 == WINED3DTA_TEXTURE || arg2 == WINED3DTA_TEXTURE) + tex_map |= 1u << stage; + if (arg0 == WINED3DTA_TFACTOR || arg1 == WINED3DTA_TFACTOR || arg2 == WINED3DTA_TFACTOR) + tfactor_used = TRUE; + if (arg0 == WINED3DTA_TEMP || arg1 == WINED3DTA_TEMP || arg2 == WINED3DTA_TEMP) + tempreg_used = TRUE; + if (arg0 == WINED3DTA_CONSTANT || arg1 == WINED3DTA_CONSTANT || arg2 == WINED3DTA_CONSTANT) + tss_const_map |= 1u << stage; + } + lowest_disabled_stage = stage; + + shader_glsl_add_version_declaration(buffer, gl_info); + + if (shader_glsl_use_explicit_attrib_location(gl_info)) + shader_addline(buffer, "#extension GL_ARB_explicit_attrib_location : enable\n"); + if (gl_info->supported[ARB_SHADING_LANGUAGE_420PACK]) + shader_addline(buffer, "#extension GL_ARB_shading_language_420pack : enable\n"); + if (gl_info->supported[ARB_TEXTURE_RECTANGLE]) + shader_addline(buffer, "#extension GL_ARB_texture_rectangle : enable\n"); + + if (!use_legacy_fragment_output(gl_info)) + { + shader_addline(buffer, "vec4 ps_out[1];\n"); + if (shader_glsl_use_explicit_attrib_location(gl_info)) + shader_addline(buffer, "layout(location = 0) "); + shader_addline(buffer, "out vec4 color_out0;\n"); + } + + shader_addline(buffer, "vec4 tmp0, tmp1;\n"); + shader_addline(buffer, "vec4 ret;\n"); + if (tempreg_used || settings->sRGB_write) + shader_addline(buffer, "vec4 temp_reg = vec4(0.0);\n"); + shader_addline(buffer, "vec4 arg0, arg1, arg2;\n"); + + for (stage = 0; stage < WINED3D_MAX_TEXTURES; ++stage) + { + const char *sampler_type; + + if (tss_const_map & (1u << stage)) + shader_addline(buffer, "uniform vec4 tss_const%u;\n", stage); + + if (!(tex_map & (1u << stage))) + continue; + + switch (settings->op[stage].tex_type) + { + case WINED3D_GL_RES_TYPE_TEX_1D: + sampler_type = "1D"; + break; + case WINED3D_GL_RES_TYPE_TEX_2D: + sampler_type = "2D"; + break; + case WINED3D_GL_RES_TYPE_TEX_3D: + sampler_type = "3D"; + break; + case WINED3D_GL_RES_TYPE_TEX_CUBE: + sampler_type = "Cube"; + break; + case WINED3D_GL_RES_TYPE_TEX_RECT: + sampler_type = "2DRect"; + break; + default: + FIXME("Unhandled sampler type %#x.\n", settings->op[stage].tex_type); + sampler_type = NULL; + break; + } + if (sampler_type) + { + if (shader_glsl_use_layout_binding_qualifier(gl_info)) + shader_glsl_append_sampler_binding_qualifier(buffer, &context_gl->c, NULL, stage); + shader_addline(buffer, "uniform sampler%s ps_sampler%u;\n", sampler_type, stage); + } + + shader_addline(buffer, "vec4 tex%u;\n", stage); + + if (!(bump_map & (1u << stage))) + continue; + shader_addline(buffer, "uniform mat2 bumpenv_mat%u;\n", stage); + + if (!(lum_map & (1u << stage))) + continue; + shader_addline(buffer, "uniform float bumpenv_lum_scale%u;\n", stage); + shader_addline(buffer, "uniform float bumpenv_lum_offset%u;\n", stage); + } + if (tfactor_used) + shader_addline(buffer, "uniform vec4 tex_factor;\n"); + if (settings->color_key_enabled) + shader_addline(buffer, "uniform vec4 color_key[2];\n"); + shader_addline(buffer, "uniform vec4 specular_enable;\n"); + + if (settings->sRGB_write) + { + shader_addline(buffer, "const vec4 srgb_const0 = "); + shader_glsl_append_imm_vec(buffer, &wined3d_srgb_const[0].x, 4, gl_info); + shader_addline(buffer, ";\n"); + shader_addline(buffer, "const vec4 srgb_const1 = "); + shader_glsl_append_imm_vec(buffer, &wined3d_srgb_const[1].x, 4, gl_info); + shader_addline(buffer, ";\n"); + } + + shader_addline(buffer, "uniform struct\n{\n"); + shader_addline(buffer, " vec4 color;\n"); + shader_addline(buffer, " float density;\n"); + shader_addline(buffer, " float end;\n"); + shader_addline(buffer, " float scale;\n"); + shader_addline(buffer, "} ffp_fog;\n"); + + if (alpha_test_func != WINED3D_CMP_ALWAYS) + shader_addline(buffer, "uniform float alpha_test_ref;\n"); + + if (legacy_syntax) + { + shader_addline(buffer, "vec4 ffp_varying_diffuse;\n"); + shader_addline(buffer, "vec4 ffp_varying_specular;\n"); + shader_addline(buffer, "vec4 ffp_varying_texcoord[%u];\n", WINED3D_MAX_TEXTURES); + shader_addline(buffer, "vec4 ffp_texcoord[%u];\n", WINED3D_MAX_TEXTURES); + shader_addline(buffer, "float ffp_varying_fogcoord;\n"); + } + else + { + declare_in_varying(gl_info, buffer, settings->flatshading, "vec4 ffp_varying_diffuse;\n"); + declare_in_varying(gl_info, buffer, settings->flatshading, "vec4 ffp_varying_specular;\n"); + declare_in_varying(gl_info, buffer, FALSE, "vec4 ffp_varying_texcoord[%u];\n", WINED3D_MAX_TEXTURES); + shader_addline(buffer, "vec4 ffp_texcoord[%u];\n", WINED3D_MAX_TEXTURES); + declare_in_varying(gl_info, buffer, FALSE, "float ffp_varying_fogcoord;\n"); + } + + shader_addline(buffer, "void main()\n{\n"); + + if (legacy_syntax) + { + shader_addline(buffer, "ffp_varying_diffuse = gl_Color;\n"); + shader_addline(buffer, "ffp_varying_specular = gl_SecondaryColor;\n"); + } + + for (stage = 0; stage < WINED3D_MAX_TEXTURES; ++stage) + { + if (tex_map & (1u << stage)) + { + if (settings->pointsprite) + shader_addline(buffer, "ffp_texcoord[%u] = vec4(gl_PointCoord.xy, 0.0, 0.0);\n", stage); + else if (settings->texcoords_initialized & (1u << stage)) + shader_addline(buffer, "ffp_texcoord[%u] = %s[%u];\n", + stage, legacy_syntax ? "gl_TexCoord" : "ffp_varying_texcoord", stage); + else + shader_addline(buffer, "ffp_texcoord[%u] = vec4(0.0);\n", stage); + } + } + + if (legacy_syntax && settings->fog != WINED3D_FFP_PS_FOG_OFF) + shader_addline(buffer, "ffp_varying_fogcoord = gl_FogFragCoord;\n"); + + if (lowest_disabled_stage < 7 && settings->emul_clipplanes) + shader_addline(buffer, "if (any(lessThan(ffp_texcoord[7], vec4(0.0)))) discard;\n"); + + /* Generate texture sampling instructions */ + for (stage = 0; stage < WINED3D_MAX_TEXTURES && settings->op[stage].cop != WINED3D_TOP_DISABLE; ++stage) + { + const char *texture_function, *coord_mask; + BOOL proj; + + if (!(tex_map & (1u << stage))) + continue; + + if (settings->op[stage].projected == WINED3D_PROJECTION_NONE) + { + proj = FALSE; + } + else if (settings->op[stage].projected == WINED3D_PROJECTION_COUNT4 + || settings->op[stage].projected == WINED3D_PROJECTION_COUNT3) + { + proj = TRUE; + } + else + { + FIXME("Unexpected projection mode %d\n", settings->op[stage].projected); + proj = TRUE; + } + + if (settings->op[stage].tex_type == WINED3D_GL_RES_TYPE_TEX_CUBE) + proj = FALSE; + + switch (settings->op[stage].tex_type) + { + case WINED3D_GL_RES_TYPE_TEX_1D: + texture_function = "texture1D"; + coord_mask = "x"; + break; + case WINED3D_GL_RES_TYPE_TEX_2D: + texture_function = "texture2D"; + coord_mask = "xy"; + break; + case WINED3D_GL_RES_TYPE_TEX_3D: + texture_function = "texture3D"; + coord_mask = "xyz"; + break; + case WINED3D_GL_RES_TYPE_TEX_CUBE: + texture_function = "textureCube"; + coord_mask = "xyz"; + break; + case WINED3D_GL_RES_TYPE_TEX_RECT: + texture_function = "texture2DRect"; + coord_mask = "xy"; + break; + default: + FIXME("Unhandled texture type %#x.\n", settings->op[stage].tex_type); + texture_function = ""; + coord_mask = "xyzw"; + proj = FALSE; + break; + } + if (!legacy_syntax) + texture_function = "texture"; + + if (stage > 0 + && (settings->op[stage - 1].cop == WINED3D_TOP_BUMPENVMAP + || settings->op[stage - 1].cop == WINED3D_TOP_BUMPENVMAP_LUMINANCE)) + { + shader_addline(buffer, "ret.xy = bumpenv_mat%u * tex%u.xy;\n", stage - 1, stage - 1); + + /* With projective textures, texbem only divides the static + * texture coordinate, not the displacement, so multiply the + * displacement with the dividing parameter before passing it to + * TXP. */ + if (settings->op[stage].projected != WINED3D_PROJECTION_NONE) + { + if (settings->op[stage].projected == WINED3D_PROJECTION_COUNT4) + { + shader_addline(buffer, "ret.xy = (ret.xy * ffp_texcoord[%u].w) + ffp_texcoord[%u].xy;\n", + stage, stage); + shader_addline(buffer, "ret.zw = ffp_texcoord[%u].ww;\n", stage); + } + else + { + shader_addline(buffer, "ret.xy = (ret.xy * ffp_texcoord[%u].z) + ffp_texcoord[%u].xy;\n", + stage, stage); + shader_addline(buffer, "ret.zw = ffp_texcoord[%u].zz;\n", stage); + } + } + else + { + shader_addline(buffer, "ret = ffp_texcoord[%u] + ret.xyxy;\n", stage); + } + + shader_addline(buffer, "tex%u = %s%s(ps_sampler%u, ret.%s%s);\n", + stage, texture_function, proj ? "Proj" : "", stage, coord_mask, proj ? "w" : ""); + + if (settings->op[stage - 1].cop == WINED3D_TOP_BUMPENVMAP_LUMINANCE) + shader_addline(buffer, "tex%u *= clamp(tex%u.z * bumpenv_lum_scale%u + bumpenv_lum_offset%u, 0.0, 1.0);\n", + stage, stage - 1, stage - 1, stage - 1); + } + else if (settings->op[stage].projected == WINED3D_PROJECTION_COUNT3) + { + shader_addline(buffer, "tex%u = %s%s(ps_sampler%u, ffp_texcoord[%u].xyz);\n", + stage, texture_function, proj ? "Proj" : "", stage, stage); + } + else + { + shader_addline(buffer, "tex%u = %s%s(ps_sampler%u, ffp_texcoord[%u].%s%s);\n", + stage, texture_function, proj ? "Proj" : "", stage, stage, coord_mask, proj ? "w" : ""); + } + + string_buffer_sprintf(tex_reg_name, "tex%u", stage); + shader_glsl_color_correction_ext(buffer, tex_reg_name->buffer, WINED3DSP_WRITEMASK_ALL, + settings->op[stage].color_fixup); + } + + if (settings->color_key_enabled) + shader_glsl_generate_colour_key_test(buffer, "tex0", "color_key[0]", "color_key[1]"); + + shader_addline(buffer, "ret = ffp_varying_diffuse;\n"); + + /* Generate the main shader */ + for (stage = 0; stage < WINED3D_MAX_TEXTURES; ++stage) + { + BOOL op_equal; + + if (settings->op[stage].cop == WINED3D_TOP_DISABLE) + break; + + if (settings->op[stage].cop == WINED3D_TOP_SELECT_ARG1 + && settings->op[stage].aop == WINED3D_TOP_SELECT_ARG1) + op_equal = settings->op[stage].carg1 == settings->op[stage].aarg1; + else if (settings->op[stage].cop == WINED3D_TOP_SELECT_ARG1 + && settings->op[stage].aop == WINED3D_TOP_SELECT_ARG2) + op_equal = settings->op[stage].carg1 == settings->op[stage].aarg2; + else if (settings->op[stage].cop == WINED3D_TOP_SELECT_ARG2 + && settings->op[stage].aop == WINED3D_TOP_SELECT_ARG1) + op_equal = settings->op[stage].carg2 == settings->op[stage].aarg1; + else if (settings->op[stage].cop == WINED3D_TOP_SELECT_ARG2 + && settings->op[stage].aop == WINED3D_TOP_SELECT_ARG2) + op_equal = settings->op[stage].carg2 == settings->op[stage].aarg2; + else + op_equal = settings->op[stage].aop == settings->op[stage].cop + && settings->op[stage].carg0 == settings->op[stage].aarg0 + && settings->op[stage].carg1 == settings->op[stage].aarg1 + && settings->op[stage].carg2 == settings->op[stage].aarg2; + + if (settings->op[stage].aop == WINED3D_TOP_DISABLE) + { + shader_glsl_ffp_fragment_op(buffer, stage, TRUE, FALSE, settings->op[stage].tmp_dst, + settings->op[stage].cop, settings->op[stage].carg0, + settings->op[stage].carg1, settings->op[stage].carg2); + } + else if (op_equal) + { + shader_glsl_ffp_fragment_op(buffer, stage, TRUE, TRUE, settings->op[stage].tmp_dst, + settings->op[stage].cop, settings->op[stage].carg0, + settings->op[stage].carg1, settings->op[stage].carg2); + } + else if (settings->op[stage].cop != WINED3D_TOP_BUMPENVMAP + && settings->op[stage].cop != WINED3D_TOP_BUMPENVMAP_LUMINANCE) + { + shader_glsl_ffp_fragment_op(buffer, stage, TRUE, FALSE, settings->op[stage].tmp_dst, + settings->op[stage].cop, settings->op[stage].carg0, + settings->op[stage].carg1, settings->op[stage].carg2); + shader_glsl_ffp_fragment_op(buffer, stage, FALSE, TRUE, settings->op[stage].tmp_dst, + settings->op[stage].aop, settings->op[stage].aarg0, + settings->op[stage].aarg1, settings->op[stage].aarg2); + } + } + + shader_addline(buffer, "%s[0] = ffp_varying_specular * specular_enable + ret;\n", + get_fragment_output(gl_info)); + + if (settings->sRGB_write) + shader_glsl_generate_srgb_write_correction(buffer, gl_info); + + shader_glsl_generate_fog_code(buffer, gl_info, settings->fog); + + shader_glsl_generate_alpha_test(buffer, gl_info, alpha_test_func); + if (!use_legacy_fragment_output(gl_info)) + shader_addline(buffer, "color_out0 = ps_out[0];\n"); + + shader_addline(buffer, "}\n"); + + shader_id = GL_EXTCALL(glCreateShader(GL_FRAGMENT_SHADER)); + shader_glsl_compile(gl_info, shader_id, buffer->buffer); + + string_buffer_release(&priv->string_buffers, tex_reg_name); + return shader_id; +} + +static struct glsl_ffp_vertex_shader *shader_glsl_find_ffp_vertex_shader(struct shader_glsl_priv *priv, + const struct wined3d_gl_info *gl_info, const struct wined3d_ffp_vs_settings *settings) +{ + struct glsl_ffp_vertex_shader *shader; + const struct wine_rb_entry *entry; + + if ((entry = wine_rb_get(&priv->ffp_vertex_shaders, settings))) + return WINE_RB_ENTRY_VALUE(entry, struct glsl_ffp_vertex_shader, desc.entry); + + if (!(shader = heap_alloc(sizeof(*shader)))) + return NULL; + + shader->desc.settings = *settings; + shader->id = shader_glsl_generate_ffp_vertex_shader(priv, settings, gl_info); + list_init(&shader->linked_programs); + if (wine_rb_put(&priv->ffp_vertex_shaders, &shader->desc.settings, &shader->desc.entry) == -1) + ERR("Failed to insert ffp vertex shader.\n"); + + return shader; +} + +static struct glsl_ffp_fragment_shader *shader_glsl_find_ffp_fragment_shader(struct shader_glsl_priv *priv, + const struct ffp_frag_settings *args, const struct wined3d_context_gl *context_gl) +{ + struct glsl_ffp_fragment_shader *glsl_desc; + const struct ffp_frag_desc *desc; + + if ((desc = find_ffp_frag_shader(&priv->ffp_fragment_shaders, args))) + return CONTAINING_RECORD(desc, struct glsl_ffp_fragment_shader, entry); + + if (!(glsl_desc = heap_alloc(sizeof(*glsl_desc)))) + return NULL; + + glsl_desc->entry.settings = *args; + glsl_desc->id = shader_glsl_generate_ffp_fragment_shader(priv, args, context_gl); + list_init(&glsl_desc->linked_programs); + add_ffp_frag_shader(&priv->ffp_fragment_shaders, &glsl_desc->entry); + + return glsl_desc; +} + + +static void shader_glsl_init_vs_uniform_locations(const struct wined3d_gl_info *gl_info, + struct shader_glsl_priv *priv, GLuint program_id, struct glsl_vs_program *vs, unsigned int vs_c_count) +{ + unsigned int i; + struct wined3d_string_buffer *name = string_buffer_get(&priv->string_buffers); + + for (i = 0; i < vs_c_count; ++i) + { + string_buffer_sprintf(name, "vs_c[%u]", i); + vs->uniform_f_locations[i] = GL_EXTCALL(glGetUniformLocation(program_id, name->buffer)); + } + memset(&vs->uniform_f_locations[vs_c_count], 0xff, (WINED3D_MAX_VS_CONSTS_F - vs_c_count) * sizeof(GLuint)); + + for (i = 0; i < WINED3D_MAX_CONSTS_I; ++i) + { + string_buffer_sprintf(name, "vs_i[%u]", i); + vs->uniform_i_locations[i] = GL_EXTCALL(glGetUniformLocation(program_id, name->buffer)); + } + + for (i = 0; i < WINED3D_MAX_CONSTS_B; ++i) + { + string_buffer_sprintf(name, "vs_b[%u]", i); + vs->uniform_b_locations[i] = GL_EXTCALL(glGetUniformLocation(program_id, name->buffer)); + } + + vs->pos_fixup_location = GL_EXTCALL(glGetUniformLocation(program_id, "pos_fixup")); + vs->base_vertex_id_location = GL_EXTCALL(glGetUniformLocation(program_id, "base_vertex_id")); + + for (i = 0; i < MAX_VERTEX_BLENDS; ++i) + { + string_buffer_sprintf(name, "ffp_modelview_matrix[%u]", i); + vs->modelview_matrix_location[i] = GL_EXTCALL(glGetUniformLocation(program_id, name->buffer)); + } + vs->projection_matrix_location = GL_EXTCALL(glGetUniformLocation(program_id, "ffp_projection_matrix")); + vs->normal_matrix_location = GL_EXTCALL(glGetUniformLocation(program_id, "ffp_normal_matrix")); + for (i = 0; i < WINED3D_MAX_TEXTURES; ++i) + { + string_buffer_sprintf(name, "ffp_texture_matrix[%u]", i); + vs->texture_matrix_location[i] = GL_EXTCALL(glGetUniformLocation(program_id, name->buffer)); + } + vs->material_ambient_location = GL_EXTCALL(glGetUniformLocation(program_id, "ffp_material.ambient")); + vs->material_diffuse_location = GL_EXTCALL(glGetUniformLocation(program_id, "ffp_material.diffuse")); + vs->material_specular_location = GL_EXTCALL(glGetUniformLocation(program_id, "ffp_material.specular")); + vs->material_emissive_location = GL_EXTCALL(glGetUniformLocation(program_id, "ffp_material.emissive")); + vs->material_shininess_location = GL_EXTCALL(glGetUniformLocation(program_id, "ffp_material.shininess")); + vs->light_ambient_location = GL_EXTCALL(glGetUniformLocation(program_id, "ffp_light_ambient")); + for (i = 0; i < WINED3D_MAX_ACTIVE_LIGHTS; ++i) + { + string_buffer_sprintf(name, "ffp_light[%u].diffuse", i); + vs->light_location[i].diffuse = GL_EXTCALL(glGetUniformLocation(program_id, name->buffer)); + string_buffer_sprintf(name, "ffp_light[%u].specular", i); + vs->light_location[i].specular = GL_EXTCALL(glGetUniformLocation(program_id, name->buffer)); + string_buffer_sprintf(name, "ffp_light[%u].ambient", i); + vs->light_location[i].ambient = GL_EXTCALL(glGetUniformLocation(program_id, name->buffer)); + string_buffer_sprintf(name, "ffp_light[%u].position", i); + vs->light_location[i].position = GL_EXTCALL(glGetUniformLocation(program_id, name->buffer)); + string_buffer_sprintf(name, "ffp_light[%u].direction", i); + vs->light_location[i].direction = GL_EXTCALL(glGetUniformLocation(program_id, name->buffer)); + string_buffer_sprintf(name, "ffp_light[%u].range", i); + vs->light_location[i].range = GL_EXTCALL(glGetUniformLocation(program_id, name->buffer)); + string_buffer_sprintf(name, "ffp_light[%u].falloff", i); + vs->light_location[i].falloff = GL_EXTCALL(glGetUniformLocation(program_id, name->buffer)); + string_buffer_sprintf(name, "ffp_light[%u].c_att", i); + vs->light_location[i].c_att = GL_EXTCALL(glGetUniformLocation(program_id, name->buffer)); + string_buffer_sprintf(name, "ffp_light[%u].l_att", i); + vs->light_location[i].l_att = GL_EXTCALL(glGetUniformLocation(program_id, name->buffer)); + string_buffer_sprintf(name, "ffp_light[%u].q_att", i); + vs->light_location[i].q_att = GL_EXTCALL(glGetUniformLocation(program_id, name->buffer)); + string_buffer_sprintf(name, "ffp_light[%u].cos_htheta", i); + vs->light_location[i].cos_htheta = GL_EXTCALL(glGetUniformLocation(program_id, name->buffer)); + string_buffer_sprintf(name, "ffp_light[%u].cos_hphi", i); + vs->light_location[i].cos_hphi = GL_EXTCALL(glGetUniformLocation(program_id, name->buffer)); + } + vs->pointsize_location = GL_EXTCALL(glGetUniformLocation(program_id, "ffp_point.size")); + vs->pointsize_min_location = GL_EXTCALL(glGetUniformLocation(program_id, "ffp_point.size_min")); + vs->pointsize_max_location = GL_EXTCALL(glGetUniformLocation(program_id, "ffp_point.size_max")); + vs->pointsize_c_att_location = GL_EXTCALL(glGetUniformLocation(program_id, "ffp_point.c_att")); + vs->pointsize_l_att_location = GL_EXTCALL(glGetUniformLocation(program_id, "ffp_point.l_att")); + vs->pointsize_q_att_location = GL_EXTCALL(glGetUniformLocation(program_id, "ffp_point.q_att")); + vs->clip_planes_location = GL_EXTCALL(glGetUniformLocation(program_id, "clip_planes")); + + string_buffer_release(&priv->string_buffers, name); +} + +static void shader_glsl_init_ds_uniform_locations(const struct wined3d_gl_info *gl_info, + struct shader_glsl_priv *priv, GLuint program_id, struct glsl_ds_program *ds) +{ + ds->pos_fixup_location = GL_EXTCALL(glGetUniformLocation(program_id, "pos_fixup")); +} + +static void shader_glsl_init_gs_uniform_locations(const struct wined3d_gl_info *gl_info, + struct shader_glsl_priv *priv, GLuint program_id, struct glsl_gs_program *gs) +{ + gs->pos_fixup_location = GL_EXTCALL(glGetUniformLocation(program_id, "pos_fixup")); +} + +static void shader_glsl_init_ps_uniform_locations(const struct wined3d_gl_info *gl_info, + struct shader_glsl_priv *priv, GLuint program_id, struct glsl_ps_program *ps, unsigned int ps_c_count) +{ + unsigned int i; + struct wined3d_string_buffer *name = string_buffer_get(&priv->string_buffers); + + for (i = 0; i < ps_c_count; ++i) + { + string_buffer_sprintf(name, "ps_c[%u]", i); + ps->uniform_f_locations[i] = GL_EXTCALL(glGetUniformLocation(program_id, name->buffer)); + } + memset(&ps->uniform_f_locations[ps_c_count], 0xff, (WINED3D_MAX_PS_CONSTS_F - ps_c_count) * sizeof(GLuint)); + + for (i = 0; i < WINED3D_MAX_CONSTS_I; ++i) + { + string_buffer_sprintf(name, "ps_i[%u]", i); + ps->uniform_i_locations[i] = GL_EXTCALL(glGetUniformLocation(program_id, name->buffer)); + } + + for (i = 0; i < WINED3D_MAX_CONSTS_B; ++i) + { + string_buffer_sprintf(name, "ps_b[%u]", i); + ps->uniform_b_locations[i] = GL_EXTCALL(glGetUniformLocation(program_id, name->buffer)); + } + + for (i = 0; i < WINED3D_MAX_TEXTURES; ++i) + { + string_buffer_sprintf(name, "bumpenv_mat%u", i); + ps->bumpenv_mat_location[i] = GL_EXTCALL(glGetUniformLocation(program_id, name->buffer)); + string_buffer_sprintf(name, "bumpenv_lum_scale%u", i); + ps->bumpenv_lum_scale_location[i] = GL_EXTCALL(glGetUniformLocation(program_id, name->buffer)); + string_buffer_sprintf(name, "bumpenv_lum_offset%u", i); + ps->bumpenv_lum_offset_location[i] = GL_EXTCALL(glGetUniformLocation(program_id, name->buffer)); + string_buffer_sprintf(name, "tss_const%u", i); + ps->tss_constant_location[i] = GL_EXTCALL(glGetUniformLocation(program_id, name->buffer)); + } + + ps->tex_factor_location = GL_EXTCALL(glGetUniformLocation(program_id, "tex_factor")); + ps->specular_enable_location = GL_EXTCALL(glGetUniformLocation(program_id, "specular_enable")); + + ps->fog_color_location = GL_EXTCALL(glGetUniformLocation(program_id, "ffp_fog.color")); + ps->fog_density_location = GL_EXTCALL(glGetUniformLocation(program_id, "ffp_fog.density")); + ps->fog_end_location = GL_EXTCALL(glGetUniformLocation(program_id, "ffp_fog.end")); + ps->fog_scale_location = GL_EXTCALL(glGetUniformLocation(program_id, "ffp_fog.scale")); + + ps->alpha_test_ref_location = GL_EXTCALL(glGetUniformLocation(program_id, "alpha_test_ref")); + + ps->np2_fixup_location = GL_EXTCALL(glGetUniformLocation(program_id, "ps_samplerNP2Fixup")); + ps->ycorrection_location = GL_EXTCALL(glGetUniformLocation(program_id, "ycorrection")); + ps->color_key_location = GL_EXTCALL(glGetUniformLocation(program_id, "color_key")); + + string_buffer_release(&priv->string_buffers, name); +} + +static HRESULT shader_glsl_compile_compute_shader(struct shader_glsl_priv *priv, + const struct wined3d_context_gl *context_gl, struct wined3d_shader *shader) +{ + struct glsl_context_data *ctx_data = context_gl->c.shader_backend_data; + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + struct wined3d_string_buffer *buffer = &priv->shader_buffer; + struct glsl_cs_compiled_shader *gl_shaders; + struct glsl_shader_private *shader_data; + struct glsl_shader_prog_link *entry; + GLuint shader_id, program_id; + + if (!(entry = heap_alloc(sizeof(*entry)))) + { + ERR("Out of memory.\n"); + return E_OUTOFMEMORY; + } + + if (!(shader->backend_data = heap_alloc_zero(sizeof(*shader_data)))) + { + ERR("Failed to allocate backend data.\n"); + heap_free(entry); + return E_OUTOFMEMORY; + } + shader_data = shader->backend_data; + + if (!(shader_data->gl_shaders.cs = heap_alloc(sizeof(*gl_shaders)))) + { + ERR("Failed to allocate GL shader array.\n"); + heap_free(entry); + heap_free(shader->backend_data); + shader->backend_data = NULL; + return E_OUTOFMEMORY; + } + shader_data->shader_array_size = 1; + gl_shaders = shader_data->gl_shaders.cs; + + TRACE("Compiling compute shader %p.\n", shader); + + string_buffer_clear(buffer); + shader_id = shader_glsl_generate_compute_shader(context_gl, buffer, &priv->string_buffers, shader); + gl_shaders[shader_data->num_gl_shaders++].id = shader_id; + + program_id = GL_EXTCALL(glCreateProgram()); + TRACE("Created new GLSL shader program %u.\n", program_id); + + entry->id = program_id; + entry->vs.id = 0; + entry->hs.id = 0; + entry->ds.id = 0; + entry->gs.id = 0; + entry->ps.id = 0; + entry->cs.id = shader_id; + entry->constant_version = 0; + entry->shader_controlled_clip_distances = 0; + entry->ps.np2_fixup_info = NULL; + add_glsl_program_entry(priv, entry); + + TRACE("Attaching GLSL shader object %u to program %u.\n", shader_id, program_id); + GL_EXTCALL(glAttachShader(program_id, shader_id)); + checkGLcall("glAttachShader"); + + list_add_head(&shader->linked_programs, &entry->cs.shader_entry); + + TRACE("Linking GLSL shader program %u.\n", program_id); + GL_EXTCALL(glLinkProgram(program_id)); + shader_glsl_validate_link(gl_info, program_id); + + GL_EXTCALL(glUseProgram(program_id)); + checkGLcall("glUseProgram"); + shader_glsl_load_program_resources(context_gl, priv, program_id, shader); + shader_glsl_load_images(gl_info, priv, program_id, &shader->reg_maps); + + entry->constant_update_mask = 0; + + GL_EXTCALL(glUseProgram(ctx_data->glsl_program ? ctx_data->glsl_program->id : 0)); + checkGLcall("glUseProgram"); + return WINED3D_OK; +} + +static GLuint find_glsl_compute_shader(const struct wined3d_context_gl *context_gl, + struct shader_glsl_priv *priv, struct wined3d_shader *shader) +{ + struct glsl_shader_private *shader_data; + + if (!shader->backend_data) + { + WARN("Failed to find GLSL program for compute shader %p.\n", shader); + if (FAILED(shader_glsl_compile_compute_shader(priv, context_gl, shader))) + { + ERR("Failed to compile compute shader %p.\n", shader); + return 0; + } + } + shader_data = shader->backend_data; + return shader_data->gl_shaders.cs[0].id; +} + +/* Context activation is done by the caller. */ +static void set_glsl_compute_shader_program(const struct wined3d_context_gl *context_gl, + const struct wined3d_state *state, struct shader_glsl_priv *priv, struct glsl_context_data *ctx_data) +{ + struct glsl_shader_prog_link *entry; + struct wined3d_shader *shader; + struct glsl_program_key key; + GLuint cs_id; + + if (!(context_gl->c.shader_update_mask & (1u << WINED3D_SHADER_TYPE_COMPUTE))) + return; + + if (!(shader = state->shader[WINED3D_SHADER_TYPE_COMPUTE])) + { + WARN("Compute shader is NULL.\n"); + ctx_data->glsl_program = NULL; + return; + } + + cs_id = find_glsl_compute_shader(context_gl, priv, shader); + memset(&key, 0, sizeof(key)); + key.cs_id = cs_id; + if (!(entry = get_glsl_program_entry(priv, &key))) + ERR("Failed to find GLSL program for compute shader %p.\n", shader); + ctx_data->glsl_program = entry; +} + +/* Context activation is done by the caller. */ +static void set_glsl_shader_program(const struct wined3d_context_gl *context_gl, const struct wined3d_state *state, + struct shader_glsl_priv *priv, struct glsl_context_data *ctx_data) +{ + const struct wined3d_d3d_info *d3d_info = context_gl->c.d3d_info; + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + const struct wined3d_shader *pre_rasterization_shader; + const struct ps_np2fixup_info *np2fixup_info = NULL; + struct wined3d_shader *hshader, *dshader, *gshader; + struct glsl_shader_prog_link *entry = NULL; + struct wined3d_shader *vshader = NULL; + struct wined3d_shader *pshader = NULL; + GLuint reorder_shader_id = 0; + struct glsl_program_key key; + GLuint program_id; + unsigned int i; + GLuint vs_id = 0; + GLuint hs_id = 0; + GLuint ds_id = 0; + GLuint gs_id = 0; + GLuint ps_id = 0; + struct list *ps_list, *vs_list; + WORD attribs_map; + struct wined3d_string_buffer *tmp_name; + + if (!(context_gl->c.shader_update_mask & (1u << WINED3D_SHADER_TYPE_VERTEX)) && ctx_data->glsl_program) + { + vs_id = ctx_data->glsl_program->vs.id; + vs_list = &ctx_data->glsl_program->vs.shader_entry; + + if (use_vs(state)) + vshader = state->shader[WINED3D_SHADER_TYPE_VERTEX]; + } + else if (use_vs(state)) + { + struct vs_compile_args vs_compile_args; + + vshader = state->shader[WINED3D_SHADER_TYPE_VERTEX]; + + find_vs_compile_args(state, vshader, context_gl->c.stream_info.swizzle_map, &vs_compile_args, &context_gl->c); + vs_id = find_glsl_vertex_shader(context_gl, priv, vshader, &vs_compile_args); + vs_list = &vshader->linked_programs; + } + else if (priv->vertex_pipe == &glsl_vertex_pipe) + { + struct glsl_ffp_vertex_shader *ffp_shader; + struct wined3d_ffp_vs_settings settings; + + wined3d_ffp_get_vs_settings(&context_gl->c, state, &settings); + ffp_shader = shader_glsl_find_ffp_vertex_shader(priv, gl_info, &settings); + vs_id = ffp_shader->id; + vs_list = &ffp_shader->linked_programs; + } + + hshader = state->shader[WINED3D_SHADER_TYPE_HULL]; + if (!(context_gl->c.shader_update_mask & (1u << WINED3D_SHADER_TYPE_HULL)) && ctx_data->glsl_program) + hs_id = ctx_data->glsl_program->hs.id; + else if (hshader) + hs_id = find_glsl_hull_shader(context_gl, priv, hshader); + + dshader = state->shader[WINED3D_SHADER_TYPE_DOMAIN]; + if (!(context_gl->c.shader_update_mask & (1u << WINED3D_SHADER_TYPE_DOMAIN)) && ctx_data->glsl_program) + { + ds_id = ctx_data->glsl_program->ds.id; + } + else if (dshader) + { + struct ds_compile_args args; + + find_ds_compile_args(state, dshader, &args, &context_gl->c); + ds_id = find_glsl_domain_shader(context_gl, priv, dshader, &args); + } + + gshader = state->shader[WINED3D_SHADER_TYPE_GEOMETRY]; + if (!(context_gl->c.shader_update_mask & (1u << WINED3D_SHADER_TYPE_GEOMETRY)) && ctx_data->glsl_program) + { + gs_id = ctx_data->glsl_program->gs.id; + } + else if (gshader) + { + struct gs_compile_args args; + + find_gs_compile_args(state, gshader, &args, &context_gl->c); + gs_id = find_glsl_geometry_shader(context_gl, priv, gshader, &args); + } + + /* A pixel shader is not used when rasterization is disabled. */ + if (is_rasterization_disabled(gshader)) + { + ps_id = 0; + ps_list = NULL; + } + else if (!(context_gl->c.shader_update_mask & (1u << WINED3D_SHADER_TYPE_PIXEL)) && ctx_data->glsl_program) + { + ps_id = ctx_data->glsl_program->ps.id; + ps_list = &ctx_data->glsl_program->ps.shader_entry; + + if (use_ps(state)) + pshader = state->shader[WINED3D_SHADER_TYPE_PIXEL]; + } + else if (use_ps(state)) + { + struct ps_compile_args ps_compile_args; + pshader = state->shader[WINED3D_SHADER_TYPE_PIXEL]; + find_ps_compile_args(state, pshader, context_gl->c.stream_info.position_transformed, + &ps_compile_args, &context_gl->c); + ps_id = find_glsl_fragment_shader(context_gl, &priv->shader_buffer, &priv->string_buffers, + pshader, &ps_compile_args, &np2fixup_info); + ps_list = &pshader->linked_programs; + } + else if (priv->fragment_pipe == &glsl_fragment_pipe + && !(vshader && vshader->reg_maps.shader_version.major >= 4)) + { + struct glsl_ffp_fragment_shader *ffp_shader; + struct ffp_frag_settings settings; + + gen_ffp_frag_op(&context_gl->c, state, &settings, FALSE); + ffp_shader = shader_glsl_find_ffp_fragment_shader(priv, &settings, context_gl); + ps_id = ffp_shader->id; + ps_list = &ffp_shader->linked_programs; + } + + key.vs_id = vs_id; + key.hs_id = hs_id; + key.ds_id = ds_id; + key.gs_id = gs_id; + key.ps_id = ps_id; + key.cs_id = 0; + if ((!vs_id && !hs_id && !ds_id && !gs_id && !ps_id) || (entry = get_glsl_program_entry(priv, &key))) + { + ctx_data->glsl_program = entry; + return; + } + + /* If we get to this point, then no matching program exists, so we create one */ + program_id = GL_EXTCALL(glCreateProgram()); + TRACE("Created new GLSL shader program %u.\n", program_id); + + /* Create the entry */ + entry = heap_alloc(sizeof(*entry)); + entry->id = program_id; + entry->vs.id = vs_id; + entry->hs.id = hs_id; + entry->ds.id = ds_id; + entry->gs.id = gs_id; + entry->ps.id = ps_id; + entry->cs.id = 0; + entry->constant_version = 0; + entry->shader_controlled_clip_distances = 0; + entry->ps.np2_fixup_info = np2fixup_info; + /* Add the hash table entry */ + add_glsl_program_entry(priv, entry); + + /* Set the current program */ + ctx_data->glsl_program = entry; + + /* Attach GLSL vshader */ + if (vs_id) + { + TRACE("Attaching GLSL shader object %u to program %u.\n", vs_id, program_id); + GL_EXTCALL(glAttachShader(program_id, vs_id)); + checkGLcall("glAttachShader"); + + list_add_head(vs_list, &entry->vs.shader_entry); + } + + if (vshader) + { + attribs_map = vshader->reg_maps.input_registers; + if (vshader->reg_maps.shader_version.major < 4) + { + reorder_shader_id = shader_glsl_generate_vs3_rasterizer_input_setup(priv, vshader, pshader, + state->primitive_type == WINED3D_PT_POINTLIST && vshader->reg_maps.point_size, + d3d_info->emulated_flatshading + && state->render_states[WINED3D_RS_SHADEMODE] == WINED3D_SHADE_FLAT, gl_info); + TRACE("Attaching GLSL shader object %u to program %u.\n", reorder_shader_id, program_id); + GL_EXTCALL(glAttachShader(program_id, reorder_shader_id)); + checkGLcall("glAttachShader"); + /* Flag the reorder function for deletion, it will be freed + * automatically when the program is destroyed. */ + GL_EXTCALL(glDeleteShader(reorder_shader_id)); + } + } + else + { + attribs_map = (1u << WINED3D_FFP_ATTRIBS_COUNT) - 1; + } + + if (!shader_glsl_use_explicit_attrib_location(gl_info)) + { + /* Bind vertex attributes to a corresponding index number to match + * the same index numbers as ARB_vertex_programs (makes loading + * vertex attributes simpler). With this method, we can use the + * exact same code to load the attributes later for both ARB and + * GLSL shaders. + * + * We have to do this here because we need to know the Program ID + * in order to make the bindings work, and it has to be done prior + * to linking the GLSL program. */ + tmp_name = string_buffer_get(&priv->string_buffers); + for (i = 0; attribs_map; attribs_map >>= 1, ++i) + { + if (!(attribs_map & 1)) + continue; + + string_buffer_sprintf(tmp_name, "vs_in%u", i); + GL_EXTCALL(glBindAttribLocation(program_id, i, tmp_name->buffer)); + if (vshader && vshader->reg_maps.shader_version.major >= 4) + { + string_buffer_sprintf(tmp_name, "vs_in_uint%u", i); + GL_EXTCALL(glBindAttribLocation(program_id, i, tmp_name->buffer)); + string_buffer_sprintf(tmp_name, "vs_in_int%u", i); + GL_EXTCALL(glBindAttribLocation(program_id, i, tmp_name->buffer)); + } + } + checkGLcall("glBindAttribLocation"); + + if (!use_legacy_fragment_output(gl_info)) + { + for (i = 0; i < WINED3D_MAX_RENDER_TARGETS; ++i) + { + string_buffer_sprintf(tmp_name, "color_out%u", i); + if (state->blend_state && state->blend_state->dual_source) + GL_EXTCALL(glBindFragDataLocationIndexed(program_id, 0, i, tmp_name->buffer)); + else + GL_EXTCALL(glBindFragDataLocation(program_id, i, tmp_name->buffer)); + checkGLcall("glBindFragDataLocation"); + } + } + string_buffer_release(&priv->string_buffers, tmp_name); + } + + if (hshader) + { + TRACE("Attaching GLSL tessellation control shader object %u to program %u.\n", hs_id, program_id); + GL_EXTCALL(glAttachShader(program_id, hs_id)); + checkGLcall("glAttachShader"); + + list_add_head(&hshader->linked_programs, &entry->hs.shader_entry); + } + + if (dshader) + { + TRACE("Attaching GLSL tessellation evaluation shader object %u to program %u.\n", ds_id, program_id); + GL_EXTCALL(glAttachShader(program_id, ds_id)); + checkGLcall("glAttachShader"); + + list_add_head(&dshader->linked_programs, &entry->ds.shader_entry); + } + + if (gshader) + { + TRACE("Attaching GLSL geometry shader object %u to program %u.\n", gs_id, program_id); + GL_EXTCALL(glAttachShader(program_id, gs_id)); + checkGLcall("glAttachShader"); + + shader_glsl_init_transform_feedback(context_gl, priv, program_id, gshader); + + list_add_head(&gshader->linked_programs, &entry->gs.shader_entry); + } + + /* Attach GLSL pshader */ + if (ps_id) + { + TRACE("Attaching GLSL shader object %u to program %u.\n", ps_id, program_id); + GL_EXTCALL(glAttachShader(program_id, ps_id)); + checkGLcall("glAttachShader"); + + list_add_head(ps_list, &entry->ps.shader_entry); + } + + /* Link the program */ + TRACE("Linking GLSL shader program %u.\n", program_id); + GL_EXTCALL(glLinkProgram(program_id)); + shader_glsl_validate_link(gl_info, program_id); + + shader_glsl_init_vs_uniform_locations(gl_info, priv, program_id, &entry->vs, + vshader ? vshader->limits->constant_float : 0); + shader_glsl_init_ds_uniform_locations(gl_info, priv, program_id, &entry->ds); + shader_glsl_init_gs_uniform_locations(gl_info, priv, program_id, &entry->gs); + shader_glsl_init_ps_uniform_locations(gl_info, priv, program_id, &entry->ps, + pshader ? pshader->limits->constant_float : 0); + checkGLcall("find glsl program uniform locations"); + + pre_rasterization_shader = gshader ? gshader : dshader ? dshader : vshader; + if (pre_rasterization_shader && pre_rasterization_shader->reg_maps.shader_version.major >= 4) + { + unsigned int clip_distance_count = wined3d_popcount(pre_rasterization_shader->reg_maps.clip_distance_mask); + entry->shader_controlled_clip_distances = 1; + entry->clip_distance_mask = (1u << clip_distance_count) - 1; + } + + if (needs_legacy_glsl_syntax(gl_info)) + { + if (pshader && pshader->reg_maps.shader_version.major >= 3 + && pshader->u.ps.declared_in_count > vec4_varyings(3, gl_info)) + { + TRACE("Shader %d needs vertex color clamping disabled.\n", program_id); + entry->vs.vertex_color_clamp = GL_FALSE; + } + else + { + entry->vs.vertex_color_clamp = GL_FIXED_ONLY_ARB; + } + } + else + { + /* With core profile we never change vertex_color_clamp from + * GL_FIXED_ONLY_MODE (which is also the initial value) so we never call + * glClampColorARB(). */ + entry->vs.vertex_color_clamp = GL_FIXED_ONLY_ARB; + } + + /* Set the shader to allow uniform loading on it */ + GL_EXTCALL(glUseProgram(program_id)); + checkGLcall("glUseProgram"); + + entry->constant_update_mask = 0; + if (vshader) + { + entry->constant_update_mask |= WINED3D_SHADER_CONST_VS_F; + if (vshader->reg_maps.integer_constants) + entry->constant_update_mask |= WINED3D_SHADER_CONST_VS_I; + if (vshader->reg_maps.boolean_constants) + entry->constant_update_mask |= WINED3D_SHADER_CONST_VS_B; + if (entry->vs.pos_fixup_location != -1) + entry->constant_update_mask |= WINED3D_SHADER_CONST_POS_FIXUP; + if (entry->vs.base_vertex_id_location != -1) + entry->constant_update_mask |= WINED3D_SHADER_CONST_BASE_VERTEX_ID; + + shader_glsl_load_program_resources(context_gl, priv, program_id, vshader); + } + else + { + entry->constant_update_mask |= WINED3D_SHADER_CONST_FFP_MODELVIEW + | WINED3D_SHADER_CONST_FFP_PROJ; + + for (i = 1; i < MAX_VERTEX_BLENDS; ++i) + { + if (entry->vs.modelview_matrix_location[i] != -1) + { + entry->constant_update_mask |= WINED3D_SHADER_CONST_FFP_VERTEXBLEND; + break; + } + } + + for (i = 0; i < WINED3D_MAX_TEXTURES; ++i) + { + if (entry->vs.texture_matrix_location[i] != -1) + { + entry->constant_update_mask |= WINED3D_SHADER_CONST_FFP_TEXMATRIX; + break; + } + } + if (entry->vs.material_ambient_location != -1 || entry->vs.material_diffuse_location != -1 + || entry->vs.material_specular_location != -1 + || entry->vs.material_emissive_location != -1 + || entry->vs.material_shininess_location != -1) + entry->constant_update_mask |= WINED3D_SHADER_CONST_FFP_MATERIAL; + if (entry->vs.light_ambient_location != -1) + entry->constant_update_mask |= WINED3D_SHADER_CONST_FFP_LIGHTS; + } + if (entry->vs.clip_planes_location != -1) + entry->constant_update_mask |= WINED3D_SHADER_CONST_VS_CLIP_PLANES; + if (entry->vs.pointsize_min_location != -1) + entry->constant_update_mask |= WINED3D_SHADER_CONST_VS_POINTSIZE; + + if (hshader) + shader_glsl_load_program_resources(context_gl, priv, program_id, hshader); + + if (dshader) + { + if (entry->ds.pos_fixup_location != -1) + entry->constant_update_mask |= WINED3D_SHADER_CONST_POS_FIXUP; + + shader_glsl_load_program_resources(context_gl, priv, program_id, dshader); + } + + if (gshader) + { + if (entry->gs.pos_fixup_location != -1) + entry->constant_update_mask |= WINED3D_SHADER_CONST_POS_FIXUP; + + shader_glsl_load_program_resources(context_gl, priv, program_id, gshader); + } + + if (ps_id) + { + if (pshader) + { + entry->constant_update_mask |= WINED3D_SHADER_CONST_PS_F; + if (pshader->reg_maps.integer_constants) + entry->constant_update_mask |= WINED3D_SHADER_CONST_PS_I; + if (pshader->reg_maps.boolean_constants) + entry->constant_update_mask |= WINED3D_SHADER_CONST_PS_B; + if (entry->ps.ycorrection_location != -1) + entry->constant_update_mask |= WINED3D_SHADER_CONST_PS_Y_CORR; + + shader_glsl_load_program_resources(context_gl, priv, program_id, pshader); + shader_glsl_load_images(gl_info, priv, program_id, &pshader->reg_maps); + } + else + { + entry->constant_update_mask |= WINED3D_SHADER_CONST_FFP_PS; + + shader_glsl_load_samplers(&context_gl->c, priv, program_id, NULL); + } + + for (i = 0; i < WINED3D_MAX_TEXTURES; ++i) + { + if (entry->ps.bumpenv_mat_location[i] != -1) + { + entry->constant_update_mask |= WINED3D_SHADER_CONST_PS_BUMP_ENV; + break; + } + } + + if (entry->ps.fog_color_location != -1) + entry->constant_update_mask |= WINED3D_SHADER_CONST_PS_FOG; + if (entry->ps.alpha_test_ref_location != -1) + entry->constant_update_mask |= WINED3D_SHADER_CONST_PS_ALPHA_TEST; + if (entry->ps.np2_fixup_location != -1) + entry->constant_update_mask |= WINED3D_SHADER_CONST_PS_NP2_FIXUP; + if (entry->ps.color_key_location != -1) + entry->constant_update_mask |= WINED3D_SHADER_CONST_FFP_COLOR_KEY; + } +} + +static void shader_glsl_precompile(void *shader_priv, struct wined3d_shader *shader) +{ + struct wined3d_device *device = shader->device; + struct wined3d_context *context; + + if (shader->reg_maps.shader_version.type == WINED3D_SHADER_TYPE_COMPUTE) + { + context = context_acquire(device, NULL, 0); + shader_glsl_compile_compute_shader(shader_priv, wined3d_context_gl(context), shader); + context_release(context); + } +} + +/* Context activation is done by the caller. */ +static void shader_glsl_select(void *shader_priv, struct wined3d_context *context, + const struct wined3d_state *state) +{ + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + struct glsl_context_data *ctx_data = context->shader_backend_data; + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + struct shader_glsl_priv *priv = shader_priv; + struct glsl_shader_prog_link *glsl_program; + GLenum current_vertex_color_clamp; + GLuint program_id, prev_id; + + priv->vertex_pipe->vp_enable(context, !use_vs(state)); + priv->fragment_pipe->fp_enable(context, !use_ps(state)); + + prev_id = ctx_data->glsl_program ? ctx_data->glsl_program->id : 0; + set_glsl_shader_program(context_gl, state, priv, ctx_data); + glsl_program = ctx_data->glsl_program; + + if (glsl_program) + { + program_id = glsl_program->id; + current_vertex_color_clamp = glsl_program->vs.vertex_color_clamp; + if (glsl_program->shader_controlled_clip_distances) + wined3d_context_gl_enable_clip_distances(context_gl, glsl_program->clip_distance_mask); + } + else + { + program_id = 0; + current_vertex_color_clamp = GL_FIXED_ONLY_ARB; + } + + if (ctx_data->vertex_color_clamp != current_vertex_color_clamp) + { + ctx_data->vertex_color_clamp = current_vertex_color_clamp; + if (gl_info->supported[ARB_COLOR_BUFFER_FLOAT]) + { + GL_EXTCALL(glClampColorARB(GL_CLAMP_VERTEX_COLOR_ARB, current_vertex_color_clamp)); + checkGLcall("glClampColorARB"); + } + else + { + FIXME("Vertex color clamp needs to be changed, but extension not supported.\n"); + } + } + + TRACE("Using GLSL program %u.\n", program_id); + + if (prev_id != program_id) + { + GL_EXTCALL(glUseProgram(program_id)); + checkGLcall("glUseProgram"); + + if (glsl_program) + context->constant_update_mask |= glsl_program->constant_update_mask; + } + + context->shader_update_mask |= (1u << WINED3D_SHADER_TYPE_COMPUTE); +} + +/* Context activation is done by the caller. */ +static void shader_glsl_select_compute(void *shader_priv, struct wined3d_context *context, + const struct wined3d_state *state) +{ + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + struct glsl_context_data *ctx_data = context->shader_backend_data; + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + struct shader_glsl_priv *priv = shader_priv; + GLuint program_id, prev_id; + + prev_id = ctx_data->glsl_program ? ctx_data->glsl_program->id : 0; + set_glsl_compute_shader_program(context_gl, state, priv, ctx_data); + program_id = ctx_data->glsl_program ? ctx_data->glsl_program->id : 0; + + TRACE("Using GLSL program %u.\n", program_id); + + if (prev_id != program_id) + { + GL_EXTCALL(glUseProgram(program_id)); + checkGLcall("glUseProgram"); + } + + context->shader_update_mask |= (1u << WINED3D_SHADER_TYPE_PIXEL) + | (1u << WINED3D_SHADER_TYPE_VERTEX) + | (1u << WINED3D_SHADER_TYPE_GEOMETRY) + | (1u << WINED3D_SHADER_TYPE_HULL) + | (1u << WINED3D_SHADER_TYPE_DOMAIN); +} + +/* "context" is not necessarily the currently active context. */ +static void shader_glsl_invalidate_current_program(struct wined3d_context *context) +{ + struct glsl_context_data *ctx_data = context->shader_backend_data; + + ctx_data->glsl_program = NULL; + context->shader_update_mask = (1u << WINED3D_SHADER_TYPE_PIXEL) + | (1u << WINED3D_SHADER_TYPE_VERTEX) + | (1u << WINED3D_SHADER_TYPE_GEOMETRY) + | (1u << WINED3D_SHADER_TYPE_HULL) + | (1u << WINED3D_SHADER_TYPE_DOMAIN) + | (1u << WINED3D_SHADER_TYPE_COMPUTE); +} + +/* Context activation is done by the caller. */ +static void shader_glsl_disable(void *shader_priv, struct wined3d_context *context) +{ + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + struct glsl_context_data *ctx_data = context->shader_backend_data; + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + struct shader_glsl_priv *priv = shader_priv; + + shader_glsl_invalidate_current_program(context); + GL_EXTCALL(glUseProgram(0)); + checkGLcall("glUseProgram"); + + priv->vertex_pipe->vp_enable(context, FALSE); + priv->fragment_pipe->fp_enable(context, FALSE); + + if (needs_legacy_glsl_syntax(gl_info) && gl_info->supported[ARB_COLOR_BUFFER_FLOAT]) + { + ctx_data->vertex_color_clamp = GL_FIXED_ONLY_ARB; + GL_EXTCALL(glClampColorARB(GL_CLAMP_VERTEX_COLOR_ARB, GL_FIXED_ONLY_ARB)); + checkGLcall("glClampColorARB"); + } +} + +static void shader_glsl_invalidate_contexts_program(struct wined3d_device *device, + const struct glsl_shader_prog_link *program) +{ + const struct glsl_context_data *ctx_data; + struct wined3d_context *context; + unsigned int i; + + for (i = 0; i < device->context_count; ++i) + { + context = device->contexts[i]; + ctx_data = context->shader_backend_data; + + if (ctx_data->glsl_program == program) + shader_glsl_invalidate_current_program(context); + } +} + +static void shader_glsl_destroy(struct wined3d_shader *shader) +{ + struct glsl_shader_private *shader_data = shader->backend_data; + struct wined3d_device *device = shader->device; + struct shader_glsl_priv *priv = device->shader_priv; + const struct wined3d_gl_info *gl_info; + const struct list *linked_programs; + struct wined3d_context *context; + + if (!shader_data || !shader_data->num_gl_shaders) + { + heap_free(shader_data); + shader->backend_data = NULL; + return; + } + + context = context_acquire(device, NULL, 0); + gl_info = wined3d_context_gl(context)->gl_info; + + TRACE("Deleting linked programs.\n"); + linked_programs = &shader->linked_programs; + if (linked_programs->next) + { + struct glsl_shader_prog_link *entry, *entry2; + UINT i; + + switch (shader->reg_maps.shader_version.type) + { + case WINED3D_SHADER_TYPE_PIXEL: + { + struct glsl_ps_compiled_shader *gl_shaders = shader_data->gl_shaders.ps; + + for (i = 0; i < shader_data->num_gl_shaders; ++i) + { + TRACE("Deleting pixel shader %u.\n", gl_shaders[i].id); + GL_EXTCALL(glDeleteShader(gl_shaders[i].id)); + checkGLcall("glDeleteShader"); + } + heap_free(shader_data->gl_shaders.ps); + + LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, linked_programs, + struct glsl_shader_prog_link, ps.shader_entry) + { + shader_glsl_invalidate_contexts_program(device, entry); + delete_glsl_program_entry(priv, gl_info, entry); + } + + break; + } + + case WINED3D_SHADER_TYPE_VERTEX: + { + struct glsl_vs_compiled_shader *gl_shaders = shader_data->gl_shaders.vs; + + for (i = 0; i < shader_data->num_gl_shaders; ++i) + { + TRACE("Deleting vertex shader %u.\n", gl_shaders[i].id); + GL_EXTCALL(glDeleteShader(gl_shaders[i].id)); + checkGLcall("glDeleteShader"); + } + heap_free(shader_data->gl_shaders.vs); + + LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, linked_programs, + struct glsl_shader_prog_link, vs.shader_entry) + { + shader_glsl_invalidate_contexts_program(device, entry); + delete_glsl_program_entry(priv, gl_info, entry); + } + + break; + } + + case WINED3D_SHADER_TYPE_HULL: + { + struct glsl_hs_compiled_shader *gl_shaders = shader_data->gl_shaders.hs; + + for (i = 0; i < shader_data->num_gl_shaders; ++i) + { + TRACE("Deleting hull shader %u.\n", gl_shaders[i].id); + GL_EXTCALL(glDeleteShader(gl_shaders[i].id)); + checkGLcall("glDeleteShader"); + } + heap_free(shader_data->gl_shaders.hs); + + LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, linked_programs, + struct glsl_shader_prog_link, hs.shader_entry) + { + shader_glsl_invalidate_contexts_program(device, entry); + delete_glsl_program_entry(priv, gl_info, entry); + } + + break; + } + + case WINED3D_SHADER_TYPE_DOMAIN: + { + struct glsl_ds_compiled_shader *gl_shaders = shader_data->gl_shaders.ds; + + for (i = 0; i < shader_data->num_gl_shaders; ++i) + { + TRACE("Deleting domain shader %u.\n", gl_shaders[i].id); + GL_EXTCALL(glDeleteShader(gl_shaders[i].id)); + checkGLcall("glDeleteShader"); + } + heap_free(shader_data->gl_shaders.ds); + + LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, linked_programs, + struct glsl_shader_prog_link, ds.shader_entry) + { + shader_glsl_invalidate_contexts_program(device, entry); + delete_glsl_program_entry(priv, gl_info, entry); + } + + break; + } + + case WINED3D_SHADER_TYPE_GEOMETRY: + { + struct glsl_gs_compiled_shader *gl_shaders = shader_data->gl_shaders.gs; + + for (i = 0; i < shader_data->num_gl_shaders; ++i) + { + TRACE("Deleting geometry shader %u.\n", gl_shaders[i].id); + GL_EXTCALL(glDeleteShader(gl_shaders[i].id)); + checkGLcall("glDeleteShader"); + } + heap_free(shader_data->gl_shaders.gs); + + LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, linked_programs, + struct glsl_shader_prog_link, gs.shader_entry) + { + shader_glsl_invalidate_contexts_program(device, entry); + delete_glsl_program_entry(priv, gl_info, entry); + } + + break; + } + + case WINED3D_SHADER_TYPE_COMPUTE: + { + struct glsl_cs_compiled_shader *gl_shaders = shader_data->gl_shaders.cs; + + for (i = 0; i < shader_data->num_gl_shaders; ++i) + { + TRACE("Deleting compute shader %u.\n", gl_shaders[i].id); + GL_EXTCALL(glDeleteShader(gl_shaders[i].id)); + checkGLcall("glDeleteShader"); + } + heap_free(shader_data->gl_shaders.cs); + + LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, linked_programs, + struct glsl_shader_prog_link, cs.shader_entry) + { + shader_glsl_invalidate_contexts_program(device, entry); + delete_glsl_program_entry(priv, gl_info, entry); + } + + break; + } + + default: + ERR("Unhandled shader type %#x.\n", shader->reg_maps.shader_version.type); + break; + } + } + + heap_free(shader->backend_data); + shader->backend_data = NULL; + + context_release(context); +} + +static int glsl_program_key_compare(const void *key, const struct wine_rb_entry *entry) +{ + const struct glsl_program_key *k = key; + const struct glsl_shader_prog_link *prog = WINE_RB_ENTRY_VALUE(entry, + const struct glsl_shader_prog_link, program_lookup_entry); + + if (k->vs_id > prog->vs.id) return 1; + else if (k->vs_id < prog->vs.id) return -1; + + if (k->gs_id > prog->gs.id) return 1; + else if (k->gs_id < prog->gs.id) return -1; + + if (k->ps_id > prog->ps.id) return 1; + else if (k->ps_id < prog->ps.id) return -1; + + if (k->hs_id > prog->hs.id) return 1; + else if (k->hs_id < prog->hs.id) return -1; + + if (k->ds_id > prog->ds.id) return 1; + else if (k->ds_id < prog->ds.id) return -1; + + if (k->cs_id > prog->cs.id) return 1; + else if (k->cs_id < prog->cs.id) return -1; + + return 0; +} + +static BOOL constant_heap_init(struct constant_heap *heap, unsigned int constant_count) +{ + SIZE_T size = (constant_count + 1) * sizeof(*heap->entries) + + constant_count * sizeof(*heap->contained) + + constant_count * sizeof(*heap->positions); + void *mem; + + if (!(mem = heap_alloc(size))) + { + ERR("Failed to allocate memory\n"); + return FALSE; + } + + heap->entries = mem; + heap->entries[1].version = 0; + heap->contained = (BOOL *)(heap->entries + constant_count + 1); + memset(heap->contained, 0, constant_count * sizeof(*heap->contained)); + heap->positions = (unsigned int *)(heap->contained + constant_count); + heap->size = 1; + + return TRUE; +} + +static void constant_heap_free(struct constant_heap *heap) +{ + heap_free(heap->entries); +} + +static HRESULT shader_glsl_alloc(struct wined3d_device *device, const struct wined3d_vertex_pipe_ops *vertex_pipe, + const struct wined3d_fragment_pipe_ops *fragment_pipe) +{ + SIZE_T stack_size = wined3d_log2i(max(WINED3D_MAX_VS_CONSTS_F, WINED3D_MAX_PS_CONSTS_F)) + 1; + struct fragment_caps fragment_caps; + void *vertex_priv, *fragment_priv; + struct shader_glsl_priv *priv; + + if (!(priv = heap_alloc_zero(sizeof(*priv)))) + return E_OUTOFMEMORY; + + string_buffer_list_init(&priv->string_buffers); + + if (!(vertex_priv = vertex_pipe->vp_alloc(&glsl_shader_backend, priv))) + { + ERR("Failed to initialize vertex pipe.\n"); + heap_free(priv); + return E_FAIL; + } + + if (!(fragment_priv = fragment_pipe->alloc_private(&glsl_shader_backend, priv))) + { + ERR("Failed to initialize fragment pipe.\n"); + vertex_pipe->vp_free(device, NULL); + heap_free(priv); + return E_FAIL; + } + + if (!string_buffer_init(&priv->shader_buffer)) + { + ERR("Failed to initialize shader buffer.\n"); + goto fail; + } + + if (!(priv->stack = heap_calloc(stack_size, sizeof(*priv->stack)))) + { + ERR("Failed to allocate memory.\n"); + goto fail; + } + + if (!constant_heap_init(&priv->vconst_heap, WINED3D_MAX_VS_CONSTS_F)) + { + ERR("Failed to initialize vertex shader constant heap\n"); + goto fail; + } + + if (!constant_heap_init(&priv->pconst_heap, WINED3D_MAX_PS_CONSTS_F)) + { + ERR("Failed to initialize pixel shader constant heap\n"); + goto fail; + } + + wine_rb_init(&priv->program_lookup, glsl_program_key_compare); + + priv->next_constant_version = 1; + priv->vertex_pipe = vertex_pipe; + priv->fragment_pipe = fragment_pipe; + fragment_pipe->get_caps(device->adapter, &fragment_caps); + priv->ffp_proj_control = fragment_caps.wined3d_caps & WINED3D_FRAGMENT_CAP_PROJ_CONTROL; + priv->legacy_lighting = device->wined3d->flags & WINED3D_LEGACY_FFP_LIGHTING; + + device->vertex_priv = vertex_priv; + device->fragment_priv = fragment_priv; + device->shader_priv = priv; + + return WINED3D_OK; + +fail: + constant_heap_free(&priv->pconst_heap); + constant_heap_free(&priv->vconst_heap); + heap_free(priv->stack); + string_buffer_free(&priv->shader_buffer); + fragment_pipe->free_private(device, NULL); + vertex_pipe->vp_free(device, NULL); + heap_free(priv); + return E_OUTOFMEMORY; +} + +/* Context activation is done by the caller. */ +static void shader_glsl_free(struct wined3d_device *device, struct wined3d_context *context) +{ + struct shader_glsl_priv *priv = device->shader_priv; + + wine_rb_destroy(&priv->program_lookup, NULL, NULL); + constant_heap_free(&priv->pconst_heap); + constant_heap_free(&priv->vconst_heap); + heap_free(priv->stack); + string_buffer_list_cleanup(&priv->string_buffers); + string_buffer_free(&priv->shader_buffer); + priv->fragment_pipe->free_private(device, context); + priv->vertex_pipe->vp_free(device, context); + + heap_free(device->shader_priv); + device->shader_priv = NULL; +} + +static BOOL shader_glsl_allocate_context_data(struct wined3d_context *context) +{ + struct glsl_context_data *ctx_data; + + if (!(ctx_data = heap_alloc_zero(sizeof(*ctx_data)))) + return FALSE; + ctx_data->vertex_color_clamp = GL_FIXED_ONLY_ARB; + context->shader_backend_data = ctx_data; + return TRUE; +} + +static void shader_glsl_free_context_data(struct wined3d_context *context) +{ + heap_free(context->shader_backend_data); +} + +static void shader_glsl_init_context_state(struct wined3d_context *context) +{ + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + + gl_info->gl_ops.gl.p_glEnable(GL_PROGRAM_POINT_SIZE); + checkGLcall("GL_PROGRAM_POINT_SIZE"); +} + +static unsigned int shader_glsl_get_shader_model(const struct wined3d_gl_info *gl_info) +{ + BOOL shader_model_4 = gl_info->glsl_version >= MAKEDWORD_VERSION(1, 50) + && gl_info->supported[ARB_SHADER_BIT_ENCODING] + && gl_info->supported[ARB_TEXTURE_SWIZZLE]; + + if (shader_model_4 + && gl_info->supported[ARB_COMPUTE_SHADER] + && gl_info->supported[ARB_CULL_DISTANCE] + && gl_info->supported[ARB_DERIVATIVE_CONTROL] + && gl_info->supported[ARB_GPU_SHADER5] + && gl_info->supported[ARB_SHADER_ATOMIC_COUNTERS] + && gl_info->supported[ARB_SHADER_IMAGE_LOAD_STORE] + && gl_info->supported[ARB_SHADER_IMAGE_SIZE] + && gl_info->supported[ARB_SHADING_LANGUAGE_PACKING] + && gl_info->supported[ARB_TESSELLATION_SHADER] + && gl_info->supported[ARB_TEXTURE_GATHER] + && gl_info->supported[ARB_TRANSFORM_FEEDBACK3]) + return 5; + + if (shader_model_4) + return 4; + + /* Support for texldd and texldl instructions in pixel shaders is required + * for SM3. */ + if (shader_glsl_has_core_grad(gl_info) || gl_info->supported[ARB_SHADER_TEXTURE_LOD]) + return 3; + + return 2; +} + +static void shader_glsl_get_caps(const struct wined3d_adapter *adapter, struct shader_caps *caps) +{ + const struct wined3d_gl_info *gl_info = &adapter->gl_info; + unsigned int shader_model = shader_glsl_get_shader_model(gl_info); + + TRACE("Shader model %u.\n", shader_model); + + caps->vs_version = min(wined3d_settings.max_sm_vs, shader_model); + caps->hs_version = min(wined3d_settings.max_sm_hs, shader_model); + caps->ds_version = min(wined3d_settings.max_sm_ds, shader_model); + caps->gs_version = min(wined3d_settings.max_sm_gs, shader_model); + caps->ps_version = min(wined3d_settings.max_sm_ps, shader_model); + caps->cs_version = min(wined3d_settings.max_sm_cs, shader_model); + + caps->vs_version = gl_info->supported[ARB_VERTEX_SHADER] ? caps->vs_version : 0; + caps->ps_version = gl_info->supported[ARB_FRAGMENT_SHADER] ? caps->ps_version : 0; + + caps->vs_uniform_count = min(WINED3D_MAX_VS_CONSTS_F, gl_info->limits.glsl_vs_float_constants); + caps->ps_uniform_count = min(WINED3D_MAX_PS_CONSTS_F, gl_info->limits.glsl_ps_float_constants); + caps->varying_count = gl_info->limits.glsl_varyings; + + /* FIXME: The following line is card dependent. -8.0 to 8.0 is the + * Direct3D minimum requirement. + * + * Both GL_ARB_fragment_program and GLSL require a "maximum representable magnitude" + * of colors to be 2^10, and 2^32 for other floats. Should we use 1024 here? + * + * The problem is that the refrast clamps temporary results in the shader to + * [-MaxValue;+MaxValue]. If the card's max value is bigger than the one we advertize here, + * then applications may miss the clamping behavior. On the other hand, if it is smaller, + * the shader will generate incorrect results too. Unfortunately, GL deliberately doesn't + * offer a way to query this. + */ + if (shader_model >= 4) + caps->ps_1x_max_value = FLT_MAX; + else + caps->ps_1x_max_value = 1024.0f; + + /* Ideally we'd only set caps like sRGB writes here if supported by both + * the shader backend and the fragment pipe, but we can get called before + * shader_glsl_alloc(). */ + caps->wined3d_caps = WINED3D_SHADER_CAP_VS_CLIPPING + | WINED3D_SHADER_CAP_SRGB_WRITE; + if (needs_interpolation_qualifiers_for_shader_outputs(gl_info)) + caps->wined3d_caps |= WINED3D_SHADER_CAP_OUTPUT_INTERPOLATION; + if (shader_glsl_full_ffp_varyings(gl_info)) + caps->wined3d_caps |= WINED3D_SHADER_CAP_FULL_FFP_VARYINGS; +} + +static BOOL shader_glsl_color_fixup_supported(struct color_fixup_desc fixup) +{ + /* We support everything except YUV conversions. */ + return !is_complex_fixup(fixup); +} + +static const SHADER_HANDLER shader_glsl_instruction_handler_table[WINED3DSIH_TABLE_SIZE] = +{ + /* WINED3DSIH_ABS */ shader_glsl_map2gl, + /* WINED3DSIH_ADD */ shader_glsl_binop, + /* WINED3DSIH_AND */ shader_glsl_binop, + /* WINED3DSIH_ATOMIC_AND */ shader_glsl_atomic, + /* WINED3DSIH_ATOMIC_CMP_STORE */ shader_glsl_atomic, + /* WINED3DSIH_ATOMIC_IADD */ shader_glsl_atomic, + /* WINED3DSIH_ATOMIC_IMAX */ shader_glsl_atomic, + /* WINED3DSIH_ATOMIC_IMIN */ shader_glsl_atomic, + /* WINED3DSIH_ATOMIC_OR */ shader_glsl_atomic, + /* WINED3DSIH_ATOMIC_UMAX */ shader_glsl_atomic, + /* WINED3DSIH_ATOMIC_UMIN */ shader_glsl_atomic, + /* WINED3DSIH_ATOMIC_XOR */ shader_glsl_atomic, + /* WINED3DSIH_BEM */ shader_glsl_bem, + /* WINED3DSIH_BFI */ shader_glsl_bitwise_op, + /* WINED3DSIH_BFREV */ shader_glsl_map2gl, + /* WINED3DSIH_BREAK */ shader_glsl_break, + /* WINED3DSIH_BREAKC */ shader_glsl_breakc, + /* WINED3DSIH_BREAKP */ shader_glsl_conditional_op, + /* WINED3DSIH_BUFINFO */ shader_glsl_bufinfo, + /* WINED3DSIH_CALL */ shader_glsl_call, + /* WINED3DSIH_CALLNZ */ shader_glsl_callnz, + /* WINED3DSIH_CASE */ shader_glsl_case, + /* WINED3DSIH_CMP */ shader_glsl_conditional_move, + /* WINED3DSIH_CND */ shader_glsl_cnd, + /* WINED3DSIH_CONTINUE */ shader_glsl_continue, + /* WINED3DSIH_CONTINUEP */ shader_glsl_conditional_op, + /* WINED3DSIH_COUNTBITS */ shader_glsl_map2gl, + /* WINED3DSIH_CRS */ shader_glsl_cross, + /* WINED3DSIH_CUT */ shader_glsl_cut, + /* WINED3DSIH_CUT_STREAM */ shader_glsl_cut, + /* WINED3DSIH_DCL */ shader_glsl_nop, + /* WINED3DSIH_DCL_CONSTANT_BUFFER */ shader_glsl_nop, + /* WINED3DSIH_DCL_FUNCTION_BODY */ NULL, + /* WINED3DSIH_DCL_FUNCTION_TABLE */ NULL, + /* WINED3DSIH_DCL_GLOBAL_FLAGS */ shader_glsl_nop, + /* WINED3DSIH_DCL_GS_INSTANCES */ shader_glsl_nop, + /* WINED3DSIH_DCL_HS_FORK_PHASE_INSTANCE_COUNT */ shader_glsl_nop, + /* WINED3DSIH_DCL_HS_JOIN_PHASE_INSTANCE_COUNT */ shader_glsl_nop, + /* WINED3DSIH_DCL_HS_MAX_TESSFACTOR */ shader_glsl_nop, + /* WINED3DSIH_DCL_IMMEDIATE_CONSTANT_BUFFER */ shader_glsl_nop, + /* WINED3DSIH_DCL_INDEX_RANGE */ shader_glsl_nop, + /* WINED3DSIH_DCL_INDEXABLE_TEMP */ shader_glsl_nop, + /* WINED3DSIH_DCL_INPUT */ shader_glsl_nop, + /* WINED3DSIH_DCL_INPUT_CONTROL_POINT_COUNT */ shader_glsl_nop, + /* WINED3DSIH_DCL_INPUT_PRIMITIVE */ shader_glsl_nop, + /* WINED3DSIH_DCL_INPUT_PS */ shader_glsl_nop, + /* WINED3DSIH_DCL_INPUT_PS_SGV */ shader_glsl_nop, + /* WINED3DSIH_DCL_INPUT_PS_SIV */ shader_glsl_nop, + /* WINED3DSIH_DCL_INPUT_SGV */ shader_glsl_nop, + /* WINED3DSIH_DCL_INPUT_SIV */ shader_glsl_nop, + /* WINED3DSIH_DCL_INTERFACE */ NULL, + /* WINED3DSIH_DCL_OUTPUT */ shader_glsl_nop, + /* WINED3DSIH_DCL_OUTPUT_CONTROL_POINT_COUNT */ shader_glsl_nop, + /* WINED3DSIH_DCL_OUTPUT_SIV */ shader_glsl_nop, + /* WINED3DSIH_DCL_OUTPUT_TOPOLOGY */ shader_glsl_nop, + /* WINED3DSIH_DCL_RESOURCE_RAW */ shader_glsl_nop, + /* WINED3DSIH_DCL_RESOURCE_STRUCTURED */ shader_glsl_nop, + /* WINED3DSIH_DCL_SAMPLER */ shader_glsl_nop, + /* WINED3DSIH_DCL_STREAM */ NULL, + /* WINED3DSIH_DCL_TEMPS */ shader_glsl_nop, + /* WINED3DSIH_DCL_TESSELLATOR_DOMAIN */ shader_glsl_nop, + /* WINED3DSIH_DCL_TESSELLATOR_OUTPUT_PRIMITIVE */ shader_glsl_nop, + /* WINED3DSIH_DCL_TESSELLATOR_PARTITIONING */ shader_glsl_nop, + /* WINED3DSIH_DCL_TGSM_RAW */ shader_glsl_nop, + /* WINED3DSIH_DCL_TGSM_STRUCTURED */ shader_glsl_nop, + /* WINED3DSIH_DCL_THREAD_GROUP */ shader_glsl_nop, + /* WINED3DSIH_DCL_UAV_RAW */ shader_glsl_nop, + /* WINED3DSIH_DCL_UAV_STRUCTURED */ shader_glsl_nop, + /* WINED3DSIH_DCL_UAV_TYPED */ shader_glsl_nop, + /* WINED3DSIH_DCL_VERTICES_OUT */ shader_glsl_nop, + /* WINED3DSIH_DEF */ shader_glsl_nop, + /* WINED3DSIH_DEFAULT */ shader_glsl_default, + /* WINED3DSIH_DEFB */ shader_glsl_nop, + /* WINED3DSIH_DEFI */ shader_glsl_nop, + /* WINED3DSIH_DIV */ shader_glsl_binop, + /* WINED3DSIH_DP2 */ shader_glsl_dot, + /* WINED3DSIH_DP2ADD */ shader_glsl_dp2add, + /* WINED3DSIH_DP3 */ shader_glsl_dot, + /* WINED3DSIH_DP4 */ shader_glsl_dot, + /* WINED3DSIH_DST */ shader_glsl_dst, + /* WINED3DSIH_DSX */ shader_glsl_map2gl, + /* WINED3DSIH_DSX_COARSE */ shader_glsl_map2gl, + /* WINED3DSIH_DSX_FINE */ shader_glsl_map2gl, + /* WINED3DSIH_DSY */ shader_glsl_map2gl, + /* WINED3DSIH_DSY_COARSE */ shader_glsl_map2gl, + /* WINED3DSIH_DSY_FINE */ shader_glsl_map2gl, + /* WINED3DSIH_ELSE */ shader_glsl_else, + /* WINED3DSIH_EMIT */ shader_glsl_emit, + /* WINED3DSIH_EMIT_STREAM */ shader_glsl_emit, + /* WINED3DSIH_ENDIF */ shader_glsl_end, + /* WINED3DSIH_ENDLOOP */ shader_glsl_end, + /* WINED3DSIH_ENDREP */ shader_glsl_end, + /* WINED3DSIH_ENDSWITCH */ shader_glsl_end, + /* WINED3DSIH_EQ */ shader_glsl_relop, + /* WINED3DSIH_EVAL_SAMPLE_INDEX */ shader_glsl_interpolate, + /* WINED3DSIH_EXP */ shader_glsl_scalar_op, + /* WINED3DSIH_EXPP */ shader_glsl_expp, + /* WINED3DSIH_F16TOF32 */ shader_glsl_float16, + /* WINED3DSIH_F32TOF16 */ shader_glsl_float16, + /* WINED3DSIH_FCALL */ NULL, + /* WINED3DSIH_FIRSTBIT_HI */ shader_glsl_map2gl, + /* WINED3DSIH_FIRSTBIT_LO */ shader_glsl_map2gl, + /* WINED3DSIH_FIRSTBIT_SHI */ shader_glsl_map2gl, + /* WINED3DSIH_FRC */ shader_glsl_map2gl, + /* WINED3DSIH_FTOI */ shader_glsl_to_int, + /* WINED3DSIH_FTOU */ shader_glsl_to_uint, + /* WINED3DSIH_GATHER4 */ shader_glsl_gather4, + /* WINED3DSIH_GATHER4_C */ shader_glsl_gather4, + /* WINED3DSIH_GATHER4_PO */ shader_glsl_gather4, + /* WINED3DSIH_GATHER4_PO_C */ shader_glsl_gather4, + /* WINED3DSIH_GE */ shader_glsl_relop, + /* WINED3DSIH_HS_CONTROL_POINT_PHASE */ shader_glsl_nop, + /* WINED3DSIH_HS_DECLS */ shader_glsl_nop, + /* WINED3DSIH_HS_FORK_PHASE */ shader_glsl_nop, + /* WINED3DSIH_HS_JOIN_PHASE */ shader_glsl_nop, + /* WINED3DSIH_IADD */ shader_glsl_binop, + /* WINED3DSIH_IBFE */ shader_glsl_bitwise_op, + /* WINED3DSIH_IEQ */ shader_glsl_relop, + /* WINED3DSIH_IF */ shader_glsl_if, + /* WINED3DSIH_IFC */ shader_glsl_ifc, + /* WINED3DSIH_IGE */ shader_glsl_relop, + /* WINED3DSIH_ILT */ shader_glsl_relop, + /* WINED3DSIH_IMAD */ shader_glsl_mad, + /* WINED3DSIH_IMAX */ shader_glsl_map2gl, + /* WINED3DSIH_IMIN */ shader_glsl_map2gl, + /* WINED3DSIH_IMM_ATOMIC_ALLOC */ shader_glsl_uav_counter, + /* WINED3DSIH_IMM_ATOMIC_AND */ shader_glsl_atomic, + /* WINED3DSIH_IMM_ATOMIC_CMP_EXCH */ shader_glsl_atomic, + /* WINED3DSIH_IMM_ATOMIC_CONSUME */ shader_glsl_uav_counter, + /* WINED3DSIH_IMM_ATOMIC_EXCH */ shader_glsl_atomic, + /* WINED3DSIH_IMM_ATOMIC_IADD */ shader_glsl_atomic, + /* WINED3DSIH_IMM_ATOMIC_IMAX */ shader_glsl_atomic, + /* WINED3DSIH_IMM_ATOMIC_IMIN */ shader_glsl_atomic, + /* WINED3DSIH_IMM_ATOMIC_OR */ shader_glsl_atomic, + /* WINED3DSIH_IMM_ATOMIC_UMAX */ shader_glsl_atomic, + /* WINED3DSIH_IMM_ATOMIC_UMIN */ shader_glsl_atomic, + /* WINED3DSIH_IMM_ATOMIC_XOR */ shader_glsl_atomic, + /* WINED3DSIH_IMUL */ shader_glsl_mul_extended, + /* WINED3DSIH_INE */ shader_glsl_relop, + /* WINED3DSIH_INEG */ shader_glsl_unary_op, + /* WINED3DSIH_ISHL */ shader_glsl_binop, + /* WINED3DSIH_ISHR */ shader_glsl_binop, + /* WINED3DSIH_ITOF */ shader_glsl_to_float, + /* WINED3DSIH_LABEL */ shader_glsl_label, + /* WINED3DSIH_LD */ shader_glsl_ld, + /* WINED3DSIH_LD2DMS */ shader_glsl_ld, + /* WINED3DSIH_LD_RAW */ shader_glsl_ld_raw_structured, + /* WINED3DSIH_LD_STRUCTURED */ shader_glsl_ld_raw_structured, + /* WINED3DSIH_LD_UAV_TYPED */ shader_glsl_ld_uav, + /* WINED3DSIH_LIT */ shader_glsl_lit, + /* WINED3DSIH_LOD */ NULL, + /* WINED3DSIH_LOG */ shader_glsl_scalar_op, + /* WINED3DSIH_LOGP */ shader_glsl_scalar_op, + /* WINED3DSIH_LOOP */ shader_glsl_loop, + /* WINED3DSIH_LRP */ shader_glsl_lrp, + /* WINED3DSIH_LT */ shader_glsl_relop, + /* WINED3DSIH_M3x2 */ shader_glsl_mnxn, + /* WINED3DSIH_M3x3 */ shader_glsl_mnxn, + /* WINED3DSIH_M3x4 */ shader_glsl_mnxn, + /* WINED3DSIH_M4x3 */ shader_glsl_mnxn, + /* WINED3DSIH_M4x4 */ shader_glsl_mnxn, + /* WINED3DSIH_MAD */ shader_glsl_mad, + /* WINED3DSIH_MAX */ shader_glsl_map2gl, + /* WINED3DSIH_MIN */ shader_glsl_map2gl, + /* WINED3DSIH_MOV */ shader_glsl_mov, + /* WINED3DSIH_MOVA */ shader_glsl_mov, + /* WINED3DSIH_MOVC */ shader_glsl_conditional_move, + /* WINED3DSIH_MUL */ shader_glsl_binop, + /* WINED3DSIH_NE */ shader_glsl_relop, + /* WINED3DSIH_NOP */ shader_glsl_nop, + /* WINED3DSIH_NOT */ shader_glsl_unary_op, + /* WINED3DSIH_NRM */ shader_glsl_nrm, + /* WINED3DSIH_OR */ shader_glsl_binop, + /* WINED3DSIH_PHASE */ shader_glsl_nop, + /* WINED3DSIH_POW */ shader_glsl_pow, + /* WINED3DSIH_RCP */ shader_glsl_scalar_op, + /* WINED3DSIH_REP */ shader_glsl_rep, + /* WINED3DSIH_RESINFO */ shader_glsl_resinfo, + /* WINED3DSIH_RET */ shader_glsl_ret, + /* WINED3DSIH_RETP */ shader_glsl_conditional_op, + /* WINED3DSIH_ROUND_NE */ shader_glsl_map2gl, + /* WINED3DSIH_ROUND_NI */ shader_glsl_map2gl, + /* WINED3DSIH_ROUND_PI */ shader_glsl_map2gl, + /* WINED3DSIH_ROUND_Z */ shader_glsl_map2gl, + /* WINED3DSIH_RSQ */ shader_glsl_scalar_op, + /* WINED3DSIH_SAMPLE */ shader_glsl_sample, + /* WINED3DSIH_SAMPLE_B */ shader_glsl_sample, + /* WINED3DSIH_SAMPLE_C */ shader_glsl_sample_c, + /* WINED3DSIH_SAMPLE_C_LZ */ shader_glsl_sample_c, + /* WINED3DSIH_SAMPLE_GRAD */ shader_glsl_sample, + /* WINED3DSIH_SAMPLE_INFO */ shader_glsl_sample_info, + /* WINED3DSIH_SAMPLE_LOD */ shader_glsl_sample, + /* WINED3DSIH_SAMPLE_POS */ NULL, + /* WINED3DSIH_SETP */ NULL, + /* WINED3DSIH_SGE */ shader_glsl_compare, + /* WINED3DSIH_SGN */ shader_glsl_sgn, + /* WINED3DSIH_SINCOS */ shader_glsl_sincos, + /* WINED3DSIH_SLT */ shader_glsl_compare, + /* WINED3DSIH_SQRT */ shader_glsl_map2gl, + /* WINED3DSIH_STORE_RAW */ shader_glsl_store_raw_structured, + /* WINED3DSIH_STORE_STRUCTURED */ shader_glsl_store_raw_structured, + /* WINED3DSIH_STORE_UAV_TYPED */ shader_glsl_store_uav, + /* WINED3DSIH_SUB */ shader_glsl_binop, + /* WINED3DSIH_SWAPC */ shader_glsl_swapc, + /* WINED3DSIH_SWITCH */ shader_glsl_switch, + /* WINED3DSIH_SYNC */ shader_glsl_sync, + /* WINED3DSIH_TEX */ shader_glsl_tex, + /* WINED3DSIH_TEXBEM */ shader_glsl_texbem, + /* WINED3DSIH_TEXBEML */ shader_glsl_texbem, + /* WINED3DSIH_TEXCOORD */ shader_glsl_texcoord, + /* WINED3DSIH_TEXDEPTH */ shader_glsl_texdepth, + /* WINED3DSIH_TEXDP3 */ shader_glsl_texdp3, + /* WINED3DSIH_TEXDP3TEX */ shader_glsl_texdp3tex, + /* WINED3DSIH_TEXKILL */ shader_glsl_texkill, + /* WINED3DSIH_TEXLDD */ shader_glsl_texldd, + /* WINED3DSIH_TEXLDL */ shader_glsl_texldl, + /* WINED3DSIH_TEXM3x2DEPTH */ shader_glsl_texm3x2depth, + /* WINED3DSIH_TEXM3x2PAD */ shader_glsl_texm3x2pad, + /* WINED3DSIH_TEXM3x2TEX */ shader_glsl_texm3x2tex, + /* WINED3DSIH_TEXM3x3 */ shader_glsl_texm3x3, + /* WINED3DSIH_TEXM3x3DIFF */ NULL, + /* WINED3DSIH_TEXM3x3PAD */ shader_glsl_texm3x3pad, + /* WINED3DSIH_TEXM3x3SPEC */ shader_glsl_texm3x3spec, + /* WINED3DSIH_TEXM3x3TEX */ shader_glsl_texm3x3tex, + /* WINED3DSIH_TEXM3x3VSPEC */ shader_glsl_texm3x3vspec, + /* WINED3DSIH_TEXREG2AR */ shader_glsl_texreg2ar, + /* WINED3DSIH_TEXREG2GB */ shader_glsl_texreg2gb, + /* WINED3DSIH_TEXREG2RGB */ shader_glsl_texreg2rgb, + /* WINED3DSIH_UBFE */ shader_glsl_bitwise_op, + /* WINED3DSIH_UDIV */ shader_glsl_udiv, + /* WINED3DSIH_UGE */ shader_glsl_relop, + /* WINED3DSIH_ULT */ shader_glsl_relop, + /* WINED3DSIH_UMAX */ shader_glsl_map2gl, + /* WINED3DSIH_UMIN */ shader_glsl_map2gl, + /* WINED3DSIH_UMUL */ shader_glsl_mul_extended, + /* WINED3DSIH_USHR */ shader_glsl_binop, + /* WINED3DSIH_UTOF */ shader_glsl_to_float, + /* WINED3DSIH_XOR */ shader_glsl_binop, +}; + +static void shader_glsl_handle_instruction(const struct wined3d_shader_instruction *ins) { + SHADER_HANDLER hw_fct; + + /* Select handler */ + hw_fct = shader_glsl_instruction_handler_table[ins->handler_idx]; + + /* Unhandled opcode */ + if (!hw_fct) + { + FIXME("Backend can't handle opcode %s.\n", debug_d3dshaderinstructionhandler(ins->handler_idx)); + return; + } + hw_fct(ins); + + shader_glsl_add_instruction_modifiers(ins); +} + +static BOOL shader_glsl_has_ffp_proj_control(void *shader_priv) +{ + struct shader_glsl_priv *priv = shader_priv; + + return priv->ffp_proj_control; +} + +const struct wined3d_shader_backend_ops glsl_shader_backend = +{ + shader_glsl_handle_instruction, + shader_glsl_precompile, + shader_glsl_select, + shader_glsl_select_compute, + shader_glsl_disable, + shader_glsl_update_float_vertex_constants, + shader_glsl_update_float_pixel_constants, + shader_glsl_load_constants, + shader_glsl_destroy, + shader_glsl_alloc, + shader_glsl_free, + shader_glsl_allocate_context_data, + shader_glsl_free_context_data, + shader_glsl_init_context_state, + shader_glsl_get_caps, + shader_glsl_color_fixup_supported, + shader_glsl_has_ffp_proj_control, +}; + +static void glsl_vertex_pipe_vp_enable(const struct wined3d_context *context, BOOL enable) {} + +static void glsl_vertex_pipe_vp_get_caps(const struct wined3d_adapter *adapter, struct wined3d_vertex_caps *caps) +{ + const struct wined3d_gl_info *gl_info = &adapter->gl_info; + + caps->xyzrhw = TRUE; + caps->emulated_flatshading = !needs_legacy_glsl_syntax(gl_info); + caps->ffp_generic_attributes = TRUE; + caps->max_active_lights = WINED3D_MAX_ACTIVE_LIGHTS; + caps->max_vertex_blend_matrices = MAX_VERTEX_BLENDS; + caps->max_vertex_blend_matrix_index = 0; + caps->vertex_processing_caps = WINED3DVTXPCAPS_TEXGEN + | WINED3DVTXPCAPS_MATERIALSOURCE7 + | WINED3DVTXPCAPS_VERTEXFOG + | WINED3DVTXPCAPS_DIRECTIONALLIGHTS + | WINED3DVTXPCAPS_POSITIONALLIGHTS + | WINED3DVTXPCAPS_LOCALVIEWER + | WINED3DVTXPCAPS_TEXGEN_SPHEREMAP; + caps->fvf_caps = WINED3DFVFCAPS_PSIZE | 8; /* 8 texture coordinates. */ + caps->max_user_clip_planes = gl_info->limits.user_clip_distances; + caps->raster_caps = WINED3DPRASTERCAPS_FOGRANGE; +} + +static DWORD glsl_vertex_pipe_vp_get_emul_mask(const struct wined3d_gl_info *gl_info) +{ + if (gl_info->supported[WINED3D_GL_LEGACY_CONTEXT]) + return GL_EXT_EMUL_ARB_MULTITEXTURE; + return 0; +} + +static void *glsl_vertex_pipe_vp_alloc(const struct wined3d_shader_backend_ops *shader_backend, void *shader_priv) +{ + struct shader_glsl_priv *priv; + + if (shader_backend == &glsl_shader_backend) + { + priv = shader_priv; + wine_rb_init(&priv->ffp_vertex_shaders, wined3d_ffp_vertex_program_key_compare); + return priv; + } + + FIXME("GLSL vertex pipe without GLSL shader backend not implemented.\n"); + + return NULL; +} + +static void shader_glsl_free_ffp_vertex_shader(struct wine_rb_entry *entry, void *param) +{ + struct glsl_ffp_vertex_shader *shader = WINE_RB_ENTRY_VALUE(entry, + struct glsl_ffp_vertex_shader, desc.entry); + struct glsl_shader_prog_link *program, *program2; + struct glsl_ffp_destroy_ctx *ctx = param; + const struct wined3d_gl_info *gl_info; + + gl_info = ctx->context_gl->gl_info; + LIST_FOR_EACH_ENTRY_SAFE(program, program2, &shader->linked_programs, + struct glsl_shader_prog_link, vs.shader_entry) + { + delete_glsl_program_entry(ctx->priv, gl_info, program); + } + GL_EXTCALL(glDeleteShader(shader->id)); + heap_free(shader); +} + +/* Context activation is done by the caller. */ +static void glsl_vertex_pipe_vp_free(struct wined3d_device *device, struct wined3d_context *context) +{ + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + struct shader_glsl_priv *priv = device->vertex_priv; + struct glsl_ffp_destroy_ctx ctx; + + ctx.priv = priv; + ctx.context_gl = context_gl; + wine_rb_destroy(&priv->ffp_vertex_shaders, shader_glsl_free_ffp_vertex_shader, &ctx); +} + +static void glsl_vertex_pipe_nop(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) {} + +static void glsl_vertex_pipe_shader(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) +{ + context->shader_update_mask |= 1u << WINED3D_SHADER_TYPE_VERTEX; +} + +static void glsl_vertex_pipe_vdecl(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) +{ + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + BOOL specular = !!(context->stream_info.use_map & (1u << WINED3D_FFP_SPECULAR)); + BOOL diffuse = !!(context->stream_info.use_map & (1u << WINED3D_FFP_DIFFUSE)); + BOOL normal = !!(context->stream_info.use_map & (1u << WINED3D_FFP_NORMAL)); + const BOOL legacy_clip_planes = needs_legacy_glsl_syntax(gl_info); + BOOL transformed = context->stream_info.position_transformed; + BOOL wasrhw = context->last_was_rhw; + unsigned int i; + + context->last_was_rhw = transformed; + + /* If the vertex declaration contains a transformed position attribute, + * the draw uses the fixed function vertex pipeline regardless of any + * vertex shader set by the application. */ + if (transformed != wasrhw + || context->stream_info.swizzle_map != context->last_swizzle_map) + context->shader_update_mask |= 1u << WINED3D_SHADER_TYPE_VERTEX; + + context->last_swizzle_map = context->stream_info.swizzle_map; + + if (!use_vs(state)) + { + if (context->last_was_vshader) + { + if (legacy_clip_planes) + for (i = 0; i < gl_info->limits.user_clip_distances; ++i) + clipplane(context, state, STATE_CLIPPLANE(i)); + else + context->constant_update_mask |= WINED3D_SHADER_CONST_VS_CLIP_PLANES; + } + + context->constant_update_mask |= WINED3D_SHADER_CONST_FFP_TEXMATRIX; + + /* Because of settings->texcoords, we have to regenerate the vertex + * shader on a vdecl change if there aren't enough varyings to just + * always output all the texture coordinates. + * + * Likewise, we have to invalidate the shader when using per-vertex + * colours and diffuse/specular attribute presence changes, or when + * normal presence changes. */ + if (!shader_glsl_full_ffp_varyings(gl_info) || (state->render_states[WINED3D_RS_COLORVERTEX] + && (diffuse != context->last_was_diffuse || specular != context->last_was_specular)) + || normal != context->last_was_normal) + context->shader_update_mask |= 1u << WINED3D_SHADER_TYPE_VERTEX; + + if (use_ps(state) + && state->shader[WINED3D_SHADER_TYPE_PIXEL]->reg_maps.shader_version.major == 1 + && state->shader[WINED3D_SHADER_TYPE_PIXEL]->reg_maps.shader_version.minor <= 3) + context->shader_update_mask |= 1u << WINED3D_SHADER_TYPE_PIXEL; + } + else + { + if (!context->last_was_vshader) + { + /* Vertex shader clipping ignores the view matrix. Update all clip planes. */ + if (legacy_clip_planes) + for (i = 0; i < gl_info->limits.user_clip_distances; ++i) + clipplane(context, state, STATE_CLIPPLANE(i)); + else + context->constant_update_mask |= WINED3D_SHADER_CONST_VS_CLIP_PLANES; + } + } + + context->last_was_vshader = use_vs(state); + context->last_was_diffuse = diffuse; + context->last_was_specular = specular; + context->last_was_normal = normal; +} + +static void glsl_vertex_pipe_vs(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) +{ + context->shader_update_mask |= 1u << WINED3D_SHADER_TYPE_VERTEX; + /* Different vertex shaders potentially require a different vertex attributes setup. */ + if (!isStateDirty(context, STATE_VDECL)) + context_apply_state(context, state, STATE_VDECL); +} + +static void glsl_vertex_pipe_hs(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) +{ + /* In Direct3D tessellator options (e.g. output primitive type, primitive + * winding) are defined in Hull Shaders, while in GLSL those are + * specified in Tessellation Evaluation Shaders. */ + context->shader_update_mask |= 1u << WINED3D_SHADER_TYPE_DOMAIN; + + if (state->shader[WINED3D_SHADER_TYPE_VERTEX]) + context->shader_update_mask |= 1u << WINED3D_SHADER_TYPE_VERTEX; +} + +static void glsl_vertex_pipe_geometry_shader(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) +{ + struct glsl_context_data *ctx_data = context->shader_backend_data; + BOOL rasterization_disabled; + + rasterization_disabled = is_rasterization_disabled(state->shader[WINED3D_SHADER_TYPE_GEOMETRY]); + if (ctx_data->rasterization_disabled != rasterization_disabled) + context->shader_update_mask |= 1u << WINED3D_SHADER_TYPE_PIXEL; + ctx_data->rasterization_disabled = rasterization_disabled; + + if (state->shader[WINED3D_SHADER_TYPE_DOMAIN]) + context->shader_update_mask |= 1u << WINED3D_SHADER_TYPE_DOMAIN; + else if (state->shader[WINED3D_SHADER_TYPE_VERTEX] + && state->shader[WINED3D_SHADER_TYPE_VERTEX]->reg_maps.shader_version.major >= 4) + context->shader_update_mask |= 1u << WINED3D_SHADER_TYPE_VERTEX; +} + +static void glsl_vertex_pipe_pixel_shader(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) +{ + if (state->shader[WINED3D_SHADER_TYPE_GEOMETRY]) + context->shader_update_mask |= 1u << WINED3D_SHADER_TYPE_GEOMETRY; + else if (state->shader[WINED3D_SHADER_TYPE_DOMAIN]) + context->shader_update_mask |= 1u << WINED3D_SHADER_TYPE_DOMAIN; + else if (state->shader[WINED3D_SHADER_TYPE_VERTEX] + && state->shader[WINED3D_SHADER_TYPE_VERTEX]->reg_maps.shader_version.major >= 4) + context->shader_update_mask |= 1u << WINED3D_SHADER_TYPE_VERTEX; +} + +static void glsl_vertex_pipe_world(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) +{ + context->constant_update_mask |= WINED3D_SHADER_CONST_FFP_MODELVIEW; +} + +static void glsl_vertex_pipe_vertexblend(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) +{ + context->constant_update_mask |= WINED3D_SHADER_CONST_FFP_VERTEXBLEND; +} + +static void glsl_vertex_pipe_view(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + unsigned int k; + + context->constant_update_mask |= WINED3D_SHADER_CONST_FFP_MODELVIEW + | WINED3D_SHADER_CONST_FFP_LIGHTS + | WINED3D_SHADER_CONST_FFP_VERTEXBLEND; + + if (needs_legacy_glsl_syntax(gl_info)) + { + for (k = 0; k < gl_info->limits.user_clip_distances; ++k) + { + if (!isStateDirty(context, STATE_CLIPPLANE(k))) + clipplane(context, state, STATE_CLIPPLANE(k)); + } + } + else + { + context->constant_update_mask |= WINED3D_SHADER_CONST_VS_CLIP_PLANES; + } +} + +static void glsl_vertex_pipe_projection(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) +{ + /* Table fog behavior depends on the projection matrix. */ + if (state->render_states[WINED3D_RS_FOGENABLE] + && state->render_states[WINED3D_RS_FOGTABLEMODE] != WINED3D_FOG_NONE) + context->shader_update_mask |= 1u << WINED3D_SHADER_TYPE_VERTEX; + context->constant_update_mask |= WINED3D_SHADER_CONST_FFP_PROJ; +} + +static void glsl_vertex_pipe_viewport(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) +{ + if (!isStateDirty(context, STATE_TRANSFORM(WINED3D_TS_PROJECTION))) + glsl_vertex_pipe_projection(context, state, STATE_TRANSFORM(WINED3D_TS_PROJECTION)); + if (!isStateDirty(context, STATE_RENDER(WINED3D_RS_POINTSCALEENABLE)) + && state->render_states[WINED3D_RS_POINTSCALEENABLE]) + context->constant_update_mask |= WINED3D_SHADER_CONST_VS_POINTSIZE; + context->constant_update_mask |= WINED3D_SHADER_CONST_POS_FIXUP; +} + +static void glsl_vertex_pipe_texmatrix(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) +{ + context->constant_update_mask |= WINED3D_SHADER_CONST_FFP_TEXMATRIX; +} + +static void glsl_vertex_pipe_texmatrix_np2(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) +{ + DWORD sampler = state_id - STATE_SAMPLER(0); + const struct wined3d_texture *texture = state->textures[sampler]; + BOOL np2; + + if (!texture) + return; + + if (sampler >= WINED3D_MAX_TEXTURES) + return; + + if ((np2 = !(texture->flags & WINED3D_TEXTURE_POW2_MAT_IDENT)) + || context->lastWasPow2Texture & (1u << sampler)) + { + if (np2) + context->lastWasPow2Texture |= 1u << sampler; + else + context->lastWasPow2Texture &= ~(1u << sampler); + + context->constant_update_mask |= WINED3D_SHADER_CONST_FFP_TEXMATRIX; + } +} + +static void glsl_vertex_pipe_material(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) +{ + context->constant_update_mask |= WINED3D_SHADER_CONST_FFP_MATERIAL; +} + +static void glsl_vertex_pipe_light(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) +{ + context->constant_update_mask |= WINED3D_SHADER_CONST_FFP_LIGHTS; +} + +static void glsl_vertex_pipe_pointsize(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) +{ + context->constant_update_mask |= WINED3D_SHADER_CONST_VS_POINTSIZE; +} + +static void glsl_vertex_pipe_pointscale(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) +{ + if (!use_vs(state)) + context->constant_update_mask |= WINED3D_SHADER_CONST_VS_POINTSIZE; +} + +static void glsl_vertex_pointsprite_core(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) +{ + static unsigned int once; + + if (state->primitive_type == WINED3D_PT_POINTLIST + && !state->render_states[WINED3D_RS_POINTSPRITEENABLE] && !once++) + FIXME("Non-point sprite points not supported in core profile.\n"); +} + +static void glsl_vertex_pipe_shademode(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) +{ + context->shader_update_mask |= 1u << WINED3D_SHADER_TYPE_VERTEX; +} + +static void glsl_vertex_pipe_clip_plane(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) +{ + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + UINT index = state_id - STATE_CLIPPLANE(0); + + if (index >= gl_info->limits.user_clip_distances) + return; + + context->constant_update_mask |= WINED3D_SHADER_CONST_VS_CLIP_PLANES; +} + +static const struct wined3d_state_entry_template glsl_vertex_pipe_vp_states[] = +{ + {STATE_VDECL, {STATE_VDECL, glsl_vertex_pipe_vdecl }, WINED3D_GL_EXT_NONE }, + {STATE_SHADER(WINED3D_SHADER_TYPE_VERTEX), {STATE_SHADER(WINED3D_SHADER_TYPE_VERTEX), glsl_vertex_pipe_vs }, WINED3D_GL_EXT_NONE }, + {STATE_SHADER(WINED3D_SHADER_TYPE_HULL), {STATE_SHADER(WINED3D_SHADER_TYPE_HULL), glsl_vertex_pipe_hs }, WINED3D_GL_EXT_NONE }, + {STATE_SHADER(WINED3D_SHADER_TYPE_GEOMETRY), {STATE_SHADER(WINED3D_SHADER_TYPE_GEOMETRY), glsl_vertex_pipe_geometry_shader}, WINED3D_GL_EXT_NONE }, + {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), glsl_vertex_pipe_pixel_shader}, WINED3D_GL_EXT_NONE }, + {STATE_MATERIAL, {STATE_RENDER(WINED3D_RS_SPECULARENABLE), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_SPECULARENABLE), {STATE_RENDER(WINED3D_RS_SPECULARENABLE), glsl_vertex_pipe_material}, WINED3D_GL_EXT_NONE }, + /* Clip planes */ + {STATE_CLIPPLANE(0), {STATE_CLIPPLANE(0), glsl_vertex_pipe_clip_plane}, WINED3D_GLSL_130 }, + {STATE_CLIPPLANE(0), {STATE_CLIPPLANE(0), clipplane }, WINED3D_GL_EXT_NONE }, + {STATE_CLIPPLANE(1), {STATE_CLIPPLANE(1), glsl_vertex_pipe_clip_plane}, WINED3D_GLSL_130 }, + {STATE_CLIPPLANE(1), {STATE_CLIPPLANE(1), clipplane }, WINED3D_GL_EXT_NONE }, + {STATE_CLIPPLANE(2), {STATE_CLIPPLANE(2), glsl_vertex_pipe_clip_plane}, WINED3D_GLSL_130 }, + {STATE_CLIPPLANE(2), {STATE_CLIPPLANE(2), clipplane }, WINED3D_GL_EXT_NONE }, + {STATE_CLIPPLANE(3), {STATE_CLIPPLANE(3), glsl_vertex_pipe_clip_plane}, WINED3D_GLSL_130 }, + {STATE_CLIPPLANE(3), {STATE_CLIPPLANE(3), clipplane }, WINED3D_GL_EXT_NONE }, + {STATE_CLIPPLANE(4), {STATE_CLIPPLANE(4), glsl_vertex_pipe_clip_plane}, WINED3D_GLSL_130 }, + {STATE_CLIPPLANE(4), {STATE_CLIPPLANE(4), clipplane }, WINED3D_GL_EXT_NONE }, + {STATE_CLIPPLANE(5), {STATE_CLIPPLANE(5), glsl_vertex_pipe_clip_plane}, WINED3D_GLSL_130 }, + {STATE_CLIPPLANE(5), {STATE_CLIPPLANE(5), clipplane }, WINED3D_GL_EXT_NONE }, + {STATE_CLIPPLANE(6), {STATE_CLIPPLANE(6), glsl_vertex_pipe_clip_plane}, WINED3D_GLSL_130 }, + {STATE_CLIPPLANE(6), {STATE_CLIPPLANE(6), clipplane }, WINED3D_GL_EXT_NONE }, + {STATE_CLIPPLANE(7), {STATE_CLIPPLANE(7), glsl_vertex_pipe_clip_plane}, WINED3D_GLSL_130 }, + {STATE_CLIPPLANE(7), {STATE_CLIPPLANE(7), clipplane }, WINED3D_GL_EXT_NONE }, + /* Lights */ + {STATE_LIGHT_TYPE, {STATE_RENDER(WINED3D_RS_FOGENABLE), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_ACTIVELIGHT(0), {STATE_ACTIVELIGHT(0), glsl_vertex_pipe_light }, WINED3D_GL_EXT_NONE }, + {STATE_ACTIVELIGHT(1), {STATE_ACTIVELIGHT(1), glsl_vertex_pipe_light }, WINED3D_GL_EXT_NONE }, + {STATE_ACTIVELIGHT(2), {STATE_ACTIVELIGHT(2), glsl_vertex_pipe_light }, WINED3D_GL_EXT_NONE }, + {STATE_ACTIVELIGHT(3), {STATE_ACTIVELIGHT(3), glsl_vertex_pipe_light }, WINED3D_GL_EXT_NONE }, + {STATE_ACTIVELIGHT(4), {STATE_ACTIVELIGHT(4), glsl_vertex_pipe_light }, WINED3D_GL_EXT_NONE }, + {STATE_ACTIVELIGHT(5), {STATE_ACTIVELIGHT(5), glsl_vertex_pipe_light }, WINED3D_GL_EXT_NONE }, + {STATE_ACTIVELIGHT(6), {STATE_ACTIVELIGHT(6), glsl_vertex_pipe_light }, WINED3D_GL_EXT_NONE }, + {STATE_ACTIVELIGHT(7), {STATE_ACTIVELIGHT(7), glsl_vertex_pipe_light }, WINED3D_GL_EXT_NONE }, + /* Viewport */ + {STATE_VIEWPORT, {STATE_VIEWPORT, glsl_vertex_pipe_viewport}, WINED3D_GL_EXT_NONE }, + /* Transform states */ + {STATE_TRANSFORM(WINED3D_TS_VIEW), {STATE_TRANSFORM(WINED3D_TS_VIEW), glsl_vertex_pipe_view }, WINED3D_GL_EXT_NONE }, + {STATE_TRANSFORM(WINED3D_TS_PROJECTION), {STATE_TRANSFORM(WINED3D_TS_PROJECTION), glsl_vertex_pipe_projection}, WINED3D_GL_EXT_NONE }, + {STATE_TRANSFORM(WINED3D_TS_TEXTURE0), {STATE_TEXTURESTAGE(0, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TRANSFORM(WINED3D_TS_TEXTURE1), {STATE_TEXTURESTAGE(1, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TRANSFORM(WINED3D_TS_TEXTURE2), {STATE_TEXTURESTAGE(2, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TRANSFORM(WINED3D_TS_TEXTURE3), {STATE_TEXTURESTAGE(3, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TRANSFORM(WINED3D_TS_TEXTURE4), {STATE_TEXTURESTAGE(4, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TRANSFORM(WINED3D_TS_TEXTURE5), {STATE_TEXTURESTAGE(5, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TRANSFORM(WINED3D_TS_TEXTURE6), {STATE_TEXTURESTAGE(6, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TRANSFORM(WINED3D_TS_TEXTURE7), {STATE_TEXTURESTAGE(7, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(0)), {STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(0)), glsl_vertex_pipe_world }, WINED3D_GL_EXT_NONE }, + {STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(1)), {STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(1)), glsl_vertex_pipe_vertexblend }, WINED3D_GL_EXT_NONE }, + {STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(2)), {STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(2)), glsl_vertex_pipe_vertexblend }, WINED3D_GL_EXT_NONE }, + {STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(3)), {STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(3)), glsl_vertex_pipe_vertexblend }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(0, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), {STATE_TEXTURESTAGE(0, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), glsl_vertex_pipe_texmatrix}, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(1, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), {STATE_TEXTURESTAGE(1, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), glsl_vertex_pipe_texmatrix}, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(2, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), {STATE_TEXTURESTAGE(2, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), glsl_vertex_pipe_texmatrix}, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(3, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), {STATE_TEXTURESTAGE(3, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), glsl_vertex_pipe_texmatrix}, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(4, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), {STATE_TEXTURESTAGE(4, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), glsl_vertex_pipe_texmatrix}, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(5, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), {STATE_TEXTURESTAGE(5, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), glsl_vertex_pipe_texmatrix}, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(6, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), {STATE_TEXTURESTAGE(6, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), glsl_vertex_pipe_texmatrix}, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(7, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), {STATE_TEXTURESTAGE(7, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), glsl_vertex_pipe_texmatrix}, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(0, WINED3D_TSS_TEXCOORD_INDEX), {STATE_SHADER(WINED3D_SHADER_TYPE_VERTEX), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(1, WINED3D_TSS_TEXCOORD_INDEX), {STATE_SHADER(WINED3D_SHADER_TYPE_VERTEX), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(2, WINED3D_TSS_TEXCOORD_INDEX), {STATE_SHADER(WINED3D_SHADER_TYPE_VERTEX), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(3, WINED3D_TSS_TEXCOORD_INDEX), {STATE_SHADER(WINED3D_SHADER_TYPE_VERTEX), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(4, WINED3D_TSS_TEXCOORD_INDEX), {STATE_SHADER(WINED3D_SHADER_TYPE_VERTEX), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(5, WINED3D_TSS_TEXCOORD_INDEX), {STATE_SHADER(WINED3D_SHADER_TYPE_VERTEX), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(6, WINED3D_TSS_TEXCOORD_INDEX), {STATE_SHADER(WINED3D_SHADER_TYPE_VERTEX), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(7, WINED3D_TSS_TEXCOORD_INDEX), {STATE_SHADER(WINED3D_SHADER_TYPE_VERTEX), NULL }, WINED3D_GL_EXT_NONE }, + /* Fog */ + {STATE_RENDER(WINED3D_RS_FOGENABLE), {STATE_RENDER(WINED3D_RS_FOGENABLE), glsl_vertex_pipe_shader}, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_FOGTABLEMODE), {STATE_RENDER(WINED3D_RS_FOGENABLE), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_FOGVERTEXMODE), {STATE_RENDER(WINED3D_RS_FOGENABLE), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_RANGEFOGENABLE), {STATE_RENDER(WINED3D_RS_FOGENABLE), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_CLIPPING), {STATE_RENDER(WINED3D_RS_CLIPPING), state_clipping }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_CLIPPLANEENABLE), {STATE_RENDER(WINED3D_RS_CLIPPING), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_LIGHTING), {STATE_SHADER(WINED3D_SHADER_TYPE_VERTEX), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_AMBIENT), {STATE_RENDER(WINED3D_RS_AMBIENT), glsl_vertex_pipe_light }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_COLORVERTEX), {STATE_RENDER(WINED3D_RS_COLORVERTEX), glsl_vertex_pipe_shader}, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_LOCALVIEWER), {STATE_SHADER(WINED3D_SHADER_TYPE_VERTEX), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_NORMALIZENORMALS), {STATE_SHADER(WINED3D_SHADER_TYPE_VERTEX), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_DIFFUSEMATERIALSOURCE), {STATE_SHADER(WINED3D_SHADER_TYPE_VERTEX), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_SPECULARMATERIALSOURCE), {STATE_SHADER(WINED3D_SHADER_TYPE_VERTEX), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_AMBIENTMATERIALSOURCE), {STATE_SHADER(WINED3D_SHADER_TYPE_VERTEX), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_EMISSIVEMATERIALSOURCE), {STATE_SHADER(WINED3D_SHADER_TYPE_VERTEX), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_VERTEXBLEND), {STATE_SHADER(WINED3D_SHADER_TYPE_VERTEX), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_POINTSIZE), {STATE_RENDER(WINED3D_RS_POINTSIZE_MIN), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_POINTSIZE_MIN), {STATE_RENDER(WINED3D_RS_POINTSIZE_MIN), glsl_vertex_pipe_pointsize}, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_POINTSPRITEENABLE), {STATE_RENDER(WINED3D_RS_POINTSPRITEENABLE), state_pointsprite }, ARB_POINT_SPRITE }, + {STATE_RENDER(WINED3D_RS_POINTSPRITEENABLE), {STATE_RENDER(WINED3D_RS_POINTSPRITEENABLE), state_pointsprite_w }, WINED3D_GL_LEGACY_CONTEXT }, + {STATE_RENDER(WINED3D_RS_POINTSPRITEENABLE), {STATE_RENDER(WINED3D_RS_POINTSPRITEENABLE), glsl_vertex_pointsprite_core}, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_POINTSCALEENABLE), {STATE_RENDER(WINED3D_RS_POINTSCALEENABLE), glsl_vertex_pipe_pointscale}, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_POINTSCALE_A), {STATE_RENDER(WINED3D_RS_POINTSCALEENABLE), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_POINTSCALE_B), {STATE_RENDER(WINED3D_RS_POINTSCALEENABLE), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_POINTSCALE_C), {STATE_RENDER(WINED3D_RS_POINTSCALEENABLE), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_POINTSIZE_MAX), {STATE_RENDER(WINED3D_RS_POINTSIZE_MIN), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_TWEENFACTOR), {STATE_SHADER(WINED3D_SHADER_TYPE_VERTEX), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_INDEXEDVERTEXBLENDENABLE), {STATE_SHADER(WINED3D_SHADER_TYPE_VERTEX), NULL }, WINED3D_GL_EXT_NONE }, + /* NP2 texture matrix fixups. They are not needed if + * GL_ARB_texture_non_power_of_two is supported. Otherwise, register + * glsl_vertex_pipe_texmatrix(), which takes care of updating the texture + * matrix. */ + {STATE_SAMPLER(0), {0, NULL }, ARB_TEXTURE_NON_POWER_OF_TWO }, + {STATE_SAMPLER(0), {0, NULL }, WINED3D_GL_NORMALIZED_TEXRECT}, + {STATE_SAMPLER(0), {STATE_SAMPLER(0), glsl_vertex_pipe_texmatrix_np2}, WINED3D_GL_EXT_NONE }, + {STATE_SAMPLER(1), {0, NULL }, ARB_TEXTURE_NON_POWER_OF_TWO }, + {STATE_SAMPLER(1), {0, NULL }, WINED3D_GL_NORMALIZED_TEXRECT}, + {STATE_SAMPLER(1), {STATE_SAMPLER(1), glsl_vertex_pipe_texmatrix_np2}, WINED3D_GL_EXT_NONE }, + {STATE_SAMPLER(2), {0, NULL }, ARB_TEXTURE_NON_POWER_OF_TWO }, + {STATE_SAMPLER(2), {0, NULL }, WINED3D_GL_NORMALIZED_TEXRECT}, + {STATE_SAMPLER(2), {STATE_SAMPLER(2), glsl_vertex_pipe_texmatrix_np2}, WINED3D_GL_EXT_NONE }, + {STATE_SAMPLER(3), {0, NULL }, ARB_TEXTURE_NON_POWER_OF_TWO }, + {STATE_SAMPLER(3), {0, NULL }, WINED3D_GL_NORMALIZED_TEXRECT}, + {STATE_SAMPLER(3), {STATE_SAMPLER(3), glsl_vertex_pipe_texmatrix_np2}, WINED3D_GL_EXT_NONE }, + {STATE_SAMPLER(4), {0, NULL }, ARB_TEXTURE_NON_POWER_OF_TWO }, + {STATE_SAMPLER(4), {0, NULL }, WINED3D_GL_NORMALIZED_TEXRECT}, + {STATE_SAMPLER(4), {STATE_SAMPLER(4), glsl_vertex_pipe_texmatrix_np2}, WINED3D_GL_EXT_NONE }, + {STATE_SAMPLER(5), {0, NULL }, ARB_TEXTURE_NON_POWER_OF_TWO }, + {STATE_SAMPLER(5), {0, NULL }, WINED3D_GL_NORMALIZED_TEXRECT}, + {STATE_SAMPLER(5), {STATE_SAMPLER(5), glsl_vertex_pipe_texmatrix_np2}, WINED3D_GL_EXT_NONE }, + {STATE_SAMPLER(6), {0, NULL }, ARB_TEXTURE_NON_POWER_OF_TWO }, + {STATE_SAMPLER(6), {0, NULL }, WINED3D_GL_NORMALIZED_TEXRECT}, + {STATE_SAMPLER(6), {STATE_SAMPLER(6), glsl_vertex_pipe_texmatrix_np2}, WINED3D_GL_EXT_NONE }, + {STATE_SAMPLER(7), {0, NULL }, ARB_TEXTURE_NON_POWER_OF_TWO }, + {STATE_SAMPLER(7), {0, NULL }, WINED3D_GL_NORMALIZED_TEXRECT}, + {STATE_SAMPLER(7), {STATE_SAMPLER(7), glsl_vertex_pipe_texmatrix_np2}, WINED3D_GL_EXT_NONE }, + {STATE_POINT_ENABLE, {STATE_POINT_ENABLE, glsl_vertex_pipe_shader}, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_SHADEMODE), {STATE_RENDER(WINED3D_RS_SHADEMODE), glsl_vertex_pipe_shademode}, WINED3D_GLSL_130 }, + {STATE_RENDER(WINED3D_RS_SHADEMODE), {STATE_RENDER(WINED3D_RS_SHADEMODE), glsl_vertex_pipe_nop }, WINED3D_GL_EXT_NONE }, + {0 /* Terminate */, {0, NULL }, WINED3D_GL_EXT_NONE }, +}; + +/* TODO: + * - Implement vertex tweening. */ +const struct wined3d_vertex_pipe_ops glsl_vertex_pipe = +{ + glsl_vertex_pipe_vp_enable, + glsl_vertex_pipe_vp_get_caps, + glsl_vertex_pipe_vp_get_emul_mask, + glsl_vertex_pipe_vp_alloc, + glsl_vertex_pipe_vp_free, + glsl_vertex_pipe_vp_states, +}; + +static void glsl_fragment_pipe_enable(const struct wined3d_context *context, BOOL enable) +{ + /* Nothing to do. */ +} + +static void glsl_fragment_pipe_get_caps(const struct wined3d_adapter *adapter, struct fragment_caps *caps) +{ + const struct wined3d_gl_info *gl_info = &adapter->gl_info; + + caps->wined3d_caps = WINED3D_FRAGMENT_CAP_PROJ_CONTROL + | WINED3D_FRAGMENT_CAP_SRGB_WRITE + | WINED3D_FRAGMENT_CAP_COLOR_KEY; + caps->PrimitiveMiscCaps = WINED3DPMISCCAPS_TSSARGTEMP + | WINED3DPMISCCAPS_PERSTAGECONSTANT; + caps->TextureOpCaps = WINED3DTEXOPCAPS_DISABLE + | WINED3DTEXOPCAPS_SELECTARG1 + | WINED3DTEXOPCAPS_SELECTARG2 + | WINED3DTEXOPCAPS_MODULATE4X + | WINED3DTEXOPCAPS_MODULATE2X + | WINED3DTEXOPCAPS_MODULATE + | WINED3DTEXOPCAPS_ADDSIGNED2X + | WINED3DTEXOPCAPS_ADDSIGNED + | WINED3DTEXOPCAPS_ADD + | WINED3DTEXOPCAPS_SUBTRACT + | WINED3DTEXOPCAPS_ADDSMOOTH + | WINED3DTEXOPCAPS_BLENDCURRENTALPHA + | WINED3DTEXOPCAPS_BLENDFACTORALPHA + | WINED3DTEXOPCAPS_BLENDTEXTUREALPHA + | WINED3DTEXOPCAPS_BLENDDIFFUSEALPHA + | WINED3DTEXOPCAPS_BLENDTEXTUREALPHAPM + | WINED3DTEXOPCAPS_MODULATEALPHA_ADDCOLOR + | WINED3DTEXOPCAPS_MODULATECOLOR_ADDALPHA + | WINED3DTEXOPCAPS_MODULATEINVCOLOR_ADDALPHA + | WINED3DTEXOPCAPS_MODULATEINVALPHA_ADDCOLOR + | WINED3DTEXOPCAPS_DOTPRODUCT3 + | WINED3DTEXOPCAPS_MULTIPLYADD + | WINED3DTEXOPCAPS_LERP + | WINED3DTEXOPCAPS_BUMPENVMAP + | WINED3DTEXOPCAPS_BUMPENVMAPLUMINANCE; + caps->MaxTextureBlendStages = WINED3D_MAX_TEXTURES; + caps->MaxSimultaneousTextures = min(gl_info->limits.samplers[WINED3D_SHADER_TYPE_PIXEL], WINED3D_MAX_TEXTURES); +} + +static DWORD glsl_fragment_pipe_get_emul_mask(const struct wined3d_gl_info *gl_info) +{ + if (gl_info->supported[WINED3D_GL_LEGACY_CONTEXT]) + return GL_EXT_EMUL_ARB_MULTITEXTURE; + return 0; +} + +static void *glsl_fragment_pipe_alloc(const struct wined3d_shader_backend_ops *shader_backend, void *shader_priv) +{ + struct shader_glsl_priv *priv; + + if (shader_backend == &glsl_shader_backend) + { + priv = shader_priv; + wine_rb_init(&priv->ffp_fragment_shaders, wined3d_ffp_frag_program_key_compare); + return priv; + } + + FIXME("GLSL fragment pipe without GLSL shader backend not implemented.\n"); + + return NULL; +} + +static void shader_glsl_free_ffp_fragment_shader(struct wine_rb_entry *entry, void *param) +{ + struct glsl_ffp_fragment_shader *shader = WINE_RB_ENTRY_VALUE(entry, + struct glsl_ffp_fragment_shader, entry.entry); + struct glsl_shader_prog_link *program, *program2; + struct glsl_ffp_destroy_ctx *ctx = param; + const struct wined3d_gl_info *gl_info; + + gl_info = ctx->context_gl->gl_info; + LIST_FOR_EACH_ENTRY_SAFE(program, program2, &shader->linked_programs, + struct glsl_shader_prog_link, ps.shader_entry) + { + delete_glsl_program_entry(ctx->priv, gl_info, program); + } + GL_EXTCALL(glDeleteShader(shader->id)); + heap_free(shader); +} + +/* Context activation is done by the caller. */ +static void glsl_fragment_pipe_free(struct wined3d_device *device, struct wined3d_context *context) +{ + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + struct shader_glsl_priv *priv = device->fragment_priv; + struct glsl_ffp_destroy_ctx ctx; + + ctx.priv = priv; + ctx.context_gl = context_gl; + wine_rb_destroy(&priv->ffp_fragment_shaders, shader_glsl_free_ffp_fragment_shader, &ctx); +} + +static void glsl_fragment_pipe_shader(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) +{ + context->last_was_pshader = use_ps(state); + + context->shader_update_mask |= 1u << WINED3D_SHADER_TYPE_PIXEL; +} + +static void glsl_fragment_pipe_fogparams(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) +{ + context->constant_update_mask |= WINED3D_SHADER_CONST_PS_FOG; +} + +static void glsl_fragment_pipe_fog(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) +{ + BOOL use_vshader = use_vs(state); + enum fogsource new_source; + DWORD fogstart = state->render_states[WINED3D_RS_FOGSTART]; + DWORD fogend = state->render_states[WINED3D_RS_FOGEND]; + + context->shader_update_mask |= 1u << WINED3D_SHADER_TYPE_PIXEL; + + if (!state->render_states[WINED3D_RS_FOGENABLE]) + return; + + if (state->render_states[WINED3D_RS_FOGTABLEMODE] == WINED3D_FOG_NONE) + { + if (use_vshader) + new_source = FOGSOURCE_VS; + else if (state->render_states[WINED3D_RS_FOGVERTEXMODE] == WINED3D_FOG_NONE || context->stream_info.position_transformed) + new_source = FOGSOURCE_COORD; + else + new_source = FOGSOURCE_FFP; + } + else + { + new_source = FOGSOURCE_FFP; + } + + if (new_source != context->fog_source || fogstart == fogend) + { + context->fog_source = new_source; + context->constant_update_mask |= WINED3D_SHADER_CONST_PS_FOG; + } +} + +static void glsl_fragment_pipe_vdecl(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl(context)->gl_info; + + /* Because of settings->texcoords_initialized and args->texcoords_initialized. */ + if (!shader_glsl_full_ffp_varyings(gl_info)) + context->shader_update_mask |= 1u << WINED3D_SHADER_TYPE_PIXEL; + + if (!isStateDirty(context, STATE_RENDER(WINED3D_RS_FOGENABLE))) + glsl_fragment_pipe_fog(context, state, state_id); +} + +static void glsl_fragment_pipe_vs(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl(context)->gl_info; + + /* Because of settings->texcoords_initialized and args->texcoords_initialized. */ + if (!shader_glsl_full_ffp_varyings(gl_info)) + context->shader_update_mask |= 1u << WINED3D_SHADER_TYPE_PIXEL; +} + +static void glsl_fragment_pipe_tex_transform(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) +{ + context->shader_update_mask |= 1u << WINED3D_SHADER_TYPE_PIXEL; +} + +static void glsl_fragment_pipe_invalidate_constants(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) +{ + context->constant_update_mask |= WINED3D_SHADER_CONST_FFP_PS; +} + +static void glsl_fragment_pipe_alpha_test_func(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl(context)->gl_info; + GLint func = wined3d_gl_compare_func(state->render_states[WINED3D_RS_ALPHAFUNC]); + float ref = wined3d_alpha_ref(state); + + if (func) + { + gl_info->gl_ops.gl.p_glAlphaFunc(func, ref); + checkGLcall("glAlphaFunc"); + } +} + +static void glsl_fragment_pipe_core_alpha_test(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) +{ + context->shader_update_mask |= 1u << WINED3D_SHADER_TYPE_PIXEL; +} + +static void glsl_fragment_pipe_alpha_test(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl(context)->gl_info; + + if (state->render_states[WINED3D_RS_ALPHATESTENABLE]) + { + gl_info->gl_ops.gl.p_glEnable(GL_ALPHA_TEST); + checkGLcall("glEnable(GL_ALPHA_TEST)"); + } + else + { + gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST); + checkGLcall("glDisable(GL_ALPHA_TEST)"); + } +} + +static void glsl_fragment_pipe_core_alpha_test_ref(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) +{ + context->constant_update_mask |= WINED3D_SHADER_CONST_PS_ALPHA_TEST; +} + +static void glsl_fragment_pipe_color_key(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) +{ + context->constant_update_mask |= WINED3D_SHADER_CONST_FFP_COLOR_KEY; +} + +static void glsl_fragment_pipe_shademode(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) +{ + context->shader_update_mask |= 1u << WINED3D_SHADER_TYPE_PIXEL; +} + +static const struct wined3d_state_entry_template glsl_fragment_pipe_state_template[] = +{ + {STATE_VDECL, {STATE_VDECL, glsl_fragment_pipe_vdecl }, WINED3D_GL_EXT_NONE }, + {STATE_SHADER(WINED3D_SHADER_TYPE_VERTEX), {STATE_SHADER(WINED3D_SHADER_TYPE_VERTEX), glsl_fragment_pipe_vs }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_TEXTUREFACTOR), {STATE_RENDER(WINED3D_RS_TEXTUREFACTOR), glsl_fragment_pipe_invalidate_constants}, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(0, WINED3D_TSS_COLOR_OP), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(0, WINED3D_TSS_COLOR_ARG1), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(0, WINED3D_TSS_COLOR_ARG2), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(0, WINED3D_TSS_COLOR_ARG0), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(0, WINED3D_TSS_ALPHA_OP), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(0, WINED3D_TSS_ALPHA_ARG1), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(0, WINED3D_TSS_ALPHA_ARG2), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(0, WINED3D_TSS_ALPHA_ARG0), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(0, WINED3D_TSS_RESULT_ARG), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(1, WINED3D_TSS_COLOR_OP), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(1, WINED3D_TSS_COLOR_ARG1), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(1, WINED3D_TSS_COLOR_ARG2), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(1, WINED3D_TSS_COLOR_ARG0), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(1, WINED3D_TSS_ALPHA_OP), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(1, WINED3D_TSS_ALPHA_ARG1), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(1, WINED3D_TSS_ALPHA_ARG2), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(1, WINED3D_TSS_ALPHA_ARG0), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(1, WINED3D_TSS_RESULT_ARG), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(2, WINED3D_TSS_COLOR_OP), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(2, WINED3D_TSS_COLOR_ARG1), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(2, WINED3D_TSS_COLOR_ARG2), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(2, WINED3D_TSS_COLOR_ARG0), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(2, WINED3D_TSS_ALPHA_OP), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(2, WINED3D_TSS_ALPHA_ARG1), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(2, WINED3D_TSS_ALPHA_ARG2), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(2, WINED3D_TSS_ALPHA_ARG0), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(2, WINED3D_TSS_RESULT_ARG), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(3, WINED3D_TSS_COLOR_OP), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(3, WINED3D_TSS_COLOR_ARG1), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(3, WINED3D_TSS_COLOR_ARG2), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(3, WINED3D_TSS_COLOR_ARG0), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(3, WINED3D_TSS_ALPHA_OP), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(3, WINED3D_TSS_ALPHA_ARG1), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(3, WINED3D_TSS_ALPHA_ARG2), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(3, WINED3D_TSS_ALPHA_ARG0), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(3, WINED3D_TSS_RESULT_ARG), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(4, WINED3D_TSS_COLOR_OP), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(4, WINED3D_TSS_COLOR_ARG1), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(4, WINED3D_TSS_COLOR_ARG2), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(4, WINED3D_TSS_COLOR_ARG0), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(4, WINED3D_TSS_ALPHA_OP), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(4, WINED3D_TSS_ALPHA_ARG1), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(4, WINED3D_TSS_ALPHA_ARG2), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(4, WINED3D_TSS_ALPHA_ARG0), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(4, WINED3D_TSS_RESULT_ARG), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(5, WINED3D_TSS_COLOR_OP), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(5, WINED3D_TSS_COLOR_ARG1), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(5, WINED3D_TSS_COLOR_ARG2), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(5, WINED3D_TSS_COLOR_ARG0), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(5, WINED3D_TSS_ALPHA_OP), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(5, WINED3D_TSS_ALPHA_ARG1), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(5, WINED3D_TSS_ALPHA_ARG2), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(5, WINED3D_TSS_ALPHA_ARG0), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(5, WINED3D_TSS_RESULT_ARG), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(6, WINED3D_TSS_COLOR_OP), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(6, WINED3D_TSS_COLOR_ARG1), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(6, WINED3D_TSS_COLOR_ARG2), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(6, WINED3D_TSS_COLOR_ARG0), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(6, WINED3D_TSS_ALPHA_OP), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(6, WINED3D_TSS_ALPHA_ARG1), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(6, WINED3D_TSS_ALPHA_ARG2), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(6, WINED3D_TSS_ALPHA_ARG0), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(6, WINED3D_TSS_RESULT_ARG), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(7, WINED3D_TSS_COLOR_OP), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(7, WINED3D_TSS_COLOR_ARG1), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(7, WINED3D_TSS_COLOR_ARG2), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(7, WINED3D_TSS_COLOR_ARG0), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(7, WINED3D_TSS_ALPHA_OP), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(7, WINED3D_TSS_ALPHA_ARG1), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(7, WINED3D_TSS_ALPHA_ARG2), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(7, WINED3D_TSS_ALPHA_ARG0), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(7, WINED3D_TSS_RESULT_ARG), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), glsl_fragment_pipe_shader }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_ALPHAFUNC), {STATE_RENDER(WINED3D_RS_ALPHAFUNC), glsl_fragment_pipe_alpha_test_func }, WINED3D_GL_LEGACY_CONTEXT}, + {STATE_RENDER(WINED3D_RS_ALPHAFUNC), {STATE_RENDER(WINED3D_RS_ALPHATESTENABLE), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_ALPHAREF), {STATE_RENDER(WINED3D_RS_ALPHAFUNC), NULL }, WINED3D_GL_LEGACY_CONTEXT}, + {STATE_RENDER(WINED3D_RS_ALPHAREF), {STATE_RENDER(WINED3D_RS_ALPHAREF), glsl_fragment_pipe_core_alpha_test_ref }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_ALPHATESTENABLE), {STATE_RENDER(WINED3D_RS_ALPHATESTENABLE), glsl_fragment_pipe_alpha_test }, WINED3D_GL_LEGACY_CONTEXT}, + {STATE_RENDER(WINED3D_RS_ALPHATESTENABLE), {STATE_RENDER(WINED3D_RS_ALPHATESTENABLE), glsl_fragment_pipe_core_alpha_test }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_COLORKEYENABLE), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_COLOR_KEY, { STATE_COLOR_KEY, glsl_fragment_pipe_color_key }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_FOGENABLE), {STATE_RENDER(WINED3D_RS_FOGENABLE), glsl_fragment_pipe_fog }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_FOGTABLEMODE), {STATE_RENDER(WINED3D_RS_FOGENABLE), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_FOGVERTEXMODE), {STATE_RENDER(WINED3D_RS_FOGENABLE), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_FOGSTART), {STATE_RENDER(WINED3D_RS_FOGSTART), glsl_fragment_pipe_fogparams }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_FOGEND), {STATE_RENDER(WINED3D_RS_FOGSTART), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_SRGBWRITEENABLE), {STATE_RENDER(WINED3D_RS_SRGBWRITEENABLE), state_srgbwrite }, ARB_FRAMEBUFFER_SRGB}, + {STATE_RENDER(WINED3D_RS_SRGBWRITEENABLE), {STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_FOGCOLOR), {STATE_RENDER(WINED3D_RS_FOGCOLOR), glsl_fragment_pipe_fogparams }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_FOGDENSITY), {STATE_RENDER(WINED3D_RS_FOGDENSITY), glsl_fragment_pipe_fogparams }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_POINTSPRITEENABLE), {STATE_RENDER(WINED3D_RS_POINTSPRITEENABLE), glsl_fragment_pipe_shader }, ARB_POINT_SPRITE }, + {STATE_RENDER(WINED3D_RS_POINTSPRITEENABLE), {STATE_RENDER(WINED3D_RS_POINTSPRITEENABLE), glsl_fragment_pipe_shader }, WINED3D_GL_VERSION_2_0}, + {STATE_TEXTURESTAGE(0,WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), {STATE_TEXTURESTAGE(0, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), glsl_fragment_pipe_tex_transform }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(1,WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), {STATE_TEXTURESTAGE(1, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), glsl_fragment_pipe_tex_transform }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(2,WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), {STATE_TEXTURESTAGE(2, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), glsl_fragment_pipe_tex_transform }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(3,WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), {STATE_TEXTURESTAGE(3, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), glsl_fragment_pipe_tex_transform }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(4,WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), {STATE_TEXTURESTAGE(4, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), glsl_fragment_pipe_tex_transform }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(5,WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), {STATE_TEXTURESTAGE(5, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), glsl_fragment_pipe_tex_transform }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(6,WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), {STATE_TEXTURESTAGE(6, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), glsl_fragment_pipe_tex_transform }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(7,WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), {STATE_TEXTURESTAGE(7, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), glsl_fragment_pipe_tex_transform }, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(0, WINED3D_TSS_CONSTANT), {STATE_TEXTURESTAGE(0, WINED3D_TSS_CONSTANT), glsl_fragment_pipe_invalidate_constants}, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(1, WINED3D_TSS_CONSTANT), {STATE_TEXTURESTAGE(1, WINED3D_TSS_CONSTANT), glsl_fragment_pipe_invalidate_constants}, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(2, WINED3D_TSS_CONSTANT), {STATE_TEXTURESTAGE(2, WINED3D_TSS_CONSTANT), glsl_fragment_pipe_invalidate_constants}, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(3, WINED3D_TSS_CONSTANT), {STATE_TEXTURESTAGE(3, WINED3D_TSS_CONSTANT), glsl_fragment_pipe_invalidate_constants}, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(4, WINED3D_TSS_CONSTANT), {STATE_TEXTURESTAGE(4, WINED3D_TSS_CONSTANT), glsl_fragment_pipe_invalidate_constants}, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(5, WINED3D_TSS_CONSTANT), {STATE_TEXTURESTAGE(5, WINED3D_TSS_CONSTANT), glsl_fragment_pipe_invalidate_constants}, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(6, WINED3D_TSS_CONSTANT), {STATE_TEXTURESTAGE(6, WINED3D_TSS_CONSTANT), glsl_fragment_pipe_invalidate_constants}, WINED3D_GL_EXT_NONE }, + {STATE_TEXTURESTAGE(7, WINED3D_TSS_CONSTANT), {STATE_TEXTURESTAGE(7, WINED3D_TSS_CONSTANT), glsl_fragment_pipe_invalidate_constants}, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_SPECULARENABLE), {STATE_RENDER(WINED3D_RS_SPECULARENABLE), glsl_fragment_pipe_invalidate_constants}, WINED3D_GL_EXT_NONE }, + {STATE_POINT_ENABLE, {STATE_POINT_ENABLE, glsl_fragment_pipe_shader }, WINED3D_GL_EXT_NONE }, + {STATE_RENDER(WINED3D_RS_SHADEMODE), {STATE_RENDER(WINED3D_RS_SHADEMODE), glsl_fragment_pipe_shademode }, WINED3D_GLSL_130 }, + {STATE_RENDER(WINED3D_RS_SHADEMODE), {STATE_RENDER(WINED3D_RS_SHADEMODE), state_shademode }, WINED3D_GL_EXT_NONE }, + {0 /* Terminate */, {0, 0 }, WINED3D_GL_EXT_NONE }, +}; + +static BOOL glsl_fragment_pipe_alloc_context_data(struct wined3d_context *context) +{ + return TRUE; +} + +static void glsl_fragment_pipe_free_context_data(struct wined3d_context *context) +{ +} + +const struct wined3d_fragment_pipe_ops glsl_fragment_pipe = +{ + glsl_fragment_pipe_enable, + glsl_fragment_pipe_get_caps, + glsl_fragment_pipe_get_emul_mask, + glsl_fragment_pipe_alloc, + glsl_fragment_pipe_free, + glsl_fragment_pipe_alloc_context_data, + glsl_fragment_pipe_free_context_data, + shader_glsl_color_fixup_supported, + glsl_fragment_pipe_state_template, +}; + +struct glsl_blitter_args +{ + GLenum texture_type; + struct color_fixup_desc fixup; + unsigned short use_colour_key; +}; + +struct glsl_blitter_program +{ + struct wine_rb_entry entry; + struct glsl_blitter_args args; + GLuint id; +}; + +struct wined3d_glsl_blitter +{ + struct wined3d_blitter blitter; + struct wined3d_string_buffer_list string_buffers; + struct wine_rb_tree programs; + GLuint palette_texture; +}; + +static int glsl_blitter_args_compare(const void *key, const struct wine_rb_entry *entry) +{ + const struct glsl_blitter_args *a = key; + const struct glsl_blitter_args *b = &WINE_RB_ENTRY_VALUE(entry, const struct glsl_blitter_program, entry)->args; + + return memcmp(a, b, sizeof(*a)); +} + +/* Context activation is done by the caller. */ +static void glsl_free_blitter_program(struct wine_rb_entry *entry, void *ctx) +{ + struct glsl_blitter_program *program = WINE_RB_ENTRY_VALUE(entry, struct glsl_blitter_program, entry); + struct wined3d_context_gl *context_gl = ctx; + const struct wined3d_gl_info *gl_info; + + gl_info = context_gl->gl_info; + GL_EXTCALL(glDeleteProgram(program->id)); + checkGLcall("glDeleteProgram()"); + heap_free(program); +} + +/* Context activation is done by the caller. */ +static void glsl_blitter_destroy(struct wined3d_blitter *blitter, struct wined3d_context *context) +{ + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + struct wined3d_glsl_blitter *glsl_blitter; + struct wined3d_blitter *next; + + if ((next = blitter->next)) + next->ops->blitter_destroy(next, context); + + glsl_blitter = CONTAINING_RECORD(blitter, struct wined3d_glsl_blitter, blitter); + + if (glsl_blitter->palette_texture) + gl_info->gl_ops.gl.p_glDeleteTextures(1, &glsl_blitter->palette_texture); + + wine_rb_destroy(&glsl_blitter->programs, glsl_free_blitter_program, context_gl); + string_buffer_list_cleanup(&glsl_blitter->string_buffers); + + heap_free(glsl_blitter); +} + +static void glsl_blitter_generate_p8_shader(struct wined3d_string_buffer *buffer, + const struct wined3d_gl_info *gl_info, const struct glsl_blitter_args *args, + const char *output, const char *tex_type, const char *swizzle) +{ + shader_addline(buffer, "uniform sampler1D sampler_palette;\n"); + shader_addline(buffer, "\nvoid main()\n{\n"); + /* The alpha-component contains the palette index. */ + shader_addline(buffer, " float index = texture%s(sampler, out_texcoord.%s).%c;\n", + needs_legacy_glsl_syntax(gl_info) ? tex_type : "", swizzle, + gl_info->supported[WINED3D_GL_LEGACY_CONTEXT] ? 'w' : 'x'); + /* Scale the index by 255/256 and add a bias of 0.5 in order to sample in + * the middle. */ + shader_addline(buffer, " index = (index * 255.0 + 0.5) / 256.0;\n"); + shader_addline(buffer, " %s = texture%s(sampler_palette, index);\n", + output, needs_legacy_glsl_syntax(gl_info) ? "1D" : ""); + if (args->use_colour_key) + shader_glsl_generate_colour_key_test(buffer, output, "colour_key.low", "colour_key.high"); + shader_addline(buffer, "}\n"); +} + +static void gen_packed_yuv_read(struct wined3d_string_buffer *buffer, + const struct wined3d_gl_info *gl_info, const struct glsl_blitter_args *args, + const char *tex_type) +{ + enum complex_fixup complex_fixup = get_complex_fixup(args->fixup); + char chroma, luminance; + const char *tex; + + /* The YUY2 and UYVY formats contain two pixels packed into a 32 bit + * macropixel, giving effectively 16 bits per pixel. The color consists of + * a luminance(Y) and two chroma(U and V) values. Each macropixel has two + * luminance values, one for each single pixel it contains, and one U and + * one V value shared between both pixels. + * + * The data is loaded into an A8L8 texture. With YUY2, the luminance + * component contains the luminance and alpha the chroma. With UYVY it is + * vice versa. Thus take the format into account when generating the read + * swizzles + * + * Reading the Y value is straightforward - just sample the texture. The + * hardware takes care of filtering in the horizontal and vertical + * direction. + * + * Reading the U and V values is harder. We have to avoid filtering + * horizontally, because that would mix the U and V values of one pixel or + * two adjacent pixels. Thus floor the texture coordinate and add 0.5 to + * get an unfiltered read, regardless of the filtering setting. Vertical + * filtering works automatically though - the U and V values of two rows + * are mixed nicely. + * + * Apart of avoiding filtering issues, the code has to know which value it + * just read, and where it can find the other one. To determine this, it + * checks if it sampled an even or odd pixel, and shifts the 2nd read + * accordingly. + * + * Handling horizontal filtering of U and V values requires reading a 2nd + * pair of pixels, extracting U and V and mixing them. This is not + * implemented yet. + * + * An alternative implementation idea is to load the texture as A8R8G8B8 + * texture, with width / 2. This way one read gives all 3 values, finding + * U and V is easy in an unfiltered situation. Finding the luminance on + * the other hand requires finding out if it is an odd or even pixel. The + * real drawback of this approach is filtering. This would have to be + * emulated completely in the shader, reading up two 2 packed pixels in up + * to 2 rows and interpolating both horizontally and vertically. Beyond + * that it would require adjustments to the texture handling code to deal + * with the width scaling. */ + + if (complex_fixup == COMPLEX_FIXUP_UYVY) + { + chroma = 'x'; + luminance = gl_info->supported[WINED3D_GL_LEGACY_CONTEXT] ? 'w' : 'y'; + } + else + { + chroma = gl_info->supported[WINED3D_GL_LEGACY_CONTEXT] ? 'w' : 'y'; + luminance = 'x'; + } + + tex = needs_legacy_glsl_syntax(gl_info) ? tex_type : ""; + + /* First we have to read the chroma values. This means we need at least + * two pixels (no filtering), or 4 pixels (with filtering). To get the + * unmodified chroma, we have to rid ourselves of the filtering when we + * sample the texture. */ + shader_addline(buffer, " texcoord.xy = out_texcoord.xy;\n"); + /* We must not allow filtering between pixel x and x+1, this would mix U + * and V. Vertical filtering is ok. However, bear in mind that the pixel + * center is at 0.5, so add 0.5. */ + shader_addline(buffer, " texcoord.x = (floor(texcoord.x * size.x) + 0.5) / size.x;\n"); + shader_addline(buffer, " luminance = texture%s(sampler, texcoord.xy).%c;\n", tex, chroma); + + /* Multiply the x coordinate by 0.5 and get the fraction. This gives 0.25 + * and 0.75 for the even and odd pixels respectively. */ + /* Put the value into either of the chroma values. */ + shader_addline(buffer, " bool even = fract(texcoord.x * size.x * 0.5) < 0.5;\n"); + shader_addline(buffer, " if (even)\n"); + shader_addline(buffer, " chroma.y = luminance;\n"); + shader_addline(buffer, " else\n"); + shader_addline(buffer, " chroma.x = luminance;\n"); + + /* Sample pixel 2. If we read an even pixel, sample the pixel right to the + * current one. Otherwise, sample the left pixel. */ + shader_addline(buffer, " texcoord.x += even ? 1.0 / size.x : -1.0 / size.x;\n"); + shader_addline(buffer, " luminance = texture%s(sampler, texcoord.xy).%c;\n", tex, chroma); + + /* Put the value into the other chroma. */ + shader_addline(buffer, " if (even)\n"); + shader_addline(buffer, " chroma.x = luminance;\n"); + shader_addline(buffer, " else\n"); + shader_addline(buffer, " chroma.y = luminance;\n"); + + /* TODO: If filtering is enabled, sample a 2nd pair of pixels left or right of + * the current one and lerp the two U and V values. */ + + /* This gives the correctly filtered luminance value. */ + shader_addline(buffer, " luminance = texture%s(sampler, out_texcoord.xy).%c;\n", tex, luminance); +} + +static void gen_yv12_read(struct wined3d_string_buffer *buffer, + const struct wined3d_gl_info *gl_info, const char *tex_type) +{ + char component = gl_info->supported[WINED3D_GL_LEGACY_CONTEXT] ? 'w' : 'x'; + const char *tex = needs_legacy_glsl_syntax(gl_info) ? tex_type : ""; + + /* YV12 surfaces contain a WxH sized luminance plane, followed by a + * (W/2)x(H/2) V and a (W/2)x(H/2) U plane, each with 8 bit per pixel. So + * the effective bitdepth is 12 bits per pixel. Since the U and V planes + * have only half the pitch of the luminance plane, the packing into the + * gl texture is a bit unfortunate. If the whole texture is interpreted as + * luminance data it looks approximately like this: + * + * +----------------------------------+---- + * | | + * | | + * | | + * | | + * | | 2 + * | LUMINANCE | - + * | | 3 + * | | + * | | + * | | + * | | + * +----------------+-----------------+---- + * | | | + * | V even rows | V odd rows | + * | | | 1 + * +----------------+------------------ - + * | | | 3 + * | U even rows | U odd rows | + * | | | + * +----------------+-----------------+---- + * | | | + * | 0.5 | 0.5 | + * + * So it appears as if there are 4 chroma images, but in fact the odd rows + * in the chroma images are in the same row as the even ones. So it is + * kinda tricky to read. */ + + /* First sample the chroma values. */ + shader_addline(buffer, " texcoord.xy = out_texcoord.xy;\n"); + /* The chroma planes have only half the width. */ + shader_addline(buffer, " texcoord.x *= 0.5;\n"); + + /* The first value is between 2/3 and 5/6 of the texture's height, so + * scale+bias the coordinate. Also read the right side of the image when + * reading odd lines. + * + * Don't forget to clamp the y values in into the range, otherwise we'll + * get filtering bleeding. */ + + /* Read odd lines from the right side (add 0.5 to the x coordinate). */ + shader_addline(buffer, " if (fract(floor(texcoord.y * size.y) * 0.5 + 1.0 / 6.0) >= 0.5)\n"); + shader_addline(buffer, " texcoord.x += 0.5;\n"); + + /* Clamp, keep the half pixel origin in mind. */ + shader_addline(buffer, " texcoord.y = clamp(2.0 / 3.0 + texcoord.y / 6.0, " + "2.0 / 3.0 + 0.5 / size.y, 5.0 / 6.0 - 0.5 / size.y);\n"); + + shader_addline(buffer, " chroma.x = texture%s(sampler, texcoord.xy).%c;\n", tex, component); + + /* The other chroma value is 1/6th of the texture lower, from 5/6th to + * 6/6th No need to clamp because we're just reusing the already clamped + * value from above. */ + shader_addline(buffer, " texcoord.y += 1.0 / 6.0;\n"); + shader_addline(buffer, " chroma.y = texture%s(sampler, texcoord.xy).%c;\n", tex, component); + + /* Sample the luminance value. It is in the top 2/3rd of the texture, so + * scale the y coordinate. Clamp the y coordinate to prevent the chroma + * values from bleeding into the sampled luminance values due to + * filtering. */ + shader_addline(buffer, " texcoord.xy = out_texcoord.xy;\n"); + /* Multiply the y coordinate by 2/3 and clamp it. */ + shader_addline(buffer, " texcoord.y = min(texcoord.y * 2.0 / 3.0, 2.0 / 3.0 - 0.5 / size.y);\n"); + shader_addline(buffer, " luminance = texture%s(sampler, texcoord.xy).%c;\n", tex, component); +} + +static void gen_nv12_read(struct wined3d_string_buffer *buffer, + const struct wined3d_gl_info *gl_info, const char *tex_type) +{ + char component = gl_info->supported[WINED3D_GL_LEGACY_CONTEXT] ? 'w' : 'x'; + const char *tex = needs_legacy_glsl_syntax(gl_info) ? tex_type : ""; + + /* NV12 surfaces contain a WxH sized luminance plane, followed by a + * (W/2)x(H/2) sized plane where each component is an UV pair. So the + * effective bitdepth is 12 bits per pixel. If the whole texture is + * interpreted as luminance data it looks approximately like this: + * + * +----------------------------------+---- + * | | + * | | + * | | + * | | + * | | 2 + * | LUMINANCE | - + * | | 3 + * | | + * | | + * | | + * | | + * +----------------------------------+---- + * |UVUVUVUVUVUVUVUVUVUVUVUVUVUVUVUVUV| + * |UVUVUVUVUVUVUVUVUVUVUVUVUVUVUVUVUV| + * | | 1 + * | | - + * | | 3 + * | | + * | | + * +----------------------------------+---- */ + + /* First sample the chroma values. */ + shader_addline(buffer, " texcoord.xy = out_texcoord.xy;\n"); + /* We only have half the number of chroma pixels. */ + shader_addline(buffer, " texcoord.x *= 0.5;\n"); + shader_addline(buffer, " texcoord.y = (texcoord.y + 2.0) / 3.0;\n"); + + /* We must not allow filtering horizontally, this would mix U and V. + * Vertical filtering is ok. However, bear in mind that the pixel center + * is at 0.5, so add 0.5. */ + + /* Convert to non-normalised coordinates so we can find the individual + * pixel. */ + shader_addline(buffer, " texcoord.x = floor(texcoord.x * size.x);\n"); + /* Multiply by 2 since chroma components are stored in UV pixel pairs, add + * 0.5 to hit the center of the pixel. Then convert back to normalised + * coordinates. */ + shader_addline(buffer, " texcoord.x = (texcoord.x * 2.0 + 0.5) / size.x;\n"); + /* Clamp, keep the half pixel origin in mind. */ + shader_addline(buffer, " texcoord.y = max(texcoord.y, 2.0 / 3.0 + 0.5 / size.y);\n"); + + shader_addline(buffer, " chroma.y = texture%s(sampler, texcoord.xy).%c;\n", tex, component); + /* Add 1.0 / size.x to sample the adjacent texel. */ + shader_addline(buffer, " texcoord.x += 1.0 / size.x;\n"); + shader_addline(buffer, " chroma.x = texture%s(sampler, texcoord.xy).%c;\n", tex, component); + + /* Sample the luminance value. It is in the top 2/3rd of the texture, so + * scale the y coordinate. Clamp the y coordinate to prevent the chroma + * values from bleeding into the sampled luminance values due to + * filtering. */ + shader_addline(buffer, " texcoord.xy = out_texcoord.xy;\n"); + /* Multiply the y coordinate by 2/3 and clamp it. */ + shader_addline(buffer, " texcoord.y = min(texcoord.y * 2.0 / 3.0, 2.0 / 3.0 - 0.5 / size.y);\n"); + shader_addline(buffer, " luminance = texture%s(sampler, texcoord.xy).%c;\n", tex, component); +} + +static void glsl_blitter_generate_yuv_shader(struct wined3d_string_buffer *buffer, + const struct wined3d_gl_info *gl_info, const struct glsl_blitter_args *args, + const char *output, const char *tex_type, const char *swizzle) +{ + enum complex_fixup complex_fixup = get_complex_fixup(args->fixup); + + shader_addline(buffer, "const vec4 yuv_coef = vec4(1.403, -0.344, -0.714, 1.770);\n"); + shader_addline(buffer, "float luminance;\n"); + shader_addline(buffer, "vec2 texcoord;\n"); + shader_addline(buffer, "vec2 chroma;\n"); + shader_addline(buffer, "uniform vec2 size;\n"); + + shader_addline(buffer, "\nvoid main()\n{\n"); + + switch (complex_fixup) + { + case COMPLEX_FIXUP_UYVY: + case COMPLEX_FIXUP_YUY2: + gen_packed_yuv_read(buffer, gl_info, args, tex_type); + break; + + case COMPLEX_FIXUP_YV12: + gen_yv12_read(buffer, gl_info, tex_type); + break; + + case COMPLEX_FIXUP_NV12: + gen_nv12_read(buffer, gl_info, tex_type); + break; + + case COMPLEX_FIXUP_YUV: + /* With APPLE_rgb_422, things are much simpler. The only thing we + * have to do here is Y'CbCr to RGB conversion. */ + shader_addline(buffer, " vec3 yuv = vec3(texture%s(sampler, out_texcoord.xy));\n", + needs_legacy_glsl_syntax(gl_info) ? tex_type : ""); + shader_addline(buffer, " luminance = yuv.y;\n"); + shader_addline(buffer, " chroma = yuv.xz;\n"); + break; + + default: + FIXME("Unsupported fixup %#x.\n", complex_fixup); + string_buffer_free(buffer); + return; + } + + /* Calculate the final result. Formula is taken from + * http://www.fourcc.org/fccyvrgb.php. Note that the chroma + * ranges from -0.5 to 0.5. */ + shader_addline(buffer, "\n chroma.xy -= 0.5;\n"); + + shader_addline(buffer, " %s.x = luminance + chroma.x * yuv_coef.x;\n", output); + shader_addline(buffer, " %s.y = luminance + chroma.y * yuv_coef.y + chroma.x * yuv_coef.z;\n", output); + shader_addline(buffer, " %s.z = luminance + chroma.y * yuv_coef.w;\n", output); + if (args->use_colour_key) + shader_glsl_generate_colour_key_test(buffer, output, "colour_key.low", "colour_key.high"); + + shader_addline(buffer, "}\n"); +} + +static void glsl_blitter_generate_plain_shader(struct wined3d_string_buffer *buffer, + const struct wined3d_gl_info *gl_info, const struct glsl_blitter_args *args, + const char *output, const char *tex_type, const char *swizzle) +{ + shader_addline(buffer, "\nvoid main()\n{\n"); + shader_addline(buffer, " %s = texture%s(sampler, out_texcoord.%s);\n", + output, needs_legacy_glsl_syntax(gl_info) ? tex_type : "", swizzle); + shader_glsl_color_correction_ext(buffer, output, WINED3DSP_WRITEMASK_ALL, args->fixup); + if (args->use_colour_key) + shader_glsl_generate_colour_key_test(buffer, output, "colour_key.low", "colour_key.high"); + shader_addline(buffer, "}\n"); +} + +/* Context activation is done by the caller. */ +static GLuint glsl_blitter_generate_program(struct wined3d_glsl_blitter *blitter, + const struct wined3d_gl_info *gl_info, const struct glsl_blitter_args *args) +{ + static const struct + { + GLenum texture_target; + const char texture_type[7]; + const char texcoord_swizzle[4]; + } + texture_data[] = + { + {GL_TEXTURE_2D, "2D", "xy"}, + {GL_TEXTURE_CUBE_MAP, "Cube", "xyz"}, + {GL_TEXTURE_RECTANGLE_ARB, "2DRect", "xy"}, + }; + static const char vshader_main[] = + "\n" + "void main()\n" + "{\n" + " gl_Position = vec4(pos, 0.0, 1.0);\n" + " out_texcoord = texcoord;\n" + "}\n"; + enum complex_fixup complex_fixup = get_complex_fixup(args->fixup); + struct wined3d_string_buffer *buffer, *output; + GLuint program, vshader_id, fshader_id; + const char *tex_type = NULL, *swizzle = NULL, *ptr; + unsigned int i; + GLint loc; + + for (i = 0; i < ARRAY_SIZE(texture_data); ++i) + { + if (args->texture_type == texture_data[i].texture_target) + { + tex_type = texture_data[i].texture_type; + swizzle = texture_data[i].texcoord_swizzle; + break; + } + } + if (i == ARRAY_SIZE(texture_data)) + { + FIXME("Unsupported texture type %#x.\n", args->texture_type); + return 0; + } + + program = GL_EXTCALL(glCreateProgram()); + + vshader_id = GL_EXTCALL(glCreateShader(GL_VERTEX_SHADER)); + + buffer = string_buffer_get(&blitter->string_buffers); + shader_glsl_add_version_declaration(buffer, gl_info); + shader_addline(buffer, "%s vec2 pos;\n", get_attribute_keyword(gl_info)); + shader_addline(buffer, "%s vec3 texcoord;\n", get_attribute_keyword(gl_info)); + declare_out_varying(gl_info, buffer, FALSE, "vec3 out_texcoord;\n"); + shader_addline(buffer, vshader_main); + + ptr = buffer->buffer; + GL_EXTCALL(glShaderSource(vshader_id, 1, &ptr, NULL)); + GL_EXTCALL(glAttachShader(program, vshader_id)); + GL_EXTCALL(glDeleteShader(vshader_id)); + + fshader_id = GL_EXTCALL(glCreateShader(GL_FRAGMENT_SHADER)); + + string_buffer_clear(buffer); + shader_glsl_add_version_declaration(buffer, gl_info); + shader_addline(buffer, "uniform sampler%s sampler;\n", tex_type); + if (args->use_colour_key) + { + shader_addline(buffer, "uniform struct\n{\n"); + shader_addline(buffer, " vec4 low, high;\n"); + shader_addline(buffer, "} colour_key;\n"); + } + declare_in_varying(gl_info, buffer, FALSE, "vec3 out_texcoord;\n"); + /* TODO: Declare the out variable with the correct type (and put it in the + * blitter args). */ + if (!use_legacy_fragment_output(gl_info)) + shader_addline(buffer, "out vec4 ps_out[1];\n"); + + output = string_buffer_get(&blitter->string_buffers); + string_buffer_sprintf(output, "%s[0]", get_fragment_output(gl_info)); + + switch (complex_fixup) + { + case COMPLEX_FIXUP_P8: + glsl_blitter_generate_p8_shader(buffer, gl_info, args, output->buffer, tex_type, swizzle); + break; + case COMPLEX_FIXUP_YUY2: + case COMPLEX_FIXUP_UYVY: + case COMPLEX_FIXUP_YV12: + case COMPLEX_FIXUP_NV12: + case COMPLEX_FIXUP_YUV: + glsl_blitter_generate_yuv_shader(buffer, gl_info, args, output->buffer, tex_type, swizzle); + break; + case COMPLEX_FIXUP_NONE: + glsl_blitter_generate_plain_shader(buffer, gl_info, args, output->buffer, tex_type, swizzle); + } + + string_buffer_release(&blitter->string_buffers, output); + + ptr = buffer->buffer; + GL_EXTCALL(glShaderSource(fshader_id, 1, &ptr, NULL)); + string_buffer_release(&blitter->string_buffers, buffer); + GL_EXTCALL(glAttachShader(program, fshader_id)); + GL_EXTCALL(glDeleteShader(fshader_id)); + + GL_EXTCALL(glBindAttribLocation(program, 0, "pos")); + GL_EXTCALL(glBindAttribLocation(program, 1, "texcoord")); + + if (!use_legacy_fragment_output(gl_info)) + GL_EXTCALL(glBindFragDataLocation(program, 0, "ps_out")); + + GL_EXTCALL(glCompileShader(vshader_id)); + print_glsl_info_log(gl_info, vshader_id, FALSE); + GL_EXTCALL(glCompileShader(fshader_id)); + print_glsl_info_log(gl_info, fshader_id, FALSE); + GL_EXTCALL(glLinkProgram(program)); + shader_glsl_validate_link(gl_info, program); + + GL_EXTCALL(glUseProgram(program)); + loc = GL_EXTCALL(glGetUniformLocation(program, "sampler")); + GL_EXTCALL(glUniform1i(loc, 0)); + if (complex_fixup == COMPLEX_FIXUP_P8) + { + loc = GL_EXTCALL(glGetUniformLocation(program, "sampler_palette")); + GL_EXTCALL(glUniform1i(loc, 1)); + } + + return program; +} + +/* Context activation is done by the caller. */ +static void glsl_blitter_upload_palette(struct wined3d_glsl_blitter *blitter, + struct wined3d_context_gl *context_gl, const struct wined3d_texture_gl *texture_gl) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + const struct wined3d_palette *palette; + + palette = texture_gl->t.swapchain ? texture_gl->t.swapchain->palette : NULL; + + if (!blitter->palette_texture) + gl_info->gl_ops.gl.p_glGenTextures(1, &blitter->palette_texture); + + wined3d_context_gl_active_texture(context_gl, gl_info, 1); + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_1D, blitter->palette_texture); + gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + + if (palette) + { + gl_info->gl_ops.gl.p_glTexImage1D(GL_TEXTURE_1D, 0, GL_RGB, 256, 0, GL_BGRA, + GL_UNSIGNED_INT_8_8_8_8_REV, palette->colors); + } + else + { + static const DWORD black; + + FIXME("P8 texture loaded without a palette.\n"); + gl_info->gl_ops.gl.p_glTexImage1D(GL_TEXTURE_1D, 0, GL_RGB, 1, 0, GL_BGRA, + GL_UNSIGNED_INT_8_8_8_8_REV, &black); + } + + wined3d_context_gl_active_texture(context_gl, gl_info, 0); +} + +/* Context activation is done by the caller. */ +static struct glsl_blitter_program *glsl_blitter_get_program(struct wined3d_glsl_blitter *blitter, + struct wined3d_context_gl *context_gl, const struct wined3d_texture_gl *texture_gl, BOOL use_colour_key) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + struct glsl_blitter_program *program; + struct glsl_blitter_args args; + struct wine_rb_entry *entry; + + memset(&args, 0, sizeof(args)); + args.texture_type = texture_gl->target; + args.fixup = texture_gl->t.resource.format->color_fixup; + args.use_colour_key = use_colour_key; + + if ((entry = wine_rb_get(&blitter->programs, &args))) + return WINE_RB_ENTRY_VALUE(entry, struct glsl_blitter_program, entry); + + if (!(program = heap_alloc(sizeof(*program)))) + { + ERR("Failed to allocate blitter program memory.\n"); + return NULL; + } + + program->args = args; + if (!(program->id = glsl_blitter_generate_program(blitter, gl_info, &args))) + { + WARN("Failed to generate blitter program.\n"); + heap_free(program); + return NULL; + } + + if (wine_rb_put(&blitter->programs, &program->args, &program->entry) == -1) + { + ERR("Failed to store blitter program.\n"); + GL_EXTCALL(glDeleteProgram(program->id)); + heap_free(program); + return NULL; + } + + return program; +} + +static BOOL glsl_blitter_supported(enum wined3d_blit_op blit_op, const struct wined3d_context *context, + const struct wined3d_texture_gl *src_texture, DWORD src_location, + const struct wined3d_texture_gl *dst_texture, DWORD dst_location) +{ + const struct wined3d_resource *src_resource = &src_texture->t.resource; + const struct wined3d_resource *dst_resource = &dst_texture->t.resource; + const struct wined3d_format *src_format = src_resource->format; + const struct wined3d_format *dst_format = dst_resource->format; + BOOL decompress; + + if (blit_op == WINED3D_BLIT_OP_RAW_BLIT && dst_format->id == src_format->id) + { + if (dst_format->depth_size || dst_format->stencil_size) + blit_op = WINED3D_BLIT_OP_DEPTH_BLIT; + else + blit_op = WINED3D_BLIT_OP_COLOR_BLIT; + } + + if (blit_op != WINED3D_BLIT_OP_COLOR_BLIT && blit_op != WINED3D_BLIT_OP_COLOR_BLIT_CKEY + && blit_op != WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST) + { + TRACE("Unsupported blit_op %#x.\n", blit_op); + return FALSE; + } + + if (src_resource->type != WINED3D_RTYPE_TEXTURE_2D) + return FALSE; + + if (src_texture->target == GL_TEXTURE_2D_MULTISAMPLE + || src_texture->target == GL_TEXTURE_2D_MULTISAMPLE_ARRAY) + { + TRACE("Multi-sample source textures not supported.\n"); + return FALSE; + } + + if (!(dst_resource->access & WINED3D_RESOURCE_ACCESS_GPU)) + { + TRACE("Destination resource does not have GPU access.\n"); + return FALSE; + } + + /* We don't necessarily want to blit from resources without + * WINED3D_RESOURCE_ACCESS_GPU, but that may be the only way to decompress + * compressed textures. */ + decompress = (src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED) + && !(dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED); + if (!decompress && !(src_resource->access & WINED3D_RESOURCE_ACCESS_GPU)) + { + TRACE("Source resource does not have GPU access.\n"); + return FALSE; + } + + if (!is_identity_fixup(dst_format->color_fixup) + && (dst_format->id != src_format->id || dst_location != WINED3D_LOCATION_DRAWABLE)) + { + TRACE("Destination fixups are not supported.\n"); + return FALSE; + } + + if (wined3d_settings.offscreen_rendering_mode == ORM_FBO + && !((dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE) + || (dst_resource->bind_flags & WINED3D_BIND_RENDER_TARGET))) + { + TRACE("Destination texture is not FBO attachable.\n"); + return FALSE; + } + + TRACE("Returning supported.\n"); + return TRUE; +} + +static DWORD glsl_blitter_blit(struct wined3d_blitter *blitter, enum wined3d_blit_op op, + struct wined3d_context *context, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx, + DWORD src_location, const RECT *src_rect, struct wined3d_texture *dst_texture, + unsigned int dst_sub_resource_idx, DWORD dst_location, const RECT *dst_rect, + const struct wined3d_color_key *colour_key, enum wined3d_texture_filter_type filter) +{ + struct wined3d_texture_gl *src_texture_gl = wined3d_texture_gl(src_texture); + struct wined3d_texture_gl *dst_texture_gl = wined3d_texture_gl(dst_texture); + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + struct wined3d_device *device = dst_texture->resource.device; + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + struct wined3d_texture *staging_texture = NULL; + struct wined3d_glsl_blitter *glsl_blitter; + struct wined3d_color_key alpha_test_key; + struct glsl_blitter_program *program; + struct wined3d_blitter *next; + unsigned int src_level; + GLint location; + RECT s, d; + + TRACE("blitter %p, op %#x, context %p, src_texture %p, src_sub_resource_idx %u, src_location %s, src_rect %s, " + "dst_texture %p, dst_sub_resource_idx %u, dst_location %s, dst_rect %s, colour_key %p, filter %s.\n", + blitter, op, context, src_texture, src_sub_resource_idx, wined3d_debug_location(src_location), + wine_dbgstr_rect(src_rect), dst_texture, dst_sub_resource_idx, wined3d_debug_location(dst_location), + wine_dbgstr_rect(dst_rect), colour_key, debug_d3dtexturefiltertype(filter)); + + if (!glsl_blitter_supported(op, context, src_texture_gl, src_location, dst_texture_gl, dst_location)) + { + if (!(next = blitter->next)) + { + ERR("No blitter to handle blit op %#x.\n", op); + return dst_location; + } + + TRACE("Forwarding to blitter %p.\n", next); + return next->ops->blitter_blit(next, op, context, src_texture, src_sub_resource_idx, src_location, + src_rect, dst_texture, dst_sub_resource_idx, dst_location, dst_rect, colour_key, filter); + } + + glsl_blitter = CONTAINING_RECORD(blitter, struct wined3d_glsl_blitter, blitter); + + if (!(src_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU)) + { + struct wined3d_resource_desc desc; + struct wined3d_box upload_box; + HRESULT hr; + + TRACE("Source texture is not GPU accessible, creating a staging texture.\n"); + + src_level = src_sub_resource_idx % src_texture->level_count; + desc.resource_type = WINED3D_RTYPE_TEXTURE_2D; + desc.format = src_texture->resource.format->id; + desc.multisample_type = src_texture->resource.multisample_type; + desc.multisample_quality = src_texture->resource.multisample_quality; + desc.usage = WINED3DUSAGE_PRIVATE; + desc.bind_flags = 0; + desc.access = WINED3D_RESOURCE_ACCESS_GPU; + desc.width = wined3d_texture_get_level_width(src_texture, src_level); + desc.height = wined3d_texture_get_level_height(src_texture, src_level); + desc.depth = 1; + desc.size = 0; + + if (FAILED(hr = wined3d_texture_create(device, &desc, 1, 1, 0, + NULL, NULL, &wined3d_null_parent_ops, &staging_texture))) + { + ERR("Failed to create staging texture, hr %#x.\n", hr); + return dst_location; + } + + wined3d_box_set(&upload_box, 0, 0, desc.width, desc.height, 0, desc.depth); + wined3d_texture_upload_from_texture(staging_texture, 0, 0, 0, 0, + src_texture, src_sub_resource_idx, &upload_box); + + src_texture = staging_texture; + src_texture_gl = wined3d_texture_gl(src_texture); + src_sub_resource_idx = 0; + } + else if (wined3d_settings.offscreen_rendering_mode != ORM_FBO + && (src_texture->sub_resources[src_sub_resource_idx].locations + & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_DRAWABLE)) == WINED3D_LOCATION_DRAWABLE + && !wined3d_resource_is_offscreen(&src_texture->resource)) + { + + /* Without FBO blits transferring from the drawable to the texture is + * expensive, because we have to flip the data in sysmem. Since we can + * flip in the blitter, we don't actually need that flip anyway. So we + * use the surface's texture as scratch texture, and flip the source + * rectangle instead. */ + texture2d_load_fb_texture(src_texture_gl, src_sub_resource_idx, FALSE, context); + + s = *src_rect; + src_level = src_sub_resource_idx % src_texture->level_count; + s.top = wined3d_texture_get_level_height(src_texture, src_level) - s.top; + s.bottom = wined3d_texture_get_level_height(src_texture, src_level) - s.bottom; + src_rect = &s; + } + else + { + wined3d_texture_load(src_texture, context, FALSE); + } + + if (wined3d_texture_is_full_rect(dst_texture, dst_sub_resource_idx % dst_texture->level_count, dst_rect)) + wined3d_texture_prepare_location(dst_texture, dst_sub_resource_idx, context, dst_location); + else + wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, dst_location); + + wined3d_context_gl_apply_blit_state(context_gl, device); + + if (dst_location == WINED3D_LOCATION_DRAWABLE) + { + d = *dst_rect; + wined3d_texture_translate_drawable_coords(dst_texture, context_gl->window, &d); + dst_rect = &d; + } + + if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) + { + GLenum buffer; + + if (dst_location == WINED3D_LOCATION_DRAWABLE) + { + TRACE("Destination texture %p is onscreen.\n", dst_texture); + buffer = wined3d_texture_get_gl_buffer(dst_texture); + } + else + { + TRACE("Destination texture %p is offscreen.\n", dst_texture); + buffer = GL_COLOR_ATTACHMENT0; + } + wined3d_context_gl_apply_fbo_state_blit(context_gl, GL_DRAW_FRAMEBUFFER, + &dst_texture->resource, dst_sub_resource_idx, NULL, 0, dst_location); + wined3d_context_gl_set_draw_buffer(context_gl, buffer); + wined3d_context_gl_check_fbo_status(context_gl, GL_DRAW_FRAMEBUFFER); + context_invalidate_state(context, STATE_FRAMEBUFFER); + } + + if (op == WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST) + { + const struct wined3d_format *f = src_texture->resource.format; + + alpha_test_key.color_space_low_value = 0; + alpha_test_key.color_space_high_value = ~(((1u << f->alpha_size) - 1) << f->alpha_offset); + colour_key = &alpha_test_key; + } + else if (op != WINED3D_BLIT_OP_COLOR_BLIT_CKEY) + { + colour_key = NULL; + } + + if (!(program = glsl_blitter_get_program(glsl_blitter, context_gl, src_texture_gl, !!colour_key))) + { + ERR("Failed to get blitter program.\n"); + return dst_location; + } + GL_EXTCALL(glUseProgram(program->id)); + switch (get_complex_fixup(program->args.fixup)) + { + case COMPLEX_FIXUP_P8: + glsl_blitter_upload_palette(glsl_blitter, context_gl, src_texture_gl); + break; + + case COMPLEX_FIXUP_YUY2: + case COMPLEX_FIXUP_UYVY: + case COMPLEX_FIXUP_YV12: + case COMPLEX_FIXUP_NV12: + case COMPLEX_FIXUP_YUV: + src_level = src_sub_resource_idx % src_texture->level_count; + location = GL_EXTCALL(glGetUniformLocation(program->id, "size")); + GL_EXTCALL(glUniform2f(location, wined3d_texture_get_level_pow2_width(src_texture, src_level), + wined3d_texture_get_level_pow2_height(src_texture, src_level))); + break; + + default: + break; + } + if (colour_key) + { + struct wined3d_color float_key[2]; + + wined3d_format_get_float_color_key(src_texture->resource.format, colour_key, float_key); + location = GL_EXTCALL(glGetUniformLocation(program->id, "colour_key.low")); + GL_EXTCALL(glUniform4fv(location, 1, &float_key[0].r)); + location = GL_EXTCALL(glGetUniformLocation(program->id, "colour_key.high")); + GL_EXTCALL(glUniform4fv(location, 1, &float_key[1].r)); + } + wined3d_context_gl_draw_shaded_quad(context_gl, src_texture_gl, src_sub_resource_idx, src_rect, dst_rect, filter); + GL_EXTCALL(glUseProgram(0)); + + if (dst_texture->swapchain && (dst_texture->swapchain->front_buffer == dst_texture)) + gl_info->gl_ops.gl.p_glFlush(); + + if (staging_texture) + wined3d_texture_decref(staging_texture); + + return dst_location; +} + +static void glsl_blitter_clear(struct wined3d_blitter *blitter, struct wined3d_device *device, + unsigned int rt_count, const struct wined3d_fb_state *fb, unsigned int rect_count, const RECT *clear_rects, + const RECT *draw_rect, DWORD flags, const struct wined3d_color *color, float depth, DWORD stencil) +{ + struct wined3d_blitter *next; + + if ((next = blitter->next)) + next->ops->blitter_clear(next, device, rt_count, fb, rect_count, + clear_rects, draw_rect, flags, color, depth, stencil); +} + +static const struct wined3d_blitter_ops glsl_blitter_ops = +{ + glsl_blitter_destroy, + glsl_blitter_clear, + glsl_blitter_blit, +}; + +struct wined3d_blitter *wined3d_glsl_blitter_create(struct wined3d_blitter **next, + const struct wined3d_device *device) +{ + const struct wined3d_gl_info *gl_info = &device->adapter->gl_info; + struct wined3d_glsl_blitter *blitter; + + if (device->shader_backend != &glsl_shader_backend) + return NULL; + + if (!gl_info->supported[ARB_VERTEX_SHADER] || !gl_info->supported[ARB_FRAGMENT_SHADER]) + return NULL; + + if (!(blitter = heap_alloc(sizeof(*blitter)))) + { + ERR("Failed to allocate blitter.\n"); + return NULL; + } + + TRACE("Created blitter %p.\n", blitter); + + blitter->blitter.ops = &glsl_blitter_ops; + blitter->blitter.next = *next; + string_buffer_list_init(&blitter->string_buffers); + wine_rb_init(&blitter->programs, glsl_blitter_args_compare); + blitter->palette_texture = 0; + *next = &blitter->blitter; + + return *next; +} diff --git a/wrappers/directx/d3dwine_wrapper/nvidia_texture_shader.c b/wrappers/directx/d3dwine_wrapper/nvidia_texture_shader.c new file mode 100644 index 00000000000..10f35e61d59 --- /dev/null +++ b/wrappers/directx/d3dwine_wrapper/nvidia_texture_shader.c @@ -0,0 +1,955 @@ +/* + * Fixed function pipeline replacement using GL_NV_register_combiners + * and GL_NV_texture_shader + * + * Copyright 2006 Henri Verbeet + * Copyright 2008 Stefan Dösinger(for CodeWeavers) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ +#include "config.h" +#include "wine/port.h" + +#include + +#include "wined3d_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(d3d); + +/* Context activation for state handlers is done by the caller. */ + +static void nvts_activate_dimensions(const struct wined3d_state *state, + unsigned int stage, struct wined3d_context_gl *context_gl) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + struct wined3d_texture *texture; + BOOL bumpmap = FALSE; + + if (stage > 0 + && (state->texture_states[stage - 1][WINED3D_TSS_COLOR_OP] == WINED3D_TOP_BUMPENVMAP_LUMINANCE + || state->texture_states[stage - 1][WINED3D_TSS_COLOR_OP] == WINED3D_TOP_BUMPENVMAP)) + { + bumpmap = TRUE; + context_gl->c.texShaderBumpMap |= (1u << stage); + } + else + { + context_gl->c.texShaderBumpMap &= ~(1u << stage); + } + + if ((texture = state->textures[stage])) + { + switch (wined3d_texture_gl(texture)->target) + { + case GL_TEXTURE_2D: + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_SHADER_NV, GL_SHADER_OPERATION_NV, + bumpmap ? GL_OFFSET_TEXTURE_2D_NV : GL_TEXTURE_2D); + checkGLcall("glTexEnvi(GL_TEXTURE_SHADER_NV, GL_SHADER_OPERATION_NV, ...)"); + break; + case GL_TEXTURE_RECTANGLE_ARB: + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_SHADER_NV, GL_SHADER_OPERATION_NV, + bumpmap ? GL_OFFSET_TEXTURE_2D_NV : GL_TEXTURE_RECTANGLE_ARB); + checkGLcall("glTexEnvi(GL_TEXTURE_SHADER_NV, GL_SHADER_OPERATION_NV, ...)"); + break; + case GL_TEXTURE_3D: + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_SHADER_NV, GL_SHADER_OPERATION_NV, GL_TEXTURE_3D); + checkGLcall("glTexEnvi(GL_TEXTURE_SHADER_NV, GL_SHADER_OPERATION_NV, GL_TEXTURE_3D)"); + break; + case GL_TEXTURE_CUBE_MAP_ARB: + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_SHADER_NV, GL_SHADER_OPERATION_NV, GL_TEXTURE_CUBE_MAP_ARB); + checkGLcall("glTexEnvi(GL_TEXTURE_SHADER_NV, GL_SHADER_OPERATION_NV, GL_TEXTURE_CUBE_MAP_ARB)"); + break; + default: + FIXME("Unhandled target %#x.\n", wined3d_texture_gl(texture)->target); + break; + } + } + else + { + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_SHADER_NV, GL_SHADER_OPERATION_NV, GL_NONE); + checkGLcall("glTexEnvi(GL_TEXTURE_SHADER_NV, GL_SHADER_OPERATION_NV, GL_NONE)"); + } +} + +struct tex_op_args +{ + GLenum input[3]; + GLenum mapping[3]; + GLenum component_usage[3]; +}; + +static GLenum d3dta_to_combiner_input(DWORD d3dta, DWORD stage, INT texture_idx) { + switch (d3dta) { + case WINED3DTA_DIFFUSE: + return GL_PRIMARY_COLOR_NV; + + case WINED3DTA_CURRENT: + if (stage) return GL_SPARE0_NV; + else return GL_PRIMARY_COLOR_NV; + + case WINED3DTA_TEXTURE: + if (texture_idx > -1) return GL_TEXTURE0_ARB + texture_idx; + else return GL_PRIMARY_COLOR_NV; + + case WINED3DTA_TFACTOR: + return GL_CONSTANT_COLOR0_NV; + + case WINED3DTA_SPECULAR: + return GL_SECONDARY_COLOR_NV; + + case WINED3DTA_TEMP: + return GL_SPARE1_NV; + + case WINED3DTA_CONSTANT: + /* TODO: Support per stage constants (WINED3D_TSS_CONSTANT, NV_register_combiners2) */ + FIXME("WINED3DTA_CONSTANT, not properly supported.\n"); + return GL_CONSTANT_COLOR1_NV; + + default: + FIXME("Unrecognized texture arg %#x\n", d3dta); + return GL_TEXTURE; + } +} + +static GLenum invert_mapping(GLenum mapping) { + if (mapping == GL_UNSIGNED_INVERT_NV) return GL_UNSIGNED_IDENTITY_NV; + else if (mapping == GL_UNSIGNED_IDENTITY_NV) return GL_UNSIGNED_INVERT_NV; + + FIXME("Unhandled mapping %#x\n", mapping); + return mapping; +} + +static void get_src_and_opr_nvrc(DWORD stage, DWORD arg, BOOL is_alpha, GLenum* input, GLenum* mapping, GLenum *component_usage, INT texture_idx) { + /* The WINED3DTA_COMPLEMENT flag specifies the complement of the input should + * be used. */ + if (arg & WINED3DTA_COMPLEMENT) *mapping = GL_UNSIGNED_INVERT_NV; + else *mapping = GL_UNSIGNED_IDENTITY_NV; /* Clamp all values to positive ranges */ + + /* The WINED3DTA_ALPHAREPLICATE flag specifies the alpha component of the input + * should be used for all input components. */ + if (is_alpha || arg & WINED3DTA_ALPHAREPLICATE) *component_usage = GL_ALPHA; + else *component_usage = GL_RGB; + + *input = d3dta_to_combiner_input(arg & WINED3DTA_SELECTMASK, stage, texture_idx); +} + +void set_tex_op_nvrc(const struct wined3d_gl_info *gl_info, const struct wined3d_state *state, BOOL is_alpha, + int stage, enum wined3d_texture_op op, DWORD arg1, DWORD arg2, DWORD arg3, INT texture_idx, DWORD dst) +{ + struct tex_op_args tex_op_args = {{0}, {0}, {0}}; + GLenum portion = is_alpha ? GL_ALPHA : GL_RGB; + GLenum target = GL_COMBINER0_NV + stage; + GLenum output; + + TRACE("stage %d, is_alpha %d, op %s, arg1 %#x, arg2 %#x, arg3 %#x, texture_idx %d\n", + stage, is_alpha, debug_d3dtop(op), arg1, arg2, arg3, texture_idx); + + /* If a texture stage references an invalid texture unit the stage just + * passes through the result from the previous stage */ + if (is_invalid_op(state, stage, op, arg1, arg2, arg3)) + { + arg1 = WINED3DTA_CURRENT; + op = WINED3D_TOP_SELECT_ARG1; + } + + get_src_and_opr_nvrc(stage, arg1, is_alpha, &tex_op_args.input[0], + &tex_op_args.mapping[0], &tex_op_args.component_usage[0], texture_idx); + get_src_and_opr_nvrc(stage, arg2, is_alpha, &tex_op_args.input[1], + &tex_op_args.mapping[1], &tex_op_args.component_usage[1], texture_idx); + get_src_and_opr_nvrc(stage, arg3, is_alpha, &tex_op_args.input[2], + &tex_op_args.mapping[2], &tex_op_args.component_usage[2], texture_idx); + + + if(dst == WINED3DTA_TEMP) { + output = GL_SPARE1_NV; + } else { + output = GL_SPARE0_NV; + } + + switch (op) + { + case WINED3D_TOP_DISABLE: + /* Only for alpha */ + if (!is_alpha) + ERR("Shouldn't be called for WINED3D_TSS_COLOR_OP (WINED3DTOP_DISABLE).\n"); + /* Input, prev_alpha*1 */ + GL_EXTCALL(glCombinerInputNV(target, portion, GL_VARIABLE_A_NV, + GL_SPARE0_NV, GL_UNSIGNED_IDENTITY_NV, GL_ALPHA)); + GL_EXTCALL(glCombinerInputNV(target, portion, GL_VARIABLE_B_NV, + GL_ZERO, GL_UNSIGNED_INVERT_NV, GL_ALPHA)); + + /* Output */ + GL_EXTCALL(glCombinerOutputNV(target, portion, GL_SPARE0_NV, GL_DISCARD_NV, + GL_DISCARD_NV, GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE)); + break; + + case WINED3D_TOP_SELECT_ARG1: + case WINED3D_TOP_SELECT_ARG2: + /* Input, arg*1 */ + if (op == WINED3D_TOP_SELECT_ARG1) + GL_EXTCALL(glCombinerInputNV(target, portion, GL_VARIABLE_A_NV, + tex_op_args.input[0], tex_op_args.mapping[0], tex_op_args.component_usage[0])); + else + GL_EXTCALL(glCombinerInputNV(target, portion, GL_VARIABLE_A_NV, + tex_op_args.input[1], tex_op_args.mapping[1], tex_op_args.component_usage[1])); + GL_EXTCALL(glCombinerInputNV(target, portion, GL_VARIABLE_B_NV, + GL_ZERO, GL_UNSIGNED_INVERT_NV, portion)); + + /* Output */ + GL_EXTCALL(glCombinerOutputNV(target, portion, output, GL_DISCARD_NV, + GL_DISCARD_NV, GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE)); + break; + + case WINED3D_TOP_MODULATE: + case WINED3D_TOP_MODULATE_2X: + case WINED3D_TOP_MODULATE_4X: + /* Input, arg1*arg2 */ + GL_EXTCALL(glCombinerInputNV(target, portion, GL_VARIABLE_A_NV, + tex_op_args.input[0], tex_op_args.mapping[0], tex_op_args.component_usage[0])); + GL_EXTCALL(glCombinerInputNV(target, portion, GL_VARIABLE_B_NV, + tex_op_args.input[1], tex_op_args.mapping[1], tex_op_args.component_usage[1])); + + /* Output */ + if (op == WINED3D_TOP_MODULATE) + GL_EXTCALL(glCombinerOutputNV(target, portion, output, GL_DISCARD_NV, + GL_DISCARD_NV, GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE)); + else if (op == WINED3D_TOP_MODULATE_2X) + GL_EXTCALL(glCombinerOutputNV(target, portion, output, GL_DISCARD_NV, + GL_DISCARD_NV, GL_SCALE_BY_TWO_NV, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE)); + else if (op == WINED3D_TOP_MODULATE_4X) + GL_EXTCALL(glCombinerOutputNV(target, portion, output, GL_DISCARD_NV, + GL_DISCARD_NV, GL_SCALE_BY_FOUR_NV, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE)); + break; + + case WINED3D_TOP_ADD: + case WINED3D_TOP_ADD_SIGNED: + case WINED3D_TOP_ADD_SIGNED_2X: + /* Input, arg1*1+arg2*1 */ + GL_EXTCALL(glCombinerInputNV(target, portion, GL_VARIABLE_A_NV, + tex_op_args.input[0], tex_op_args.mapping[0], tex_op_args.component_usage[0])); + GL_EXTCALL(glCombinerInputNV(target, portion, GL_VARIABLE_B_NV, + GL_ZERO, GL_UNSIGNED_INVERT_NV, portion)); + GL_EXTCALL(glCombinerInputNV(target, portion, GL_VARIABLE_C_NV, + tex_op_args.input[1], tex_op_args.mapping[1], tex_op_args.component_usage[1])); + GL_EXTCALL(glCombinerInputNV(target, portion, GL_VARIABLE_D_NV, + GL_ZERO, GL_UNSIGNED_INVERT_NV, portion)); + + /* Output */ + if (op == WINED3D_TOP_ADD) + GL_EXTCALL(glCombinerOutputNV(target, portion, GL_DISCARD_NV, GL_DISCARD_NV, + output, GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE)); + else if (op == WINED3D_TOP_ADD_SIGNED) + GL_EXTCALL(glCombinerOutputNV(target, portion, GL_DISCARD_NV, GL_DISCARD_NV, + output, GL_NONE, GL_BIAS_BY_NEGATIVE_ONE_HALF_NV, GL_FALSE, GL_FALSE, GL_FALSE)); + else if (op == WINED3D_TOP_ADD_SIGNED_2X) + GL_EXTCALL(glCombinerOutputNV(target, portion, GL_DISCARD_NV, GL_DISCARD_NV, + output, GL_SCALE_BY_TWO_NV, GL_BIAS_BY_NEGATIVE_ONE_HALF_NV, GL_FALSE, GL_FALSE, GL_FALSE)); + break; + + case WINED3D_TOP_SUBTRACT: + /* Input, arg1*1+-arg2*1 */ + GL_EXTCALL(glCombinerInputNV(target, portion, GL_VARIABLE_A_NV, + tex_op_args.input[0], tex_op_args.mapping[0], tex_op_args.component_usage[0])); + GL_EXTCALL(glCombinerInputNV(target, portion, GL_VARIABLE_B_NV, + GL_ZERO, GL_UNSIGNED_INVERT_NV, portion)); + GL_EXTCALL(glCombinerInputNV(target, portion, GL_VARIABLE_C_NV, + tex_op_args.input[1], GL_SIGNED_NEGATE_NV, tex_op_args.component_usage[1])); + GL_EXTCALL(glCombinerInputNV(target, portion, GL_VARIABLE_D_NV, + GL_ZERO, GL_UNSIGNED_INVERT_NV, portion)); + + /* Output */ + GL_EXTCALL(glCombinerOutputNV(target, portion, GL_DISCARD_NV, GL_DISCARD_NV, + output, GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE)); + break; + + case WINED3D_TOP_ADD_SMOOTH: + /* Input, arg1*1+(1-arg1)*arg2 */ + GL_EXTCALL(glCombinerInputNV(target, portion, GL_VARIABLE_A_NV, + tex_op_args.input[0], tex_op_args.mapping[0], tex_op_args.component_usage[0])); + GL_EXTCALL(glCombinerInputNV(target, portion, GL_VARIABLE_B_NV, + GL_ZERO, GL_UNSIGNED_INVERT_NV, portion)); + GL_EXTCALL(glCombinerInputNV(target, portion, GL_VARIABLE_C_NV, + tex_op_args.input[0], invert_mapping(tex_op_args.mapping[0]), tex_op_args.component_usage[0])); + GL_EXTCALL(glCombinerInputNV(target, portion, GL_VARIABLE_D_NV, + tex_op_args.input[1], tex_op_args.mapping[1], tex_op_args.component_usage[1])); + + /* Output */ + GL_EXTCALL(glCombinerOutputNV(target, portion, GL_DISCARD_NV, GL_DISCARD_NV, + output, GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE)); + break; + + case WINED3D_TOP_BLEND_DIFFUSE_ALPHA: + case WINED3D_TOP_BLEND_TEXTURE_ALPHA: + case WINED3D_TOP_BLEND_FACTOR_ALPHA: + case WINED3D_TOP_BLEND_TEXTURE_ALPHA_PM: + case WINED3D_TOP_BLEND_CURRENT_ALPHA: + { + GLenum alpha_src = GL_PRIMARY_COLOR_NV; + if (op == WINED3D_TOP_BLEND_DIFFUSE_ALPHA) + alpha_src = d3dta_to_combiner_input(WINED3DTA_DIFFUSE, stage, texture_idx); + else if (op == WINED3D_TOP_BLEND_TEXTURE_ALPHA) + alpha_src = d3dta_to_combiner_input(WINED3DTA_TEXTURE, stage, texture_idx); + else if (op == WINED3D_TOP_BLEND_FACTOR_ALPHA) + alpha_src = d3dta_to_combiner_input(WINED3DTA_TFACTOR, stage, texture_idx); + else if (op == WINED3D_TOP_BLEND_TEXTURE_ALPHA_PM) + alpha_src = d3dta_to_combiner_input(WINED3DTA_TEXTURE, stage, texture_idx); + else if (op == WINED3D_TOP_BLEND_CURRENT_ALPHA) + alpha_src = d3dta_to_combiner_input(WINED3DTA_CURRENT, stage, texture_idx); + else + FIXME("Unhandled texture op %s, shouldn't happen.\n", debug_d3dtop(op)); + + /* Input, arg1*alpha_src+arg2*(1-alpha_src) */ + GL_EXTCALL(glCombinerInputNV(target, portion, GL_VARIABLE_A_NV, + tex_op_args.input[0], tex_op_args.mapping[0], tex_op_args.component_usage[0])); + if (op == WINED3D_TOP_BLEND_TEXTURE_ALPHA_PM) + { + GL_EXTCALL(glCombinerInputNV(target, portion, GL_VARIABLE_B_NV, + GL_ZERO, GL_UNSIGNED_INVERT_NV, portion)); + } else { + GL_EXTCALL(glCombinerInputNV(target, portion, GL_VARIABLE_B_NV, + alpha_src, GL_UNSIGNED_IDENTITY_NV, GL_ALPHA)); + } + GL_EXTCALL(glCombinerInputNV(target, portion, GL_VARIABLE_C_NV, + tex_op_args.input[1], tex_op_args.mapping[1], tex_op_args.component_usage[1])); + GL_EXTCALL(glCombinerInputNV(target, portion, GL_VARIABLE_D_NV, + alpha_src, GL_UNSIGNED_INVERT_NV, GL_ALPHA)); + + /* Output */ + GL_EXTCALL(glCombinerOutputNV(target, portion, GL_DISCARD_NV, GL_DISCARD_NV, + output, GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE)); + break; + } + + case WINED3D_TOP_MODULATE_ALPHA_ADD_COLOR: + /* Input, arg1_alpha*arg2_rgb+arg1_rgb*1 */ + if (is_alpha) + ERR("Only supported for WINED3D_TSS_COLOR_OP (WINED3DTOP_MODULATEALPHA_ADDCOLOR).\n"); + GL_EXTCALL(glCombinerInputNV(target, portion, GL_VARIABLE_A_NV, + tex_op_args.input[0], tex_op_args.mapping[0], GL_ALPHA)); + GL_EXTCALL(glCombinerInputNV(target, portion, GL_VARIABLE_B_NV, + tex_op_args.input[1], tex_op_args.mapping[1], tex_op_args.component_usage[1])); + GL_EXTCALL(glCombinerInputNV(target, portion, GL_VARIABLE_C_NV, + tex_op_args.input[0], tex_op_args.mapping[0], tex_op_args.component_usage[0])); + GL_EXTCALL(glCombinerInputNV(target, portion, GL_VARIABLE_D_NV, + GL_ZERO, GL_UNSIGNED_INVERT_NV, portion)); + + /* Output */ + GL_EXTCALL(glCombinerOutputNV(target, portion, GL_DISCARD_NV, GL_DISCARD_NV, + output, GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE)); + break; + + case WINED3D_TOP_MODULATE_COLOR_ADD_ALPHA: + /* Input, arg1_rgb*arg2_rgb+arg1_alpha*1 */ + if (is_alpha) + ERR("Only supported for WINED3D_TSS_COLOR_OP (WINED3DTOP_MODULATECOLOR_ADDALPHA).\n"); + GL_EXTCALL(glCombinerInputNV(target, portion, GL_VARIABLE_A_NV, + tex_op_args.input[0], tex_op_args.mapping[0], tex_op_args.component_usage[0])); + GL_EXTCALL(glCombinerInputNV(target, portion, GL_VARIABLE_B_NV, + tex_op_args.input[1], tex_op_args.mapping[1], tex_op_args.component_usage[1])); + GL_EXTCALL(glCombinerInputNV(target, portion, GL_VARIABLE_C_NV, + tex_op_args.input[0], tex_op_args.mapping[0], GL_ALPHA)); + GL_EXTCALL(glCombinerInputNV(target, portion, GL_VARIABLE_D_NV, + GL_ZERO, GL_UNSIGNED_INVERT_NV, portion)); + + /* Output */ + GL_EXTCALL(glCombinerOutputNV(target, portion, GL_DISCARD_NV, GL_DISCARD_NV, + output, GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE)); + break; + + case WINED3D_TOP_MODULATE_INVALPHA_ADD_COLOR: + /* Input, (1-arg1_alpha)*arg2_rgb+arg1_rgb*1 */ + if (is_alpha) + ERR("Only supported for WINED3D_TSS_COLOR_OP (WINED3DTOP_MODULATEINVALPHA_ADDCOLOR).\n"); + GL_EXTCALL(glCombinerInputNV(target, portion, GL_VARIABLE_A_NV, + tex_op_args.input[0], invert_mapping(tex_op_args.mapping[0]), GL_ALPHA)); + GL_EXTCALL(glCombinerInputNV(target, portion, GL_VARIABLE_B_NV, + tex_op_args.input[1], tex_op_args.mapping[1], tex_op_args.component_usage[1])); + GL_EXTCALL(glCombinerInputNV(target, portion, GL_VARIABLE_C_NV, + tex_op_args.input[0], tex_op_args.mapping[0], tex_op_args.component_usage[0])); + GL_EXTCALL(glCombinerInputNV(target, portion, GL_VARIABLE_D_NV, + GL_ZERO, GL_UNSIGNED_INVERT_NV, portion)); + + /* Output */ + GL_EXTCALL(glCombinerOutputNV(target, portion, GL_DISCARD_NV, GL_DISCARD_NV, + output, GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE)); + break; + + case WINED3D_TOP_MODULATE_INVCOLOR_ADD_ALPHA: + /* Input, (1-arg1_rgb)*arg2_rgb+arg1_alpha*1 */ + if (is_alpha) + ERR("Only supported for WINED3D_TSS_COLOR_OP (WINED3DTOP_MODULATEINVCOLOR_ADDALPHA).\n"); + GL_EXTCALL(glCombinerInputNV(target, portion, GL_VARIABLE_A_NV, + tex_op_args.input[0], invert_mapping(tex_op_args.mapping[0]), tex_op_args.component_usage[0])); + GL_EXTCALL(glCombinerInputNV(target, portion, GL_VARIABLE_B_NV, + tex_op_args.input[1], tex_op_args.mapping[1], tex_op_args.component_usage[1])); + GL_EXTCALL(glCombinerInputNV(target, portion, GL_VARIABLE_C_NV, + tex_op_args.input[0], tex_op_args.mapping[0], GL_ALPHA)); + GL_EXTCALL(glCombinerInputNV(target, portion, GL_VARIABLE_D_NV, + GL_ZERO, GL_UNSIGNED_INVERT_NV, portion)); + + /* Output */ + GL_EXTCALL(glCombinerOutputNV(target, portion, GL_DISCARD_NV, GL_DISCARD_NV, + output, GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE)); + break; + + case WINED3D_TOP_DOTPRODUCT3: + /* Input, arg1 . arg2 */ + /* FIXME: DX7 uses a different calculation? */ + GL_EXTCALL(glCombinerInputNV(target, portion, GL_VARIABLE_A_NV, + tex_op_args.input[0], GL_EXPAND_NORMAL_NV, tex_op_args.component_usage[0])); + GL_EXTCALL(glCombinerInputNV(target, portion, GL_VARIABLE_B_NV, + tex_op_args.input[1], GL_EXPAND_NORMAL_NV, tex_op_args.component_usage[1])); + + /* Output */ + GL_EXTCALL(glCombinerOutputNV(target, portion, output, GL_DISCARD_NV, + GL_DISCARD_NV, GL_NONE, GL_NONE, GL_TRUE, GL_FALSE, GL_FALSE)); + break; + + case WINED3D_TOP_MULTIPLY_ADD: + /* Input, arg3*1+arg1*arg2 */ + GL_EXTCALL(glCombinerInputNV(target, portion, GL_VARIABLE_A_NV, + tex_op_args.input[2], tex_op_args.mapping[2], tex_op_args.component_usage[2])); + GL_EXTCALL(glCombinerInputNV(target, portion, GL_VARIABLE_B_NV, + GL_ZERO, GL_UNSIGNED_INVERT_NV, portion)); + GL_EXTCALL(glCombinerInputNV(target, portion, GL_VARIABLE_C_NV, + tex_op_args.input[0], tex_op_args.mapping[0], tex_op_args.component_usage[0])); + GL_EXTCALL(glCombinerInputNV(target, portion, GL_VARIABLE_D_NV, + tex_op_args.input[1], tex_op_args.mapping[1], tex_op_args.component_usage[1])); + + /* Output */ + GL_EXTCALL(glCombinerOutputNV(target, portion, GL_DISCARD_NV, GL_DISCARD_NV, + output, GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE)); + break; + + case WINED3D_TOP_LERP: + /* Input, arg3*arg1+(1-arg3)*arg2 */ + GL_EXTCALL(glCombinerInputNV(target, portion, GL_VARIABLE_A_NV, + tex_op_args.input[2], tex_op_args.mapping[2], tex_op_args.component_usage[2])); + GL_EXTCALL(glCombinerInputNV(target, portion, GL_VARIABLE_B_NV, + tex_op_args.input[0], tex_op_args.mapping[0], tex_op_args.component_usage[0])); + GL_EXTCALL(glCombinerInputNV(target, portion, GL_VARIABLE_C_NV, + tex_op_args.input[2], invert_mapping(tex_op_args.mapping[2]), tex_op_args.component_usage[2])); + GL_EXTCALL(glCombinerInputNV(target, portion, GL_VARIABLE_D_NV, + tex_op_args.input[1], tex_op_args.mapping[1], tex_op_args.component_usage[1])); + + /* Output */ + GL_EXTCALL(glCombinerOutputNV(target, portion, GL_DISCARD_NV, GL_DISCARD_NV, + output, GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE)); + break; + + case WINED3D_TOP_BUMPENVMAP_LUMINANCE: + case WINED3D_TOP_BUMPENVMAP: + if (!gl_info->supported[NV_TEXTURE_SHADER]) + { + WARN("BUMPENVMAP requires GL_NV_texture_shader in this codepath\n"); + break; + } + + /* The bump map stage itself isn't exciting, just read the texture. But tell the next stage to + * perform bump mapping and source from the current stage. Pretty much a SELECTARG2. + * ARG2 is passed through unmodified(apps will most likely use D3DTA_CURRENT for arg2, arg1 + * (which will most likely be D3DTA_TEXTURE) is available as a texture shader input for the + * next stage */ + GL_EXTCALL(glCombinerInputNV(target, portion, GL_VARIABLE_A_NV, + tex_op_args.input[1], tex_op_args.mapping[1], tex_op_args.component_usage[1])); + GL_EXTCALL(glCombinerInputNV(target, portion, GL_VARIABLE_B_NV, + GL_ZERO, GL_UNSIGNED_INVERT_NV, portion)); + /* Always pass through to CURRENT, ignore temp arg */ + GL_EXTCALL(glCombinerOutputNV(target, portion, GL_SPARE0_NV, GL_DISCARD_NV, + GL_DISCARD_NV, GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE)); + break; + + default: + FIXME("Unhandled texture op: stage %d, is_alpha %d, op %s (%#x), arg1 %#x, arg2 %#x, arg3 %#x, texture_idx %d.\n", + stage, is_alpha, debug_d3dtop(op), op, arg1, arg2, arg3, texture_idx); + } + + checkGLcall("set_tex_op_nvrc()"); +} + + +static void nvrc_colorop(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + DWORD stage = (state_id - STATE_TEXTURESTAGE(0, 0)) / (WINED3D_HIGHEST_TEXTURE_STATE + 1); + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + BOOL tex_used = context->fixed_function_usage_map & (1u << stage); + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + unsigned int mapped_stage = context_gl->tex_unit_map[stage]; + + TRACE("Setting color op for stage %u.\n", stage); + + /* Using a pixel shader? Don't care for anything here, the shader applying does it */ + if (use_ps(state)) return; + + if (stage != mapped_stage) WARN("Using non 1:1 mapping: %d -> %d!\n", stage, mapped_stage); + + if (mapped_stage != WINED3D_UNMAPPED_STAGE) + { + if (tex_used && mapped_stage >= gl_info->limits.textures) + { + FIXME("Attempt to enable unsupported stage!\n"); + return; + } + wined3d_context_gl_active_texture(context_gl, gl_info, mapped_stage); + } + + if (context->lowest_disabled_stage > 0) + { + gl_info->gl_ops.gl.p_glEnable(GL_REGISTER_COMBINERS_NV); + GL_EXTCALL(glCombinerParameteriNV(GL_NUM_GENERAL_COMBINERS_NV, context->lowest_disabled_stage)); + } + else + { + gl_info->gl_ops.gl.p_glDisable(GL_REGISTER_COMBINERS_NV); + } + if (stage >= context->lowest_disabled_stage) + { + TRACE("Stage disabled\n"); + if (mapped_stage != WINED3D_UNMAPPED_STAGE) + { + /* Disable everything here */ + gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_2D); + checkGLcall("glDisable(GL_TEXTURE_2D)"); + gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_3D); + checkGLcall("glDisable(GL_TEXTURE_3D)"); + if (gl_info->supported[ARB_TEXTURE_CUBE_MAP]) + { + gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_CUBE_MAP_ARB); + checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)"); + } + if (gl_info->supported[ARB_TEXTURE_RECTANGLE]) + { + gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_RECTANGLE_ARB); + checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)"); + } + if (gl_info->supported[NV_TEXTURE_SHADER2] && mapped_stage < gl_info->limits.textures) + { + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_SHADER_NV, GL_SHADER_OPERATION_NV, GL_NONE); + } + } + /* All done */ + return; + } + + /* The sampler will also activate the correct texture dimensions, so no need to do it here + * if the sampler for this stage is dirty + */ + if (!isStateDirty(context, STATE_SAMPLER(stage))) + { + if (tex_used) + { + if (gl_info->supported[NV_TEXTURE_SHADER2]) + nvts_activate_dimensions(state, stage, context_gl); + else + texture_activate_dimensions(state->textures[stage], gl_info); + } + } + + /* Set the texture combiners */ + set_tex_op_nvrc(gl_info, state, FALSE, stage, + state->texture_states[stage][WINED3D_TSS_COLOR_OP], + state->texture_states[stage][WINED3D_TSS_COLOR_ARG1], + state->texture_states[stage][WINED3D_TSS_COLOR_ARG2], + state->texture_states[stage][WINED3D_TSS_COLOR_ARG0], + mapped_stage, + state->texture_states[stage][WINED3D_TSS_RESULT_ARG]); + + /* In register combiners bump mapping is done in the stage AFTER the one that has the bump map operation set, + * thus the texture shader may have to be updated + */ + if (gl_info->supported[NV_TEXTURE_SHADER2]) + { + BOOL usesBump = (state->texture_states[stage][WINED3D_TSS_COLOR_OP] == WINED3D_TOP_BUMPENVMAP_LUMINANCE + || state->texture_states[stage][WINED3D_TSS_COLOR_OP] == WINED3D_TOP_BUMPENVMAP); + BOOL usedBump = !!(context->texShaderBumpMap & 1u << (stage + 1)); + if (usesBump != usedBump) + { + wined3d_context_gl_active_texture(context_gl, gl_info, mapped_stage + 1); + nvts_activate_dimensions(state, stage + 1, context_gl); + wined3d_context_gl_active_texture(context_gl, gl_info, mapped_stage); + } + } +} + +static void nvrc_resultarg(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + DWORD stage = (state_id - STATE_TEXTURESTAGE(0, 0)) / (WINED3D_HIGHEST_TEXTURE_STATE + 1); + + TRACE("Setting result arg for stage %u.\n", stage); + + if (!isStateDirty(context, STATE_TEXTURESTAGE(stage, WINED3D_TSS_COLOR_OP))) + { + context_apply_state(context, state, STATE_TEXTURESTAGE(stage, WINED3D_TSS_COLOR_OP)); + } + if (!isStateDirty(context, STATE_TEXTURESTAGE(stage, WINED3D_TSS_ALPHA_OP))) + { + context_apply_state(context, state, STATE_TEXTURESTAGE(stage, WINED3D_TSS_ALPHA_OP)); + } +} + +static void nvts_texdim(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + unsigned int sampler, mapped_stage; + + sampler = state_id - STATE_SAMPLER(0); + mapped_stage = context_gl->tex_unit_map[sampler]; + + /* No need to enable / disable anything here for unused samplers. The tex_colorop + * handler takes care. Also no action is needed with pixel shaders, or if tex_colorop + * will take care of this business. */ + if (mapped_stage == WINED3D_UNMAPPED_STAGE || mapped_stage >= context_gl->gl_info->limits.textures) + return; + if (sampler >= context->lowest_disabled_stage) + return; + if (isStateDirty(context, STATE_TEXTURESTAGE(sampler, WINED3D_TSS_COLOR_OP))) + return; + + nvts_activate_dimensions(state, sampler, context_gl); +} + +static void nvts_bumpenvmat(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + DWORD stage = (state_id - STATE_TEXTURESTAGE(0, 0)) / (WINED3D_HIGHEST_TEXTURE_STATE + 1); + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + unsigned int mapped_stage = context_gl->tex_unit_map[stage + 1]; + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + float mat[2][2]; + + /* Direct3D sets the matrix in the stage reading the perturbation map. The result is used to + * offset the destination stage(always stage + 1 in d3d). In GL_NV_texture_shader, the bump + * map offsetting is done in the stage reading the bump mapped texture, and the perturbation + * map is read from a specified source stage(always stage - 1 for d3d). Thus set the matrix + * for stage + 1. Keep the nvrc tex unit mapping in mind too + */ + if (mapped_stage < gl_info->limits.textures) + { + wined3d_context_gl_active_texture(context_gl, gl_info, mapped_stage); + + /* We can't just pass a pointer to the state to GL due to the + * different matrix format (column major vs row major). */ + mat[0][0] = *((float *)&state->texture_states[stage][WINED3D_TSS_BUMPENV_MAT00]); + mat[1][0] = *((float *)&state->texture_states[stage][WINED3D_TSS_BUMPENV_MAT01]); + mat[0][1] = *((float *)&state->texture_states[stage][WINED3D_TSS_BUMPENV_MAT10]); + mat[1][1] = *((float *)&state->texture_states[stage][WINED3D_TSS_BUMPENV_MAT11]); + gl_info->gl_ops.gl.p_glTexEnvfv(GL_TEXTURE_SHADER_NV, GL_OFFSET_TEXTURE_MATRIX_NV, (float *)mat); + checkGLcall("glTexEnvfv(GL_TEXTURE_SHADER_NV, GL_OFFSET_TEXTURE_MATRIX_NV, mat)"); + } +} + +static void nvrc_texfactor(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + struct wined3d_color color; + + wined3d_color_from_d3dcolor(&color, state->render_states[WINED3D_RS_TEXTUREFACTOR]); + GL_EXTCALL(glCombinerParameterfvNV(GL_CONSTANT_COLOR0_NV, &color.r)); +} + +/* Context activation is done by the caller. */ +static void nvrc_enable(const struct wined3d_context *context, BOOL enable) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl_const(context)->gl_info; + + if (enable) + { + gl_info->gl_ops.gl.p_glEnable(GL_REGISTER_COMBINERS_NV); + checkGLcall("glEnable(GL_REGISTER_COMBINERS_NV)"); + } + else + { + gl_info->gl_ops.gl.p_glDisable(GL_REGISTER_COMBINERS_NV); + checkGLcall("glDisable(GL_REGISTER_COMBINERS_NV)"); + } +} + +/* Context activation is done by the caller. */ +static void nvts_enable(const struct wined3d_context *context, BOOL enable) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl_const(context)->gl_info; + + nvrc_enable(context, enable); + if (enable) + { + gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_SHADER_NV); + checkGLcall("glEnable(GL_TEXTURE_SHADER_NV)"); + } + else + { + gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_SHADER_NV); + checkGLcall("glDisable(GL_TEXTURE_SHADER_NV)"); + } +} + +static void nvrc_fragment_get_caps(const struct wined3d_adapter *adapter, struct fragment_caps *caps) +{ + const struct wined3d_gl_info *gl_info = &adapter->gl_info; + + caps->wined3d_caps = 0; + caps->PrimitiveMiscCaps = WINED3DPMISCCAPS_TSSARGTEMP; + + /* The caps below can be supported but aren't handled yet in utils.c + * 'd3dta_to_combiner_input', disable them until support is fixed */ +#if 0 + if (gl_info->supported[NV_REGISTER_COMBINERS2]) + caps->PrimitiveMiscCaps |= WINED3DPMISCCAPS_PERSTAGECONSTANT; +#endif + + caps->TextureOpCaps = WINED3DTEXOPCAPS_ADD + | WINED3DTEXOPCAPS_ADDSIGNED + | WINED3DTEXOPCAPS_ADDSIGNED2X + | WINED3DTEXOPCAPS_MODULATE + | WINED3DTEXOPCAPS_MODULATE2X + | WINED3DTEXOPCAPS_MODULATE4X + | WINED3DTEXOPCAPS_SELECTARG1 + | WINED3DTEXOPCAPS_SELECTARG2 + | WINED3DTEXOPCAPS_DISABLE + | WINED3DTEXOPCAPS_BLENDDIFFUSEALPHA + | WINED3DTEXOPCAPS_BLENDTEXTUREALPHA + | WINED3DTEXOPCAPS_BLENDFACTORALPHA + | WINED3DTEXOPCAPS_BLENDCURRENTALPHA + | WINED3DTEXOPCAPS_LERP + | WINED3DTEXOPCAPS_SUBTRACT + | WINED3DTEXOPCAPS_ADDSMOOTH + | WINED3DTEXOPCAPS_MULTIPLYADD + | WINED3DTEXOPCAPS_MODULATEALPHA_ADDCOLOR + | WINED3DTEXOPCAPS_MODULATECOLOR_ADDALPHA + | WINED3DTEXOPCAPS_BLENDTEXTUREALPHAPM + | WINED3DTEXOPCAPS_DOTPRODUCT3 + | WINED3DTEXOPCAPS_MODULATEINVALPHA_ADDCOLOR + | WINED3DTEXOPCAPS_MODULATEINVCOLOR_ADDALPHA; + + if (gl_info->supported[NV_TEXTURE_SHADER2]) + { + /* Bump mapping is supported already in NV_TEXTURE_SHADER, but that extension does + * not support 3D textures. This asks for trouble if an app uses both bump mapping + * and 3D textures. It also allows us to keep the code simpler by having texture + * shaders constantly enabled. */ + caps->TextureOpCaps |= WINED3DTEXOPCAPS_BUMPENVMAP; + /* TODO: Luminance bump map? */ + } + +#if 0 + /* FIXME: Add + caps->TextureOpCaps |= WINED3DTEXOPCAPS_BUMPENVMAPLUMINANCE + WINED3DTEXOPCAPS_PREMODULATE */ +#endif + + caps->MaxTextureBlendStages = min(WINED3D_MAX_TEXTURES, gl_info->limits.general_combiners); + caps->MaxSimultaneousTextures = gl_info->limits.textures; +} + +static DWORD nvrc_fragment_get_emul_mask(const struct wined3d_gl_info *gl_info) +{ + return GL_EXT_EMUL_ARB_MULTITEXTURE | GL_EXT_EMUL_EXT_FOG_COORD; +} + +static void *nvrc_fragment_alloc(const struct wined3d_shader_backend_ops *shader_backend, void *shader_priv) +{ + return shader_priv; +} + +/* Context activation is done by the caller. */ +static void nvrc_fragment_free(struct wined3d_device *device, struct wined3d_context *context) {} + +/* Two fixed function pipeline implementations using GL_NV_register_combiners and + * GL_NV_texture_shader. The nvts_fragment_pipeline assumes that both extensions + * are available(geforce 3 and newer), while nvrc_fragment_pipeline uses only the + * register combiners extension(Pre-GF3). + */ + +static BOOL nvts_color_fixup_supported(struct color_fixup_desc fixup) +{ + /* We only support identity conversions. */ + return is_identity_fixup(fixup); +} + +static const struct wined3d_state_entry_template nvrc_fragmentstate_template[] = +{ + { STATE_TEXTURESTAGE(0, WINED3D_TSS_COLOR_OP), { STATE_TEXTURESTAGE(0, WINED3D_TSS_COLOR_OP), nvrc_colorop }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(0, WINED3D_TSS_COLOR_ARG1), { STATE_TEXTURESTAGE(0, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(0, WINED3D_TSS_COLOR_ARG2), { STATE_TEXTURESTAGE(0, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(0, WINED3D_TSS_ALPHA_OP), { STATE_TEXTURESTAGE(0, WINED3D_TSS_ALPHA_OP), tex_alphaop }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(0, WINED3D_TSS_ALPHA_ARG1), { STATE_TEXTURESTAGE(0, WINED3D_TSS_ALPHA_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(0, WINED3D_TSS_ALPHA_ARG2), { STATE_TEXTURESTAGE(0, WINED3D_TSS_ALPHA_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_MAT00), { STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_MAT00), nvts_bumpenvmat }, NV_TEXTURE_SHADER2 }, + { STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_MAT01), { STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_MAT00), NULL }, NV_TEXTURE_SHADER2 }, + { STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_MAT10), { STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_MAT00), NULL }, NV_TEXTURE_SHADER2 }, + { STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_MAT11), { STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_MAT00), NULL }, NV_TEXTURE_SHADER2 }, + { STATE_TEXTURESTAGE(0, WINED3D_TSS_COLOR_ARG0), { STATE_TEXTURESTAGE(0, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(0, WINED3D_TSS_ALPHA_ARG0), { STATE_TEXTURESTAGE(0, WINED3D_TSS_ALPHA_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(0, WINED3D_TSS_RESULT_ARG), { STATE_TEXTURESTAGE(0, WINED3D_TSS_RESULT_ARG), nvrc_resultarg }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(1, WINED3D_TSS_COLOR_OP), { STATE_TEXTURESTAGE(1, WINED3D_TSS_COLOR_OP), nvrc_colorop }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(1, WINED3D_TSS_COLOR_ARG1), { STATE_TEXTURESTAGE(1, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(1, WINED3D_TSS_COLOR_ARG2), { STATE_TEXTURESTAGE(1, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(1, WINED3D_TSS_ALPHA_OP), { STATE_TEXTURESTAGE(1, WINED3D_TSS_ALPHA_OP), tex_alphaop }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(1, WINED3D_TSS_ALPHA_ARG1), { STATE_TEXTURESTAGE(1, WINED3D_TSS_ALPHA_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(1, WINED3D_TSS_ALPHA_ARG2), { STATE_TEXTURESTAGE(1, WINED3D_TSS_ALPHA_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_MAT00), { STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_MAT00), nvts_bumpenvmat }, NV_TEXTURE_SHADER2 }, + { STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_MAT01), { STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_MAT00), NULL }, NV_TEXTURE_SHADER2 }, + { STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_MAT10), { STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_MAT00), NULL }, NV_TEXTURE_SHADER2 }, + { STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_MAT11), { STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_MAT00), NULL }, NV_TEXTURE_SHADER2 }, + { STATE_TEXTURESTAGE(1, WINED3D_TSS_COLOR_ARG0), { STATE_TEXTURESTAGE(1, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(1, WINED3D_TSS_ALPHA_ARG0), { STATE_TEXTURESTAGE(1, WINED3D_TSS_ALPHA_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(1, WINED3D_TSS_RESULT_ARG), { STATE_TEXTURESTAGE(1, WINED3D_TSS_RESULT_ARG), nvrc_resultarg }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(2, WINED3D_TSS_COLOR_OP), { STATE_TEXTURESTAGE(2, WINED3D_TSS_COLOR_OP), nvrc_colorop }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(2, WINED3D_TSS_COLOR_ARG1), { STATE_TEXTURESTAGE(2, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(2, WINED3D_TSS_COLOR_ARG2), { STATE_TEXTURESTAGE(2, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(2, WINED3D_TSS_ALPHA_OP), { STATE_TEXTURESTAGE(2, WINED3D_TSS_ALPHA_OP), tex_alphaop }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(2, WINED3D_TSS_ALPHA_ARG1), { STATE_TEXTURESTAGE(2, WINED3D_TSS_ALPHA_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(2, WINED3D_TSS_ALPHA_ARG2), { STATE_TEXTURESTAGE(2, WINED3D_TSS_ALPHA_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_MAT00), { STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_MAT00), nvts_bumpenvmat }, NV_TEXTURE_SHADER2 }, + { STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_MAT01), { STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_MAT00), NULL }, NV_TEXTURE_SHADER2 }, + { STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_MAT10), { STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_MAT00), NULL }, NV_TEXTURE_SHADER2 }, + { STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_MAT11), { STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_MAT00), NULL }, NV_TEXTURE_SHADER2 }, + { STATE_TEXTURESTAGE(2, WINED3D_TSS_COLOR_ARG0), { STATE_TEXTURESTAGE(2, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(2, WINED3D_TSS_ALPHA_ARG0), { STATE_TEXTURESTAGE(2, WINED3D_TSS_ALPHA_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(2, WINED3D_TSS_RESULT_ARG), { STATE_TEXTURESTAGE(2, WINED3D_TSS_RESULT_ARG), nvrc_resultarg }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(3, WINED3D_TSS_COLOR_OP), { STATE_TEXTURESTAGE(3, WINED3D_TSS_COLOR_OP), nvrc_colorop }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(3, WINED3D_TSS_COLOR_ARG1), { STATE_TEXTURESTAGE(3, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(3, WINED3D_TSS_COLOR_ARG2), { STATE_TEXTURESTAGE(3, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(3, WINED3D_TSS_ALPHA_OP), { STATE_TEXTURESTAGE(3, WINED3D_TSS_ALPHA_OP), tex_alphaop }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(3, WINED3D_TSS_ALPHA_ARG1), { STATE_TEXTURESTAGE(3, WINED3D_TSS_ALPHA_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(3, WINED3D_TSS_ALPHA_ARG2), { STATE_TEXTURESTAGE(3, WINED3D_TSS_ALPHA_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_MAT00), { STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_MAT00), nvts_bumpenvmat }, NV_TEXTURE_SHADER2 }, + { STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_MAT01), { STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_MAT00), NULL }, NV_TEXTURE_SHADER2 }, + { STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_MAT10), { STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_MAT00), NULL }, NV_TEXTURE_SHADER2 }, + { STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_MAT11), { STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_MAT00), NULL }, NV_TEXTURE_SHADER2 }, + { STATE_TEXTURESTAGE(3, WINED3D_TSS_COLOR_ARG0), { STATE_TEXTURESTAGE(3, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(3, WINED3D_TSS_ALPHA_ARG0), { STATE_TEXTURESTAGE(3, WINED3D_TSS_ALPHA_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(3, WINED3D_TSS_RESULT_ARG), { STATE_TEXTURESTAGE(3, WINED3D_TSS_RESULT_ARG), nvrc_resultarg }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(4, WINED3D_TSS_COLOR_OP), { STATE_TEXTURESTAGE(4, WINED3D_TSS_COLOR_OP), nvrc_colorop }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(4, WINED3D_TSS_COLOR_ARG1), { STATE_TEXTURESTAGE(4, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(4, WINED3D_TSS_COLOR_ARG2), { STATE_TEXTURESTAGE(4, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(4, WINED3D_TSS_ALPHA_OP), { STATE_TEXTURESTAGE(4, WINED3D_TSS_ALPHA_OP), tex_alphaop }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(4, WINED3D_TSS_ALPHA_ARG1), { STATE_TEXTURESTAGE(4, WINED3D_TSS_ALPHA_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(4, WINED3D_TSS_ALPHA_ARG2), { STATE_TEXTURESTAGE(4, WINED3D_TSS_ALPHA_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_MAT00), { STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_MAT00), nvts_bumpenvmat }, NV_TEXTURE_SHADER2 }, + { STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_MAT01), { STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_MAT00), NULL }, NV_TEXTURE_SHADER2 }, + { STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_MAT10), { STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_MAT00), NULL }, NV_TEXTURE_SHADER2 }, + { STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_MAT11), { STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_MAT00), NULL }, NV_TEXTURE_SHADER2 }, + { STATE_TEXTURESTAGE(4, WINED3D_TSS_COLOR_ARG0), { STATE_TEXTURESTAGE(4, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(4, WINED3D_TSS_ALPHA_ARG0), { STATE_TEXTURESTAGE(4, WINED3D_TSS_ALPHA_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(4, WINED3D_TSS_RESULT_ARG), { STATE_TEXTURESTAGE(4, WINED3D_TSS_RESULT_ARG), nvrc_resultarg }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(5, WINED3D_TSS_COLOR_OP), { STATE_TEXTURESTAGE(5, WINED3D_TSS_COLOR_OP), nvrc_colorop }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(5, WINED3D_TSS_COLOR_ARG1), { STATE_TEXTURESTAGE(5, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(5, WINED3D_TSS_COLOR_ARG2), { STATE_TEXTURESTAGE(5, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(5, WINED3D_TSS_ALPHA_OP), { STATE_TEXTURESTAGE(5, WINED3D_TSS_ALPHA_OP), tex_alphaop }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(5, WINED3D_TSS_ALPHA_ARG1), { STATE_TEXTURESTAGE(5, WINED3D_TSS_ALPHA_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(5, WINED3D_TSS_ALPHA_ARG2), { STATE_TEXTURESTAGE(5, WINED3D_TSS_ALPHA_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_MAT00), { STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_MAT00), nvts_bumpenvmat }, NV_TEXTURE_SHADER2 }, + { STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_MAT01), { STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_MAT00), NULL }, NV_TEXTURE_SHADER2 }, + { STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_MAT10), { STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_MAT00), NULL }, NV_TEXTURE_SHADER2 }, + { STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_MAT11), { STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_MAT00), NULL }, NV_TEXTURE_SHADER2 }, + { STATE_TEXTURESTAGE(5, WINED3D_TSS_COLOR_ARG0), { STATE_TEXTURESTAGE(5, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(5, WINED3D_TSS_ALPHA_ARG0), { STATE_TEXTURESTAGE(5, WINED3D_TSS_ALPHA_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(5, WINED3D_TSS_RESULT_ARG), { STATE_TEXTURESTAGE(5, WINED3D_TSS_RESULT_ARG), nvrc_resultarg }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(6, WINED3D_TSS_COLOR_OP), { STATE_TEXTURESTAGE(6, WINED3D_TSS_COLOR_OP), nvrc_colorop }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(6, WINED3D_TSS_COLOR_ARG1), { STATE_TEXTURESTAGE(6, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(6, WINED3D_TSS_COLOR_ARG2), { STATE_TEXTURESTAGE(6, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(6, WINED3D_TSS_ALPHA_OP), { STATE_TEXTURESTAGE(6, WINED3D_TSS_ALPHA_OP), tex_alphaop }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(6, WINED3D_TSS_ALPHA_ARG1), { STATE_TEXTURESTAGE(6, WINED3D_TSS_ALPHA_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(6, WINED3D_TSS_ALPHA_ARG2), { STATE_TEXTURESTAGE(6, WINED3D_TSS_ALPHA_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_MAT00), { STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_MAT00), nvts_bumpenvmat }, NV_TEXTURE_SHADER2 }, + { STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_MAT01), { STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_MAT00), NULL }, NV_TEXTURE_SHADER2 }, + { STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_MAT10), { STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_MAT00), NULL }, NV_TEXTURE_SHADER2 }, + { STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_MAT11), { STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_MAT00), NULL }, NV_TEXTURE_SHADER2 }, + { STATE_TEXTURESTAGE(6, WINED3D_TSS_COLOR_ARG0), { STATE_TEXTURESTAGE(6, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(6, WINED3D_TSS_ALPHA_ARG0), { STATE_TEXTURESTAGE(6, WINED3D_TSS_ALPHA_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(6, WINED3D_TSS_RESULT_ARG), { STATE_TEXTURESTAGE(6, WINED3D_TSS_RESULT_ARG), nvrc_resultarg }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(7, WINED3D_TSS_COLOR_OP), { STATE_TEXTURESTAGE(7, WINED3D_TSS_COLOR_OP), nvrc_colorop }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(7, WINED3D_TSS_COLOR_ARG1), { STATE_TEXTURESTAGE(7, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(7, WINED3D_TSS_COLOR_ARG2), { STATE_TEXTURESTAGE(7, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(7, WINED3D_TSS_ALPHA_OP), { STATE_TEXTURESTAGE(7, WINED3D_TSS_ALPHA_OP), tex_alphaop }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(7, WINED3D_TSS_ALPHA_ARG1), { STATE_TEXTURESTAGE(7, WINED3D_TSS_ALPHA_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(7, WINED3D_TSS_ALPHA_ARG2), { STATE_TEXTURESTAGE(7, WINED3D_TSS_ALPHA_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_MAT00), { STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_MAT00), nvts_bumpenvmat }, NV_TEXTURE_SHADER2 }, + { STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_MAT01), { STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_MAT00), NULL }, NV_TEXTURE_SHADER2 }, + { STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_MAT10), { STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_MAT00), NULL }, NV_TEXTURE_SHADER2 }, + { STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_MAT11), { STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_MAT00), NULL }, NV_TEXTURE_SHADER2 }, + { STATE_TEXTURESTAGE(7, WINED3D_TSS_COLOR_ARG0), { STATE_TEXTURESTAGE(7, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(7, WINED3D_TSS_ALPHA_ARG0), { STATE_TEXTURESTAGE(7, WINED3D_TSS_ALPHA_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(7, WINED3D_TSS_RESULT_ARG), { STATE_TEXTURESTAGE(7, WINED3D_TSS_RESULT_ARG), nvrc_resultarg }, WINED3D_GL_EXT_NONE }, + { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), apply_pixelshader }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_SRGBWRITEENABLE), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_TEXTUREFACTOR), { STATE_RENDER(WINED3D_RS_TEXTUREFACTOR), nvrc_texfactor }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_ALPHAFUNC), { STATE_RENDER(WINED3D_RS_ALPHATESTENABLE), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_ALPHAREF), { STATE_RENDER(WINED3D_RS_ALPHATESTENABLE), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_ALPHATESTENABLE), { STATE_RENDER(WINED3D_RS_ALPHATESTENABLE), state_alpha_test }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_COLORKEYENABLE), { STATE_RENDER(WINED3D_RS_ALPHATESTENABLE), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_COLOR_KEY, { STATE_COLOR_KEY, state_nop }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_FOGCOLOR), { STATE_RENDER(WINED3D_RS_FOGCOLOR), state_fogcolor }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_FOGDENSITY), { STATE_RENDER(WINED3D_RS_FOGDENSITY), state_fogdensity }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_FOGENABLE), { STATE_RENDER(WINED3D_RS_FOGENABLE), state_fog_fragpart }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_FOGTABLEMODE), { STATE_RENDER(WINED3D_RS_FOGENABLE), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_FOGVERTEXMODE), { STATE_RENDER(WINED3D_RS_FOGENABLE), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_FOGSTART), { STATE_RENDER(WINED3D_RS_FOGSTART), state_fogstartend }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_FOGEND), { STATE_RENDER(WINED3D_RS_FOGSTART), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_SHADEMODE), { STATE_RENDER(WINED3D_RS_SHADEMODE), state_shademode }, WINED3D_GL_EXT_NONE }, + { STATE_SAMPLER(0), { STATE_SAMPLER(0), nvts_texdim }, NV_TEXTURE_SHADER2 }, + { STATE_SAMPLER(0), { STATE_SAMPLER(0), sampler_texdim }, WINED3D_GL_EXT_NONE }, + { STATE_SAMPLER(1), { STATE_SAMPLER(1), nvts_texdim }, NV_TEXTURE_SHADER2 }, + { STATE_SAMPLER(1), { STATE_SAMPLER(1), sampler_texdim }, WINED3D_GL_EXT_NONE }, + { STATE_SAMPLER(2), { STATE_SAMPLER(2), nvts_texdim }, NV_TEXTURE_SHADER2 }, + { STATE_SAMPLER(2), { STATE_SAMPLER(2), sampler_texdim }, WINED3D_GL_EXT_NONE }, + { STATE_SAMPLER(3), { STATE_SAMPLER(3), nvts_texdim }, NV_TEXTURE_SHADER2 }, + { STATE_SAMPLER(3), { STATE_SAMPLER(3), sampler_texdim }, WINED3D_GL_EXT_NONE }, + { STATE_SAMPLER(4), { STATE_SAMPLER(4), nvts_texdim }, NV_TEXTURE_SHADER2 }, + { STATE_SAMPLER(4), { STATE_SAMPLER(4), sampler_texdim }, WINED3D_GL_EXT_NONE }, + { STATE_SAMPLER(5), { STATE_SAMPLER(5), nvts_texdim }, NV_TEXTURE_SHADER2 }, + { STATE_SAMPLER(5), { STATE_SAMPLER(5), sampler_texdim }, WINED3D_GL_EXT_NONE }, + { STATE_SAMPLER(6), { STATE_SAMPLER(6), nvts_texdim }, NV_TEXTURE_SHADER2 }, + { STATE_SAMPLER(6), { STATE_SAMPLER(6), sampler_texdim }, WINED3D_GL_EXT_NONE }, + { STATE_SAMPLER(7), { STATE_SAMPLER(7), nvts_texdim }, NV_TEXTURE_SHADER2 }, + { STATE_SAMPLER(7), { STATE_SAMPLER(7), sampler_texdim }, WINED3D_GL_EXT_NONE }, + {0 /* Terminate */, { 0, 0 }, WINED3D_GL_EXT_NONE }, +}; + +static BOOL nvrc_context_alloc(struct wined3d_context *context) +{ + return TRUE; +} + +static void nvrc_context_free(struct wined3d_context *context) +{ +} + + +const struct wined3d_fragment_pipe_ops nvts_fragment_pipeline = +{ + nvts_enable, + nvrc_fragment_get_caps, + nvrc_fragment_get_emul_mask, + nvrc_fragment_alloc, + nvrc_fragment_free, + nvrc_context_alloc, + nvrc_context_free, + nvts_color_fixup_supported, + nvrc_fragmentstate_template, +}; + +const struct wined3d_fragment_pipe_ops nvrc_fragment_pipeline = +{ + nvrc_enable, + nvrc_fragment_get_caps, + nvrc_fragment_get_emul_mask, + nvrc_fragment_alloc, + nvrc_fragment_free, + nvrc_context_alloc, + nvrc_context_free, + nvts_color_fixup_supported, + nvrc_fragmentstate_template, +}; diff --git a/wrappers/directx/d3dwine_wrapper/palette.c b/wrappers/directx/d3dwine_wrapper/palette.c new file mode 100644 index 00000000000..615fb00905f --- /dev/null +++ b/wrappers/directx/d3dwine_wrapper/palette.c @@ -0,0 +1,181 @@ +/* DirectDraw - IDirectPalette base interface + * + * Copyright 1997-2000 Marcus Meissner + * Copyright 2000-2001 TransGaming Technologies Inc. + * Copyright 2006 Stefan Dösinger for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ +#include "config.h" +#include "wine/port.h" +#include "wined3d_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(d3d); + +ULONG CDECL wined3d_palette_incref(struct wined3d_palette *palette) +{ + ULONG refcount = InterlockedIncrement(&palette->ref); + + TRACE("%p increasing refcount to %u.\n", palette, refcount); + + return refcount; +} + +static void wined3d_palette_destroy_object(void *object) +{ + TRACE("object %p.\n", object); + + heap_free(object); +} + +ULONG CDECL wined3d_palette_decref(struct wined3d_palette *palette) +{ + ULONG refcount = InterlockedDecrement(&palette->ref); + + TRACE("%p decreasing refcount to %u.\n", palette, refcount); + + if (!refcount) + wined3d_cs_destroy_object(palette->device->cs, wined3d_palette_destroy_object, palette); + + return refcount; +} + +HRESULT CDECL wined3d_palette_get_entries(const struct wined3d_palette *palette, + DWORD flags, DWORD start, DWORD count, PALETTEENTRY *entries) +{ + unsigned int i; + TRACE("palette %p, flags %#x, start %u, count %u, entries %p.\n", + palette, flags, start, count, entries); + + if (flags) + return WINED3DERR_INVALIDCALL; /* unchecked */ + if (start > palette->size || count > palette->size - start) + return WINED3DERR_INVALIDCALL; + + if (palette->flags & WINED3D_PALETTE_8BIT_ENTRIES) + { + BYTE *entry = (BYTE *)entries; + + for (i = start; i < count + start; ++i) + *entry++ = palette->colors[i].rgbRed; + } + else + { + for (i = 0; i < count; ++i) + { + entries[i].peRed = palette->colors[i + start].rgbRed; + entries[i].peGreen = palette->colors[i + start].rgbGreen; + entries[i].peBlue = palette->colors[i + start].rgbBlue; + entries[i].peFlags = palette->colors[i + start].rgbReserved; + } + } + + return WINED3D_OK; +} + +void CDECL wined3d_palette_apply_to_dc(const struct wined3d_palette *palette, HDC dc) +{ + if (SetDIBColorTable(dc, 0, 256, palette->colors) != 256) + ERR("Failed to set DIB color table.\n"); +} + +HRESULT CDECL wined3d_palette_set_entries(struct wined3d_palette *palette, + DWORD flags, DWORD start, DWORD count, const PALETTEENTRY *entries) +{ + unsigned int i; + + TRACE("palette %p, flags %#x, start %u, count %u, entries %p.\n", + palette, flags, start, count, entries); + TRACE("Palette flags: %#x.\n", palette->flags); + + wined3d_cs_finish(palette->device->cs, WINED3D_CS_QUEUE_DEFAULT); + + if (palette->flags & WINED3D_PALETTE_8BIT_ENTRIES) + { + const BYTE *entry = (const BYTE *)entries; + + for (i = start; i < count + start; ++i) + palette->colors[i].rgbRed = *entry++; + } + else + { + for (i = 0; i < count; ++i) + { + palette->colors[i + start].rgbRed = entries[i].peRed; + palette->colors[i + start].rgbGreen = entries[i].peGreen; + palette->colors[i + start].rgbBlue = entries[i].peBlue; + palette->colors[i + start].rgbReserved = entries[i].peFlags; + } + + /* When WINEDDCAPS_ALLOW256 isn't set we need to override entry 0 with black and 255 with white */ + if (!(palette->flags & WINED3D_PALETTE_ALLOW_256)) + { + TRACE("WINED3D_PALETTE_ALLOW_256 not set, overriding palette entry 0 with black and 255 with white.\n"); + palette->colors[0].rgbRed = 0; + palette->colors[0].rgbGreen = 0; + palette->colors[0].rgbBlue = 0; + + palette->colors[255].rgbRed = 255; + palette->colors[255].rgbGreen = 255; + palette->colors[255].rgbBlue = 255; + } + } + + return WINED3D_OK; +} + +static HRESULT wined3d_palette_init(struct wined3d_palette *palette, struct wined3d_device *device, + DWORD flags, unsigned int entry_count, const PALETTEENTRY *entries) +{ + HRESULT hr; + + palette->ref = 1; + palette->device = device; + palette->flags = flags; + palette->size = entry_count; + + if (FAILED(hr = wined3d_palette_set_entries(palette, 0, 0, entry_count, entries))) + { + WARN("Failed to set palette entries, hr %#x.\n", hr); + return hr; + } + + return WINED3D_OK; +} + +HRESULT CDECL wined3d_palette_create(struct wined3d_device *device, DWORD flags, + unsigned int entry_count, const PALETTEENTRY *entries, struct wined3d_palette **palette) +{ + struct wined3d_palette *object; + HRESULT hr; + + TRACE("device %p, flags %#x, entry_count %u, entries %p, palette %p.\n", + device, flags, entry_count, entries, palette); + + if (!(object = heap_alloc_zero(sizeof(*object)))) + return E_OUTOFMEMORY; + + if (FAILED(hr = wined3d_palette_init(object, device, flags, entry_count, entries))) + { + WARN("Failed to initialize palette, hr %#x.\n", hr); + heap_free(object); + return hr; + } + + TRACE("Created palette %p.\n", object); + *palette = object; + + return WINED3D_OK; +} diff --git a/wrappers/directx/d3dwine_wrapper/precomp.h b/wrappers/directx/d3dwine_wrapper/precomp.h new file mode 100644 index 00000000000..4443eecd341 --- /dev/null +++ b/wrappers/directx/d3dwine_wrapper/precomp.h @@ -0,0 +1,44 @@ +/* + * Direct3D wine internal private include file + * + * Copyright 2002-2003 The wine-d3d team + * Copyright 2002-2003 Raphael Junqueira + * Copyright 2002-2003, 2004 Jason Edmeades + * Copyright 2005 Oliver Stieber + * Copyright 2006-2011, 2013 Stefan Dösinger for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __WINE_WINED3D_PRECOMP_H +#define __WINE_WINED3D_PRECOMP_H + +#include +#include + +#ifdef HAVE_FLOAT_H +# include +#endif + +#include + +#define _INC_WINDOWS +#define COM_NO_WINDOWS_H + +#include "wined3d_private.h" + +#include + +#endif /* !__WINE_WINED3D_PRECOMP_H */ diff --git a/wrappers/directx/d3dwine_wrapper/query.c b/wrappers/directx/d3dwine_wrapper/query.c new file mode 100644 index 00000000000..b239ee895f6 --- /dev/null +++ b/wrappers/directx/d3dwine_wrapper/query.c @@ -0,0 +1,1899 @@ +/* + * Copyright 2005 Oliver Stieber + * Copyright 2007-2008 Stefan Dösinger for CodeWeavers + * Copyright 2009-2010 Henri Verbeet for CodeWeavers. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + + +#include "config.h" +#include "wine/port.h" +#include "wined3d_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(d3d); + +static void wined3d_query_buffer_invalidate(struct wined3d_query *query) +{ + /* map[0] != map[1]: exact values do not have any significance. */ + query->map_ptr[0] = 0; + query->map_ptr[1] = ~(UINT64)0; +} + +static BOOL wined3d_query_buffer_is_valid(struct wined3d_query *query) +{ + return query->map_ptr[0] == query->map_ptr[1]; +} + +static void wined3d_query_create_buffer_object(struct wined3d_context_gl *context_gl, struct wined3d_query *query) +{ + const GLuint map_flags = GL_MAP_READ_BIT | GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT; + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + GLuint buffer_object; + + GL_EXTCALL(glGenBuffers(1, &buffer_object)); + GL_EXTCALL(glBindBuffer(GL_QUERY_BUFFER, buffer_object)); + GL_EXTCALL(glBufferStorage(GL_QUERY_BUFFER, sizeof(query->map_ptr[0]) * 2, NULL, map_flags)); + query->map_ptr = GL_EXTCALL(glMapBufferRange(GL_QUERY_BUFFER, 0, sizeof(query->map_ptr[0]) * 2, map_flags)); + GL_EXTCALL(glBindBuffer(GL_QUERY_BUFFER, 0)); + checkGLcall("query buffer object creation"); + + wined3d_query_buffer_invalidate(query); + query->buffer_object = buffer_object; +} + +void wined3d_query_gl_destroy_buffer_object(struct wined3d_context_gl *context_gl, struct wined3d_query *query) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + + GL_EXTCALL(glDeleteBuffers(1, &query->buffer_object)); + checkGLcall("query buffer object destruction"); + + query->buffer_object = 0; + query->map_ptr = NULL; +} + +/* From ARB_occlusion_query: "Querying the state for a given occlusion query + * forces that occlusion query to complete within a finite amount of time." + * In practice, that means drivers flush when retrieving + * GL_QUERY_RESULT_AVAILABLE, which can be undesirable when applications use a + * significant number of queries. Using a persistently mapped query buffer + * object allows us to avoid these implicit flushes. An additional benefit is + * that it allows us to poll the query status from the application-thread + * instead of from the csmt-thread. */ +static BOOL wined3d_query_buffer_queue_result(struct wined3d_context_gl *context_gl, + struct wined3d_query *query, GLuint id) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + GLsync tmp_sync; + + if (!gl_info->supported[ARB_QUERY_BUFFER_OBJECT] || !gl_info->supported[ARB_BUFFER_STORAGE]) + return FALSE; + /* Don't use query buffers without CSMT, mainly for simplicity. */ + if (!context_gl->c.device->cs->thread) + return FALSE; + + if (query->buffer_object) + { + /* If there's still a query result in-flight for the existing buffer + * object (i.e., the query was restarted before we received its + * result), we can't reuse the existing buffer object. */ + if (wined3d_query_buffer_is_valid(query)) + wined3d_query_buffer_invalidate(query); + else + wined3d_query_gl_destroy_buffer_object(context_gl, query); + } + + if (!query->buffer_object) + wined3d_query_create_buffer_object(context_gl, query); + + GL_EXTCALL(glBindBuffer(GL_QUERY_BUFFER, query->buffer_object)); + /* Read the same value twice. We know we have the result if map_ptr[0] == map_ptr[1]. */ + GL_EXTCALL(glGetQueryObjectui64v(id, GL_QUERY_RESULT, (void *)0)); + GL_EXTCALL(glGetQueryObjectui64v(id, GL_QUERY_RESULT, (void *)sizeof(query->map_ptr[0]))); + GL_EXTCALL(glBindBuffer(GL_QUERY_BUFFER, 0)); + checkGLcall("queue query result"); + + /* ARB_buffer_storage requires the client to call FenceSync with + * SYNC_GPU_COMMANDS_COMPLETE after the server does a write. This behavior + * is not enforced by Mesa. + */ + tmp_sync = GL_EXTCALL(glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0)); + GL_EXTCALL(glDeleteSync(tmp_sync)); + checkGLcall("query buffer sync"); + + return TRUE; +} + +static UINT64 get_query_result64(GLuint id, const struct wined3d_gl_info *gl_info) +{ + if (gl_info->supported[ARB_TIMER_QUERY]) + { + GLuint64 result; + GL_EXTCALL(glGetQueryObjectui64v(id, GL_QUERY_RESULT, &result)); + return result; + } + else + { + GLuint result; + GL_EXTCALL(glGetQueryObjectuiv(id, GL_QUERY_RESULT, &result)); + return result; + } +} + +static void wined3d_query_init(struct wined3d_query *query, struct wined3d_device *device, + enum wined3d_query_type type, const void *data, DWORD data_size, + const struct wined3d_query_ops *query_ops, void *parent, const struct wined3d_parent_ops *parent_ops) +{ + query->ref = 1; + query->parent = parent; + query->parent_ops = parent_ops; + query->device = device; + query->state = QUERY_CREATED; + query->type = type; + query->data = data; + query->data_size = data_size; + query->query_ops = query_ops; + list_init(&query->poll_list_entry); +} + +static struct wined3d_event_query *wined3d_event_query_from_query(struct wined3d_query *query) +{ + return CONTAINING_RECORD(query, struct wined3d_event_query, query); +} + +static struct wined3d_occlusion_query *wined3d_occlusion_query_from_query(struct wined3d_query *query) +{ + return CONTAINING_RECORD(query, struct wined3d_occlusion_query, query); +} + +static struct wined3d_timestamp_query *wined3d_timestamp_query_from_query(struct wined3d_query *query) +{ + return CONTAINING_RECORD(query, struct wined3d_timestamp_query, query); +} + +static struct wined3d_so_statistics_query *wined3d_so_statistics_query_from_query(struct wined3d_query *query) +{ + return CONTAINING_RECORD(query, struct wined3d_so_statistics_query, query); +} + +static struct wined3d_pipeline_statistics_query *wined3d_pipeline_statistics_query_from_query( + struct wined3d_query *query) +{ + return CONTAINING_RECORD(query, struct wined3d_pipeline_statistics_query, query); +} + +static BOOL wined3d_fence_supported(const struct wined3d_gl_info *gl_info) +{ + return gl_info->supported[ARB_SYNC] || gl_info->supported[NV_FENCE] || gl_info->supported[APPLE_FENCE]; +} + +enum wined3d_fence_result wined3d_fence_test(const struct wined3d_fence *fence, + struct wined3d_device *device, DWORD flags) +{ + const struct wined3d_gl_info *gl_info; + struct wined3d_context_gl *context_gl; + enum wined3d_fence_result ret; + BOOL fence_result; + + TRACE("fence %p, device %p, flags %#x.\n", fence, device, flags); + + if (!fence->context_gl) + { + TRACE("Fence not issued.\n"); + return WINED3D_FENCE_NOT_STARTED; + } + + if (!(context_gl = wined3d_context_gl_reacquire(fence->context_gl))) + { + if (!fence->context_gl->gl_info->supported[ARB_SYNC]) + { + WARN("Fence tested from wrong thread.\n"); + return WINED3D_FENCE_WRONG_THREAD; + } + context_gl = wined3d_context_gl(context_acquire(device, NULL, 0)); + } + gl_info = context_gl->gl_info; + + if (gl_info->supported[ARB_SYNC]) + { + GLenum gl_ret = GL_EXTCALL(glClientWaitSync(fence->object.sync, + (flags & WINED3DGETDATA_FLUSH) ? GL_SYNC_FLUSH_COMMANDS_BIT : 0, 0)); + checkGLcall("glClientWaitSync"); + + switch (gl_ret) + { + case GL_ALREADY_SIGNALED: + case GL_CONDITION_SATISFIED: + ret = WINED3D_FENCE_OK; + break; + + case GL_TIMEOUT_EXPIRED: + ret = WINED3D_FENCE_WAITING; + break; + + case GL_WAIT_FAILED: + default: + ERR("glClientWaitSync returned %#x.\n", gl_ret); + ret = WINED3D_FENCE_ERROR; + } + } + else if (gl_info->supported[APPLE_FENCE]) + { + fence_result = GL_EXTCALL(glTestFenceAPPLE(fence->object.id)); + checkGLcall("glTestFenceAPPLE"); + if (fence_result) + ret = WINED3D_FENCE_OK; + else + ret = WINED3D_FENCE_WAITING; + } + else if (gl_info->supported[NV_FENCE]) + { + fence_result = GL_EXTCALL(glTestFenceNV(fence->object.id)); + checkGLcall("glTestFenceNV"); + if (fence_result) + ret = WINED3D_FENCE_OK; + else + ret = WINED3D_FENCE_WAITING; + } + else + { + ERR("Fence created despite lack of GL support.\n"); + ret = WINED3D_FENCE_ERROR; + } + + context_release(&context_gl->c); + return ret; +} + +enum wined3d_fence_result wined3d_fence_wait(const struct wined3d_fence *fence, + struct wined3d_device *device) +{ + const struct wined3d_gl_info *gl_info; + struct wined3d_context_gl *context_gl; + enum wined3d_fence_result ret; + + TRACE("fence %p, device %p.\n", fence, device); + + if (!fence->context_gl) + { + TRACE("Fence not issued.\n"); + return WINED3D_FENCE_NOT_STARTED; + } + gl_info = fence->context_gl->gl_info; + + if (!(context_gl = wined3d_context_gl_reacquire(fence->context_gl))) + { + /* A glFinish does not reliably wait for draws in other contexts. The caller has + * to find its own way to cope with the thread switch + */ + if (!gl_info->supported[ARB_SYNC]) + { + WARN("Fence finished from wrong thread.\n"); + return WINED3D_FENCE_WRONG_THREAD; + } + context_gl = wined3d_context_gl(context_acquire(device, NULL, 0)); + } + gl_info = context_gl->gl_info; + + if (gl_info->supported[ARB_SYNC]) + { + /* Timeouts near 0xffffffffffffffff may immediately return GL_TIMEOUT_EXPIRED, + * possibly because macOS internally adds some slop to the timer. To avoid this, + * we use a large number that isn't near the point of overflow (macOS 10.12.5). + */ + GLenum gl_ret = GL_EXTCALL(glClientWaitSync(fence->object.sync, + GL_SYNC_FLUSH_COMMANDS_BIT, ~(GLuint64)0 >> 1)); + checkGLcall("glClientWaitSync"); + + switch (gl_ret) + { + case GL_ALREADY_SIGNALED: + case GL_CONDITION_SATISFIED: + ret = WINED3D_FENCE_OK; + break; + + /* We don't expect a timeout for a ~292 year wait */ + default: + ERR("glClientWaitSync returned %#x.\n", gl_ret); + ret = WINED3D_FENCE_ERROR; + } + } + else if (gl_info->supported[APPLE_FENCE]) + { + GL_EXTCALL(glFinishFenceAPPLE(fence->object.id)); + checkGLcall("glFinishFenceAPPLE"); + ret = WINED3D_FENCE_OK; + } + else if (gl_info->supported[NV_FENCE]) + { + GL_EXTCALL(glFinishFenceNV(fence->object.id)); + checkGLcall("glFinishFenceNV"); + ret = WINED3D_FENCE_OK; + } + else + { + ERR("Fence created without GL support.\n"); + ret = WINED3D_FENCE_ERROR; + } + + context_release(&context_gl->c); + return ret; +} + +void wined3d_fence_issue(struct wined3d_fence *fence, struct wined3d_device *device) +{ + struct wined3d_context_gl *context_gl = NULL; + const struct wined3d_gl_info *gl_info; + + if (fence->context_gl && !(context_gl = wined3d_context_gl_reacquire(fence->context_gl)) + && !fence->context_gl->gl_info->supported[ARB_SYNC]) + wined3d_context_gl_free_fence(fence); + if (!context_gl) + context_gl = wined3d_context_gl(context_acquire(device, NULL, 0)); + gl_info = context_gl->gl_info; + if (!fence->context_gl) + wined3d_context_gl_alloc_fence(context_gl, fence); + + if (gl_info->supported[ARB_SYNC]) + { + if (fence->object.sync) + GL_EXTCALL(glDeleteSync(fence->object.sync)); + checkGLcall("glDeleteSync"); + fence->object.sync = GL_EXTCALL(glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0)); + checkGLcall("glFenceSync"); + } + else if (gl_info->supported[APPLE_FENCE]) + { + GL_EXTCALL(glSetFenceAPPLE(fence->object.id)); + checkGLcall("glSetFenceAPPLE"); + } + else if (gl_info->supported[NV_FENCE]) + { + GL_EXTCALL(glSetFenceNV(fence->object.id, GL_ALL_COMPLETED_NV)); + checkGLcall("glSetFenceNV"); + } + + context_release(&context_gl->c); +} + +static void wined3d_fence_free(struct wined3d_fence *fence) +{ + if (fence->context_gl) + wined3d_context_gl_free_fence(fence); +} + +void wined3d_fence_destroy(struct wined3d_fence *fence) +{ + wined3d_fence_free(fence); + heap_free(fence); +} + +static HRESULT wined3d_fence_init(struct wined3d_fence *fence, const struct wined3d_gl_info *gl_info) +{ + if (!wined3d_fence_supported(gl_info)) + { + WARN("Fences not supported.\n"); + return WINED3DERR_NOTAVAILABLE; + } + + return WINED3D_OK; +} + +HRESULT wined3d_fence_create(struct wined3d_device *device, struct wined3d_fence **fence) +{ + const struct wined3d_gl_info *gl_info = &device->adapter->gl_info; + struct wined3d_fence *object; + HRESULT hr; + + TRACE("device %p, fence %p.\n", device, fence); + + if (!(object = heap_alloc_zero(sizeof(*object)))) + return E_OUTOFMEMORY; + + if (FAILED(hr = wined3d_fence_init(object, gl_info))) + { + heap_free(object); + return hr; + } + + TRACE("Created fence %p.\n", object); + *fence = object; + + return WINED3D_OK; +} + +ULONG CDECL wined3d_query_incref(struct wined3d_query *query) +{ + ULONG refcount = InterlockedIncrement(&query->ref); + + TRACE("%p increasing refcount to %u.\n", query, refcount); + + return refcount; +} + +static void wined3d_query_destroy_object(void *object) +{ + struct wined3d_query *query = object; + + TRACE("query %p.\n", query); + + if (!list_empty(&query->poll_list_entry)) + list_remove(&query->poll_list_entry); +} + +ULONG CDECL wined3d_query_decref(struct wined3d_query *query) +{ + ULONG refcount = InterlockedDecrement(&query->ref); + + TRACE("%p decreasing refcount to %u.\n", query, refcount); + + if (!refcount) + { + struct wined3d_device *device = query->device; + + query->parent_ops->wined3d_object_destroyed(query->parent); + wined3d_cs_destroy_object(device->cs, wined3d_query_destroy_object, query); + device->adapter->adapter_ops->adapter_destroy_query(query); + } + + return refcount; +} + +HRESULT CDECL wined3d_query_get_data(struct wined3d_query *query, + void *data, UINT data_size, DWORD flags) +{ + TRACE("query %p, data %p, data_size %u, flags %#x.\n", + query, data, data_size, flags); + + if (query->state == QUERY_BUILDING) + { + WARN("Query is building, returning S_FALSE.\n"); + return S_FALSE; + } + + if (query->state == QUERY_CREATED) + { + WARN("Query wasn't started yet.\n"); + return WINED3DERR_INVALIDCALL; + } + + if (query->device->cs->thread) + { + if (query->counter_main != query->counter_retrieved + || (query->buffer_object && !wined3d_query_buffer_is_valid(query))) + { + if (flags & WINED3DGETDATA_FLUSH && !query->device->cs->queries_flushed) + wined3d_cs_emit_flush(query->device->cs); + return S_FALSE; + } + if (query->buffer_object) + query->data = query->map_ptr; + } + else if (!query->query_ops->query_poll(query, flags)) + { + return S_FALSE; + } + + if (data) + memcpy(data, query->data, min(data_size, query->data_size)); + + return S_OK; +} + +UINT CDECL wined3d_query_get_data_size(const struct wined3d_query *query) +{ + TRACE("query %p.\n", query); + + return query->data_size; +} + +HRESULT CDECL wined3d_query_issue(struct wined3d_query *query, DWORD flags) +{ + TRACE("query %p, flags %#x.\n", query, flags); + + if (flags & WINED3DISSUE_END) + ++query->counter_main; + + wined3d_cs_emit_query_issue(query->device->cs, query, flags); + + if (flags & WINED3DISSUE_BEGIN) + query->state = QUERY_BUILDING; + else + query->state = QUERY_SIGNALLED; + + return WINED3D_OK; +} + +static BOOL wined3d_occlusion_query_ops_poll(struct wined3d_query *query, DWORD flags) +{ + struct wined3d_occlusion_query *oq = wined3d_occlusion_query_from_query(query); + const struct wined3d_gl_info *gl_info; + struct wined3d_context_gl *context_gl; + GLuint available; + + TRACE("query %p, flags %#x.\n", query, flags); + + if (!(context_gl = wined3d_context_gl_reacquire(oq->context_gl))) + { + FIXME("%p Wrong thread, returning 1.\n", query); + oq->samples = 1; + return TRUE; + } + gl_info = context_gl->gl_info; + + GL_EXTCALL(glGetQueryObjectuiv(oq->id, GL_QUERY_RESULT_AVAILABLE, &available)); + TRACE("Available %#x.\n", available); + + if (available) + { + oq->samples = get_query_result64(oq->id, gl_info); + TRACE("Returning 0x%s samples.\n", wine_dbgstr_longlong(oq->samples)); + } + + checkGLcall("poll occlusion query"); + context_release(&context_gl->c); + + return available; +} + +static BOOL wined3d_event_query_ops_poll(struct wined3d_query *query, DWORD flags) +{ + struct wined3d_event_query *event_query = wined3d_event_query_from_query(query); + enum wined3d_fence_result ret; + + TRACE("query %p, flags %#x.\n", query, flags); + + ret = wined3d_fence_test(&event_query->fence, query->device, flags); + switch (ret) + { + case WINED3D_FENCE_OK: + case WINED3D_FENCE_NOT_STARTED: + return event_query->signalled = TRUE; + + case WINED3D_FENCE_WAITING: + return event_query->signalled = FALSE; + + case WINED3D_FENCE_WRONG_THREAD: + FIXME("(%p) Wrong thread, reporting GPU idle.\n", query); + return event_query->signalled = TRUE; + + case WINED3D_FENCE_ERROR: + ERR("The GL event query failed.\n"); + return event_query->signalled = TRUE; + + default: + ERR("Unexpected wined3d_event_query_test result %#x.\n", ret); + return event_query->signalled = TRUE; + } +} + +void * CDECL wined3d_query_get_parent(const struct wined3d_query *query) +{ + TRACE("query %p.\n", query); + + return query->parent; +} + +enum wined3d_query_type CDECL wined3d_query_get_type(const struct wined3d_query *query) +{ + TRACE("query %p.\n", query); + + return query->type; +} + +static BOOL wined3d_event_query_ops_issue(struct wined3d_query *query, DWORD flags) +{ + TRACE("query %p, flags %#x.\n", query, flags); + + if (flags & WINED3DISSUE_END) + { + struct wined3d_event_query *event_query = wined3d_event_query_from_query(query); + + wined3d_fence_issue(&event_query->fence, query->device); + return TRUE; + } + else if (flags & WINED3DISSUE_BEGIN) + { + /* Started implicitly at query creation. */ + ERR("Event query issued with START flag - what to do?\n"); + } + + return FALSE; +} + +static BOOL wined3d_occlusion_query_ops_issue(struct wined3d_query *query, DWORD flags) +{ + struct wined3d_occlusion_query *oq = wined3d_occlusion_query_from_query(query); + struct wined3d_device *device = query->device; + const struct wined3d_gl_info *gl_info; + struct wined3d_context_gl *context_gl; + BOOL poll = FALSE; + + TRACE("query %p, flags %#x.\n", query, flags); + + /* This is allowed according to MSDN and our tests. Reset the query and + * restart. */ + if (flags & WINED3DISSUE_BEGIN) + { + if (oq->started) + { + if ((context_gl = wined3d_context_gl_reacquire(oq->context_gl))) + { + gl_info = context_gl->gl_info; + GL_EXTCALL(glEndQuery(GL_SAMPLES_PASSED)); + checkGLcall("glEndQuery()"); + } + else + { + FIXME("Wrong thread, can't restart query.\n"); + wined3d_context_gl_free_occlusion_query(oq); + context_gl = wined3d_context_gl(context_acquire(device, NULL, 0)); + wined3d_context_gl_alloc_occlusion_query(context_gl, oq); + } + } + else + { + if (oq->context_gl) + wined3d_context_gl_free_occlusion_query(oq); + context_gl = wined3d_context_gl(context_acquire(device, NULL, 0)); + wined3d_context_gl_alloc_occlusion_query(context_gl, oq); + } + gl_info = context_gl->gl_info; + + GL_EXTCALL(glBeginQuery(GL_SAMPLES_PASSED, oq->id)); + checkGLcall("glBeginQuery()"); + + context_release(&context_gl->c); + oq->started = TRUE; + } + if (flags & WINED3DISSUE_END) + { + /* MSDN says END on a non-building occlusion query returns an error, + * but our tests show that it returns OK. But OpenGL doesn't like it, + * so avoid generating an error. */ + if (oq->started) + { + if ((context_gl = wined3d_context_gl_reacquire(oq->context_gl))) + { + gl_info = context_gl->gl_info; + GL_EXTCALL(glEndQuery(GL_SAMPLES_PASSED)); + checkGLcall("glEndQuery()"); + wined3d_query_buffer_queue_result(context_gl, query, oq->id); + + context_release(&context_gl->c); + poll = TRUE; + } + else + { + FIXME("Wrong thread, can't end query.\n"); + } + } + oq->started = FALSE; + } + + return poll; +} + +static BOOL wined3d_timestamp_query_ops_poll(struct wined3d_query *query, DWORD flags) +{ + struct wined3d_timestamp_query *tq = wined3d_timestamp_query_from_query(query); + const struct wined3d_gl_info *gl_info; + struct wined3d_context_gl *context_gl; + GLuint64 timestamp; + GLuint available; + + TRACE("query %p, flags %#x.\n", query, flags); + + if (!(context_gl = wined3d_context_gl_reacquire(tq->context_gl))) + { + FIXME("%p Wrong thread, returning 1.\n", query); + tq->timestamp = 1; + return TRUE; + } + gl_info = context_gl->gl_info; + + GL_EXTCALL(glGetQueryObjectuiv(tq->id, GL_QUERY_RESULT_AVAILABLE, &available)); + checkGLcall("glGetQueryObjectuiv(GL_QUERY_RESULT_AVAILABLE)"); + TRACE("available %#x.\n", available); + + if (available) + { + GL_EXTCALL(glGetQueryObjectui64v(tq->id, GL_QUERY_RESULT, ×tamp)); + checkGLcall("glGetQueryObjectui64v(GL_QUERY_RESULT)"); + TRACE("Returning timestamp %s.\n", wine_dbgstr_longlong(timestamp)); + tq->timestamp = timestamp; + } + + context_release(&context_gl->c); + + return available; +} + +static BOOL wined3d_timestamp_query_ops_issue(struct wined3d_query *query, DWORD flags) +{ + struct wined3d_timestamp_query *tq = wined3d_timestamp_query_from_query(query); + const struct wined3d_gl_info *gl_info; + struct wined3d_context_gl *context_gl; + + TRACE("query %p, flags %#x.\n", query, flags); + + if (flags & WINED3DISSUE_BEGIN) + { + WARN("Ignoring WINED3DISSUE_BEGIN with a TIMESTAMP query.\n"); + } + if (flags & WINED3DISSUE_END) + { + if (tq->context_gl) + wined3d_context_gl_free_timestamp_query(tq); + context_gl = wined3d_context_gl(context_acquire(query->device, NULL, 0)); + gl_info = context_gl->gl_info; + wined3d_context_gl_alloc_timestamp_query(context_gl, tq); + GL_EXTCALL(glQueryCounter(tq->id, GL_TIMESTAMP)); + checkGLcall("glQueryCounter()"); + context_release(&context_gl->c); + + return TRUE; + } + + return FALSE; +} + +static BOOL wined3d_timestamp_disjoint_query_ops_poll(struct wined3d_query *query, DWORD flags) +{ + TRACE("query %p, flags %#x.\n", query, flags); + + return TRUE; +} + +static BOOL wined3d_timestamp_disjoint_query_ops_issue(struct wined3d_query *query, DWORD flags) +{ + TRACE("query %p, flags %#x.\n", query, flags); + + return FALSE; +} + +static BOOL wined3d_so_statistics_query_ops_poll(struct wined3d_query *query, DWORD flags) +{ + struct wined3d_so_statistics_query *pq = wined3d_so_statistics_query_from_query(query); + GLuint written_available, generated_available; + const struct wined3d_gl_info *gl_info; + struct wined3d_context_gl *context_gl; + + TRACE("query %p, flags %#x.\n", query, flags); + + if (!(context_gl = wined3d_context_gl_reacquire(pq->context_gl))) + { + FIXME("%p Wrong thread, returning 0 primitives.\n", query); + memset(&pq->statistics, 0, sizeof(pq->statistics)); + return TRUE; + } + gl_info = context_gl->gl_info; + + GL_EXTCALL(glGetQueryObjectuiv(pq->u.query.written, + GL_QUERY_RESULT_AVAILABLE, &written_available)); + GL_EXTCALL(glGetQueryObjectuiv(pq->u.query.generated, + GL_QUERY_RESULT_AVAILABLE, &generated_available)); + TRACE("Available %#x, %#x.\n", written_available, generated_available); + + if (written_available && generated_available) + { + pq->statistics.primitives_written = get_query_result64(pq->u.query.written, gl_info); + pq->statistics.primitives_generated = get_query_result64(pq->u.query.generated, gl_info); + TRACE("Returning %s, %s primitives.\n", + wine_dbgstr_longlong(pq->statistics.primitives_written), + wine_dbgstr_longlong(pq->statistics.primitives_generated)); + } + + checkGLcall("poll SO statistics query"); + context_release(&context_gl->c); + + return written_available && generated_available; +} + +static void wined3d_so_statistics_query_end(struct wined3d_so_statistics_query *query, + struct wined3d_context_gl *context_gl) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + + if (gl_info->supported[ARB_TRANSFORM_FEEDBACK3]) + { + GL_EXTCALL(glEndQueryIndexed(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, query->stream_idx)); + GL_EXTCALL(glEndQueryIndexed(GL_PRIMITIVES_GENERATED, query->stream_idx)); + } + else + { + GL_EXTCALL(glEndQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN)); + GL_EXTCALL(glEndQuery(GL_PRIMITIVES_GENERATED)); + } + checkGLcall("end query"); +} + +static BOOL wined3d_so_statistics_query_ops_issue(struct wined3d_query *query, DWORD flags) +{ + struct wined3d_so_statistics_query *pq = wined3d_so_statistics_query_from_query(query); + struct wined3d_device *device = query->device; + const struct wined3d_gl_info *gl_info; + struct wined3d_context_gl *context_gl; + BOOL poll = FALSE; + + TRACE("query %p, flags %#x.\n", query, flags); + + if (flags & WINED3DISSUE_BEGIN) + { + if (pq->started) + { + if ((context_gl = wined3d_context_gl_reacquire(pq->context_gl))) + { + wined3d_so_statistics_query_end(pq, context_gl); + } + else + { + FIXME("Wrong thread, can't restart query.\n"); + wined3d_context_gl_free_so_statistics_query(pq); + context_gl = wined3d_context_gl(context_acquire(device, NULL, 0)); + wined3d_context_gl_alloc_so_statistics_query(context_gl, pq); + } + } + else + { + if (pq->context_gl) + wined3d_context_gl_free_so_statistics_query(pq); + context_gl = wined3d_context_gl(context_acquire(device, NULL, 0)); + wined3d_context_gl_alloc_so_statistics_query(context_gl, pq); + } + gl_info = context_gl->gl_info; + + if (gl_info->supported[ARB_TRANSFORM_FEEDBACK3]) + { + GL_EXTCALL(glBeginQueryIndexed(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, + pq->stream_idx, pq->u.query.written)); + GL_EXTCALL(glBeginQueryIndexed(GL_PRIMITIVES_GENERATED, + pq->stream_idx, pq->u.query.generated)); + } + else + { + GL_EXTCALL(glBeginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, + pq->u.query.written)); + GL_EXTCALL(glBeginQuery(GL_PRIMITIVES_GENERATED, + pq->u.query.generated)); + } + checkGLcall("begin query"); + + context_release(&context_gl->c); + pq->started = TRUE; + } + if (flags & WINED3DISSUE_END) + { + if (pq->started) + { + if ((context_gl = wined3d_context_gl_reacquire(pq->context_gl))) + { + wined3d_so_statistics_query_end(pq, context_gl); + + context_release(&context_gl->c); + poll = TRUE; + } + else + { + FIXME("Wrong thread, can't end query.\n"); + } + } + pq->started = FALSE; + } + + return poll; +} + +static BOOL wined3d_pipeline_query_ops_poll(struct wined3d_query *query, DWORD flags) +{ + struct wined3d_pipeline_statistics_query *pq = wined3d_pipeline_statistics_query_from_query(query); + const struct wined3d_gl_info *gl_info; + struct wined3d_context_gl *context_gl; + GLuint available; + int i; + + TRACE("query %p, flags %#x.\n", query, flags); + + if (!(context_gl = wined3d_context_gl_reacquire(pq->context_gl))) + { + FIXME("%p Wrong thread.\n", query); + memset(&pq->statistics, 0, sizeof(pq->statistics)); + return TRUE; + } + gl_info = context_gl->gl_info; + + for (i = 0; i < ARRAY_SIZE(pq->u.id); ++i) + { + GL_EXTCALL(glGetQueryObjectuiv(pq->u.id[i], GL_QUERY_RESULT_AVAILABLE, &available)); + if (!available) + break; + } + + if (available) + { + pq->statistics.vertices_submitted = get_query_result64(pq->u.query.vertices, gl_info); + pq->statistics.primitives_submitted = get_query_result64(pq->u.query.primitives, gl_info); + pq->statistics.vs_invocations = get_query_result64(pq->u.query.vertex_shader, gl_info); + pq->statistics.hs_invocations = get_query_result64(pq->u.query.tess_control_shader, gl_info); + pq->statistics.ds_invocations = get_query_result64(pq->u.query.tess_eval_shader, gl_info); + pq->statistics.gs_invocations = get_query_result64(pq->u.query.geometry_shader, gl_info); + pq->statistics.gs_primitives = get_query_result64(pq->u.query.geometry_primitives, gl_info); + pq->statistics.ps_invocations = get_query_result64(pq->u.query.fragment_shader, gl_info); + pq->statistics.cs_invocations = get_query_result64(pq->u.query.compute_shader, gl_info); + pq->statistics.clipping_input_primitives = get_query_result64(pq->u.query.clipping_input, gl_info); + pq->statistics.clipping_output_primitives = get_query_result64(pq->u.query.clipping_output, gl_info); + } + + checkGLcall("poll pipeline statistics query"); + context_release(&context_gl->c); + return available; +} + +static void wined3d_pipeline_statistics_query_end(struct wined3d_pipeline_statistics_query *query, + struct wined3d_context_gl *context_gl) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + + GL_EXTCALL(glEndQuery(GL_VERTICES_SUBMITTED_ARB)); + GL_EXTCALL(glEndQuery(GL_PRIMITIVES_SUBMITTED_ARB)); + GL_EXTCALL(glEndQuery(GL_VERTEX_SHADER_INVOCATIONS_ARB)); + GL_EXTCALL(glEndQuery(GL_TESS_CONTROL_SHADER_PATCHES_ARB)); + GL_EXTCALL(glEndQuery(GL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB)); + GL_EXTCALL(glEndQuery(GL_GEOMETRY_SHADER_INVOCATIONS)); + GL_EXTCALL(glEndQuery(GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB)); + GL_EXTCALL(glEndQuery(GL_FRAGMENT_SHADER_INVOCATIONS_ARB)); + GL_EXTCALL(glEndQuery(GL_COMPUTE_SHADER_INVOCATIONS_ARB)); + GL_EXTCALL(glEndQuery(GL_CLIPPING_INPUT_PRIMITIVES_ARB)); + GL_EXTCALL(glEndQuery(GL_CLIPPING_OUTPUT_PRIMITIVES_ARB)); + checkGLcall("end query"); +} + +static BOOL wined3d_pipeline_query_ops_issue(struct wined3d_query *query, DWORD flags) +{ + struct wined3d_pipeline_statistics_query *pq = wined3d_pipeline_statistics_query_from_query(query); + struct wined3d_device *device = query->device; + const struct wined3d_gl_info *gl_info; + struct wined3d_context_gl *context_gl; + BOOL poll = FALSE; + + TRACE("query %p, flags %#x.\n", query, flags); + + if (flags & WINED3DISSUE_BEGIN) + { + if (pq->started) + { + if ((context_gl = wined3d_context_gl_reacquire(pq->context_gl))) + { + wined3d_pipeline_statistics_query_end(pq, context_gl); + } + else + { + FIXME("Wrong thread, can't restart query.\n"); + wined3d_context_gl_free_pipeline_statistics_query(pq); + context_gl = wined3d_context_gl(context_acquire(device, NULL, 0)); + wined3d_context_gl_alloc_pipeline_statistics_query(context_gl, pq); + } + } + else + { + if (pq->context_gl) + wined3d_context_gl_free_pipeline_statistics_query(pq); + context_gl = wined3d_context_gl(context_acquire(device, NULL, 0)); + wined3d_context_gl_alloc_pipeline_statistics_query(context_gl, pq); + } + gl_info = context_gl->gl_info; + + GL_EXTCALL(glBeginQuery(GL_VERTICES_SUBMITTED_ARB, pq->u.query.vertices)); + GL_EXTCALL(glBeginQuery(GL_PRIMITIVES_SUBMITTED_ARB, pq->u.query.primitives)); + GL_EXTCALL(glBeginQuery(GL_VERTEX_SHADER_INVOCATIONS_ARB, pq->u.query.vertex_shader)); + GL_EXTCALL(glBeginQuery(GL_TESS_CONTROL_SHADER_PATCHES_ARB, pq->u.query.tess_control_shader)); + GL_EXTCALL(glBeginQuery(GL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB, pq->u.query.tess_eval_shader)); + GL_EXTCALL(glBeginQuery(GL_GEOMETRY_SHADER_INVOCATIONS, pq->u.query.geometry_shader)); + GL_EXTCALL(glBeginQuery(GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB, pq->u.query.geometry_primitives)); + GL_EXTCALL(glBeginQuery(GL_FRAGMENT_SHADER_INVOCATIONS_ARB, pq->u.query.fragment_shader)); + GL_EXTCALL(glBeginQuery(GL_COMPUTE_SHADER_INVOCATIONS_ARB, pq->u.query.compute_shader)); + GL_EXTCALL(glBeginQuery(GL_CLIPPING_INPUT_PRIMITIVES_ARB, pq->u.query.clipping_input)); + GL_EXTCALL(glBeginQuery(GL_CLIPPING_OUTPUT_PRIMITIVES_ARB, pq->u.query.clipping_output)); + checkGLcall("begin query"); + + context_release(&context_gl->c); + pq->started = TRUE; + } + if (flags & WINED3DISSUE_END) + { + if (pq->started) + { + if ((context_gl = wined3d_context_gl_reacquire(pq->context_gl))) + { + wined3d_pipeline_statistics_query_end(pq, context_gl); + context_release(&context_gl->c); + poll = TRUE; + } + else + { + FIXME("Wrong thread, can't end query.\n"); + } + } + pq->started = FALSE; + } + + return poll; +} + +static void wined3d_event_query_ops_destroy(struct wined3d_query *query) +{ + struct wined3d_event_query *event_query = wined3d_event_query_from_query(query); + + wined3d_fence_free(&event_query->fence); + heap_free(event_query); +} + +static const struct wined3d_query_ops event_query_ops = +{ + wined3d_event_query_ops_poll, + wined3d_event_query_ops_issue, + wined3d_event_query_ops_destroy, +}; + +static HRESULT wined3d_event_query_create(struct wined3d_device *device, + enum wined3d_query_type type, void *parent, const struct wined3d_parent_ops *parent_ops, + struct wined3d_query **query) +{ + const struct wined3d_gl_info *gl_info = &device->adapter->gl_info; + struct wined3d_event_query *object; + HRESULT hr; + + TRACE("device %p, type %#x, parent %p, parent_ops %p, query %p.\n", + device, type, parent, parent_ops, query); + + if (!(object = heap_alloc_zero(sizeof(*object)))) + return E_OUTOFMEMORY; + + if (FAILED(hr = wined3d_fence_init(&object->fence, gl_info))) + { + WARN("Event queries not supported.\n"); + heap_free(object); + return hr; + } + + wined3d_query_init(&object->query, device, type, &object->signalled, + sizeof(object->signalled), &event_query_ops, parent, parent_ops); + + TRACE("Created query %p.\n", object); + *query = &object->query; + + return WINED3D_OK; +} + +static void wined3d_occlusion_query_ops_destroy(struct wined3d_query *query) +{ + struct wined3d_occlusion_query *oq = wined3d_occlusion_query_from_query(query); + + if (oq->context_gl) + wined3d_context_gl_free_occlusion_query(oq); + heap_free(oq); +} + +static const struct wined3d_query_ops occlusion_query_ops = +{ + wined3d_occlusion_query_ops_poll, + wined3d_occlusion_query_ops_issue, + wined3d_occlusion_query_ops_destroy, +}; + +static HRESULT wined3d_occlusion_query_create(struct wined3d_device *device, + enum wined3d_query_type type, void *parent, const struct wined3d_parent_ops *parent_ops, + struct wined3d_query **query) +{ + const struct wined3d_gl_info *gl_info = &device->adapter->gl_info; + struct wined3d_occlusion_query *object; + + TRACE("device %p, type %#x, parent %p, parent_ops %p, query %p.\n", + device, type, parent, parent_ops, query); + + if (!gl_info->supported[ARB_OCCLUSION_QUERY]) + { + WARN("Unsupported in local OpenGL implementation: ARB_OCCLUSION_QUERY.\n"); + return WINED3DERR_NOTAVAILABLE; + } + + if (!(object = heap_alloc_zero(sizeof(*object)))) + return E_OUTOFMEMORY; + + wined3d_query_init(&object->query, device, type, &object->samples, + sizeof(object->samples), &occlusion_query_ops, parent, parent_ops); + + TRACE("Created query %p.\n", object); + *query = &object->query; + + return WINED3D_OK; +} + +static void wined3d_timestamp_query_ops_destroy(struct wined3d_query *query) +{ + struct wined3d_timestamp_query *tq = wined3d_timestamp_query_from_query(query); + + if (tq->context_gl) + wined3d_context_gl_free_timestamp_query(tq); + heap_free(tq); +} + +static const struct wined3d_query_ops timestamp_query_ops = +{ + wined3d_timestamp_query_ops_poll, + wined3d_timestamp_query_ops_issue, + wined3d_timestamp_query_ops_destroy, +}; + +static HRESULT wined3d_timestamp_query_create(struct wined3d_device *device, + enum wined3d_query_type type, void *parent, const struct wined3d_parent_ops *parent_ops, + struct wined3d_query **query) +{ + const struct wined3d_gl_info *gl_info = &device->adapter->gl_info; + struct wined3d_timestamp_query *object; + + TRACE("device %p, type %#x, parent %p, parent_ops %p, query %p.\n", + device, type, parent, parent_ops, query); + + if (!gl_info->supported[ARB_TIMER_QUERY]) + { + WARN("Unsupported in local OpenGL implementation: ARB_TIMER_QUERY.\n"); + return WINED3DERR_NOTAVAILABLE; + } + + if (!(object = heap_alloc_zero(sizeof(*object)))) + return E_OUTOFMEMORY; + + wined3d_query_init(&object->query, device, type, &object->timestamp, + sizeof(object->timestamp), ×tamp_query_ops, parent, parent_ops); + + TRACE("Created query %p.\n", object); + *query = &object->query; + + return WINED3D_OK; +} + +static void wined3d_timestamp_disjoint_query_ops_destroy(struct wined3d_query *query) +{ + heap_free(query); +} + +static const struct wined3d_query_ops timestamp_disjoint_query_ops = +{ + wined3d_timestamp_disjoint_query_ops_poll, + wined3d_timestamp_disjoint_query_ops_issue, + wined3d_timestamp_disjoint_query_ops_destroy, +}; + +static HRESULT wined3d_timestamp_disjoint_query_create(struct wined3d_device *device, + enum wined3d_query_type type, void *parent, const struct wined3d_parent_ops *parent_ops, + struct wined3d_query **query) +{ + const struct wined3d_gl_info *gl_info = &device->adapter->gl_info; + struct wined3d_query *object; + + TRACE("device %p, type %#x, parent %p, parent_ops %p, query %p.\n", + device, type, parent, parent_ops, query); + + if (!gl_info->supported[ARB_TIMER_QUERY]) + { + WARN("Unsupported in local OpenGL implementation: ARB_TIMER_QUERY.\n"); + return WINED3DERR_NOTAVAILABLE; + } + + if (!(object = heap_alloc_zero(sizeof(*object)))) + return E_OUTOFMEMORY; + + if (type == WINED3D_QUERY_TYPE_TIMESTAMP_DISJOINT) + { + static const struct wined3d_query_data_timestamp_disjoint disjoint_data = {1000 * 1000 * 1000, FALSE}; + + wined3d_query_init(object, device, type, &disjoint_data, + sizeof(disjoint_data), ×tamp_disjoint_query_ops, parent, parent_ops); + } + else + { + static const UINT64 freq = 1000 * 1000 * 1000; + + wined3d_query_init(object, device, type, &freq, + sizeof(freq), ×tamp_disjoint_query_ops, parent, parent_ops); + } + + TRACE("Created query %p.\n", object); + *query = object; + + return WINED3D_OK; +} + +static void wined3d_so_statistics_query_ops_destroy(struct wined3d_query *query) +{ + struct wined3d_so_statistics_query *pq = wined3d_so_statistics_query_from_query(query); + + if (pq->context_gl) + wined3d_context_gl_free_so_statistics_query(pq); + heap_free(pq); +} + +static const struct wined3d_query_ops so_statistics_query_ops = +{ + wined3d_so_statistics_query_ops_poll, + wined3d_so_statistics_query_ops_issue, + wined3d_so_statistics_query_ops_destroy, +}; + +static HRESULT wined3d_so_statistics_query_create(struct wined3d_device *device, + enum wined3d_query_type type, void *parent, const struct wined3d_parent_ops *parent_ops, + struct wined3d_query **query) +{ + const struct wined3d_gl_info *gl_info = &device->adapter->gl_info; + struct wined3d_so_statistics_query *object; + unsigned int stream_idx; + + if (WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM0 <= type && type <= WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM3) + stream_idx = type - WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM0; + else if (type == WINED3D_QUERY_TYPE_SO_STATISTICS) + stream_idx = 0; + else + return WINED3DERR_NOTAVAILABLE; + + TRACE("device %p, type %#x, parent %p, parent_ops %p, query %p.\n", + device, type, parent, parent_ops, query); + + if (!gl_info->supported[WINED3D_GL_PRIMITIVE_QUERY]) + { + WARN("OpenGL implementation does not support primitive queries.\n"); + return WINED3DERR_NOTAVAILABLE; + } + if (stream_idx && !gl_info->supported[ARB_TRANSFORM_FEEDBACK3]) + { + WARN("OpenGL implementation does not support indexed queries.\n"); + return WINED3DERR_NOTAVAILABLE; + } + + if (!(object = heap_alloc_zero(sizeof(*object)))) + return E_OUTOFMEMORY; + + wined3d_query_init(&object->query, device, type, &object->statistics, + sizeof(object->statistics), &so_statistics_query_ops, parent, parent_ops); + object->stream_idx = stream_idx; + + TRACE("Created query %p.\n", object); + *query = &object->query; + + return WINED3D_OK; +} + +static void wined3d_pipeline_query_ops_destroy(struct wined3d_query *query) +{ + struct wined3d_pipeline_statistics_query *pq = wined3d_pipeline_statistics_query_from_query(query); + if (pq->context_gl) + wined3d_context_gl_free_pipeline_statistics_query(pq); + heap_free(pq); +} + +static const struct wined3d_query_ops pipeline_query_ops = +{ + wined3d_pipeline_query_ops_poll, + wined3d_pipeline_query_ops_issue, + wined3d_pipeline_query_ops_destroy, +}; + +static HRESULT wined3d_pipeline_query_create(struct wined3d_device *device, + enum wined3d_query_type type, void *parent, const struct wined3d_parent_ops *parent_ops, + struct wined3d_query **query) +{ + const struct wined3d_gl_info *gl_info = &device->adapter->gl_info; + struct wined3d_pipeline_statistics_query *object; + + TRACE("device %p, type %#x, parent %p, parent_ops %p, query %p.\n", + device, type, parent, parent_ops, query); + + if (!gl_info->supported[ARB_PIPELINE_STATISTICS_QUERY]) + { + WARN("OpenGL implementation does not support pipeline statistics queries.\n"); + return WINED3DERR_NOTAVAILABLE; + } + + if (!(object = heap_alloc_zero(sizeof(*object)))) + return E_OUTOFMEMORY; + + wined3d_query_init(&object->query, device, type, &object->statistics, + sizeof(object->statistics), &pipeline_query_ops, parent, parent_ops); + + TRACE("Created query %p.\n", object); + *query = &object->query; + + return WINED3D_OK; +} + +HRESULT wined3d_query_gl_create(struct wined3d_device *device, enum wined3d_query_type type, + void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_query **query) +{ + TRACE("device %p, type %#x, parent %p, parent_ops %p, query %p.\n", + device, type, parent, parent_ops, query); + + switch (type) + { + case WINED3D_QUERY_TYPE_EVENT: + return wined3d_event_query_create(device, type, parent, parent_ops, query); + + case WINED3D_QUERY_TYPE_OCCLUSION: + return wined3d_occlusion_query_create(device, type, parent, parent_ops, query); + + case WINED3D_QUERY_TYPE_TIMESTAMP: + return wined3d_timestamp_query_create(device, type, parent, parent_ops, query); + + case WINED3D_QUERY_TYPE_TIMESTAMP_DISJOINT: + case WINED3D_QUERY_TYPE_TIMESTAMP_FREQ: + return wined3d_timestamp_disjoint_query_create(device, type, parent, parent_ops, query); + + case WINED3D_QUERY_TYPE_SO_STATISTICS: + case WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM0: + case WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM1: + case WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM2: + case WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM3: + return wined3d_so_statistics_query_create(device, type, parent, parent_ops, query); + + case WINED3D_QUERY_TYPE_PIPELINE_STATISTICS: + return wined3d_pipeline_query_create(device, type, parent, parent_ops, query); + + default: + FIXME("Unhandled query type %#x.\n", type); + return WINED3DERR_NOTAVAILABLE; + } +} + +void wined3d_query_pool_vk_free_query(struct wined3d_query_pool_vk *pool_vk, size_t idx) +{ + wined3d_bitmap_clear(pool_vk->allocated, idx); + + if (list_empty(&pool_vk->entry)) + list_add_tail(pool_vk->free_list, &pool_vk->entry); +} + +bool wined3d_query_pool_vk_allocate_query(struct wined3d_query_pool_vk *pool_vk, size_t *idx) +{ + if ((*idx = wined3d_bitmap_ffz(pool_vk->allocated, WINED3D_QUERY_POOL_SIZE, 0)) > WINED3D_QUERY_POOL_SIZE) + return false; + wined3d_bitmap_set(pool_vk->allocated, *idx); + + return true; +} + +void wined3d_query_pool_vk_cleanup(struct wined3d_query_pool_vk *pool_vk, struct wined3d_context_vk *context_vk) +{ + struct wined3d_device_vk *device_vk = wined3d_device_vk(context_vk->c.device); + const struct wined3d_vk_info *vk_info = context_vk->vk_info; + + VK_CALL(vkDestroyQueryPool(device_vk->vk_device, pool_vk->vk_query_pool, NULL)); + list_remove(&pool_vk->entry); +} + +bool wined3d_query_pool_vk_init(struct wined3d_query_pool_vk *pool_vk, + struct wined3d_context_vk *context_vk, enum wined3d_query_type type, struct list *free_pools) +{ + struct wined3d_device_vk *device_vk = wined3d_device_vk(context_vk->c.device); + const struct wined3d_vk_info *vk_info = context_vk->vk_info; + VkQueryPoolCreateInfo pool_info; + VkResult vr; + + list_init(&pool_vk->entry); + pool_vk->free_list = free_pools; + + pool_info.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO; + pool_info.pNext = NULL; + pool_info.flags = 0; + pool_info.queryCount = WINED3D_QUERY_POOL_SIZE; + + switch (type) + { + case WINED3D_QUERY_TYPE_OCCLUSION: + pool_info.queryType = VK_QUERY_TYPE_OCCLUSION; + pool_info.pipelineStatistics = 0; + break; + + case WINED3D_QUERY_TYPE_TIMESTAMP: + pool_info.queryType = VK_QUERY_TYPE_TIMESTAMP; + pool_info.pipelineStatistics = 0; + break; + + case WINED3D_QUERY_TYPE_PIPELINE_STATISTICS: + pool_info.queryType = VK_QUERY_TYPE_PIPELINE_STATISTICS; + pool_info.pipelineStatistics = VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_VERTICES_BIT + | VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_PRIMITIVES_BIT + | VK_QUERY_PIPELINE_STATISTIC_VERTEX_SHADER_INVOCATIONS_BIT + | VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_INVOCATIONS_BIT + | VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_PRIMITIVES_BIT + | VK_QUERY_PIPELINE_STATISTIC_CLIPPING_INVOCATIONS_BIT + | VK_QUERY_PIPELINE_STATISTIC_CLIPPING_PRIMITIVES_BIT + | VK_QUERY_PIPELINE_STATISTIC_FRAGMENT_SHADER_INVOCATIONS_BIT + | VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_CONTROL_SHADER_PATCHES_BIT + | VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_EVALUATION_SHADER_INVOCATIONS_BIT + | VK_QUERY_PIPELINE_STATISTIC_COMPUTE_SHADER_INVOCATIONS_BIT; + break; + + case WINED3D_QUERY_TYPE_SO_STATISTICS: + case WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM0: + case WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM1: + case WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM2: + case WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM3: + pool_info.queryType = VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT; + pool_info.pipelineStatistics = 0; + break; + + default: + FIXME("Unhandled query type %#x.\n", type); + return false; + } + + if ((vr = VK_CALL(vkCreateQueryPool(device_vk->vk_device, &pool_info, NULL, &pool_vk->vk_query_pool))) < 0) + { + ERR("Failed to create Vulkan query pool, vr %s.\n", wined3d_debug_vkresult(vr)); + return false; + } + + list_add_head(free_pools, &pool_vk->entry); + + return true; +} + +bool wined3d_query_vk_accumulate_data(struct wined3d_query_vk *query_vk, + struct wined3d_context_vk *context_vk, const struct wined3d_query_pool_idx_vk *pool_idx) +{ + struct wined3d_device_vk *device_vk = wined3d_device_vk(context_vk->c.device); + const struct wined3d_query_data_pipeline_statistics *ps_tmp; + const struct wined3d_vk_info *vk_info = context_vk->vk_info; + struct wined3d_query_data_pipeline_statistics *ps_result; + VkResult vr; + union + { + uint64_t occlusion; + uint64_t timestamp; + struct wined3d_query_data_pipeline_statistics pipeline_statistics; + struct wined3d_query_data_so_statistics so_statistics; + } tmp, *result; + + if ((vr = VK_CALL(vkGetQueryPoolResults(device_vk->vk_device, pool_idx->pool_vk->vk_query_pool, + pool_idx->idx, 1, sizeof(tmp), &tmp, sizeof(tmp), VK_QUERY_RESULT_64_BIT))) < 0) + { + ERR("Failed to get query results, vr %s.\n", wined3d_debug_vkresult(vr)); + return false; + } + + if (vr == VK_NOT_READY) + return false; + + result = (void *)query_vk->q.data; + switch (query_vk->q.type) + { + case WINED3D_QUERY_TYPE_OCCLUSION: + result->occlusion += tmp.occlusion; + break; + + case WINED3D_QUERY_TYPE_TIMESTAMP: + result->timestamp = tmp.timestamp; + break; + + case WINED3D_QUERY_TYPE_PIPELINE_STATISTICS: + ps_result = &result->pipeline_statistics; + ps_tmp = &tmp.pipeline_statistics; + ps_result->vertices_submitted += ps_tmp->vertices_submitted; + ps_result->primitives_submitted += ps_tmp->primitives_submitted; + ps_result->vs_invocations += ps_tmp->vs_invocations; + ps_result->gs_invocations += ps_tmp->gs_invocations; + ps_result->gs_primitives += ps_tmp->gs_primitives; + ps_result->clipping_input_primitives += ps_tmp->clipping_input_primitives; + ps_result->clipping_output_primitives += ps_tmp->clipping_output_primitives; + ps_result->ps_invocations += ps_tmp->ps_invocations; + ps_result->hs_invocations += ps_tmp->hs_invocations; + ps_result->ds_invocations += ps_tmp->ds_invocations; + ps_result->cs_invocations += ps_tmp->cs_invocations; + break; + + case WINED3D_QUERY_TYPE_SO_STATISTICS: + case WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM0: + case WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM1: + case WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM2: + case WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM3: + result->so_statistics.primitives_written += tmp.so_statistics.primitives_written; + result->so_statistics.primitives_generated += tmp.so_statistics.primitives_generated; + break; + + default: + FIXME("Unhandled query type %#x.\n", query_vk->q.type); + return false; + } + + return true; +} + +static void wined3d_query_vk_begin(struct wined3d_query_vk *query_vk, + struct wined3d_context_vk *context_vk, VkCommandBuffer vk_command_buffer) +{ + const struct wined3d_vk_info *vk_info = context_vk->vk_info; + struct wined3d_query_pool_vk *pool_vk; + size_t idx; + + if (!query_vk->pool_idx.pool_vk + && !wined3d_context_vk_allocate_query(context_vk, query_vk->q.type, &query_vk->pool_idx)) + { + ERR("Failed to allocate new query.\n"); + return; + } + pool_vk = query_vk->pool_idx.pool_vk; + idx = query_vk->pool_idx.idx; + + VK_CALL(vkCmdResetQueryPool(vk_command_buffer, pool_vk->vk_query_pool, idx, 1)); + if (query_vk->q.type >= WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM1 + && query_vk->q.type <= WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM3) + VK_CALL(vkCmdBeginQueryIndexedEXT(vk_command_buffer, pool_vk->vk_query_pool, idx, + query_vk->control_flags, query_vk->q.type - WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM0)); + else + VK_CALL(vkCmdBeginQuery(vk_command_buffer, pool_vk->vk_query_pool, idx, query_vk->control_flags)); + wined3d_context_vk_reference_query(context_vk, query_vk); +} + +static void wined3d_query_vk_end(struct wined3d_query_vk *query_vk, + struct wined3d_context_vk *context_vk, VkCommandBuffer vk_command_buffer) +{ + const struct wined3d_vk_info *vk_info = context_vk->vk_info; + struct wined3d_query_pool_vk *pool_vk; + size_t idx; + + pool_vk = query_vk->pool_idx.pool_vk; + idx = query_vk->pool_idx.idx; + + if (query_vk->q.type >= WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM1 + && query_vk->q.type <= WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM3) + VK_CALL(vkCmdEndQueryIndexedEXT(vk_command_buffer, pool_vk->vk_query_pool, + idx, query_vk->q.type - WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM0)); + else + VK_CALL(vkCmdEndQuery(vk_command_buffer, pool_vk->vk_query_pool, idx)); +} + +void wined3d_query_vk_resume(struct wined3d_query_vk *query_vk, struct wined3d_context_vk *context_vk) +{ + VkCommandBuffer vk_command_buffer = context_vk->current_command_buffer.vk_command_buffer; + + wined3d_query_vk_begin(query_vk, context_vk, vk_command_buffer); +} + +void wined3d_query_vk_suspend(struct wined3d_query_vk *query_vk, struct wined3d_context_vk *context_vk) +{ + VkCommandBuffer vk_command_buffer = context_vk->current_command_buffer.vk_command_buffer; + + wined3d_query_vk_end(query_vk, context_vk, vk_command_buffer); + wined3d_context_vk_add_pending_query(context_vk, query_vk); + query_vk->pool_idx.pool_vk = NULL; +} + +static BOOL wined3d_query_vk_poll(struct wined3d_query *query, uint32_t flags) +{ + struct wined3d_query_vk *query_vk = wined3d_query_vk(query); + struct wined3d_context_vk *context_vk; + + context_vk = wined3d_context_vk(context_acquire(query->device, NULL, 0)); + + if (flags & WINED3DGETDATA_FLUSH) + wined3d_context_vk_submit_command_buffer(context_vk, 0, NULL, NULL, 0, NULL); + if (query_vk->command_buffer_id == context_vk->current_command_buffer.id) + goto unavailable; + + if (query_vk->command_buffer_id > context_vk->completed_command_buffer_id) + wined3d_context_vk_poll_command_buffers(context_vk); + if (query_vk->command_buffer_id > context_vk->completed_command_buffer_id) + goto unavailable; + + if (query_vk->pending_count) + wined3d_context_vk_accumulate_pending_queries(context_vk); + if (query_vk->pending_count) + goto unavailable; + + /* If the query was suspended, and then ended before it was resumed, + * there's no data to accumulate here. */ + if (query_vk->pool_idx.pool_vk && !wined3d_query_vk_accumulate_data(query_vk, context_vk, &query_vk->pool_idx)) + goto unavailable; + + context_release(&context_vk->c); + + return TRUE; + +unavailable: + context_release(&context_vk->c); + return FALSE; +} + +static BOOL wined3d_query_vk_issue(struct wined3d_query *query, uint32_t flags) +{ + struct wined3d_device_vk *device_vk = wined3d_device_vk(query->device); + struct wined3d_query_vk *query_vk = wined3d_query_vk(query); + struct wined3d_context_vk *context_vk; + VkCommandBuffer vk_command_buffer; + bool poll = false; + + TRACE("query %p, flags %#x.\n", query, flags); + + if (flags & WINED3DISSUE_BEGIN) + { + context_vk = wined3d_context_vk(context_acquire(&device_vk->d, NULL, 0)); + + wined3d_context_vk_end_current_render_pass(context_vk); + list_remove(&query_vk->entry); + if (query_vk->pending_count) + wined3d_context_vk_remove_pending_queries(context_vk, query_vk); + memset((void *)query->data, 0, query->data_size); + vk_command_buffer = wined3d_context_vk_get_command_buffer(context_vk); + wined3d_query_vk_begin(query_vk, context_vk, vk_command_buffer); + list_add_head(&context_vk->active_queries, &query_vk->entry); + query_vk->started = true; + + context_release(&context_vk->c); + } + if (flags & WINED3DISSUE_END && query_vk->started) + { + context_vk = wined3d_context_vk(context_acquire(&device_vk->d, NULL, 0)); + + /* If the query was already ended because the command buffer was + * flushed, we don't need to end it here. */ + if ((vk_command_buffer = context_vk->current_command_buffer.vk_command_buffer)) + wined3d_query_vk_end(query_vk, context_vk, vk_command_buffer); + list_remove(&query_vk->entry); + query_vk->started = false; + poll = true; + + context_release(&context_vk->c); + } + + return poll; +} + +static void wined3d_query_vk_destroy(struct wined3d_query *query) +{ + struct wined3d_query_vk *query_vk = wined3d_query_vk(query); + struct wined3d_context_vk *context_vk; + + list_remove(&query_vk->entry); + if (query_vk->pending_count) + { + context_vk = wined3d_context_vk(context_acquire(query_vk->q.device, NULL, 0)); + wined3d_context_vk_remove_pending_queries(context_vk, query_vk); + context_release(&context_vk->c); + } + if (query_vk->pool_idx.pool_vk) + wined3d_query_pool_vk_free_query(query_vk->pool_idx.pool_vk, query_vk->pool_idx.idx); + heap_free(query_vk); +} + +static const struct wined3d_query_ops wined3d_query_vk_ops = +{ + .query_poll = wined3d_query_vk_poll, + .query_issue = wined3d_query_vk_issue, + .query_destroy = wined3d_query_vk_destroy, +}; + +static BOOL wined3d_query_event_vk_poll(struct wined3d_query *query, uint32_t flags) +{ + struct wined3d_query_vk *query_vk = wined3d_query_vk(query); + struct wined3d_context_vk *context_vk; + BOOL *signalled; + + context_vk = wined3d_context_vk(context_acquire(query->device, NULL, 0)); + + signalled = (BOOL *)query->data; + if (flags & WINED3DGETDATA_FLUSH) + wined3d_context_vk_submit_command_buffer(context_vk, 0, NULL, NULL, 0, NULL); + if (query_vk->command_buffer_id == context_vk->current_command_buffer.id) + { + context_release(&context_vk->c); + return *signalled = FALSE; + } + + if (query_vk->command_buffer_id > context_vk->completed_command_buffer_id) + wined3d_context_vk_poll_command_buffers(context_vk); + *signalled = context_vk->completed_command_buffer_id >= query_vk->command_buffer_id; + + context_release(&context_vk->c); + + return *signalled; +} + +static BOOL wined3d_query_event_vk_issue(struct wined3d_query *query, uint32_t flags) +{ + struct wined3d_device_vk *device_vk = wined3d_device_vk(query->device); + struct wined3d_query_vk *query_vk = wined3d_query_vk(query); + struct wined3d_context_vk *context_vk; + + TRACE("query %p, flags %#x.\n", query, flags); + + if (flags & WINED3DISSUE_END) + { + context_vk = wined3d_context_vk(context_acquire(&device_vk->d, NULL, 0)); + wined3d_context_vk_reference_query(context_vk, query_vk); + /* Because we don't actually submit any commands to the command buffer + * for event queries, the context's current command buffer may still + * be empty, and we should wait on the preceding command buffer + * instead. That's not merely an optimisation; if the command buffer + * referenced by the query is still empty by the time the application + * waits for it, that wait will never complete. */ + if (!context_vk->current_command_buffer.vk_command_buffer) + --query_vk->command_buffer_id; + context_release(&context_vk->c); + + return TRUE; + } + + return FALSE; +} + +static const struct wined3d_query_ops wined3d_query_event_vk_ops = +{ + .query_poll = wined3d_query_event_vk_poll, + .query_issue = wined3d_query_event_vk_issue, + .query_destroy = wined3d_query_vk_destroy, +}; + +static BOOL wined3d_query_timestamp_vk_issue(struct wined3d_query *query, uint32_t flags) +{ + struct wined3d_device_vk *device_vk = wined3d_device_vk(query->device); + struct wined3d_query_vk *query_vk = wined3d_query_vk(query); + const struct wined3d_vk_info *vk_info; + struct wined3d_context_vk *context_vk; + VkCommandBuffer command_buffer; + + TRACE("query %p, flags %#x.\n", query, flags); + + if (flags & WINED3DISSUE_BEGIN) + TRACE("Ignoring WINED3DISSUE_BEGIN.\n"); + + if (flags & WINED3DISSUE_END) + { + context_vk = wined3d_context_vk(context_acquire(&device_vk->d, NULL, 0)); + vk_info = context_vk->vk_info; + + wined3d_context_vk_end_current_render_pass(context_vk); + command_buffer = wined3d_context_vk_get_command_buffer(context_vk); + if (!query_vk->pool_idx.pool_vk) + wined3d_context_vk_allocate_query(context_vk, query_vk->q.type, &query_vk->pool_idx); + VK_CALL(vkCmdResetQueryPool(command_buffer, query_vk->pool_idx.pool_vk->vk_query_pool, + query_vk->pool_idx.idx, 1)); + VK_CALL(vkCmdWriteTimestamp(command_buffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, + query_vk->pool_idx.pool_vk->vk_query_pool, query_vk->pool_idx.idx)); + wined3d_context_vk_reference_query(context_vk, query_vk); + + context_release(&context_vk->c); + + return TRUE; + } + + return FALSE; +} + +static const struct wined3d_query_ops wined3d_query_timestamp_vk_ops = +{ + .query_poll = wined3d_query_vk_poll, + .query_issue = wined3d_query_timestamp_vk_issue, + .query_destroy = wined3d_query_vk_destroy, +}; + +static const struct wined3d_query_ops wined3d_query_timestamp_disjoint_vk_ops = +{ + .query_poll = wined3d_timestamp_disjoint_query_ops_poll, + .query_issue = wined3d_timestamp_disjoint_query_ops_issue, + .query_destroy = wined3d_query_vk_destroy, +}; + +HRESULT wined3d_query_vk_create(struct wined3d_device *device, enum wined3d_query_type type, + void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_query **query) +{ + struct wined3d_query_data_timestamp_disjoint *disjoint_data; + const struct wined3d_query_ops *ops = &wined3d_query_vk_ops; + struct wined3d_query_vk *query_vk; + unsigned int data_size; + void *data; + + TRACE("device %p, type %#x, parent %p, parent_ops %p, query %p.\n", + device, type, parent, parent_ops, query); + + switch (type) + { + case WINED3D_QUERY_TYPE_EVENT: + ops = &wined3d_query_event_vk_ops; + data_size = sizeof(BOOL); + break; + + case WINED3D_QUERY_TYPE_OCCLUSION: + data_size = sizeof(uint64_t); + break; + + case WINED3D_QUERY_TYPE_TIMESTAMP: + if (!wined3d_device_vk(device)->timestamp_bits) + { + WARN("Timestamp queries not supported.\n"); + return WINED3DERR_NOTAVAILABLE; + } + ops = &wined3d_query_timestamp_vk_ops; + data_size = sizeof(uint64_t); + break; + + case WINED3D_QUERY_TYPE_TIMESTAMP_DISJOINT: + if (!wined3d_device_vk(device)->timestamp_bits) + { + WARN("Timestamp queries not supported.\n"); + return WINED3DERR_NOTAVAILABLE; + } + ops = &wined3d_query_timestamp_disjoint_vk_ops; + data_size = sizeof(struct wined3d_query_data_timestamp_disjoint); + break; + + case WINED3D_QUERY_TYPE_PIPELINE_STATISTICS: + data_size = sizeof(struct wined3d_query_data_pipeline_statistics); + break; + + case WINED3D_QUERY_TYPE_SO_STATISTICS: + case WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM0: + case WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM1: + case WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM2: + case WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM3: + if (!wined3d_adapter_vk(device->adapter)->vk_info.supported[WINED3D_VK_EXT_TRANSFORM_FEEDBACK]) + { + WARN("Stream output queries not supported.\n"); + return WINED3DERR_NOTAVAILABLE; + } + data_size = sizeof(struct wined3d_query_data_so_statistics); + break; + + default: + FIXME("Unhandled query type %#x.\n", type); + return WINED3DERR_NOTAVAILABLE; + } + + if (!(query_vk = heap_alloc_zero(sizeof(*query_vk) + data_size))) + return E_OUTOFMEMORY; + data = query_vk + 1; + + wined3d_query_init(&query_vk->q, device, type, data, data_size, ops, parent, parent_ops); + list_init(&query_vk->entry); + + switch (type) + { + case WINED3D_QUERY_TYPE_OCCLUSION: + query_vk->control_flags = VK_QUERY_CONTROL_PRECISE_BIT; + break; + + case WINED3D_QUERY_TYPE_TIMESTAMP_DISJOINT: + disjoint_data = data; + disjoint_data->frequency = 1000000000 / wined3d_adapter_vk(device->adapter)->device_limits.timestampPeriod; + disjoint_data->disjoint = FALSE; + break; + + default: + break; + } + + TRACE("Created query %p.\n", query_vk); + *query = &query_vk->q; + + return WINED3D_OK; +} + +HRESULT CDECL wined3d_query_create(struct wined3d_device *device, enum wined3d_query_type type, + void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_query **query) +{ + TRACE("device %p, type %#x, parent %p, parent_ops %p, query %p.\n", + device, type, parent, parent_ops, query); + + return device->adapter->adapter_ops->adapter_create_query(device, type, parent, parent_ops, query); +} diff --git a/wrappers/directx/d3dwine_wrapper/resource.c b/wrappers/directx/d3dwine_wrapper/resource.c new file mode 100644 index 00000000000..8f2dc9ad095 --- /dev/null +++ b/wrappers/directx/d3dwine_wrapper/resource.c @@ -0,0 +1,607 @@ +/* + * Copyright 2002-2004 Jason Edmeades + * Copyright 2003-2004 Raphael Junqueira + * Copyright 2004 Christian Costa + * Copyright 2005 Oliver Stieber + * Copyright 2009-2010 Henri Verbeet for CodeWeavers + * Copyright 2006-2008, 2013 Stefan Dösinger for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "config.h" +#include "wine/port.h" +#include "wined3d_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(d3d); +WINE_DECLARE_DEBUG_CHANNEL(d3d_perf); + +static void resource_check_usage(DWORD usage, unsigned int access) +{ + static const DWORD handled = WINED3DUSAGE_DYNAMIC + | WINED3DUSAGE_STATICDECL + | WINED3DUSAGE_OVERLAY + | WINED3DUSAGE_SCRATCH + | WINED3DUSAGE_PRIVATE + | WINED3DUSAGE_LEGACY_CUBEMAP + | ~WINED3DUSAGE_MASK; + + /* Write-only CPU access is supposed to result in write-combined mappings + * being returned. OpenGL doesn't give us explicit control over that, but + * the hints and access flags we set for typical access patterns on + * dynamic resources should in theory have the same effect on the OpenGL + * driver. */ + + if (usage & ~handled) + FIXME("Unhandled usage flags %#x.\n", usage & ~handled); + if (usage & WINED3DUSAGE_DYNAMIC && access & WINED3D_RESOURCE_ACCESS_MAP_R) + WARN_(d3d_perf)("WINED3DUSAGE_DYNAMIC used with WINED3D_RESOURCE_ACCESS_MAP_R.\n"); +} + +HRESULT resource_init(struct wined3d_resource *resource, struct wined3d_device *device, + enum wined3d_resource_type type, const struct wined3d_format *format, + enum wined3d_multisample_type multisample_type, unsigned int multisample_quality, unsigned int usage, + unsigned int bind_flags, unsigned int access, unsigned int width, unsigned int height, unsigned int depth, + unsigned int size, void *parent, const struct wined3d_parent_ops *parent_ops, + const struct wined3d_resource_ops *resource_ops) +{ + const struct wined3d_d3d_info *d3d_info = &device->adapter->d3d_info; + enum wined3d_gl_resource_type base_type = WINED3D_GL_RES_TYPE_COUNT; + enum wined3d_gl_resource_type gl_type = WINED3D_GL_RES_TYPE_COUNT; + const struct wined3d_gl_info *gl_info = &device->adapter->gl_info; + BOOL tex_2d_ok = FALSE; + unsigned int i; + + static const struct + { + enum wined3d_resource_type type; + DWORD cube_usage; + enum wined3d_gl_resource_type gl_type; + } + resource_types[] = + { + {WINED3D_RTYPE_BUFFER, 0, WINED3D_GL_RES_TYPE_BUFFER}, + {WINED3D_RTYPE_TEXTURE_1D, 0, WINED3D_GL_RES_TYPE_TEX_1D}, + {WINED3D_RTYPE_TEXTURE_2D, 0, WINED3D_GL_RES_TYPE_TEX_2D}, + {WINED3D_RTYPE_TEXTURE_2D, 0, WINED3D_GL_RES_TYPE_TEX_RECT}, + {WINED3D_RTYPE_TEXTURE_2D, 0, WINED3D_GL_RES_TYPE_RB}, + {WINED3D_RTYPE_TEXTURE_2D, WINED3DUSAGE_LEGACY_CUBEMAP, WINED3D_GL_RES_TYPE_TEX_CUBE}, + {WINED3D_RTYPE_TEXTURE_3D, 0, WINED3D_GL_RES_TYPE_TEX_3D}, + }; + + resource_check_usage(usage, access); + + if (usage & WINED3DUSAGE_SCRATCH && access & WINED3D_RESOURCE_ACCESS_GPU) + { + ERR("Trying to create a scratch resource with access flags %s.\n", + wined3d_debug_resource_access(access)); + return WINED3DERR_INVALIDCALL; + } + + if (bind_flags & (WINED3D_BIND_RENDER_TARGET | WINED3D_BIND_DEPTH_STENCIL)) + { + if ((access & (WINED3D_RESOURCE_ACCESS_CPU | WINED3D_RESOURCE_ACCESS_GPU)) != WINED3D_RESOURCE_ACCESS_GPU) + { + WARN("Bind flags %s are incompatible with resource access %s.\n", + wined3d_debug_bind_flags(bind_flags), wined3d_debug_resource_access(access)); + return WINED3DERR_INVALIDCALL; + } + + /* Dynamic usage is incompatible with GPU writes. */ + if (usage & WINED3DUSAGE_DYNAMIC) + { + WARN("Bind flags %s are incompatible with resource usage %s.\n", + wined3d_debug_bind_flags(bind_flags), debug_d3dusage(usage)); + return WINED3DERR_INVALIDCALL; + } + } + + if (!size) + ERR("Attempting to create a zero-sized resource.\n"); + + for (i = 0; i < ARRAY_SIZE(resource_types); ++i) + { + if (resource_types[i].type != type + || resource_types[i].cube_usage != (usage & WINED3DUSAGE_LEGACY_CUBEMAP)) + continue; + + gl_type = resource_types[i].gl_type; + if (base_type == WINED3D_GL_RES_TYPE_COUNT) + base_type = gl_type; + + if (type != WINED3D_RTYPE_BUFFER) + { + if ((bind_flags & WINED3D_BIND_RENDER_TARGET) + && !(format->flags[gl_type] & WINED3DFMT_FLAG_RENDERTARGET)) + { + WARN("Format %s cannot be used for render targets.\n", debug_d3dformat(format->id)); + continue; + } + if ((bind_flags & WINED3D_BIND_DEPTH_STENCIL) + && !(format->flags[gl_type] & WINED3DFMT_FLAG_DEPTH_STENCIL)) + { + WARN("Format %s cannot be used for depth/stencil buffers.\n", debug_d3dformat(format->id)); + continue; + } + if ((bind_flags & WINED3D_BIND_SHADER_RESOURCE) + && !(format->flags[gl_type] & WINED3DFMT_FLAG_TEXTURE)) + { + WARN("Format %s cannot be used for texturing.\n", debug_d3dformat(format->id)); + continue; + } + } + if (((width & (width - 1)) || (height & (height - 1))) + && !d3d_info->texture_npot + && !gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT] + && gl_type == WINED3D_GL_RES_TYPE_TEX_2D) + { + TRACE("Skipping 2D texture type to try texture rectangle.\n"); + tex_2d_ok = TRUE; + continue; + } + break; + } + + if (base_type != WINED3D_GL_RES_TYPE_COUNT && i == ARRAY_SIZE(resource_types)) + { + if (tex_2d_ok) + { + /* Non power of 2 texture and rectangle textures or renderbuffers do not work. + * Use 2D textures, the texture code will pad to a power of 2 size. */ + gl_type = WINED3D_GL_RES_TYPE_TEX_2D; + } + else if (usage & WINED3DUSAGE_SCRATCH) + { + /* Needed for proper format information. */ + gl_type = base_type; + } + else + { + WARN("Did not find a suitable GL resource type for resource type %s.\n", + debug_d3dresourcetype(type)); + return WINED3DERR_INVALIDCALL; + } + } + + if (base_type != WINED3D_GL_RES_TYPE_COUNT + && (format->flags[base_type] & (WINED3DFMT_FLAG_BLOCKS | WINED3DFMT_FLAG_BLOCKS_NO_VERIFY)) + == WINED3DFMT_FLAG_BLOCKS) + { + UINT width_mask = format->block_width - 1; + UINT height_mask = format->block_height - 1; + if (width & width_mask || height & height_mask) + return WINED3DERR_INVALIDCALL; + } + + resource->ref = 1; + resource->device = device; + resource->type = type; + resource->gl_type = gl_type; + resource->format = format; + if (gl_type < WINED3D_GL_RES_TYPE_COUNT) + resource->format_flags = format->flags[gl_type]; + resource->multisample_type = multisample_type; + resource->multisample_quality = multisample_quality; + resource->usage = usage; + resource->bind_flags = bind_flags; + if (resource->format_flags & WINED3DFMT_FLAG_MAPPABLE) + access |= WINED3D_RESOURCE_ACCESS_MAP_R | WINED3D_RESOURCE_ACCESS_MAP_W; + resource->access = access; + resource->width = width; + resource->height = height; + resource->depth = depth; + resource->size = size; + resource->priority = 0; + resource->parent = parent; + resource->parent_ops = parent_ops; + resource->resource_ops = resource_ops; + resource->map_binding = WINED3D_LOCATION_SYSMEM; + resource->heap_memory = NULL; + + if (!(usage & WINED3DUSAGE_PRIVATE)) + { + /* Check that we have enough video ram left */ + if (!(access & WINED3D_RESOURCE_ACCESS_CPU) && device->wined3d->flags & WINED3D_VIDMEM_ACCOUNTING) + { + if (size > wined3d_device_get_available_texture_mem(device)) + { + ERR("Out of adapter memory.\n"); + return WINED3DERR_OUTOFVIDEOMEMORY; + } + adapter_adjust_memory(device->adapter, size); + } + + device_resource_add(device, resource); + } + + return WINED3D_OK; +} + +static void wined3d_resource_destroy_object(void *object) +{ + struct wined3d_resource *resource = object; + + TRACE("resource %p.\n", resource); + + heap_free(resource->sub_resource_bind_counts_device); + wined3d_resource_free_sysmem(resource); + context_resource_released(resource->device, resource); + wined3d_resource_release(resource); +} + +void resource_cleanup(struct wined3d_resource *resource) +{ + const struct wined3d *d3d = resource->device->wined3d; + + TRACE("Cleaning up resource %p.\n", resource); + + if (!(resource->usage & WINED3DUSAGE_PRIVATE)) + { + if (!(resource->access & WINED3D_RESOURCE_ACCESS_CPU) && d3d->flags & WINED3D_VIDMEM_ACCOUNTING) + { + TRACE("Decrementing device memory pool by %u.\n", resource->size); + adapter_adjust_memory(resource->device->adapter, (INT64)0 - resource->size); + } + + device_resource_released(resource->device, resource); + } + wined3d_resource_acquire(resource); + wined3d_cs_destroy_object(resource->device->cs, wined3d_resource_destroy_object, resource); +} + +void resource_unload(struct wined3d_resource *resource) +{ + if (resource->map_count) + ERR("Resource %p is being unloaded while mapped.\n", resource); +} + +DWORD CDECL wined3d_resource_set_priority(struct wined3d_resource *resource, DWORD priority) +{ + DWORD prev; + + if (!wined3d_resource_access_is_managed(resource->access)) + { + WARN("Called on non-managed resource %p, ignoring.\n", resource); + return 0; + } + + prev = resource->priority; + resource->priority = priority; + TRACE("resource %p, new priority %u, returning old priority %u.\n", resource, priority, prev); + return prev; +} + +DWORD CDECL wined3d_resource_get_priority(const struct wined3d_resource *resource) +{ + TRACE("resource %p, returning %u.\n", resource, resource->priority); + return resource->priority; +} + +void * CDECL wined3d_resource_get_parent(const struct wined3d_resource *resource) +{ + return resource->parent; +} + +void CDECL wined3d_resource_set_parent(struct wined3d_resource *resource, void *parent) +{ + resource->parent = parent; +} + +void CDECL wined3d_resource_get_desc(const struct wined3d_resource *resource, struct wined3d_resource_desc *desc) +{ + desc->resource_type = resource->type; + desc->format = resource->format->id; + desc->multisample_type = resource->multisample_type; + desc->multisample_quality = resource->multisample_quality; + desc->usage = resource->usage; + desc->bind_flags = resource->bind_flags; + desc->access = resource->access; + desc->width = resource->width; + desc->height = resource->height; + desc->depth = resource->depth; + desc->size = resource->size; +} + +static DWORD wined3d_resource_sanitise_map_flags(const struct wined3d_resource *resource, DWORD flags) +{ + /* Not all flags make sense together, but Windows never returns an error. + * Catch the cases that could cause issues. */ + if (flags & WINED3D_MAP_READ) + { + if (flags & WINED3D_MAP_DISCARD) + { + WARN("WINED3D_MAP_READ combined with WINED3D_MAP_DISCARD, ignoring flags.\n"); + return flags & (WINED3D_MAP_READ | WINED3D_MAP_WRITE); + } + if (flags & WINED3D_MAP_NOOVERWRITE) + { + WARN("WINED3D_MAP_READ combined with WINED3D_MAP_NOOVERWRITE, ignoring flags.\n"); + return flags & (WINED3D_MAP_READ | WINED3D_MAP_WRITE); + } + } + else if (flags & (WINED3D_MAP_DISCARD | WINED3D_MAP_NOOVERWRITE)) + { + if (!(resource->usage & WINED3DUSAGE_DYNAMIC)) + { + WARN("DISCARD or NOOVERWRITE map on non-dynamic buffer, ignoring.\n"); + return flags & (WINED3D_MAP_READ | WINED3D_MAP_WRITE); + } + if ((flags & (WINED3D_MAP_DISCARD | WINED3D_MAP_NOOVERWRITE)) + == (WINED3D_MAP_DISCARD | WINED3D_MAP_NOOVERWRITE)) + { + WARN("WINED3D_MAP_NOOVERWRITE used with WINED3D_MAP_DISCARD, ignoring WINED3D_MAP_DISCARD.\n"); + flags &= ~WINED3D_MAP_DISCARD; + } + } + + return flags; +} + +HRESULT CDECL wined3d_resource_map(struct wined3d_resource *resource, unsigned int sub_resource_idx, + struct wined3d_map_desc *map_desc, const struct wined3d_box *box, DWORD flags) +{ + TRACE("resource %p, sub_resource_idx %u, map_desc %p, box %s, flags %#x.\n", + resource, sub_resource_idx, map_desc, debug_box(box), flags); + + if (!(flags & (WINED3D_MAP_READ | WINED3D_MAP_WRITE))) + { + WARN("No read/write flags specified.\n"); + return E_INVALIDARG; + } + + if ((flags & WINED3D_MAP_READ) && !(resource->access & WINED3D_RESOURCE_ACCESS_MAP_R)) + { + WARN("Resource does not have MAP_R access.\n"); + return E_INVALIDARG; + } + + if ((flags & WINED3D_MAP_WRITE) && !(resource->access & WINED3D_RESOURCE_ACCESS_MAP_W)) + { + WARN("Resource does not have MAP_W access.\n"); + return E_INVALIDARG; + } + + flags = wined3d_resource_sanitise_map_flags(resource, flags); + wined3d_resource_wait_idle(resource); + + return wined3d_cs_map(resource->device->cs, resource, sub_resource_idx, map_desc, box, flags); +} + +HRESULT CDECL wined3d_resource_unmap(struct wined3d_resource *resource, unsigned int sub_resource_idx) +{ + TRACE("resource %p, sub_resource_idx %u.\n", resource, sub_resource_idx); + + return wined3d_cs_unmap(resource->device->cs, resource, sub_resource_idx); +} + +void CDECL wined3d_resource_preload(struct wined3d_resource *resource) +{ + wined3d_cs_emit_preload_resource(resource->device->cs, resource); +} + +static BOOL wined3d_resource_allocate_sysmem(struct wined3d_resource *resource) +{ + void **p; + SIZE_T align = RESOURCE_ALIGNMENT - 1 + sizeof(*p); + void *mem; + + if (!(mem = heap_alloc_zero(resource->size + align))) + { + ERR("Failed to allocate system memory.\n"); + return FALSE; + } + + p = (void **)(((ULONG_PTR)mem + align) & ~(RESOURCE_ALIGNMENT - 1)) - 1; + *p = mem; + + resource->heap_memory = ++p; + + return TRUE; +} + +BOOL wined3d_resource_prepare_sysmem(struct wined3d_resource *resource) +{ + if (resource->heap_memory) + return TRUE; + + return wined3d_resource_allocate_sysmem(resource); +} + +void wined3d_resource_free_sysmem(struct wined3d_resource *resource) +{ + void **p = resource->heap_memory; + + if (!p) + return; + + heap_free(*(--p)); + resource->heap_memory = NULL; +} + +GLbitfield wined3d_resource_gl_storage_flags(const struct wined3d_resource *resource) +{ + uint32_t access = resource->access; + GLbitfield flags = 0; + + if (resource->usage & WINED3DUSAGE_DYNAMIC) + flags |= GL_CLIENT_STORAGE_BIT; + if (access & WINED3D_RESOURCE_ACCESS_MAP_W) + flags |= GL_MAP_WRITE_BIT; + if (access & WINED3D_RESOURCE_ACCESS_MAP_R) + flags |= GL_MAP_READ_BIT; + + return flags; +} + +GLbitfield wined3d_resource_gl_map_flags(DWORD d3d_flags) +{ + GLbitfield ret = 0; + + if (d3d_flags & WINED3D_MAP_WRITE) + ret |= GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT; + if (d3d_flags & WINED3D_MAP_READ) + ret |= GL_MAP_READ_BIT; + else + ret |= GL_MAP_UNSYNCHRONIZED_BIT; + + return ret; +} + +GLenum wined3d_resource_gl_legacy_map_flags(DWORD d3d_flags) +{ + switch (d3d_flags & (WINED3D_MAP_READ | WINED3D_MAP_WRITE)) + { + case WINED3D_MAP_READ: + return GL_READ_ONLY_ARB; + + case WINED3D_MAP_WRITE: + return GL_WRITE_ONLY_ARB; + + default: + return GL_READ_WRITE_ARB; + } +} + +BOOL wined3d_resource_is_offscreen(struct wined3d_resource *resource) +{ + struct wined3d_swapchain *swapchain; + + /* Only 2D texture resources can be onscreen. */ + if (resource->type != WINED3D_RTYPE_TEXTURE_2D) + return TRUE; + + /* Not on a swapchain - must be offscreen */ + if (!(swapchain = texture_from_resource(resource)->swapchain)) + return TRUE; + + /* The front buffer is always onscreen */ + if (resource == &swapchain->front_buffer->resource) + return FALSE; + + /* If the swapchain is rendered to an FBO, the backbuffer is + * offscreen, otherwise onscreen */ + return swapchain->render_to_fbo; +} + +void wined3d_resource_update_draw_binding(struct wined3d_resource *resource) +{ + const struct wined3d_d3d_info *d3d_info = &resource->device->adapter->d3d_info; + + if (!wined3d_resource_is_offscreen(resource) || wined3d_settings.offscreen_rendering_mode != ORM_FBO) + { + resource->draw_binding = WINED3D_LOCATION_DRAWABLE; + } + else if (resource->multisample_type) + { + resource->draw_binding = d3d_info->multisample_draw_location; + } + else if (resource->gl_type == WINED3D_GL_RES_TYPE_RB) + { + resource->draw_binding = WINED3D_LOCATION_RB_RESOLVED; + } + else + { + resource->draw_binding = WINED3D_LOCATION_TEXTURE_RGB; + } +} + +const struct wined3d_format *wined3d_resource_get_decompress_format(const struct wined3d_resource *resource) +{ + const struct wined3d_adapter *adapter = resource->device->adapter; + if (resource->format_flags & (WINED3DFMT_FLAG_SRGB_READ | WINED3DFMT_FLAG_SRGB_WRITE) + && !(adapter->d3d_info.wined3d_creation_flags & WINED3D_SRGB_READ_WRITE_CONTROL)) + return wined3d_get_format(adapter, WINED3DFMT_B8G8R8A8_UNORM_SRGB, resource->bind_flags); + return wined3d_get_format(adapter, WINED3DFMT_B8G8R8A8_UNORM, resource->bind_flags); +} + +unsigned int wined3d_resource_get_sample_count(const struct wined3d_resource *resource) +{ + const struct wined3d_format *format = resource->format; + + /* TODO: NVIDIA expose their Coverage Sample Anti-Aliasing (CSAA) + * feature through type == MULTISAMPLE_XX and quality != 0. This could + * be mapped to GL_NV_framebuffer_multisample_coverage. + * + * AMD have a similar feature called Enhanced Quality Anti-Aliasing + * (EQAA), but it does not have an equivalent OpenGL extension. */ + + /* We advertise as many WINED3D_MULTISAMPLE_NON_MASKABLE quality + * levels as the count of advertised multisample types for the texture + * format. */ + if (resource->multisample_type == WINED3D_MULTISAMPLE_NON_MASKABLE) + { + unsigned int i, count = 0; + + for (i = 0; i < sizeof(format->multisample_types) * CHAR_BIT; ++i) + { + if (format->multisample_types & 1u << i) + { + if (resource->multisample_quality == count++) + break; + } + } + return i + 1; + } + + return resource->multisample_type; +} + +VkAccessFlags vk_access_mask_from_bind_flags(uint32_t bind_flags) +{ + VkAccessFlags flags = 0; + + if (bind_flags & WINED3D_BIND_VERTEX_BUFFER) + flags |= VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT; + if (bind_flags & WINED3D_BIND_INDEX_BUFFER) + flags |= VK_ACCESS_INDEX_READ_BIT; + if (bind_flags & WINED3D_BIND_CONSTANT_BUFFER) + flags |= VK_ACCESS_UNIFORM_READ_BIT; + if (bind_flags & WINED3D_BIND_SHADER_RESOURCE) + flags |= VK_ACCESS_SHADER_READ_BIT; + if (bind_flags & WINED3D_BIND_UNORDERED_ACCESS) + flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; + if (bind_flags & WINED3D_BIND_INDIRECT_BUFFER) + flags |= VK_ACCESS_INDIRECT_COMMAND_READ_BIT; + if (bind_flags & WINED3D_BIND_RENDER_TARGET) + flags |= VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + if (bind_flags & WINED3D_BIND_DEPTH_STENCIL) + flags |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + if (bind_flags & WINED3D_BIND_STREAM_OUTPUT) + flags |= VK_ACCESS_TRANSFORM_FEEDBACK_WRITE_BIT_EXT; + + return flags; +} + +VkPipelineStageFlags vk_pipeline_stage_mask_from_bind_flags(uint32_t bind_flags) +{ + VkPipelineStageFlags flags = 0; + + if (bind_flags & (WINED3D_BIND_VERTEX_BUFFER | WINED3D_BIND_INDEX_BUFFER)) + flags |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT; + if (bind_flags & (WINED3D_BIND_CONSTANT_BUFFER | WINED3D_BIND_SHADER_RESOURCE | WINED3D_BIND_UNORDERED_ACCESS)) + flags |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT + | VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT | VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT + | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + if (bind_flags & WINED3D_BIND_INDIRECT_BUFFER) + flags |= VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT; + if (bind_flags & WINED3D_BIND_RENDER_TARGET) + flags |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + if (bind_flags & WINED3D_BIND_DEPTH_STENCIL) + flags |= VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; + if (bind_flags & WINED3D_BIND_STREAM_OUTPUT) + flags |= VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT; + + return flags; +} diff --git a/wrappers/directx/d3dwine_wrapper/roshacks.txt b/wrappers/directx/d3dwine_wrapper/roshacks.txt new file mode 100644 index 00000000000..68c40567cad --- /dev/null +++ b/wrappers/directx/d3dwine_wrapper/roshacks.txt @@ -0,0 +1,7 @@ +Note +directx.c +----------- +gdi32.dll to opengl32.dll +comment out line 186 as well in function +WineD3D_ReleaseFakeGLContext +row pwglDeleteContext(glCtx); diff --git a/wrappers/directx/d3dwine_wrapper/sampler.c b/wrappers/directx/d3dwine_wrapper/sampler.c new file mode 100644 index 00000000000..38ede905deb --- /dev/null +++ b/wrappers/directx/d3dwine_wrapper/sampler.c @@ -0,0 +1,317 @@ +/* + * Copyright 2012, 2015 Henri Verbeet for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + */ + +#include "config.h" +#include "wine/port.h" + +#include "wined3d_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(d3d); + +ULONG CDECL wined3d_sampler_incref(struct wined3d_sampler *sampler) +{ + ULONG refcount = InterlockedIncrement(&sampler->refcount); + + TRACE("%p increasing refcount to %u.\n", sampler, refcount); + + return refcount; +} + +ULONG CDECL wined3d_sampler_decref(struct wined3d_sampler *sampler) +{ + ULONG refcount = InterlockedDecrement(&sampler->refcount); + + TRACE("%p decreasing refcount to %u.\n", sampler, refcount); + + if (!refcount) + { + sampler->parent_ops->wined3d_object_destroyed(sampler->parent); + sampler->device->adapter->adapter_ops->adapter_destroy_sampler(sampler); + } + + return refcount; +} + +void * CDECL wined3d_sampler_get_parent(const struct wined3d_sampler *sampler) +{ + TRACE("sampler %p.\n", sampler); + + return sampler->parent; +} + +static void wined3d_sampler_init(struct wined3d_sampler *sampler, struct wined3d_device *device, + const struct wined3d_sampler_desc *desc, void *parent, const struct wined3d_parent_ops *parent_ops) +{ + TRACE("sampler %p, device %p, desc %p, parent %p, parent_ops %p.\n", + sampler, device, desc, parent, parent_ops); + + sampler->refcount = 1; + sampler->device = device; + sampler->parent = parent; + sampler->parent_ops = parent_ops; + sampler->desc = *desc; +} + +static void wined3d_sampler_gl_cs_init(void *object) +{ + struct wined3d_sampler_gl *sampler_gl = object; + const struct wined3d_sampler_desc *desc; + const struct wined3d_gl_info *gl_info; + struct wined3d_context *context; + GLuint name; + + TRACE("sampler_gl %p.\n", sampler_gl); + + context = context_acquire(sampler_gl->s.device, NULL, 0); + gl_info = wined3d_context_gl(context)->gl_info; + + desc = &sampler_gl->s.desc; + GL_EXTCALL(glGenSamplers(1, &name)); + GL_EXTCALL(glSamplerParameteri(name, GL_TEXTURE_WRAP_S, + gl_info->wrap_lookup[desc->address_u - WINED3D_TADDRESS_WRAP])); + GL_EXTCALL(glSamplerParameteri(name, GL_TEXTURE_WRAP_T, + gl_info->wrap_lookup[desc->address_v - WINED3D_TADDRESS_WRAP])); + GL_EXTCALL(glSamplerParameteri(name, GL_TEXTURE_WRAP_R, + gl_info->wrap_lookup[desc->address_w - WINED3D_TADDRESS_WRAP])); + GL_EXTCALL(glSamplerParameterfv(name, GL_TEXTURE_BORDER_COLOR, &desc->border_color[0])); + GL_EXTCALL(glSamplerParameteri(name, GL_TEXTURE_MAG_FILTER, + wined3d_gl_mag_filter(desc->mag_filter))); + GL_EXTCALL(glSamplerParameteri(name, GL_TEXTURE_MIN_FILTER, + wined3d_gl_min_mip_filter(desc->min_filter, desc->mip_filter))); + GL_EXTCALL(glSamplerParameterf(name, GL_TEXTURE_LOD_BIAS, desc->lod_bias)); + GL_EXTCALL(glSamplerParameterf(name, GL_TEXTURE_MIN_LOD, desc->min_lod)); + GL_EXTCALL(glSamplerParameterf(name, GL_TEXTURE_MAX_LOD, desc->max_lod)); + if (gl_info->supported[ARB_TEXTURE_FILTER_ANISOTROPIC]) + GL_EXTCALL(glSamplerParameteri(name, GL_TEXTURE_MAX_ANISOTROPY, desc->max_anisotropy)); + if (desc->compare) + GL_EXTCALL(glSamplerParameteri(name, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE)); + GL_EXTCALL(glSamplerParameteri(name, GL_TEXTURE_COMPARE_FUNC, + wined3d_gl_compare_func(desc->comparison_func))); + if ((context->d3d_info->wined3d_creation_flags & WINED3D_SRGB_READ_WRITE_CONTROL) + && gl_info->supported[EXT_TEXTURE_SRGB_DECODE] && !desc->srgb_decode) + GL_EXTCALL(glSamplerParameteri(name, GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT)); + checkGLcall("sampler creation"); + + TRACE("Created sampler %u.\n", name); + sampler_gl->name = name; + + context_release(context); +} + +void wined3d_sampler_gl_init(struct wined3d_sampler_gl *sampler_gl, struct wined3d_device *device, + const struct wined3d_sampler_desc *desc, void *parent, const struct wined3d_parent_ops *parent_ops) +{ + TRACE("sampler_gl %p, device %p, desc %p, parent %p, parent_ops %p.\n", + sampler_gl, device, desc, parent, parent_ops); + + wined3d_sampler_init(&sampler_gl->s, device, desc, parent, parent_ops); + + if (device->adapter->gl_info.supported[ARB_SAMPLER_OBJECTS]) + wined3d_cs_init_object(device->cs, wined3d_sampler_gl_cs_init, sampler_gl); +} + +static VkFilter vk_filter_from_wined3d(enum wined3d_texture_filter_type f) +{ + switch (f) + { + default: + ERR("Invalid filter type %#x.\n", f); + case WINED3D_TEXF_POINT: + return VK_FILTER_NEAREST; + case WINED3D_TEXF_LINEAR: + return VK_FILTER_LINEAR; + } +} + +static VkSamplerMipmapMode vk_mipmap_mode_from_wined3d(enum wined3d_texture_filter_type f) +{ + switch (f) + { + default: + ERR("Invalid filter type %#x.\n", f); + case WINED3D_TEXF_NONE: + case WINED3D_TEXF_POINT: + return VK_SAMPLER_MIPMAP_MODE_NEAREST; + case WINED3D_TEXF_LINEAR: + return VK_SAMPLER_MIPMAP_MODE_LINEAR; + } +} + +static VkSamplerAddressMode vk_address_mode_from_wined3d(enum wined3d_texture_address a) +{ + switch (a) + { + default: + ERR("Invalid address mode %#x.\n", a); + case WINED3D_TADDRESS_WRAP: + return VK_SAMPLER_ADDRESS_MODE_REPEAT; + case WINED3D_TADDRESS_MIRROR: + return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT; + case WINED3D_TADDRESS_CLAMP: + return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + case WINED3D_TADDRESS_BORDER: + return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER; + case WINED3D_TADDRESS_MIRROR_ONCE: + return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE; + } +} + +static void wined3d_sampler_vk_cs_init(void *object) +{ + struct wined3d_sampler_vk *sampler_vk = object; + const struct wined3d_sampler_desc *desc; + const struct wined3d_d3d_info *d3d_info; + struct VkSamplerCreateInfo sampler_desc; + const struct wined3d_vk_info *vk_info; + struct wined3d_context_vk *context_vk; + struct wined3d_device_vk *device_vk; + VkSampler vk_sampler; + VkResult vr; + + TRACE("sampler_vk %p.\n", sampler_vk); + + context_vk = wined3d_context_vk(context_acquire(sampler_vk->s.device, NULL, 0)); + device_vk = wined3d_device_vk(context_vk->c.device); + d3d_info = context_vk->c.d3d_info; + vk_info = context_vk->vk_info; + + desc = &sampler_vk->s.desc; + sampler_desc.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + sampler_desc.pNext = NULL; + sampler_desc.flags = 0; + sampler_desc.magFilter = vk_filter_from_wined3d(desc->mag_filter); + sampler_desc.minFilter = vk_filter_from_wined3d(desc->min_filter); + sampler_desc.mipmapMode = vk_mipmap_mode_from_wined3d(desc->mip_filter); + sampler_desc.addressModeU = vk_address_mode_from_wined3d(desc->address_u); + sampler_desc.addressModeV = vk_address_mode_from_wined3d(desc->address_v); + sampler_desc.addressModeW = vk_address_mode_from_wined3d(desc->address_w); + sampler_desc.mipLodBias = desc->lod_bias; + sampler_desc.anisotropyEnable = desc->max_anisotropy != 1; + sampler_desc.maxAnisotropy = desc->max_anisotropy; + sampler_desc.compareEnable = desc->compare; + sampler_desc.compareOp = vk_compare_op_from_wined3d(desc->comparison_func); + sampler_desc.minLod = desc->min_lod; + sampler_desc.maxLod = desc->max_lod; + sampler_desc.borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK; + sampler_desc.unnormalizedCoordinates = VK_FALSE; + + if ((desc->address_u == WINED3D_TADDRESS_BORDER || desc->address_v == WINED3D_TADDRESS_BORDER + || desc->address_w == WINED3D_TADDRESS_BORDER) + && (desc->border_color[0] != 0.0f || desc->border_color[1] != 0.0f + || desc->border_color[2] != 0.0f || desc->border_color[3] != 0.0f)) + FIXME("Unhandled border colour {%.8e, %.8e, %.8e, %.8e}.\n", + desc->border_color[0], desc->border_color[1], + desc->border_color[2], desc->border_color[3]); + if (desc->mip_base_level) + FIXME("Unhandled mip_base_level %u.\n", desc->mip_base_level); + if ((d3d_info->wined3d_creation_flags & WINED3D_SRGB_READ_WRITE_CONTROL) && !desc->srgb_decode) + FIXME("Unhandled srgb_decode %#x.\n", desc->srgb_decode); + + vr = VK_CALL(vkCreateSampler(device_vk->vk_device, &sampler_desc, NULL, &vk_sampler)); + context_release(&context_vk->c); + if (vr < 0) + { + ERR("Failed to create Vulkan sampler, vr %s.\n", wined3d_debug_vkresult(vr)); + return; + } + + TRACE("Created sampler 0x%s.\n", wine_dbgstr_longlong(vk_sampler)); + + sampler_vk->vk_image_info.sampler = vk_sampler; + sampler_vk->vk_image_info.imageView = VK_NULL_HANDLE; + sampler_vk->vk_image_info.imageLayout = VK_IMAGE_LAYOUT_UNDEFINED; +} + +void wined3d_sampler_vk_init(struct wined3d_sampler_vk *sampler_vk, struct wined3d_device *device, + const struct wined3d_sampler_desc *desc, void *parent, const struct wined3d_parent_ops *parent_ops) +{ + TRACE("sampler_vk %p, device %p, desc %p, parent %p, parent_ops %p.\n", + sampler_vk, device, desc, parent, parent_ops); + + wined3d_sampler_init(&sampler_vk->s, device, desc, parent, parent_ops); + wined3d_cs_init_object(device->cs, wined3d_sampler_vk_cs_init, sampler_vk); +} + +HRESULT CDECL wined3d_sampler_create(struct wined3d_device *device, const struct wined3d_sampler_desc *desc, + void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_sampler **sampler) +{ + TRACE("device %p, desc %p, parent %p, parent_ops %p, sampler %p.\n", + device, desc, parent, parent_ops, sampler); + + if (desc->address_u < WINED3D_TADDRESS_WRAP || desc->address_u > WINED3D_TADDRESS_MIRROR_ONCE + || desc->address_v < WINED3D_TADDRESS_WRAP || desc->address_v > WINED3D_TADDRESS_MIRROR_ONCE + || desc->address_w < WINED3D_TADDRESS_WRAP || desc->address_w > WINED3D_TADDRESS_MIRROR_ONCE) + return WINED3DERR_INVALIDCALL; + + if (desc->mag_filter < WINED3D_TEXF_POINT || desc->mag_filter > WINED3D_TEXF_LINEAR + || desc->min_filter < WINED3D_TEXF_POINT || desc->min_filter > WINED3D_TEXF_LINEAR + || desc->mip_filter > WINED3D_TEXF_LINEAR) + return WINED3DERR_INVALIDCALL; + + return device->adapter->adapter_ops->adapter_create_sampler(device, desc, parent, parent_ops, sampler); +} + +static void texture_gl_apply_base_level(struct wined3d_texture_gl *texture_gl, + const struct wined3d_sampler_desc *desc, const struct wined3d_gl_info *gl_info) +{ + struct gl_texture *gl_tex; + unsigned int base_level; + + if (texture_gl->t.flags & WINED3D_TEXTURE_COND_NP2) + base_level = 0; + else if (desc->mip_filter == WINED3D_TEXF_NONE) + base_level = texture_gl->t.lod; + else + base_level = min(max(desc->mip_base_level, texture_gl->t.lod), texture_gl->t.level_count - 1); + + gl_tex = wined3d_texture_gl_get_gl_texture(texture_gl, texture_gl->t.flags & WINED3D_TEXTURE_IS_SRGB); + if (base_level != gl_tex->base_level) + { + /* Note that WINED3D_SAMP_MAX_MIP_LEVEL specifies the largest mipmap + * (default 0), while GL_TEXTURE_MAX_LEVEL specifies the smallest + * mipmap used (default 1000). So WINED3D_SAMP_MAX_MIP_LEVEL + * corresponds to GL_TEXTURE_BASE_LEVEL. */ + gl_info->gl_ops.gl.p_glTexParameteri(texture_gl->target, GL_TEXTURE_BASE_LEVEL, base_level); + gl_tex->base_level = base_level; + } +} + +/* This function relies on the correct texture being bound and loaded. */ +void wined3d_sampler_gl_bind(struct wined3d_sampler_gl *sampler_gl, unsigned int unit, + struct wined3d_texture_gl *texture_gl, const struct wined3d_context_gl *context_gl) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + + if (gl_info->supported[ARB_SAMPLER_OBJECTS]) + { + GL_EXTCALL(glBindSampler(unit, sampler_gl->name)); + checkGLcall("bind sampler"); + } + else if (texture_gl) + { + wined3d_texture_gl_apply_sampler_desc(texture_gl, &sampler_gl->s.desc, context_gl); + } + else + { + ERR("Could not apply sampler state.\n"); + } + + if (texture_gl) + texture_gl_apply_base_level(texture_gl, &sampler_gl->s.desc, gl_info); +} diff --git a/wrappers/directx/d3dwine_wrapper/shader.c b/wrappers/directx/d3dwine_wrapper/shader.c new file mode 100644 index 00000000000..e1142990139 --- /dev/null +++ b/wrappers/directx/d3dwine_wrapper/shader.c @@ -0,0 +1,4489 @@ +/* + * Copyright 2002-2003 Jason Edmeades + * Copyright 2002-2003 Raphael Junqueira + * Copyright 2004 Christian Costa + * Copyright 2005 Oliver Stieber + * Copyright 2006 Ivan Gyurdiev + * Copyright 2007-2008, 2013 Stefan Dösinger for CodeWeavers + * Copyright 2009-2011 Henri Verbeet for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "config.h" +#include "wine/port.h" + +#include +#include + +#include "wined3d_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(d3d_shader); + +const struct wined3d_vec4 wined3d_srgb_const[] = +{ + /* pow, mul_high, sub_high, mul_low */ + {0.41666f, 1.055f, 0.055f, 12.92f}, + /* cmp */ + {0.0031308f, 0.0f, 0.0f, 0.0f}, +}; + +static const char * const shader_opcode_names[] = +{ + /* WINED3DSIH_ABS */ "abs", + /* WINED3DSIH_ADD */ "add", + /* WINED3DSIH_AND */ "and", + /* WINED3DSIH_ATOMIC_AND */ "atomic_and", + /* WINED3DSIH_ATOMIC_CMP_STORE */ "atomic_cmp_store", + /* WINED3DSIH_ATOMIC_IADD */ "atomic_iadd", + /* WINED3DSIH_ATOMIC_IMAX */ "atomic_imax", + /* WINED3DSIH_ATOMIC_IMIN */ "atomic_imin", + /* WINED3DSIH_ATOMIC_OR */ "atomic_or", + /* WINED3DSIH_ATOMIC_UMAX */ "atomic_umax", + /* WINED3DSIH_ATOMIC_UMIN */ "atomic_umin", + /* WINED3DSIH_ATOMIC_XOR */ "atomic_xor", + /* WINED3DSIH_BEM */ "bem", + /* WINED3DSIH_BFI */ "bfi", + /* WINED3DSIH_BFREV */ "bfrev", + /* WINED3DSIH_BREAK */ "break", + /* WINED3DSIH_BREAKC */ "breakc", + /* WINED3DSIH_BREAKP */ "breakp", + /* WINED3DSIH_BUFINFO */ "bufinfo", + /* WINED3DSIH_CALL */ "call", + /* WINED3DSIH_CALLNZ */ "callnz", + /* WINED3DSIH_CASE */ "case", + /* WINED3DSIH_CMP */ "cmp", + /* WINED3DSIH_CND */ "cnd", + /* WINED3DSIH_CONTINUE */ "continue", + /* WINED3DSIH_CONTINUEP */ "continuec", + /* WINED3DSIH_COUNTBITS */ "countbits", + /* WINED3DSIH_CRS */ "crs", + /* WINED3DSIH_CUT */ "cut", + /* WINED3DSIH_CUT_STREAM */ "cut_stream", + /* WINED3DSIH_DCL */ "dcl", + /* WINED3DSIH_DCL_CONSTANT_BUFFER */ "dcl_constantBuffer", + /* WINED3DSIH_DCL_FUNCTION_BODY */ "dcl_function_body", + /* WINED3DSIH_DCL_FUNCTION_TABLE */ "dcl_function_table", + /* WINED3DSIH_DCL_GLOBAL_FLAGS */ "dcl_globalFlags", + /* WINED3DSIH_DCL_GS_INSTANCES */ "dcl_gs_instances", + /* WINED3DSIH_DCL_HS_FORK_PHASE_INSTANCE_COUNT */ "dcl_hs_fork_phase_instance_count", + /* WINED3DSIH_DCL_HS_JOIN_PHASE_INSTANCE_COUNT */ "dcl_hs_join_phase_instance_count", + /* WINED3DSIH_DCL_HS_MAX_TESSFACTOR */ "dcl_hs_max_tessfactor", + /* WINED3DSIH_DCL_IMMEDIATE_CONSTANT_BUFFER */ "dcl_immediateConstantBuffer", + /* WINED3DSIH_DCL_INDEX_RANGE */ "dcl_index_range", + /* WINED3DSIH_DCL_INDEXABLE_TEMP */ "dcl_indexableTemp", + /* WINED3DSIH_DCL_INPUT */ "dcl_input", + /* WINED3DSIH_DCL_INPUT_CONTROL_POINT_COUNT */ "dcl_input_control_point_count", + /* WINED3DSIH_DCL_INPUT_PRIMITIVE */ "dcl_inputPrimitive", + /* WINED3DSIH_DCL_INPUT_PS */ "dcl_input_ps", + /* WINED3DSIH_DCL_INPUT_PS_SGV */ "dcl_input_ps_sgv", + /* WINED3DSIH_DCL_INPUT_PS_SIV */ "dcl_input_ps_siv", + /* WINED3DSIH_DCL_INPUT_SGV */ "dcl_input_sgv", + /* WINED3DSIH_DCL_INPUT_SIV */ "dcl_input_siv", + /* WINED3DSIH_DCL_INTERFACE */ "dcl_interface", + /* WINED3DSIH_DCL_OUTPUT */ "dcl_output", + /* WINED3DSIH_DCL_OUTPUT_CONTROL_POINT_COUNT */ "dcl_output_control_point_count", + /* WINED3DSIH_DCL_OUTPUT_SIV */ "dcl_output_siv", + /* WINED3DSIH_DCL_OUTPUT_TOPOLOGY */ "dcl_outputTopology", + /* WINED3DSIH_DCL_RESOURCE_RAW */ "dcl_resource_raw", + /* WINED3DSIH_DCL_RESOURCE_STRUCTURED */ "dcl_resource_structured", + /* WINED3DSIH_DCL_SAMPLER */ "dcl_sampler", + /* WINED3DSIH_DCL_STREAM */ "dcl_stream", + /* WINED3DSIH_DCL_TEMPS */ "dcl_temps", + /* WINED3DSIH_DCL_TESSELLATOR_DOMAIN */ "dcl_tessellator_domain", + /* WINED3DSIH_DCL_TESSELLATOR_OUTPUT_PRIMITIVE */ "dcl_tessellator_output_primitive", + /* WINED3DSIH_DCL_TESSELLATOR_PARTITIONING */ "dcl_tessellator_partitioning", + /* WINED3DSIH_DCL_TGSM_RAW */ "dcl_tgsm_raw", + /* WINED3DSIH_DCL_TGSM_STRUCTURED */ "dcl_tgsm_structured", + /* WINED3DSIH_DCL_THREAD_GROUP */ "dcl_thread_group", + /* WINED3DSIH_DCL_UAV_RAW */ "dcl_uav_raw", + /* WINED3DSIH_DCL_UAV_STRUCTURED */ "dcl_uav_structured", + /* WINED3DSIH_DCL_UAV_TYPED */ "dcl_uav_typed", + /* WINED3DSIH_DCL_VERTICES_OUT */ "dcl_maxOutputVertexCount", + /* WINED3DSIH_DEF */ "def", + /* WINED3DSIH_DEFAULT */ "default", + /* WINED3DSIH_DEFB */ "defb", + /* WINED3DSIH_DEFI */ "defi", + /* WINED3DSIH_DIV */ "div", + /* WINED3DSIH_DP2 */ "dp2", + /* WINED3DSIH_DP2ADD */ "dp2add", + /* WINED3DSIH_DP3 */ "dp3", + /* WINED3DSIH_DP4 */ "dp4", + /* WINED3DSIH_DST */ "dst", + /* WINED3DSIH_DSX */ "dsx", + /* WINED3DSIH_DSX_COARSE */ "deriv_rtx_coarse", + /* WINED3DSIH_DSX_FINE */ "deriv_rtx_fine", + /* WINED3DSIH_DSY */ "dsy", + /* WINED3DSIH_DSY_COARSE */ "deriv_rty_coarse", + /* WINED3DSIH_DSY_FINE */ "deriv_rty_fine", + /* WINED3DSIH_ELSE */ "else", + /* WINED3DSIH_EMIT */ "emit", + /* WINED3DSIH_EMIT_STREAM */ "emit_stream", + /* WINED3DSIH_ENDIF */ "endif", + /* WINED3DSIH_ENDLOOP */ "endloop", + /* WINED3DSIH_ENDREP */ "endrep", + /* WINED3DSIH_ENDSWITCH */ "endswitch", + /* WINED3DSIH_EQ */ "eq", + /* WINED3DSIH_EVAL_SAMPLE_INDEX */ "eval_sample_index", + /* WINED3DSIH_EXP */ "exp", + /* WINED3DSIH_EXPP */ "expp", + /* WINED3DSIH_F16TOF32 */ "f16tof32", + /* WINED3DSIH_F32TOF16 */ "f32tof16", + /* WINED3DSIH_FCALL */ "fcall", + /* WINED3DSIH_FIRSTBIT_HI */ "firstbit_hi", + /* WINED3DSIH_FIRSTBIT_LO */ "firstbit_lo", + /* WINED3DSIH_FIRSTBIT_SHI */ "firstbit_shi", + /* WINED3DSIH_FRC */ "frc", + /* WINED3DSIH_FTOI */ "ftoi", + /* WINED3DSIH_FTOU */ "ftou", + /* WINED3DSIH_GATHER4 */ "gather4", + /* WINED3DSIH_GATHER4_C */ "gather4_c", + /* WINED3DSIH_GATHER4_PO */ "gather4_po", + /* WINED3DSIH_GATHER4_PO_C */ "gather4_po_c", + /* WINED3DSIH_GE */ "ge", + /* WINED3DSIH_HS_CONTROL_POINT_PHASE */ "hs_control_point_phase", + /* WINED3DSIH_HS_DECLS */ "hs_decls", + /* WINED3DSIH_HS_FORK_PHASE */ "hs_fork_phase", + /* WINED3DSIH_HS_JOIN_PHASE */ "hs_join_phase", + /* WINED3DSIH_IADD */ "iadd", + /* WINED3DSIH_IBFE */ "ibfe", + /* WINED3DSIH_IEQ */ "ieq", + /* WINED3DSIH_IF */ "if", + /* WINED3DSIH_IFC */ "ifc", + /* WINED3DSIH_IGE */ "ige", + /* WINED3DSIH_ILT */ "ilt", + /* WINED3DSIH_IMAD */ "imad", + /* WINED3DSIH_IMAX */ "imax", + /* WINED3DSIH_IMIN */ "imin", + /* WINED3DSIH_IMM_ATOMIC_ALLOC */ "imm_atomic_alloc", + /* WINED3DSIH_IMM_ATOMIC_AND */ "imm_atomic_and", + /* WINED3DSIH_IMM_ATOMIC_CMP_EXCH */ "imm_atomic_cmp_exch", + /* WINED3DSIH_IMM_ATOMIC_CONSUME */ "imm_atomic_consume", + /* WINED3DSIH_IMM_ATOMIC_EXCH */ "imm_atomic_exch", + /* WINED3DSIH_IMM_ATOMIC_IADD */ "imm_atomic_iadd", + /* WINED3DSIH_IMM_ATOMIC_IMAX */ "imm_atomic_imax", + /* WINED3DSIH_IMM_ATOMIC_IMIN */ "imm_atomic_imin", + /* WINED3DSIH_IMM_ATOMIC_OR */ "imm_atomic_or", + /* WINED3DSIH_IMM_ATOMIC_UMAX */ "imm_atomic_umax", + /* WINED3DSIH_IMM_ATOMIC_UMIN */ "imm_atomic_umin", + /* WINED3DSIH_IMM_ATOMIC_XOR */ "imm_atomic_xor", + /* WINED3DSIH_IMUL */ "imul", + /* WINED3DSIH_INE */ "ine", + /* WINED3DSIH_INEG */ "ineg", + /* WINED3DSIH_ISHL */ "ishl", + /* WINED3DSIH_ISHR */ "ishr", + /* WINED3DSIH_ITOF */ "itof", + /* WINED3DSIH_LABEL */ "label", + /* WINED3DSIH_LD */ "ld", + /* WINED3DSIH_LD2DMS */ "ld2dms", + /* WINED3DSIH_LD_RAW */ "ld_raw", + /* WINED3DSIH_LD_STRUCTURED */ "ld_structured", + /* WINED3DSIH_LD_UAV_TYPED */ "ld_uav_typed", + /* WINED3DSIH_LIT */ "lit", + /* WINED3DSIH_LOD */ "lod", + /* WINED3DSIH_LOG */ "log", + /* WINED3DSIH_LOGP */ "logp", + /* WINED3DSIH_LOOP */ "loop", + /* WINED3DSIH_LRP */ "lrp", + /* WINED3DSIH_LT */ "lt", + /* WINED3DSIH_M3x2 */ "m3x2", + /* WINED3DSIH_M3x3 */ "m3x3", + /* WINED3DSIH_M3x4 */ "m3x4", + /* WINED3DSIH_M4x3 */ "m4x3", + /* WINED3DSIH_M4x4 */ "m4x4", + /* WINED3DSIH_MAD */ "mad", + /* WINED3DSIH_MAX */ "max", + /* WINED3DSIH_MIN */ "min", + /* WINED3DSIH_MOV */ "mov", + /* WINED3DSIH_MOVA */ "mova", + /* WINED3DSIH_MOVC */ "movc", + /* WINED3DSIH_MUL */ "mul", + /* WINED3DSIH_NE */ "ne", + /* WINED3DSIH_NOP */ "nop", + /* WINED3DSIH_NOT */ "not", + /* WINED3DSIH_NRM */ "nrm", + /* WINED3DSIH_OR */ "or", + /* WINED3DSIH_PHASE */ "phase", + /* WINED3DSIH_POW */ "pow", + /* WINED3DSIH_RCP */ "rcp", + /* WINED3DSIH_REP */ "rep", + /* WINED3DSIH_RESINFO */ "resinfo", + /* WINED3DSIH_RET */ "ret", + /* WINED3DSIH_RETP */ "retp", + /* WINED3DSIH_ROUND_NE */ "round_ne", + /* WINED3DSIH_ROUND_NI */ "round_ni", + /* WINED3DSIH_ROUND_PI */ "round_pi", + /* WINED3DSIH_ROUND_Z */ "round_z", + /* WINED3DSIH_RSQ */ "rsq", + /* WINED3DSIH_SAMPLE */ "sample", + /* WINED3DSIH_SAMPLE_B */ "sample_b", + /* WINED3DSIH_SAMPLE_C */ "sample_c", + /* WINED3DSIH_SAMPLE_C_LZ */ "sample_c_lz", + /* WINED3DSIH_SAMPLE_GRAD */ "sample_d", + /* WINED3DSIH_SAMPLE_INFO */ "sample_info", + /* WINED3DSIH_SAMPLE_LOD */ "sample_l", + /* WINED3DSIH_SAMPLE_POS */ "sample_pos", + /* WINED3DSIH_SETP */ "setp", + /* WINED3DSIH_SGE */ "sge", + /* WINED3DSIH_SGN */ "sgn", + /* WINED3DSIH_SINCOS */ "sincos", + /* WINED3DSIH_SLT */ "slt", + /* WINED3DSIH_SQRT */ "sqrt", + /* WINED3DSIH_STORE_RAW */ "store_raw", + /* WINED3DSIH_STORE_STRUCTURED */ "store_structured", + /* WINED3DSIH_STORE_UAV_TYPED */ "store_uav_typed", + /* WINED3DSIH_SUB */ "sub", + /* WINED3DSIH_SWAPC */ "swapc", + /* WINED3DSIH_SWITCH */ "switch", + /* WINED3DSIH_SYNC */ "sync", + /* WINED3DSIH_TEX */ "texld", + /* WINED3DSIH_TEXBEM */ "texbem", + /* WINED3DSIH_TEXBEML */ "texbeml", + /* WINED3DSIH_TEXCOORD */ "texcrd", + /* WINED3DSIH_TEXDEPTH */ "texdepth", + /* WINED3DSIH_TEXDP3 */ "texdp3", + /* WINED3DSIH_TEXDP3TEX */ "texdp3tex", + /* WINED3DSIH_TEXKILL */ "texkill", + /* WINED3DSIH_TEXLDD */ "texldd", + /* WINED3DSIH_TEXLDL */ "texldl", + /* WINED3DSIH_TEXM3x2DEPTH */ "texm3x2depth", + /* WINED3DSIH_TEXM3x2PAD */ "texm3x2pad", + /* WINED3DSIH_TEXM3x2TEX */ "texm3x2tex", + /* WINED3DSIH_TEXM3x3 */ "texm3x3", + /* WINED3DSIH_TEXM3x3DIFF */ "texm3x3diff", + /* WINED3DSIH_TEXM3x3PAD */ "texm3x3pad", + /* WINED3DSIH_TEXM3x3SPEC */ "texm3x3spec", + /* WINED3DSIH_TEXM3x3TEX */ "texm3x3tex", + /* WINED3DSIH_TEXM3x3VSPEC */ "texm3x3vspec", + /* WINED3DSIH_TEXREG2AR */ "texreg2ar", + /* WINED3DSIH_TEXREG2GB */ "texreg2gb", + /* WINED3DSIH_TEXREG2RGB */ "texreg2rgb", + /* WINED3DSIH_UBFE */ "ubfe", + /* WINED3DSIH_UDIV */ "udiv", + /* WINED3DSIH_UGE */ "uge", + /* WINED3DSIH_ULT */ "ult", + /* WINED3DSIH_UMAX */ "umax", + /* WINED3DSIH_UMIN */ "umin", + /* WINED3DSIH_UMUL */ "umul", + /* WINED3DSIH_USHR */ "ushr", + /* WINED3DSIH_UTOF */ "utof", + /* WINED3DSIH_XOR */ "xor", +}; + +static const char * const semantic_names[] = +{ + /* WINED3D_DECL_USAGE_POSITION */ "SV_POSITION", + /* WINED3D_DECL_USAGE_BLEND_WEIGHT */ "BLENDWEIGHT", + /* WINED3D_DECL_USAGE_BLEND_INDICES */ "BLENDINDICES", + /* WINED3D_DECL_USAGE_NORMAL */ "NORMAL", + /* WINED3D_DECL_USAGE_PSIZE */ "PSIZE", + /* WINED3D_DECL_USAGE_TEXCOORD */ "TEXCOORD", + /* WINED3D_DECL_USAGE_TANGENT */ "TANGENT", + /* WINED3D_DECL_USAGE_BINORMAL */ "BINORMAL", + /* WINED3D_DECL_USAGE_TESS_FACTOR */ "TESSFACTOR", + /* WINED3D_DECL_USAGE_POSITIONT */ "POSITIONT", + /* WINED3D_DECL_USAGE_COLOR */ "COLOR", + /* WINED3D_DECL_USAGE_FOG */ "FOG", + /* WINED3D_DECL_USAGE_DEPTH */ "DEPTH", + /* WINED3D_DECL_USAGE_SAMPLE */ "SAMPLE", +}; + +static const struct +{ + enum wined3d_shader_input_sysval_semantic sysval_semantic; + const char *sysval_name; +} +shader_input_sysval_semantic_names[] = +{ + {WINED3D_SIV_POSITION, "position"}, + {WINED3D_SIV_CLIP_DISTANCE, "clip_distance"}, + {WINED3D_SIV_CULL_DISTANCE, "cull_distance"}, + {WINED3D_SIV_RENDER_TARGET_ARRAY_INDEX, "render_target_array_index"}, + {WINED3D_SIV_VIEWPORT_ARRAY_INDEX, "viewport_array_index"}, + {WINED3D_SIV_VERTEX_ID, "vertex_id"}, + {WINED3D_SIV_INSTANCE_ID, "instance_id"}, + {WINED3D_SIV_PRIMITIVE_ID, "primitive_id"}, + {WINED3D_SIV_IS_FRONT_FACE, "is_front_face"}, + {WINED3D_SIV_SAMPLE_INDEX, "sample_index"}, + {WINED3D_SIV_QUAD_U0_TESS_FACTOR, "finalQuadUeq0EdgeTessFactor"}, + {WINED3D_SIV_QUAD_V0_TESS_FACTOR, "finalQuadVeq0EdgeTessFactor"}, + {WINED3D_SIV_QUAD_U1_TESS_FACTOR, "finalQuadUeq1EdgeTessFactor"}, + {WINED3D_SIV_QUAD_V1_TESS_FACTOR, "finalQuadVeq1EdgeTessFactor"}, + {WINED3D_SIV_QUAD_U_INNER_TESS_FACTOR, "finalQuadUInsideTessFactor"}, + {WINED3D_SIV_QUAD_V_INNER_TESS_FACTOR, "finalQuadVInsideTessFactor"}, + {WINED3D_SIV_TRIANGLE_U_TESS_FACTOR, "finalTriUeq0EdgeTessFactor"}, + {WINED3D_SIV_TRIANGLE_V_TESS_FACTOR, "finalTriVeq0EdgeTessFactor"}, + {WINED3D_SIV_TRIANGLE_W_TESS_FACTOR, "finalTriWeq0EdgeTessFactor"}, + {WINED3D_SIV_TRIANGLE_INNER_TESS_FACTOR, "finalTriInsideTessFactor"}, + {WINED3D_SIV_LINE_DETAIL_TESS_FACTOR, "finalLineDetailTessFactor"}, + {WINED3D_SIV_LINE_DENSITY_TESS_FACTOR, "finalLineDensityTessFactor"}, +}; + +static void shader_dump_src_param(struct wined3d_string_buffer *buffer, + const struct wined3d_shader_src_param *param, const struct wined3d_shader_version *shader_version); + +const char *debug_d3dshaderinstructionhandler(enum WINED3D_SHADER_INSTRUCTION_HANDLER handler_idx) +{ + if (handler_idx >= ARRAY_SIZE(shader_opcode_names)) + return wine_dbg_sprintf("UNRECOGNIZED(%#x)", handler_idx); + + return shader_opcode_names[handler_idx]; +} + +static const char *shader_semantic_name_from_usage(enum wined3d_decl_usage usage) +{ + if (usage >= ARRAY_SIZE(semantic_names)) + { + FIXME("Unrecognized usage %#x.\n", usage); + return "UNRECOGNIZED"; + } + + return semantic_names[usage]; +} + +static enum wined3d_decl_usage shader_usage_from_semantic_name(const char *name) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(semantic_names); ++i) + { + if (!strcmp(name, semantic_names[i])) + return i; + } + + return ~0U; +} + +static enum wined3d_sysval_semantic shader_sysval_semantic_from_usage(enum wined3d_decl_usage usage) +{ + switch (usage) + { + case WINED3D_DECL_USAGE_POSITION: + return WINED3D_SV_POSITION; + default: + return 0; + } +} + +BOOL shader_match_semantic(const char *semantic_name, enum wined3d_decl_usage usage) +{ + return !strcmp(semantic_name, shader_semantic_name_from_usage(usage)); +} + +static void shader_signature_from_semantic(struct wined3d_shader_signature_element *e, + const struct wined3d_shader_semantic *s) +{ + e->semantic_name = shader_semantic_name_from_usage(s->usage); + e->semantic_idx = s->usage_idx; + e->stream_idx = 0; + e->sysval_semantic = shader_sysval_semantic_from_usage(s->usage); + e->component_type = WINED3D_TYPE_FLOAT; + e->register_idx = s->reg.reg.idx[0].offset; + e->mask = s->reg.write_mask; +} + +static void shader_signature_from_usage(struct wined3d_shader_signature_element *e, + enum wined3d_decl_usage usage, UINT usage_idx, UINT reg_idx, DWORD write_mask) +{ + e->semantic_name = shader_semantic_name_from_usage(usage); + e->semantic_idx = usage_idx; + e->stream_idx = 0; + e->sysval_semantic = shader_sysval_semantic_from_usage(usage); + e->component_type = WINED3D_TYPE_FLOAT; + e->register_idx = reg_idx; + e->mask = write_mask; +} + +static const struct wined3d_shader_frontend *shader_select_frontend(enum wined3d_shader_byte_code_format format) +{ + switch (format) + { + case WINED3D_SHADER_BYTE_CODE_FORMAT_SM1: + return &sm1_shader_frontend; + + case WINED3D_SHADER_BYTE_CODE_FORMAT_SM4: + return &sm4_shader_frontend; + + default: + WARN("Invalid byte code format %#x specified.\n", format); + return NULL; + } +} + +void string_buffer_clear(struct wined3d_string_buffer *buffer) +{ + buffer->buffer[0] = '\0'; + buffer->content_size = 0; +} + +BOOL string_buffer_init(struct wined3d_string_buffer *buffer) +{ + buffer->buffer_size = 32; + if (!(buffer->buffer = heap_alloc(buffer->buffer_size))) + { + ERR("Failed to allocate shader buffer memory.\n"); + return FALSE; + } + + string_buffer_clear(buffer); + return TRUE; +} + +void string_buffer_free(struct wined3d_string_buffer *buffer) +{ + heap_free(buffer->buffer); +} + +BOOL string_buffer_resize(struct wined3d_string_buffer *buffer, int rc) +{ + char *new_buffer; + unsigned int new_buffer_size = buffer->buffer_size * 2; + + while (rc > 0 && (unsigned int)rc >= new_buffer_size - buffer->content_size) + new_buffer_size *= 2; + if (!(new_buffer = heap_realloc(buffer->buffer, new_buffer_size))) + { + ERR("Failed to grow buffer.\n"); + buffer->buffer[buffer->content_size] = '\0'; + return FALSE; + } + buffer->buffer = new_buffer; + buffer->buffer_size = new_buffer_size; + return TRUE; +} + +int shader_vaddline(struct wined3d_string_buffer *buffer, const char *format, va_list args) +{ + unsigned int rem; + int rc; + + rem = buffer->buffer_size - buffer->content_size; + rc = vsnprintf(&buffer->buffer[buffer->content_size], rem, format, args); + if (rc < 0 /* C89 */ || (unsigned int)rc >= rem /* C99 */) + return rc; + + buffer->content_size += rc; + return 0; +} + +int shader_addline(struct wined3d_string_buffer *buffer, const char *format, ...) +{ + va_list args; + int ret; + + for (;;) + { + va_start(args, format); + ret = shader_vaddline(buffer, format, args); + va_end(args); + if (!ret) + return ret; + if (!string_buffer_resize(buffer, ret)) + return -1; + } +} + +struct wined3d_string_buffer *string_buffer_get(struct wined3d_string_buffer_list *list) +{ + struct wined3d_string_buffer *buffer; + + if (list_empty(&list->list)) + { + buffer = heap_alloc(sizeof(*buffer)); + if (!buffer || !string_buffer_init(buffer)) + { + ERR("Couldn't allocate buffer for temporary string.\n"); + heap_free(buffer); + return NULL; + } + } + else + { + buffer = LIST_ENTRY(list_head(&list->list), struct wined3d_string_buffer, entry); + list_remove(&buffer->entry); + } + string_buffer_clear(buffer); + return buffer; +} + +static int string_buffer_vsprintf(struct wined3d_string_buffer *buffer, const char *format, va_list args) +{ + if (!buffer) + return 0; + string_buffer_clear(buffer); + return shader_vaddline(buffer, format, args); +} + +void string_buffer_sprintf(struct wined3d_string_buffer *buffer, const char *format, ...) +{ + va_list args; + int ret; + + for (;;) + { + va_start(args, format); + ret = string_buffer_vsprintf(buffer, format, args); + va_end(args); + if (!ret) + return; + if (!string_buffer_resize(buffer, ret)) + return; + } +} + +void string_buffer_release(struct wined3d_string_buffer_list *list, struct wined3d_string_buffer *buffer) +{ + if (!buffer) + return; + list_add_head(&list->list, &buffer->entry); +} + +void string_buffer_list_init(struct wined3d_string_buffer_list *list) +{ + list_init(&list->list); +} + +void string_buffer_list_cleanup(struct wined3d_string_buffer_list *list) +{ + struct wined3d_string_buffer *buffer, *buffer_next; + + LIST_FOR_EACH_ENTRY_SAFE(buffer, buffer_next, &list->list, struct wined3d_string_buffer, entry) + { + string_buffer_free(buffer); + heap_free(buffer); + } + list_init(&list->list); +} + +/* Convert floating point offset relative to a register file to an absolute + * offset for float constants. */ +static unsigned int shader_get_float_offset(enum wined3d_shader_register_type register_type, UINT register_idx) +{ + switch (register_type) + { + case WINED3DSPR_CONST: return register_idx; + case WINED3DSPR_CONST2: return 2048 + register_idx; + case WINED3DSPR_CONST3: return 4096 + register_idx; + case WINED3DSPR_CONST4: return 6144 + register_idx; + default: + FIXME("Unsupported register type: %u.\n", register_type); + return register_idx; + } +} + +static void shader_delete_constant_list(struct list *clist) +{ + struct wined3d_shader_lconst *constant, *constant_next; + + LIST_FOR_EACH_ENTRY_SAFE(constant, constant_next, clist, struct wined3d_shader_lconst, entry) + heap_free(constant); + list_init(clist); +} + +static void shader_set_limits(struct wined3d_shader *shader) +{ + static const struct limits_entry + { + unsigned int min_version; + unsigned int max_version; + struct wined3d_shader_limits limits; + } + vs_limits[] = + { + /* min_version, max_version, sampler, constant_int, constant_float, constant_bool, packed_output, packed_input */ + {WINED3D_SHADER_VERSION(1, 0), WINED3D_SHADER_VERSION(1, 1), { 0, 0, 256, 0, 12, 0}}, + {WINED3D_SHADER_VERSION(2, 0), WINED3D_SHADER_VERSION(2, 1), { 0, 16, 256, 16, 12, 0}}, + /* DX10 cards on Windows advertise a D3D9 constant limit of 256 + * even though they are capable of supporting much more (GL + * drivers advertise 1024). d3d9.dll and d3d8.dll clamp the + * wined3d-advertised maximum. Clamp the constant limit for <= 3.0 + * shaders to 256. */ + {WINED3D_SHADER_VERSION(3, 0), WINED3D_SHADER_VERSION(3, 0), { 4, 16, 256, 16, 12, 0}}, + {WINED3D_SHADER_VERSION(4, 0), WINED3D_SHADER_VERSION(4, 0), {16, 0, 0, 0, 16, 0}}, + {WINED3D_SHADER_VERSION(4, 1), WINED3D_SHADER_VERSION(5, 0), {16, 0, 0, 0, 32, 0}}, + {0} + }, + hs_limits[] = + { + /* min_version, max_version, sampler, constant_int, constant_float, constant_bool, packed_output, packet_input */ + {WINED3D_SHADER_VERSION(5, 0), WINED3D_SHADER_VERSION(5, 0), {16, 0, 0, 0, 32, 32}}, + }, + ds_limits[] = + { + /* min_version, max_version, sampler, constant_int, constant_float, constant_bool, packed_output, packet_input */ + {WINED3D_SHADER_VERSION(5, 0), WINED3D_SHADER_VERSION(5, 0), {16, 0, 0, 0, 32, 32}}, + }, + gs_limits[] = + { + /* min_version, max_version, sampler, constant_int, constant_float, constant_bool, packed_output, packed_input */ + {WINED3D_SHADER_VERSION(4, 0), WINED3D_SHADER_VERSION(4, 0), {16, 0, 0, 0, 32, 16}}, + {WINED3D_SHADER_VERSION(4, 1), WINED3D_SHADER_VERSION(5, 0), {16, 0, 0, 0, 32, 32}}, + {0} + }, + ps_limits[] = + { + /* min_version, max_version, sampler, constant_int, constant_float, constant_bool, packed_output, packed_input */ + {WINED3D_SHADER_VERSION(1, 0), WINED3D_SHADER_VERSION(1, 3), { 4, 0, 8, 0, 0, 0}}, + {WINED3D_SHADER_VERSION(1, 4), WINED3D_SHADER_VERSION(1, 4), { 6, 0, 8, 0, 0, 0}}, + {WINED3D_SHADER_VERSION(2, 0), WINED3D_SHADER_VERSION(2, 0), {16, 0, 32, 0, 0, 0}}, + {WINED3D_SHADER_VERSION(2, 1), WINED3D_SHADER_VERSION(2, 1), {16, 16, 32, 16, 0, 0}}, + {WINED3D_SHADER_VERSION(3, 0), WINED3D_SHADER_VERSION(3, 0), {16, 16, 224, 16, 0, 10}}, + {WINED3D_SHADER_VERSION(4, 0), WINED3D_SHADER_VERSION(5, 0), {16, 0, 0, 0, 0, 32}}, + {0} + }, + cs_limits[] = + { + /* min_version, max_version, sampler, constant_int, constant_float, constant_bool, packed_output, packed_input */ + {WINED3D_SHADER_VERSION(5, 0), WINED3D_SHADER_VERSION(5, 0), {16, 0, 0, 0, 0, 0}}, + }; + const struct limits_entry *limits_array; + DWORD shader_version = WINED3D_SHADER_VERSION(shader->reg_maps.shader_version.major, + shader->reg_maps.shader_version.minor); + int i = 0; + + switch (shader->reg_maps.shader_version.type) + { + default: + FIXME("Unexpected shader type %u found.\n", shader->reg_maps.shader_version.type); + /* Fall-through. */ + case WINED3D_SHADER_TYPE_VERTEX: + limits_array = vs_limits; + break; + case WINED3D_SHADER_TYPE_HULL: + limits_array = hs_limits; + break; + case WINED3D_SHADER_TYPE_DOMAIN: + limits_array = ds_limits; + break; + case WINED3D_SHADER_TYPE_GEOMETRY: + limits_array = gs_limits; + break; + case WINED3D_SHADER_TYPE_PIXEL: + limits_array = ps_limits; + break; + case WINED3D_SHADER_TYPE_COMPUTE: + limits_array = cs_limits; + break; + } + + while (limits_array[i].min_version && limits_array[i].min_version <= shader_version) + { + if (shader_version <= limits_array[i].max_version) + { + shader->limits = &limits_array[i].limits; + break; + } + ++i; + } + if (!shader->limits) + { + FIXME("Unexpected shader version \"%u.%u\".\n", + shader->reg_maps.shader_version.major, + shader->reg_maps.shader_version.minor); + shader->limits = &limits_array[max(0, i - 1)].limits; + } +} + +static BOOL shader_record_register_usage(struct wined3d_shader *shader, struct wined3d_shader_reg_maps *reg_maps, + const struct wined3d_shader_register *reg, enum wined3d_shader_type shader_type, unsigned int constf_size) +{ + switch (reg->type) + { + case WINED3DSPR_TEXTURE: /* WINED3DSPR_ADDR */ + if (shader_type == WINED3D_SHADER_TYPE_PIXEL) + reg_maps->texcoord |= 1u << reg->idx[0].offset; + else + reg_maps->address |= 1u << reg->idx[0].offset; + break; + + case WINED3DSPR_TEMP: + reg_maps->temporary |= 1u << reg->idx[0].offset; + break; + + case WINED3DSPR_INPUT: + if (reg->idx[0].rel_addr) + reg_maps->input_rel_addressing = 1; + if (shader_type == WINED3D_SHADER_TYPE_PIXEL) + { + /* If relative addressing is used, we must assume that all + * registers are used. Even if it is a construct like v3[aL], + * we can't assume that v0, v1 and v2 aren't read because aL + * can be negative. */ + if (reg->idx[0].rel_addr) + shader->u.ps.input_reg_used = ~0u; + else + shader->u.ps.input_reg_used |= 1u << reg->idx[0].offset; + } + else + { + reg_maps->input_registers |= 1u << reg->idx[0].offset; + } + break; + + case WINED3DSPR_RASTOUT: + if (reg->idx[0].offset == 1) + reg_maps->fog = 1; + if (reg->idx[0].offset == 2) + reg_maps->point_size = 1; + break; + + case WINED3DSPR_MISCTYPE: + if (shader_type == WINED3D_SHADER_TYPE_PIXEL) + { + if (!reg->idx[0].offset) + reg_maps->vpos = 1; + else if (reg->idx[0].offset == 1) + reg_maps->usesfacing = 1; + } + break; + + case WINED3DSPR_CONST: + if (reg->idx[0].rel_addr) + { + if (reg->idx[0].offset < reg_maps->min_rel_offset) + reg_maps->min_rel_offset = reg->idx[0].offset; + if (reg->idx[0].offset > reg_maps->max_rel_offset) + reg_maps->max_rel_offset = reg->idx[0].offset; + reg_maps->usesrelconstF = TRUE; + } + else + { + if (reg->idx[0].offset >= min(shader->limits->constant_float, constf_size)) + { + WARN("Shader using float constant %u which is not supported.\n", reg->idx[0].offset); + return FALSE; + } + else + { + wined3d_insert_bits(reg_maps->constf, reg->idx[0].offset, 1, 0x1); + } + } + break; + + case WINED3DSPR_CONSTINT: + if (reg->idx[0].offset >= shader->limits->constant_int) + { + WARN("Shader using integer constant %u which is not supported.\n", reg->idx[0].offset); + return FALSE; + } + else + { + reg_maps->integer_constants |= (1u << reg->idx[0].offset); + } + break; + + case WINED3DSPR_CONSTBOOL: + if (reg->idx[0].offset >= shader->limits->constant_bool) + { + WARN("Shader using bool constant %u which is not supported.\n", reg->idx[0].offset); + return FALSE; + } + else + { + reg_maps->boolean_constants |= (1u << reg->idx[0].offset); + } + break; + + case WINED3DSPR_COLOROUT: + reg_maps->rt_mask |= (1u << reg->idx[0].offset); + break; + + case WINED3DSPR_OUTCONTROLPOINT: + reg_maps->vocp = 1; + break; + + case WINED3DSPR_SAMPLEMASK: + reg_maps->sample_mask = 1; + break; + + default: + TRACE("Not recording register of type %#x and [%#x][%#x].\n", + reg->type, reg->idx[0].offset, reg->idx[1].offset); + break; + } + return TRUE; +} + +static void shader_record_sample(struct wined3d_shader_reg_maps *reg_maps, + unsigned int resource_idx, unsigned int sampler_idx, unsigned int bind_idx) +{ + struct wined3d_shader_sampler_map_entry *entries, *entry; + struct wined3d_shader_sampler_map *map; + unsigned int i; + + map = ®_maps->sampler_map; + entries = map->entries; + for (i = 0; i < map->count; ++i) + { + if (entries[i].resource_idx == resource_idx && entries[i].sampler_idx == sampler_idx) + return; + } + + if (!map->size) + { + if (!(entries = heap_calloc(4, sizeof(*entries)))) + { + ERR("Failed to allocate sampler map entries.\n"); + return; + } + map->size = 4; + map->entries = entries; + } + else if (map->count == map->size) + { + size_t new_size = map->size * 2; + + if (sizeof(*entries) * new_size <= sizeof(*entries) * map->size + || !(entries = heap_realloc(entries, sizeof(*entries) * new_size))) + { + ERR("Failed to resize sampler map entries.\n"); + return; + } + map->size = new_size; + map->entries = entries; + } + + entry = &entries[map->count++]; + entry->resource_idx = resource_idx; + entry->sampler_idx = sampler_idx; + entry->bind_idx = bind_idx; +} + +static unsigned int get_instr_extra_regcount(enum WINED3D_SHADER_INSTRUCTION_HANDLER instr, unsigned int param) +{ + switch (instr) + { + case WINED3DSIH_M4x4: + case WINED3DSIH_M3x4: + return param == 1 ? 3 : 0; + + case WINED3DSIH_M4x3: + case WINED3DSIH_M3x3: + return param == 1 ? 2 : 0; + + case WINED3DSIH_M3x2: + return param == 1 ? 1 : 0; + + default: + return 0; + } +} + +static HRESULT shader_reg_maps_add_tgsm(struct wined3d_shader_reg_maps *reg_maps, + unsigned int register_idx, unsigned int size, unsigned int stride) +{ + struct wined3d_shader_tgsm *tgsm; + + if (register_idx >= MAX_TGSM_REGISTERS) + { + ERR("Invalid TGSM register index %u.\n", register_idx); + return S_OK; + } + if (reg_maps->shader_version.type != WINED3D_SHADER_TYPE_COMPUTE) + { + FIXME("TGSM declarations are allowed only in compute shaders.\n"); + return S_OK; + } + + if (!wined3d_array_reserve((void **)®_maps->tgsm, ®_maps->tgsm_capacity, + register_idx + 1, sizeof(*reg_maps->tgsm))) + return E_OUTOFMEMORY; + + reg_maps->tgsm_count = max(register_idx + 1, reg_maps->tgsm_count); + tgsm = ®_maps->tgsm[register_idx]; + tgsm->size = size; + tgsm->stride = stride; + return S_OK; +} + +static HRESULT shader_record_shader_phase(struct wined3d_shader *shader, + struct wined3d_shader_phase **current_phase, const struct wined3d_shader_instruction *ins, + const DWORD *current_instruction_ptr, const DWORD *previous_instruction_ptr) +{ + struct wined3d_shader_phase *phase; + + if ((phase = *current_phase)) + { + phase->end = previous_instruction_ptr; + *current_phase = NULL; + } + + if (shader->reg_maps.shader_version.type != WINED3D_SHADER_TYPE_HULL) + { + ERR("Unexpected shader type %s.\n", debug_shader_type(shader->reg_maps.shader_version.type)); + return E_FAIL; + } + + switch (ins->handler_idx) + { + case WINED3DSIH_HS_CONTROL_POINT_PHASE: + if (shader->u.hs.phases.control_point) + { + FIXME("Multiple control point phases.\n"); + heap_free(shader->u.hs.phases.control_point); + } + if (!(shader->u.hs.phases.control_point = heap_alloc_zero(sizeof(*shader->u.hs.phases.control_point)))) + return E_OUTOFMEMORY; + phase = shader->u.hs.phases.control_point; + break; + case WINED3DSIH_HS_FORK_PHASE: + if (!wined3d_array_reserve((void **)&shader->u.hs.phases.fork, + &shader->u.hs.phases.fork_size, shader->u.hs.phases.fork_count + 1, + sizeof(*shader->u.hs.phases.fork))) + return E_OUTOFMEMORY; + phase = &shader->u.hs.phases.fork[shader->u.hs.phases.fork_count++]; + break; + case WINED3DSIH_HS_JOIN_PHASE: + if (!wined3d_array_reserve((void **)&shader->u.hs.phases.join, + &shader->u.hs.phases.join_size, shader->u.hs.phases.join_count + 1, + sizeof(*shader->u.hs.phases.join))) + return E_OUTOFMEMORY; + phase = &shader->u.hs.phases.join[shader->u.hs.phases.join_count++]; + break; + default: + ERR("Unexpected opcode %s.\n", debug_d3dshaderinstructionhandler(ins->handler_idx)); + return E_FAIL; + } + + phase->start = current_instruction_ptr; + *current_phase = phase; + + return WINED3D_OK; +} + +static HRESULT shader_calculate_clip_or_cull_distance_mask( + const struct wined3d_shader_signature_element *e, unsigned int *mask) +{ + /* Clip and cull distances are packed in 4 component registers. 0 and 1 are + * the only allowed semantic indices. + */ + if (e->semantic_idx >= WINED3D_MAX_CLIP_DISTANCES / 4) + { + *mask = 0; + WARN("Invalid clip/cull distance index %u.\n", e->semantic_idx); + return WINED3DERR_INVALIDCALL; + } + + *mask = (e->mask & WINED3DSP_WRITEMASK_ALL) << (4 * e->semantic_idx); + return WINED3D_OK; +} + +static void wined3d_insert_interpolation_mode(DWORD *packed_interpolation_mode, + unsigned int register_idx, enum wined3d_shader_interpolation_mode mode) +{ + if (mode > WINED3DSIM_LINEAR_NOPERSPECTIVE_SAMPLE) + FIXME("Unexpected interpolation mode %#x.\n", mode); + + wined3d_insert_bits(packed_interpolation_mode, + register_idx * WINED3D_PACKED_INTERPOLATION_BIT_COUNT, WINED3D_PACKED_INTERPOLATION_BIT_COUNT, mode); +} + +static HRESULT shader_scan_output_signature(struct wined3d_shader *shader) +{ + const struct wined3d_shader_signature *output_signature = &shader->output_signature; + struct wined3d_shader_reg_maps *reg_maps = &shader->reg_maps; + unsigned int i; + HRESULT hr; + + for (i = 0; i < output_signature->element_count; ++i) + { + const struct wined3d_shader_signature_element *e = &output_signature->elements[i]; + unsigned int mask; + + reg_maps->output_registers |= 1u << e->register_idx; + if (e->sysval_semantic == WINED3D_SV_CLIP_DISTANCE) + { + if (FAILED(hr = shader_calculate_clip_or_cull_distance_mask(e, &mask))) + return hr; + reg_maps->clip_distance_mask |= mask; + } + else if (e->sysval_semantic == WINED3D_SV_CULL_DISTANCE) + { + if (FAILED(hr = shader_calculate_clip_or_cull_distance_mask(e, &mask))) + return hr; + reg_maps->cull_distance_mask |= mask; + } + else if (e->sysval_semantic == WINED3D_SV_VIEWPORT_ARRAY_INDEX) + { + reg_maps->viewport_array = 1; + } + } + + return WINED3D_OK; +} + +/* Note that this does not count the loop register as an address register. */ +static HRESULT shader_get_registers_used(struct wined3d_shader *shader, DWORD constf_size) +{ + struct wined3d_shader_signature_element input_signature_elements[max(MAX_ATTRIBS, MAX_REG_INPUT)]; + struct wined3d_shader_signature_element output_signature_elements[MAX_REG_OUTPUT]; + struct wined3d_shader_signature *output_signature = &shader->output_signature; + struct wined3d_shader_signature *input_signature = &shader->input_signature; + struct wined3d_shader_reg_maps *reg_maps = &shader->reg_maps; + const struct wined3d_shader_frontend *fe = shader->frontend; + unsigned int cur_loop_depth = 0, max_loop_depth = 0; + struct wined3d_shader_version shader_version; + struct wined3d_shader_phase *phase = NULL; + const DWORD *ptr, *prev_ins, *current_ins; + void *fe_data = shader->frontend_data; + unsigned int i; + HRESULT hr; + + memset(reg_maps, 0, sizeof(*reg_maps)); + memset(input_signature_elements, 0, sizeof(input_signature_elements)); + memset(output_signature_elements, 0, sizeof(output_signature_elements)); + reg_maps->min_rel_offset = ~0U; + list_init(®_maps->indexable_temps); + + fe->shader_read_header(fe_data, &ptr, &shader_version); + prev_ins = current_ins = ptr; + reg_maps->shader_version = shader_version; + + shader_set_limits(shader); + + if (!(reg_maps->constf = heap_calloc(((min(shader->limits->constant_float, constf_size) + 31) / 32), + sizeof(*reg_maps->constf)))) + { + ERR("Failed to allocate constant map memory.\n"); + return E_OUTOFMEMORY; + } + + while (!fe->shader_is_end(fe_data, &ptr)) + { + struct wined3d_shader_instruction ins; + + current_ins = ptr; + /* Fetch opcode. */ + fe->shader_read_instruction(fe_data, &ptr, &ins); + + /* Unhandled opcode, and its parameters. */ + if (ins.handler_idx == WINED3DSIH_TABLE_SIZE) + { + WARN("Encountered unrecognised or invalid instruction.\n"); + return WINED3DERR_INVALIDCALL; + } + + /* Handle declarations. */ + if (ins.handler_idx == WINED3DSIH_DCL + || ins.handler_idx == WINED3DSIH_DCL_UAV_TYPED) + { + struct wined3d_shader_semantic *semantic = &ins.declaration.semantic; + unsigned int reg_idx = semantic->reg.reg.idx[0].offset; + + switch (semantic->reg.reg.type) + { + /* Mark input registers used. */ + case WINED3DSPR_INPUT: + if (reg_idx >= MAX_REG_INPUT) + { + ERR("Invalid input register index %u.\n", reg_idx); + break; + } + if (shader_version.type == WINED3D_SHADER_TYPE_PIXEL && shader_version.major == 3 + && semantic->usage == WINED3D_DECL_USAGE_POSITION && !semantic->usage_idx) + return WINED3DERR_INVALIDCALL; + reg_maps->input_registers |= 1u << reg_idx; + shader_signature_from_semantic(&input_signature_elements[reg_idx], semantic); + break; + + /* Vertex shader: mark 3.0 output registers used, save token. */ + case WINED3DSPR_OUTPUT: + if (reg_idx >= MAX_REG_OUTPUT) + { + ERR("Invalid output register index %u.\n", reg_idx); + break; + } + reg_maps->output_registers |= 1u << reg_idx; + shader_signature_from_semantic(&output_signature_elements[reg_idx], semantic); + if (semantic->usage == WINED3D_DECL_USAGE_FOG) + reg_maps->fog = 1; + if (semantic->usage == WINED3D_DECL_USAGE_PSIZE) + reg_maps->point_size = 1; + break; + + case WINED3DSPR_SAMPLER: + shader_record_sample(reg_maps, reg_idx, reg_idx, reg_idx); + case WINED3DSPR_RESOURCE: + if (reg_idx >= ARRAY_SIZE(reg_maps->resource_info)) + { + ERR("Invalid resource index %u.\n", reg_idx); + break; + } + reg_maps->resource_info[reg_idx].type = semantic->resource_type; + reg_maps->resource_info[reg_idx].data_type = semantic->resource_data_type; + wined3d_bitmap_set(reg_maps->resource_map, reg_idx); + break; + + case WINED3DSPR_UAV: + if (reg_idx >= ARRAY_SIZE(reg_maps->uav_resource_info)) + { + ERR("Invalid UAV resource index %u.\n", reg_idx); + break; + } + reg_maps->uav_resource_info[reg_idx].type = semantic->resource_type; + reg_maps->uav_resource_info[reg_idx].data_type = semantic->resource_data_type; + if (ins.flags) + FIXME("Ignoring typed UAV flags %#x.\n", ins.flags); + break; + + default: + TRACE("Not recording DCL register type %#x.\n", semantic->reg.reg.type); + break; + } + } + else if (ins.handler_idx == WINED3DSIH_DCL_CONSTANT_BUFFER) + { + struct wined3d_shader_register *reg = &ins.declaration.src.reg; + if (reg->idx[0].offset >= WINED3D_MAX_CBS) + { + ERR("Invalid CB index %u.\n", reg->idx[0].offset); + } + else + { + reg_maps->cb_sizes[reg->idx[0].offset] = reg->idx[1].offset; + wined3d_bitmap_set(®_maps->cb_map, reg->idx[0].offset); + } + } + else if (ins.handler_idx == WINED3DSIH_DCL_GLOBAL_FLAGS) + { + if (ins.flags & WINED3DSGF_FORCE_EARLY_DEPTH_STENCIL) + { + if (shader_version.type == WINED3D_SHADER_TYPE_PIXEL) + shader->u.ps.force_early_depth_stencil = TRUE; + else + FIXME("Invalid instruction %#x for shader type %#x.\n", + ins.handler_idx, shader_version.type); + } + else + { + WARN("Ignoring global flags %#x.\n", ins.flags); + } + } + else if (ins.handler_idx == WINED3DSIH_DCL_GS_INSTANCES) + { + if (shader_version.type == WINED3D_SHADER_TYPE_GEOMETRY) + shader->u.gs.instance_count = ins.declaration.count; + else + FIXME("Invalid instruction %#x for shader type %#x.\n", + ins.handler_idx, shader_version.type); + } + else if (ins.handler_idx == WINED3DSIH_DCL_HS_FORK_PHASE_INSTANCE_COUNT + || ins.handler_idx == WINED3DSIH_DCL_HS_JOIN_PHASE_INSTANCE_COUNT) + { + if (phase) + phase->instance_count = ins.declaration.count; + else + FIXME("Instruction %s outside of shader phase.\n", + debug_d3dshaderinstructionhandler(ins.handler_idx)); + } + else if (ins.handler_idx == WINED3DSIH_DCL_IMMEDIATE_CONSTANT_BUFFER) + { + if (reg_maps->icb) + FIXME("Multiple immediate constant buffers.\n"); + reg_maps->icb = ins.declaration.icb; + } + else if (ins.handler_idx == WINED3DSIH_DCL_INDEXABLE_TEMP) + { + if (phase) + { + FIXME("Indexable temporary registers not supported.\n"); + } + else + { + struct wined3d_shader_indexable_temp *reg; + + if (!(reg = heap_alloc(sizeof(*reg)))) + return E_OUTOFMEMORY; + + *reg = ins.declaration.indexable_temp; + list_add_tail(®_maps->indexable_temps, ®->entry); + } + } + else if (ins.handler_idx == WINED3DSIH_DCL_INPUT_PRIMITIVE) + { + if (shader_version.type == WINED3D_SHADER_TYPE_GEOMETRY) + shader->u.gs.input_type = ins.declaration.primitive_type.type; + else + FIXME("Invalid instruction %#x for shader type %#x.\n", + ins.handler_idx, shader_version.type); + } + else if (ins.handler_idx == WINED3DSIH_DCL_INPUT_PS) + { + unsigned int reg_idx = ins.declaration.dst.reg.idx[0].offset; + if (reg_idx >= MAX_REG_INPUT) + { + ERR("Invalid register index %u.\n", reg_idx); + break; + } + if (shader_version.type == WINED3D_SHADER_TYPE_PIXEL) + wined3d_insert_interpolation_mode(shader->u.ps.interpolation_mode, reg_idx, ins.flags); + else + FIXME("Invalid instruction %#x for shader type %#x.\n", + ins.handler_idx, shader_version.type); + } + else if (ins.handler_idx == WINED3DSIH_DCL_OUTPUT) + { + if (ins.declaration.dst.reg.type == WINED3DSPR_DEPTHOUT + || ins.declaration.dst.reg.type == WINED3DSPR_DEPTHOUTGE + || ins.declaration.dst.reg.type == WINED3DSPR_DEPTHOUTLE) + { + if (shader_version.type == WINED3D_SHADER_TYPE_PIXEL) + shader->u.ps.depth_output = ins.declaration.dst.reg.type; + else + FIXME("Invalid instruction %#x for shader type %#x.\n", + ins.handler_idx, shader_version.type); + } + } + else if (ins.handler_idx == WINED3DSIH_DCL_OUTPUT_CONTROL_POINT_COUNT) + { + if (shader_version.type == WINED3D_SHADER_TYPE_HULL) + shader->u.hs.output_vertex_count = ins.declaration.count; + else + FIXME("Invalid instruction %#x for shader type %#x.\n", ins.handler_idx, shader_version.type); + } + else if (ins.handler_idx == WINED3DSIH_DCL_OUTPUT_TOPOLOGY) + { + if (shader_version.type == WINED3D_SHADER_TYPE_GEOMETRY) + shader->u.gs.output_type = ins.declaration.primitive_type.type; + else + FIXME("Invalid instruction %#x for shader type %#x.\n", + ins.handler_idx, shader_version.type); + } + else if (ins.handler_idx == WINED3DSIH_DCL_RESOURCE_RAW) + { + unsigned int reg_idx = ins.declaration.dst.reg.idx[0].offset; + if (reg_idx >= ARRAY_SIZE(reg_maps->resource_info)) + { + ERR("Invalid resource index %u.\n", reg_idx); + break; + } + reg_maps->resource_info[reg_idx].type = WINED3D_SHADER_RESOURCE_BUFFER; + reg_maps->resource_info[reg_idx].data_type = WINED3D_DATA_UINT; + reg_maps->resource_info[reg_idx].flags = WINED3D_VIEW_BUFFER_RAW; + wined3d_bitmap_set(reg_maps->resource_map, reg_idx); + } + else if (ins.handler_idx == WINED3DSIH_DCL_RESOURCE_STRUCTURED) + { + unsigned int reg_idx = ins.declaration.structured_resource.reg.reg.idx[0].offset; + if (reg_idx >= ARRAY_SIZE(reg_maps->resource_info)) + { + ERR("Invalid resource index %u.\n", reg_idx); + break; + } + reg_maps->resource_info[reg_idx].type = WINED3D_SHADER_RESOURCE_BUFFER; + reg_maps->resource_info[reg_idx].data_type = WINED3D_DATA_UINT; + reg_maps->resource_info[reg_idx].flags = 0; + reg_maps->resource_info[reg_idx].stride = ins.declaration.structured_resource.byte_stride / 4; + wined3d_bitmap_set(reg_maps->resource_map, reg_idx); + } + else if (ins.handler_idx == WINED3DSIH_DCL_SAMPLER) + { + if (ins.flags & WINED3DSI_SAMPLER_COMPARISON_MODE) + reg_maps->sampler_comparison_mode |= (1u << ins.declaration.dst.reg.idx[0].offset); + } + else if (ins.handler_idx == WINED3DSIH_DCL_TEMPS) + { + if (phase) + phase->temporary_count = ins.declaration.count; + else + reg_maps->temporary_count = ins.declaration.count; + } + else if (ins.handler_idx == WINED3DSIH_DCL_TESSELLATOR_DOMAIN) + { + if (shader_version.type == WINED3D_SHADER_TYPE_DOMAIN) + shader->u.ds.tessellator_domain = ins.declaration.tessellator_domain; + else if (shader_version.type != WINED3D_SHADER_TYPE_HULL) + FIXME("Invalid instruction %#x for shader type %#x.\n", ins.handler_idx, shader_version.type); + } + else if (ins.handler_idx == WINED3DSIH_DCL_TESSELLATOR_OUTPUT_PRIMITIVE) + { + if (shader_version.type == WINED3D_SHADER_TYPE_HULL) + shader->u.hs.tessellator_output_primitive = ins.declaration.tessellator_output_primitive; + else + FIXME("Invalid instruction %#x for shader type %#x.\n", ins.handler_idx, shader_version.type); + } + else if (ins.handler_idx == WINED3DSIH_DCL_TESSELLATOR_PARTITIONING) + { + if (shader_version.type == WINED3D_SHADER_TYPE_HULL) + shader->u.hs.tessellator_partitioning = ins.declaration.tessellator_partitioning; + else + FIXME("Invalid instruction %#x for shader type %#x.\n", ins.handler_idx, shader_version.type); + } + else if (ins.handler_idx == WINED3DSIH_DCL_TGSM_RAW) + { + if (FAILED(hr = shader_reg_maps_add_tgsm(reg_maps, ins.declaration.tgsm_raw.reg.reg.idx[0].offset, + ins.declaration.tgsm_raw.byte_count / 4, 0))) + return hr; + } + else if (ins.handler_idx == WINED3DSIH_DCL_TGSM_STRUCTURED) + { + unsigned int stride = ins.declaration.tgsm_structured.byte_stride / 4; + unsigned int size = stride * ins.declaration.tgsm_structured.structure_count; + if (FAILED(hr = shader_reg_maps_add_tgsm(reg_maps, + ins.declaration.tgsm_structured.reg.reg.idx[0].offset, size, stride))) + return hr; + } + else if (ins.handler_idx == WINED3DSIH_DCL_THREAD_GROUP) + { + if (shader_version.type == WINED3D_SHADER_TYPE_COMPUTE) + { + shader->u.cs.thread_group_size = ins.declaration.thread_group_size; + } + else + { + FIXME("Invalid instruction %#x for shader type %#x.\n", + ins.handler_idx, shader_version.type); + } + } + else if (ins.handler_idx == WINED3DSIH_DCL_UAV_RAW) + { + unsigned int reg_idx = ins.declaration.dst.reg.idx[0].offset; + if (reg_idx >= ARRAY_SIZE(reg_maps->uav_resource_info)) + { + ERR("Invalid UAV resource index %u.\n", reg_idx); + break; + } + if (ins.flags) + FIXME("Ignoring raw UAV flags %#x.\n", ins.flags); + reg_maps->uav_resource_info[reg_idx].type = WINED3D_SHADER_RESOURCE_BUFFER; + reg_maps->uav_resource_info[reg_idx].data_type = WINED3D_DATA_UINT; + reg_maps->uav_resource_info[reg_idx].flags = WINED3D_VIEW_BUFFER_RAW; + } + else if (ins.handler_idx == WINED3DSIH_DCL_UAV_STRUCTURED) + { + unsigned int reg_idx = ins.declaration.structured_resource.reg.reg.idx[0].offset; + if (reg_idx >= ARRAY_SIZE(reg_maps->uav_resource_info)) + { + ERR("Invalid UAV resource index %u.\n", reg_idx); + break; + } + if (ins.flags) + FIXME("Ignoring structured UAV flags %#x.\n", ins.flags); + reg_maps->uav_resource_info[reg_idx].type = WINED3D_SHADER_RESOURCE_BUFFER; + reg_maps->uav_resource_info[reg_idx].data_type = WINED3D_DATA_UINT; + reg_maps->uav_resource_info[reg_idx].flags = 0; + reg_maps->uav_resource_info[reg_idx].stride = ins.declaration.structured_resource.byte_stride / 4; + } + else if (ins.handler_idx == WINED3DSIH_DCL_VERTICES_OUT) + { + if (shader_version.type == WINED3D_SHADER_TYPE_GEOMETRY) + shader->u.gs.vertices_out = ins.declaration.count; + else + FIXME("Invalid instruction %#x for shader type %#x.\n", + ins.handler_idx, shader_version.type); + } + else if (ins.handler_idx == WINED3DSIH_DEF) + { + struct wined3d_shader_lconst *lconst; + float *value; + + if (!(lconst = heap_alloc(sizeof(*lconst)))) + return E_OUTOFMEMORY; + + lconst->idx = ins.dst[0].reg.idx[0].offset; + memcpy(lconst->value, ins.src[0].reg.u.immconst_data, 4 * sizeof(DWORD)); + value = (float *)lconst->value; + + /* In pixel shader 1.X shaders, the constants are clamped between [-1;1] */ + if (shader_version.major == 1 && shader_version.type == WINED3D_SHADER_TYPE_PIXEL) + { + if (value[0] < -1.0f) value[0] = -1.0f; + else if (value[0] > 1.0f) value[0] = 1.0f; + if (value[1] < -1.0f) value[1] = -1.0f; + else if (value[1] > 1.0f) value[1] = 1.0f; + if (value[2] < -1.0f) value[2] = -1.0f; + else if (value[2] > 1.0f) value[2] = 1.0f; + if (value[3] < -1.0f) value[3] = -1.0f; + else if (value[3] > 1.0f) value[3] = 1.0f; + } + + list_add_head(&shader->constantsF, &lconst->entry); + + if (isinf(value[0]) || isnan(value[0]) || isinf(value[1]) || isnan(value[1]) + || isinf(value[2]) || isnan(value[2]) || isinf(value[3]) || isnan(value[3])) + { + shader->lconst_inf_or_nan = TRUE; + } + } + else if (ins.handler_idx == WINED3DSIH_DEFI) + { + struct wined3d_shader_lconst *lconst; + + if (!(lconst = heap_alloc(sizeof(*lconst)))) + return E_OUTOFMEMORY; + + lconst->idx = ins.dst[0].reg.idx[0].offset; + memcpy(lconst->value, ins.src[0].reg.u.immconst_data, 4 * sizeof(DWORD)); + + list_add_head(&shader->constantsI, &lconst->entry); + reg_maps->local_int_consts |= (1u << lconst->idx); + } + else if (ins.handler_idx == WINED3DSIH_DEFB) + { + struct wined3d_shader_lconst *lconst; + + if (!(lconst = heap_alloc(sizeof(*lconst)))) + return E_OUTOFMEMORY; + + lconst->idx = ins.dst[0].reg.idx[0].offset; + memcpy(lconst->value, ins.src[0].reg.u.immconst_data, sizeof(DWORD)); + + list_add_head(&shader->constantsB, &lconst->entry); + reg_maps->local_bool_consts |= (1u << lconst->idx); + } + /* Handle shader phases. */ + else if (ins.handler_idx == WINED3DSIH_HS_CONTROL_POINT_PHASE + || ins.handler_idx == WINED3DSIH_HS_FORK_PHASE + || ins.handler_idx == WINED3DSIH_HS_JOIN_PHASE) + { + if (FAILED(hr = shader_record_shader_phase(shader, &phase, &ins, current_ins, prev_ins))) + return hr; + } + /* For subroutine prototypes. */ + else if (ins.handler_idx == WINED3DSIH_LABEL) + { + reg_maps->labels |= 1u << ins.src[0].reg.idx[0].offset; + } + /* Set texture, address, temporary registers. */ + else + { + BOOL color0_mov = FALSE; + unsigned int i; + + /* This will loop over all the registers and try to + * make a bitmask of the ones we're interested in. + * + * Relative addressing tokens are ignored, but that's + * okay, since we'll catch any address registers when + * they are initialized (required by spec). */ + for (i = 0; i < ins.dst_count; ++i) + { + if (!shader_record_register_usage(shader, reg_maps, &ins.dst[i].reg, + shader_version.type, constf_size)) + return WINED3DERR_INVALIDCALL; + + if (shader_version.type == WINED3D_SHADER_TYPE_VERTEX) + { + UINT idx = ins.dst[i].reg.idx[0].offset; + + switch (ins.dst[i].reg.type) + { + case WINED3DSPR_RASTOUT: + if (shader_version.major >= 3) + break; + switch (idx) + { + case 0: /* oPos */ + reg_maps->output_registers |= 1u << 10; + shader_signature_from_usage(&output_signature_elements[10], + WINED3D_DECL_USAGE_POSITION, 0, 10, WINED3DSP_WRITEMASK_ALL); + break; + + case 1: /* oFog */ + reg_maps->output_registers |= 1u << 11; + shader_signature_from_usage(&output_signature_elements[11], + WINED3D_DECL_USAGE_FOG, 0, 11, WINED3DSP_WRITEMASK_0); + break; + + case 2: /* oPts */ + reg_maps->output_registers |= 1u << 11; + shader_signature_from_usage(&output_signature_elements[11], + WINED3D_DECL_USAGE_PSIZE, 0, 11, WINED3DSP_WRITEMASK_1); + break; + } + break; + + case WINED3DSPR_ATTROUT: + if (shader_version.major >= 3) + break; + if (idx < 2) + { + idx += 8; + if (reg_maps->output_registers & (1u << idx)) + { + output_signature_elements[idx].mask |= ins.dst[i].write_mask; + } + else + { + reg_maps->output_registers |= 1u << idx; + shader_signature_from_usage(&output_signature_elements[idx], + WINED3D_DECL_USAGE_COLOR, idx - 8, idx, ins.dst[i].write_mask); + } + } + break; + + case WINED3DSPR_TEXCRDOUT: /* WINED3DSPR_OUTPUT */ + if (shader_version.major >= 3) + { + if (idx >= ARRAY_SIZE(reg_maps->u.output_registers_mask)) + { + WARN("Invalid output register index %u.\n", idx); + break; + } + reg_maps->u.output_registers_mask[idx] |= ins.dst[i].write_mask; + break; + } + if (idx >= ARRAY_SIZE(reg_maps->u.texcoord_mask)) + { + WARN("Invalid texcoord index %u.\n", idx); + break; + } + reg_maps->u.texcoord_mask[idx] |= ins.dst[i].write_mask; + if (reg_maps->output_registers & (1u << idx)) + { + output_signature_elements[idx].mask |= ins.dst[i].write_mask; + } + else + { + reg_maps->output_registers |= 1u << idx; + shader_signature_from_usage(&output_signature_elements[idx], + WINED3D_DECL_USAGE_TEXCOORD, idx, idx, ins.dst[i].write_mask); + } + break; + + default: + break; + } + } + + if (shader_version.type == WINED3D_SHADER_TYPE_PIXEL) + { + if (ins.dst[i].reg.type == WINED3DSPR_COLOROUT && !ins.dst[i].reg.idx[0].offset) + { + /* Many 2.0 and 3.0 pixel shaders end with a MOV from a temp register to + * COLOROUT 0. If we know this in advance, the ARB shader backend can skip + * the mov and perform the sRGB write correction from the source register. + * + * However, if the mov is only partial, we can't do this, and if the write + * comes from an instruction other than MOV it is hard to do as well. If + * COLOROUT 0 is overwritten partially later, the marker is dropped again. */ + shader->u.ps.color0_mov = FALSE; + if (ins.handler_idx == WINED3DSIH_MOV + && ins.dst[i].write_mask == WINED3DSP_WRITEMASK_ALL) + { + /* Used later when the source register is read. */ + color0_mov = TRUE; + } + } + /* Also drop the MOV marker if the source register is overwritten prior to the shader + * end + */ + else if (ins.dst[i].reg.type == WINED3DSPR_TEMP + && ins.dst[i].reg.idx[0].offset == shader->u.ps.color0_reg) + { + shader->u.ps.color0_mov = FALSE; + } + } + + /* Declare 1.x samplers implicitly, based on the destination reg. number. */ + if (shader_version.major == 1 + && (ins.handler_idx == WINED3DSIH_TEX + || ins.handler_idx == WINED3DSIH_TEXBEM + || ins.handler_idx == WINED3DSIH_TEXBEML + || ins.handler_idx == WINED3DSIH_TEXDP3TEX + || ins.handler_idx == WINED3DSIH_TEXM3x2TEX + || ins.handler_idx == WINED3DSIH_TEXM3x3SPEC + || ins.handler_idx == WINED3DSIH_TEXM3x3TEX + || ins.handler_idx == WINED3DSIH_TEXM3x3VSPEC + || ins.handler_idx == WINED3DSIH_TEXREG2AR + || ins.handler_idx == WINED3DSIH_TEXREG2GB + || ins.handler_idx == WINED3DSIH_TEXREG2RGB)) + { + unsigned int reg_idx = ins.dst[i].reg.idx[0].offset; + + if (reg_idx >= ARRAY_SIZE(reg_maps->resource_info)) + { + WARN("Invalid 1.x sampler index %u.\n", reg_idx); + continue; + } + + TRACE("Setting fake 2D resource for 1.x pixelshader.\n"); + reg_maps->resource_info[reg_idx].type = WINED3D_SHADER_RESOURCE_TEXTURE_2D; + reg_maps->resource_info[reg_idx].data_type = WINED3D_DATA_FLOAT; + shader_record_sample(reg_maps, reg_idx, reg_idx, reg_idx); + wined3d_bitmap_set(reg_maps->resource_map, reg_idx); + + /* texbem is only valid with < 1.4 pixel shaders */ + if (ins.handler_idx == WINED3DSIH_TEXBEM + || ins.handler_idx == WINED3DSIH_TEXBEML) + { + reg_maps->bumpmat |= 1u << reg_idx; + if (ins.handler_idx == WINED3DSIH_TEXBEML) + { + reg_maps->luminanceparams |= 1u << reg_idx; + } + } + } + else if (ins.handler_idx == WINED3DSIH_BEM) + { + reg_maps->bumpmat |= 1u << ins.dst[i].reg.idx[0].offset; + } + } + + if (ins.handler_idx == WINED3DSIH_IMM_ATOMIC_ALLOC || ins.handler_idx == WINED3DSIH_IMM_ATOMIC_CONSUME) + { + unsigned int reg_idx = ins.src[0].reg.idx[0].offset; + if (reg_idx >= MAX_UNORDERED_ACCESS_VIEWS) + { + ERR("Invalid UAV index %u.\n", reg_idx); + break; + } + reg_maps->uav_counter_mask |= (1u << reg_idx); + } + else if ((WINED3DSIH_ATOMIC_AND <= ins.handler_idx && ins.handler_idx <= WINED3DSIH_ATOMIC_XOR) + || (WINED3DSIH_IMM_ATOMIC_AND <= ins.handler_idx && ins.handler_idx <= WINED3DSIH_IMM_ATOMIC_XOR) + || (ins.handler_idx == WINED3DSIH_BUFINFO && ins.src[0].reg.type == WINED3DSPR_UAV) + || ins.handler_idx == WINED3DSIH_LD_UAV_TYPED + || (ins.handler_idx == WINED3DSIH_LD_RAW && ins.src[1].reg.type == WINED3DSPR_UAV) + || (ins.handler_idx == WINED3DSIH_LD_STRUCTURED && ins.src[2].reg.type == WINED3DSPR_UAV)) + { + unsigned int reg_idx; + if (ins.handler_idx == WINED3DSIH_LD_UAV_TYPED || ins.handler_idx == WINED3DSIH_LD_RAW) + reg_idx = ins.src[1].reg.idx[0].offset; + else if (ins.handler_idx == WINED3DSIH_LD_STRUCTURED) + reg_idx = ins.src[2].reg.idx[0].offset; + else if (WINED3DSIH_ATOMIC_AND <= ins.handler_idx && ins.handler_idx <= WINED3DSIH_ATOMIC_XOR) + reg_idx = ins.dst[0].reg.idx[0].offset; + else if (ins.handler_idx == WINED3DSIH_BUFINFO) + reg_idx = ins.src[0].reg.idx[0].offset; + else + reg_idx = ins.dst[1].reg.idx[0].offset; + if (reg_idx >= MAX_UNORDERED_ACCESS_VIEWS) + { + ERR("Invalid UAV index %u.\n", reg_idx); + break; + } + reg_maps->uav_read_mask |= (1u << reg_idx); + } + else if (ins.handler_idx == WINED3DSIH_NRM) + { + reg_maps->usesnrm = 1; + } + else if (ins.handler_idx == WINED3DSIH_DSY + || ins.handler_idx == WINED3DSIH_DSY_COARSE + || ins.handler_idx == WINED3DSIH_DSY_FINE) + { + reg_maps->usesdsy = 1; + } + else if (ins.handler_idx == WINED3DSIH_DSX + || ins.handler_idx == WINED3DSIH_DSX_COARSE + || ins.handler_idx == WINED3DSIH_DSX_FINE) + { + reg_maps->usesdsx = 1; + } + else if (ins.handler_idx == WINED3DSIH_TEXLDD) reg_maps->usestexldd = 1; + else if (ins.handler_idx == WINED3DSIH_TEXLDL) reg_maps->usestexldl = 1; + else if (ins.handler_idx == WINED3DSIH_MOVA) reg_maps->usesmova = 1; + else if (ins.handler_idx == WINED3DSIH_IFC) reg_maps->usesifc = 1; + else if (ins.handler_idx == WINED3DSIH_CALL) reg_maps->usescall = 1; + else if (ins.handler_idx == WINED3DSIH_POW) reg_maps->usespow = 1; + else if (ins.handler_idx == WINED3DSIH_LOOP + || ins.handler_idx == WINED3DSIH_REP) + { + ++cur_loop_depth; + if (cur_loop_depth > max_loop_depth) + max_loop_depth = cur_loop_depth; + } + else if (ins.handler_idx == WINED3DSIH_ENDLOOP + || ins.handler_idx == WINED3DSIH_ENDREP) + { + --cur_loop_depth; + } + else if (ins.handler_idx == WINED3DSIH_GATHER4 + || ins.handler_idx == WINED3DSIH_GATHER4_C + || ins.handler_idx == WINED3DSIH_SAMPLE + || ins.handler_idx == WINED3DSIH_SAMPLE_B + || ins.handler_idx == WINED3DSIH_SAMPLE_C + || ins.handler_idx == WINED3DSIH_SAMPLE_C_LZ + || ins.handler_idx == WINED3DSIH_SAMPLE_GRAD + || ins.handler_idx == WINED3DSIH_SAMPLE_LOD) + { + shader_record_sample(reg_maps, ins.src[1].reg.idx[0].offset, + ins.src[2].reg.idx[0].offset, reg_maps->sampler_map.count); + } + else if (ins.handler_idx == WINED3DSIH_GATHER4_PO + || ins.handler_idx == WINED3DSIH_GATHER4_PO_C) + { + shader_record_sample(reg_maps, ins.src[2].reg.idx[0].offset, + ins.src[3].reg.idx[0].offset, reg_maps->sampler_map.count); + } + else if ((ins.handler_idx == WINED3DSIH_BUFINFO && ins.src[0].reg.type == WINED3DSPR_RESOURCE) + || (ins.handler_idx == WINED3DSIH_SAMPLE_INFO && ins.src[0].reg.type == WINED3DSPR_RESOURCE)) + { + shader_record_sample(reg_maps, ins.src[0].reg.idx[0].offset, + WINED3D_SAMPLER_DEFAULT, reg_maps->sampler_map.count); + } + else if (ins.handler_idx == WINED3DSIH_LD + || ins.handler_idx == WINED3DSIH_LD2DMS + || (ins.handler_idx == WINED3DSIH_LD_RAW && ins.src[1].reg.type == WINED3DSPR_RESOURCE) + || (ins.handler_idx == WINED3DSIH_RESINFO && ins.src[1].reg.type == WINED3DSPR_RESOURCE)) + { + shader_record_sample(reg_maps, ins.src[1].reg.idx[0].offset, + WINED3D_SAMPLER_DEFAULT, reg_maps->sampler_map.count); + } + else if (ins.handler_idx == WINED3DSIH_LD_STRUCTURED + && ins.src[2].reg.type == WINED3DSPR_RESOURCE) + { + shader_record_sample(reg_maps, ins.src[2].reg.idx[0].offset, + WINED3D_SAMPLER_DEFAULT, reg_maps->sampler_map.count); + } + + if (ins.predicate) + if (!shader_record_register_usage(shader, reg_maps, &ins.predicate->reg, + shader_version.type, constf_size)) + return WINED3DERR_INVALIDCALL; + + for (i = 0; i < ins.src_count; ++i) + { + unsigned int count = get_instr_extra_regcount(ins.handler_idx, i); + struct wined3d_shader_register reg = ins.src[i].reg; + + if (!shader_record_register_usage(shader, reg_maps, &ins.src[i].reg, + shader_version.type, constf_size)) + return WINED3DERR_INVALIDCALL; + while (count) + { + ++reg.idx[0].offset; + if (!shader_record_register_usage(shader, reg_maps, ®, + shader_version.type, constf_size)) + return WINED3DERR_INVALIDCALL; + --count; + } + + if (color0_mov) + { + if (ins.src[i].reg.type == WINED3DSPR_TEMP + && ins.src[i].swizzle == WINED3DSP_NOSWIZZLE) + { + shader->u.ps.color0_mov = TRUE; + shader->u.ps.color0_reg = ins.src[i].reg.idx[0].offset; + } + } + } + } + + prev_ins = current_ins; + } + reg_maps->loop_depth = max_loop_depth; + + if (phase) + { + phase->end = prev_ins; + phase = NULL; + } + + /* PS before 2.0 don't have explicit color outputs. Instead the value of + * R0 is written to the render target. */ + if (shader_version.major < 2 && shader_version.type == WINED3D_SHADER_TYPE_PIXEL) + reg_maps->rt_mask |= (1u << 0); + + if (input_signature->elements) + { + for (i = 0; i < input_signature->element_count; ++i) + { + if (shader_version.type == WINED3D_SHADER_TYPE_VERTEX) + { + if (input_signature->elements[i].register_idx >= ARRAY_SIZE(shader->u.vs.attributes)) + { + WARN("Invalid input signature register index %u.\n", input_signature->elements[i].register_idx); + return WINED3DERR_INVALIDCALL; + } + } + else if (shader_version.type == WINED3D_SHADER_TYPE_PIXEL) + { + if (input_signature->elements[i].sysval_semantic == WINED3D_SV_POSITION) + reg_maps->vpos = 1; + else if (input_signature->elements[i].sysval_semantic == WINED3D_SV_IS_FRONT_FACE) + reg_maps->usesfacing = 1; + } + reg_maps->input_registers |= 1u << input_signature->elements[i].register_idx; + } + } + else if (!input_signature->elements && reg_maps->input_registers) + { + unsigned int count = wined3d_popcount(reg_maps->input_registers); + struct wined3d_shader_signature_element *e; + unsigned int i; + + if (!(input_signature->elements = heap_calloc(count, sizeof(*input_signature->elements)))) + return E_OUTOFMEMORY; + input_signature->element_count = count; + + e = input_signature->elements; + for (i = 0; i < ARRAY_SIZE(input_signature_elements); ++i) + { + if (!(reg_maps->input_registers & (1u << i))) + continue; + input_signature_elements[i].register_idx = i; + *e++ = input_signature_elements[i]; + } + } + + if (output_signature->elements) + { + if (FAILED(hr = shader_scan_output_signature(shader))) + return hr; + } + else if (reg_maps->output_registers) + { + unsigned int count = wined3d_popcount(reg_maps->output_registers); + struct wined3d_shader_signature_element *e; + + if (!(output_signature->elements = heap_calloc(count, sizeof(*output_signature->elements)))) + return E_OUTOFMEMORY; + output_signature->element_count = count; + + e = output_signature->elements; + for (i = 0; i < ARRAY_SIZE(output_signature_elements); ++i) + { + if (!(reg_maps->output_registers & (1u << i))) + continue; + *e++ = output_signature_elements[i]; + } + } + + return WINED3D_OK; +} + +static void shader_cleanup_reg_maps(struct wined3d_shader_reg_maps *reg_maps) +{ + struct wined3d_shader_indexable_temp *reg, *reg_next; + + heap_free(reg_maps->constf); + heap_free(reg_maps->sampler_map.entries); + + LIST_FOR_EACH_ENTRY_SAFE(reg, reg_next, ®_maps->indexable_temps, struct wined3d_shader_indexable_temp, entry) + heap_free(reg); + list_init(®_maps->indexable_temps); + + heap_free(reg_maps->tgsm); +} + +unsigned int shader_find_free_input_register(const struct wined3d_shader_reg_maps *reg_maps, unsigned int max) +{ + DWORD map = 1u << max; + map |= map - 1; + map &= reg_maps->shader_version.major < 3 ? ~reg_maps->texcoord : ~reg_maps->input_registers; + + return wined3d_log2i(map); +} + +static void shader_dump_global_flags(struct wined3d_string_buffer *buffer, DWORD global_flags) +{ + if (global_flags & WINED3DSGF_REFACTORING_ALLOWED) + { + shader_addline(buffer, "refactoringAllowed"); + global_flags &= ~WINED3DSGF_REFACTORING_ALLOWED; + if (global_flags) + shader_addline(buffer, " | "); + } + + if (global_flags & WINED3DSGF_FORCE_EARLY_DEPTH_STENCIL) + { + shader_addline(buffer, "forceEarlyDepthStencil"); + global_flags &= ~WINED3DSGF_FORCE_EARLY_DEPTH_STENCIL; + if (global_flags) + shader_addline(buffer, " | "); + } + + if (global_flags & WINED3DSGF_ENABLE_RAW_AND_STRUCTURED_BUFFERS) + { + shader_addline(buffer, "enableRawAndStructuredBuffers"); + global_flags &= ~WINED3DSGF_ENABLE_RAW_AND_STRUCTURED_BUFFERS; + } + + if (global_flags) + shader_addline(buffer, "unknown_flags(%#x)", global_flags); +} + +static void shader_dump_sync_flags(struct wined3d_string_buffer *buffer, DWORD sync_flags) +{ + if (sync_flags & WINED3DSSF_GLOBAL_UAV) + { + shader_addline(buffer, "_uglobal"); + sync_flags &= ~WINED3DSSF_GLOBAL_UAV; + } + if (sync_flags & WINED3DSSF_GROUP_SHARED_MEMORY) + { + shader_addline(buffer, "_g"); + sync_flags &= ~WINED3DSSF_GROUP_SHARED_MEMORY; + } + if (sync_flags & WINED3DSSF_THREAD_GROUP) + { + shader_addline(buffer, "_t"); + sync_flags &= ~WINED3DSSF_THREAD_GROUP; + } + + if (sync_flags) + shader_addline(buffer, "_unknown_flags(%#x)", sync_flags); +} + +static void shader_dump_precise_flags(struct wined3d_string_buffer *buffer, DWORD flags) +{ + if (!(flags & WINED3DSI_PRECISE_XYZW)) + return; + + shader_addline(buffer, " [precise"); + if (flags != WINED3DSI_PRECISE_XYZW) + { + shader_addline(buffer, "(%s%s%s%s)", + flags & WINED3DSI_PRECISE_X ? "x" : "", + flags & WINED3DSI_PRECISE_Y ? "y" : "", + flags & WINED3DSI_PRECISE_Z ? "z" : "", + flags & WINED3DSI_PRECISE_W ? "w" : ""); + } + shader_addline(buffer, "]"); +} + +static void shader_dump_uav_flags(struct wined3d_string_buffer *buffer, DWORD uav_flags) +{ + if (uav_flags & WINED3DSUF_GLOBALLY_COHERENT) + { + shader_addline(buffer, "_glc"); + uav_flags &= ~WINED3DSUF_GLOBALLY_COHERENT; + } + if (uav_flags & WINED3DSUF_ORDER_PRESERVING_COUNTER) + { + shader_addline(buffer, "_opc"); + uav_flags &= ~WINED3DSUF_ORDER_PRESERVING_COUNTER; + } + + if (uav_flags) + shader_addline(buffer, "_unknown_flags(%#x)", uav_flags); +} + +static void shader_dump_tessellator_domain(struct wined3d_string_buffer *buffer, + enum wined3d_tessellator_domain domain) +{ + switch (domain) + { + case WINED3D_TESSELLATOR_DOMAIN_LINE: + shader_addline(buffer, "line"); + break; + case WINED3D_TESSELLATOR_DOMAIN_TRIANGLE: + shader_addline(buffer, "triangle"); + break; + case WINED3D_TESSELLATOR_DOMAIN_QUAD: + shader_addline(buffer, "quad"); + break; + default: + shader_addline(buffer, "unknown_tessellator_domain(%#x)", domain); + break; + } +} + +static void shader_dump_tessellator_output_primitive(struct wined3d_string_buffer *buffer, + enum wined3d_tessellator_output_primitive output_primitive) +{ + switch (output_primitive) + { + case WINED3D_TESSELLATOR_OUTPUT_POINT: + shader_addline(buffer, "point"); + break; + case WINED3D_TESSELLATOR_OUTPUT_LINE: + shader_addline(buffer, "line"); + break; + case WINED3D_TESSELLATOR_OUTPUT_TRIANGLE_CW: + shader_addline(buffer, "triangle_cw"); + break; + case WINED3D_TESSELLATOR_OUTPUT_TRIANGLE_CCW: + shader_addline(buffer, "triangle_ccw"); + break; + default: + shader_addline(buffer, "unknown_tessellator_output_primitive(%#x)", output_primitive); + break; + } +} + +static void shader_dump_tessellator_partitioning(struct wined3d_string_buffer *buffer, + enum wined3d_tessellator_partitioning partitioning) +{ + switch (partitioning) + { + case WINED3D_TESSELLATOR_PARTITIONING_INTEGER: + shader_addline(buffer, "integer"); + break; + case WINED3D_TESSELLATOR_PARTITIONING_POW2: + shader_addline(buffer, "pow2"); + break; + case WINED3D_TESSELLATOR_PARTITIONING_FRACTIONAL_ODD: + shader_addline(buffer, "fractional_odd"); + break; + case WINED3D_TESSELLATOR_PARTITIONING_FRACTIONAL_EVEN: + shader_addline(buffer, "fractional_even"); + break; + default: + shader_addline(buffer, "unknown_tessellator_partitioning(%#x)", partitioning); + break; + } +} + +static void shader_dump_shader_input_sysval_semantic(struct wined3d_string_buffer *buffer, + enum wined3d_shader_input_sysval_semantic semantic) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(shader_input_sysval_semantic_names); ++i) + { + if (shader_input_sysval_semantic_names[i].sysval_semantic == semantic) + { + shader_addline(buffer, "%s", shader_input_sysval_semantic_names[i].sysval_name); + return; + } + } + + shader_addline(buffer, "unknown_shader_input_sysval_semantic(%#x)", semantic); +} + +static void shader_dump_resource_type(struct wined3d_string_buffer *buffer, enum wined3d_shader_resource_type type) +{ + static const char *const resource_type_names[] = + { + /* WINED3D_SHADER_RESOURCE_NONE */ "none", + /* WINED3D_SHADER_RESOURCE_BUFFER */ "buffer", + /* WINED3D_SHADER_RESOURCE_TEXTURE_1D */ "texture1d", + /* WINED3D_SHADER_RESOURCE_TEXTURE_2D */ "texture2d", + /* WINED3D_SHADER_RESOURCE_TEXTURE_2DMS */ "texture2dms", + /* WINED3D_SHADER_RESOURCE_TEXTURE_3D */ "texture3d", + /* WINED3D_SHADER_RESOURCE_TEXTURE_CUBE */ "texturecube", + /* WINED3D_SHADER_RESOURCE_TEXTURE_1DARRAY */ "texture1darray", + /* WINED3D_SHADER_RESOURCE_TEXTURE_2DARRAY */ "texture2darray", + /* WINED3D_SHADER_RESOURCE_TEXTURE_2DMSARRAY */ "texture2dmsarray", + /* WINED3D_SHADER_RESOURCE_TEXTURE_CUBEARRAY */ "texturecubearray", + }; + + if (type < ARRAY_SIZE(resource_type_names)) + shader_addline(buffer, "%s", resource_type_names[type]); + else + shader_addline(buffer, "unknown"); +} + +static void shader_dump_data_type(struct wined3d_string_buffer *buffer, enum wined3d_data_type type) +{ + static const char *const data_type_names[] = + { + /* WINED3D_DATA_FLOAT */ "(float)", + /* WINED3D_DATA_INT */ "(int)", + /* WINED3D_DATA_RESOURCE */ "(resource)", + /* WINED3D_DATA_SAMPLER */ "(sampler)", + /* WINED3D_DATA_UAV */ "(uav)", + /* WINED3D_DATA_UINT */ "(uint)", + /* WINED3D_DATA_UNORM */ "(unorm)", + /* WINED3D_DATA_SNORM */ "(snorm)", + /* WINED3D_DATA_OPAQUE */ "(opaque)", + }; + + if (type < ARRAY_SIZE(data_type_names)) + shader_addline(buffer, "%s", data_type_names[type]); + else + shader_addline(buffer, "(unknown)"); +} + +static void shader_dump_decl_usage(struct wined3d_string_buffer *buffer, + const struct wined3d_shader_semantic *semantic, unsigned int flags, + const struct wined3d_shader_version *shader_version) +{ + shader_addline(buffer, "dcl"); + + if (semantic->reg.reg.type == WINED3DSPR_SAMPLER) + { + switch (semantic->resource_type) + { + case WINED3D_SHADER_RESOURCE_TEXTURE_2D: + shader_addline(buffer, "_2d"); + break; + + case WINED3D_SHADER_RESOURCE_TEXTURE_3D: + shader_addline(buffer, "_3d"); + break; + + case WINED3D_SHADER_RESOURCE_TEXTURE_CUBE: + shader_addline(buffer, "_cube"); + break; + + default: + shader_addline(buffer, "_unknown_resource_type(%#x)", semantic->resource_type); + break; + } + } + else if (semantic->reg.reg.type == WINED3DSPR_RESOURCE || semantic->reg.reg.type == WINED3DSPR_UAV) + { + if (semantic->reg.reg.type == WINED3DSPR_RESOURCE) + shader_addline(buffer, "_resource_"); + else + shader_addline(buffer, "_uav_"); + shader_dump_resource_type(buffer, semantic->resource_type); + if (semantic->reg.reg.type == WINED3DSPR_UAV) + shader_dump_uav_flags(buffer, flags); + shader_dump_data_type(buffer, semantic->resource_data_type); + } + else + { + /* Pixel shaders 3.0 don't have usage semantics. */ + if (shader_version->major < 3 && shader_version->type == WINED3D_SHADER_TYPE_PIXEL) + return; + else + shader_addline(buffer, "_"); + + switch (semantic->usage) + { + case WINED3D_DECL_USAGE_POSITION: + shader_addline(buffer, "position%u", semantic->usage_idx); + break; + + case WINED3D_DECL_USAGE_BLEND_INDICES: + shader_addline(buffer, "blend"); + break; + + case WINED3D_DECL_USAGE_BLEND_WEIGHT: + shader_addline(buffer, "weight"); + break; + + case WINED3D_DECL_USAGE_NORMAL: + shader_addline(buffer, "normal%u", semantic->usage_idx); + break; + + case WINED3D_DECL_USAGE_PSIZE: + shader_addline(buffer, "psize"); + break; + + case WINED3D_DECL_USAGE_COLOR: + if (!semantic->usage_idx) + shader_addline(buffer, "color"); + else + shader_addline(buffer, "specular%u", (semantic->usage_idx - 1)); + break; + + case WINED3D_DECL_USAGE_TEXCOORD: + shader_addline(buffer, "texture%u", semantic->usage_idx); + break; + + case WINED3D_DECL_USAGE_TANGENT: + shader_addline(buffer, "tangent"); + break; + + case WINED3D_DECL_USAGE_BINORMAL: + shader_addline(buffer, "binormal"); + break; + + case WINED3D_DECL_USAGE_TESS_FACTOR: + shader_addline(buffer, "tessfactor"); + break; + + case WINED3D_DECL_USAGE_POSITIONT: + shader_addline(buffer, "positionT%u", semantic->usage_idx); + break; + + case WINED3D_DECL_USAGE_FOG: + shader_addline(buffer, "fog"); + break; + + case WINED3D_DECL_USAGE_DEPTH: + shader_addline(buffer, "depth"); + break; + + case WINED3D_DECL_USAGE_SAMPLE: + shader_addline(buffer, "sample"); + break; + + default: + shader_addline(buffer, "", semantic->usage); + FIXME("Unrecognised semantic usage %#x.\n", semantic->usage); + } + } +} + +static void shader_dump_register(struct wined3d_string_buffer *buffer, + const struct wined3d_shader_register *reg, const struct wined3d_shader_version *shader_version) +{ + static const char * const rastout_reg_names[] = {"oPos", "oFog", "oPts"}; + static const char * const misctype_reg_names[] = {"vPos", "vFace"}; + UINT offset = reg->idx[0].offset; + + switch (reg->type) + { + case WINED3DSPR_TEMP: + shader_addline(buffer, "r"); + break; + + case WINED3DSPR_INPUT: + shader_addline(buffer, "v"); + break; + + case WINED3DSPR_CONST: + case WINED3DSPR_CONST2: + case WINED3DSPR_CONST3: + case WINED3DSPR_CONST4: + shader_addline(buffer, "c"); + offset = shader_get_float_offset(reg->type, offset); + break; + + case WINED3DSPR_TEXTURE: /* vs: case WINED3DSPR_ADDR */ + shader_addline(buffer, "%c", shader_version->type == WINED3D_SHADER_TYPE_PIXEL ? 't' : 'a'); + break; + + case WINED3DSPR_RASTOUT: + shader_addline(buffer, "%s", rastout_reg_names[offset]); + break; + + case WINED3DSPR_COLOROUT: + shader_addline(buffer, "oC"); + break; + + case WINED3DSPR_DEPTHOUT: + shader_addline(buffer, "oDepth"); + break; + + case WINED3DSPR_DEPTHOUTGE: + shader_addline(buffer, "oDepthGE"); + break; + + case WINED3DSPR_DEPTHOUTLE: + shader_addline(buffer, "oDepthLE"); + break; + + case WINED3DSPR_ATTROUT: + shader_addline(buffer, "oD"); + break; + + case WINED3DSPR_TEXCRDOUT: + /* Vertex shaders >= 3.0 use general purpose output registers + * (WINED3DSPR_OUTPUT), which can include an address token. */ + if (shader_version->major >= 3) + shader_addline(buffer, "o"); + else + shader_addline(buffer, "oT"); + break; + + case WINED3DSPR_CONSTINT: + shader_addline(buffer, "i"); + break; + + case WINED3DSPR_CONSTBOOL: + shader_addline(buffer, "b"); + break; + + case WINED3DSPR_LABEL: + shader_addline(buffer, "l"); + break; + + case WINED3DSPR_LOOP: + shader_addline(buffer, "aL"); + break; + + case WINED3DSPR_SAMPLER: + shader_addline(buffer, "s"); + break; + + case WINED3DSPR_MISCTYPE: + if (offset > 1) + { + FIXME("Unhandled misctype register %u.\n", offset); + shader_addline(buffer, "", offset); + } + else + { + shader_addline(buffer, "%s", misctype_reg_names[offset]); + } + break; + + case WINED3DSPR_PREDICATE: + shader_addline(buffer, "p"); + break; + + case WINED3DSPR_IMMCONST: + shader_addline(buffer, "l"); + break; + + case WINED3DSPR_CONSTBUFFER: + shader_addline(buffer, "cb"); + break; + + case WINED3DSPR_IMMCONSTBUFFER: + shader_addline(buffer, "icb"); + break; + + case WINED3DSPR_PRIMID: + shader_addline(buffer, "primID"); + break; + + case WINED3DSPR_NULL: + shader_addline(buffer, "null"); + break; + + case WINED3DSPR_RASTERIZER: + shader_addline(buffer, "rasterizer"); + break; + + case WINED3DSPR_RESOURCE: + shader_addline(buffer, "t"); + break; + + case WINED3DSPR_UAV: + shader_addline(buffer, "u"); + break; + + case WINED3DSPR_OUTPOINTID: + shader_addline(buffer, "vOutputControlPointID"); + break; + + case WINED3DSPR_FORKINSTID: + shader_addline(buffer, "vForkInstanceId"); + break; + + case WINED3DSPR_JOININSTID: + shader_addline(buffer, "vJoinInstanceId"); + break; + + case WINED3DSPR_INCONTROLPOINT: + shader_addline(buffer, "vicp"); + break; + + case WINED3DSPR_OUTCONTROLPOINT: + shader_addline(buffer, "vocp"); + break; + + case WINED3DSPR_PATCHCONST: + shader_addline(buffer, "vpc"); + break; + + case WINED3DSPR_TESSCOORD: + shader_addline(buffer, "vDomainLocation"); + break; + + case WINED3DSPR_GROUPSHAREDMEM: + shader_addline(buffer, "g"); + break; + + case WINED3DSPR_THREADID: + shader_addline(buffer, "vThreadID"); + break; + + case WINED3DSPR_THREADGROUPID: + shader_addline(buffer, "vThreadGroupID"); + break; + + case WINED3DSPR_LOCALTHREADID: + shader_addline(buffer, "vThreadIDInGroup"); + break; + + case WINED3DSPR_LOCALTHREADINDEX: + shader_addline(buffer, "vThreadIDInGroupFlattened"); + break; + + case WINED3DSPR_IDXTEMP: + shader_addline(buffer, "x"); + break; + + case WINED3DSPR_STREAM: + shader_addline(buffer, "m"); + break; + + case WINED3DSPR_FUNCTIONBODY: + shader_addline(buffer, "fb"); + break; + + case WINED3DSPR_FUNCTIONPOINTER: + shader_addline(buffer, "fp"); + break; + + case WINED3DSPR_COVERAGE: + shader_addline(buffer, "vCoverage"); + break; + + case WINED3DSPR_SAMPLEMASK: + shader_addline(buffer, "oMask"); + break; + + case WINED3DSPR_GSINSTID: + shader_addline(buffer, "vGSInstanceID"); + break; + + default: + shader_addline(buffer, "", reg->type); + break; + } + + if (reg->type == WINED3DSPR_IMMCONST) + { + shader_addline(buffer, "("); + switch (reg->immconst_type) + { + case WINED3D_IMMCONST_SCALAR: + switch (reg->data_type) + { + case WINED3D_DATA_FLOAT: + shader_addline(buffer, "%.8e", *(const float *)reg->u.immconst_data); + break; + case WINED3D_DATA_INT: + shader_addline(buffer, "%d", reg->u.immconst_data[0]); + break; + case WINED3D_DATA_RESOURCE: + case WINED3D_DATA_SAMPLER: + case WINED3D_DATA_UINT: + shader_addline(buffer, "%u", reg->u.immconst_data[0]); + break; + default: + shader_addline(buffer, "", reg->data_type); + break; + } + break; + + case WINED3D_IMMCONST_VEC4: + switch (reg->data_type) + { + case WINED3D_DATA_FLOAT: + shader_addline(buffer, "%.8e, %.8e, %.8e, %.8e", + *(const float *)®->u.immconst_data[0], *(const float *)®->u.immconst_data[1], + *(const float *)®->u.immconst_data[2], *(const float *)®->u.immconst_data[3]); + break; + case WINED3D_DATA_INT: + shader_addline(buffer, "%d, %d, %d, %d", + reg->u.immconst_data[0], reg->u.immconst_data[1], + reg->u.immconst_data[2], reg->u.immconst_data[3]); + break; + case WINED3D_DATA_RESOURCE: + case WINED3D_DATA_SAMPLER: + case WINED3D_DATA_UINT: + shader_addline(buffer, "%u, %u, %u, %u", + reg->u.immconst_data[0], reg->u.immconst_data[1], + reg->u.immconst_data[2], reg->u.immconst_data[3]); + break; + default: + shader_addline(buffer, "", reg->data_type); + break; + } + break; + + default: + shader_addline(buffer, "", reg->immconst_type); + break; + } + shader_addline(buffer, ")"); + } + else if (reg->type != WINED3DSPR_RASTOUT + && reg->type != WINED3DSPR_MISCTYPE + && reg->type != WINED3DSPR_NULL) + { + if (offset != ~0u) + { + shader_addline(buffer, "["); + if (reg->idx[0].rel_addr) + { + shader_dump_src_param(buffer, reg->idx[0].rel_addr, shader_version); + shader_addline(buffer, " + "); + } + shader_addline(buffer, "%u]", offset); + + if (reg->idx[1].offset != ~0u) + { + shader_addline(buffer, "["); + if (reg->idx[1].rel_addr) + { + shader_dump_src_param(buffer, reg->idx[1].rel_addr, shader_version); + shader_addline(buffer, " + "); + } + shader_addline(buffer, "%u]", reg->idx[1].offset); + } + } + + if (reg->type == WINED3DSPR_FUNCTIONPOINTER) + shader_addline(buffer, "[%u]", reg->u.fp_body_idx); + } +} + +static void shader_dump_dst_param(struct wined3d_string_buffer *buffer, + const struct wined3d_shader_dst_param *param, const struct wined3d_shader_version *shader_version) +{ + DWORD write_mask = param->write_mask; + + shader_dump_register(buffer, ¶m->reg, shader_version); + + if (write_mask && write_mask != WINED3DSP_WRITEMASK_ALL) + { + static const char write_mask_chars[] = "xyzw"; + + shader_addline(buffer, "."); + if (write_mask & WINED3DSP_WRITEMASK_0) + shader_addline(buffer, "%c", write_mask_chars[0]); + if (write_mask & WINED3DSP_WRITEMASK_1) + shader_addline(buffer, "%c", write_mask_chars[1]); + if (write_mask & WINED3DSP_WRITEMASK_2) + shader_addline(buffer, "%c", write_mask_chars[2]); + if (write_mask & WINED3DSP_WRITEMASK_3) + shader_addline(buffer, "%c", write_mask_chars[3]); + } +} + +static void shader_dump_src_param(struct wined3d_string_buffer *buffer, + const struct wined3d_shader_src_param *param, const struct wined3d_shader_version *shader_version) +{ + enum wined3d_shader_src_modifier src_modifier = param->modifiers; + DWORD swizzle = param->swizzle; + + if (src_modifier == WINED3DSPSM_NEG + || src_modifier == WINED3DSPSM_BIASNEG + || src_modifier == WINED3DSPSM_SIGNNEG + || src_modifier == WINED3DSPSM_X2NEG + || src_modifier == WINED3DSPSM_ABSNEG) + shader_addline(buffer, "-"); + else if (src_modifier == WINED3DSPSM_COMP) + shader_addline(buffer, "1-"); + else if (src_modifier == WINED3DSPSM_NOT) + shader_addline(buffer, "!"); + + if (src_modifier == WINED3DSPSM_ABS || src_modifier == WINED3DSPSM_ABSNEG) + shader_addline(buffer, "abs("); + + shader_dump_register(buffer, ¶m->reg, shader_version); + + switch (src_modifier) + { + case WINED3DSPSM_NONE: break; + case WINED3DSPSM_NEG: break; + case WINED3DSPSM_NOT: break; + case WINED3DSPSM_BIAS: shader_addline(buffer, "_bias"); break; + case WINED3DSPSM_BIASNEG: shader_addline(buffer, "_bias"); break; + case WINED3DSPSM_SIGN: shader_addline(buffer, "_bx2"); break; + case WINED3DSPSM_SIGNNEG: shader_addline(buffer, "_bx2"); break; + case WINED3DSPSM_COMP: break; + case WINED3DSPSM_X2: shader_addline(buffer, "_x2"); break; + case WINED3DSPSM_X2NEG: shader_addline(buffer, "_x2"); break; + case WINED3DSPSM_DZ: shader_addline(buffer, "_dz"); break; + case WINED3DSPSM_DW: shader_addline(buffer, "_dw"); break; + case WINED3DSPSM_ABSNEG: shader_addline(buffer, ")"); break; + case WINED3DSPSM_ABS: shader_addline(buffer, ")"); break; + default: shader_addline(buffer, "_unknown_modifier(%#x)", src_modifier); + } + + if (swizzle != WINED3DSP_NOSWIZZLE) + { + static const char swizzle_chars[] = "xyzw"; + DWORD swizzle_x = swizzle & 0x03; + DWORD swizzle_y = (swizzle >> 2) & 0x03; + DWORD swizzle_z = (swizzle >> 4) & 0x03; + DWORD swizzle_w = (swizzle >> 6) & 0x03; + + if (swizzle_x == swizzle_y + && swizzle_x == swizzle_z + && swizzle_x == swizzle_w) + { + shader_addline(buffer, ".%c", swizzle_chars[swizzle_x]); + } + else + { + shader_addline(buffer, ".%c%c%c%c", swizzle_chars[swizzle_x], swizzle_chars[swizzle_y], + swizzle_chars[swizzle_z], swizzle_chars[swizzle_w]); + } + } +} + +/* Shared code in order to generate the bulk of the shader string. */ +HRESULT shader_generate_code(const struct wined3d_shader *shader, struct wined3d_string_buffer *buffer, + const struct wined3d_shader_reg_maps *reg_maps, void *backend_ctx, + const DWORD *start, const DWORD *end) +{ + struct wined3d_device *device = shader->device; + const struct wined3d_shader_frontend *fe = shader->frontend; + void *fe_data = shader->frontend_data; + struct wined3d_shader_version shader_version; + struct wined3d_shader_parser_state state; + struct wined3d_shader_instruction ins; + struct wined3d_shader_tex_mx tex_mx; + struct wined3d_shader_context ctx; + const DWORD *ptr; + + /* Initialize current parsing state. */ + tex_mx.current_row = 0; + state.current_loop_depth = 0; + state.current_loop_reg = 0; + state.in_subroutine = FALSE; + + ctx.shader = shader; + ctx.reg_maps = reg_maps; + ctx.buffer = buffer; + ctx.tex_mx = &tex_mx; + ctx.state = &state; + ctx.backend_data = backend_ctx; + ins.ctx = &ctx; + + fe->shader_read_header(fe_data, &ptr, &shader_version); + if (start) + ptr = start; + + while (!fe->shader_is_end(fe_data, &ptr) && ptr != end) + { + /* Read opcode. */ + fe->shader_read_instruction(fe_data, &ptr, &ins); + + /* Unknown opcode and its parameters. */ + if (ins.handler_idx == WINED3DSIH_TABLE_SIZE) + { + WARN("Encountered unrecognised or invalid instruction.\n"); + return WINED3DERR_INVALIDCALL; + } + + if (ins.predicate) + FIXME("Predicates not implemented.\n"); + + /* Call appropriate function for output target */ + device->shader_backend->shader_handle_instruction(&ins); + } + + return WINED3D_OK; +} + +static void shader_dump_ins_modifiers(struct wined3d_string_buffer *buffer, + const struct wined3d_shader_dst_param *dst) +{ + DWORD mmask = dst->modifiers; + + switch (dst->shift) + { + case 0: break; + case 13: shader_addline(buffer, "_d8"); break; + case 14: shader_addline(buffer, "_d4"); break; + case 15: shader_addline(buffer, "_d2"); break; + case 1: shader_addline(buffer, "_x2"); break; + case 2: shader_addline(buffer, "_x4"); break; + case 3: shader_addline(buffer, "_x8"); break; + default: shader_addline(buffer, "_unhandled_shift(%d)", dst->shift); break; + } + + if (mmask & WINED3DSPDM_SATURATE) shader_addline(buffer, "_sat"); + if (mmask & WINED3DSPDM_PARTIALPRECISION) shader_addline(buffer, "_pp"); + if (mmask & WINED3DSPDM_MSAMPCENTROID) shader_addline(buffer, "_centroid"); + + mmask &= ~(WINED3DSPDM_SATURATE | WINED3DSPDM_PARTIALPRECISION | WINED3DSPDM_MSAMPCENTROID); + if (mmask) FIXME("Unrecognised modifier %#x.\n", mmask); +} + +static void shader_dump_primitive_type(struct wined3d_string_buffer *buffer, + const struct wined3d_shader_primitive_type *primitive_type) +{ + switch (primitive_type->type) + { + case WINED3D_PT_UNDEFINED: + shader_addline(buffer, "undefined"); + break; + case WINED3D_PT_POINTLIST: + shader_addline(buffer, "pointlist"); + break; + case WINED3D_PT_LINELIST: + shader_addline(buffer, "linelist"); + break; + case WINED3D_PT_LINESTRIP: + shader_addline(buffer, "linestrip"); + break; + case WINED3D_PT_TRIANGLELIST: + shader_addline(buffer, "trianglelist"); + break; + case WINED3D_PT_TRIANGLESTRIP: + shader_addline(buffer, "trianglestrip"); + break; + case WINED3D_PT_TRIANGLEFAN: + shader_addline(buffer, "trianglefan"); + break; + case WINED3D_PT_LINELIST_ADJ: + shader_addline(buffer, "linelist_adj"); + break; + case WINED3D_PT_LINESTRIP_ADJ: + shader_addline(buffer, "linestrip_adj"); + break; + case WINED3D_PT_TRIANGLELIST_ADJ: + shader_addline(buffer, "trianglelist_adj"); + break; + case WINED3D_PT_TRIANGLESTRIP_ADJ: + shader_addline(buffer, "trianglestrip_adj"); + break; + case WINED3D_PT_PATCH: + shader_addline(buffer, "patch%u", primitive_type->patch_vertex_count); + break; + default: + shader_addline(buffer, "", primitive_type->type); + break; + } +} + +static void shader_dump_interpolation_mode(struct wined3d_string_buffer *buffer, + enum wined3d_shader_interpolation_mode interpolation_mode) +{ + switch (interpolation_mode) + { + case WINED3DSIM_CONSTANT: + shader_addline(buffer, "constant"); + break; + case WINED3DSIM_LINEAR: + shader_addline(buffer, "linear"); + break; + case WINED3DSIM_LINEAR_CENTROID: + shader_addline(buffer, "linear centroid"); + break; + case WINED3DSIM_LINEAR_NOPERSPECTIVE: + shader_addline(buffer, "linear noperspective"); + break; + case WINED3DSIM_LINEAR_SAMPLE: + shader_addline(buffer, "linear sample"); + break; + case WINED3DSIM_LINEAR_NOPERSPECTIVE_CENTROID: + shader_addline(buffer, "linear noperspective centroid"); + break; + case WINED3DSIM_LINEAR_NOPERSPECTIVE_SAMPLE: + shader_addline(buffer, "linear noperspective sample"); + break; + default: + shader_addline(buffer, "", interpolation_mode); + break; + } +} + +static void shader_trace_init(const struct wined3d_shader_frontend *fe, void *fe_data) +{ + struct wined3d_shader_version shader_version; + struct wined3d_string_buffer buffer; + const char *type_prefix; + const char *p, *q; + const DWORD *ptr; + DWORD i; + + if (!string_buffer_init(&buffer)) + { + ERR("Failed to initialize string buffer.\n"); + return; + } + + fe->shader_read_header(fe_data, &ptr, &shader_version); + + TRACE("Parsing %p.\n", ptr); + + switch (shader_version.type) + { + case WINED3D_SHADER_TYPE_VERTEX: + type_prefix = "vs"; + break; + + case WINED3D_SHADER_TYPE_HULL: + type_prefix = "hs"; + break; + + case WINED3D_SHADER_TYPE_DOMAIN: + type_prefix = "ds"; + break; + + case WINED3D_SHADER_TYPE_GEOMETRY: + type_prefix = "gs"; + break; + + case WINED3D_SHADER_TYPE_PIXEL: + type_prefix = "ps"; + break; + + case WINED3D_SHADER_TYPE_COMPUTE: + type_prefix = "cs"; + break; + + default: + FIXME("Unhandled shader type %#x.\n", shader_version.type); + type_prefix = "unknown"; + break; + } + + shader_addline(&buffer, "%s_%u_%u\n", type_prefix, shader_version.major, shader_version.minor); + + while (!fe->shader_is_end(fe_data, &ptr)) + { + struct wined3d_shader_instruction ins; + + fe->shader_read_instruction(fe_data, &ptr, &ins); + if (ins.handler_idx == WINED3DSIH_TABLE_SIZE) + { + WARN("Skipping unrecognized instruction.\n"); + shader_addline(&buffer, "\n"); + continue; + } + + if (ins.handler_idx == WINED3DSIH_DCL || ins.handler_idx == WINED3DSIH_DCL_UAV_TYPED) + { + shader_dump_decl_usage(&buffer, &ins.declaration.semantic, ins.flags, &shader_version); + shader_dump_ins_modifiers(&buffer, &ins.declaration.semantic.reg); + shader_addline(&buffer, " "); + shader_dump_dst_param(&buffer, &ins.declaration.semantic.reg, &shader_version); + } + else if (ins.handler_idx == WINED3DSIH_DCL_CONSTANT_BUFFER) + { + shader_addline(&buffer, "%s ", shader_opcode_names[ins.handler_idx]); + shader_dump_src_param(&buffer, &ins.declaration.src, &shader_version); + shader_addline(&buffer, ", %s", + ins.flags & WINED3DSI_INDEXED_DYNAMIC ? "dynamicIndexed" : "immediateIndexed"); + } + else if (ins.handler_idx == WINED3DSIH_DCL_FUNCTION_BODY) + { + shader_addline(&buffer, "%s fb%u", + shader_opcode_names[ins.handler_idx], ins.declaration.index); + } + else if (ins.handler_idx == WINED3DSIH_DCL_FUNCTION_TABLE) + { + shader_addline(&buffer, "%s ft%u = {...}", + shader_opcode_names[ins.handler_idx], ins.declaration.index); + } + else if (ins.handler_idx == WINED3DSIH_DCL_GLOBAL_FLAGS) + { + shader_addline(&buffer, "%s ", shader_opcode_names[ins.handler_idx]); + shader_dump_global_flags(&buffer, ins.flags); + } + else if (ins.handler_idx == WINED3DSIH_DCL_HS_MAX_TESSFACTOR) + { + shader_addline(&buffer, "%s %.8e", shader_opcode_names[ins.handler_idx], + ins.declaration.max_tessellation_factor); + } + else if (ins.handler_idx == WINED3DSIH_DCL_IMMEDIATE_CONSTANT_BUFFER) + { + shader_addline(&buffer, "%s {\n", shader_opcode_names[ins.handler_idx]); + for (i = 0; i < ins.declaration.icb->vec4_count; ++i) + { + shader_addline(&buffer, " {0x%08x, 0x%08x, 0x%08x, 0x%08x},\n", + ins.declaration.icb->data[4 * i + 0], + ins.declaration.icb->data[4 * i + 1], + ins.declaration.icb->data[4 * i + 2], + ins.declaration.icb->data[4 * i + 3]); + } + shader_addline(&buffer, "}"); + } + else if (ins.handler_idx == WINED3DSIH_DCL_INDEX_RANGE) + { + shader_addline(&buffer, "%s ", shader_opcode_names[ins.handler_idx]); + shader_dump_dst_param(&buffer, &ins.declaration.index_range.first_register, &shader_version); + shader_addline(&buffer, " %u", ins.declaration.index_range.last_register); + } + else if (ins.handler_idx == WINED3DSIH_DCL_INDEXABLE_TEMP) + { + shader_addline(&buffer, "%s x[%u][%u], %u", shader_opcode_names[ins.handler_idx], + ins.declaration.indexable_temp.register_idx, + ins.declaration.indexable_temp.register_size, + ins.declaration.indexable_temp.component_count); + } + else if (ins.handler_idx == WINED3DSIH_DCL_INPUT_PS) + { + shader_addline(&buffer, "%s ", shader_opcode_names[ins.handler_idx]); + shader_dump_interpolation_mode(&buffer, ins.flags); + shader_addline(&buffer, " "); + shader_dump_dst_param(&buffer, &ins.declaration.dst, &shader_version); + } + else if (ins.handler_idx == WINED3DSIH_DCL_INPUT_PS_SGV + || ins.handler_idx == WINED3DSIH_DCL_INPUT_SGV + || ins.handler_idx == WINED3DSIH_DCL_INPUT_SIV + || ins.handler_idx == WINED3DSIH_DCL_OUTPUT_SIV) + { + shader_addline(&buffer, "%s ", shader_opcode_names[ins.handler_idx]); + shader_dump_dst_param(&buffer, &ins.declaration.register_semantic.reg, &shader_version); + shader_addline(&buffer, ", "); + shader_dump_shader_input_sysval_semantic(&buffer, ins.declaration.register_semantic.sysval_semantic); + } + else if (ins.handler_idx == WINED3DSIH_DCL_INPUT_PS_SIV) + { + shader_addline(&buffer, "%s ", shader_opcode_names[ins.handler_idx]); + shader_dump_interpolation_mode(&buffer, ins.flags); + shader_addline(&buffer, " "); + shader_dump_dst_param(&buffer, &ins.declaration.register_semantic.reg, &shader_version); + shader_addline(&buffer, ", "); + shader_dump_shader_input_sysval_semantic(&buffer, ins.declaration.register_semantic.sysval_semantic); + } + else if (ins.handler_idx == WINED3DSIH_DCL_INPUT + || ins.handler_idx == WINED3DSIH_DCL_OUTPUT) + { + shader_addline(&buffer, "%s ", shader_opcode_names[ins.handler_idx]); + shader_dump_dst_param(&buffer, &ins.declaration.dst, &shader_version); + } + else if (ins.handler_idx == WINED3DSIH_DCL_INPUT_PRIMITIVE + || ins.handler_idx == WINED3DSIH_DCL_OUTPUT_TOPOLOGY) + { + shader_addline(&buffer, "%s ", shader_opcode_names[ins.handler_idx]); + shader_dump_primitive_type(&buffer, &ins.declaration.primitive_type); + } + else if (ins.handler_idx == WINED3DSIH_DCL_INTERFACE) + { + shader_addline(&buffer, "%s fp[%u][%u][%u] = {...}", + shader_opcode_names[ins.handler_idx], ins.declaration.fp.index, + ins.declaration.fp.array_size, ins.declaration.fp.body_count); + } + else if (ins.handler_idx == WINED3DSIH_DCL_RESOURCE_RAW) + { + shader_addline(&buffer, "%s ", shader_opcode_names[ins.handler_idx]); + shader_dump_dst_param(&buffer, &ins.declaration.dst, &shader_version); + } + else if (ins.handler_idx == WINED3DSIH_DCL_RESOURCE_STRUCTURED) + { + shader_addline(&buffer, "%s ", shader_opcode_names[ins.handler_idx]); + shader_dump_dst_param(&buffer, &ins.declaration.structured_resource.reg, &shader_version); + shader_addline(&buffer, ", %u", ins.declaration.structured_resource.byte_stride); + } + else if (ins.handler_idx == WINED3DSIH_DCL_SAMPLER) + { + shader_addline(&buffer, "%s ", shader_opcode_names[ins.handler_idx]); + shader_dump_dst_param(&buffer, &ins.declaration.dst, &shader_version); + if (ins.flags == WINED3DSI_SAMPLER_COMPARISON_MODE) + shader_addline(&buffer, ", comparisonMode"); + } + else if (ins.handler_idx == WINED3DSIH_DCL_TEMPS + || ins.handler_idx == WINED3DSIH_DCL_GS_INSTANCES + || ins.handler_idx == WINED3DSIH_DCL_HS_FORK_PHASE_INSTANCE_COUNT + || ins.handler_idx == WINED3DSIH_DCL_HS_JOIN_PHASE_INSTANCE_COUNT + || ins.handler_idx == WINED3DSIH_DCL_INPUT_CONTROL_POINT_COUNT + || ins.handler_idx == WINED3DSIH_DCL_OUTPUT_CONTROL_POINT_COUNT + || ins.handler_idx == WINED3DSIH_DCL_VERTICES_OUT) + { + shader_addline(&buffer, "%s %u", shader_opcode_names[ins.handler_idx], ins.declaration.count); + } + else if (ins.handler_idx == WINED3DSIH_DCL_TESSELLATOR_DOMAIN) + { + shader_addline(&buffer, "%s ", shader_opcode_names[ins.handler_idx]); + shader_dump_tessellator_domain(&buffer, ins.declaration.tessellator_domain); + } + else if (ins.handler_idx == WINED3DSIH_DCL_TESSELLATOR_OUTPUT_PRIMITIVE) + { + shader_addline(&buffer, "%s ", shader_opcode_names[ins.handler_idx]); + shader_dump_tessellator_output_primitive(&buffer, ins.declaration.tessellator_output_primitive); + } + else if (ins.handler_idx == WINED3DSIH_DCL_TESSELLATOR_PARTITIONING) + { + shader_addline(&buffer, "%s ", shader_opcode_names[ins.handler_idx]); + shader_dump_tessellator_partitioning(&buffer, ins.declaration.tessellator_partitioning); + } + else if (ins.handler_idx == WINED3DSIH_DCL_TGSM_RAW) + { + shader_addline(&buffer, "%s ", shader_opcode_names[ins.handler_idx]); + shader_dump_dst_param(&buffer, &ins.declaration.tgsm_raw.reg, &shader_version); + shader_addline(&buffer, ", %u", ins.declaration.tgsm_raw.byte_count); + } + else if (ins.handler_idx == WINED3DSIH_DCL_TGSM_STRUCTURED) + { + shader_addline(&buffer, "%s ", shader_opcode_names[ins.handler_idx]); + shader_dump_dst_param(&buffer, &ins.declaration.tgsm_structured.reg, &shader_version); + shader_addline(&buffer, ", %u, %u", ins.declaration.tgsm_structured.byte_stride, + ins.declaration.tgsm_structured.structure_count); + } + else if (ins.handler_idx == WINED3DSIH_DCL_THREAD_GROUP) + { + shader_addline(&buffer, "%s %u, %u, %u", shader_opcode_names[ins.handler_idx], + ins.declaration.thread_group_size.x, + ins.declaration.thread_group_size.y, + ins.declaration.thread_group_size.z); + } + else if (ins.handler_idx == WINED3DSIH_DCL_UAV_RAW) + { + shader_addline(&buffer, "%s", shader_opcode_names[ins.handler_idx]); + shader_dump_uav_flags(&buffer, ins.flags); + shader_addline(&buffer, " "); + shader_dump_dst_param(&buffer, &ins.declaration.dst, &shader_version); + } + else if (ins.handler_idx == WINED3DSIH_DCL_UAV_STRUCTURED) + { + shader_addline(&buffer, "%s", shader_opcode_names[ins.handler_idx]); + shader_dump_uav_flags(&buffer, ins.flags); + shader_addline(&buffer, " "); + shader_dump_dst_param(&buffer, &ins.declaration.structured_resource.reg, &shader_version); + shader_addline(&buffer, ", %u", ins.declaration.structured_resource.byte_stride); + } + else if (ins.handler_idx == WINED3DSIH_DEF) + { + shader_addline(&buffer, "def c%u = %.8e, %.8e, %.8e, %.8e", shader_get_float_offset(ins.dst[0].reg.type, + ins.dst[0].reg.idx[0].offset), + *(const float *)&ins.src[0].reg.u.immconst_data[0], + *(const float *)&ins.src[0].reg.u.immconst_data[1], + *(const float *)&ins.src[0].reg.u.immconst_data[2], + *(const float *)&ins.src[0].reg.u.immconst_data[3]); + } + else if (ins.handler_idx == WINED3DSIH_DEFI) + { + shader_addline(&buffer, "defi i%u = %d, %d, %d, %d", ins.dst[0].reg.idx[0].offset, + ins.src[0].reg.u.immconst_data[0], + ins.src[0].reg.u.immconst_data[1], + ins.src[0].reg.u.immconst_data[2], + ins.src[0].reg.u.immconst_data[3]); + } + else if (ins.handler_idx == WINED3DSIH_DEFB) + { + shader_addline(&buffer, "defb b%u = %s", + ins.dst[0].reg.idx[0].offset, ins.src[0].reg.u.immconst_data[0] ? "true" : "false"); + } + else + { + if (ins.predicate) + { + shader_addline(&buffer, "("); + shader_dump_src_param(&buffer, ins.predicate, &shader_version); + shader_addline(&buffer, ") "); + } + + /* PixWin marks instructions with the coissue flag with a '+' */ + if (ins.coissue) + shader_addline(&buffer, "+"); + + shader_addline(&buffer, "%s", shader_opcode_names[ins.handler_idx]); + + if (ins.handler_idx == WINED3DSIH_BREAKP + || ins.handler_idx == WINED3DSIH_CONTINUEP + || ins.handler_idx == WINED3DSIH_IF + || ins.handler_idx == WINED3DSIH_RETP + || ins.handler_idx == WINED3DSIH_TEXKILL) + { + switch (ins.flags) + { + case WINED3D_SHADER_CONDITIONAL_OP_NZ: shader_addline(&buffer, "_nz"); break; + case WINED3D_SHADER_CONDITIONAL_OP_Z: shader_addline(&buffer, "_z"); break; + default: shader_addline(&buffer, "_unrecognized(%#x)", ins.flags); break; + } + } + else if (ins.handler_idx == WINED3DSIH_IFC + || ins.handler_idx == WINED3DSIH_BREAKC) + { + switch (ins.flags) + { + case WINED3D_SHADER_REL_OP_GT: shader_addline(&buffer, "_gt"); break; + case WINED3D_SHADER_REL_OP_EQ: shader_addline(&buffer, "_eq"); break; + case WINED3D_SHADER_REL_OP_GE: shader_addline(&buffer, "_ge"); break; + case WINED3D_SHADER_REL_OP_LT: shader_addline(&buffer, "_lt"); break; + case WINED3D_SHADER_REL_OP_NE: shader_addline(&buffer, "_ne"); break; + case WINED3D_SHADER_REL_OP_LE: shader_addline(&buffer, "_le"); break; + default: shader_addline(&buffer, "_(%u)", ins.flags); + } + } + else if (ins.handler_idx == WINED3DSIH_TEX + && shader_version.major >= 2 + && (ins.flags & WINED3DSI_TEXLD_PROJECT)) + { + shader_addline(&buffer, "p"); + } + else if (ins.handler_idx == WINED3DSIH_RESINFO && ins.flags) + { + switch (ins.flags) + { + case WINED3DSI_RESINFO_RCP_FLOAT: shader_addline(&buffer, "_rcpFloat"); break; + case WINED3DSI_RESINFO_UINT: shader_addline(&buffer, "_uint"); break; + default: shader_addline(&buffer, "_unrecognized(%#x)", ins.flags); + } + } + else if (ins.handler_idx == WINED3DSIH_SAMPLE_INFO && ins.flags) + { + switch (ins.flags) + { + case WINED3DSI_SAMPLE_INFO_UINT: shader_addline(&buffer, "_uint"); break; + default: shader_addline(&buffer, "_unrecognized(%#x)", ins.flags); + } + } + else if (ins.handler_idx == WINED3DSIH_SYNC) + { + shader_dump_sync_flags(&buffer, ins.flags); + } + else + { + shader_dump_precise_flags(&buffer, ins.flags); + } + + if (wined3d_shader_instruction_has_texel_offset(&ins)) + shader_addline(&buffer, "(%d,%d,%d)", ins.texel_offset.u, ins.texel_offset.v, ins.texel_offset.w); + + if (ins.resource_type != WINED3D_SHADER_RESOURCE_NONE) + { + shader_addline(&buffer, "("); + shader_dump_resource_type(&buffer, ins.resource_type); + shader_addline(&buffer, ")"); + } + + if (ins.resource_data_type != WINED3D_DATA_FLOAT) + shader_dump_data_type(&buffer, ins.resource_data_type); + + for (i = 0; i < ins.dst_count; ++i) + { + shader_dump_ins_modifiers(&buffer, &ins.dst[i]); + shader_addline(&buffer, !i ? " " : ", "); + shader_dump_dst_param(&buffer, &ins.dst[i], &shader_version); + } + + /* Other source tokens */ + for (i = ins.dst_count; i < (ins.dst_count + ins.src_count); ++i) + { + shader_addline(&buffer, !i ? " " : ", "); + shader_dump_src_param(&buffer, &ins.src[i - ins.dst_count], &shader_version); + } + } + shader_addline(&buffer, "\n"); + } + + for (p = buffer.buffer; *p; p = q) + { + if (!(q = strstr(p, "\n"))) + q = p + strlen(p); + else + ++q; + TRACE(" %.*s", (int)(q - p), p); + } + + string_buffer_free(&buffer); +} + +static void shader_cleanup(struct wined3d_shader *shader) +{ + if (shader->reg_maps.shader_version.type == WINED3D_SHADER_TYPE_HULL) + { + heap_free(shader->u.hs.phases.control_point); + heap_free(shader->u.hs.phases.fork); + heap_free(shader->u.hs.phases.join); + } + + heap_free(shader->patch_constant_signature.elements); + heap_free(shader->output_signature.elements); + heap_free(shader->input_signature.elements); + shader->device->shader_backend->shader_destroy(shader); + shader_cleanup_reg_maps(&shader->reg_maps); + heap_free(shader->byte_code); + shader_delete_constant_list(&shader->constantsF); + shader_delete_constant_list(&shader->constantsB); + shader_delete_constant_list(&shader->constantsI); + list_remove(&shader->shader_list_entry); + + if (shader->frontend && shader->frontend_data) + shader->frontend->shader_free(shader->frontend_data); +} + +struct shader_none_priv +{ + const struct wined3d_vertex_pipe_ops *vertex_pipe; + const struct wined3d_fragment_pipe_ops *fragment_pipe; + BOOL ffp_proj_control; +}; + +static void shader_none_handle_instruction(const struct wined3d_shader_instruction *ins) {} +static void shader_none_precompile(void *shader_priv, struct wined3d_shader *shader) {} +static void shader_none_select_compute(void *shader_priv, struct wined3d_context *context, + const struct wined3d_state *state) {} +static void shader_none_update_float_vertex_constants(struct wined3d_device *device, UINT start, UINT count) {} +static void shader_none_update_float_pixel_constants(struct wined3d_device *device, UINT start, UINT count) {} +static void shader_none_load_constants(void *shader_priv, struct wined3d_context *context, + const struct wined3d_state *state) {} +static void shader_none_destroy(struct wined3d_shader *shader) {} +static void shader_none_free_context_data(struct wined3d_context *context) {} +static void shader_none_init_context_state(struct wined3d_context *context) {} + +/* Context activation is done by the caller. */ +static void shader_none_select(void *shader_priv, struct wined3d_context *context, + const struct wined3d_state *state) +{ + struct shader_none_priv *priv = shader_priv; + + priv->vertex_pipe->vp_enable(context, !use_vs(state)); + priv->fragment_pipe->fp_enable(context, !use_ps(state)); +} + +/* Context activation is done by the caller. */ +static void shader_none_disable(void *shader_priv, struct wined3d_context *context) +{ + struct shader_none_priv *priv = shader_priv; + + priv->vertex_pipe->vp_enable(context, FALSE); + priv->fragment_pipe->fp_enable(context, FALSE); + + context->shader_update_mask = (1u << WINED3D_SHADER_TYPE_PIXEL) + | (1u << WINED3D_SHADER_TYPE_VERTEX) + | (1u << WINED3D_SHADER_TYPE_GEOMETRY) + | (1u << WINED3D_SHADER_TYPE_HULL) + | (1u << WINED3D_SHADER_TYPE_DOMAIN) + | (1u << WINED3D_SHADER_TYPE_COMPUTE); +} + +static HRESULT shader_none_alloc(struct wined3d_device *device, const struct wined3d_vertex_pipe_ops *vertex_pipe, + const struct wined3d_fragment_pipe_ops *fragment_pipe) +{ + struct fragment_caps fragment_caps; + void *vertex_priv, *fragment_priv; + struct shader_none_priv *priv; + + if (!(priv = heap_alloc(sizeof(*priv)))) + return E_OUTOFMEMORY; + + if (!(vertex_priv = vertex_pipe->vp_alloc(&none_shader_backend, priv))) + { + ERR("Failed to initialize vertex pipe.\n"); + heap_free(priv); + return E_FAIL; + } + + if (!(fragment_priv = fragment_pipe->alloc_private(&none_shader_backend, priv))) + { + ERR("Failed to initialize fragment pipe.\n"); + vertex_pipe->vp_free(device, NULL); + heap_free(priv); + return E_FAIL; + } + + priv->vertex_pipe = vertex_pipe; + priv->fragment_pipe = fragment_pipe; + fragment_pipe->get_caps(device->adapter, &fragment_caps); + priv->ffp_proj_control = fragment_caps.wined3d_caps & WINED3D_FRAGMENT_CAP_PROJ_CONTROL; + + device->vertex_priv = vertex_priv; + device->fragment_priv = fragment_priv; + device->shader_priv = priv; + + return WINED3D_OK; +} + +static void shader_none_free(struct wined3d_device *device, struct wined3d_context *context) +{ + struct shader_none_priv *priv = device->shader_priv; + + priv->fragment_pipe->free_private(device, context); + priv->vertex_pipe->vp_free(device, context); + heap_free(priv); +} + +static BOOL shader_none_allocate_context_data(struct wined3d_context *context) +{ + return TRUE; +} + +static void shader_none_get_caps(const struct wined3d_adapter *adapter, struct shader_caps *caps) +{ + /* Set the shader caps to 0 for the none shader backend */ + memset(caps, 0, sizeof(*caps)); +} + +static BOOL shader_none_color_fixup_supported(struct color_fixup_desc fixup) +{ + /* We "support" every possible fixup, since we don't support any shader + * model, and will never have to actually sample a texture. */ + return TRUE; +} + +static BOOL shader_none_has_ffp_proj_control(void *shader_priv) +{ + struct shader_none_priv *priv = shader_priv; + + return priv->ffp_proj_control; +} + +const struct wined3d_shader_backend_ops none_shader_backend = +{ + shader_none_handle_instruction, + shader_none_precompile, + shader_none_select, + shader_none_select_compute, + shader_none_disable, + shader_none_update_float_vertex_constants, + shader_none_update_float_pixel_constants, + shader_none_load_constants, + shader_none_destroy, + shader_none_alloc, + shader_none_free, + shader_none_allocate_context_data, + shader_none_free_context_data, + shader_none_init_context_state, + shader_none_get_caps, + shader_none_color_fixup_supported, + shader_none_has_ffp_proj_control, +}; + +static unsigned int shader_max_version_from_feature_level(enum wined3d_feature_level level) +{ + switch (level) + { + case WINED3D_FEATURE_LEVEL_11_1: + case WINED3D_FEATURE_LEVEL_11: + return 5; + case WINED3D_FEATURE_LEVEL_10_1: + case WINED3D_FEATURE_LEVEL_10: + return 4; + case WINED3D_FEATURE_LEVEL_9_3: + return 3; + case WINED3D_FEATURE_LEVEL_9_2: + case WINED3D_FEATURE_LEVEL_9_1: + return 2; + default: + return 1; + } +} + +static HRESULT shader_set_function(struct wined3d_shader *shader, struct wined3d_device *device, + enum wined3d_shader_type type, unsigned int float_const_count) +{ + const struct wined3d_d3d_info *d3d_info = &shader->device->adapter->d3d_info; + struct wined3d_shader_reg_maps *reg_maps = &shader->reg_maps; + const struct wined3d_shader_version *version = ®_maps->shader_version; + const struct wined3d_shader_frontend *fe; + unsigned int backend_version; + HRESULT hr; + + TRACE("shader %p, device %p, type %s, float_const_count %u.\n", + shader, device, debug_shader_type(type), float_const_count); + + fe = shader->frontend; + if (!(shader->frontend_data = fe->shader_init(shader->function, + shader->functionLength, &shader->output_signature))) + { + FIXME("Failed to initialize frontend.\n"); + return WINED3DERR_INVALIDCALL; + } + + /* First pass: trace shader. */ + if (TRACE_ON(d3d_shader)) + shader_trace_init(fe, shader->frontend_data); + + /* Second pass: figure out which registers are used, what the semantics are, etc. */ + if (FAILED(hr = shader_get_registers_used(shader, float_const_count))) + return hr; + + if (version->type != type) + { + WARN("Wrong shader type %s.\n", debug_shader_type(reg_maps->shader_version.type)); + return WINED3DERR_INVALIDCALL; + } + if (version->major > shader_max_version_from_feature_level(device->feature_level)) + { + WARN("Shader version %u not supported by this device.\n", version->major); + return WINED3DERR_INVALIDCALL; + } + switch (type) + { + case WINED3D_SHADER_TYPE_VERTEX: + backend_version = d3d_info->limits.vs_version; + break; + case WINED3D_SHADER_TYPE_HULL: + backend_version = d3d_info->limits.hs_version; + break; + case WINED3D_SHADER_TYPE_DOMAIN: + backend_version = d3d_info->limits.ds_version; + break; + case WINED3D_SHADER_TYPE_GEOMETRY: + backend_version = d3d_info->limits.gs_version; + break; + case WINED3D_SHADER_TYPE_PIXEL: + backend_version = d3d_info->limits.ps_version; + break; + case WINED3D_SHADER_TYPE_COMPUTE: + backend_version = d3d_info->limits.cs_version; + break; + default: + FIXME("No backend version-checking for this shader type.\n"); + backend_version = 0; + } + if (version->major > backend_version) + { + WARN("Shader version %u.%u not supported by the current shader backend.\n", + version->major, version->minor); + return WINED3DERR_INVALIDCALL; + } + + shader->load_local_constsF = shader->lconst_inf_or_nan; + + return WINED3D_OK; +} + +ULONG CDECL wined3d_shader_incref(struct wined3d_shader *shader) +{ + ULONG refcount = InterlockedIncrement(&shader->ref); + + TRACE("%p increasing refcount to %u.\n", shader, refcount); + + return refcount; +} + +static void wined3d_shader_init_object(void *object) +{ + struct wined3d_shader *shader = object; + struct wined3d_device *device = shader->device; + + TRACE("shader %p.\n", shader); + + list_add_head(&device->shaders, &shader->shader_list_entry); + + device->shader_backend->shader_precompile(device->shader_priv, shader); +} + +static void wined3d_shader_destroy_object(void *object) +{ + TRACE("object %p.\n", object); + + shader_cleanup(object); + heap_free(object); +} + +ULONG CDECL wined3d_shader_decref(struct wined3d_shader *shader) +{ + ULONG refcount = InterlockedDecrement(&shader->ref); + + TRACE("%p decreasing refcount to %u.\n", shader, refcount); + + if (!refcount) + { + shader->parent_ops->wined3d_object_destroyed(shader->parent); + wined3d_cs_destroy_object(shader->device->cs, wined3d_shader_destroy_object, shader); + } + + return refcount; +} + +void * CDECL wined3d_shader_get_parent(const struct wined3d_shader *shader) +{ + TRACE("shader %p.\n", shader); + + return shader->parent; +} + +HRESULT CDECL wined3d_shader_get_byte_code(const struct wined3d_shader *shader, + void *byte_code, UINT *byte_code_size) +{ + TRACE("shader %p, byte_code %p, byte_code_size %p.\n", shader, byte_code, byte_code_size); + + if (!byte_code) + { + *byte_code_size = shader->byte_code_size; + return WINED3D_OK; + } + + if (*byte_code_size < shader->byte_code_size) + { + /* MSDN claims (for d3d8 at least) that if *byte_code_size is smaller + * than the required size we should write the required size and + * return D3DERR_MOREDATA. That's not actually true. */ + return WINED3DERR_INVALIDCALL; + } + + memcpy(byte_code, shader->byte_code, shader->byte_code_size); + + return WINED3D_OK; +} + +/* Set local constants for d3d8 shaders. */ +HRESULT CDECL wined3d_shader_set_local_constants_float(struct wined3d_shader *shader, + UINT start_idx, const float *src_data, UINT count) +{ + UINT end_idx = start_idx + count; + UINT i; + + TRACE("shader %p, start_idx %u, src_data %p, count %u.\n", shader, start_idx, src_data, count); + + if (end_idx > shader->limits->constant_float) + { + WARN("end_idx %u > float constants limit %u.\n", + end_idx, shader->limits->constant_float); + end_idx = shader->limits->constant_float; + } + + for (i = start_idx; i < end_idx; ++i) + { + struct wined3d_shader_lconst *lconst; + float *value; + + if (!(lconst = heap_alloc(sizeof(*lconst)))) + return E_OUTOFMEMORY; + + lconst->idx = i; + value = (float *)lconst->value; + memcpy(value, src_data + (i - start_idx) * 4 /* 4 components */, 4 * sizeof(float)); + list_add_head(&shader->constantsF, &lconst->entry); + + if (isinf(value[0]) || isnan(value[0]) || isinf(value[1]) || isnan(value[1]) + || isinf(value[2]) || isnan(value[2]) || isinf(value[3]) || isnan(value[3])) + { + shader->lconst_inf_or_nan = TRUE; + } + } + + return WINED3D_OK; +} + +static void init_interpolation_compile_args(DWORD *interpolation_args, + const struct wined3d_shader *pixel_shader, const struct wined3d_d3d_info *d3d_info) +{ + if (!d3d_info->shader_output_interpolation || !pixel_shader + || pixel_shader->reg_maps.shader_version.major < 4) + { + memset(interpolation_args, 0, sizeof(pixel_shader->u.ps.interpolation_mode)); + return; + } + + memcpy(interpolation_args, pixel_shader->u.ps.interpolation_mode, + sizeof(pixel_shader->u.ps.interpolation_mode)); +} + +void find_vs_compile_args(const struct wined3d_state *state, const struct wined3d_shader *shader, + WORD swizzle_map, struct vs_compile_args *args, const struct wined3d_context *context) +{ + const struct wined3d_shader *geometry_shader = state->shader[WINED3D_SHADER_TYPE_GEOMETRY]; + const struct wined3d_shader *pixel_shader = state->shader[WINED3D_SHADER_TYPE_PIXEL]; + const struct wined3d_shader *hull_shader = state->shader[WINED3D_SHADER_TYPE_HULL]; + const struct wined3d_d3d_info *d3d_info = context->d3d_info; + + args->fog_src = state->render_states[WINED3D_RS_FOGTABLEMODE] + == WINED3D_FOG_NONE ? VS_FOG_COORD : VS_FOG_Z; + args->clip_enabled = state->render_states[WINED3D_RS_CLIPPING] + && state->render_states[WINED3D_RS_CLIPPLANEENABLE]; + args->point_size = state->primitive_type == WINED3D_PT_POINTLIST; + args->per_vertex_point_size = shader->reg_maps.point_size; + args->next_shader_type = hull_shader ? WINED3D_SHADER_TYPE_HULL + : geometry_shader ? WINED3D_SHADER_TYPE_GEOMETRY : WINED3D_SHADER_TYPE_PIXEL; + if (shader->reg_maps.shader_version.major >= 4) + args->next_shader_input_count = hull_shader ? hull_shader->limits->packed_input + : geometry_shader ? geometry_shader->limits->packed_input + : pixel_shader ? pixel_shader->limits->packed_input : 0; + else + args->next_shader_input_count = 0; + args->swizzle_map = swizzle_map; + if (d3d_info->emulated_flatshading) + args->flatshading = state->render_states[WINED3D_RS_SHADEMODE] == WINED3D_SHADE_FLAT; + else + args->flatshading = 0; + + init_interpolation_compile_args(args->interpolation_mode, + args->next_shader_type == WINED3D_SHADER_TYPE_PIXEL ? pixel_shader : NULL, d3d_info); +} + +static BOOL match_usage(BYTE usage1, BYTE usage_idx1, BYTE usage2, BYTE usage_idx2) +{ + if (usage_idx1 != usage_idx2) + return FALSE; + if (usage1 == usage2) + return TRUE; + if (usage1 == WINED3D_DECL_USAGE_POSITION && usage2 == WINED3D_DECL_USAGE_POSITIONT) + return TRUE; + if (usage2 == WINED3D_DECL_USAGE_POSITION && usage1 == WINED3D_DECL_USAGE_POSITIONT) + return TRUE; + + return FALSE; +} + +BOOL vshader_get_input(const struct wined3d_shader *shader, + BYTE usage_req, BYTE usage_idx_req, unsigned int *regnum) +{ + WORD map = shader->reg_maps.input_registers; + unsigned int i; + + for (i = 0; map; map >>= 1, ++i) + { + if (!(map & 1)) continue; + + if (match_usage(shader->u.vs.attributes[i].usage, + shader->u.vs.attributes[i].usage_idx, usage_req, usage_idx_req)) + { + *regnum = i; + return TRUE; + } + } + return FALSE; +} + +static HRESULT shader_init(struct wined3d_shader *shader, struct wined3d_device *device, + const struct wined3d_shader_desc *desc, void *parent, const struct wined3d_parent_ops *parent_ops) +{ + HRESULT hr; + + TRACE("byte_code %p, byte_code_size %#lx.\n", desc->byte_code, (long)desc->byte_code_size); + + if (!desc->byte_code) + return WINED3DERR_INVALIDCALL; + + shader->ref = 1; + shader->device = device; + shader->parent = parent; + shader->parent_ops = parent_ops; + + list_init(&shader->linked_programs); + list_init(&shader->constantsF); + list_init(&shader->constantsB); + list_init(&shader->constantsI); + shader->lconst_inf_or_nan = FALSE; + list_init(&shader->reg_maps.indexable_temps); + list_init(&shader->shader_list_entry); + + if (desc->byte_code_size == ~(size_t)0) + { + struct wined3d_shader_version shader_version; + const struct wined3d_shader_frontend *fe; + struct wined3d_shader_instruction ins; + const DWORD *ptr; + void *fe_data; + + if (!(shader->frontend = shader_select_frontend(WINED3D_SHADER_BYTE_CODE_FORMAT_SM1))) + { + FIXME("Unable to find frontend for shader.\n"); + hr = WINED3DERR_INVALIDCALL; + goto fail; + } + + fe = shader->frontend; + if (!(fe_data = fe->shader_init(desc->byte_code, desc->byte_code_size, &shader->output_signature))) + { + WARN("Failed to initialise frontend data.\n"); + hr = WINED3DERR_INVALIDCALL; + goto fail; + } + + fe->shader_read_header(fe_data, &ptr, &shader_version); + while (!fe->shader_is_end(fe_data, &ptr)) + fe->shader_read_instruction(fe_data, &ptr, &ins); + + fe->shader_free(fe_data); + + shader->byte_code_size = (ptr - desc->byte_code) * sizeof(*ptr); + + if (!(shader->byte_code = heap_alloc(shader->byte_code_size))) + { + hr = E_OUTOFMEMORY; + goto fail; + } + memcpy(shader->byte_code, desc->byte_code, shader->byte_code_size); + + shader->function = shader->byte_code; + shader->functionLength = shader->byte_code_size; + } + else + { + enum wined3d_shader_byte_code_format format; + unsigned int max_version; + + if (!(shader->byte_code = heap_alloc(desc->byte_code_size))) + { + hr = E_OUTOFMEMORY; + goto fail; + } + memcpy(shader->byte_code, desc->byte_code, desc->byte_code_size); + shader->byte_code_size = desc->byte_code_size; + + max_version = shader_max_version_from_feature_level(device->feature_level); + if (FAILED(hr = shader_extract_from_dxbc(shader, max_version, &format))) + goto fail; + + if (!(shader->frontend = shader_select_frontend(format))) + { + FIXME("Unable to find frontend for shader.\n"); + hr = WINED3DERR_INVALIDCALL; + goto fail; + } + } + + return WINED3D_OK; + +fail: + shader_cleanup(shader); + return hr; +} + +static HRESULT vertex_shader_init(struct wined3d_shader *shader, struct wined3d_device *device, + const struct wined3d_shader_desc *desc, void *parent, const struct wined3d_parent_ops *parent_ops) +{ + struct wined3d_shader_reg_maps *reg_maps = &shader->reg_maps; + unsigned int i; + HRESULT hr; + + if (FAILED(hr = shader_init(shader, device, desc, parent, parent_ops))) + return hr; + + if (FAILED(hr = shader_set_function(shader, device, + WINED3D_SHADER_TYPE_VERTEX, device->adapter->d3d_info.limits.vs_uniform_count))) + { + shader_cleanup(shader); + return hr; + } + + for (i = 0; i < shader->input_signature.element_count; ++i) + { + const struct wined3d_shader_signature_element *input = &shader->input_signature.elements[i]; + + if (!(reg_maps->input_registers & (1u << input->register_idx)) || !input->semantic_name) + continue; + + shader->u.vs.attributes[input->register_idx].usage = + shader_usage_from_semantic_name(input->semantic_name); + shader->u.vs.attributes[input->register_idx].usage_idx = input->semantic_idx; + } + + if (reg_maps->usesrelconstF && !list_empty(&shader->constantsF)) + shader->load_local_constsF = TRUE; + + return WINED3D_OK; +} + +static struct wined3d_shader_signature_element *shader_find_signature_element(const struct wined3d_shader_signature *s, + unsigned int stream_idx, const char *semantic_name, unsigned int semantic_idx) +{ + struct wined3d_shader_signature_element *e = s->elements; + unsigned int i; + + for (i = 0; i < s->element_count; ++i) + { + if (e[i].stream_idx == stream_idx + && !_strnicmp(e[i].semantic_name, semantic_name, -1) + && e[i].semantic_idx == semantic_idx) + return &e[i]; + } + + return NULL; +} + +BOOL shader_get_stream_output_register_info(const struct wined3d_shader *shader, + const struct wined3d_stream_output_element *so_element, unsigned int *register_idx, unsigned int *component_idx) +{ + const struct wined3d_shader_signature_element *output; + unsigned int idx; + + if (!(output = shader_find_signature_element(&shader->output_signature, + so_element->stream_idx, so_element->semantic_name, so_element->semantic_idx))) + return FALSE; + + for (idx = 0; idx < 4; ++idx) + { + if (output->mask & (1u << idx)) + break; + } + idx += so_element->component_idx; + + *register_idx = output->register_idx; + *component_idx = idx; + return TRUE; +} + +static HRESULT geometry_shader_init_so_desc(struct wined3d_geometry_shader *gs, struct wined3d_device *device, + const struct wined3d_stream_output_desc *so_desc) +{ + struct wined3d_so_desc_entry *s; + struct wine_rb_entry *entry; + unsigned int i; + size_t size; + char *name; + + if ((entry = wine_rb_get(&device->so_descs, so_desc))) + { + gs->so_desc = &WINE_RB_ENTRY_VALUE(entry, struct wined3d_so_desc_entry, entry)->desc; + return WINED3D_OK; + } + + size = FIELD_OFFSET(struct wined3d_so_desc_entry, elements[so_desc->element_count]); + for (i = 0; i < so_desc->element_count; ++i) + { + const char *n = so_desc->elements[i].semantic_name; + + if (n) + size += strlen(n) + 1; + } + if (!(s = heap_alloc(size))) + return E_OUTOFMEMORY; + + s->desc = *so_desc; + + memcpy(s->elements, so_desc->elements, so_desc->element_count * sizeof(*s->elements)); + s->desc.elements = s->elements; + + name = (char *)&s->elements[s->desc.element_count]; + for (i = 0; i < so_desc->element_count; ++i) + { + struct wined3d_stream_output_element *e = &s->elements[i]; + + if (!e->semantic_name) + continue; + + size = strlen(e->semantic_name) + 1; + memcpy(name, e->semantic_name, size); + e->semantic_name = name; + name += size; + } + + if (wine_rb_put(&device->so_descs, &s->desc, &s->entry) == -1) + { + heap_free(s); + return E_FAIL; + } + gs->so_desc = &s->desc; + + return WINED3D_OK; +} + +static HRESULT geometry_shader_init_stream_output(struct wined3d_shader *shader, + const struct wined3d_stream_output_desc *so_desc) +{ + const struct wined3d_shader_frontend *fe = shader->frontend; + const struct wined3d_shader_signature_element *output; + unsigned int i, component_idx, register_idx, mask; + struct wined3d_shader_version shader_version; + const DWORD *ptr; + void *fe_data; + HRESULT hr; + + if (!so_desc) + return WINED3D_OK; + + if (!(fe_data = fe->shader_init(shader->function, shader->functionLength, &shader->output_signature))) + { + WARN("Failed to initialise frontend data.\n"); + return WINED3DERR_INVALIDCALL; + } + fe->shader_read_header(fe_data, &ptr, &shader_version); + fe->shader_free(fe_data); + + switch (shader_version.type) + { + case WINED3D_SHADER_TYPE_VERTEX: + case WINED3D_SHADER_TYPE_DOMAIN: + shader->function = NULL; + shader->functionLength = 0; + break; + case WINED3D_SHADER_TYPE_GEOMETRY: + break; + default: + WARN("Wrong shader type %s.\n", debug_shader_type(shader_version.type)); + return E_INVALIDARG; + } + + if (!shader->function) + { + shader->reg_maps.shader_version = shader_version; + shader->reg_maps.shader_version.type = WINED3D_SHADER_TYPE_GEOMETRY; + shader_set_limits(shader); + if (FAILED(hr = shader_scan_output_signature(shader))) + return hr; + } + + for (i = 0; i < so_desc->element_count; ++i) + { + const struct wined3d_stream_output_element *e = &so_desc->elements[i]; + + if (!e->semantic_name) + continue; + if (!(output = shader_find_signature_element(&shader->output_signature, + e->stream_idx, e->semantic_name, e->semantic_idx)) + || !shader_get_stream_output_register_info(shader, e, ®ister_idx, &component_idx)) + { + WARN("Failed to find output signature element for stream output entry.\n"); + return E_INVALIDARG; + } + + mask = ((1u << e->component_count) - 1) << component_idx; + if ((output->mask & 0xff & mask) != mask) + { + WARN("Invalid component range %u-%u (mask %#x), output mask %#x.\n", + component_idx, e->component_count, mask, output->mask & 0xff); + return E_INVALIDARG; + } + } + + if (FAILED(hr = geometry_shader_init_so_desc(&shader->u.gs, shader->device, so_desc))) + { + WARN("Failed to initialise stream output description, hr %#x.\n", hr); + return hr; + } + + return WINED3D_OK; +} + +static HRESULT geometry_shader_init(struct wined3d_shader *shader, struct wined3d_device *device, + const struct wined3d_shader_desc *desc, const struct wined3d_stream_output_desc *so_desc, + void *parent, const struct wined3d_parent_ops *parent_ops) +{ + HRESULT hr; + + if (FAILED(hr = shader_init(shader, device, desc, parent, parent_ops))) + return hr; + + if (FAILED(hr = geometry_shader_init_stream_output(shader, so_desc))) + goto fail; + + if (shader->function + && FAILED(hr = shader_set_function(shader, device, WINED3D_SHADER_TYPE_GEOMETRY, 0))) + goto fail; + + return WINED3D_OK; + +fail: + shader_cleanup(shader); + return hr; +} + +void find_ds_compile_args(const struct wined3d_state *state, const struct wined3d_shader *shader, + struct ds_compile_args *args, const struct wined3d_context *context) +{ + const struct wined3d_shader *geometry_shader = state->shader[WINED3D_SHADER_TYPE_GEOMETRY]; + const struct wined3d_shader *pixel_shader = state->shader[WINED3D_SHADER_TYPE_PIXEL]; + const struct wined3d_shader *hull_shader = state->shader[WINED3D_SHADER_TYPE_HULL]; + + args->tessellator_output_primitive = hull_shader->u.hs.tessellator_output_primitive; + args->tessellator_partitioning = hull_shader->u.hs.tessellator_partitioning; + + args->output_count = geometry_shader ? geometry_shader->limits->packed_input + : pixel_shader ? pixel_shader->limits->packed_input : shader->limits->packed_output; + args->next_shader_type = geometry_shader ? WINED3D_SHADER_TYPE_GEOMETRY : WINED3D_SHADER_TYPE_PIXEL; + + args->render_offscreen = context->render_offscreen; + + init_interpolation_compile_args(args->interpolation_mode, + args->next_shader_type == WINED3D_SHADER_TYPE_PIXEL ? pixel_shader : NULL, context->d3d_info); + + args->padding = 0; +} + +void find_gs_compile_args(const struct wined3d_state *state, const struct wined3d_shader *shader, + struct gs_compile_args *args, const struct wined3d_context *context) +{ + const struct wined3d_shader *pixel_shader = state->shader[WINED3D_SHADER_TYPE_PIXEL]; + + args->output_count = pixel_shader ? pixel_shader->limits->packed_input : shader->limits->packed_output; + + if (!(args->primitive_type = shader->u.gs.input_type)) + args->primitive_type = state->primitive_type; + + init_interpolation_compile_args(args->interpolation_mode, pixel_shader, context->d3d_info); +} + +void find_ps_compile_args(const struct wined3d_state *state, const struct wined3d_shader *shader, + BOOL position_transformed, struct ps_compile_args *args, const struct wined3d_context *context) +{ + const struct wined3d_gl_info *gl_info = &context->device->adapter->gl_info; + const struct wined3d_d3d_info *d3d_info = context->d3d_info; + struct wined3d_texture *texture; + unsigned int i; + + memset(args, 0, sizeof(*args)); /* FIXME: Make sure all bits are set. */ + if (!d3d_info->srgb_write_control && needs_srgb_write(d3d_info, state, &state->fb)) + { + static unsigned int warned = 0; + + args->srgb_correction = 1; + if (state->blend_state && state->blend_state->desc.rt[0].enable && !warned++) + WARN("Blending into a sRGB render target with no GL_ARB_framebuffer_sRGB " + "support, expect rendering artifacts.\n"); + } + + if (shader->reg_maps.shader_version.major == 1 + && shader->reg_maps.shader_version.minor <= 3) + { + for (i = 0; i < shader->limits->sampler; ++i) + { + DWORD flags = state->texture_states[i][WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS]; + + if (flags & WINED3D_TTFF_PROJECTED) + { + DWORD tex_transform = flags & ~WINED3D_TTFF_PROJECTED; + + if (!state->shader[WINED3D_SHADER_TYPE_VERTEX]) + { + enum wined3d_shader_resource_type resource_type = shader->reg_maps.resource_info[i].type; + unsigned int j; + unsigned int index = state->texture_states[i][WINED3D_TSS_TEXCOORD_INDEX]; + DWORD max_valid = WINED3D_TTFF_COUNT4; + + for (j = 0; j < state->vertex_declaration->element_count; ++j) + { + struct wined3d_vertex_declaration_element *element = + &state->vertex_declaration->elements[j]; + + if (element->usage == WINED3D_DECL_USAGE_TEXCOORD + && element->usage_idx == index) + { + max_valid = element->format->component_count; + break; + } + } + if (!tex_transform || tex_transform > max_valid) + { + WARN("Fixing up projected texture transform flags from %#x to %#x.\n", + tex_transform, max_valid); + tex_transform = max_valid; + } + if ((resource_type == WINED3D_SHADER_RESOURCE_TEXTURE_1D && tex_transform > WINED3D_TTFF_COUNT1) + || (resource_type == WINED3D_SHADER_RESOURCE_TEXTURE_2D + && tex_transform > WINED3D_TTFF_COUNT2) + || (resource_type == WINED3D_SHADER_RESOURCE_TEXTURE_3D + && tex_transform > WINED3D_TTFF_COUNT3)) + tex_transform |= WINED3D_PSARGS_PROJECTED; + else + { + WARN("Application requested projected texture with unsuitable texture coordinates.\n"); + WARN("(texture unit %u, transform flags %#x, sampler type %u).\n", + i, tex_transform, resource_type); + } + } + else + tex_transform = WINED3D_TTFF_COUNT4 | WINED3D_PSARGS_PROJECTED; + + args->tex_transform |= tex_transform << i * WINED3D_PSARGS_TEXTRANSFORM_SHIFT; + } + } + } + if (shader->reg_maps.shader_version.major == 1 + && shader->reg_maps.shader_version.minor <= 4) + { + for (i = 0; i < shader->limits->sampler; ++i) + { + if (!shader->reg_maps.resource_info[i].type) + continue; + + /* Treat unbound textures as 2D. The dummy texture will provide + * the proper sample value. The tex_types bitmap defaults to + * 2D because of the memset. */ + if (!(texture = state->textures[i])) + continue; + + switch (wined3d_texture_gl(texture)->target) + { + /* RECT textures are distinguished from 2D textures via np2_fixup */ + default: + break; + + case GL_TEXTURE_3D: + args->tex_types |= WINED3D_SHADER_TEX_3D << i * WINED3D_PSARGS_TEXTYPE_SHIFT; + break; + + case GL_TEXTURE_CUBE_MAP_ARB: + args->tex_types |= WINED3D_SHADER_TEX_CUBE << i * WINED3D_PSARGS_TEXTYPE_SHIFT; + break; + } + } + } + else if (shader->reg_maps.shader_version.major <= 3) + { + for (i = 0; i < shader->limits->sampler; ++i) + { + enum wined3d_shader_resource_type resource_type; + enum wined3d_shader_tex_types tex_type; + + if (!(resource_type = shader->reg_maps.resource_info[i].type)) + continue; + + switch (resource_type) + { + case WINED3D_SHADER_RESOURCE_TEXTURE_3D: + tex_type = WINED3D_SHADER_TEX_3D; + break; + case WINED3D_SHADER_RESOURCE_TEXTURE_CUBE: + tex_type = WINED3D_SHADER_TEX_CUBE; + break; + default: + tex_type = WINED3D_SHADER_TEX_2D; + break; + } + + if ((texture = state->textures[i])) + { + if (texture->resource.type == WINED3D_RTYPE_TEXTURE_2D + && resource_type == WINED3D_SHADER_RESOURCE_TEXTURE_3D + && !(texture->resource.usage & WINED3DUSAGE_LEGACY_CUBEMAP)) + tex_type = WINED3D_SHADER_TEX_2D; + else if (texture->resource.type == WINED3D_RTYPE_TEXTURE_3D + && resource_type == WINED3D_SHADER_RESOURCE_TEXTURE_2D) + tex_type = WINED3D_SHADER_TEX_3D; + } + args->tex_types |= tex_type << i * WINED3D_PSARGS_TEXTYPE_SHIFT; + } + } + + if (shader->reg_maps.shader_version.major >= 4) + { + /* In SM4+ we use dcl_sampler in order to determine if we should use shadow sampler. */ + args->shadow = 0; + for (i = 0 ; i < WINED3D_MAX_FRAGMENT_SAMPLERS; ++i) + args->color_fixup[i] = COLOR_FIXUP_IDENTITY; + args->np2_fixup = 0; + } + else + { + for (i = 0; i < WINED3D_MAX_FRAGMENT_SAMPLERS; ++i) + { + if (!shader->reg_maps.resource_info[i].type) + continue; + + texture = state->textures[i]; + if (!texture) + { + args->color_fixup[i] = COLOR_FIXUP_IDENTITY; + continue; + } + if (can_use_texture_swizzle(d3d_info, texture->resource.format)) + args->color_fixup[i] = COLOR_FIXUP_IDENTITY; + else + args->color_fixup[i] = texture->resource.format->color_fixup; + + if (texture->resource.format_flags & WINED3DFMT_FLAG_SHADOW) + args->shadow |= 1u << i; + + /* Flag samplers that need NP2 texcoord fixup. */ + if (!(texture->flags & WINED3D_TEXTURE_POW2_MAT_IDENT)) + args->np2_fixup |= (1u << i); + } + } + + if (shader->reg_maps.shader_version.major >= 3) + { + if (position_transformed) + args->vp_mode = WINED3D_VP_MODE_NONE; + else if (use_vs(state)) + args->vp_mode = WINED3D_VP_MODE_SHADER; + else + args->vp_mode = WINED3D_VP_MODE_FF; + args->fog = WINED3D_FFP_PS_FOG_OFF; + } + else + { + args->vp_mode = WINED3D_VP_MODE_SHADER; + if (state->render_states[WINED3D_RS_FOGENABLE]) + { + switch (state->render_states[WINED3D_RS_FOGTABLEMODE]) + { + case WINED3D_FOG_NONE: + if (position_transformed || use_vs(state)) + { + args->fog = WINED3D_FFP_PS_FOG_LINEAR; + break; + } + + switch (state->render_states[WINED3D_RS_FOGVERTEXMODE]) + { + case WINED3D_FOG_NONE: /* Fall through. */ + case WINED3D_FOG_LINEAR: args->fog = WINED3D_FFP_PS_FOG_LINEAR; break; + case WINED3D_FOG_EXP: args->fog = WINED3D_FFP_PS_FOG_EXP; break; + case WINED3D_FOG_EXP2: args->fog = WINED3D_FFP_PS_FOG_EXP2; break; + } + break; + + case WINED3D_FOG_LINEAR: args->fog = WINED3D_FFP_PS_FOG_LINEAR; break; + case WINED3D_FOG_EXP: args->fog = WINED3D_FFP_PS_FOG_EXP; break; + case WINED3D_FOG_EXP2: args->fog = WINED3D_FFP_PS_FOG_EXP2; break; + } + } + else + { + args->fog = WINED3D_FFP_PS_FOG_OFF; + } + } + + if (!d3d_info->full_ffp_varyings) + { + const struct wined3d_shader *vs = state->shader[WINED3D_SHADER_TYPE_VERTEX]; + + args->texcoords_initialized = 0; + for (i = 0; i < WINED3D_MAX_TEXTURES; ++i) + { + if (vs) + { + if (state->shader[WINED3D_SHADER_TYPE_VERTEX]->reg_maps.output_registers & (1u << i)) + args->texcoords_initialized |= 1u << i; + } + else + { + const struct wined3d_stream_info *si = &context->stream_info; + unsigned int coord_idx = state->texture_states[i][WINED3D_TSS_TEXCOORD_INDEX]; + + if ((state->texture_states[i][WINED3D_TSS_TEXCOORD_INDEX] >> WINED3D_FFP_TCI_SHIFT) + & WINED3D_FFP_TCI_MASK + || (coord_idx < WINED3D_MAX_TEXTURES && (si->use_map & (1u << (WINED3D_FFP_TEXCOORD0 + coord_idx))))) + args->texcoords_initialized |= 1u << i; + } + } + } + else + { + args->texcoords_initialized = (1u << WINED3D_MAX_TEXTURES) - 1; + } + + args->pointsprite = state->render_states[WINED3D_RS_POINTSPRITEENABLE] + && state->primitive_type == WINED3D_PT_POINTLIST; + + if (d3d_info->ffp_alpha_test) + args->alpha_test_func = WINED3D_CMP_ALWAYS - 1; + else + args->alpha_test_func = (state->render_states[WINED3D_RS_ALPHATESTENABLE] + ? wined3d_sanitize_cmp_func(state->render_states[WINED3D_RS_ALPHAFUNC]) + : WINED3D_CMP_ALWAYS) - 1; + + if (d3d_info->emulated_flatshading) + args->flatshading = state->render_states[WINED3D_RS_SHADEMODE] == WINED3D_SHADE_FLAT; + + args->render_offscreen = shader->reg_maps.vpos && gl_info->supported[ARB_FRAGMENT_COORD_CONVENTIONS] + ? context->render_offscreen : 0; + + for (i = 0; i < ARRAY_SIZE(state->fb.render_targets); ++i) + { + struct wined3d_rendertarget_view *rtv = state->fb.render_targets[i]; + if (rtv && rtv->format->id == WINED3DFMT_A8_UNORM && !is_identity_fixup(rtv->format->color_fixup)) + args->rt_alpha_swizzle |= 1u << i; + } + + args->dual_source_blend = state->blend_state && state->blend_state->dual_source; +} + +static HRESULT pixel_shader_init(struct wined3d_shader *shader, struct wined3d_device *device, + const struct wined3d_shader_desc *desc, void *parent, const struct wined3d_parent_ops *parent_ops) +{ + const struct wined3d_gl_info *gl_info = &device->adapter->gl_info; + unsigned int i, highest_reg_used = 0, num_regs_used = 0; + HRESULT hr; + + if (FAILED(hr = shader_init(shader, device, desc, parent, parent_ops))) + return hr; + + if (FAILED(hr = shader_set_function(shader, device, + WINED3D_SHADER_TYPE_PIXEL, device->adapter->d3d_info.limits.ps_uniform_count))) + { + shader_cleanup(shader); + return hr; + } + + for (i = 0; i < MAX_REG_INPUT; ++i) + { + if (shader->u.ps.input_reg_used & (1u << i)) + { + ++num_regs_used; + highest_reg_used = i; + } + } + + /* Don't do any register mapping magic if it is not needed, or if we can't + * achieve anything anyway */ + if (highest_reg_used < (gl_info->limits.glsl_varyings / 4) + || num_regs_used > (gl_info->limits.glsl_varyings / 4) + || shader->reg_maps.shader_version.major >= 4) + { + if (num_regs_used > (gl_info->limits.glsl_varyings / 4)) + { + /* This happens with relative addressing. The input mapper function + * warns about this if the higher registers are declared too, so + * don't write a FIXME here */ + WARN("More varying registers used than supported\n"); + } + + for (i = 0; i < MAX_REG_INPUT; ++i) + { + shader->u.ps.input_reg_map[i] = i; + } + + shader->u.ps.declared_in_count = highest_reg_used + 1; + } + else + { + shader->u.ps.declared_in_count = 0; + for (i = 0; i < MAX_REG_INPUT; ++i) + { + if (shader->u.ps.input_reg_used & (1u << i)) + shader->u.ps.input_reg_map[i] = shader->u.ps.declared_in_count++; + else shader->u.ps.input_reg_map[i] = ~0U; + } + } + + return WINED3D_OK; +} + +enum wined3d_shader_resource_type pixelshader_get_resource_type(const struct wined3d_shader_reg_maps *reg_maps, + unsigned int resource_idx, DWORD tex_types) +{ + static enum wined3d_shader_resource_type shader_resource_type_from_shader_tex_types[] = + { + WINED3D_SHADER_RESOURCE_TEXTURE_2D, /* WINED3D_SHADER_TEX_2D */ + WINED3D_SHADER_RESOURCE_TEXTURE_3D, /* WINED3D_SHADER_TEX_3D */ + WINED3D_SHADER_RESOURCE_TEXTURE_CUBE, /* WINED3D_SHADER_TEX_CUBE */ + }; + + unsigned int idx; + + if (reg_maps->shader_version.major > 3) + return reg_maps->resource_info[resource_idx].type; + + if (!reg_maps->resource_info[resource_idx].type) + return 0; + + idx = (tex_types >> resource_idx * WINED3D_PSARGS_TEXTYPE_SHIFT) & WINED3D_PSARGS_TEXTYPE_MASK; + assert(idx < ARRAY_SIZE(shader_resource_type_from_shader_tex_types)); + return shader_resource_type_from_shader_tex_types[idx]; +} + +HRESULT CDECL wined3d_shader_create_cs(struct wined3d_device *device, const struct wined3d_shader_desc *desc, + void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_shader **shader) +{ + struct wined3d_shader *object; + HRESULT hr; + + TRACE("device %p, desc %p, parent %p, parent_ops %p, shader %p.\n", + device, desc, parent, parent_ops, shader); + + if (!(object = heap_alloc_zero(sizeof(*object)))) + return E_OUTOFMEMORY; + + if (FAILED(hr = shader_init(object, device, desc, parent, parent_ops))) + { + WARN("Failed to initialize compute shader, hr %#x.\n", hr); + heap_free(object); + return hr; + } + + if (FAILED(hr = shader_set_function(object, device, WINED3D_SHADER_TYPE_COMPUTE, 0))) + { + shader_cleanup(object); + heap_free(object); + return hr; + } + + wined3d_cs_init_object(device->cs, wined3d_shader_init_object, object); + + TRACE("Created compute shader %p.\n", object); + *shader = object; + + return WINED3D_OK; +} + +HRESULT CDECL wined3d_shader_create_ds(struct wined3d_device *device, const struct wined3d_shader_desc *desc, + void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_shader **shader) +{ + struct wined3d_shader *object; + HRESULT hr; + + TRACE("device %p, desc %p, parent %p, parent_ops %p, shader %p.\n", + device, desc, parent, parent_ops, shader); + + if (!(object = heap_alloc_zero(sizeof(*object)))) + return E_OUTOFMEMORY; + + if (FAILED(hr = shader_init(object, device, desc, parent, parent_ops))) + { + WARN("Failed to initialize domain shader, hr %#x.\n", hr); + heap_free(object); + return hr; + } + + if (FAILED(hr = shader_set_function(object, device, WINED3D_SHADER_TYPE_DOMAIN, 0))) + { + shader_cleanup(object); + heap_free(object); + return hr; + } + + wined3d_cs_init_object(device->cs, wined3d_shader_init_object, object); + + TRACE("Created domain shader %p.\n", object); + *shader = object; + + return WINED3D_OK; +} + +HRESULT CDECL wined3d_shader_create_gs(struct wined3d_device *device, const struct wined3d_shader_desc *desc, + const struct wined3d_stream_output_desc *so_desc, void *parent, + const struct wined3d_parent_ops *parent_ops, struct wined3d_shader **shader) +{ + struct wined3d_shader *object; + HRESULT hr; + + TRACE("device %p, desc %p, so_desc %p, parent %p, parent_ops %p, shader %p.\n", + device, desc, so_desc, parent, parent_ops, shader); + + if (!(object = heap_alloc_zero(sizeof(*object)))) + return E_OUTOFMEMORY; + + if (FAILED(hr = geometry_shader_init(object, device, desc, so_desc, parent, parent_ops))) + { + WARN("Failed to initialize geometry shader, hr %#x.\n", hr); + heap_free(object); + return hr; + } + + wined3d_cs_init_object(device->cs, wined3d_shader_init_object, object); + + TRACE("Created geometry shader %p.\n", object); + *shader = object; + + return WINED3D_OK; +} + +HRESULT CDECL wined3d_shader_create_hs(struct wined3d_device *device, const struct wined3d_shader_desc *desc, + void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_shader **shader) +{ + struct wined3d_shader *object; + HRESULT hr; + + TRACE("device %p, desc %p, parent %p, parent_ops %p, shader %p.\n", + device, desc, parent, parent_ops, shader); + + if (!(object = heap_alloc_zero(sizeof(*object)))) + return E_OUTOFMEMORY; + + if (FAILED(hr = shader_init(object, device, desc, parent, parent_ops))) + { + WARN("Failed to initialize hull shader, hr %#x.\n", hr); + heap_free(object); + return hr; + } + + if (FAILED(hr = shader_set_function(object, device, WINED3D_SHADER_TYPE_HULL, 0))) + { + shader_cleanup(object); + heap_free(object); + return hr; + } + + wined3d_cs_init_object(device->cs, wined3d_shader_init_object, object); + + TRACE("Created hull shader %p.\n", object); + *shader = object; + + return WINED3D_OK; +} + +HRESULT CDECL wined3d_shader_create_ps(struct wined3d_device *device, const struct wined3d_shader_desc *desc, + void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_shader **shader) +{ + struct wined3d_shader *object; + HRESULT hr; + + TRACE("device %p, desc %p, parent %p, parent_ops %p, shader %p.\n", + device, desc, parent, parent_ops, shader); + + if (!(object = heap_alloc_zero(sizeof(*object)))) + return E_OUTOFMEMORY; + + if (FAILED(hr = pixel_shader_init(object, device, desc, parent, parent_ops))) + { + WARN("Failed to initialize pixel shader, hr %#x.\n", hr); + heap_free(object); + return hr; + } + + wined3d_cs_init_object(device->cs, wined3d_shader_init_object, object); + + TRACE("Created pixel shader %p.\n", object); + *shader = object; + + return WINED3D_OK; +} + +HRESULT CDECL wined3d_shader_create_vs(struct wined3d_device *device, const struct wined3d_shader_desc *desc, + void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_shader **shader) +{ + struct wined3d_shader *object; + HRESULT hr; + + TRACE("device %p, desc %p, parent %p, parent_ops %p, shader %p.\n", + device, desc, parent, parent_ops, shader); + + if (!(object = heap_alloc_zero(sizeof(*object)))) + return E_OUTOFMEMORY; + + if (FAILED(hr = vertex_shader_init(object, device, desc, parent, parent_ops))) + { + WARN("Failed to initialize vertex shader, hr %#x.\n", hr); + heap_free(object); + return hr; + } + + wined3d_cs_init_object(device->cs, wined3d_shader_init_object, object); + + TRACE("Created vertex shader %p.\n", object); + *shader = object; + + return WINED3D_OK; +} diff --git a/wrappers/directx/d3dwine_wrapper/shader_sm1.c b/wrappers/directx/d3dwine_wrapper/shader_sm1.c new file mode 100644 index 00000000000..0c6bb933174 --- /dev/null +++ b/wrappers/directx/d3dwine_wrapper/shader_sm1.c @@ -0,0 +1,814 @@ +/* + * Copyright 2002-2003 Jason Edmeades + * Copyright 2002-2003 Raphael Junqueira + * Copyright 2004 Christian Costa + * Copyright 2005 Oliver Stieber + * Copyright 2006 Ivan Gyurdiev + * Copyright 2007-2008 Stefan Dösinger for CodeWeavers + * Copyright 2009 Henri Verbeet for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "config.h" +#include "wine/port.h" + +#include "wined3d_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(d3d_shader); + +/* DCL usage masks */ +#define WINED3D_SM1_DCL_USAGE_SHIFT 0 +#define WINED3D_SM1_DCL_USAGE_MASK (0xfu << WINED3D_SM1_DCL_USAGE_SHIFT) +#define WINED3D_SM1_DCL_USAGE_INDEX_SHIFT 16 +#define WINED3D_SM1_DCL_USAGE_INDEX_MASK (0xfu << WINED3D_SM1_DCL_USAGE_INDEX_SHIFT) + +/* DCL sampler type */ +#define WINED3D_SM1_RESOURCE_TYPE_SHIFT 27 +#define WINED3D_SM1_RESOURCE_TYPE_MASK (0xfu << WINED3D_SM1_RESOURCE_TYPE_SHIFT) + +/* Opcode-related masks */ +#define WINED3D_SM1_OPCODE_MASK 0x0000ffff + +#define WINED3D_SM1_INSTRUCTION_FLAGS_SHIFT 16 +#define WINED3D_SM1_INSTRUCTION_FLAGS_MASK (0xffu << WINED3D_SM1_INSTRUCTION_FLAGS_SHIFT) + +#define WINED3D_SM1_INSTRUCTION_LENGTH_SHIFT 24 +#define WINED3D_SM1_INSTRUCTION_LENGTH_MASK (0xfu << WINED3D_SM1_INSTRUCTION_LENGTH_SHIFT) + +#define WINED3D_SM1_COISSUE (0x1u << 30) + +#define WINED3D_SM1_COMMENT_SIZE_SHIFT 16 +#define WINED3D_SM1_COMMENT_SIZE_MASK (0x7fffu << WINED3D_SM1_COMMENT_SIZE_SHIFT) + +#define WINED3D_SM1_INSTRUCTION_PREDICATED (0x1u << 28) + +/* Register number mask */ +#define WINED3D_SM1_REGISTER_NUMBER_MASK 0x000007ff + +/* Register type masks */ +#define WINED3D_SM1_REGISTER_TYPE_SHIFT 28 +#define WINED3D_SM1_REGISTER_TYPE_MASK (0x7u << WINED3D_SM1_REGISTER_TYPE_SHIFT) +#define WINED3D_SM1_REGISTER_TYPE_SHIFT2 8 +#define WINED3D_SM1_REGISTER_TYPE_MASK2 (0x18u << WINED3D_SM1_REGISTER_TYPE_SHIFT2) + +/* Relative addressing mask */ +#define WINED3D_SM1_ADDRESS_MODE_SHIFT 13 +#define WINED3D_SM1_ADDRESS_MODE_MASK (0x1u << WINED3D_SM1_ADDRESS_MODE_SHIFT) + +/* Destination modifier mask */ +#define WINED3D_SM1_DST_MODIFIER_SHIFT 20 +#define WINED3D_SM1_DST_MODIFIER_MASK (0xfu << WINED3D_SM1_DST_MODIFIER_SHIFT) + +/* Destination shift mask */ +#define WINED3D_SM1_DSTSHIFT_SHIFT 24 +#define WINED3D_SM1_DSTSHIFT_MASK (0xfu << WINED3D_SM1_DSTSHIFT_SHIFT) + +/* Write mask */ +#define WINED3D_SM1_WRITEMASK_SHIFT 16 +#define WINED3D_SM1_WRITEMASK_MASK (0xfu << WINED3D_SM1_WRITEMASK_SHIFT) + +/* Swizzle mask */ +#define WINED3D_SM1_SWIZZLE_SHIFT 16 +#define WINED3D_SM1_SWIZZLE_MASK (0xffu << WINED3D_SM1_SWIZZLE_SHIFT) + +/* Source modifier mask */ +#define WINED3D_SM1_SRC_MODIFIER_SHIFT 24 +#define WINED3D_SM1_SRC_MODIFIER_MASK (0xfu << WINED3D_SM1_SRC_MODIFIER_SHIFT) + +#define WINED3D_SM1_END 0x0000ffff + +#define WINED3D_SM1_VERSION_MAJOR(version) (((version) >> 8) & 0xff) +#define WINED3D_SM1_VERSION_MINOR(version) (((version) >> 0) & 0xff) + +enum wined3d_sm1_address_mode_type +{ + WINED3D_SM1_ADDRESS_MODE_ABSOLUTE = 0u << WINED3D_SM1_ADDRESS_MODE_SHIFT, + WINED3D_SM1_ADDRESS_MODE_RELATIVE = 1u << WINED3D_SM1_ADDRESS_MODE_SHIFT, +}; + +enum wined3d_sm1_resource_type +{ + WINED3D_SM1_RESOURCE_UNKNOWN = 0x0, + WINED3D_SM1_RESOURCE_TEXTURE_1D = 0x1, + WINED3D_SM1_RESOURCE_TEXTURE_2D = 0x2, + WINED3D_SM1_RESOURCE_TEXTURE_CUBE = 0x3, + WINED3D_SM1_RESOURCE_TEXTURE_3D = 0x4, +}; + +enum wined3d_sm1_opcode +{ + WINED3D_SM1_OP_NOP = 0x00, + WINED3D_SM1_OP_MOV = 0x01, + WINED3D_SM1_OP_ADD = 0x02, + WINED3D_SM1_OP_SUB = 0x03, + WINED3D_SM1_OP_MAD = 0x04, + WINED3D_SM1_OP_MUL = 0x05, + WINED3D_SM1_OP_RCP = 0x06, + WINED3D_SM1_OP_RSQ = 0x07, + WINED3D_SM1_OP_DP3 = 0x08, + WINED3D_SM1_OP_DP4 = 0x09, + WINED3D_SM1_OP_MIN = 0x0a, + WINED3D_SM1_OP_MAX = 0x0b, + WINED3D_SM1_OP_SLT = 0x0c, + WINED3D_SM1_OP_SGE = 0x0d, + WINED3D_SM1_OP_EXP = 0x0e, + WINED3D_SM1_OP_LOG = 0x0f, + WINED3D_SM1_OP_LIT = 0x10, + WINED3D_SM1_OP_DST = 0x11, + WINED3D_SM1_OP_LRP = 0x12, + WINED3D_SM1_OP_FRC = 0x13, + WINED3D_SM1_OP_M4x4 = 0x14, + WINED3D_SM1_OP_M4x3 = 0x15, + WINED3D_SM1_OP_M3x4 = 0x16, + WINED3D_SM1_OP_M3x3 = 0x17, + WINED3D_SM1_OP_M3x2 = 0x18, + WINED3D_SM1_OP_CALL = 0x19, + WINED3D_SM1_OP_CALLNZ = 0x1a, + WINED3D_SM1_OP_LOOP = 0x1b, + WINED3D_SM1_OP_RET = 0x1c, + WINED3D_SM1_OP_ENDLOOP = 0x1d, + WINED3D_SM1_OP_LABEL = 0x1e, + WINED3D_SM1_OP_DCL = 0x1f, + WINED3D_SM1_OP_POW = 0x20, + WINED3D_SM1_OP_CRS = 0x21, + WINED3D_SM1_OP_SGN = 0x22, + WINED3D_SM1_OP_ABS = 0x23, + WINED3D_SM1_OP_NRM = 0x24, + WINED3D_SM1_OP_SINCOS = 0x25, + WINED3D_SM1_OP_REP = 0x26, + WINED3D_SM1_OP_ENDREP = 0x27, + WINED3D_SM1_OP_IF = 0x28, + WINED3D_SM1_OP_IFC = 0x29, + WINED3D_SM1_OP_ELSE = 0x2a, + WINED3D_SM1_OP_ENDIF = 0x2b, + WINED3D_SM1_OP_BREAK = 0x2c, + WINED3D_SM1_OP_BREAKC = 0x2d, + WINED3D_SM1_OP_MOVA = 0x2e, + WINED3D_SM1_OP_DEFB = 0x2f, + WINED3D_SM1_OP_DEFI = 0x30, + + WINED3D_SM1_OP_TEXCOORD = 0x40, + WINED3D_SM1_OP_TEXKILL = 0x41, + WINED3D_SM1_OP_TEX = 0x42, + WINED3D_SM1_OP_TEXBEM = 0x43, + WINED3D_SM1_OP_TEXBEML = 0x44, + WINED3D_SM1_OP_TEXREG2AR = 0x45, + WINED3D_SM1_OP_TEXREG2GB = 0x46, + WINED3D_SM1_OP_TEXM3x2PAD = 0x47, + WINED3D_SM1_OP_TEXM3x2TEX = 0x48, + WINED3D_SM1_OP_TEXM3x3PAD = 0x49, + WINED3D_SM1_OP_TEXM3x3TEX = 0x4a, + WINED3D_SM1_OP_TEXM3x3DIFF = 0x4b, + WINED3D_SM1_OP_TEXM3x3SPEC = 0x4c, + WINED3D_SM1_OP_TEXM3x3VSPEC = 0x4d, + WINED3D_SM1_OP_EXPP = 0x4e, + WINED3D_SM1_OP_LOGP = 0x4f, + WINED3D_SM1_OP_CND = 0x50, + WINED3D_SM1_OP_DEF = 0x51, + WINED3D_SM1_OP_TEXREG2RGB = 0x52, + WINED3D_SM1_OP_TEXDP3TEX = 0x53, + WINED3D_SM1_OP_TEXM3x2DEPTH = 0x54, + WINED3D_SM1_OP_TEXDP3 = 0x55, + WINED3D_SM1_OP_TEXM3x3 = 0x56, + WINED3D_SM1_OP_TEXDEPTH = 0x57, + WINED3D_SM1_OP_CMP = 0x58, + WINED3D_SM1_OP_BEM = 0x59, + WINED3D_SM1_OP_DP2ADD = 0x5a, + WINED3D_SM1_OP_DSX = 0x5b, + WINED3D_SM1_OP_DSY = 0x5c, + WINED3D_SM1_OP_TEXLDD = 0x5d, + WINED3D_SM1_OP_SETP = 0x5e, + WINED3D_SM1_OP_TEXLDL = 0x5f, + WINED3D_SM1_OP_BREAKP = 0x60, + + WINED3D_SM1_OP_PHASE = 0xfffd, + WINED3D_SM1_OP_COMMENT = 0xfffe, + WINED3D_SM1_OP_END = 0Xffff, +}; + +struct wined3d_sm1_opcode_info +{ + enum wined3d_sm1_opcode opcode; + unsigned int dst_count; + unsigned int param_count; + enum WINED3D_SHADER_INSTRUCTION_HANDLER handler_idx; + DWORD min_version; + DWORD max_version; +}; + +struct wined3d_sm1_data +{ + struct wined3d_shader_version shader_version; + const struct wined3d_sm1_opcode_info *opcode_table; + const DWORD *start; + + struct wined3d_shader_src_param src_rel_addr[4]; + struct wined3d_shader_src_param pred_rel_addr; + struct wined3d_shader_src_param dst_rel_addr; + struct wined3d_shader_src_param src_param[4]; + struct wined3d_shader_src_param pred_param; + struct wined3d_shader_dst_param dst_param; +}; + +/* This table is not order or position dependent. */ +static const struct wined3d_sm1_opcode_info vs_opcode_table[] = +{ + /* Arithmetic */ + {WINED3D_SM1_OP_NOP, 0, 0, WINED3DSIH_NOP, 0, 0 }, + {WINED3D_SM1_OP_MOV, 1, 2, WINED3DSIH_MOV, 0, 0 }, + {WINED3D_SM1_OP_MOVA, 1, 2, WINED3DSIH_MOVA, WINED3D_SHADER_VERSION(2,0), -1 }, + {WINED3D_SM1_OP_ADD, 1, 3, WINED3DSIH_ADD, 0, 0 }, + {WINED3D_SM1_OP_SUB, 1, 3, WINED3DSIH_SUB, 0, 0 }, + {WINED3D_SM1_OP_MAD, 1, 4, WINED3DSIH_MAD, 0, 0 }, + {WINED3D_SM1_OP_MUL, 1, 3, WINED3DSIH_MUL, 0, 0 }, + {WINED3D_SM1_OP_RCP, 1, 2, WINED3DSIH_RCP, 0, 0 }, + {WINED3D_SM1_OP_RSQ, 1, 2, WINED3DSIH_RSQ, 0, 0 }, + {WINED3D_SM1_OP_DP3, 1, 3, WINED3DSIH_DP3, 0, 0 }, + {WINED3D_SM1_OP_DP4, 1, 3, WINED3DSIH_DP4, 0, 0 }, + {WINED3D_SM1_OP_MIN, 1, 3, WINED3DSIH_MIN, 0, 0 }, + {WINED3D_SM1_OP_MAX, 1, 3, WINED3DSIH_MAX, 0, 0 }, + {WINED3D_SM1_OP_SLT, 1, 3, WINED3DSIH_SLT, 0, 0 }, + {WINED3D_SM1_OP_SGE, 1, 3, WINED3DSIH_SGE, 0, 0 }, + {WINED3D_SM1_OP_ABS, 1, 2, WINED3DSIH_ABS, 0, 0 }, + {WINED3D_SM1_OP_EXP, 1, 2, WINED3DSIH_EXP, 0, 0 }, + {WINED3D_SM1_OP_LOG, 1, 2, WINED3DSIH_LOG, 0, 0 }, + {WINED3D_SM1_OP_EXPP, 1, 2, WINED3DSIH_EXPP, 0, 0 }, + {WINED3D_SM1_OP_LOGP, 1, 2, WINED3DSIH_LOGP, 0, 0 }, + {WINED3D_SM1_OP_LIT, 1, 2, WINED3DSIH_LIT, 0, 0 }, + {WINED3D_SM1_OP_DST, 1, 3, WINED3DSIH_DST, 0, 0 }, + {WINED3D_SM1_OP_LRP, 1, 4, WINED3DSIH_LRP, 0, 0 }, + {WINED3D_SM1_OP_FRC, 1, 2, WINED3DSIH_FRC, 0, 0 }, + {WINED3D_SM1_OP_POW, 1, 3, WINED3DSIH_POW, 0, 0 }, + {WINED3D_SM1_OP_CRS, 1, 3, WINED3DSIH_CRS, 0, 0 }, + {WINED3D_SM1_OP_SGN, 1, 4, WINED3DSIH_SGN, WINED3D_SHADER_VERSION(2,0), WINED3D_SHADER_VERSION(2,1)}, + {WINED3D_SM1_OP_SGN, 1, 2, WINED3DSIH_SGN, WINED3D_SHADER_VERSION(3,0), -1 }, + {WINED3D_SM1_OP_NRM, 1, 2, WINED3DSIH_NRM, 0, 0 }, + {WINED3D_SM1_OP_SINCOS, 1, 4, WINED3DSIH_SINCOS, WINED3D_SHADER_VERSION(2,0), WINED3D_SHADER_VERSION(2,1)}, + {WINED3D_SM1_OP_SINCOS, 1, 2, WINED3DSIH_SINCOS, WINED3D_SHADER_VERSION(3,0), -1 }, + /* Matrix */ + {WINED3D_SM1_OP_M4x4, 1, 3, WINED3DSIH_M4x4, 0, 0 }, + {WINED3D_SM1_OP_M4x3, 1, 3, WINED3DSIH_M4x3, 0, 0 }, + {WINED3D_SM1_OP_M3x4, 1, 3, WINED3DSIH_M3x4, 0, 0 }, + {WINED3D_SM1_OP_M3x3, 1, 3, WINED3DSIH_M3x3, 0, 0 }, + {WINED3D_SM1_OP_M3x2, 1, 3, WINED3DSIH_M3x2, 0, 0 }, + /* Declare registers */ + {WINED3D_SM1_OP_DCL, 0, 2, WINED3DSIH_DCL, 0, 0 }, + /* Constant definitions */ + {WINED3D_SM1_OP_DEF, 1, 5, WINED3DSIH_DEF, 0, 0 }, + {WINED3D_SM1_OP_DEFB, 1, 2, WINED3DSIH_DEFB, 0, 0 }, + {WINED3D_SM1_OP_DEFI, 1, 5, WINED3DSIH_DEFI, 0, 0 }, + /* Flow control */ + {WINED3D_SM1_OP_REP, 0, 1, WINED3DSIH_REP, WINED3D_SHADER_VERSION(2,0), -1 }, + {WINED3D_SM1_OP_ENDREP, 0, 0, WINED3DSIH_ENDREP, WINED3D_SHADER_VERSION(2,0), -1 }, + {WINED3D_SM1_OP_IF, 0, 1, WINED3DSIH_IF, WINED3D_SHADER_VERSION(2,0), -1 }, + {WINED3D_SM1_OP_IFC, 0, 2, WINED3DSIH_IFC, WINED3D_SHADER_VERSION(2,1), -1 }, + {WINED3D_SM1_OP_ELSE, 0, 0, WINED3DSIH_ELSE, WINED3D_SHADER_VERSION(2,0), -1 }, + {WINED3D_SM1_OP_ENDIF, 0, 0, WINED3DSIH_ENDIF, WINED3D_SHADER_VERSION(2,0), -1 }, + {WINED3D_SM1_OP_BREAK, 0, 0, WINED3DSIH_BREAK, WINED3D_SHADER_VERSION(2,1), -1 }, + {WINED3D_SM1_OP_BREAKC, 0, 2, WINED3DSIH_BREAKC, WINED3D_SHADER_VERSION(2,1), -1 }, + {WINED3D_SM1_OP_BREAKP, 0, 1, WINED3DSIH_BREAKP, 0, 0 }, + {WINED3D_SM1_OP_CALL, 0, 1, WINED3DSIH_CALL, WINED3D_SHADER_VERSION(2,0), -1 }, + {WINED3D_SM1_OP_CALLNZ, 0, 2, WINED3DSIH_CALLNZ, WINED3D_SHADER_VERSION(2,0), -1 }, + {WINED3D_SM1_OP_LOOP, 0, 2, WINED3DSIH_LOOP, WINED3D_SHADER_VERSION(2,0), -1 }, + {WINED3D_SM1_OP_RET, 0, 0, WINED3DSIH_RET, WINED3D_SHADER_VERSION(2,0), -1 }, + {WINED3D_SM1_OP_ENDLOOP, 0, 0, WINED3DSIH_ENDLOOP, WINED3D_SHADER_VERSION(2,0), -1 }, + {WINED3D_SM1_OP_LABEL, 0, 1, WINED3DSIH_LABEL, WINED3D_SHADER_VERSION(2,0), -1 }, + + {WINED3D_SM1_OP_SETP, 1, 3, WINED3DSIH_SETP, 0, 0 }, + {WINED3D_SM1_OP_TEXLDL, 1, 3, WINED3DSIH_TEXLDL, WINED3D_SHADER_VERSION(3,0), -1 }, + {0, 0, 0, WINED3DSIH_TABLE_SIZE, 0, 0 }, +}; + +static const struct wined3d_sm1_opcode_info ps_opcode_table[] = +{ + /* Arithmetic */ + {WINED3D_SM1_OP_NOP, 0, 0, WINED3DSIH_NOP, 0, 0 }, + {WINED3D_SM1_OP_MOV, 1, 2, WINED3DSIH_MOV, 0, 0 }, + {WINED3D_SM1_OP_ADD, 1, 3, WINED3DSIH_ADD, 0, 0 }, + {WINED3D_SM1_OP_SUB, 1, 3, WINED3DSIH_SUB, 0, 0 }, + {WINED3D_SM1_OP_MAD, 1, 4, WINED3DSIH_MAD, 0, 0 }, + {WINED3D_SM1_OP_MUL, 1, 3, WINED3DSIH_MUL, 0, 0 }, + {WINED3D_SM1_OP_RCP, 1, 2, WINED3DSIH_RCP, 0, 0 }, + {WINED3D_SM1_OP_RSQ, 1, 2, WINED3DSIH_RSQ, 0, 0 }, + {WINED3D_SM1_OP_DP3, 1, 3, WINED3DSIH_DP3, 0, 0 }, + {WINED3D_SM1_OP_DP4, 1, 3, WINED3DSIH_DP4, 0, 0 }, + {WINED3D_SM1_OP_MIN, 1, 3, WINED3DSIH_MIN, 0, 0 }, + {WINED3D_SM1_OP_MAX, 1, 3, WINED3DSIH_MAX, 0, 0 }, + {WINED3D_SM1_OP_SLT, 1, 3, WINED3DSIH_SLT, 0, 0 }, + {WINED3D_SM1_OP_SGE, 1, 3, WINED3DSIH_SGE, 0, 0 }, + {WINED3D_SM1_OP_ABS, 1, 2, WINED3DSIH_ABS, 0, 0 }, + {WINED3D_SM1_OP_EXP, 1, 2, WINED3DSIH_EXP, 0, 0 }, + {WINED3D_SM1_OP_LOG, 1, 2, WINED3DSIH_LOG, 0, 0 }, + {WINED3D_SM1_OP_EXPP, 1, 2, WINED3DSIH_EXPP, 0, 0 }, + {WINED3D_SM1_OP_LOGP, 1, 2, WINED3DSIH_LOGP, 0, 0 }, + {WINED3D_SM1_OP_DST, 1, 3, WINED3DSIH_DST, 0, 0 }, + {WINED3D_SM1_OP_LRP, 1, 4, WINED3DSIH_LRP, 0, 0 }, + {WINED3D_SM1_OP_FRC, 1, 2, WINED3DSIH_FRC, 0, 0 }, + {WINED3D_SM1_OP_CND, 1, 4, WINED3DSIH_CND, WINED3D_SHADER_VERSION(1,0), WINED3D_SHADER_VERSION(1,4)}, + {WINED3D_SM1_OP_CMP, 1, 4, WINED3DSIH_CMP, WINED3D_SHADER_VERSION(1,2), WINED3D_SHADER_VERSION(3,0)}, + {WINED3D_SM1_OP_POW, 1, 3, WINED3DSIH_POW, 0, 0 }, + {WINED3D_SM1_OP_CRS, 1, 3, WINED3DSIH_CRS, 0, 0 }, + {WINED3D_SM1_OP_NRM, 1, 2, WINED3DSIH_NRM, 0, 0 }, + {WINED3D_SM1_OP_SINCOS, 1, 4, WINED3DSIH_SINCOS, WINED3D_SHADER_VERSION(2,0), WINED3D_SHADER_VERSION(2,1)}, + {WINED3D_SM1_OP_SINCOS, 1, 2, WINED3DSIH_SINCOS, WINED3D_SHADER_VERSION(3,0), -1 }, + {WINED3D_SM1_OP_DP2ADD, 1, 4, WINED3DSIH_DP2ADD, WINED3D_SHADER_VERSION(2,0), -1 }, + /* Matrix */ + {WINED3D_SM1_OP_M4x4, 1, 3, WINED3DSIH_M4x4, 0, 0 }, + {WINED3D_SM1_OP_M4x3, 1, 3, WINED3DSIH_M4x3, 0, 0 }, + {WINED3D_SM1_OP_M3x4, 1, 3, WINED3DSIH_M3x4, 0, 0 }, + {WINED3D_SM1_OP_M3x3, 1, 3, WINED3DSIH_M3x3, 0, 0 }, + {WINED3D_SM1_OP_M3x2, 1, 3, WINED3DSIH_M3x2, 0, 0 }, + /* Register declarations */ + {WINED3D_SM1_OP_DCL, 0, 2, WINED3DSIH_DCL, 0, 0 }, + /* Flow control */ + {WINED3D_SM1_OP_REP, 0, 1, WINED3DSIH_REP, WINED3D_SHADER_VERSION(2,1), -1 }, + {WINED3D_SM1_OP_ENDREP, 0, 0, WINED3DSIH_ENDREP, WINED3D_SHADER_VERSION(2,1), -1 }, + {WINED3D_SM1_OP_IF, 0, 1, WINED3DSIH_IF, WINED3D_SHADER_VERSION(2,1), -1 }, + {WINED3D_SM1_OP_IFC, 0, 2, WINED3DSIH_IFC, WINED3D_SHADER_VERSION(2,1), -1 }, + {WINED3D_SM1_OP_ELSE, 0, 0, WINED3DSIH_ELSE, WINED3D_SHADER_VERSION(2,1), -1 }, + {WINED3D_SM1_OP_ENDIF, 0, 0, WINED3DSIH_ENDIF, WINED3D_SHADER_VERSION(2,1), -1 }, + {WINED3D_SM1_OP_BREAK, 0, 0, WINED3DSIH_BREAK, WINED3D_SHADER_VERSION(2,1), -1 }, + {WINED3D_SM1_OP_BREAKC, 0, 2, WINED3DSIH_BREAKC, WINED3D_SHADER_VERSION(2,1), -1 }, + {WINED3D_SM1_OP_BREAKP, 0, 1, WINED3DSIH_BREAKP, 0, 0 }, + {WINED3D_SM1_OP_CALL, 0, 1, WINED3DSIH_CALL, WINED3D_SHADER_VERSION(2,1), -1 }, + {WINED3D_SM1_OP_CALLNZ, 0, 2, WINED3DSIH_CALLNZ, WINED3D_SHADER_VERSION(2,1), -1 }, + {WINED3D_SM1_OP_LOOP, 0, 2, WINED3DSIH_LOOP, WINED3D_SHADER_VERSION(3,0), -1 }, + {WINED3D_SM1_OP_RET, 0, 0, WINED3DSIH_RET, WINED3D_SHADER_VERSION(2,1), -1 }, + {WINED3D_SM1_OP_ENDLOOP, 0, 0, WINED3DSIH_ENDLOOP, WINED3D_SHADER_VERSION(3,0), -1 }, + {WINED3D_SM1_OP_LABEL, 0, 1, WINED3DSIH_LABEL, WINED3D_SHADER_VERSION(2,1), -1 }, + /* Constant definitions */ + {WINED3D_SM1_OP_DEF, 1, 5, WINED3DSIH_DEF, 0, 0 }, + {WINED3D_SM1_OP_DEFB, 1, 2, WINED3DSIH_DEFB, 0, 0 }, + {WINED3D_SM1_OP_DEFI, 1, 5, WINED3DSIH_DEFI, 0, 0 }, + /* Texture */ + {WINED3D_SM1_OP_TEXCOORD, 1, 1, WINED3DSIH_TEXCOORD, 0, WINED3D_SHADER_VERSION(1,3)}, + {WINED3D_SM1_OP_TEXCOORD, 1, 2, WINED3DSIH_TEXCOORD, WINED3D_SHADER_VERSION(1,4), WINED3D_SHADER_VERSION(1,4)}, + {WINED3D_SM1_OP_TEXKILL, 1, 1, WINED3DSIH_TEXKILL, WINED3D_SHADER_VERSION(1,0), WINED3D_SHADER_VERSION(3,0)}, + {WINED3D_SM1_OP_TEX, 1, 1, WINED3DSIH_TEX, 0, WINED3D_SHADER_VERSION(1,3)}, + {WINED3D_SM1_OP_TEX, 1, 2, WINED3DSIH_TEX, WINED3D_SHADER_VERSION(1,4), WINED3D_SHADER_VERSION(1,4)}, + {WINED3D_SM1_OP_TEX, 1, 3, WINED3DSIH_TEX, WINED3D_SHADER_VERSION(2,0), -1 }, + {WINED3D_SM1_OP_TEXBEM, 1, 2, WINED3DSIH_TEXBEM, 0, WINED3D_SHADER_VERSION(1,3)}, + {WINED3D_SM1_OP_TEXBEML, 1, 2, WINED3DSIH_TEXBEML, WINED3D_SHADER_VERSION(1,0), WINED3D_SHADER_VERSION(1,3)}, + {WINED3D_SM1_OP_TEXREG2AR, 1, 2, WINED3DSIH_TEXREG2AR, WINED3D_SHADER_VERSION(1,0), WINED3D_SHADER_VERSION(1,3)}, + {WINED3D_SM1_OP_TEXREG2GB, 1, 2, WINED3DSIH_TEXREG2GB, WINED3D_SHADER_VERSION(1,0), WINED3D_SHADER_VERSION(1,3)}, + {WINED3D_SM1_OP_TEXREG2RGB, 1, 2, WINED3DSIH_TEXREG2RGB, WINED3D_SHADER_VERSION(1,2), WINED3D_SHADER_VERSION(1,3)}, + {WINED3D_SM1_OP_TEXM3x2PAD, 1, 2, WINED3DSIH_TEXM3x2PAD, WINED3D_SHADER_VERSION(1,0), WINED3D_SHADER_VERSION(1,3)}, + {WINED3D_SM1_OP_TEXM3x2TEX, 1, 2, WINED3DSIH_TEXM3x2TEX, WINED3D_SHADER_VERSION(1,0), WINED3D_SHADER_VERSION(1,3)}, + {WINED3D_SM1_OP_TEXM3x3PAD, 1, 2, WINED3DSIH_TEXM3x3PAD, WINED3D_SHADER_VERSION(1,0), WINED3D_SHADER_VERSION(1,3)}, + {WINED3D_SM1_OP_TEXM3x3DIFF, 1, 2, WINED3DSIH_TEXM3x3DIFF, WINED3D_SHADER_VERSION(0,0), WINED3D_SHADER_VERSION(0,0)}, + {WINED3D_SM1_OP_TEXM3x3SPEC, 1, 3, WINED3DSIH_TEXM3x3SPEC, WINED3D_SHADER_VERSION(1,0), WINED3D_SHADER_VERSION(1,3)}, + {WINED3D_SM1_OP_TEXM3x3VSPEC, 1, 2, WINED3DSIH_TEXM3x3VSPEC, WINED3D_SHADER_VERSION(1,0), WINED3D_SHADER_VERSION(1,3)}, + {WINED3D_SM1_OP_TEXM3x3TEX, 1, 2, WINED3DSIH_TEXM3x3TEX, WINED3D_SHADER_VERSION(1,0), WINED3D_SHADER_VERSION(1,3)}, + {WINED3D_SM1_OP_TEXDP3TEX, 1, 2, WINED3DSIH_TEXDP3TEX, WINED3D_SHADER_VERSION(1,2), WINED3D_SHADER_VERSION(1,3)}, + {WINED3D_SM1_OP_TEXM3x2DEPTH, 1, 2, WINED3DSIH_TEXM3x2DEPTH, WINED3D_SHADER_VERSION(1,3), WINED3D_SHADER_VERSION(1,3)}, + {WINED3D_SM1_OP_TEXDP3, 1, 2, WINED3DSIH_TEXDP3, WINED3D_SHADER_VERSION(1,2), WINED3D_SHADER_VERSION(1,3)}, + {WINED3D_SM1_OP_TEXM3x3, 1, 2, WINED3DSIH_TEXM3x3, WINED3D_SHADER_VERSION(1,2), WINED3D_SHADER_VERSION(1,3)}, + {WINED3D_SM1_OP_TEXDEPTH, 1, 1, WINED3DSIH_TEXDEPTH, WINED3D_SHADER_VERSION(1,4), WINED3D_SHADER_VERSION(1,4)}, + {WINED3D_SM1_OP_BEM, 1, 3, WINED3DSIH_BEM, WINED3D_SHADER_VERSION(1,4), WINED3D_SHADER_VERSION(1,4)}, + {WINED3D_SM1_OP_DSX, 1, 2, WINED3DSIH_DSX, WINED3D_SHADER_VERSION(2,1), -1 }, + {WINED3D_SM1_OP_DSY, 1, 2, WINED3DSIH_DSY, WINED3D_SHADER_VERSION(2,1), -1 }, + {WINED3D_SM1_OP_TEXLDD, 1, 5, WINED3DSIH_TEXLDD, WINED3D_SHADER_VERSION(2,1), -1 }, + {WINED3D_SM1_OP_SETP, 1, 3, WINED3DSIH_SETP, 0, 0 }, + {WINED3D_SM1_OP_TEXLDL, 1, 3, WINED3DSIH_TEXLDL, WINED3D_SHADER_VERSION(3,0), -1 }, + {WINED3D_SM1_OP_PHASE, 0, 0, WINED3DSIH_PHASE, 0, 0 }, + {0, 0, 0, WINED3DSIH_TABLE_SIZE, 0, 0 }, +}; + +static const enum wined3d_shader_resource_type resource_type_table[] = +{ + /* WINED3D_SM1_RESOURCE_UNKNOWN */ WINED3D_SHADER_RESOURCE_NONE, + /* WINED3D_SM1_RESOURCE_TEXTURE_1D */ WINED3D_SHADER_RESOURCE_TEXTURE_1D, + /* WINED3D_SM1_RESOURCE_TEXTURE_2D */ WINED3D_SHADER_RESOURCE_TEXTURE_2D, + /* WINED3D_SM1_RESOURCE_TEXTURE_CUBE */ WINED3D_SHADER_RESOURCE_TEXTURE_CUBE, + /* WINED3D_SM1_RESOURCE_TEXTURE_3D */ WINED3D_SHADER_RESOURCE_TEXTURE_3D, +}; + +/* Read a parameter opcode from the input stream, + * and possibly a relative addressing token. + * Return the number of tokens read */ +static unsigned int shader_get_param(const struct wined3d_sm1_data *priv, const DWORD *ptr, + DWORD *token, DWORD *addr_token) +{ + unsigned int count = 1; + + *token = *ptr; + + /* PS >= 3.0 have relative addressing (with token) + * VS >= 2.0 have relative addressing (with token) + * VS >= 1.0 < 2.0 have relative addressing (without token) + * The version check below should work in general */ + if (*ptr & WINED3D_SM1_ADDRESS_MODE_RELATIVE) + { + if (priv->shader_version.major < 2) + { + *addr_token = (1u << 31) + | ((WINED3DSPR_ADDR << WINED3D_SM1_REGISTER_TYPE_SHIFT2) & WINED3D_SM1_REGISTER_TYPE_MASK2) + | ((WINED3DSPR_ADDR << WINED3D_SM1_REGISTER_TYPE_SHIFT) & WINED3D_SM1_REGISTER_TYPE_MASK) + | (WINED3DSP_NOSWIZZLE << WINED3D_SM1_SWIZZLE_SHIFT); + } + else + { + *addr_token = *(ptr + 1); + ++count; + } + } + + return count; +} + +static const struct wined3d_sm1_opcode_info *shader_get_opcode(const struct wined3d_sm1_data *priv, DWORD token) +{ + DWORD shader_version = WINED3D_SHADER_VERSION(priv->shader_version.major, priv->shader_version.minor); + const struct wined3d_sm1_opcode_info *opcode_table = priv->opcode_table; + unsigned int i = 0; + + while (opcode_table[i].handler_idx != WINED3DSIH_TABLE_SIZE) + { + if ((token & WINED3D_SM1_OPCODE_MASK) == opcode_table[i].opcode + && shader_version >= opcode_table[i].min_version + && (!opcode_table[i].max_version || shader_version <= opcode_table[i].max_version)) + { + return &opcode_table[i]; + } + ++i; + } + + FIXME("Unsupported opcode %#x, token 0x%08x, shader version %#x.\n", + token & WINED3D_SM1_OPCODE_MASK, token, shader_version); + + return NULL; +} + +/* Return the number of parameters to skip for an opcode */ +static unsigned int shader_skip_opcode(const struct wined3d_sm1_data *priv, + const struct wined3d_sm1_opcode_info *opcode_info, DWORD opcode_token) +{ + unsigned int length; + /* Shaders >= 2.0 may contain address tokens, but fortunately they + * have a useful length mask - use it here. Shaders 1.0 contain no such tokens */ + length = (opcode_token & WINED3D_SM1_INSTRUCTION_LENGTH_MASK) >> WINED3D_SM1_INSTRUCTION_LENGTH_SHIFT; + return (priv->shader_version.major >= 2) ? length : opcode_info->param_count; +} + +static void shader_parse_src_param(DWORD param, const struct wined3d_shader_src_param *rel_addr, + struct wined3d_shader_src_param *src) +{ + src->reg.type = ((param & WINED3D_SM1_REGISTER_TYPE_MASK) >> WINED3D_SM1_REGISTER_TYPE_SHIFT) + | ((param & WINED3D_SM1_REGISTER_TYPE_MASK2) >> WINED3D_SM1_REGISTER_TYPE_SHIFT2); + src->reg.data_type = WINED3D_DATA_FLOAT; + src->reg.idx[0].offset = param & WINED3D_SM1_REGISTER_NUMBER_MASK; + src->reg.idx[0].rel_addr = rel_addr; + src->reg.idx[1].offset = ~0U; + src->reg.idx[1].rel_addr = NULL; + src->swizzle = (param & WINED3D_SM1_SWIZZLE_MASK) >> WINED3D_SM1_SWIZZLE_SHIFT; + src->modifiers = (param & WINED3D_SM1_SRC_MODIFIER_MASK) >> WINED3D_SM1_SRC_MODIFIER_SHIFT; +} + +static void shader_parse_dst_param(DWORD param, const struct wined3d_shader_src_param *rel_addr, + struct wined3d_shader_dst_param *dst) +{ + dst->reg.type = ((param & WINED3D_SM1_REGISTER_TYPE_MASK) >> WINED3D_SM1_REGISTER_TYPE_SHIFT) + | ((param & WINED3D_SM1_REGISTER_TYPE_MASK2) >> WINED3D_SM1_REGISTER_TYPE_SHIFT2); + dst->reg.data_type = WINED3D_DATA_FLOAT; + dst->reg.idx[0].offset = param & WINED3D_SM1_REGISTER_NUMBER_MASK; + dst->reg.idx[0].rel_addr = rel_addr; + dst->reg.idx[1].offset = ~0U; + dst->reg.idx[1].rel_addr = NULL; + dst->write_mask = (param & WINED3D_SM1_WRITEMASK_MASK) >> WINED3D_SM1_WRITEMASK_SHIFT; + dst->modifiers = (param & WINED3D_SM1_DST_MODIFIER_MASK) >> WINED3D_SM1_DST_MODIFIER_SHIFT; + dst->shift = (param & WINED3D_SM1_DSTSHIFT_MASK) >> WINED3D_SM1_DSTSHIFT_SHIFT; +} + +/* Read the parameters of an unrecognized opcode from the input stream + * Return the number of tokens read. + * + * Note: This function assumes source or destination token format. + * It will not work with specially-formatted tokens like DEF or DCL, + * but hopefully those would be recognized */ +static unsigned int shader_skip_unrecognized(const struct wined3d_sm1_data *priv, const DWORD *ptr) +{ + unsigned int tokens_read = 0, i = 0; + + /* TODO: Think of a good name for 0x80000000 and replace it with a constant */ + while (*ptr & 0x80000000) + { + DWORD token, addr_token = 0; + struct wined3d_shader_src_param rel_addr; + + tokens_read += shader_get_param(priv, ptr, &token, &addr_token); + ptr += tokens_read; + + FIXME("Unrecognized opcode param: token=0x%08x addr_token=0x%08x.\n", token, addr_token); + + if (token & WINED3D_SM1_ADDRESS_MODE_RELATIVE) + shader_parse_src_param(addr_token, NULL, &rel_addr); + + if (!i) + { + struct wined3d_shader_dst_param dst; + + shader_parse_dst_param(token, token & WINED3D_SM1_ADDRESS_MODE_RELATIVE ? &rel_addr : NULL, &dst); + } + else + { + struct wined3d_shader_src_param src; + + shader_parse_src_param(token, token & WINED3D_SM1_ADDRESS_MODE_RELATIVE ? &rel_addr : NULL, &src); + } + ++i; + } + return tokens_read; +} + +static void *shader_sm1_init(const DWORD *byte_code, size_t byte_code_size, + const struct wined3d_shader_signature *output_signature) +{ + struct wined3d_sm1_data *priv; + BYTE major, minor; + + TRACE("Version: 0x%08x.\n", *byte_code); + + major = WINED3D_SM1_VERSION_MAJOR(*byte_code); + minor = WINED3D_SM1_VERSION_MINOR(*byte_code); + if (WINED3D_SHADER_VERSION(major, minor) > WINED3D_SHADER_VERSION(3, 0)) + { + WARN("Invalid shader version %u.%u (%#x).\n", major, minor, *byte_code); + return NULL; + } + + if (!(priv = heap_alloc(sizeof(*priv)))) + return NULL; + + if (output_signature->element_count) + FIXME("SM 1-3 shader shouldn't have output signatures.\n"); + + switch (*byte_code >> 16) + { + case WINED3D_SM1_VS: + priv->shader_version.type = WINED3D_SHADER_TYPE_VERTEX; + priv->opcode_table = vs_opcode_table; + break; + + case WINED3D_SM1_PS: + priv->shader_version.type = WINED3D_SHADER_TYPE_PIXEL; + priv->opcode_table = ps_opcode_table; + break; + + default: + FIXME("Unrecognized shader type %#x.\n", *byte_code >> 16); + heap_free(priv); + return NULL; + } + priv->shader_version.major = WINED3D_SM1_VERSION_MAJOR(*byte_code); + priv->shader_version.minor = WINED3D_SM1_VERSION_MINOR(*byte_code); + + priv->start = &byte_code[1]; + + return priv; +} + +static void shader_sm1_free(void *data) +{ + heap_free(data); +} + +static void shader_sm1_read_header(void *data, const DWORD **ptr, struct wined3d_shader_version *shader_version) +{ + struct wined3d_sm1_data *priv = data; + + *ptr = priv->start; + *shader_version = priv->shader_version; +} + +static void shader_sm1_read_src_param(struct wined3d_sm1_data *priv, const DWORD **ptr, + struct wined3d_shader_src_param *src_param, struct wined3d_shader_src_param *src_rel_addr) +{ + DWORD token, addr_token; + + *ptr += shader_get_param(priv, *ptr, &token, &addr_token); + if (token & WINED3D_SM1_ADDRESS_MODE_RELATIVE) + { + shader_parse_src_param(addr_token, NULL, src_rel_addr); + shader_parse_src_param(token, src_rel_addr, src_param); + } + else + { + shader_parse_src_param(token, NULL, src_param); + } +} + +static void shader_sm1_read_dst_param(struct wined3d_sm1_data *priv, const DWORD **ptr, + struct wined3d_shader_dst_param *dst_param, struct wined3d_shader_src_param *dst_rel_addr) +{ + DWORD token, addr_token; + + *ptr += shader_get_param(priv, *ptr, &token, &addr_token); + if (token & WINED3D_SM1_ADDRESS_MODE_RELATIVE) + { + shader_parse_src_param(addr_token, NULL, dst_rel_addr); + shader_parse_dst_param(token, dst_rel_addr, dst_param); + } + else + { + shader_parse_dst_param(token, NULL, dst_param); + } +} + +static void shader_sm1_read_semantic(const DWORD **ptr, struct wined3d_shader_semantic *semantic) +{ + enum wined3d_sm1_resource_type resource_type; + DWORD usage_token = *(*ptr)++; + DWORD dst_token = *(*ptr)++; + + semantic->usage = (usage_token & WINED3D_SM1_DCL_USAGE_MASK) >> WINED3D_SM1_DCL_USAGE_SHIFT; + semantic->usage_idx = (usage_token & WINED3D_SM1_DCL_USAGE_INDEX_MASK) >> WINED3D_SM1_DCL_USAGE_INDEX_SHIFT; + resource_type = (usage_token & WINED3D_SM1_RESOURCE_TYPE_MASK) >> WINED3D_SM1_RESOURCE_TYPE_SHIFT; + if (resource_type >= ARRAY_SIZE(resource_type_table)) + { + FIXME("Unhandled resource type %#x.\n", resource_type); + semantic->resource_type = WINED3D_SHADER_RESOURCE_NONE; + } + else + { + semantic->resource_type = resource_type_table[resource_type]; + } + semantic->resource_data_type = WINED3D_DATA_FLOAT; + shader_parse_dst_param(dst_token, NULL, &semantic->reg); +} + +static void shader_sm1_read_immconst(const DWORD **ptr, struct wined3d_shader_src_param *src_param, + enum wined3d_immconst_type type, enum wined3d_data_type data_type) +{ + unsigned int count = type == WINED3D_IMMCONST_VEC4 ? 4 : 1; + src_param->reg.type = WINED3DSPR_IMMCONST; + src_param->reg.data_type = data_type; + src_param->reg.idx[0].offset = ~0U; + src_param->reg.idx[0].rel_addr = NULL; + src_param->reg.idx[1].offset = ~0U; + src_param->reg.idx[1].rel_addr = NULL; + src_param->reg.immconst_type = type; + memcpy(src_param->reg.u.immconst_data, *ptr, count * sizeof(DWORD)); + src_param->swizzle = WINED3DSP_NOSWIZZLE; + src_param->modifiers = 0; + + *ptr += count; +} + +static void shader_sm1_read_comment(const DWORD **ptr) +{ + DWORD token = **ptr; + const char *comment; + UINT size; + + while ((token & WINED3D_SM1_OPCODE_MASK) == WINED3D_SM1_OP_COMMENT) + { + size = (token & WINED3D_SM1_COMMENT_SIZE_MASK) >> WINED3D_SM1_COMMENT_SIZE_SHIFT; + comment = (const char *)++(*ptr); + *ptr += size; + + if (size > 1 && *(const DWORD *)comment == WINEMAKEFOURCC('T', 'E', 'X', 'T')) + { + const char *end = comment + size * sizeof(token); + const char *p = comment + sizeof(token); + const char *line = p; + + TRACE("// TEXT\n"); + while (p != end) + { + if (*p == '\n') + { + UINT len = p - line; + if (len && *(p - 1) == '\r') --len; + TRACE("// %s\n", debugstr_an(line, len)); + line = ++p; + } + else ++p; + } + if (line != p) + TRACE("// %s\n", debugstr_an(line, p - line)); + } + else if (size) + TRACE("// %s\n", debugstr_an(comment, size * sizeof(token))); + else + break; + + token = **ptr; + } +} + +static void shader_sm1_validate_instruction(struct wined3d_shader_instruction *ins) +{ + if (ins->handler_idx == WINED3DSIH_BREAKP || ins->handler_idx == WINED3DSIH_IF) + { + if (ins->flags) + { + FIXME("Ignoring unexpected instruction flags %#x for %s.\n", + ins->flags, debug_d3dshaderinstructionhandler(ins->handler_idx)); + ins->flags = 0; + } + } +} + +static void shader_sm1_read_instruction(void *data, const DWORD **ptr, struct wined3d_shader_instruction *ins) +{ + const struct wined3d_sm1_opcode_info *opcode_info; + struct wined3d_sm1_data *priv = data; + DWORD opcode_token; + unsigned int i; + const DWORD *p; + + shader_sm1_read_comment(ptr); + + opcode_token = *(*ptr)++; + if (!(opcode_info = shader_get_opcode(priv, opcode_token))) + { + FIXME("Unrecognized opcode: token=0x%08x.\n", opcode_token); + ins->handler_idx = WINED3DSIH_TABLE_SIZE; + *ptr += shader_skip_unrecognized(priv, *ptr); + return; + } + + ins->handler_idx = opcode_info->handler_idx; + ins->flags = (opcode_token & WINED3D_SM1_INSTRUCTION_FLAGS_MASK) >> WINED3D_SM1_INSTRUCTION_FLAGS_SHIFT; + ins->coissue = opcode_token & WINED3D_SM1_COISSUE; + ins->predicate = opcode_token & WINED3D_SM1_INSTRUCTION_PREDICATED ? &priv->pred_param : NULL; + ins->dst_count = opcode_info->dst_count ? 1 : 0; + ins->dst = &priv->dst_param; + ins->src_count = opcode_info->param_count - opcode_info->dst_count; + ins->src = priv->src_param; + memset(&ins->texel_offset, 0, sizeof(ins->texel_offset)); + + p = *ptr; + *ptr += shader_skip_opcode(priv, opcode_info, opcode_token); + + if (ins->handler_idx == WINED3DSIH_DCL) + { + shader_sm1_read_semantic(&p, &ins->declaration.semantic); + } + else if (ins->handler_idx == WINED3DSIH_DEF) + { + shader_sm1_read_dst_param(priv, &p, &priv->dst_param, &priv->dst_rel_addr); + shader_sm1_read_immconst(&p, &priv->src_param[0], WINED3D_IMMCONST_VEC4, WINED3D_DATA_FLOAT); + } + else if (ins->handler_idx == WINED3DSIH_DEFB) + { + shader_sm1_read_dst_param(priv, &p, &priv->dst_param, &priv->dst_rel_addr); + shader_sm1_read_immconst(&p, &priv->src_param[0], WINED3D_IMMCONST_SCALAR, WINED3D_DATA_UINT); + } + else if (ins->handler_idx == WINED3DSIH_DEFI) + { + shader_sm1_read_dst_param(priv, &p, &priv->dst_param, &priv->dst_rel_addr); + shader_sm1_read_immconst(&p, &priv->src_param[0], WINED3D_IMMCONST_VEC4, WINED3D_DATA_INT); + } + else + { + /* Destination token */ + if (ins->dst_count) + shader_sm1_read_dst_param(priv, &p, &priv->dst_param, &priv->dst_rel_addr); + + /* Predication token */ + if (ins->predicate) + shader_sm1_read_src_param(priv, &p, &priv->pred_param, &priv->pred_rel_addr); + + /* Other source tokens */ + for (i = 0; i < ins->src_count; ++i) + shader_sm1_read_src_param(priv, &p, &priv->src_param[i], &priv->src_rel_addr[i]); + } + + shader_sm1_validate_instruction(ins); +} + +static BOOL shader_sm1_is_end(void *data, const DWORD **ptr) +{ + shader_sm1_read_comment(ptr); + + if (**ptr == WINED3D_SM1_END) + { + ++(*ptr); + return TRUE; + } + + return FALSE; +} + +const struct wined3d_shader_frontend sm1_shader_frontend = +{ + shader_sm1_init, + shader_sm1_free, + shader_sm1_read_header, + shader_sm1_read_instruction, + shader_sm1_is_end, +}; diff --git a/wrappers/directx/d3dwine_wrapper/shader_sm4.c b/wrappers/directx/d3dwine_wrapper/shader_sm4.c new file mode 100644 index 00000000000..4635910269f --- /dev/null +++ b/wrappers/directx/d3dwine_wrapper/shader_sm4.c @@ -0,0 +1,2166 @@ +/* + * Copyright 2009 Henri Verbeet for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "config.h" +#include "wine/port.h" + +#include "wined3d_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(d3d_shader); +WINE_DECLARE_DEBUG_CHANNEL(d3d_bytecode); + +#define WINED3D_SM4_INSTRUCTION_MODIFIER (0x1u << 31) + +#define WINED3D_SM4_MODIFIER_MASK 0x3fu + +#define WINED3D_SM5_MODIFIER_DATA_TYPE_SHIFT 6 +#define WINED3D_SM5_MODIFIER_DATA_TYPE_MASK (0xffffu << WINED3D_SM5_MODIFIER_DATA_TYPE_SHIFT) + +#define WINED3D_SM5_MODIFIER_RESOURCE_TYPE_SHIFT 6 +#define WINED3D_SM5_MODIFIER_RESOURCE_TYPE_MASK (0xfu << WINED3D_SM5_MODIFIER_RESOURCE_TYPE_SHIFT) + +#define WINED3D_SM4_AOFFIMMI_U_SHIFT 9 +#define WINED3D_SM4_AOFFIMMI_U_MASK (0xfu << WINED3D_SM4_AOFFIMMI_U_SHIFT) +#define WINED3D_SM4_AOFFIMMI_V_SHIFT 13 +#define WINED3D_SM4_AOFFIMMI_V_MASK (0xfu << WINED3D_SM4_AOFFIMMI_V_SHIFT) +#define WINED3D_SM4_AOFFIMMI_W_SHIFT 17 +#define WINED3D_SM4_AOFFIMMI_W_MASK (0xfu << WINED3D_SM4_AOFFIMMI_W_SHIFT) + +#define WINED3D_SM4_INSTRUCTION_LENGTH_SHIFT 24 +#define WINED3D_SM4_INSTRUCTION_LENGTH_MASK (0x1fu << WINED3D_SM4_INSTRUCTION_LENGTH_SHIFT) + +#define WINED3D_SM4_INSTRUCTION_FLAGS_SHIFT 11 +#define WINED3D_SM4_INSTRUCTION_FLAGS_MASK (0x7u << WINED3D_SM4_INSTRUCTION_FLAGS_SHIFT) + +#define WINED3D_SM4_RESOURCE_TYPE_SHIFT 11 +#define WINED3D_SM4_RESOURCE_TYPE_MASK (0xfu << WINED3D_SM4_RESOURCE_TYPE_SHIFT) + +#define WINED3D_SM4_PRIMITIVE_TYPE_SHIFT 11 +#define WINED3D_SM4_PRIMITIVE_TYPE_MASK (0x3fu << WINED3D_SM4_PRIMITIVE_TYPE_SHIFT) + +#define WINED3D_SM4_INDEX_TYPE_SHIFT 11 +#define WINED3D_SM4_INDEX_TYPE_MASK (0x1u << WINED3D_SM4_INDEX_TYPE_SHIFT) + +#define WINED3D_SM4_SAMPLER_MODE_SHIFT 11 +#define WINED3D_SM4_SAMPLER_MODE_MASK (0xfu << WINED3D_SM4_SAMPLER_MODE_SHIFT) + +#define WINED3D_SM4_SHADER_DATA_TYPE_SHIFT 11 +#define WINED3D_SM4_SHADER_DATA_TYPE_MASK (0xfu << WINED3D_SM4_SHADER_DATA_TYPE_SHIFT) + +#define WINED3D_SM4_INTERPOLATION_MODE_SHIFT 11 +#define WINED3D_SM4_INTERPOLATION_MODE_MASK (0xfu << WINED3D_SM4_INTERPOLATION_MODE_SHIFT) + +#define WINED3D_SM4_GLOBAL_FLAGS_SHIFT 11 +#define WINED3D_SM4_GLOBAL_FLAGS_MASK (0xffu << WINED3D_SM4_GLOBAL_FLAGS_SHIFT) + +#define WINED3D_SM5_PRECISE_SHIFT 19 +#define WINED3D_SM5_PRECISE_MASK (0xfu << WINED3D_SM5_PRECISE_SHIFT) + +#define WINED3D_SM5_CONTROL_POINT_COUNT_SHIFT 11 +#define WINED3D_SM5_CONTROL_POINT_COUNT_MASK (0xffu << WINED3D_SM5_CONTROL_POINT_COUNT_SHIFT) + +#define WINED3D_SM5_FP_ARRAY_SIZE_SHIFT 16 +#define WINED3D_SM5_FP_TABLE_COUNT_MASK 0xffffu + +#define WINED3D_SM5_UAV_FLAGS_SHIFT 15 +#define WINED3D_SM5_UAV_FLAGS_MASK (0x1ffu << WINED3D_SM5_UAV_FLAGS_SHIFT) + +#define WINED3D_SM5_SYNC_FLAGS_SHIFT 11 +#define WINED3D_SM5_SYNC_FLAGS_MASK (0xffu << WINED3D_SM5_SYNC_FLAGS_SHIFT) + +#define WINED3D_SM5_TESSELLATOR_SHIFT 11 +#define WINED3D_SM5_TESSELLATOR_MASK (0xfu << WINED3D_SM5_TESSELLATOR_SHIFT) + +#define WINED3D_SM4_OPCODE_MASK 0xff + +#define WINED3D_SM4_REGISTER_MODIFIER (0x1u << 31) + +#define WINED3D_SM4_ADDRESSING_SHIFT1 25 +#define WINED3D_SM4_ADDRESSING_MASK1 (0x3u << WINED3D_SM4_ADDRESSING_SHIFT1) + +#define WINED3D_SM4_ADDRESSING_SHIFT0 22 +#define WINED3D_SM4_ADDRESSING_MASK0 (0x3u << WINED3D_SM4_ADDRESSING_SHIFT0) + +#define WINED3D_SM4_REGISTER_ORDER_SHIFT 20 +#define WINED3D_SM4_REGISTER_ORDER_MASK (0x3u << WINED3D_SM4_REGISTER_ORDER_SHIFT) + +#define WINED3D_SM4_REGISTER_TYPE_SHIFT 12 +#define WINED3D_SM4_REGISTER_TYPE_MASK (0xffu << WINED3D_SM4_REGISTER_TYPE_SHIFT) + +#define WINED3D_SM4_SWIZZLE_TYPE_SHIFT 2 +#define WINED3D_SM4_SWIZZLE_TYPE_MASK (0x3u << WINED3D_SM4_SWIZZLE_TYPE_SHIFT) + +#define WINED3D_SM4_DIMENSION_SHIFT 0 +#define WINED3D_SM4_DIMENSION_MASK (0x3u << WINED3D_SM4_DIMENSION_SHIFT) + +#define WINED3D_SM4_WRITEMASK_SHIFT 4 +#define WINED3D_SM4_WRITEMASK_MASK (0xfu << WINED3D_SM4_WRITEMASK_SHIFT) + +#define WINED3D_SM4_SWIZZLE_SHIFT 4 +#define WINED3D_SM4_SWIZZLE_MASK (0xffu << WINED3D_SM4_SWIZZLE_SHIFT) + +#define WINED3D_SM4_VERSION_MAJOR(version) (((version) >> 4) & 0xf) +#define WINED3D_SM4_VERSION_MINOR(version) (((version) >> 0) & 0xf) + +#define WINED3D_SM4_ADDRESSING_RELATIVE 0x2 +#define WINED3D_SM4_ADDRESSING_OFFSET 0x1 + +#define WINED3D_SM4_INSTRUCTION_FLAG_SATURATE 0x4 + +#define WINED3D_SM4_CONDITIONAL_NZ (0x1u << 18) + +enum wined3d_sm4_opcode +{ + WINED3D_SM4_OP_ADD = 0x00, + WINED3D_SM4_OP_AND = 0x01, + WINED3D_SM4_OP_BREAK = 0x02, + WINED3D_SM4_OP_BREAKC = 0x03, + WINED3D_SM4_OP_CASE = 0x06, + WINED3D_SM4_OP_CONTINUE = 0x07, + WINED3D_SM4_OP_CONTINUEC = 0x08, + WINED3D_SM4_OP_CUT = 0x09, + WINED3D_SM4_OP_DEFAULT = 0x0a, + WINED3D_SM4_OP_DERIV_RTX = 0x0b, + WINED3D_SM4_OP_DERIV_RTY = 0x0c, + WINED3D_SM4_OP_DISCARD = 0x0d, + WINED3D_SM4_OP_DIV = 0x0e, + WINED3D_SM4_OP_DP2 = 0x0f, + WINED3D_SM4_OP_DP3 = 0x10, + WINED3D_SM4_OP_DP4 = 0x11, + WINED3D_SM4_OP_ELSE = 0x12, + WINED3D_SM4_OP_EMIT = 0x13, + WINED3D_SM4_OP_ENDIF = 0x15, + WINED3D_SM4_OP_ENDLOOP = 0x16, + WINED3D_SM4_OP_ENDSWITCH = 0x17, + WINED3D_SM4_OP_EQ = 0x18, + WINED3D_SM4_OP_EXP = 0x19, + WINED3D_SM4_OP_FRC = 0x1a, + WINED3D_SM4_OP_FTOI = 0x1b, + WINED3D_SM4_OP_FTOU = 0x1c, + WINED3D_SM4_OP_GE = 0x1d, + WINED3D_SM4_OP_IADD = 0x1e, + WINED3D_SM4_OP_IF = 0x1f, + WINED3D_SM4_OP_IEQ = 0x20, + WINED3D_SM4_OP_IGE = 0x21, + WINED3D_SM4_OP_ILT = 0x22, + WINED3D_SM4_OP_IMAD = 0x23, + WINED3D_SM4_OP_IMAX = 0x24, + WINED3D_SM4_OP_IMIN = 0x25, + WINED3D_SM4_OP_IMUL = 0x26, + WINED3D_SM4_OP_INE = 0x27, + WINED3D_SM4_OP_INEG = 0x28, + WINED3D_SM4_OP_ISHL = 0x29, + WINED3D_SM4_OP_ISHR = 0x2a, + WINED3D_SM4_OP_ITOF = 0x2b, + WINED3D_SM4_OP_LABEL = 0x2c, + WINED3D_SM4_OP_LD = 0x2d, + WINED3D_SM4_OP_LD2DMS = 0x2e, + WINED3D_SM4_OP_LOG = 0x2f, + WINED3D_SM4_OP_LOOP = 0x30, + WINED3D_SM4_OP_LT = 0x31, + WINED3D_SM4_OP_MAD = 0x32, + WINED3D_SM4_OP_MIN = 0x33, + WINED3D_SM4_OP_MAX = 0x34, + WINED3D_SM4_OP_SHADER_DATA = 0x35, + WINED3D_SM4_OP_MOV = 0x36, + WINED3D_SM4_OP_MOVC = 0x37, + WINED3D_SM4_OP_MUL = 0x38, + WINED3D_SM4_OP_NE = 0x39, + WINED3D_SM4_OP_NOP = 0x3a, + WINED3D_SM4_OP_NOT = 0x3b, + WINED3D_SM4_OP_OR = 0x3c, + WINED3D_SM4_OP_RESINFO = 0x3d, + WINED3D_SM4_OP_RET = 0x3e, + WINED3D_SM4_OP_RETC = 0x3f, + WINED3D_SM4_OP_ROUND_NE = 0x40, + WINED3D_SM4_OP_ROUND_NI = 0x41, + WINED3D_SM4_OP_ROUND_PI = 0x42, + WINED3D_SM4_OP_ROUND_Z = 0x43, + WINED3D_SM4_OP_RSQ = 0x44, + WINED3D_SM4_OP_SAMPLE = 0x45, + WINED3D_SM4_OP_SAMPLE_C = 0x46, + WINED3D_SM4_OP_SAMPLE_C_LZ = 0x47, + WINED3D_SM4_OP_SAMPLE_LOD = 0x48, + WINED3D_SM4_OP_SAMPLE_GRAD = 0x49, + WINED3D_SM4_OP_SAMPLE_B = 0x4a, + WINED3D_SM4_OP_SQRT = 0x4b, + WINED3D_SM4_OP_SWITCH = 0x4c, + WINED3D_SM4_OP_SINCOS = 0x4d, + WINED3D_SM4_OP_UDIV = 0x4e, + WINED3D_SM4_OP_ULT = 0x4f, + WINED3D_SM4_OP_UGE = 0x50, + WINED3D_SM4_OP_UMUL = 0x51, + WINED3D_SM4_OP_UMAX = 0x53, + WINED3D_SM4_OP_UMIN = 0x54, + WINED3D_SM4_OP_USHR = 0x55, + WINED3D_SM4_OP_UTOF = 0x56, + WINED3D_SM4_OP_XOR = 0x57, + WINED3D_SM4_OP_DCL_RESOURCE = 0x58, + WINED3D_SM4_OP_DCL_CONSTANT_BUFFER = 0x59, + WINED3D_SM4_OP_DCL_SAMPLER = 0x5a, + WINED3D_SM4_OP_DCL_INDEX_RANGE = 0x5b, + WINED3D_SM4_OP_DCL_OUTPUT_TOPOLOGY = 0x5c, + WINED3D_SM4_OP_DCL_INPUT_PRIMITIVE = 0x5d, + WINED3D_SM4_OP_DCL_VERTICES_OUT = 0x5e, + WINED3D_SM4_OP_DCL_INPUT = 0x5f, + WINED3D_SM4_OP_DCL_INPUT_SGV = 0x60, + WINED3D_SM4_OP_DCL_INPUT_SIV = 0x61, + WINED3D_SM4_OP_DCL_INPUT_PS = 0x62, + WINED3D_SM4_OP_DCL_INPUT_PS_SGV = 0x63, + WINED3D_SM4_OP_DCL_INPUT_PS_SIV = 0x64, + WINED3D_SM4_OP_DCL_OUTPUT = 0x65, + WINED3D_SM4_OP_DCL_OUTPUT_SIV = 0x67, + WINED3D_SM4_OP_DCL_TEMPS = 0x68, + WINED3D_SM4_OP_DCL_INDEXABLE_TEMP = 0x69, + WINED3D_SM4_OP_DCL_GLOBAL_FLAGS = 0x6a, + WINED3D_SM4_OP_LOD = 0x6c, + WINED3D_SM4_OP_GATHER4 = 0x6d, + WINED3D_SM4_OP_SAMPLE_POS = 0x6e, + WINED3D_SM4_OP_SAMPLE_INFO = 0x6f, + WINED3D_SM5_OP_HS_DECLS = 0x71, + WINED3D_SM5_OP_HS_CONTROL_POINT_PHASE = 0x72, + WINED3D_SM5_OP_HS_FORK_PHASE = 0x73, + WINED3D_SM5_OP_HS_JOIN_PHASE = 0x74, + WINED3D_SM5_OP_EMIT_STREAM = 0x75, + WINED3D_SM5_OP_CUT_STREAM = 0x76, + WINED3D_SM5_OP_FCALL = 0x78, + WINED3D_SM5_OP_BUFINFO = 0x79, + WINED3D_SM5_OP_DERIV_RTX_COARSE = 0x7a, + WINED3D_SM5_OP_DERIV_RTX_FINE = 0x7b, + WINED3D_SM5_OP_DERIV_RTY_COARSE = 0x7c, + WINED3D_SM5_OP_DERIV_RTY_FINE = 0x7d, + WINED3D_SM5_OP_GATHER4_C = 0x7e, + WINED3D_SM5_OP_GATHER4_PO = 0x7f, + WINED3D_SM5_OP_GATHER4_PO_C = 0x80, + WINED3D_SM5_OP_RCP = 0x81, + WINED3D_SM5_OP_F32TOF16 = 0x82, + WINED3D_SM5_OP_F16TOF32 = 0x83, + WINED3D_SM5_OP_COUNTBITS = 0x86, + WINED3D_SM5_OP_FIRSTBIT_HI = 0x87, + WINED3D_SM5_OP_FIRSTBIT_LO = 0x88, + WINED3D_SM5_OP_FIRSTBIT_SHI = 0x89, + WINED3D_SM5_OP_UBFE = 0x8a, + WINED3D_SM5_OP_IBFE = 0x8b, + WINED3D_SM5_OP_BFI = 0x8c, + WINED3D_SM5_OP_BFREV = 0x8d, + WINED3D_SM5_OP_SWAPC = 0x8e, + WINED3D_SM5_OP_DCL_STREAM = 0x8f, + WINED3D_SM5_OP_DCL_FUNCTION_BODY = 0x90, + WINED3D_SM5_OP_DCL_FUNCTION_TABLE = 0x91, + WINED3D_SM5_OP_DCL_INTERFACE = 0x92, + WINED3D_SM5_OP_DCL_INPUT_CONTROL_POINT_COUNT = 0x93, + WINED3D_SM5_OP_DCL_OUTPUT_CONTROL_POINT_COUNT = 0x94, + WINED3D_SM5_OP_DCL_TESSELLATOR_DOMAIN = 0x95, + WINED3D_SM5_OP_DCL_TESSELLATOR_PARTITIONING = 0x96, + WINED3D_SM5_OP_DCL_TESSELLATOR_OUTPUT_PRIMITIVE = 0x97, + WINED3D_SM5_OP_DCL_HS_MAX_TESSFACTOR = 0x98, + WINED3D_SM5_OP_DCL_HS_FORK_PHASE_INSTANCE_COUNT = 0x99, + WINED3D_SM5_OP_DCL_HS_JOIN_PHASE_INSTANCE_COUNT = 0x9a, + WINED3D_SM5_OP_DCL_THREAD_GROUP = 0x9b, + WINED3D_SM5_OP_DCL_UAV_TYPED = 0x9c, + WINED3D_SM5_OP_DCL_UAV_RAW = 0x9d, + WINED3D_SM5_OP_DCL_UAV_STRUCTURED = 0x9e, + WINED3D_SM5_OP_DCL_TGSM_RAW = 0x9f, + WINED3D_SM5_OP_DCL_TGSM_STRUCTURED = 0xa0, + WINED3D_SM5_OP_DCL_RESOURCE_RAW = 0xa1, + WINED3D_SM5_OP_DCL_RESOURCE_STRUCTURED = 0xa2, + WINED3D_SM5_OP_LD_UAV_TYPED = 0xa3, + WINED3D_SM5_OP_STORE_UAV_TYPED = 0xa4, + WINED3D_SM5_OP_LD_RAW = 0xa5, + WINED3D_SM5_OP_STORE_RAW = 0xa6, + WINED3D_SM5_OP_LD_STRUCTURED = 0xa7, + WINED3D_SM5_OP_STORE_STRUCTURED = 0xa8, + WINED3D_SM5_OP_ATOMIC_AND = 0xa9, + WINED3D_SM5_OP_ATOMIC_OR = 0xaa, + WINED3D_SM5_OP_ATOMIC_XOR = 0xab, + WINED3D_SM5_OP_ATOMIC_CMP_STORE = 0xac, + WINED3D_SM5_OP_ATOMIC_IADD = 0xad, + WINED3D_SM5_OP_ATOMIC_IMAX = 0xae, + WINED3D_SM5_OP_ATOMIC_IMIN = 0xaf, + WINED3D_SM5_OP_ATOMIC_UMAX = 0xb0, + WINED3D_SM5_OP_ATOMIC_UMIN = 0xb1, + WINED3D_SM5_OP_IMM_ATOMIC_ALLOC = 0xb2, + WINED3D_SM5_OP_IMM_ATOMIC_CONSUME = 0xb3, + WINED3D_SM5_OP_IMM_ATOMIC_IADD = 0xb4, + WINED3D_SM5_OP_IMM_ATOMIC_AND = 0xb5, + WINED3D_SM5_OP_IMM_ATOMIC_OR = 0xb6, + WINED3D_SM5_OP_IMM_ATOMIC_XOR = 0xb7, + WINED3D_SM5_OP_IMM_ATOMIC_EXCH = 0xb8, + WINED3D_SM5_OP_IMM_ATOMIC_CMP_EXCH = 0xb9, + WINED3D_SM5_OP_IMM_ATOMIC_IMAX = 0xba, + WINED3D_SM5_OP_IMM_ATOMIC_IMIN = 0xbb, + WINED3D_SM5_OP_IMM_ATOMIC_UMAX = 0xbc, + WINED3D_SM5_OP_IMM_ATOMIC_UMIN = 0xbd, + WINED3D_SM5_OP_SYNC = 0xbe, + WINED3D_SM5_OP_EVAL_SAMPLE_INDEX = 0xcc, + WINED3D_SM5_OP_DCL_GS_INSTANCES = 0xce, +}; + +enum wined3d_sm4_instruction_modifier +{ + WINED3D_SM4_MODIFIER_AOFFIMMI = 0x1, + WINED3D_SM5_MODIFIER_RESOURCE_TYPE = 0x2, + WINED3D_SM5_MODIFIER_DATA_TYPE = 0x3, +}; + +enum wined3d_sm4_register_type +{ + WINED3D_SM4_RT_TEMP = 0x00, + WINED3D_SM4_RT_INPUT = 0x01, + WINED3D_SM4_RT_OUTPUT = 0x02, + WINED3D_SM4_RT_INDEXABLE_TEMP = 0x03, + WINED3D_SM4_RT_IMMCONST = 0x04, + WINED3D_SM4_RT_SAMPLER = 0x06, + WINED3D_SM4_RT_RESOURCE = 0x07, + WINED3D_SM4_RT_CONSTBUFFER = 0x08, + WINED3D_SM4_RT_IMMCONSTBUFFER = 0x09, + WINED3D_SM4_RT_PRIMID = 0x0b, + WINED3D_SM4_RT_DEPTHOUT = 0x0c, + WINED3D_SM4_RT_NULL = 0x0d, + WINED3D_SM4_RT_RASTERIZER = 0x0e, + WINED3D_SM4_RT_OMASK = 0x0f, + WINED3D_SM5_RT_STREAM = 0x10, + WINED3D_SM5_RT_FUNCTION_BODY = 0x11, + WINED3D_SM5_RT_FUNCTION_POINTER = 0x13, + WINED3D_SM5_RT_OUTPUT_CONTROL_POINT_ID = 0x16, + WINED3D_SM5_RT_FORK_INSTANCE_ID = 0x17, + WINED3D_SM5_RT_JOIN_INSTANCE_ID = 0x18, + WINED3D_SM5_RT_INPUT_CONTROL_POINT = 0x19, + WINED3D_SM5_RT_OUTPUT_CONTROL_POINT = 0x1a, + WINED3D_SM5_RT_PATCH_CONSTANT_DATA = 0x1b, + WINED3D_SM5_RT_DOMAIN_LOCATION = 0x1c, + WINED3D_SM5_RT_UAV = 0x1e, + WINED3D_SM5_RT_SHARED_MEMORY = 0x1f, + WINED3D_SM5_RT_THREAD_ID = 0x20, + WINED3D_SM5_RT_THREAD_GROUP_ID = 0x21, + WINED3D_SM5_RT_LOCAL_THREAD_ID = 0x22, + WINED3D_SM5_RT_COVERAGE = 0x23, + WINED3D_SM5_RT_LOCAL_THREAD_INDEX = 0x24, + WINED3D_SM5_RT_GS_INSTANCE_ID = 0x25, + WINED3D_SM5_RT_DEPTHOUT_GREATER_EQUAL = 0x26, + WINED3D_SM5_RT_DEPTHOUT_LESS_EQUAL = 0x27, +}; + +enum wined3d_sm4_output_primitive_type +{ + WINED3D_SM4_OUTPUT_PT_POINTLIST = 0x1, + WINED3D_SM4_OUTPUT_PT_LINESTRIP = 0x3, + WINED3D_SM4_OUTPUT_PT_TRIANGLESTRIP = 0x5, +}; + +enum wined3d_sm4_input_primitive_type +{ + WINED3D_SM4_INPUT_PT_POINT = 0x01, + WINED3D_SM4_INPUT_PT_LINE = 0x02, + WINED3D_SM4_INPUT_PT_TRIANGLE = 0x03, + WINED3D_SM4_INPUT_PT_LINEADJ = 0x06, + WINED3D_SM4_INPUT_PT_TRIANGLEADJ = 0x07, + WINED3D_SM5_INPUT_PT_PATCH1 = 0x08, + WINED3D_SM5_INPUT_PT_PATCH2 = 0x09, + WINED3D_SM5_INPUT_PT_PATCH3 = 0x0a, + WINED3D_SM5_INPUT_PT_PATCH4 = 0x0b, + WINED3D_SM5_INPUT_PT_PATCH5 = 0x0c, + WINED3D_SM5_INPUT_PT_PATCH6 = 0x0d, + WINED3D_SM5_INPUT_PT_PATCH7 = 0x0e, + WINED3D_SM5_INPUT_PT_PATCH8 = 0x0f, + WINED3D_SM5_INPUT_PT_PATCH9 = 0x10, + WINED3D_SM5_INPUT_PT_PATCH10 = 0x11, + WINED3D_SM5_INPUT_PT_PATCH11 = 0x12, + WINED3D_SM5_INPUT_PT_PATCH12 = 0x13, + WINED3D_SM5_INPUT_PT_PATCH13 = 0x14, + WINED3D_SM5_INPUT_PT_PATCH14 = 0x15, + WINED3D_SM5_INPUT_PT_PATCH15 = 0x16, + WINED3D_SM5_INPUT_PT_PATCH16 = 0x17, + WINED3D_SM5_INPUT_PT_PATCH17 = 0x18, + WINED3D_SM5_INPUT_PT_PATCH18 = 0x19, + WINED3D_SM5_INPUT_PT_PATCH19 = 0x1a, + WINED3D_SM5_INPUT_PT_PATCH20 = 0x1b, + WINED3D_SM5_INPUT_PT_PATCH21 = 0x1c, + WINED3D_SM5_INPUT_PT_PATCH22 = 0x1d, + WINED3D_SM5_INPUT_PT_PATCH23 = 0x1e, + WINED3D_SM5_INPUT_PT_PATCH24 = 0x1f, + WINED3D_SM5_INPUT_PT_PATCH25 = 0x20, + WINED3D_SM5_INPUT_PT_PATCH26 = 0x21, + WINED3D_SM5_INPUT_PT_PATCH27 = 0x22, + WINED3D_SM5_INPUT_PT_PATCH28 = 0x23, + WINED3D_SM5_INPUT_PT_PATCH29 = 0x24, + WINED3D_SM5_INPUT_PT_PATCH30 = 0x25, + WINED3D_SM5_INPUT_PT_PATCH31 = 0x26, + WINED3D_SM5_INPUT_PT_PATCH32 = 0x27, +}; + +enum wined3d_sm4_swizzle_type +{ + WINED3D_SM4_SWIZZLE_NONE = 0x0, + WINED3D_SM4_SWIZZLE_VEC4 = 0x1, + WINED3D_SM4_SWIZZLE_SCALAR = 0x2, +}; + +enum wined3d_sm4_dimension +{ + WINED3D_SM4_DIMENSION_SCALAR = 0x1, + WINED3D_SM4_DIMENSION_VEC4 = 0x2, +}; + +enum wined3d_sm4_resource_type +{ + WINED3D_SM4_RESOURCE_BUFFER = 0x1, + WINED3D_SM4_RESOURCE_TEXTURE_1D = 0x2, + WINED3D_SM4_RESOURCE_TEXTURE_2D = 0x3, + WINED3D_SM4_RESOURCE_TEXTURE_2DMS = 0x4, + WINED3D_SM4_RESOURCE_TEXTURE_3D = 0x5, + WINED3D_SM4_RESOURCE_TEXTURE_CUBE = 0x6, + WINED3D_SM4_RESOURCE_TEXTURE_1DARRAY = 0x7, + WINED3D_SM4_RESOURCE_TEXTURE_2DARRAY = 0x8, + WINED3D_SM4_RESOURCE_TEXTURE_2DMSARRAY = 0x9, + WINED3D_SM4_RESOURCE_TEXTURE_CUBEARRAY = 0xa, +}; + +enum wined3d_sm4_data_type +{ + WINED3D_SM4_DATA_UNORM = 0x1, + WINED3D_SM4_DATA_SNORM = 0x2, + WINED3D_SM4_DATA_INT = 0x3, + WINED3D_SM4_DATA_UINT = 0x4, + WINED3D_SM4_DATA_FLOAT = 0x5, +}; + +enum wined3d_sm4_sampler_mode +{ + WINED3D_SM4_SAMPLER_DEFAULT = 0x0, + WINED3D_SM4_SAMPLER_COMPARISON = 0x1, +}; + +enum wined3d_sm4_shader_data_type +{ + WINED3D_SM4_SHADER_DATA_IMMEDIATE_CONSTANT_BUFFER = 0x3, + WINED3D_SM4_SHADER_DATA_MESSAGE = 0x4, +}; + +struct wined3d_shader_src_param_entry +{ + struct list entry; + struct wined3d_shader_src_param param; +}; + +struct wined3d_sm4_data +{ + struct wined3d_shader_version shader_version; + const DWORD *start, *end; + + unsigned int output_map[MAX_REG_OUTPUT]; + + struct wined3d_shader_src_param src_param[5]; + struct wined3d_shader_dst_param dst_param[2]; + struct list src_free; + struct list src; + struct wined3d_shader_immediate_constant_buffer icb; +}; + +struct wined3d_sm4_opcode_info +{ + enum wined3d_sm4_opcode opcode; + enum WINED3D_SHADER_INSTRUCTION_HANDLER handler_idx; + const char *dst_info; + const char *src_info; + void (*read_opcode_func)(struct wined3d_shader_instruction *ins, + DWORD opcode, DWORD opcode_token, const DWORD *tokens, unsigned int token_count, + struct wined3d_sm4_data *priv); +}; + +static const enum wined3d_primitive_type output_primitive_type_table[] = +{ + /* UNKNOWN */ WINED3D_PT_UNDEFINED, + /* WINED3D_SM4_OUTPUT_PT_POINTLIST */ WINED3D_PT_POINTLIST, + /* UNKNOWN */ WINED3D_PT_UNDEFINED, + /* WINED3D_SM4_OUTPUT_PT_LINESTRIP */ WINED3D_PT_LINESTRIP, + /* UNKNOWN */ WINED3D_PT_UNDEFINED, + /* WINED3D_SM4_OUTPUT_PT_TRIANGLESTRIP */ WINED3D_PT_TRIANGLESTRIP, +}; + +static const enum wined3d_primitive_type input_primitive_type_table[] = +{ + /* UNKNOWN */ WINED3D_PT_UNDEFINED, + /* WINED3D_SM4_INPUT_PT_POINT */ WINED3D_PT_POINTLIST, + /* WINED3D_SM4_INPUT_PT_LINE */ WINED3D_PT_LINELIST, + /* WINED3D_SM4_INPUT_PT_TRIANGLE */ WINED3D_PT_TRIANGLELIST, + /* UNKNOWN */ WINED3D_PT_UNDEFINED, + /* UNKNOWN */ WINED3D_PT_UNDEFINED, + /* WINED3D_SM4_INPUT_PT_LINEADJ */ WINED3D_PT_LINELIST_ADJ, + /* WINED3D_SM4_INPUT_PT_TRIANGLEADJ */ WINED3D_PT_TRIANGLELIST_ADJ, +}; + +static const enum wined3d_shader_resource_type resource_type_table[] = +{ + /* 0 */ WINED3D_SHADER_RESOURCE_NONE, + /* WINED3D_SM4_RESOURCE_BUFFER */ WINED3D_SHADER_RESOURCE_BUFFER, + /* WINED3D_SM4_RESOURCE_TEXTURE_1D */ WINED3D_SHADER_RESOURCE_TEXTURE_1D, + /* WINED3D_SM4_RESOURCE_TEXTURE_2D */ WINED3D_SHADER_RESOURCE_TEXTURE_2D, + /* WINED3D_SM4_RESOURCE_TEXTURE_2DMS */ WINED3D_SHADER_RESOURCE_TEXTURE_2DMS, + /* WINED3D_SM4_RESOURCE_TEXTURE_3D */ WINED3D_SHADER_RESOURCE_TEXTURE_3D, + /* WINED3D_SM4_RESOURCE_TEXTURE_CUBE */ WINED3D_SHADER_RESOURCE_TEXTURE_CUBE, + /* WINED3D_SM4_RESOURCE_TEXTURE_1DARRAY */ WINED3D_SHADER_RESOURCE_TEXTURE_1DARRAY, + /* WINED3D_SM4_RESOURCE_TEXTURE_2DARRAY */ WINED3D_SHADER_RESOURCE_TEXTURE_2DARRAY, + /* WINED3D_SM4_RESOURCE_TEXTURE_2DMSARRAY */ WINED3D_SHADER_RESOURCE_TEXTURE_2DMSARRAY, + /* WINED3D_SM4_RESOURCE_TEXTURE_CUBEARRAY */ WINED3D_SHADER_RESOURCE_TEXTURE_CUBEARRAY, +}; + +static const enum wined3d_data_type data_type_table[] = +{ + /* 0 */ WINED3D_DATA_FLOAT, + /* WINED3D_SM4_DATA_UNORM */ WINED3D_DATA_UNORM, + /* WINED3D_SM4_DATA_SNORM */ WINED3D_DATA_SNORM, + /* WINED3D_SM4_DATA_INT */ WINED3D_DATA_INT, + /* WINED3D_SM4_DATA_UINT */ WINED3D_DATA_UINT, + /* WINED3D_SM4_DATA_FLOAT */ WINED3D_DATA_FLOAT, +}; + +static BOOL shader_sm4_read_src_param(struct wined3d_sm4_data *priv, const DWORD **ptr, const DWORD *end, + enum wined3d_data_type data_type, struct wined3d_shader_src_param *src_param); +static BOOL shader_sm4_read_dst_param(struct wined3d_sm4_data *priv, const DWORD **ptr, const DWORD *end, + enum wined3d_data_type data_type, struct wined3d_shader_dst_param *dst_param); + +static void shader_sm4_read_conditional_op(struct wined3d_shader_instruction *ins, + DWORD opcode, DWORD opcode_token, const DWORD *tokens, unsigned int token_count, + struct wined3d_sm4_data *priv) +{ + shader_sm4_read_src_param(priv, &tokens, &tokens[token_count], WINED3D_DATA_UINT, &priv->src_param[0]); + ins->flags = (opcode_token & WINED3D_SM4_CONDITIONAL_NZ) ? + WINED3D_SHADER_CONDITIONAL_OP_NZ : WINED3D_SHADER_CONDITIONAL_OP_Z; +} + +static void shader_sm4_read_shader_data(struct wined3d_shader_instruction *ins, + DWORD opcode, DWORD opcode_token, const DWORD *tokens, unsigned int token_count, + struct wined3d_sm4_data *priv) +{ + enum wined3d_sm4_shader_data_type type; + unsigned int icb_size; + + type = (opcode_token & WINED3D_SM4_SHADER_DATA_TYPE_MASK) >> WINED3D_SM4_SHADER_DATA_TYPE_SHIFT; + if (type != WINED3D_SM4_SHADER_DATA_IMMEDIATE_CONSTANT_BUFFER) + { + FIXME("Ignoring shader data type %#x.\n", type); + ins->handler_idx = WINED3DSIH_NOP; + return; + } + + ++tokens; + icb_size = token_count - 1; + if (icb_size % 4 || icb_size > MAX_IMMEDIATE_CONSTANT_BUFFER_SIZE) + { + FIXME("Unexpected immediate constant buffer size %u.\n", icb_size); + ins->handler_idx = WINED3DSIH_TABLE_SIZE; + return; + } + + priv->icb.vec4_count = icb_size / 4; + memcpy(priv->icb.data, tokens, sizeof(*tokens) * icb_size); + ins->declaration.icb = &priv->icb; +} + +static void shader_sm4_read_dcl_resource(struct wined3d_shader_instruction *ins, + DWORD opcode, DWORD opcode_token, const DWORD *tokens, unsigned int token_count, + struct wined3d_sm4_data *priv) +{ + enum wined3d_sm4_resource_type resource_type; + enum wined3d_sm4_data_type data_type; + enum wined3d_data_type reg_data_type; + DWORD components; + + resource_type = (opcode_token & WINED3D_SM4_RESOURCE_TYPE_MASK) >> WINED3D_SM4_RESOURCE_TYPE_SHIFT; + if (!resource_type || (resource_type >= ARRAY_SIZE(resource_type_table))) + { + FIXME("Unhandled resource type %#x.\n", resource_type); + ins->declaration.semantic.resource_type = WINED3D_SHADER_RESOURCE_NONE; + } + else + { + ins->declaration.semantic.resource_type = resource_type_table[resource_type]; + } + reg_data_type = opcode == WINED3D_SM4_OP_DCL_RESOURCE ? WINED3D_DATA_RESOURCE : WINED3D_DATA_UAV; + shader_sm4_read_dst_param(priv, &tokens, &tokens[token_count], reg_data_type, &ins->declaration.semantic.reg); + + components = *tokens++; + if ((components & 0xfff0) != (components & 0xf) * 0x1110) + FIXME("Components (%#x) have different data types.\n", components); + data_type = components & 0xf; + + if (!data_type || (data_type >= ARRAY_SIZE(data_type_table))) + { + FIXME("Unhandled data type %#x.\n", data_type); + ins->declaration.semantic.resource_data_type = WINED3D_DATA_FLOAT; + } + else + { + ins->declaration.semantic.resource_data_type = data_type_table[data_type]; + } + + if (reg_data_type == WINED3D_DATA_UAV) + ins->flags = (opcode_token & WINED3D_SM5_UAV_FLAGS_MASK) >> WINED3D_SM5_UAV_FLAGS_SHIFT; +} + +static void shader_sm4_read_dcl_constant_buffer(struct wined3d_shader_instruction *ins, + DWORD opcode, DWORD opcode_token, const DWORD *tokens, unsigned int token_count, + struct wined3d_sm4_data *priv) +{ + shader_sm4_read_src_param(priv, &tokens, &tokens[token_count], WINED3D_DATA_FLOAT, &ins->declaration.src); + if (opcode_token & WINED3D_SM4_INDEX_TYPE_MASK) + ins->flags |= WINED3DSI_INDEXED_DYNAMIC; +} + +static void shader_sm4_read_dcl_sampler(struct wined3d_shader_instruction *ins, + DWORD opcode, DWORD opcode_token, const DWORD *tokens, unsigned int token_count, + struct wined3d_sm4_data *priv) +{ + ins->flags = (opcode_token & WINED3D_SM4_SAMPLER_MODE_MASK) >> WINED3D_SM4_SAMPLER_MODE_SHIFT; + if (ins->flags & ~WINED3D_SM4_SAMPLER_COMPARISON) + FIXME("Unhandled sampler mode %#x.\n", ins->flags); + shader_sm4_read_dst_param(priv, &tokens, &tokens[token_count], WINED3D_DATA_SAMPLER, &ins->declaration.dst); +} + +static void shader_sm4_read_dcl_index_range(struct wined3d_shader_instruction *ins, + DWORD opcode, DWORD opcode_token, const DWORD *tokens, unsigned int token_count, + struct wined3d_sm4_data *priv) +{ + shader_sm4_read_dst_param(priv, &tokens, &tokens[token_count], WINED3D_DATA_OPAQUE, + &ins->declaration.index_range.first_register); + ins->declaration.index_range.last_register = *tokens; +} + +static void shader_sm4_read_dcl_output_topology(struct wined3d_shader_instruction *ins, + DWORD opcode, DWORD opcode_token, const DWORD *tokens, unsigned int token_count, + struct wined3d_sm4_data *priv) +{ + enum wined3d_sm4_output_primitive_type primitive_type; + + primitive_type = (opcode_token & WINED3D_SM4_PRIMITIVE_TYPE_MASK) >> WINED3D_SM4_PRIMITIVE_TYPE_SHIFT; + if (primitive_type >= ARRAY_SIZE(output_primitive_type_table)) + ins->declaration.primitive_type.type = WINED3D_PT_UNDEFINED; + else + ins->declaration.primitive_type.type = output_primitive_type_table[primitive_type]; + + if (ins->declaration.primitive_type.type == WINED3D_PT_UNDEFINED) + FIXME("Unhandled output primitive type %#x.\n", primitive_type); +} + +static void shader_sm4_read_dcl_input_primitive(struct wined3d_shader_instruction *ins, + DWORD opcode, DWORD opcode_token, const DWORD *tokens, unsigned int token_count, + struct wined3d_sm4_data *priv) +{ + enum wined3d_sm4_input_primitive_type primitive_type; + + primitive_type = (opcode_token & WINED3D_SM4_PRIMITIVE_TYPE_MASK) >> WINED3D_SM4_PRIMITIVE_TYPE_SHIFT; + if (WINED3D_SM5_INPUT_PT_PATCH1 <= primitive_type && primitive_type <= WINED3D_SM5_INPUT_PT_PATCH32) + { + ins->declaration.primitive_type.type = WINED3D_PT_PATCH; + ins->declaration.primitive_type.patch_vertex_count = primitive_type - WINED3D_SM5_INPUT_PT_PATCH1 + 1; + } + else if (primitive_type >= ARRAY_SIZE(input_primitive_type_table)) + { + ins->declaration.primitive_type.type = WINED3D_PT_UNDEFINED; + } + else + { + ins->declaration.primitive_type.type = input_primitive_type_table[primitive_type]; + } + + if (ins->declaration.primitive_type.type == WINED3D_PT_UNDEFINED) + FIXME("Unhandled input primitive type %#x.\n", primitive_type); +} + +static void shader_sm4_read_declaration_count(struct wined3d_shader_instruction *ins, + DWORD opcode, DWORD opcode_token, const DWORD *tokens, unsigned int token_count, + struct wined3d_sm4_data *priv) +{ + ins->declaration.count = *tokens; +} + +static void shader_sm4_read_declaration_dst(struct wined3d_shader_instruction *ins, + DWORD opcode, DWORD opcode_token, const DWORD *tokens, unsigned int token_count, + struct wined3d_sm4_data *priv) +{ + shader_sm4_read_dst_param(priv, &tokens, &tokens[token_count], WINED3D_DATA_FLOAT, &ins->declaration.dst); +} + +static void shader_sm4_read_declaration_register_semantic(struct wined3d_shader_instruction *ins, + DWORD opcode, DWORD opcode_token, const DWORD *tokens, unsigned int token_count, + struct wined3d_sm4_data *priv) +{ + shader_sm4_read_dst_param(priv, &tokens, &tokens[token_count], WINED3D_DATA_FLOAT, + &ins->declaration.register_semantic.reg); + ins->declaration.register_semantic.sysval_semantic = *tokens; +} + +static void shader_sm4_read_dcl_input_ps(struct wined3d_shader_instruction *ins, + DWORD opcode, DWORD opcode_token, const DWORD *tokens, unsigned int token_count, + struct wined3d_sm4_data *priv) +{ + ins->flags = (opcode_token & WINED3D_SM4_INTERPOLATION_MODE_MASK) >> WINED3D_SM4_INTERPOLATION_MODE_SHIFT; + shader_sm4_read_dst_param(priv, &tokens, &tokens[token_count], WINED3D_DATA_FLOAT, &ins->declaration.dst); +} + +static void shader_sm4_read_dcl_input_ps_siv(struct wined3d_shader_instruction *ins, + DWORD opcode, DWORD opcode_token, const DWORD *tokens, unsigned int token_count, + struct wined3d_sm4_data *priv) +{ + ins->flags = (opcode_token & WINED3D_SM4_INTERPOLATION_MODE_MASK) >> WINED3D_SM4_INTERPOLATION_MODE_SHIFT; + shader_sm4_read_dst_param(priv, &tokens, &tokens[token_count], WINED3D_DATA_FLOAT, + &ins->declaration.register_semantic.reg); + ins->declaration.register_semantic.sysval_semantic = *tokens; +} + +static void shader_sm4_read_dcl_indexable_temp(struct wined3d_shader_instruction *ins, + DWORD opcode, DWORD opcode_token, const DWORD *tokens, unsigned int token_count, + struct wined3d_sm4_data *priv) +{ + ins->declaration.indexable_temp.register_idx = *tokens++; + ins->declaration.indexable_temp.register_size = *tokens++; + ins->declaration.indexable_temp.component_count = *tokens; +} + +static void shader_sm4_read_dcl_global_flags(struct wined3d_shader_instruction *ins, + DWORD opcode, DWORD opcode_token, const DWORD *tokens, unsigned int token_count, + struct wined3d_sm4_data *priv) +{ + ins->flags = (opcode_token & WINED3D_SM4_GLOBAL_FLAGS_MASK) >> WINED3D_SM4_GLOBAL_FLAGS_SHIFT; +} + +static void shader_sm5_read_fcall(struct wined3d_shader_instruction *ins, + DWORD opcode, DWORD opcode_token, const DWORD *tokens, unsigned int token_count, + struct wined3d_sm4_data *priv) +{ + priv->src_param[0].reg.u.fp_body_idx = *tokens++; + shader_sm4_read_src_param(priv, &tokens, &tokens[token_count], WINED3D_DATA_OPAQUE, &priv->src_param[0]); +} + +static void shader_sm5_read_dcl_function_body(struct wined3d_shader_instruction *ins, + DWORD opcode, DWORD opcode_token, const DWORD *tokens, unsigned int token_count, + struct wined3d_sm4_data *priv) +{ + ins->declaration.index = *tokens; +} + +static void shader_sm5_read_dcl_function_table(struct wined3d_shader_instruction *ins, + DWORD opcode, DWORD opcode_token, const DWORD *tokens, unsigned int token_count, + struct wined3d_sm4_data *priv) +{ + ins->declaration.index = *tokens++; + FIXME("Ignoring set of function bodies (count %u).\n", *tokens); +} + +static void shader_sm5_read_dcl_interface(struct wined3d_shader_instruction *ins, + DWORD opcode, DWORD opcode_token, const DWORD *tokens, unsigned int token_count, + struct wined3d_sm4_data *priv) +{ + ins->declaration.fp.index = *tokens++; + ins->declaration.fp.body_count = *tokens++; + ins->declaration.fp.array_size = *tokens >> WINED3D_SM5_FP_ARRAY_SIZE_SHIFT; + ins->declaration.fp.table_count = *tokens++ & WINED3D_SM5_FP_TABLE_COUNT_MASK; + FIXME("Ignoring set of function tables (count %u).\n", ins->declaration.fp.table_count); +} + +static void shader_sm5_read_control_point_count(struct wined3d_shader_instruction *ins, + DWORD opcode, DWORD opcode_token, const DWORD *tokens, unsigned int token_count, + struct wined3d_sm4_data *priv) +{ + ins->declaration.count = (opcode_token & WINED3D_SM5_CONTROL_POINT_COUNT_MASK) + >> WINED3D_SM5_CONTROL_POINT_COUNT_SHIFT; +} + +static void shader_sm5_read_dcl_tessellator_domain(struct wined3d_shader_instruction *ins, + DWORD opcode, DWORD opcode_token, const DWORD *tokens, unsigned int token_count, + struct wined3d_sm4_data *priv) +{ + ins->declaration.tessellator_domain = (opcode_token & WINED3D_SM5_TESSELLATOR_MASK) + >> WINED3D_SM5_TESSELLATOR_SHIFT; +} + +static void shader_sm5_read_dcl_tessellator_partitioning(struct wined3d_shader_instruction *ins, + DWORD opcode, DWORD opcode_token, const DWORD *tokens, unsigned int token_count, + struct wined3d_sm4_data *priv) +{ + ins->declaration.tessellator_partitioning = (opcode_token & WINED3D_SM5_TESSELLATOR_MASK) + >> WINED3D_SM5_TESSELLATOR_SHIFT; +} + +static void shader_sm5_read_dcl_tessellator_output_primitive(struct wined3d_shader_instruction *ins, + DWORD opcode, DWORD opcode_token, const DWORD *tokens, unsigned int token_count, + struct wined3d_sm4_data *priv) +{ + ins->declaration.tessellator_output_primitive = (opcode_token & WINED3D_SM5_TESSELLATOR_MASK) + >> WINED3D_SM5_TESSELLATOR_SHIFT; +} + +static void shader_sm5_read_dcl_hs_max_tessfactor(struct wined3d_shader_instruction *ins, + DWORD opcode, DWORD opcode_token, const DWORD *tokens, unsigned int token_count, + struct wined3d_sm4_data *priv) +{ + ins->declaration.max_tessellation_factor = *(float *)tokens; +} + +static void shader_sm5_read_dcl_thread_group(struct wined3d_shader_instruction *ins, + DWORD opcode, DWORD opcode_token, const DWORD *tokens, unsigned int token_count, + struct wined3d_sm4_data *priv) +{ + ins->declaration.thread_group_size.x = *tokens++; + ins->declaration.thread_group_size.y = *tokens++; + ins->declaration.thread_group_size.z = *tokens++; +} + +static void shader_sm5_read_dcl_uav_raw(struct wined3d_shader_instruction *ins, + DWORD opcode, DWORD opcode_token, const DWORD *tokens, unsigned int token_count, + struct wined3d_sm4_data *priv) +{ + shader_sm4_read_dst_param(priv, &tokens, &tokens[token_count], WINED3D_DATA_UAV, &ins->declaration.dst); + ins->flags = (opcode_token & WINED3D_SM5_UAV_FLAGS_MASK) >> WINED3D_SM5_UAV_FLAGS_SHIFT; +} + +static void shader_sm5_read_dcl_uav_structured(struct wined3d_shader_instruction *ins, + DWORD opcode, DWORD opcode_token, const DWORD *tokens, unsigned int token_count, + struct wined3d_sm4_data *priv) +{ + shader_sm4_read_dst_param(priv, &tokens, &tokens[token_count], WINED3D_DATA_UAV, + &ins->declaration.structured_resource.reg); + ins->flags = (opcode_token & WINED3D_SM5_UAV_FLAGS_MASK) >> WINED3D_SM5_UAV_FLAGS_SHIFT; + ins->declaration.structured_resource.byte_stride = *tokens; + if (ins->declaration.structured_resource.byte_stride % 4) + FIXME("Byte stride %u is not multiple of 4.\n", ins->declaration.structured_resource.byte_stride); +} + +static void shader_sm5_read_dcl_tgsm_raw(struct wined3d_shader_instruction *ins, + DWORD opcode, DWORD opcode_token, const DWORD *tokens, unsigned int token_count, + struct wined3d_sm4_data *priv) +{ + shader_sm4_read_dst_param(priv, &tokens, &tokens[token_count], WINED3D_DATA_FLOAT, &ins->declaration.tgsm_raw.reg); + ins->declaration.tgsm_raw.byte_count = *tokens; + if (ins->declaration.tgsm_raw.byte_count % 4) + FIXME("Byte count %u is not multiple of 4.\n", ins->declaration.tgsm_raw.byte_count); +} + +static void shader_sm5_read_dcl_tgsm_structured(struct wined3d_shader_instruction *ins, + DWORD opcode, DWORD opcode_token, const DWORD *tokens, unsigned int token_count, + struct wined3d_sm4_data *priv) +{ + shader_sm4_read_dst_param(priv, &tokens, &tokens[token_count], WINED3D_DATA_FLOAT, + &ins->declaration.tgsm_structured.reg); + ins->declaration.tgsm_structured.byte_stride = *tokens++; + ins->declaration.tgsm_structured.structure_count = *tokens; + if (ins->declaration.tgsm_structured.byte_stride % 4) + FIXME("Byte stride %u is not multiple of 4.\n", ins->declaration.tgsm_structured.byte_stride); +} + +static void shader_sm5_read_dcl_resource_structured(struct wined3d_shader_instruction *ins, + DWORD opcode, DWORD opcode_token, const DWORD *tokens, unsigned int token_count, + struct wined3d_sm4_data *priv) +{ + shader_sm4_read_dst_param(priv, &tokens, &tokens[token_count], WINED3D_DATA_RESOURCE, + &ins->declaration.structured_resource.reg); + ins->declaration.structured_resource.byte_stride = *tokens; + if (ins->declaration.structured_resource.byte_stride % 4) + FIXME("Byte stride %u is not multiple of 4.\n", ins->declaration.structured_resource.byte_stride); +} + +static void shader_sm5_read_dcl_resource_raw(struct wined3d_shader_instruction *ins, + DWORD opcode, DWORD opcode_token, const DWORD *tokens, unsigned int token_count, + struct wined3d_sm4_data *priv) +{ + shader_sm4_read_dst_param(priv, &tokens, &tokens[token_count], WINED3D_DATA_RESOURCE, &ins->declaration.dst); +} + +static void shader_sm5_read_sync(struct wined3d_shader_instruction *ins, + DWORD opcode, DWORD opcode_token, const DWORD *tokens, unsigned int token_count, + struct wined3d_sm4_data *priv) +{ + ins->flags = (opcode_token & WINED3D_SM5_SYNC_FLAGS_MASK) >> WINED3D_SM5_SYNC_FLAGS_SHIFT; +} + +/* + * f -> WINED3D_DATA_FLOAT + * i -> WINED3D_DATA_INT + * u -> WINED3D_DATA_UINT + * O -> WINED3D_DATA_OPAQUE + * R -> WINED3D_DATA_RESOURCE + * S -> WINED3D_DATA_SAMPLER + * U -> WINED3D_DATA_UAV + */ +static const struct wined3d_sm4_opcode_info opcode_table[] = +{ + {WINED3D_SM4_OP_ADD, WINED3DSIH_ADD, "f", "ff"}, + {WINED3D_SM4_OP_AND, WINED3DSIH_AND, "u", "uu"}, + {WINED3D_SM4_OP_BREAK, WINED3DSIH_BREAK, "", ""}, + {WINED3D_SM4_OP_BREAKC, WINED3DSIH_BREAKP, "", "u", + shader_sm4_read_conditional_op}, + {WINED3D_SM4_OP_CASE, WINED3DSIH_CASE, "", "u"}, + {WINED3D_SM4_OP_CONTINUE, WINED3DSIH_CONTINUE, "", ""}, + {WINED3D_SM4_OP_CONTINUEC, WINED3DSIH_CONTINUEP, "", "u", + shader_sm4_read_conditional_op}, + {WINED3D_SM4_OP_CUT, WINED3DSIH_CUT, "", ""}, + {WINED3D_SM4_OP_DEFAULT, WINED3DSIH_DEFAULT, "", ""}, + {WINED3D_SM4_OP_DERIV_RTX, WINED3DSIH_DSX, "f", "f"}, + {WINED3D_SM4_OP_DERIV_RTY, WINED3DSIH_DSY, "f", "f"}, + {WINED3D_SM4_OP_DISCARD, WINED3DSIH_TEXKILL, "", "u", + shader_sm4_read_conditional_op}, + {WINED3D_SM4_OP_DIV, WINED3DSIH_DIV, "f", "ff"}, + {WINED3D_SM4_OP_DP2, WINED3DSIH_DP2, "f", "ff"}, + {WINED3D_SM4_OP_DP3, WINED3DSIH_DP3, "f", "ff"}, + {WINED3D_SM4_OP_DP4, WINED3DSIH_DP4, "f", "ff"}, + {WINED3D_SM4_OP_ELSE, WINED3DSIH_ELSE, "", ""}, + {WINED3D_SM4_OP_EMIT, WINED3DSIH_EMIT, "", ""}, + {WINED3D_SM4_OP_ENDIF, WINED3DSIH_ENDIF, "", ""}, + {WINED3D_SM4_OP_ENDLOOP, WINED3DSIH_ENDLOOP, "", ""}, + {WINED3D_SM4_OP_ENDSWITCH, WINED3DSIH_ENDSWITCH, "", ""}, + {WINED3D_SM4_OP_EQ, WINED3DSIH_EQ, "u", "ff"}, + {WINED3D_SM4_OP_EXP, WINED3DSIH_EXP, "f", "f"}, + {WINED3D_SM4_OP_FRC, WINED3DSIH_FRC, "f", "f"}, + {WINED3D_SM4_OP_FTOI, WINED3DSIH_FTOI, "i", "f"}, + {WINED3D_SM4_OP_FTOU, WINED3DSIH_FTOU, "u", "f"}, + {WINED3D_SM4_OP_GE, WINED3DSIH_GE, "u", "ff"}, + {WINED3D_SM4_OP_IADD, WINED3DSIH_IADD, "i", "ii"}, + {WINED3D_SM4_OP_IF, WINED3DSIH_IF, "", "u", + shader_sm4_read_conditional_op}, + {WINED3D_SM4_OP_IEQ, WINED3DSIH_IEQ, "u", "ii"}, + {WINED3D_SM4_OP_IGE, WINED3DSIH_IGE, "u", "ii"}, + {WINED3D_SM4_OP_ILT, WINED3DSIH_ILT, "u", "ii"}, + {WINED3D_SM4_OP_IMAD, WINED3DSIH_IMAD, "i", "iii"}, + {WINED3D_SM4_OP_IMAX, WINED3DSIH_IMAX, "i", "ii"}, + {WINED3D_SM4_OP_IMIN, WINED3DSIH_IMIN, "i", "ii"}, + {WINED3D_SM4_OP_IMUL, WINED3DSIH_IMUL, "ii", "ii"}, + {WINED3D_SM4_OP_INE, WINED3DSIH_INE, "u", "ii"}, + {WINED3D_SM4_OP_INEG, WINED3DSIH_INEG, "i", "i"}, + {WINED3D_SM4_OP_ISHL, WINED3DSIH_ISHL, "i", "ii"}, + {WINED3D_SM4_OP_ISHR, WINED3DSIH_ISHR, "i", "ii"}, + {WINED3D_SM4_OP_ITOF, WINED3DSIH_ITOF, "f", "i"}, + {WINED3D_SM4_OP_LABEL, WINED3DSIH_LABEL, "", "O"}, + {WINED3D_SM4_OP_LD, WINED3DSIH_LD, "u", "iR"}, + {WINED3D_SM4_OP_LD2DMS, WINED3DSIH_LD2DMS, "u", "iRi"}, + {WINED3D_SM4_OP_LOG, WINED3DSIH_LOG, "f", "f"}, + {WINED3D_SM4_OP_LOOP, WINED3DSIH_LOOP, "", ""}, + {WINED3D_SM4_OP_LT, WINED3DSIH_LT, "u", "ff"}, + {WINED3D_SM4_OP_MAD, WINED3DSIH_MAD, "f", "fff"}, + {WINED3D_SM4_OP_MIN, WINED3DSIH_MIN, "f", "ff"}, + {WINED3D_SM4_OP_MAX, WINED3DSIH_MAX, "f", "ff"}, + {WINED3D_SM4_OP_SHADER_DATA, WINED3DSIH_DCL_IMMEDIATE_CONSTANT_BUFFER, "", "", + shader_sm4_read_shader_data}, + {WINED3D_SM4_OP_MOV, WINED3DSIH_MOV, "f", "f"}, + {WINED3D_SM4_OP_MOVC, WINED3DSIH_MOVC, "f", "uff"}, + {WINED3D_SM4_OP_MUL, WINED3DSIH_MUL, "f", "ff"}, + {WINED3D_SM4_OP_NE, WINED3DSIH_NE, "u", "ff"}, + {WINED3D_SM4_OP_NOP, WINED3DSIH_NOP, "", ""}, + {WINED3D_SM4_OP_NOT, WINED3DSIH_NOT, "u", "u"}, + {WINED3D_SM4_OP_OR, WINED3DSIH_OR, "u", "uu"}, + {WINED3D_SM4_OP_RESINFO, WINED3DSIH_RESINFO, "f", "iR"}, + {WINED3D_SM4_OP_RET, WINED3DSIH_RET, "", ""}, + {WINED3D_SM4_OP_RETC, WINED3DSIH_RETP, "", "u", + shader_sm4_read_conditional_op}, + {WINED3D_SM4_OP_ROUND_NE, WINED3DSIH_ROUND_NE, "f", "f"}, + {WINED3D_SM4_OP_ROUND_NI, WINED3DSIH_ROUND_NI, "f", "f"}, + {WINED3D_SM4_OP_ROUND_PI, WINED3DSIH_ROUND_PI, "f", "f"}, + {WINED3D_SM4_OP_ROUND_Z, WINED3DSIH_ROUND_Z, "f", "f"}, + {WINED3D_SM4_OP_RSQ, WINED3DSIH_RSQ, "f", "f"}, + {WINED3D_SM4_OP_SAMPLE, WINED3DSIH_SAMPLE, "u", "fRS"}, + {WINED3D_SM4_OP_SAMPLE_C, WINED3DSIH_SAMPLE_C, "f", "fRSf"}, + {WINED3D_SM4_OP_SAMPLE_C_LZ, WINED3DSIH_SAMPLE_C_LZ, "f", "fRSf"}, + {WINED3D_SM4_OP_SAMPLE_LOD, WINED3DSIH_SAMPLE_LOD, "u", "fRSf"}, + {WINED3D_SM4_OP_SAMPLE_GRAD, WINED3DSIH_SAMPLE_GRAD, "u", "fRSff"}, + {WINED3D_SM4_OP_SAMPLE_B, WINED3DSIH_SAMPLE_B, "u", "fRSf"}, + {WINED3D_SM4_OP_SQRT, WINED3DSIH_SQRT, "f", "f"}, + {WINED3D_SM4_OP_SWITCH, WINED3DSIH_SWITCH, "", "u"}, + {WINED3D_SM4_OP_SINCOS, WINED3DSIH_SINCOS, "ff", "f"}, + {WINED3D_SM4_OP_UDIV, WINED3DSIH_UDIV, "uu", "uu"}, + {WINED3D_SM4_OP_ULT, WINED3DSIH_ULT, "u", "uu"}, + {WINED3D_SM4_OP_UGE, WINED3DSIH_UGE, "u", "uu"}, + {WINED3D_SM4_OP_UMUL, WINED3DSIH_UMUL, "uu", "uu"}, + {WINED3D_SM4_OP_UMAX, WINED3DSIH_UMAX, "u", "uu"}, + {WINED3D_SM4_OP_UMIN, WINED3DSIH_UMIN, "u", "uu"}, + {WINED3D_SM4_OP_USHR, WINED3DSIH_USHR, "u", "uu"}, + {WINED3D_SM4_OP_UTOF, WINED3DSIH_UTOF, "f", "u"}, + {WINED3D_SM4_OP_XOR, WINED3DSIH_XOR, "u", "uu"}, + {WINED3D_SM4_OP_DCL_RESOURCE, WINED3DSIH_DCL, "R", "", + shader_sm4_read_dcl_resource}, + {WINED3D_SM4_OP_DCL_CONSTANT_BUFFER, WINED3DSIH_DCL_CONSTANT_BUFFER, "", "", + shader_sm4_read_dcl_constant_buffer}, + {WINED3D_SM4_OP_DCL_SAMPLER, WINED3DSIH_DCL_SAMPLER, "", "", + shader_sm4_read_dcl_sampler}, + {WINED3D_SM4_OP_DCL_INDEX_RANGE, WINED3DSIH_DCL_INDEX_RANGE, "", "", + shader_sm4_read_dcl_index_range}, + {WINED3D_SM4_OP_DCL_OUTPUT_TOPOLOGY, WINED3DSIH_DCL_OUTPUT_TOPOLOGY, "", "", + shader_sm4_read_dcl_output_topology}, + {WINED3D_SM4_OP_DCL_INPUT_PRIMITIVE, WINED3DSIH_DCL_INPUT_PRIMITIVE, "", "", + shader_sm4_read_dcl_input_primitive}, + {WINED3D_SM4_OP_DCL_VERTICES_OUT, WINED3DSIH_DCL_VERTICES_OUT, "", "", + shader_sm4_read_declaration_count}, + {WINED3D_SM4_OP_DCL_INPUT, WINED3DSIH_DCL_INPUT, "", "", + shader_sm4_read_declaration_dst}, + {WINED3D_SM4_OP_DCL_INPUT_SGV, WINED3DSIH_DCL_INPUT_SGV, "", "", + shader_sm4_read_declaration_register_semantic}, + {WINED3D_SM4_OP_DCL_INPUT_SIV, WINED3DSIH_DCL_INPUT_SIV, "", "", + shader_sm4_read_declaration_register_semantic}, + {WINED3D_SM4_OP_DCL_INPUT_PS, WINED3DSIH_DCL_INPUT_PS, "", "", + shader_sm4_read_dcl_input_ps}, + {WINED3D_SM4_OP_DCL_INPUT_PS_SGV, WINED3DSIH_DCL_INPUT_PS_SGV, "", "", + shader_sm4_read_declaration_register_semantic}, + {WINED3D_SM4_OP_DCL_INPUT_PS_SIV, WINED3DSIH_DCL_INPUT_PS_SIV, "", "", + shader_sm4_read_dcl_input_ps_siv}, + {WINED3D_SM4_OP_DCL_OUTPUT, WINED3DSIH_DCL_OUTPUT, "", "", + shader_sm4_read_declaration_dst}, + {WINED3D_SM4_OP_DCL_OUTPUT_SIV, WINED3DSIH_DCL_OUTPUT_SIV, "", "", + shader_sm4_read_declaration_register_semantic}, + {WINED3D_SM4_OP_DCL_TEMPS, WINED3DSIH_DCL_TEMPS, "", "", + shader_sm4_read_declaration_count}, + {WINED3D_SM4_OP_DCL_INDEXABLE_TEMP, WINED3DSIH_DCL_INDEXABLE_TEMP, "", "", + shader_sm4_read_dcl_indexable_temp}, + {WINED3D_SM4_OP_DCL_GLOBAL_FLAGS, WINED3DSIH_DCL_GLOBAL_FLAGS, "", "", + shader_sm4_read_dcl_global_flags}, + {WINED3D_SM4_OP_LOD, WINED3DSIH_LOD, "f", "fRS"}, + {WINED3D_SM4_OP_GATHER4, WINED3DSIH_GATHER4, "u", "fRS"}, + {WINED3D_SM4_OP_SAMPLE_POS, WINED3DSIH_SAMPLE_POS, "f", "Ru"}, + {WINED3D_SM4_OP_SAMPLE_INFO, WINED3DSIH_SAMPLE_INFO, "f", "R"}, + {WINED3D_SM5_OP_HS_DECLS, WINED3DSIH_HS_DECLS, "", ""}, + {WINED3D_SM5_OP_HS_CONTROL_POINT_PHASE, WINED3DSIH_HS_CONTROL_POINT_PHASE, "", ""}, + {WINED3D_SM5_OP_HS_FORK_PHASE, WINED3DSIH_HS_FORK_PHASE, "", ""}, + {WINED3D_SM5_OP_HS_JOIN_PHASE, WINED3DSIH_HS_JOIN_PHASE, "", ""}, + {WINED3D_SM5_OP_EMIT_STREAM, WINED3DSIH_EMIT_STREAM, "", "f"}, + {WINED3D_SM5_OP_CUT_STREAM, WINED3DSIH_CUT_STREAM, "", "f"}, + {WINED3D_SM5_OP_FCALL, WINED3DSIH_FCALL, "", "O", + shader_sm5_read_fcall}, + {WINED3D_SM5_OP_BUFINFO, WINED3DSIH_BUFINFO, "i", "U"}, + {WINED3D_SM5_OP_DERIV_RTX_COARSE, WINED3DSIH_DSX_COARSE, "f", "f"}, + {WINED3D_SM5_OP_DERIV_RTX_FINE, WINED3DSIH_DSX_FINE, "f", "f"}, + {WINED3D_SM5_OP_DERIV_RTY_COARSE, WINED3DSIH_DSY_COARSE, "f", "f"}, + {WINED3D_SM5_OP_DERIV_RTY_FINE, WINED3DSIH_DSY_FINE, "f", "f"}, + {WINED3D_SM5_OP_GATHER4_C, WINED3DSIH_GATHER4_C, "f", "fRSf"}, + {WINED3D_SM5_OP_GATHER4_PO, WINED3DSIH_GATHER4_PO, "f", "fiRS"}, + {WINED3D_SM5_OP_GATHER4_PO_C, WINED3DSIH_GATHER4_PO_C, "f", "fiRSf"}, + {WINED3D_SM5_OP_RCP, WINED3DSIH_RCP, "f", "f"}, + {WINED3D_SM5_OP_F32TOF16, WINED3DSIH_F32TOF16, "u", "f"}, + {WINED3D_SM5_OP_F16TOF32, WINED3DSIH_F16TOF32, "f", "u"}, + {WINED3D_SM5_OP_COUNTBITS, WINED3DSIH_COUNTBITS, "u", "u"}, + {WINED3D_SM5_OP_FIRSTBIT_HI, WINED3DSIH_FIRSTBIT_HI, "u", "u"}, + {WINED3D_SM5_OP_FIRSTBIT_LO, WINED3DSIH_FIRSTBIT_LO, "u", "u"}, + {WINED3D_SM5_OP_FIRSTBIT_SHI, WINED3DSIH_FIRSTBIT_SHI, "u", "i"}, + {WINED3D_SM5_OP_UBFE, WINED3DSIH_UBFE, "u", "iiu"}, + {WINED3D_SM5_OP_IBFE, WINED3DSIH_IBFE, "i", "iii"}, + {WINED3D_SM5_OP_BFI, WINED3DSIH_BFI, "u", "iiuu"}, + {WINED3D_SM5_OP_BFREV, WINED3DSIH_BFREV, "u", "u"}, + {WINED3D_SM5_OP_SWAPC, WINED3DSIH_SWAPC, "ff", "uff"}, + {WINED3D_SM5_OP_DCL_STREAM, WINED3DSIH_DCL_STREAM, "", "O"}, + {WINED3D_SM5_OP_DCL_FUNCTION_BODY, WINED3DSIH_DCL_FUNCTION_BODY, "", "", + shader_sm5_read_dcl_function_body}, + {WINED3D_SM5_OP_DCL_FUNCTION_TABLE, WINED3DSIH_DCL_FUNCTION_TABLE, "", "", + shader_sm5_read_dcl_function_table}, + {WINED3D_SM5_OP_DCL_INTERFACE, WINED3DSIH_DCL_INTERFACE, "", "", + shader_sm5_read_dcl_interface}, + {WINED3D_SM5_OP_DCL_INPUT_CONTROL_POINT_COUNT, WINED3DSIH_DCL_INPUT_CONTROL_POINT_COUNT, "", "", + shader_sm5_read_control_point_count}, + {WINED3D_SM5_OP_DCL_OUTPUT_CONTROL_POINT_COUNT, WINED3DSIH_DCL_OUTPUT_CONTROL_POINT_COUNT, "", "", + shader_sm5_read_control_point_count}, + {WINED3D_SM5_OP_DCL_TESSELLATOR_DOMAIN, WINED3DSIH_DCL_TESSELLATOR_DOMAIN, "", "", + shader_sm5_read_dcl_tessellator_domain}, + {WINED3D_SM5_OP_DCL_TESSELLATOR_PARTITIONING, WINED3DSIH_DCL_TESSELLATOR_PARTITIONING, "", "", + shader_sm5_read_dcl_tessellator_partitioning}, + {WINED3D_SM5_OP_DCL_TESSELLATOR_OUTPUT_PRIMITIVE, WINED3DSIH_DCL_TESSELLATOR_OUTPUT_PRIMITIVE, "", "", + shader_sm5_read_dcl_tessellator_output_primitive}, + {WINED3D_SM5_OP_DCL_HS_MAX_TESSFACTOR, WINED3DSIH_DCL_HS_MAX_TESSFACTOR, "", "", + shader_sm5_read_dcl_hs_max_tessfactor}, + {WINED3D_SM5_OP_DCL_HS_FORK_PHASE_INSTANCE_COUNT, WINED3DSIH_DCL_HS_FORK_PHASE_INSTANCE_COUNT, "", "", + shader_sm4_read_declaration_count}, + {WINED3D_SM5_OP_DCL_HS_JOIN_PHASE_INSTANCE_COUNT, WINED3DSIH_DCL_HS_JOIN_PHASE_INSTANCE_COUNT, "", "", + shader_sm4_read_declaration_count}, + {WINED3D_SM5_OP_DCL_THREAD_GROUP, WINED3DSIH_DCL_THREAD_GROUP, "", "", + shader_sm5_read_dcl_thread_group}, + {WINED3D_SM5_OP_DCL_UAV_TYPED, WINED3DSIH_DCL_UAV_TYPED, "", "", + shader_sm4_read_dcl_resource}, + {WINED3D_SM5_OP_DCL_UAV_RAW, WINED3DSIH_DCL_UAV_RAW, "", "", + shader_sm5_read_dcl_uav_raw}, + {WINED3D_SM5_OP_DCL_UAV_STRUCTURED, WINED3DSIH_DCL_UAV_STRUCTURED, "", "", + shader_sm5_read_dcl_uav_structured}, + {WINED3D_SM5_OP_DCL_TGSM_RAW, WINED3DSIH_DCL_TGSM_RAW, "", "", + shader_sm5_read_dcl_tgsm_raw}, + {WINED3D_SM5_OP_DCL_TGSM_STRUCTURED, WINED3DSIH_DCL_TGSM_STRUCTURED, "", "", + shader_sm5_read_dcl_tgsm_structured}, + {WINED3D_SM5_OP_DCL_RESOURCE_RAW, WINED3DSIH_DCL_RESOURCE_RAW, "", "", + shader_sm5_read_dcl_resource_raw}, + {WINED3D_SM5_OP_DCL_RESOURCE_STRUCTURED, WINED3DSIH_DCL_RESOURCE_STRUCTURED, "", "", + shader_sm5_read_dcl_resource_structured}, + {WINED3D_SM5_OP_LD_UAV_TYPED, WINED3DSIH_LD_UAV_TYPED, "u", "iU"}, + {WINED3D_SM5_OP_STORE_UAV_TYPED, WINED3DSIH_STORE_UAV_TYPED, "U", "iu"}, + {WINED3D_SM5_OP_LD_RAW, WINED3DSIH_LD_RAW, "u", "iU"}, + {WINED3D_SM5_OP_STORE_RAW, WINED3DSIH_STORE_RAW, "U", "iu"}, + {WINED3D_SM5_OP_LD_STRUCTURED, WINED3DSIH_LD_STRUCTURED, "u", "iiR"}, + {WINED3D_SM5_OP_STORE_STRUCTURED, WINED3DSIH_STORE_STRUCTURED, "U", "iiu"}, + {WINED3D_SM5_OP_ATOMIC_AND, WINED3DSIH_ATOMIC_AND, "U", "iu"}, + {WINED3D_SM5_OP_ATOMIC_OR, WINED3DSIH_ATOMIC_OR, "U", "iu"}, + {WINED3D_SM5_OP_ATOMIC_XOR, WINED3DSIH_ATOMIC_XOR, "U", "iu"}, + {WINED3D_SM5_OP_ATOMIC_CMP_STORE, WINED3DSIH_ATOMIC_CMP_STORE, "U", "iuu"}, + {WINED3D_SM5_OP_ATOMIC_IADD, WINED3DSIH_ATOMIC_IADD, "U", "ii"}, + {WINED3D_SM5_OP_ATOMIC_IMAX, WINED3DSIH_ATOMIC_IMAX, "U", "ii"}, + {WINED3D_SM5_OP_ATOMIC_IMIN, WINED3DSIH_ATOMIC_IMIN, "U", "ii"}, + {WINED3D_SM5_OP_ATOMIC_UMAX, WINED3DSIH_ATOMIC_UMAX, "U", "iu"}, + {WINED3D_SM5_OP_ATOMIC_UMIN, WINED3DSIH_ATOMIC_UMIN, "U", "iu"}, + {WINED3D_SM5_OP_IMM_ATOMIC_ALLOC, WINED3DSIH_IMM_ATOMIC_ALLOC, "u", "U"}, + {WINED3D_SM5_OP_IMM_ATOMIC_CONSUME, WINED3DSIH_IMM_ATOMIC_CONSUME, "u", "U"}, + {WINED3D_SM5_OP_IMM_ATOMIC_IADD, WINED3DSIH_IMM_ATOMIC_IADD, "uU", "ii"}, + {WINED3D_SM5_OP_IMM_ATOMIC_AND, WINED3DSIH_IMM_ATOMIC_AND, "uU", "iu"}, + {WINED3D_SM5_OP_IMM_ATOMIC_OR, WINED3DSIH_IMM_ATOMIC_OR, "uU", "iu"}, + {WINED3D_SM5_OP_IMM_ATOMIC_XOR, WINED3DSIH_IMM_ATOMIC_XOR, "uU", "iu"}, + {WINED3D_SM5_OP_IMM_ATOMIC_EXCH, WINED3DSIH_IMM_ATOMIC_EXCH, "uU", "iu"}, + {WINED3D_SM5_OP_IMM_ATOMIC_CMP_EXCH, WINED3DSIH_IMM_ATOMIC_CMP_EXCH, "uU", "iuu"}, + {WINED3D_SM5_OP_IMM_ATOMIC_IMAX, WINED3DSIH_IMM_ATOMIC_IMAX, "iU", "ii"}, + {WINED3D_SM5_OP_IMM_ATOMIC_IMIN, WINED3DSIH_IMM_ATOMIC_IMIN, "iU", "ii"}, + {WINED3D_SM5_OP_IMM_ATOMIC_UMAX, WINED3DSIH_IMM_ATOMIC_UMAX, "uU", "iu"}, + {WINED3D_SM5_OP_IMM_ATOMIC_UMIN, WINED3DSIH_IMM_ATOMIC_UMIN, "uU", "iu"}, + {WINED3D_SM5_OP_SYNC, WINED3DSIH_SYNC, "", "", + shader_sm5_read_sync}, + {WINED3D_SM5_OP_EVAL_SAMPLE_INDEX, WINED3DSIH_EVAL_SAMPLE_INDEX, "f", "fi"}, + {WINED3D_SM5_OP_DCL_GS_INSTANCES, WINED3DSIH_DCL_GS_INSTANCES, "", "", + shader_sm4_read_declaration_count}, +}; + +static const enum wined3d_shader_register_type register_type_table[] = +{ + /* WINED3D_SM4_RT_TEMP */ WINED3DSPR_TEMP, + /* WINED3D_SM4_RT_INPUT */ WINED3DSPR_INPUT, + /* WINED3D_SM4_RT_OUTPUT */ WINED3DSPR_OUTPUT, + /* WINED3D_SM4_RT_INDEXABLE_TEMP */ WINED3DSPR_IDXTEMP, + /* WINED3D_SM4_RT_IMMCONST */ WINED3DSPR_IMMCONST, + /* UNKNOWN */ ~0u, + /* WINED3D_SM4_RT_SAMPLER */ WINED3DSPR_SAMPLER, + /* WINED3D_SM4_RT_RESOURCE */ WINED3DSPR_RESOURCE, + /* WINED3D_SM4_RT_CONSTBUFFER */ WINED3DSPR_CONSTBUFFER, + /* WINED3D_SM4_RT_IMMCONSTBUFFER */ WINED3DSPR_IMMCONSTBUFFER, + /* UNKNOWN */ ~0u, + /* WINED3D_SM4_RT_PRIMID */ WINED3DSPR_PRIMID, + /* WINED3D_SM4_RT_DEPTHOUT */ WINED3DSPR_DEPTHOUT, + /* WINED3D_SM4_RT_NULL */ WINED3DSPR_NULL, + /* WINED3D_SM4_RT_RASTERIZER */ WINED3DSPR_RASTERIZER, + /* WINED3D_SM4_RT_OMASK */ WINED3DSPR_SAMPLEMASK, + /* WINED3D_SM5_RT_STREAM */ WINED3DSPR_STREAM, + /* WINED3D_SM5_RT_FUNCTION_BODY */ WINED3DSPR_FUNCTIONBODY, + /* UNKNOWN */ ~0u, + /* WINED3D_SM5_RT_FUNCTION_POINTER */ WINED3DSPR_FUNCTIONPOINTER, + /* UNKNOWN */ ~0u, + /* UNKNOWN */ ~0u, + /* WINED3D_SM5_RT_OUTPUT_CONTROL_POINT_ID */ WINED3DSPR_OUTPOINTID, + /* WINED3D_SM5_RT_FORK_INSTANCE_ID */ WINED3DSPR_FORKINSTID, + /* WINED3D_SM5_RT_JOIN_INSTANCE_ID */ WINED3DSPR_JOININSTID, + /* WINED3D_SM5_RT_INPUT_CONTROL_POINT */ WINED3DSPR_INCONTROLPOINT, + /* WINED3D_SM5_RT_OUTPUT_CONTROL_POINT */ WINED3DSPR_OUTCONTROLPOINT, + /* WINED3D_SM5_RT_PATCH_CONSTANT_DATA */ WINED3DSPR_PATCHCONST, + /* WINED3D_SM5_RT_DOMAIN_LOCATION */ WINED3DSPR_TESSCOORD, + /* UNKNOWN */ ~0u, + /* WINED3D_SM5_RT_UAV */ WINED3DSPR_UAV, + /* WINED3D_SM5_RT_SHARED_MEMORY */ WINED3DSPR_GROUPSHAREDMEM, + /* WINED3D_SM5_RT_THREAD_ID */ WINED3DSPR_THREADID, + /* WINED3D_SM5_RT_THREAD_GROUP_ID */ WINED3DSPR_THREADGROUPID, + /* WINED3D_SM5_RT_LOCAL_THREAD_ID */ WINED3DSPR_LOCALTHREADID, + /* WINED3D_SM5_RT_COVERAGE */ WINED3DSPR_COVERAGE, + /* WINED3D_SM5_RT_LOCAL_THREAD_INDEX */ WINED3DSPR_LOCALTHREADINDEX, + /* WINED3D_SM5_RT_GS_INSTANCE_ID */ WINED3DSPR_GSINSTID, + /* WINED3D_SM5_RT_DEPTHOUT_GREATER_EQUAL */ WINED3DSPR_DEPTHOUTGE, + /* WINED3D_SM5_RT_DEPTHOUT_LESS_EQUAL */ WINED3DSPR_DEPTHOUTLE, +}; + +static const struct wined3d_sm4_opcode_info *get_opcode_info(enum wined3d_sm4_opcode opcode) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(opcode_table); ++i) + { + if (opcode == opcode_table[i].opcode) return &opcode_table[i]; + } + + return NULL; +} + +static void map_register(const struct wined3d_sm4_data *priv, struct wined3d_shader_register *reg) +{ + switch (priv->shader_version.type) + { + case WINED3D_SHADER_TYPE_PIXEL: + if (reg->type == WINED3DSPR_OUTPUT) + { + unsigned int reg_idx = reg->idx[0].offset; + + if (reg_idx >= ARRAY_SIZE(priv->output_map)) + { + ERR("Invalid output index %u.\n", reg_idx); + break; + } + + reg->type = WINED3DSPR_COLOROUT; + reg->idx[0].offset = priv->output_map[reg_idx]; + } + break; + + default: + break; + } +} + +static enum wined3d_data_type map_data_type(char t) +{ + switch (t) + { + case 'f': + return WINED3D_DATA_FLOAT; + case 'i': + return WINED3D_DATA_INT; + case 'u': + return WINED3D_DATA_UINT; + case 'O': + return WINED3D_DATA_OPAQUE; + case 'R': + return WINED3D_DATA_RESOURCE; + case 'S': + return WINED3D_DATA_SAMPLER; + case 'U': + return WINED3D_DATA_UAV; + default: + ERR("Invalid data type '%c'.\n", t); + return WINED3D_DATA_FLOAT; + } +} + +static enum wined3d_shader_type wined3d_get_sm4_shader_type(const DWORD *byte_code, size_t byte_code_size) +{ + DWORD shader_type; + + if (byte_code_size / sizeof(*byte_code) < 1) + { + WARN("Invalid byte code size %lu.\n", (long)byte_code_size); + return WINED3D_SHADER_TYPE_INVALID; + } + + shader_type = byte_code[0] >> 16; + switch (shader_type) + { + case WINED3D_SM4_PS: + return WINED3D_SHADER_TYPE_PIXEL; + break; + case WINED3D_SM4_VS: + return WINED3D_SHADER_TYPE_VERTEX; + break; + case WINED3D_SM4_GS: + return WINED3D_SHADER_TYPE_GEOMETRY; + break; + case WINED3D_SM5_HS: + return WINED3D_SHADER_TYPE_HULL; + break; + case WINED3D_SM5_DS: + return WINED3D_SHADER_TYPE_DOMAIN; + break; + case WINED3D_SM5_CS: + return WINED3D_SHADER_TYPE_COMPUTE; + break; + default: + FIXME("Unrecognised shader type %#x.\n", shader_type); + return WINED3D_SHADER_TYPE_INVALID; + } +} + +static void *shader_sm4_init(const DWORD *byte_code, size_t byte_code_size, + const struct wined3d_shader_signature *output_signature) +{ + DWORD version_token, token_count; + struct wined3d_sm4_data *priv; + unsigned int i; + + if (byte_code_size / sizeof(*byte_code) < 2) + { + WARN("Invalid byte code size %lu.\n", (long)byte_code_size); + return NULL; + } + + version_token = byte_code[0]; + TRACE("Version: 0x%08x.\n", version_token); + token_count = byte_code[1]; + TRACE("Token count: %u.\n", token_count); + + if (token_count < 2 || byte_code_size / sizeof(*byte_code) < token_count) + { + WARN("Invalid token count %u.\n", token_count); + return NULL; + } + + if (!(priv = heap_alloc(sizeof(*priv)))) + { + ERR("Failed to allocate private data\n"); + return NULL; + } + + priv->start = &byte_code[2]; + priv->end = &byte_code[token_count]; + + priv->shader_version.type = wined3d_get_sm4_shader_type(byte_code, byte_code_size); + if (priv->shader_version.type == WINED3D_SHADER_TYPE_INVALID) + { + heap_free(priv); + return NULL; + } + + priv->shader_version.major = WINED3D_SM4_VERSION_MAJOR(version_token); + priv->shader_version.minor = WINED3D_SM4_VERSION_MINOR(version_token); + + memset(priv->output_map, 0xff, sizeof(priv->output_map)); + for (i = 0; i < output_signature->element_count; ++i) + { + struct wined3d_shader_signature_element *e = &output_signature->elements[i]; + + if (e->register_idx >= ARRAY_SIZE(priv->output_map)) + { + WARN("Invalid output index %u.\n", e->register_idx); + continue; + } + + priv->output_map[e->register_idx] = e->semantic_idx; + } + + list_init(&priv->src_free); + list_init(&priv->src); + + return priv; +} + +static void shader_sm4_free(void *data) +{ + struct wined3d_shader_src_param_entry *e1, *e2; + struct wined3d_sm4_data *priv = data; + + list_move_head(&priv->src_free, &priv->src); + LIST_FOR_EACH_ENTRY_SAFE(e1, e2, &priv->src_free, struct wined3d_shader_src_param_entry, entry) + { + heap_free(e1); + } + heap_free(priv); +} + +static struct wined3d_shader_src_param *get_src_param(struct wined3d_sm4_data *priv) +{ + struct wined3d_shader_src_param_entry *e; + struct list *elem; + + if (!list_empty(&priv->src_free)) + { + elem = list_head(&priv->src_free); + list_remove(elem); + } + else + { + if (!(e = heap_alloc(sizeof(*e)))) + return NULL; + elem = &e->entry; + } + + list_add_tail(&priv->src, elem); + e = LIST_ENTRY(elem, struct wined3d_shader_src_param_entry, entry); + return &e->param; +} + +static void shader_sm4_read_header(void *data, const DWORD **ptr, struct wined3d_shader_version *shader_version) +{ + struct wined3d_sm4_data *priv = data; + + *ptr = priv->start; + *shader_version = priv->shader_version; +} + +static BOOL shader_sm4_read_reg_idx(struct wined3d_sm4_data *priv, const DWORD **ptr, const DWORD *end, + DWORD addressing, struct wined3d_shader_register_index *reg_idx) +{ + if (addressing & WINED3D_SM4_ADDRESSING_RELATIVE) + { + struct wined3d_shader_src_param *rel_addr = get_src_param(priv); + + if (!(reg_idx->rel_addr = rel_addr)) + { + ERR("Failed to get src param for relative addressing.\n"); + return FALSE; + } + + if (addressing & WINED3D_SM4_ADDRESSING_OFFSET) + reg_idx->offset = *(*ptr)++; + else + reg_idx->offset = 0; + shader_sm4_read_src_param(priv, ptr, end, WINED3D_DATA_INT, rel_addr); + } + else + { + reg_idx->rel_addr = NULL; + reg_idx->offset = *(*ptr)++; + } + + return TRUE; +} + +static BOOL shader_sm4_read_param(struct wined3d_sm4_data *priv, const DWORD **ptr, const DWORD *end, + enum wined3d_data_type data_type, struct wined3d_shader_register *param, + enum wined3d_shader_src_modifier *modifier) +{ + enum wined3d_sm4_register_type register_type; + DWORD token, order; + + if (*ptr >= end) + { + WARN("Invalid ptr %p >= end %p.\n", *ptr, end); + return FALSE; + } + token = *(*ptr)++; + + register_type = (token & WINED3D_SM4_REGISTER_TYPE_MASK) >> WINED3D_SM4_REGISTER_TYPE_SHIFT; + if (register_type >= ARRAY_SIZE(register_type_table) + || register_type_table[register_type] == ~0u) + { + FIXME("Unhandled register type %#x.\n", register_type); + param->type = WINED3DSPR_TEMP; + } + else + { + param->type = register_type_table[register_type]; + } + param->data_type = data_type; + + if (token & WINED3D_SM4_REGISTER_MODIFIER) + { + DWORD m; + + if (*ptr >= end) + { + WARN("Invalid ptr %p >= end %p.\n", *ptr, end); + return FALSE; + } + m = *(*ptr)++; + + switch (m) + { + case 0x41: + *modifier = WINED3DSPSM_NEG; + break; + + case 0x81: + *modifier = WINED3DSPSM_ABS; + break; + + case 0xc1: + *modifier = WINED3DSPSM_ABSNEG; + break; + + default: + FIXME("Skipping modifier 0x%08x.\n", m); + *modifier = WINED3DSPSM_NONE; + break; + } + } + else + { + *modifier = WINED3DSPSM_NONE; + } + + order = (token & WINED3D_SM4_REGISTER_ORDER_MASK) >> WINED3D_SM4_REGISTER_ORDER_SHIFT; + + if (order < 1) + param->idx[0].offset = ~0U; + else + { + DWORD addressing = (token & WINED3D_SM4_ADDRESSING_MASK0) >> WINED3D_SM4_ADDRESSING_SHIFT0; + if (!(shader_sm4_read_reg_idx(priv, ptr, end, addressing, ¶m->idx[0]))) + { + ERR("Failed to read register index.\n"); + return FALSE; + } + } + + if (order < 2) + param->idx[1].offset = ~0U; + else + { + DWORD addressing = (token & WINED3D_SM4_ADDRESSING_MASK1) >> WINED3D_SM4_ADDRESSING_SHIFT1; + if (!(shader_sm4_read_reg_idx(priv, ptr, end, addressing, ¶m->idx[1]))) + { + ERR("Failed to read register index.\n"); + return FALSE; + } + } + + if (order > 2) + FIXME("Unhandled order %u.\n", order); + + if (register_type == WINED3D_SM4_RT_IMMCONST) + { + enum wined3d_sm4_dimension dimension = (token & WINED3D_SM4_DIMENSION_MASK) >> WINED3D_SM4_DIMENSION_SHIFT; + + switch (dimension) + { + case WINED3D_SM4_DIMENSION_SCALAR: + param->immconst_type = WINED3D_IMMCONST_SCALAR; + if (end - *ptr < 1) + { + WARN("Invalid ptr %p, end %p.\n", *ptr, end); + return FALSE; + } + memcpy(param->u.immconst_data, *ptr, 1 * sizeof(DWORD)); + *ptr += 1; + break; + + case WINED3D_SM4_DIMENSION_VEC4: + param->immconst_type = WINED3D_IMMCONST_VEC4; + if (end - *ptr < 4) + { + WARN("Invalid ptr %p, end %p.\n", *ptr, end); + return FALSE; + } + memcpy(param->u.immconst_data, *ptr, 4 * sizeof(DWORD)); + *ptr += 4; + break; + + default: + FIXME("Unhandled dimension %#x.\n", dimension); + break; + } + } + + map_register(priv, param); + + return TRUE; +} + +static BOOL shader_sm4_read_src_param(struct wined3d_sm4_data *priv, const DWORD **ptr, const DWORD *end, + enum wined3d_data_type data_type, struct wined3d_shader_src_param *src_param) +{ + DWORD token; + + if (*ptr >= end) + { + WARN("Invalid ptr %p >= end %p.\n", *ptr, end); + return FALSE; + } + token = **ptr; + + if (!shader_sm4_read_param(priv, ptr, end, data_type, &src_param->reg, &src_param->modifiers)) + { + ERR("Failed to read parameter.\n"); + return FALSE; + } + + if (src_param->reg.type == WINED3DSPR_IMMCONST) + { + src_param->swizzle = WINED3DSP_NOSWIZZLE; + } + else + { + enum wined3d_sm4_swizzle_type swizzle_type = + (token & WINED3D_SM4_SWIZZLE_TYPE_MASK) >> WINED3D_SM4_SWIZZLE_TYPE_SHIFT; + + switch (swizzle_type) + { + case WINED3D_SM4_SWIZZLE_NONE: + src_param->swizzle = WINED3DSP_NOSWIZZLE; + break; + + case WINED3D_SM4_SWIZZLE_SCALAR: + src_param->swizzle = (token & WINED3D_SM4_SWIZZLE_MASK) >> WINED3D_SM4_SWIZZLE_SHIFT; + src_param->swizzle = (src_param->swizzle & 0x3) * 0x55; + break; + + case WINED3D_SM4_SWIZZLE_VEC4: + src_param->swizzle = (token & WINED3D_SM4_SWIZZLE_MASK) >> WINED3D_SM4_SWIZZLE_SHIFT; + break; + + default: + FIXME("Unhandled swizzle type %#x.\n", swizzle_type); + break; + } + } + + return TRUE; +} + +static BOOL shader_sm4_read_dst_param(struct wined3d_sm4_data *priv, const DWORD **ptr, const DWORD *end, + enum wined3d_data_type data_type, struct wined3d_shader_dst_param *dst_param) +{ + enum wined3d_shader_src_modifier modifier; + DWORD token; + + if (*ptr >= end) + { + WARN("Invalid ptr %p >= end %p.\n", *ptr, end); + return FALSE; + } + token = **ptr; + + if (!shader_sm4_read_param(priv, ptr, end, data_type, &dst_param->reg, &modifier)) + { + ERR("Failed to read parameter.\n"); + return FALSE; + } + + if (modifier != WINED3DSPSM_NONE) + { + ERR("Invalid source modifier %#x on destination register.\n", modifier); + return FALSE; + } + + dst_param->write_mask = (token & WINED3D_SM4_WRITEMASK_MASK) >> WINED3D_SM4_WRITEMASK_SHIFT; + dst_param->modifiers = 0; + dst_param->shift = 0; + + return TRUE; +} + +static void shader_sm4_read_instruction_modifier(DWORD modifier, struct wined3d_shader_instruction *ins) +{ + enum wined3d_sm4_instruction_modifier modifier_type = modifier & WINED3D_SM4_MODIFIER_MASK; + + switch (modifier_type) + { + case WINED3D_SM4_MODIFIER_AOFFIMMI: + { + static const DWORD recognized_bits = WINED3D_SM4_INSTRUCTION_MODIFIER + | WINED3D_SM4_MODIFIER_MASK + | WINED3D_SM4_AOFFIMMI_U_MASK + | WINED3D_SM4_AOFFIMMI_V_MASK + | WINED3D_SM4_AOFFIMMI_W_MASK; + + /* Bit fields are used for sign extension. */ + struct + { + int u : 4; + int v : 4; + int w : 4; + } aoffimmi; + + if (modifier & ~recognized_bits) + FIXME("Unhandled instruction modifier %#x.\n", modifier); + + aoffimmi.u = (modifier & WINED3D_SM4_AOFFIMMI_U_MASK) >> WINED3D_SM4_AOFFIMMI_U_SHIFT; + aoffimmi.v = (modifier & WINED3D_SM4_AOFFIMMI_V_MASK) >> WINED3D_SM4_AOFFIMMI_V_SHIFT; + aoffimmi.w = (modifier & WINED3D_SM4_AOFFIMMI_W_MASK) >> WINED3D_SM4_AOFFIMMI_W_SHIFT; + ins->texel_offset.u = aoffimmi.u; + ins->texel_offset.v = aoffimmi.v; + ins->texel_offset.w = aoffimmi.w; + break; + } + + case WINED3D_SM5_MODIFIER_DATA_TYPE: + { + DWORD components = (modifier & WINED3D_SM5_MODIFIER_DATA_TYPE_MASK) >> WINED3D_SM5_MODIFIER_DATA_TYPE_SHIFT; + enum wined3d_sm4_data_type data_type = components & 0xf; + + if ((components & 0xfff0) != (components & 0xf) * 0x1110) + FIXME("Components (%#x) have different data types.\n", components); + ins->resource_data_type = data_type_table[data_type]; + break; + } + + case WINED3D_SM5_MODIFIER_RESOURCE_TYPE: + { + enum wined3d_sm4_resource_type resource_type + = (modifier & WINED3D_SM5_MODIFIER_RESOURCE_TYPE_MASK) >> WINED3D_SM5_MODIFIER_RESOURCE_TYPE_SHIFT; + + ins->resource_type = resource_type_table[resource_type]; + break; + } + + default: + FIXME("Unhandled instruction modifier %#x.\n", modifier); + } +} + +static void shader_sm4_read_instruction(void *data, const DWORD **ptr, struct wined3d_shader_instruction *ins) +{ + const struct wined3d_sm4_opcode_info *opcode_info; + DWORD opcode_token, opcode, previous_token; + struct wined3d_sm4_data *priv = data; + unsigned int i, len; + SIZE_T remaining; + const DWORD *p; + DWORD precise; + + list_move_head(&priv->src_free, &priv->src); + + if (*ptr >= priv->end) + { + WARN("End of byte-code, failed to read opcode.\n"); + goto fail; + } + remaining = priv->end - *ptr; + + opcode_token = *(*ptr)++; + opcode = opcode_token & WINED3D_SM4_OPCODE_MASK; + + len = ((opcode_token & WINED3D_SM4_INSTRUCTION_LENGTH_MASK) >> WINED3D_SM4_INSTRUCTION_LENGTH_SHIFT); + if (!len) + { + if (remaining < 2) + { + WARN("End of byte-code, failed to read length token.\n"); + goto fail; + } + len = **ptr; + } + if (!len || remaining < len) + { + WARN("Read invalid length %u (remaining %lu).\n", len, remaining); + goto fail; + } + --len; + + if (TRACE_ON(d3d_bytecode)) + { + TRACE_(d3d_bytecode)("[ %08x ", opcode_token); + for (i = 0; i < len; ++i) + { + TRACE_(d3d_bytecode)("%08x ", (*ptr)[i]); + } + TRACE_(d3d_bytecode)("]\n"); + } + + if (!(opcode_info = get_opcode_info(opcode))) + { + FIXME("Unrecognized opcode %#x, opcode_token 0x%08x.\n", opcode, opcode_token); + ins->handler_idx = WINED3DSIH_TABLE_SIZE; + *ptr += len; + return; + } + + ins->handler_idx = opcode_info->handler_idx; + ins->flags = 0; + ins->coissue = 0; + ins->predicate = NULL; + ins->dst_count = strlen(opcode_info->dst_info); + ins->dst = priv->dst_param; + ins->src_count = strlen(opcode_info->src_info); + ins->src = priv->src_param; + ins->resource_type = WINED3D_SHADER_RESOURCE_NONE; + ins->resource_data_type = WINED3D_DATA_FLOAT; + memset(&ins->texel_offset, 0, sizeof(ins->texel_offset)); + + p = *ptr; + *ptr += len; + + if (opcode_info->read_opcode_func) + { + opcode_info->read_opcode_func(ins, opcode, opcode_token, p, len, priv); + } + else + { + enum wined3d_shader_dst_modifier instruction_dst_modifier = WINED3DSPDM_NONE; + + previous_token = opcode_token; + while (previous_token & WINED3D_SM4_INSTRUCTION_MODIFIER && p != *ptr) + shader_sm4_read_instruction_modifier(previous_token = *p++, ins); + + ins->flags = (opcode_token & WINED3D_SM4_INSTRUCTION_FLAGS_MASK) >> WINED3D_SM4_INSTRUCTION_FLAGS_SHIFT; + if (ins->flags & WINED3D_SM4_INSTRUCTION_FLAG_SATURATE) + { + ins->flags &= ~WINED3D_SM4_INSTRUCTION_FLAG_SATURATE; + instruction_dst_modifier = WINED3DSPDM_SATURATE; + } + precise = (opcode_token & WINED3D_SM5_PRECISE_MASK) >> WINED3D_SM5_PRECISE_SHIFT; + ins->flags |= precise << WINED3DSI_PRECISE_SHIFT; + + for (i = 0; i < ins->dst_count; ++i) + { + if (!(shader_sm4_read_dst_param(priv, &p, *ptr, map_data_type(opcode_info->dst_info[i]), + &priv->dst_param[i]))) + { + ins->handler_idx = WINED3DSIH_TABLE_SIZE; + return; + } + priv->dst_param[i].modifiers |= instruction_dst_modifier; + } + + for (i = 0; i < ins->src_count; ++i) + { + if (!(shader_sm4_read_src_param(priv, &p, *ptr, map_data_type(opcode_info->src_info[i]), + &priv->src_param[i]))) + { + ins->handler_idx = WINED3DSIH_TABLE_SIZE; + return; + } + } + } + + return; + +fail: + *ptr = priv->end; + ins->handler_idx = WINED3DSIH_TABLE_SIZE; + return; +} + +static BOOL shader_sm4_is_end(void *data, const DWORD **ptr) +{ + struct wined3d_sm4_data *priv = data; + return *ptr == priv->end; +} + +const struct wined3d_shader_frontend sm4_shader_frontend = +{ + shader_sm4_init, + shader_sm4_free, + shader_sm4_read_header, + shader_sm4_read_instruction, + shader_sm4_is_end, +}; + +#define TAG_AON9 WINEMAKEFOURCC('A', 'o', 'n', '9') +#define TAG_DXBC WINEMAKEFOURCC('D', 'X', 'B', 'C') +#define TAG_ISGN WINEMAKEFOURCC('I', 'S', 'G', 'N') +#define TAG_OSG5 WINEMAKEFOURCC('O', 'S', 'G', '5') +#define TAG_OSGN WINEMAKEFOURCC('O', 'S', 'G', 'N') +#define TAG_PCSG WINEMAKEFOURCC('P', 'C', 'S', 'G') +#define TAG_SHDR WINEMAKEFOURCC('S', 'H', 'D', 'R') +#define TAG_SHEX WINEMAKEFOURCC('S', 'H', 'E', 'X') + +struct aon9_header +{ + DWORD chunk_size; + DWORD shader_version; + DWORD unknown; + DWORD byte_code_offset; +}; + +struct shader_handler_context +{ + struct wined3d_shader *shader; + enum wined3d_shader_byte_code_format *format; + unsigned int max_version; +}; + +static void read_dword(const char **ptr, DWORD *d) +{ + memcpy(d, *ptr, sizeof(*d)); + *ptr += sizeof(*d); +} + +static BOOL require_space(size_t offset, size_t count, size_t size, size_t data_size) +{ + return !count || (data_size - offset) / count >= size; +} + +static void skip_dword_unknown(const char **ptr, unsigned int count) +{ + unsigned int i; + DWORD d; + + WARN("Skipping %u unknown DWORDs:\n", count); + for (i = 0; i < count; ++i) + { + read_dword(ptr, &d); + WARN("\t0x%08x\n", d); + } +} + +static HRESULT parse_dxbc(const char *data, SIZE_T data_size, + HRESULT (*chunk_handler)(const char *data, DWORD data_size, DWORD tag, void *ctx), void *ctx) +{ + const char *ptr = data; + HRESULT hr = S_OK; + DWORD chunk_count; + DWORD total_size; + unsigned int i; + DWORD version; + DWORD tag; + + read_dword(&ptr, &tag); + TRACE("tag: %s.\n", debugstr_an((const char *)&tag, 4)); + + if (tag != TAG_DXBC) + { + WARN("Wrong tag.\n"); + return E_INVALIDARG; + } + + WARN("Ignoring DXBC checksum.\n"); + skip_dword_unknown(&ptr, 4); + + read_dword(&ptr, &version); + TRACE("version: %#x.\n", version); + if (version != 0x00000001) + { + WARN("Got unexpected DXBC version %#x.\n", version); + return E_INVALIDARG; + } + + read_dword(&ptr, &total_size); + TRACE("total size: %#x\n", total_size); + + read_dword(&ptr, &chunk_count); + TRACE("chunk count: %#x\n", chunk_count); + + for (i = 0; i < chunk_count; ++i) + { + DWORD chunk_tag, chunk_size; + const char *chunk_ptr; + DWORD chunk_offset; + + read_dword(&ptr, &chunk_offset); + TRACE("chunk %u at offset %#x\n", i, chunk_offset); + + if (chunk_offset >= data_size || !require_space(chunk_offset, 2, sizeof(DWORD), data_size)) + { + WARN("Invalid chunk offset %#x (data size %#lx).\n", chunk_offset, data_size); + return E_FAIL; + } + + chunk_ptr = data + chunk_offset; + + read_dword(&chunk_ptr, &chunk_tag); + read_dword(&chunk_ptr, &chunk_size); + + if (!require_space(chunk_ptr - data, 1, chunk_size, data_size)) + { + WARN("Invalid chunk size %#x (data size %#lx, chunk offset %#x).\n", + chunk_size, data_size, chunk_offset); + return E_FAIL; + } + + if (FAILED(hr = chunk_handler(chunk_ptr, chunk_size, chunk_tag, ctx))) + break; + } + + return hr; +} + +static const char *shader_get_string(const char *data, size_t data_size, DWORD offset) +{ + if (offset >= data_size) + { + WARN("Invalid offset %#x (data size %#lx).\n", offset, (long)data_size); + return NULL; + } + + if (!memchr( data + offset, 0, data_size - offset )) + return NULL; + + return data + offset; +} + +static HRESULT shader_parse_signature(DWORD tag, const char *data, DWORD data_size, + struct wined3d_shader_signature *s) +{ + struct wined3d_shader_signature_element *e; + const char *ptr = data; + unsigned int i; + DWORD count; + + if (!require_space(0, 2, sizeof(DWORD), data_size)) + { + WARN("Invalid data size %#x.\n", data_size); + return E_INVALIDARG; + } + + read_dword(&ptr, &count); + TRACE("%u elements.\n", count); + + skip_dword_unknown(&ptr, 1); /* It seems to always be 0x00000008. */ + + if (!require_space(ptr - data, count, 6 * sizeof(DWORD), data_size)) + { + WARN("Invalid count %#x (data size %#x).\n", count, data_size); + return E_INVALIDARG; + } + + if (!(e = heap_calloc(count, sizeof(*e)))) + { + ERR("Failed to allocate input signature memory.\n"); + return E_OUTOFMEMORY; + } + + for (i = 0; i < count; ++i) + { + DWORD name_offset; + + if (tag == TAG_OSG5) + read_dword(&ptr, &e[i].stream_idx); + else + e[i].stream_idx = 0; + read_dword(&ptr, &name_offset); + if (!(e[i].semantic_name = shader_get_string(data, data_size, name_offset))) + { + WARN("Invalid name offset %#x (data size %#x).\n", name_offset, data_size); + heap_free(e); + return E_INVALIDARG; + } + read_dword(&ptr, &e[i].semantic_idx); + read_dword(&ptr, &e[i].sysval_semantic); + read_dword(&ptr, &e[i].component_type); + read_dword(&ptr, &e[i].register_idx); + read_dword(&ptr, &e[i].mask); + + TRACE("Stream: %u, semantic: %s, semantic idx: %u, sysval_semantic %#x, " + "type %u, register idx: %u, use_mask %#x, input_mask %#x.\n", + e[i].stream_idx, debugstr_a(e[i].semantic_name), e[i].semantic_idx, e[i].sysval_semantic, + e[i].component_type, e[i].register_idx, (e[i].mask >> 8) & 0xff, e[i].mask & 0xff); + } + + s->elements = e; + s->element_count = count; + + return S_OK; +} + +static HRESULT shader_dxbc_chunk_handler(const char *data, DWORD data_size, DWORD tag, void *context) +{ + struct shader_handler_context *ctx = context; + struct wined3d_shader *shader = ctx->shader; + HRESULT hr; + + switch (tag) + { + case TAG_ISGN: + if (ctx->max_version < 4) + { + TRACE("Skipping shader input signature.\n"); + break; + } + if (shader->input_signature.elements) + { + FIXME("Multiple input signatures.\n"); + break; + } + if (FAILED(hr = shader_parse_signature(tag, data, data_size, &shader->input_signature))) + return hr; + break; + + case TAG_OSGN: + case TAG_OSG5: + if (ctx->max_version < 4) + { + TRACE("Skipping shader output signature.\n"); + break; + } + if (shader->output_signature.elements) + { + FIXME("Multiple output signatures.\n"); + break; + } + if (FAILED(hr = shader_parse_signature(tag, data, data_size, &shader->output_signature))) + return hr; + break; + + case TAG_PCSG: + if (shader->patch_constant_signature.elements) + { + FIXME("Multiple patch constant signatures.\n"); + break; + } + if (FAILED(hr = shader_parse_signature(tag, data, data_size, &shader->patch_constant_signature))) + return hr; + break; + + case TAG_SHDR: + case TAG_SHEX: + if (ctx->max_version < 4) + { + TRACE("Skipping SM4+ shader.\n"); + break; + } + if (shader->function) + FIXME("Multiple shader code chunks.\n"); + shader->function = (const DWORD *)data; + shader->functionLength = data_size; + *ctx->format = WINED3D_SHADER_BYTE_CODE_FORMAT_SM4; + break; + + case TAG_AON9: + if (ctx->max_version < 4) + { + const struct aon9_header *header = (const struct aon9_header *)data; + unsigned int unknown_dword_count; + const char *byte_code; + + if (data_size < sizeof(*header)) + { + WARN("Invalid Aon9 data size %#x.\n", data_size); + return E_FAIL; + } + byte_code = data + header->byte_code_offset; + unknown_dword_count = (header->byte_code_offset - sizeof(*header)) / sizeof(DWORD); + + if (data_size - 2 * sizeof(DWORD) < header->byte_code_offset) + { + WARN("Invalid byte code offset %#x (size %#x).\n", header->byte_code_offset, data_size); + return E_FAIL; + } + FIXME("Skipping %u unknown DWORDs.\n", unknown_dword_count); + + if (shader->function) + FIXME("Multiple shader code chunks.\n"); + shader->function = (const DWORD *)byte_code; + shader->functionLength = data_size - header->byte_code_offset; + *ctx->format = WINED3D_SHADER_BYTE_CODE_FORMAT_SM1; + TRACE("Feature level 9 shader version 0%08x, 0%08x.\n", + header->shader_version, *shader->function); + } + else + { + TRACE("Skipping feature level 9 shader code.\n"); + } + break; + + default: + TRACE("Skipping chunk %s.\n", debugstr_an((const char *)&tag, 4)); + break; + } + + return S_OK; +} + +HRESULT shader_extract_from_dxbc(struct wined3d_shader *shader, + unsigned int max_shader_version, enum wined3d_shader_byte_code_format *format) +{ + struct shader_handler_context ctx; + HRESULT hr; + + ctx.shader = shader; + ctx.format = format; + ctx.max_version = max_shader_version; + + hr = parse_dxbc(shader->byte_code, shader->byte_code_size, shader_dxbc_chunk_handler, &ctx); + if (!shader->function) + hr = E_INVALIDARG; + + if (FAILED(hr)) + WARN("Failed to parse DXBC, hr %#x.\n", hr); + + return hr; +} + +static HRESULT shader_isgn_chunk_handler(const char *data, DWORD data_size, DWORD tag, void *ctx) +{ + struct wined3d_shader_signature *is = ctx; + + if (tag != TAG_ISGN) + return S_OK; + + if (is->elements) + { + FIXME("Multiple shader signatures.\n"); + return S_OK; + } + + return shader_parse_signature(tag, data, data_size, is); +} + +HRESULT CDECL wined3d_extract_shader_input_signature_from_dxbc(struct wined3d_shader_signature *signature, + const void *code, SIZE_T code_size) +{ + memset(signature, 0, sizeof(*signature)); + return parse_dxbc(code, code_size, shader_isgn_chunk_handler, signature); +} diff --git a/wrappers/directx/d3dwine_wrapper/shader_spirv.c b/wrappers/directx/d3dwine_wrapper/shader_spirv.c new file mode 100644 index 00000000000..eb2647db12d --- /dev/null +++ b/wrappers/directx/d3dwine_wrapper/shader_spirv.c @@ -0,0 +1,1333 @@ +/* + * Copyright 2018 Henri Verbeet for CodeWeavers + * Copyright 2019 Józef Kucia for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "config.h" +#include "wine/port.h" + +#include "wined3d_private.h" + +WINE_DECLARE_DEBUG_CHANNEL(winediag); + +#ifdef SONAME_LIBVKD3D_SHADER + +#define VKD3D_SHADER_NO_PROTOTYPES +#include + +WINE_DEFAULT_DEBUG_CHANNEL(d3d_shader); + +static PFN_vkd3d_shader_compile vkd3d_shader_compile; +static PFN_vkd3d_shader_free_messages vkd3d_shader_free_messages; +static PFN_vkd3d_shader_free_scan_descriptor_info vkd3d_shader_free_scan_descriptor_info; +static PFN_vkd3d_shader_free_shader_code vkd3d_shader_free_shader_code; +static PFN_vkd3d_shader_get_version vkd3d_shader_get_version; +static PFN_vkd3d_shader_scan vkd3d_shader_scan; + +static const struct wined3d_shader_backend_ops spirv_shader_backend_vk; + +static void *vkd3d_shader_handle; + +struct shader_spirv_resource_bindings +{ + struct vkd3d_shader_resource_binding *bindings; + SIZE_T bindings_size, binding_count; + + struct vkd3d_shader_uav_counter_binding uav_counters[MAX_UNORDERED_ACCESS_VIEWS]; + SIZE_T uav_counter_count; + + VkDescriptorSetLayoutBinding *vk_bindings; + SIZE_T vk_bindings_size, vk_binding_count; + + size_t binding_base[WINED3D_SHADER_TYPE_COUNT]; + enum wined3d_shader_type so_stage; +}; + +struct shader_spirv_priv +{ + const struct wined3d_vertex_pipe_ops *vertex_pipe; + const struct wined3d_fragment_pipe_ops *fragment_pipe; + bool ffp_proj_control; + + struct shader_spirv_resource_bindings bindings; +}; + +struct shader_spirv_compile_arguments +{ + union + { + struct + { + uint32_t alpha_swizzle; + unsigned int sample_count; + } fs; + } u; +}; + +struct shader_spirv_graphics_program_variant_vk +{ + struct shader_spirv_compile_arguments compile_args; + const struct wined3d_stream_output_desc *so_desc; + size_t binding_base; + + VkShaderModule vk_module; +}; + +struct shader_spirv_graphics_program_vk +{ + struct shader_spirv_graphics_program_variant_vk *variants; + SIZE_T variants_size, variant_count; + + struct vkd3d_shader_scan_descriptor_info descriptor_info; +}; + +struct shader_spirv_compute_program_vk +{ + VkShaderModule vk_module; + VkPipeline vk_pipeline; + VkPipelineLayout vk_pipeline_layout; + VkDescriptorSetLayout vk_set_layout; + + struct vkd3d_shader_scan_descriptor_info descriptor_info; +}; + +struct wined3d_shader_spirv_compile_args +{ + struct vkd3d_shader_spirv_target_info spirv_target; + struct vkd3d_shader_parameter sample_count; + unsigned int ps_alpha_swizzle[WINED3D_MAX_RENDER_TARGETS]; +}; + +struct wined3d_shader_spirv_shader_interface +{ + struct vkd3d_shader_interface_info vkd3d_interface; + struct vkd3d_shader_transform_feedback_info xfb_info; +}; + +static bool wined3d_load_vkd3d_shader_functions(void *vkd3d_shader_handle) +{ +#define LOAD_FUNCPTR(f) if (!(f = dlsym(vkd3d_shader_handle, #f))) return false; + LOAD_FUNCPTR(vkd3d_shader_compile) + LOAD_FUNCPTR(vkd3d_shader_free_messages) + LOAD_FUNCPTR(vkd3d_shader_free_scan_descriptor_info) + LOAD_FUNCPTR(vkd3d_shader_free_shader_code) + LOAD_FUNCPTR(vkd3d_shader_get_version) + LOAD_FUNCPTR(vkd3d_shader_scan) +#undef LOAD_FUNCPTR + + return true; +} + +static void wined3d_unload_vkd3d_shader(void) +{ + if (vkd3d_shader_handle) + { + dlclose(vkd3d_shader_handle); + vkd3d_shader_handle = NULL; + } +} + +static BOOL WINAPI wined3d_init_vkd3d_once(INIT_ONCE *once, void *param, void **context) +{ + TRACE("Loading vkd3d-shader %s.\n", SONAME_LIBVKD3D_SHADER); + + if ((vkd3d_shader_handle = dlopen(SONAME_LIBVKD3D_SHADER, RTLD_NOW))) + { + if (!wined3d_load_vkd3d_shader_functions(vkd3d_shader_handle)) + { + ERR_(winediag)("Failed to load libvkd3d-shader functions.\n"); + wined3d_unload_vkd3d_shader(); + } + TRACE("Using %s.\n", vkd3d_shader_get_version(NULL, NULL)); + } + else + { + ERR_(winediag)("Failed to load libvkd3d-shader.\n"); + } + + return TRUE; +} + +static bool wined3d_init_vkd3d(void) +{ + static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT; + InitOnceExecuteOnce(&init_once, wined3d_init_vkd3d_once, NULL, NULL); + return !!vkd3d_shader_handle; +} + +static enum vkd3d_shader_visibility vkd3d_shader_visibility_from_wined3d(enum wined3d_shader_type shader_type) +{ + switch (shader_type) + { + case WINED3D_SHADER_TYPE_VERTEX: + return VKD3D_SHADER_VISIBILITY_VERTEX; + case WINED3D_SHADER_TYPE_HULL: + return VKD3D_SHADER_VISIBILITY_HULL; + case WINED3D_SHADER_TYPE_DOMAIN: + return VKD3D_SHADER_VISIBILITY_DOMAIN; + case WINED3D_SHADER_TYPE_GEOMETRY: + return VKD3D_SHADER_VISIBILITY_GEOMETRY; + case WINED3D_SHADER_TYPE_PIXEL: + return VKD3D_SHADER_VISIBILITY_PIXEL; + case WINED3D_SHADER_TYPE_COMPUTE: + return VKD3D_SHADER_VISIBILITY_COMPUTE; + default: + ERR("Invalid shader type %s.\n", debug_shader_type(shader_type)); + return VKD3D_SHADER_VISIBILITY_ALL; + } +} + +static void shader_spirv_handle_instruction(const struct wined3d_shader_instruction *ins) +{ +} + +static void shader_spirv_compile_arguments_init(struct shader_spirv_compile_arguments *args, + const struct wined3d_context *context, const struct wined3d_shader *shader, + const struct wined3d_state *state, unsigned int sample_count) +{ + struct wined3d_rendertarget_view *rtv; + unsigned int i; + + memset(args, 0, sizeof(*args)); + + switch (shader->reg_maps.shader_version.type) + { + case WINED3D_SHADER_TYPE_PIXEL: + for (i = 0; i < ARRAY_SIZE(state->fb.render_targets); ++i) + { + if (!(rtv = state->fb.render_targets[i]) || rtv->format->id == WINED3DFMT_NULL) + continue; + if (rtv->format->id == WINED3DFMT_A8_UNORM && !is_identity_fixup(rtv->format->color_fixup)) + args->u.fs.alpha_swizzle |= 1u << i; + } + args->u.fs.sample_count = sample_count; + break; + + default: + break; + } +} + +static void shader_spirv_init_compile_args(struct wined3d_shader_spirv_compile_args *args, + struct vkd3d_shader_interface_info *vkd3d_interface, enum vkd3d_shader_spirv_environment environment, + enum wined3d_shader_type shader_type, const struct shader_spirv_compile_arguments *compile_args) +{ + unsigned int i; + + memset(args, 0, sizeof(*args)); + args->spirv_target.type = VKD3D_SHADER_STRUCTURE_TYPE_SPIRV_TARGET_INFO; + args->spirv_target.next = vkd3d_interface; + args->spirv_target.entry_point = "main"; + args->spirv_target.environment = environment; + + if (shader_type == WINED3D_SHADER_TYPE_PIXEL) + { + unsigned int rt_alpha_swizzle = compile_args->u.fs.alpha_swizzle; + struct vkd3d_shader_parameter *shader_parameter; + + shader_parameter = &args->sample_count; + shader_parameter->name = VKD3D_SHADER_PARAMETER_NAME_RASTERIZER_SAMPLE_COUNT; + shader_parameter->type = VKD3D_SHADER_PARAMETER_TYPE_IMMEDIATE_CONSTANT; + shader_parameter->data_type = VKD3D_SHADER_PARAMETER_DATA_TYPE_UINT32; + shader_parameter->u.immediate_constant.u.u32 = compile_args->u.fs.sample_count; + + args->spirv_target.parameter_count = 1; + args->spirv_target.parameters = shader_parameter; + + for (i = 0; i < ARRAY_SIZE(args->ps_alpha_swizzle); ++i) + { + if (rt_alpha_swizzle && (1u << i)) + args->ps_alpha_swizzle[i] = VKD3D_SHADER_SWIZZLE(W, X, Y, Z); + else + args->ps_alpha_swizzle[i] = VKD3D_SHADER_NO_SWIZZLE; + } + + args->spirv_target.output_swizzles = args->ps_alpha_swizzle; + args->spirv_target.output_swizzle_count = ARRAY_SIZE(args->ps_alpha_swizzle); + } +} + +static const char *get_line(const char **ptr) +{ + const char *p, *q; + + p = *ptr; + if (!(q = strstr(p, "\n"))) + { + if (!*p) return NULL; + *ptr += strlen(p); + return p; + } + *ptr = q + 1; + + return p; +} + +static void shader_spirv_init_shader_interface_vk(struct wined3d_shader_spirv_shader_interface *iface, + struct wined3d_shader *shader, const struct shader_spirv_resource_bindings *b, + const struct wined3d_stream_output_desc *so_desc) +{ + memset(iface, 0, sizeof(*iface)); + iface->vkd3d_interface.type = VKD3D_SHADER_STRUCTURE_TYPE_INTERFACE_INFO; + + if (so_desc) + { + iface->xfb_info.type = VKD3D_SHADER_STRUCTURE_TYPE_TRANSFORM_FEEDBACK_INFO; + iface->xfb_info.next = NULL; + + iface->xfb_info.elements = (const struct vkd3d_shader_transform_feedback_element *)so_desc->elements; + iface->xfb_info.element_count = so_desc->element_count; + iface->xfb_info.buffer_strides = so_desc->buffer_strides; + iface->xfb_info.buffer_stride_count = so_desc->buffer_stride_count; + + iface->vkd3d_interface.next = &iface->xfb_info; + } + + iface->vkd3d_interface.bindings = b->bindings; + iface->vkd3d_interface.binding_count = b->binding_count; + + iface->vkd3d_interface.uav_counters = b->uav_counters; + iface->vkd3d_interface.uav_counter_count = b->uav_counter_count; +} + +static VkShaderModule shader_spirv_compile(struct wined3d_context_vk *context_vk, + struct wined3d_shader *shader, const struct shader_spirv_compile_arguments *args, + const struct shader_spirv_resource_bindings *bindings, const struct wined3d_stream_output_desc *so_desc) +{ + struct wined3d_shader_spirv_compile_args compile_args; + struct wined3d_shader_spirv_shader_interface iface; + struct vkd3d_shader_compile_info info; + const struct wined3d_vk_info *vk_info; + enum wined3d_shader_type shader_type; + VkShaderModuleCreateInfo shader_desc; + struct wined3d_device_vk *device_vk; + struct vkd3d_shader_code spirv; + VkShaderModule module; + char *messages; + VkResult vr; + int ret; + + shader_spirv_init_shader_interface_vk(&iface, shader, bindings, so_desc); + shader_type = shader->reg_maps.shader_version.type; + shader_spirv_init_compile_args(&compile_args, &iface.vkd3d_interface, + VKD3D_SHADER_SPIRV_ENVIRONMENT_VULKAN_1_0, shader_type, args); + + info.type = VKD3D_SHADER_STRUCTURE_TYPE_COMPILE_INFO; + info.next = &compile_args.spirv_target; + info.source.code = shader->byte_code; + info.source.size = shader->byte_code_size; + info.source_type = VKD3D_SHADER_SOURCE_DXBC_TPF; + info.target_type = VKD3D_SHADER_TARGET_SPIRV_BINARY; + info.options = NULL; + info.option_count = 0; + info.log_level = VKD3D_SHADER_LOG_WARNING; + info.source_name = NULL; + + ret = vkd3d_shader_compile(&info, &spirv, &messages); + if (messages && *messages && FIXME_ON(d3d_shader)) + { + const char *ptr = messages; + const char *line; + + FIXME("Shader log:\n"); + while ((line = get_line(&ptr))) + { + FIXME(" %.*s", (int)(ptr - line), line); + } + FIXME("\n"); + } + vkd3d_shader_free_messages(messages); + + if (ret < 0) + { + ERR("Failed to compile DXBC, ret %d.\n", ret); + return VK_NULL_HANDLE; + } + + device_vk = wined3d_device_vk(context_vk->c.device); + vk_info = &device_vk->vk_info; + + shader_desc.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + shader_desc.pNext = NULL; + shader_desc.flags = 0; + shader_desc.codeSize = spirv.size; + shader_desc.pCode = spirv.code; + if ((vr = VK_CALL(vkCreateShaderModule(device_vk->vk_device, &shader_desc, NULL, &module))) < 0) + { + vkd3d_shader_free_shader_code(&spirv); + WARN("Failed to create Vulkan shader module, vr %s.\n", wined3d_debug_vkresult(vr)); + return VK_NULL_HANDLE; + } + + vkd3d_shader_free_shader_code(&spirv); + + return module; +} + +static struct shader_spirv_graphics_program_variant_vk *shader_spirv_find_graphics_program_variant_vk( + struct shader_spirv_priv *priv, struct wined3d_context_vk *context_vk, struct wined3d_shader *shader, + const struct wined3d_state *state, const struct shader_spirv_resource_bindings *bindings) +{ + enum wined3d_shader_type shader_type = shader->reg_maps.shader_version.type; + struct shader_spirv_graphics_program_variant_vk *variant_vk; + size_t binding_base = bindings->binding_base[shader_type]; + const struct wined3d_stream_output_desc *so_desc = NULL; + struct shader_spirv_graphics_program_vk *program_vk; + struct shader_spirv_compile_arguments args; + size_t variant_count, i; + + shader_spirv_compile_arguments_init(&args, &context_vk->c, shader, state, context_vk->sample_count); + if (bindings->so_stage == shader_type) + so_desc = state->shader[WINED3D_SHADER_TYPE_GEOMETRY]->u.gs.so_desc; + + if (!(program_vk = shader->backend_data)) + return NULL; + + variant_count = program_vk->variant_count; + for (i = 0; i < variant_count; ++i) + { + variant_vk = &program_vk->variants[i]; + if (variant_vk->so_desc == so_desc && variant_vk->binding_base == binding_base + && !memcmp(&variant_vk->compile_args, &args, sizeof(args))) + return variant_vk; + } + + if (!wined3d_array_reserve((void **)&program_vk->variants, &program_vk->variants_size, + variant_count + 1, sizeof(*program_vk->variants))) + return NULL; + + variant_vk = &program_vk->variants[variant_count]; + variant_vk->compile_args = args; + variant_vk->binding_base = binding_base; + if (!(variant_vk->vk_module = shader_spirv_compile(context_vk, shader, &args, bindings, so_desc))) + return NULL; + ++program_vk->variant_count; + + return variant_vk; +} + +static struct shader_spirv_compute_program_vk *shader_spirv_find_compute_program_vk(struct shader_spirv_priv *priv, + struct wined3d_context_vk *context_vk, struct wined3d_shader *shader, + const struct shader_spirv_resource_bindings *bindings) +{ + struct wined3d_device_vk *device_vk = wined3d_device_vk(context_vk->c.device); + const struct wined3d_vk_info *vk_info = context_vk->vk_info; + struct shader_spirv_compute_program_vk *program; + struct wined3d_pipeline_layout_vk *layout; + VkComputePipelineCreateInfo pipeline_info; + VkResult vr; + + if (!(program = shader->backend_data)) + return NULL; + + if (program->vk_module) + return program; + + if (!(program->vk_module = shader_spirv_compile(context_vk, shader, NULL, bindings, NULL))) + return NULL; + + if (!(layout = wined3d_context_vk_get_pipeline_layout(context_vk, + bindings->vk_bindings, bindings->vk_binding_count))) + { + VK_CALL(vkDestroyShaderModule(device_vk->vk_device, program->vk_module, NULL)); + program->vk_module = VK_NULL_HANDLE; + return NULL; + } + program->vk_set_layout = layout->vk_set_layout; + program->vk_pipeline_layout = layout->vk_pipeline_layout; + + pipeline_info.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; + pipeline_info.pNext = NULL; + pipeline_info.flags = 0; + pipeline_info.stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + pipeline_info.stage.pNext = NULL; + pipeline_info.stage.flags = 0; + pipeline_info.stage.stage = VK_SHADER_STAGE_COMPUTE_BIT; + pipeline_info.stage.pName = "main"; + pipeline_info.stage.pSpecializationInfo = NULL; + pipeline_info.stage.module = program->vk_module; + pipeline_info.layout = program->vk_pipeline_layout; + pipeline_info.basePipelineHandle = VK_NULL_HANDLE; + pipeline_info.basePipelineIndex = -1; + if ((vr = VK_CALL(vkCreateComputePipelines(device_vk->vk_device, + VK_NULL_HANDLE, 1, &pipeline_info, NULL, &program->vk_pipeline))) < 0) + { + ERR("Failed to create Vulkan compute pipeline, vr %s.\n", wined3d_debug_vkresult(vr)); + VK_CALL(vkDestroyShaderModule(device_vk->vk_device, program->vk_module, NULL)); + program->vk_module = VK_NULL_HANDLE; + return NULL; + } + + return program; +} + +static void shader_spirv_resource_bindings_cleanup(struct shader_spirv_resource_bindings *bindings) +{ + heap_free(bindings->vk_bindings); + heap_free(bindings->bindings); +} + +static bool shader_spirv_resource_bindings_add_vk_binding(struct shader_spirv_resource_bindings *bindings, + VkDescriptorType vk_type, VkShaderStageFlagBits vk_stage, size_t *binding_idx) +{ + SIZE_T binding_count = bindings->vk_binding_count; + VkDescriptorSetLayoutBinding *binding; + + if (!wined3d_array_reserve((void **)&bindings->vk_bindings, &bindings->vk_bindings_size, + binding_count + 1, sizeof(*bindings->vk_bindings))) + return false; + + *binding_idx = binding_count; + binding = &bindings->vk_bindings[binding_count]; + binding->binding = binding_count; + binding->descriptorType = vk_type; + binding->descriptorCount = 1; + binding->stageFlags = vk_stage; + binding->pImmutableSamplers = NULL; + ++bindings->vk_binding_count; + + return true; +} + +static bool shader_spirv_resource_bindings_add_binding(struct shader_spirv_resource_bindings *bindings, + enum vkd3d_shader_descriptor_type vkd3d_type, VkDescriptorType vk_type, size_t register_idx, + enum vkd3d_shader_visibility vkd3d_visibility, VkShaderStageFlagBits vk_stage, + uint32_t flags, size_t *binding_idx) +{ + SIZE_T binding_count = bindings->binding_count; + struct vkd3d_shader_resource_binding *binding; + + if (!wined3d_array_reserve((void **)&bindings->bindings, &bindings->bindings_size, + binding_count + 1, sizeof(*bindings->bindings))) + return false; + + if (!shader_spirv_resource_bindings_add_vk_binding(bindings, vk_type, vk_stage, binding_idx)) + return false; + + binding = &bindings->bindings[binding_count]; + binding->type = vkd3d_type; + binding->register_space = 0; + binding->register_index = register_idx; + binding->shader_visibility = vkd3d_visibility; + binding->flags = flags; + binding->binding.set = 0; + binding->binding.binding = *binding_idx; + binding->binding.count = 1; + ++bindings->binding_count; + + return true; +} + +static bool shader_spirv_resource_bindings_add_uav_counter_binding(struct shader_spirv_resource_bindings *bindings, + size_t register_idx, enum vkd3d_shader_visibility vkd3d_visibility, + VkShaderStageFlagBits vk_stage, size_t *binding_idx) +{ + SIZE_T uav_counter_count = bindings->uav_counter_count; + struct vkd3d_shader_uav_counter_binding *counter; + + if (uav_counter_count >= ARRAY_SIZE(bindings->uav_counters)) + return false; + + if (!shader_spirv_resource_bindings_add_vk_binding(bindings, + VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, vk_stage, binding_idx)) + return false; + + counter = &bindings->uav_counters[uav_counter_count]; + counter->register_space = 0; + counter->register_index = register_idx; + counter->shader_visibility = vkd3d_visibility; + counter->binding.set = 0; + counter->binding.binding = *binding_idx; + counter->binding.count = 1; + counter->offset = 0; + ++bindings->uav_counter_count; + + return true; +} + +static bool wined3d_shader_resource_bindings_add_binding(struct wined3d_shader_resource_bindings *bindings, + enum wined3d_shader_type shader_type, enum wined3d_shader_descriptor_type shader_descriptor_type, + size_t resource_idx, enum wined3d_shader_resource_type resource_type, + enum wined3d_data_type resource_data_type, size_t binding_idx) +{ + struct wined3d_shader_resource_binding *binding; + SIZE_T binding_count = bindings->count; + + if (!wined3d_array_reserve((void **)&bindings->bindings, &bindings->size, + binding_count + 1, sizeof(*bindings->bindings))) + return false; + + binding = &bindings->bindings[binding_count]; + binding->shader_type = shader_type; + binding->shader_descriptor_type = shader_descriptor_type; + binding->resource_idx = resource_idx; + binding->resource_type = resource_type; + binding->resource_data_type = resource_data_type; + binding->binding_idx = binding_idx; + ++bindings->count; + + return true; +} + +static VkDescriptorType vk_descriptor_type_from_vkd3d(enum vkd3d_shader_descriptor_type type, + enum vkd3d_shader_resource_type resource_type) +{ + switch (type) + { + case VKD3D_SHADER_DESCRIPTOR_TYPE_CBV: + return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + + case VKD3D_SHADER_DESCRIPTOR_TYPE_SRV: + if (resource_type == VKD3D_SHADER_RESOURCE_BUFFER) + return VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; + return VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + + case VKD3D_SHADER_DESCRIPTOR_TYPE_UAV: + if (resource_type == VKD3D_SHADER_RESOURCE_BUFFER) + return VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER; + return VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + + case VKD3D_SHADER_DESCRIPTOR_TYPE_SAMPLER: + return VK_DESCRIPTOR_TYPE_SAMPLER; + + default: + FIXME("Unhandled descriptor type %#x.\n", type); + return ~0u; + } +} + +static enum wined3d_shader_descriptor_type wined3d_descriptor_type_from_vkd3d(enum vkd3d_shader_descriptor_type type) +{ + switch (type) + { + case VKD3D_SHADER_DESCRIPTOR_TYPE_CBV: + return WINED3D_SHADER_DESCRIPTOR_TYPE_CBV; + + case VKD3D_SHADER_DESCRIPTOR_TYPE_SRV: + return WINED3D_SHADER_DESCRIPTOR_TYPE_SRV; + + case VKD3D_SHADER_DESCRIPTOR_TYPE_UAV: + return WINED3D_SHADER_DESCRIPTOR_TYPE_UAV; + + case VKD3D_SHADER_DESCRIPTOR_TYPE_SAMPLER: + return WINED3D_SHADER_DESCRIPTOR_TYPE_SAMPLER; + + default: + FIXME("Unhandled descriptor type %#x.\n", type); + return ~0u; + } +} + +static enum wined3d_shader_resource_type wined3d_shader_resource_type_from_vkd3d(enum vkd3d_shader_resource_type t) +{ + return (enum wined3d_shader_resource_type)t; +} + +static enum wined3d_data_type wined3d_data_type_from_vkd3d(enum vkd3d_shader_resource_data_type t) +{ + switch (t) + { + case VKD3D_SHADER_RESOURCE_DATA_UNORM: + return WINED3D_DATA_UNORM; + case VKD3D_SHADER_RESOURCE_DATA_SNORM: + return WINED3D_DATA_SNORM; + case VKD3D_SHADER_RESOURCE_DATA_INT: + return WINED3D_DATA_INT; + case VKD3D_SHADER_RESOURCE_DATA_UINT: + return WINED3D_DATA_UINT; + case VKD3D_SHADER_RESOURCE_DATA_FLOAT: + return WINED3D_DATA_FLOAT; + default: + FIXME("Unhandled resource data type %#x.\n", t); + return WINED3D_DATA_FLOAT; + } +} + +static bool shader_spirv_resource_bindings_init(struct shader_spirv_resource_bindings *bindings, + struct wined3d_shader_resource_bindings *wined3d_bindings, + const struct wined3d_state *state, uint32_t shader_mask) +{ + struct vkd3d_shader_scan_descriptor_info *descriptor_info; + enum wined3d_shader_descriptor_type wined3d_type; + enum vkd3d_shader_visibility shader_visibility; + enum wined3d_shader_type shader_type; + VkDescriptorType vk_descriptor_type; + VkShaderStageFlagBits vk_stage; + struct wined3d_shader *shader; + size_t binding_idx; + unsigned int i; + + bindings->binding_count = 0; + bindings->uav_counter_count = 0; + bindings->vk_binding_count = 0; + bindings->so_stage = WINED3D_SHADER_TYPE_GEOMETRY; + wined3d_bindings->count = 0; + + for (shader_type = 0; shader_type < WINED3D_SHADER_TYPE_COUNT; ++shader_type) + { + bindings->binding_base[shader_type] = bindings->vk_binding_count; + + if (!(shader_mask & (1u << shader_type)) || !(shader = state->shader[shader_type])) + continue; + + if (shader_type == WINED3D_SHADER_TYPE_COMPUTE) + { + descriptor_info = &((struct shader_spirv_compute_program_vk *)shader->backend_data)->descriptor_info; + } + else + { + descriptor_info = &((struct shader_spirv_graphics_program_vk *)shader->backend_data)->descriptor_info; + if (shader_type == WINED3D_SHADER_TYPE_GEOMETRY && !shader->function) + bindings->so_stage = WINED3D_SHADER_TYPE_VERTEX; + } + + vk_stage = vk_shader_stage_from_wined3d(shader_type); + shader_visibility = vkd3d_shader_visibility_from_wined3d(shader_type); + + for (i = 0; i < descriptor_info->descriptor_count; ++i) + { + struct vkd3d_shader_descriptor_info *d = &descriptor_info->descriptors[i]; + uint32_t flags; + + if (d->register_space) + { + WARN("Unsupported register space %u.\n", d->register_space); + return false; + } + + if (d->resource_type == VKD3D_SHADER_RESOURCE_BUFFER) + flags = VKD3D_SHADER_BINDING_FLAG_BUFFER; + else + flags = VKD3D_SHADER_BINDING_FLAG_IMAGE; + + vk_descriptor_type = vk_descriptor_type_from_vkd3d(d->type, d->resource_type); + if (!shader_spirv_resource_bindings_add_binding(bindings, d->type, vk_descriptor_type, + d->register_index, shader_visibility, vk_stage, flags, &binding_idx)) + return false; + + wined3d_type = wined3d_descriptor_type_from_vkd3d(d->type); + if (!wined3d_shader_resource_bindings_add_binding(wined3d_bindings, shader_type, + wined3d_type, d->register_index, wined3d_shader_resource_type_from_vkd3d(d->resource_type), + wined3d_data_type_from_vkd3d(d->resource_data_type), binding_idx)) + return false; + + if (d->type == VKD3D_SHADER_DESCRIPTOR_TYPE_UAV + && (d->flags & VKD3D_SHADER_DESCRIPTOR_INFO_FLAG_UAV_COUNTER)) + { + if (!shader_spirv_resource_bindings_add_uav_counter_binding(bindings, + d->register_index, shader_visibility, vk_stage, &binding_idx)) + return false; + if (!wined3d_shader_resource_bindings_add_binding(wined3d_bindings, + shader_type, WINED3D_SHADER_DESCRIPTOR_TYPE_UAV_COUNTER, d->register_index, + WINED3D_SHADER_RESOURCE_BUFFER, WINED3D_DATA_UINT, binding_idx)) + return false; + } + } + } + + return true; +} + +static void shader_spirv_scan_shader(struct wined3d_shader *shader, + struct vkd3d_shader_scan_descriptor_info *descriptor_info) +{ + struct vkd3d_shader_compile_info info; + char *messages; + int ret; + + memset(descriptor_info, 0, sizeof(*descriptor_info)); + descriptor_info->type = VKD3D_SHADER_STRUCTURE_TYPE_SCAN_DESCRIPTOR_INFO; + + info.type = VKD3D_SHADER_STRUCTURE_TYPE_COMPILE_INFO; + info.next = descriptor_info; + info.source.code = shader->byte_code; + info.source.size = shader->byte_code_size; + info.source_type = VKD3D_SHADER_SOURCE_DXBC_TPF; + info.target_type = VKD3D_SHADER_TARGET_SPIRV_BINARY; + info.options = NULL; + info.option_count = 0; + info.log_level = VKD3D_SHADER_LOG_WARNING; + info.source_name = NULL; + + if ((ret = vkd3d_shader_scan(&info, &messages)) < 0) + ERR("Failed to scan shader, ret %d.\n", ret); + if (messages && *messages && FIXME_ON(d3d_shader)) + { + const char *ptr = messages; + const char *line; + + FIXME("Shader log:\n"); + while ((line = get_line(&ptr))) + { + FIXME(" %.*s", (int)(ptr - line), line); + } + FIXME("\n"); + } + vkd3d_shader_free_messages(messages); +} + +static void shader_spirv_precompile_compute(struct wined3d_shader *shader) +{ + struct shader_spirv_compute_program_vk *program_vk; + + if (!(program_vk = shader->backend_data)) + { + if (!(program_vk = heap_alloc_zero(sizeof(*program_vk)))) + ERR("Failed to allocate program.\n"); + shader->backend_data = program_vk; + } + + shader_spirv_scan_shader(shader, &program_vk->descriptor_info); +} + +static void shader_spirv_precompile(void *shader_priv, struct wined3d_shader *shader) +{ + struct shader_spirv_graphics_program_vk *program_vk; + + TRACE("shader_priv %p, shader %p.\n", shader_priv, shader); + + if (shader->reg_maps.shader_version.type == WINED3D_SHADER_TYPE_COMPUTE) + { + shader_spirv_precompile_compute(shader); + return; + } + + if (!(program_vk = shader->backend_data)) + { + if (!(program_vk = heap_alloc_zero(sizeof(*program_vk)))) + ERR("Failed to allocate program.\n"); + shader->backend_data = program_vk; + } + + shader_spirv_scan_shader(shader, &program_vk->descriptor_info); +} + +static void shader_spirv_select(void *shader_priv, struct wined3d_context *context, + const struct wined3d_state *state) +{ + struct wined3d_context_vk *context_vk = wined3d_context_vk(context); + struct shader_spirv_graphics_program_variant_vk *variant_vk; + struct shader_spirv_resource_bindings *bindings; + size_t binding_base[WINED3D_SHADER_TYPE_COUNT]; + struct wined3d_pipeline_layout_vk *layout_vk; + struct shader_spirv_priv *priv = shader_priv; + enum wined3d_shader_type shader_type; + struct wined3d_shader *shader; + + priv->vertex_pipe->vp_enable(context, !use_vs(state)); + priv->fragment_pipe->fp_enable(context, !use_ps(state)); + + bindings = &priv->bindings; + memcpy(binding_base, bindings->binding_base, sizeof(bindings->binding_base)); + if (!shader_spirv_resource_bindings_init(bindings, &context_vk->graphics.bindings, + state, ~(1u << WINED3D_SHADER_TYPE_COMPUTE))) + { + ERR("Failed to initialise shader resource bindings.\n"); + goto fail; + } + if (context->shader_update_mask & (1u << WINED3D_SHADER_TYPE_GEOMETRY)) + context->shader_update_mask |= 1u << bindings->so_stage; + + layout_vk = wined3d_context_vk_get_pipeline_layout(context_vk, bindings->vk_bindings, bindings->vk_binding_count); + context_vk->graphics.vk_set_layout = layout_vk->vk_set_layout; + context_vk->graphics.vk_pipeline_layout = layout_vk->vk_pipeline_layout; + + for (shader_type = 0; shader_type < ARRAY_SIZE(context_vk->graphics.vk_modules); ++shader_type) + { + if (!(context->shader_update_mask & (1u << shader_type)) && (!context_vk->graphics.vk_modules[shader_type] + || binding_base[shader_type] == bindings->binding_base[shader_type])) + continue; + + if (!(shader = state->shader[shader_type]) || !shader->function) + { + context_vk->graphics.vk_modules[shader_type] = VK_NULL_HANDLE; + continue; + } + + if (!(variant_vk = shader_spirv_find_graphics_program_variant_vk(priv, context_vk, shader, state, bindings))) + goto fail; + context_vk->graphics.vk_modules[shader_type] = variant_vk->vk_module; + } + + return; + +fail: + context_vk->graphics.vk_set_layout = VK_NULL_HANDLE; + context_vk->graphics.vk_pipeline_layout = VK_NULL_HANDLE; +} + +static void shader_spirv_select_compute(void *shader_priv, + struct wined3d_context *context, const struct wined3d_state *state) +{ + struct wined3d_context_vk *context_vk = wined3d_context_vk(context); + struct shader_spirv_compute_program_vk *program; + struct shader_spirv_priv *priv = shader_priv; + struct wined3d_shader *shader; + + if (!shader_spirv_resource_bindings_init(&priv->bindings, + &context_vk->compute.bindings, state, 1u << WINED3D_SHADER_TYPE_COMPUTE)) + ERR("Failed to initialise shader resource bindings.\n"); + + if ((shader = state->shader[WINED3D_SHADER_TYPE_COMPUTE])) + program = shader_spirv_find_compute_program_vk(priv, context_vk, shader, &priv->bindings); + else + program = NULL; + + if (program) + { + context_vk->compute.vk_pipeline = program->vk_pipeline; + context_vk->compute.vk_set_layout = program->vk_set_layout; + context_vk->compute.vk_pipeline_layout = program->vk_pipeline_layout; + } + else + { + context_vk->compute.vk_pipeline = VK_NULL_HANDLE; + context_vk->compute.vk_set_layout = VK_NULL_HANDLE; + context_vk->compute.vk_pipeline_layout = VK_NULL_HANDLE; + } +} + +static void shader_spirv_disable(void *shader_priv, struct wined3d_context *context) +{ + struct wined3d_context_vk *context_vk = wined3d_context_vk(context); + struct shader_spirv_priv *priv = shader_priv; + + priv->vertex_pipe->vp_enable(context, false); + priv->fragment_pipe->fp_enable(context, false); + + context_vk->compute.vk_pipeline = VK_NULL_HANDLE; + context->shader_update_mask = (1u << WINED3D_SHADER_TYPE_PIXEL) + | (1u << WINED3D_SHADER_TYPE_VERTEX) + | (1u << WINED3D_SHADER_TYPE_GEOMETRY) + | (1u << WINED3D_SHADER_TYPE_HULL) + | (1u << WINED3D_SHADER_TYPE_DOMAIN) + | (1u << WINED3D_SHADER_TYPE_COMPUTE); +} + +static void shader_spirv_update_float_vertex_constants(struct wined3d_device *device, UINT start, UINT count) +{ + WARN("Not implemented.\n"); +} + +static void shader_spirv_update_float_pixel_constants(struct wined3d_device *device, UINT start, UINT count) +{ + WARN("Not implemented.\n"); +} + +static void shader_spirv_load_constants(void *shader_priv, struct wined3d_context *context, + const struct wined3d_state *state) +{ + WARN("Not implemented.\n"); +} + +static void shader_spirv_invalidate_compute_program(struct wined3d_context_vk *context_vk, + const struct shader_spirv_compute_program_vk *program) +{ + if (context_vk->compute.vk_pipeline == program->vk_pipeline) + { + context_vk->c.shader_update_mask |= (1u << WINED3D_SHADER_TYPE_COMPUTE); + context_vk->compute.vk_pipeline = VK_NULL_HANDLE; + } +} + +static void shader_spirv_invalidate_contexts_compute_program(struct wined3d_device *device, + const struct shader_spirv_compute_program_vk *program) +{ + unsigned int i; + + for (i = 0; i < device->context_count; ++i) + { + shader_spirv_invalidate_compute_program(wined3d_context_vk(device->contexts[i]), program); + } +} + +static void shader_spirv_invalidate_graphics_program_variant(struct wined3d_context_vk *context_vk, + const struct shader_spirv_graphics_program_variant_vk *variant) +{ + enum wined3d_shader_type shader_type; + + for (shader_type = 0; shader_type < WINED3D_SHADER_TYPE_GRAPHICS_COUNT; ++shader_type) + { + if (context_vk->graphics.vk_modules[shader_type] != variant->vk_module) + continue; + + context_vk->graphics.vk_modules[shader_type] = VK_NULL_HANDLE; + context_vk->c.shader_update_mask |= (1u << shader_type); + } +} + +static void shader_spirv_invalidate_contexts_graphics_program_variant(struct wined3d_device *device, + const struct shader_spirv_graphics_program_variant_vk *variant) +{ + unsigned int i; + + for (i = 0; i < device->context_count; ++i) + { + shader_spirv_invalidate_graphics_program_variant(wined3d_context_vk(device->contexts[i]), variant); + } +} + +static void shader_spirv_destroy_compute_vk(struct wined3d_shader *shader) +{ + struct wined3d_device_vk *device_vk = wined3d_device_vk(shader->device); + struct shader_spirv_compute_program_vk *program = shader->backend_data; + struct wined3d_vk_info *vk_info = &device_vk->vk_info; + + shader_spirv_invalidate_contexts_compute_program(&device_vk->d, program); + VK_CALL(vkDestroyPipeline(device_vk->vk_device, program->vk_pipeline, NULL)); + VK_CALL(vkDestroyShaderModule(device_vk->vk_device, program->vk_module, NULL)); + vkd3d_shader_free_scan_descriptor_info(&program->descriptor_info); + shader->backend_data = NULL; + heap_free(program); +} + +static void shader_spirv_destroy(struct wined3d_shader *shader) +{ + struct wined3d_device_vk *device_vk = wined3d_device_vk(shader->device); + struct shader_spirv_graphics_program_variant_vk *variant_vk; + struct wined3d_vk_info *vk_info = &device_vk->vk_info; + struct shader_spirv_graphics_program_vk *program_vk; + size_t i; + + if (!shader->backend_data) + return; + + if (shader->reg_maps.shader_version.type == WINED3D_SHADER_TYPE_COMPUTE) + { + shader_spirv_destroy_compute_vk(shader); + return; + } + + program_vk = shader->backend_data; + for (i = 0; i < program_vk->variant_count; ++i) + { + variant_vk = &program_vk->variants[i]; + shader_spirv_invalidate_contexts_graphics_program_variant(&device_vk->d, variant_vk); + VK_CALL(vkDestroyShaderModule(device_vk->vk_device, variant_vk->vk_module, NULL)); + } + vkd3d_shader_free_scan_descriptor_info(&program_vk->descriptor_info); + + shader->backend_data = NULL; + heap_free(program_vk); +} + +static HRESULT shader_spirv_alloc(struct wined3d_device *device, + const struct wined3d_vertex_pipe_ops *vertex_pipe, const struct wined3d_fragment_pipe_ops *fragment_pipe) +{ + struct fragment_caps fragment_caps; + void *vertex_priv, *fragment_priv; + struct shader_spirv_priv *priv; + + if (!(priv = heap_alloc(sizeof(*priv)))) + return E_OUTOFMEMORY; + + if (!(vertex_priv = vertex_pipe->vp_alloc(&spirv_shader_backend_vk, priv))) + { + ERR("Failed to initialise vertex pipe.\n"); + heap_free(priv); + return E_FAIL; + } + + if (!(fragment_priv = fragment_pipe->alloc_private(&spirv_shader_backend_vk, priv))) + { + ERR("Failed to initialise fragment pipe.\n"); + vertex_pipe->vp_free(device, NULL); + heap_free(priv); + return E_FAIL; + } + + priv->vertex_pipe = vertex_pipe; + priv->fragment_pipe = fragment_pipe; + fragment_pipe->get_caps(device->adapter, &fragment_caps); + priv->ffp_proj_control = fragment_caps.wined3d_caps & WINED3D_FRAGMENT_CAP_PROJ_CONTROL; + memset(&priv->bindings, 0, sizeof(priv->bindings)); + + device->vertex_priv = vertex_priv; + device->fragment_priv = fragment_priv; + device->shader_priv = priv; + + return WINED3D_OK; +} + +static void shader_spirv_free(struct wined3d_device *device, struct wined3d_context *context) +{ + struct shader_spirv_priv *priv = device->shader_priv; + + shader_spirv_resource_bindings_cleanup(&priv->bindings); + priv->fragment_pipe->free_private(device, context); + priv->vertex_pipe->vp_free(device, context); + heap_free(priv); +} + +static BOOL shader_spirv_allocate_context_data(struct wined3d_context *context) +{ + return TRUE; +} + +static void shader_spirv_free_context_data(struct wined3d_context *context) +{ +} + +static void shader_spirv_init_context_state(struct wined3d_context *context) +{ +} + +static void shader_spirv_get_caps(const struct wined3d_adapter *adapter, struct shader_caps *caps) +{ + caps->vs_version = min(wined3d_settings.max_sm_vs, 5); + caps->hs_version = min(wined3d_settings.max_sm_hs, 5); + caps->ds_version = min(wined3d_settings.max_sm_ds, 5); + caps->gs_version = min(wined3d_settings.max_sm_gs, 5); + caps->ps_version = min(wined3d_settings.max_sm_ps, 5); + caps->cs_version = min(wined3d_settings.max_sm_cs, 5); + + caps->vs_uniform_count = WINED3D_MAX_VS_CONSTS_F; + caps->ps_uniform_count = WINED3D_MAX_PS_CONSTS_F; + caps->ps_1x_max_value = FLT_MAX; + caps->varying_count = 0; + caps->wined3d_caps = WINED3D_SHADER_CAP_VS_CLIPPING + | WINED3D_SHADER_CAP_SRGB_WRITE + | WINED3D_SHADER_CAP_FULL_FFP_VARYINGS; +} + +static BOOL shader_spirv_color_fixup_supported(struct color_fixup_desc fixup) +{ + return is_identity_fixup(fixup); +} + +static BOOL shader_spirv_has_ffp_proj_control(void *shader_priv) +{ + struct shader_spirv_priv *priv = shader_priv; + + return priv->ffp_proj_control; +} + +static const struct wined3d_shader_backend_ops spirv_shader_backend_vk = +{ + .shader_handle_instruction = shader_spirv_handle_instruction, + .shader_precompile = shader_spirv_precompile, + .shader_select = shader_spirv_select, + .shader_select_compute = shader_spirv_select_compute, + .shader_disable = shader_spirv_disable, + .shader_update_float_vertex_constants = shader_spirv_update_float_vertex_constants, + .shader_update_float_pixel_constants = shader_spirv_update_float_pixel_constants, + .shader_load_constants = shader_spirv_load_constants, + .shader_destroy = shader_spirv_destroy, + .shader_alloc_private = shader_spirv_alloc, + .shader_free_private = shader_spirv_free, + .shader_allocate_context_data = shader_spirv_allocate_context_data, + .shader_free_context_data = shader_spirv_free_context_data, + .shader_init_context_state = shader_spirv_init_context_state, + .shader_get_caps = shader_spirv_get_caps, + .shader_color_fixup_supported = shader_spirv_color_fixup_supported, + .shader_has_ffp_proj_control = shader_spirv_has_ffp_proj_control, +}; + +const struct wined3d_shader_backend_ops *wined3d_spirv_shader_backend_init_vk(void) +{ + if (!wined3d_init_vkd3d()) + return NULL; + + return &spirv_shader_backend_vk; +} + +void wined3d_spirv_shader_backend_cleanup(void) +{ + wined3d_unload_vkd3d_shader(); +} + +static void spirv_vertex_pipe_vk_vp_enable(const struct wined3d_context *context, BOOL enable) +{ + /* Nothing to do. */ +} + +static void spirv_vertex_pipe_vk_vp_get_caps(const struct wined3d_adapter *adapter, struct wined3d_vertex_caps *caps) +{ + memset(caps, 0, sizeof(*caps)); +} + +static uint32_t spirv_vertex_pipe_vk_vp_get_emul_mask(const struct wined3d_gl_info *gl_info) +{ + return 0; +} + +static void *spirv_vertex_pipe_vk_vp_alloc(const struct wined3d_shader_backend_ops *shader_backend, void *shader_priv) +{ + if (shader_backend != &spirv_shader_backend_vk) + { + FIXME("SPIR-V vertex pipe without SPIR-V shader backend not implemented.\n"); + return NULL; + } + + return shader_priv; +} + +static void spirv_vertex_pipe_vk_vp_free(struct wined3d_device *device, struct wined3d_context *context) +{ + /* Nothing to do. */ +} + +static const struct wined3d_state_entry_template spirv_vertex_pipe_vk_vp_states[] = +{ + {STATE_RENDER(WINED3D_RS_RANGEFOGENABLE), {STATE_RENDER(WINED3D_RS_RANGEFOGENABLE), state_nop}}, + {STATE_RENDER(WINED3D_RS_CLIPPING), {STATE_RENDER(WINED3D_RS_CLIPPING), state_nop}}, + {STATE_RENDER(WINED3D_RS_LIGHTING), {STATE_RENDER(WINED3D_RS_LIGHTING), state_nop}}, + {STATE_RENDER(WINED3D_RS_AMBIENT), {STATE_RENDER(WINED3D_RS_AMBIENT), state_nop}}, + {STATE_RENDER(WINED3D_RS_COLORVERTEX), {STATE_RENDER(WINED3D_RS_COLORVERTEX), state_nop}}, + {STATE_RENDER(WINED3D_RS_LOCALVIEWER), {STATE_RENDER(WINED3D_RS_LOCALVIEWER), state_nop}}, + {STATE_RENDER(WINED3D_RS_NORMALIZENORMALS), {STATE_RENDER(WINED3D_RS_NORMALIZENORMALS), state_nop}}, + {STATE_RENDER(WINED3D_RS_DIFFUSEMATERIALSOURCE), {STATE_RENDER(WINED3D_RS_DIFFUSEMATERIALSOURCE), state_nop}}, + {STATE_RENDER(WINED3D_RS_SPECULARMATERIALSOURCE), {STATE_RENDER(WINED3D_RS_SPECULARMATERIALSOURCE), state_nop}}, + {STATE_RENDER(WINED3D_RS_AMBIENTMATERIALSOURCE), {STATE_RENDER(WINED3D_RS_AMBIENTMATERIALSOURCE), state_nop}}, + {STATE_RENDER(WINED3D_RS_EMISSIVEMATERIALSOURCE), {STATE_RENDER(WINED3D_RS_EMISSIVEMATERIALSOURCE), state_nop}}, + {STATE_RENDER(WINED3D_RS_VERTEXBLEND), {STATE_RENDER(WINED3D_RS_VERTEXBLEND), state_nop}}, + {STATE_RENDER(WINED3D_RS_CLIPPLANEENABLE), {STATE_RENDER(WINED3D_RS_CLIPPLANEENABLE), state_nop}}, + {STATE_RENDER(WINED3D_RS_POINTSIZE), {STATE_RENDER(WINED3D_RS_POINTSIZE), state_nop}}, + {STATE_RENDER(WINED3D_RS_POINTSIZE_MIN), {STATE_RENDER(WINED3D_RS_POINTSIZE_MIN), state_nop}}, + {STATE_RENDER(WINED3D_RS_POINTSCALEENABLE), {STATE_RENDER(WINED3D_RS_POINTSCALEENABLE), state_nop}}, + {STATE_RENDER(WINED3D_RS_POINTSCALE_A), {STATE_RENDER(WINED3D_RS_POINTSCALE_A), state_nop}}, + {STATE_RENDER(WINED3D_RS_POINTSCALE_B), {STATE_RENDER(WINED3D_RS_POINTSCALE_B), state_nop}}, + {STATE_RENDER(WINED3D_RS_POINTSCALE_C), {STATE_RENDER(WINED3D_RS_POINTSCALE_C), state_nop}}, + {STATE_RENDER(WINED3D_RS_POINTSIZE_MAX), {STATE_RENDER(WINED3D_RS_POINTSIZE_MAX), state_nop}}, + {STATE_RENDER(WINED3D_RS_INDEXEDVERTEXBLENDENABLE), {STATE_RENDER(WINED3D_RS_INDEXEDVERTEXBLENDENABLE), state_nop}}, + {STATE_RENDER(WINED3D_RS_TWEENFACTOR), {STATE_RENDER(WINED3D_RS_TWEENFACTOR), state_nop}}, + {STATE_MATERIAL, {STATE_MATERIAL, state_nop}}, + {STATE_SHADER(WINED3D_SHADER_TYPE_VERTEX), {STATE_SHADER(WINED3D_SHADER_TYPE_VERTEX), state_nop}}, + {STATE_LIGHT_TYPE, {STATE_LIGHT_TYPE, state_nop}}, + {0}, /* Terminate */ +}; + +static const struct wined3d_vertex_pipe_ops spirv_vertex_pipe_vk = +{ + .vp_enable = spirv_vertex_pipe_vk_vp_enable, + .vp_get_caps = spirv_vertex_pipe_vk_vp_get_caps, + .vp_get_emul_mask = spirv_vertex_pipe_vk_vp_get_emul_mask, + .vp_alloc = spirv_vertex_pipe_vk_vp_alloc, + .vp_free = spirv_vertex_pipe_vk_vp_free, + .vp_states = spirv_vertex_pipe_vk_vp_states, +}; + +const struct wined3d_vertex_pipe_ops *wined3d_spirv_vertex_pipe_init_vk(void) +{ + return &spirv_vertex_pipe_vk; +} + +static void spirv_fragment_pipe_vk_fp_enable(const struct wined3d_context *context, BOOL enable) +{ + /* Nothing to do. */ +} + +static void spirv_fragment_pipe_vk_fp_get_caps(const struct wined3d_adapter *adapter, struct fragment_caps *caps) +{ + memset(caps, 0, sizeof(*caps)); +} + +static uint32_t spirv_fragment_pipe_vk_fp_get_emul_mask(const struct wined3d_gl_info *gl_info) +{ + return 0; +} + +static void *spirv_fragment_pipe_vk_fp_alloc(const struct wined3d_shader_backend_ops *shader_backend, void *shader_priv) +{ + if (shader_backend != &spirv_shader_backend_vk) + { + FIXME("SPIR-V fragment pipe without SPIR-V shader backend not implemented.\n"); + return NULL; + } + + return shader_priv; +} + +static void spirv_fragment_pipe_vk_fp_free(struct wined3d_device *device, struct wined3d_context *context) +{ + /* Nothing to do. */ +} + +static BOOL spirv_fragment_pipe_vk_fp_alloc_context_data(struct wined3d_context *context) +{ + return TRUE; +} + +static void spirv_fragment_pipe_vk_fp_free_context_data(struct wined3d_context *context) +{ + /* Nothing to do. */ +} + +static const struct wined3d_state_entry_template spirv_fragment_pipe_vk_fp_states[] = +{ + {STATE_RENDER(WINED3D_RS_SHADEMODE), {STATE_RENDER(WINED3D_RS_SHADEMODE), state_nop}}, + {STATE_RENDER(WINED3D_RS_ALPHATESTENABLE), {STATE_RENDER(WINED3D_RS_ALPHATESTENABLE), state_nop}}, + {STATE_RENDER(WINED3D_RS_ALPHAREF), {STATE_RENDER(WINED3D_RS_ALPHAREF), state_nop}}, + {STATE_RENDER(WINED3D_RS_ALPHAFUNC), {STATE_RENDER(WINED3D_RS_ALPHAFUNC), state_nop}}, + {STATE_RENDER(WINED3D_RS_FOGENABLE), {STATE_RENDER(WINED3D_RS_FOGENABLE), state_nop}}, + {STATE_RENDER(WINED3D_RS_SPECULARENABLE), {STATE_RENDER(WINED3D_RS_SPECULARENABLE), state_nop}}, + {STATE_RENDER(WINED3D_RS_FOGCOLOR), {STATE_RENDER(WINED3D_RS_FOGCOLOR), state_nop}}, + {STATE_RENDER(WINED3D_RS_FOGTABLEMODE), {STATE_RENDER(WINED3D_RS_FOGTABLEMODE), state_nop}}, + {STATE_RENDER(WINED3D_RS_FOGSTART), {STATE_RENDER(WINED3D_RS_FOGSTART), state_nop}}, + {STATE_RENDER(WINED3D_RS_FOGEND), {STATE_RENDER(WINED3D_RS_FOGEND), state_nop}}, + {STATE_RENDER(WINED3D_RS_FOGDENSITY), {STATE_RENDER(WINED3D_RS_FOGDENSITY), state_nop}}, + {STATE_RENDER(WINED3D_RS_COLORKEYENABLE), {STATE_RENDER(WINED3D_RS_COLORKEYENABLE), state_nop}}, + {STATE_RENDER(WINED3D_RS_TEXTUREFACTOR), {STATE_RENDER(WINED3D_RS_TEXTUREFACTOR), state_nop}}, + {STATE_RENDER(WINED3D_RS_FOGVERTEXMODE), {STATE_RENDER(WINED3D_RS_FOGVERTEXMODE), state_nop}}, + {STATE_RENDER(WINED3D_RS_POINTSPRITEENABLE), {STATE_RENDER(WINED3D_RS_POINTSPRITEENABLE), state_nop}}, + {STATE_RENDER(WINED3D_RS_SRGBWRITEENABLE), {STATE_RENDER(WINED3D_RS_SRGBWRITEENABLE), state_nop}}, + {STATE_POINT_ENABLE, {STATE_POINT_ENABLE, state_nop}}, + {STATE_COLOR_KEY, {STATE_COLOR_KEY, state_nop}}, + {0}, /* Terminate */ +}; + +static const struct wined3d_fragment_pipe_ops spirv_fragment_pipe_vk = +{ + .fp_enable = spirv_fragment_pipe_vk_fp_enable, + .get_caps = spirv_fragment_pipe_vk_fp_get_caps, + .get_emul_mask = spirv_fragment_pipe_vk_fp_get_emul_mask, + .alloc_private = spirv_fragment_pipe_vk_fp_alloc, + .free_private = spirv_fragment_pipe_vk_fp_free, + .allocate_context_data = spirv_fragment_pipe_vk_fp_alloc_context_data, + .free_context_data = spirv_fragment_pipe_vk_fp_free_context_data, + .color_fixup_supported = shader_spirv_color_fixup_supported, + .states = spirv_fragment_pipe_vk_fp_states, +}; + +const struct wined3d_fragment_pipe_ops *wined3d_spirv_fragment_pipe_init_vk(void) +{ + return &spirv_fragment_pipe_vk; +} + +#else + +const struct wined3d_shader_backend_ops *wined3d_spirv_shader_backend_init_vk(void) +{ + ERR_(winediag)("Wine was built without libvkd3d-shader support.\n"); + return NULL; +} + +void wined3d_spirv_shader_backend_cleanup(void) +{ +} + +const struct wined3d_vertex_pipe_ops *wined3d_spirv_vertex_pipe_init_vk(void) +{ + return &none_vertex_pipe; +} + +const struct wined3d_fragment_pipe_ops *wined3d_spirv_fragment_pipe_init_vk(void) +{ + return &none_fragment_pipe; +} + +#endif /* defined(SONAME_LIBVKD3D_SHADER) */ diff --git a/wrappers/directx/d3dwine_wrapper/state.c b/wrappers/directx/d3dwine_wrapper/state.c new file mode 100644 index 00000000000..1194604e7dc --- /dev/null +++ b/wrappers/directx/d3dwine_wrapper/state.c @@ -0,0 +1,5790 @@ +/* + * Direct3D state management + * + * Copyright 2002 Lionel Ulmer + * Copyright 2002-2005 Jason Edmeades + * Copyright 2003-2004 Raphael Junqueira + * Copyright 2004 Christian Costa + * Copyright 2005 Oliver Stieber + * Copyright 2006 Henri Verbeet + * Copyright 2006-2008 Stefan Dösinger for CodeWeavers + * Copyright 2009-2011 Henri Verbeet for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "config.h" +#include "wine/port.h" + +#include +#ifdef HAVE_FLOAT_H +# include +#endif + +#include "wined3d_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(d3d); + +ULONG CDECL wined3d_blend_state_incref(struct wined3d_blend_state *state) +{ + ULONG refcount = InterlockedIncrement(&state->refcount); + + TRACE("%p increasing refcount to %u.\n", state, refcount); + + return refcount; +} + +static void wined3d_blend_state_destroy_object(void *object) +{ + TRACE("object %p.\n", object); + + heap_free(object); +} + +ULONG CDECL wined3d_blend_state_decref(struct wined3d_blend_state *state) +{ + ULONG refcount = InterlockedDecrement(&state->refcount); + struct wined3d_device *device = state->device; + + TRACE("%p decreasing refcount to %u.\n", state, refcount); + + if (!refcount) + { + state->parent_ops->wined3d_object_destroyed(state->parent); + wined3d_cs_destroy_object(device->cs, wined3d_blend_state_destroy_object, state); + } + + return refcount; +} + +void * CDECL wined3d_blend_state_get_parent(const struct wined3d_blend_state *state) +{ + TRACE("state %p.\n", state); + + return state->parent; +} + +static BOOL is_dual_source(enum wined3d_blend state) +{ + return state >= WINED3D_BLEND_SRC1COLOR && state <= WINED3D_BLEND_INVSRC1ALPHA; +} + +HRESULT CDECL wined3d_blend_state_create(struct wined3d_device *device, + const struct wined3d_blend_state_desc *desc, void *parent, + const struct wined3d_parent_ops *parent_ops, struct wined3d_blend_state **state) +{ + struct wined3d_blend_state *object; + + TRACE("device %p, desc %p, parent %p, parent_ops %p, state %p.\n", + device, desc, parent, parent_ops, state); + + if (!(object = heap_alloc_zero(sizeof(*object)))) + return E_OUTOFMEMORY; + + object->refcount = 1; + object->desc = *desc; + object->parent = parent; + object->parent_ops = parent_ops; + object->device = device; + + object->dual_source = desc->rt[0].enable + && (is_dual_source(desc->rt[0].src) + || is_dual_source(desc->rt[0].dst) + || is_dual_source(desc->rt[0].src_alpha) + || is_dual_source(desc->rt[0].dst_alpha)); + + TRACE("Created blend state %p.\n", object); + *state = object; + + return WINED3D_OK; +} + +ULONG CDECL wined3d_depth_stencil_state_incref(struct wined3d_depth_stencil_state *state) +{ + ULONG refcount = InterlockedIncrement(&state->refcount); + + TRACE("%p increasing refcount to %u.\n", state, refcount); + + return refcount; +} + +static void wined3d_depth_stencil_state_destroy_object(void *object) +{ + TRACE("object %p.\n", object); + + heap_free(object); +} + +ULONG CDECL wined3d_depth_stencil_state_decref(struct wined3d_depth_stencil_state *state) +{ + ULONG refcount = InterlockedDecrement(&state->refcount); + struct wined3d_device *device = state->device; + + TRACE("%p decreasing refcount to %u.\n", state, refcount); + + if (!refcount) + { + state->parent_ops->wined3d_object_destroyed(state->parent); + wined3d_cs_destroy_object(device->cs, wined3d_depth_stencil_state_destroy_object, state); + } + + return refcount; +} + +void * CDECL wined3d_depth_stencil_state_get_parent(const struct wined3d_depth_stencil_state *state) +{ + TRACE("state %p.\n", state); + + return state->parent; +} + +HRESULT CDECL wined3d_depth_stencil_state_create(struct wined3d_device *device, + const struct wined3d_depth_stencil_state_desc *desc, void *parent, + const struct wined3d_parent_ops *parent_ops, struct wined3d_depth_stencil_state **state) +{ + struct wined3d_depth_stencil_state *object; + + TRACE("device %p, desc %p, parent %p, parent_ops %p, state %p.\n", + device, desc, parent, parent_ops, state); + + if (!(object = heap_alloc_zero(sizeof(*object)))) + return E_OUTOFMEMORY; + + object->refcount = 1; + object->desc = *desc; + object->parent = parent; + object->parent_ops = parent_ops; + object->device = device; + + TRACE("Created depth/stencil state %p.\n", object); + *state = object; + + return WINED3D_OK; +} + +ULONG CDECL wined3d_rasterizer_state_incref(struct wined3d_rasterizer_state *state) +{ + ULONG refcount = InterlockedIncrement(&state->refcount); + + TRACE("%p increasing refcount to %u.\n", state, refcount); + + return refcount; +} + +static void wined3d_rasterizer_state_destroy_object(void *object) +{ + TRACE("object %p.\n", object); + + heap_free(object); +} + +ULONG CDECL wined3d_rasterizer_state_decref(struct wined3d_rasterizer_state *state) +{ + ULONG refcount = InterlockedDecrement(&state->refcount); + struct wined3d_device *device = state->device; + + TRACE("%p decreasing refcount to %u.\n", state, refcount); + + if (!refcount) + { + state->parent_ops->wined3d_object_destroyed(state->parent); + wined3d_cs_destroy_object(device->cs, wined3d_rasterizer_state_destroy_object, state); + } + + return refcount; +} + +void * CDECL wined3d_rasterizer_state_get_parent(const struct wined3d_rasterizer_state *state) +{ + TRACE("rasterizer_state %p.\n", state); + + return state->parent; +} + +HRESULT CDECL wined3d_rasterizer_state_create(struct wined3d_device *device, + const struct wined3d_rasterizer_state_desc *desc, void *parent, + const struct wined3d_parent_ops *parent_ops, struct wined3d_rasterizer_state **state) +{ + struct wined3d_rasterizer_state *object; + + TRACE("device %p, desc %p, parent %p, parent_ops %p, state %p.\n", + device, desc, parent, parent_ops, state); + + if (!(object = heap_alloc_zero(sizeof(*object)))) + return E_OUTOFMEMORY; + + object->refcount = 1; + object->desc = *desc; + object->parent = parent; + object->parent_ops = parent_ops; + object->device = device; + + TRACE("Created rasterizer state %p.\n", object); + *state = object; + + return WINED3D_OK; +} + +/* Context activation for state handler is done by the caller. */ + +static void state_undefined(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + ERR("Undefined state %s (%#x).\n", debug_d3dstate(state_id), state_id); +} + +void state_nop(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + TRACE("%s: nop in current pipe config.\n", debug_d3dstate(state_id)); +} + +static void fillmode(const struct wined3d_rasterizer_state *r, const struct wined3d_gl_info *gl_info) +{ + enum wined3d_fill_mode mode = r ? r->desc.fill_mode : WINED3D_FILL_SOLID; + + switch (mode) + { + case WINED3D_FILL_POINT: + gl_info->gl_ops.gl.p_glPolygonMode(GL_FRONT_AND_BACK, GL_POINT); + checkGLcall("glPolygonMode(GL_FRONT_AND_BACK, GL_POINT)"); + break; + case WINED3D_FILL_WIREFRAME: + gl_info->gl_ops.gl.p_glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + checkGLcall("glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)"); + break; + case WINED3D_FILL_SOLID: + gl_info->gl_ops.gl.p_glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + checkGLcall("glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)"); + break; + default: + FIXME("Unrecognized fill mode %#x.\n", mode); + } +} + +static void state_lighting(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl(context)->gl_info; + + /* Lighting is not enabled if transformed vertices are drawn, but lighting + * does not affect the stream sources, so it is not grouped for + * performance reasons. This state reads the decoded vertex declaration, + * so if it is dirty don't do anything. The vertex declaration applying + * function calls this function for updating. */ + if (isStateDirty(context, STATE_VDECL)) + return; + + if (state->render_states[WINED3D_RS_LIGHTING] + && !context->stream_info.position_transformed) + { + gl_info->gl_ops.gl.p_glEnable(GL_LIGHTING); + checkGLcall("glEnable GL_LIGHTING"); + } + else + { + gl_info->gl_ops.gl.p_glDisable(GL_LIGHTING); + checkGLcall("glDisable GL_LIGHTING"); + } +} + +static void cullmode(const struct wined3d_rasterizer_state *r, const struct wined3d_gl_info *gl_info) +{ + enum wined3d_cull mode = r ? r->desc.cull_mode : WINED3D_CULL_BACK; + + /* glFrontFace() is set in context.c at context init and on an + * offscreen / onscreen rendering switch. */ + switch (mode) + { + case WINED3D_CULL_NONE: + gl_info->gl_ops.gl.p_glDisable(GL_CULL_FACE); + checkGLcall("glDisable GL_CULL_FACE"); + break; + case WINED3D_CULL_FRONT: + gl_info->gl_ops.gl.p_glEnable(GL_CULL_FACE); + checkGLcall("glEnable GL_CULL_FACE"); + gl_info->gl_ops.gl.p_glCullFace(GL_FRONT); + checkGLcall("glCullFace(GL_FRONT)"); + break; + case WINED3D_CULL_BACK: + gl_info->gl_ops.gl.p_glEnable(GL_CULL_FACE); + checkGLcall("glEnable GL_CULL_FACE"); + gl_info->gl_ops.gl.p_glCullFace(GL_BACK); + checkGLcall("glCullFace(GL_BACK)"); + break; + default: + FIXME("Unrecognized cull mode %#x.\n", mode); + } +} + +void state_shademode(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl(context)->gl_info; + + switch (state->render_states[WINED3D_RS_SHADEMODE]) + { + case WINED3D_SHADE_FLAT: + gl_info->gl_ops.gl.p_glShadeModel(GL_FLAT); + checkGLcall("glShadeModel(GL_FLAT)"); + break; + case WINED3D_SHADE_GOURAUD: + /* WINED3D_SHADE_PHONG in practice is the same as WINED3D_SHADE_GOURAUD + * in D3D. */ + case WINED3D_SHADE_PHONG: + gl_info->gl_ops.gl.p_glShadeModel(GL_SMOOTH); + checkGLcall("glShadeModel(GL_SMOOTH)"); + break; + default: + FIXME("Unrecognized shade mode %#x.\n", + state->render_states[WINED3D_RS_SHADEMODE]); + } +} + +static void state_ditherenable(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl(context)->gl_info; + + if (state->render_states[WINED3D_RS_DITHERENABLE]) + { + gl_info->gl_ops.gl.p_glEnable(GL_DITHER); + checkGLcall("glEnable GL_DITHER"); + } + else + { + gl_info->gl_ops.gl.p_glDisable(GL_DITHER); + checkGLcall("glDisable GL_DITHER"); + } +} + +GLenum wined3d_gl_compare_func(enum wined3d_cmp_func f) +{ + switch (f) + { + case WINED3D_CMP_NEVER: + return GL_NEVER; + case WINED3D_CMP_LESS: + return GL_LESS; + case WINED3D_CMP_EQUAL: + return GL_EQUAL; + case WINED3D_CMP_LESSEQUAL: + return GL_LEQUAL; + case WINED3D_CMP_GREATER: + return GL_GREATER; + case WINED3D_CMP_NOTEQUAL: + return GL_NOTEQUAL; + case WINED3D_CMP_GREATEREQUAL: + return GL_GEQUAL; + case WINED3D_CMP_ALWAYS: + return GL_ALWAYS; + default: + if (!f) + WARN("Unrecognized compare function %#x.\n", f); + else + FIXME("Unrecognized compare function %#x.\n", f); + return GL_NONE; + } +} + +static void state_ambient(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl(context)->gl_info; + struct wined3d_color color; + + wined3d_color_from_d3dcolor(&color, state->render_states[WINED3D_RS_AMBIENT]); + TRACE("Setting ambient to %s.\n", debug_color(&color)); + gl_info->gl_ops.gl.p_glLightModelfv(GL_LIGHT_MODEL_AMBIENT, &color.r); + checkGLcall("glLightModel for MODEL_AMBIENT"); +} + +static GLenum gl_blend_op(const struct wined3d_gl_info *gl_info, enum wined3d_blend_op op) +{ + switch (op) + { + case WINED3D_BLEND_OP_ADD: + return GL_FUNC_ADD; + case WINED3D_BLEND_OP_SUBTRACT: + return gl_info->supported[EXT_BLEND_SUBTRACT] ? GL_FUNC_SUBTRACT : GL_FUNC_ADD; + case WINED3D_BLEND_OP_REVSUBTRACT: + return gl_info->supported[EXT_BLEND_SUBTRACT] ? GL_FUNC_REVERSE_SUBTRACT : GL_FUNC_ADD; + case WINED3D_BLEND_OP_MIN: + return gl_info->supported[EXT_BLEND_MINMAX] ? GL_MIN : GL_FUNC_ADD; + case WINED3D_BLEND_OP_MAX: + return gl_info->supported[EXT_BLEND_MINMAX] ? GL_MAX : GL_FUNC_ADD; + default: + if (!op) + WARN("Unhandled blend op %#x.\n", op); + else + FIXME("Unhandled blend op %#x.\n", op); + return GL_FUNC_ADD; + } +} + +static void blendop(const struct wined3d_state *state, const struct wined3d_gl_info *gl_info) +{ + const struct wined3d_blend_state *b = state->blend_state; + GLenum blend_equation_alpha = GL_FUNC_ADD_EXT; + GLenum blend_equation = GL_FUNC_ADD_EXT; + + if (!gl_info->supported[WINED3D_GL_BLEND_EQUATION]) + { + WARN("Unsupported in local OpenGL implementation: glBlendEquation.\n"); + return; + } + + /* BLENDOPALPHA requires GL_EXT_blend_equation_separate, so make sure it is around */ + if (b->desc.rt[0].op_alpha && !gl_info->supported[EXT_BLEND_EQUATION_SEPARATE]) + { + WARN("Unsupported in local OpenGL implementation: glBlendEquationSeparate.\n"); + return; + } + + blend_equation = gl_blend_op(gl_info, b->desc.rt[0].op); + blend_equation_alpha = gl_blend_op(gl_info, b->desc.rt[0].op_alpha); + TRACE("blend_equation %#x, blend_equation_alpha %#x.\n", blend_equation, blend_equation_alpha); + + if (b->desc.rt[0].op != b->desc.rt[0].op_alpha) + { + GL_EXTCALL(glBlendEquationSeparate(blend_equation, blend_equation_alpha)); + checkGLcall("glBlendEquationSeparate"); + } + else + { + GL_EXTCALL(glBlendEquation(blend_equation)); + checkGLcall("glBlendEquation"); + } +} + +static GLenum gl_blend_factor(enum wined3d_blend factor, const struct wined3d_format *dst_format) +{ + switch (factor) + { + case WINED3D_BLEND_ZERO: + return GL_ZERO; + case WINED3D_BLEND_ONE: + return GL_ONE; + case WINED3D_BLEND_SRCCOLOR: + return GL_SRC_COLOR; + case WINED3D_BLEND_INVSRCCOLOR: + return GL_ONE_MINUS_SRC_COLOR; + case WINED3D_BLEND_SRCALPHA: + return GL_SRC_ALPHA; + case WINED3D_BLEND_INVSRCALPHA: + return GL_ONE_MINUS_SRC_ALPHA; + case WINED3D_BLEND_DESTCOLOR: + return GL_DST_COLOR; + case WINED3D_BLEND_INVDESTCOLOR: + return GL_ONE_MINUS_DST_COLOR; + /* To compensate for the lack of format switching with backbuffer + * offscreen rendering, and with onscreen rendering, we modify the + * alpha test parameters for (INV)DESTALPHA if the render target + * doesn't support alpha blending. A nonexistent alpha channel + * returns 1.0, so WINED3D_BLEND_DESTALPHA becomes GL_ONE, and + * WINED3D_BLEND_INVDESTALPHA becomes GL_ZERO. */ + case WINED3D_BLEND_DESTALPHA: + return dst_format->alpha_size ? GL_DST_ALPHA : GL_ONE; + case WINED3D_BLEND_INVDESTALPHA: + return dst_format->alpha_size ? GL_ONE_MINUS_DST_ALPHA : GL_ZERO; + case WINED3D_BLEND_SRCALPHASAT: + return GL_SRC_ALPHA_SATURATE; + case WINED3D_BLEND_BLENDFACTOR: + return GL_CONSTANT_COLOR_EXT; + case WINED3D_BLEND_INVBLENDFACTOR: + return GL_ONE_MINUS_CONSTANT_COLOR_EXT; + case WINED3D_BLEND_SRC1COLOR: + return GL_SRC1_COLOR; + case WINED3D_BLEND_INVSRC1COLOR: + return GL_ONE_MINUS_SRC1_COLOR; + case WINED3D_BLEND_SRC1ALPHA: + return GL_SRC1_ALPHA; + case WINED3D_BLEND_INVSRC1ALPHA: + return GL_ONE_MINUS_SRC1_ALPHA; + default: + if (!factor) + WARN("Unhandled blend factor %#x.\n", factor); + else + FIXME("Unhandled blend factor %#x.\n", factor); + return GL_NONE; + } +} + +static void gl_blend_from_d3d(GLenum *src_blend, GLenum *dst_blend, + enum wined3d_blend d3d_src_blend, enum wined3d_blend d3d_dst_blend, + const struct wined3d_format *rt_format) +{ + /* WINED3D_BLEND_BOTHSRCALPHA and WINED3D_BLEND_BOTHINVSRCALPHA are legacy + * source blending values which are still valid up to d3d9. They should + * not occur as dest blend values. */ + if (d3d_src_blend == WINED3D_BLEND_BOTHSRCALPHA) + { + *src_blend = GL_SRC_ALPHA; + *dst_blend = GL_ONE_MINUS_SRC_ALPHA; + } + else if (d3d_src_blend == WINED3D_BLEND_BOTHINVSRCALPHA) + { + *src_blend = GL_ONE_MINUS_SRC_ALPHA; + *dst_blend = GL_SRC_ALPHA; + } + else + { + *src_blend = gl_blend_factor(d3d_src_blend, rt_format); + *dst_blend = gl_blend_factor(d3d_dst_blend, rt_format); + } +} + +static void state_blend_factor_w(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + WARN("Unsupported in local OpenGL implementation: glBlendColor.\n"); +} + +static void state_blend_factor(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl(context)->gl_info; + const struct wined3d_color *factor = &state->blend_factor; + + TRACE("Setting blend factor to %s.\n", debug_color(factor)); + + GL_EXTCALL(glBlendColor(factor->r, factor->g, factor->b, factor->a)); + checkGLcall("glBlendColor"); +} + +static BOOL is_blend_enabled(struct wined3d_context *context, const struct wined3d_state *state, UINT index) +{ + const struct wined3d_blend_state *b = state->blend_state; + + if (!state->fb.render_targets[index]) + return FALSE; + + if (!b->desc.rt[index].enable) + return FALSE; + + /* Disable blending in all cases even without pixel shaders. + * With blending on we could face a big performance penalty. + * The d3d9 visual test confirms the behavior. */ + if (context->render_offscreen + && !(state->fb.render_targets[index]->format_flags & WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING)) + return FALSE; + + return TRUE; +} + +static void blend(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl(context)->gl_info; + const struct wined3d_blend_state *b = state->blend_state; + const struct wined3d_format *rt_format; + GLenum src_blend, dst_blend; + unsigned int mask; + + if (gl_info->supported[ARB_MULTISAMPLE]) + { + if (b && b->desc.alpha_to_coverage) + gl_info->gl_ops.gl.p_glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE); + else + gl_info->gl_ops.gl.p_glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE); + checkGLcall("glEnable GL_SAMPLE_ALPHA_TO_COVERAGE"); + } + + if (b && b->desc.independent) + WARN("Independent blend is not supported by this GL implementation.\n"); + + mask = b ? b->desc.rt[0].writemask : 0xf; + + gl_info->gl_ops.gl.p_glColorMask(mask & WINED3DCOLORWRITEENABLE_RED ? GL_TRUE : GL_FALSE, + mask & WINED3DCOLORWRITEENABLE_GREEN ? GL_TRUE : GL_FALSE, + mask & WINED3DCOLORWRITEENABLE_BLUE ? GL_TRUE : GL_FALSE, + mask & WINED3DCOLORWRITEENABLE_ALPHA ? GL_TRUE : GL_FALSE); + checkGLcall("glColorMask"); + + if (!b || !is_blend_enabled(context, state, 0)) + { + gl_info->gl_ops.gl.p_glDisable(GL_BLEND); + checkGLcall("glDisable GL_BLEND"); + return; + } + + gl_info->gl_ops.gl.p_glEnable(GL_BLEND); + checkGLcall("glEnable GL_BLEND"); + + rt_format = state->fb.render_targets[0]->format; + + gl_blend_from_d3d(&src_blend, &dst_blend, b->desc.rt[0].src, b->desc.rt[0].dst, rt_format); + + blendop(state, gl_info); + + if (b->desc.rt[0].src != b->desc.rt[0].src_alpha || b->desc.rt[0].dst != b->desc.rt[0].dst_alpha) + { + GLenum src_blend_alpha, dst_blend_alpha; + + /* Separate alpha blending requires GL_EXT_blend_function_separate, so make sure it is around */ + if (!gl_info->supported[EXT_BLEND_FUNC_SEPARATE]) + { + WARN("Unsupported in local OpenGL implementation: glBlendFuncSeparate.\n"); + return; + } + + gl_blend_from_d3d(&src_blend_alpha, &dst_blend_alpha, b->desc.rt[0].src_alpha, b->desc.rt[0].dst_alpha, rt_format); + + GL_EXTCALL(glBlendFuncSeparate(src_blend, dst_blend, src_blend_alpha, dst_blend_alpha)); + checkGLcall("glBlendFuncSeparate"); + } + else + { + TRACE("glBlendFunc src=%x, dst=%x.\n", src_blend, dst_blend); + gl_info->gl_ops.gl.p_glBlendFunc(src_blend, dst_blend); + checkGLcall("glBlendFunc"); + } + + /* Colorkey fixup for stage 0 alphaop depends on blend state, so it may need + * updating. */ + if (state->render_states[WINED3D_RS_COLORKEYENABLE]) + context_apply_state(context, state, STATE_TEXTURESTAGE(0, WINED3D_TSS_ALPHA_OP)); +} + +static void set_color_mask(const struct wined3d_gl_info *gl_info, UINT index, DWORD mask) +{ + GL_EXTCALL(glColorMaski(index, + mask & WINED3DCOLORWRITEENABLE_RED ? GL_TRUE : GL_FALSE, + mask & WINED3DCOLORWRITEENABLE_GREEN ? GL_TRUE : GL_FALSE, + mask & WINED3DCOLORWRITEENABLE_BLUE ? GL_TRUE : GL_FALSE, + mask & WINED3DCOLORWRITEENABLE_ALPHA ? GL_TRUE : GL_FALSE)); + checkGLcall("glColorMaski"); +} + +static void blend_db2(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl(context)->gl_info; + GLenum src_blend, dst_blend, src_blend_alpha, dst_blend_alpha; + const struct wined3d_blend_state *b = state->blend_state; + const struct wined3d_format *rt_format; + BOOL dual_source = b && b->dual_source; + unsigned int i; + + if (b && b->desc.alpha_to_coverage) + gl_info->gl_ops.gl.p_glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE); + else + gl_info->gl_ops.gl.p_glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE); + checkGLcall("glEnable GL_SAMPLE_ALPHA_TO_COVERAGE"); + + if (context->last_was_dual_source_blend != dual_source) + { + /* Dual source blending changes the location of the output varyings. */ + context->shader_update_mask |= 1u << WINED3D_SHADER_TYPE_PIXEL; + context->last_was_dual_source_blend = dual_source; + } + + if (!b || !b->desc.independent) + { + blend(context, state, state_id); + return; + } + + rt_format = state->fb.render_targets[0]->format; + gl_blend_from_d3d(&src_blend, &dst_blend, b->desc.rt[0].src, b->desc.rt[0].dst, rt_format); + gl_blend_from_d3d(&src_blend_alpha, &dst_blend_alpha, b->desc.rt[0].src_alpha, b->desc.rt[0].dst_alpha, rt_format); + + GL_EXTCALL(glBlendFuncSeparate(src_blend, dst_blend, src_blend_alpha, dst_blend_alpha)); + checkGLcall("glBlendFuncSeparate"); + + GL_EXTCALL(glBlendEquationSeparate(gl_blend_op(gl_info, b->desc.rt[0].op), + gl_blend_op(gl_info, b->desc.rt[0].op_alpha))); + checkGLcall("glBlendEquationSeparate"); + + for (i = 0; i < WINED3D_MAX_RENDER_TARGETS; ++i) + { + set_color_mask(gl_info, i, b->desc.rt[i].writemask); + + if (!is_blend_enabled(context, state, i)) + { + GL_EXTCALL(glDisablei(GL_BLEND, i)); + checkGLcall("glDisablei GL_BLEND"); + continue; + } + + GL_EXTCALL(glEnablei(GL_BLEND, i)); + checkGLcall("glEnablei GL_BLEND"); + + if (b->desc.rt[i].src != b->desc.rt[0].src + || b->desc.rt[i].dst != b->desc.rt[0].dst + || b->desc.rt[i].op != b->desc.rt[0].op + || b->desc.rt[i].src_alpha != b->desc.rt[0].src_alpha + || b->desc.rt[i].dst_alpha != b->desc.rt[0].dst_alpha + || b->desc.rt[i].op_alpha != b->desc.rt[0].op_alpha) + WARN("Independent blend equations and blend functions are not supported by this GL implementation.\n"); + } + + /* Colorkey fixup for stage 0 alphaop depends on blend state, so it may need + * updating. */ + if (state->render_states[WINED3D_RS_COLORKEYENABLE]) + context_apply_state(context, state, STATE_TEXTURESTAGE(0, WINED3D_TSS_ALPHA_OP)); +} + +static void blend_dbb(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl(context)->gl_info; + const struct wined3d_blend_state *b = state->blend_state; + BOOL dual_source = b && b->dual_source; + unsigned int i; + + if (b && b->desc.alpha_to_coverage) + gl_info->gl_ops.gl.p_glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE); + else + gl_info->gl_ops.gl.p_glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE); + checkGLcall("glEnable GL_SAMPLE_ALPHA_TO_COVERAGE"); + + if (context->last_was_dual_source_blend != dual_source) + { + /* Dual source blending changes the location of the output varyings. */ + context->shader_update_mask |= 1u << WINED3D_SHADER_TYPE_PIXEL; + context->last_was_dual_source_blend = dual_source; + } + + if (!b || !b->desc.independent) + { + blend(context, state, state_id); + return; + } + + for (i = 0; i < WINED3D_MAX_RENDER_TARGETS; ++i) + { + GLenum src_blend, dst_blend, src_blend_alpha, dst_blend_alpha; + const struct wined3d_format *rt_format; + + set_color_mask(gl_info, i, b->desc.rt[i].writemask); + + if (!is_blend_enabled(context, state, i)) + { + GL_EXTCALL(glDisablei(GL_BLEND, i)); + checkGLcall("glDisablei GL_BLEND"); + continue; + } + + GL_EXTCALL(glEnablei(GL_BLEND, i)); + checkGLcall("glEnablei GL_BLEND"); + + rt_format = state->fb.render_targets[i]->format; + gl_blend_from_d3d(&src_blend, &dst_blend, b->desc.rt[i].src, b->desc.rt[i].dst, rt_format); + gl_blend_from_d3d(&src_blend_alpha, &dst_blend_alpha, + b->desc.rt[i].src_alpha, b->desc.rt[i].dst_alpha, rt_format); + + GL_EXTCALL(glBlendFuncSeparatei(i, src_blend, dst_blend, src_blend_alpha, dst_blend_alpha)); + checkGLcall("glBlendFuncSeparatei"); + + GL_EXTCALL(glBlendEquationSeparatei(i, gl_blend_op(gl_info, b->desc.rt[i].op), + gl_blend_op(gl_info, b->desc.rt[i].op_alpha))); + checkGLcall("glBlendEquationSeparatei"); + } + + /* Colorkey fixup for stage 0 alphaop depends on blend state, so it may need + * updating. */ + if (state->render_states[WINED3D_RS_COLORKEYENABLE]) + context_apply_state(context, state, STATE_TEXTURESTAGE(0, WINED3D_TSS_ALPHA_OP)); +} + +void state_alpha_test(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl(context)->gl_info; + int glParm = 0; + float ref; + BOOL enable_ckey = FALSE; + + TRACE("context %p, state %p, state_id %#x.\n", context, state, state_id); + + /* Find out if the texture on the first stage has a ckey set. The alpha + * state func reads the texture settings, even though alpha and texture + * are not grouped together. This is to avoid making a huge alpha + + * texture + texture stage + ckey block due to the hardly used + * WINED3D_RS_COLORKEYENABLE state(which is d3d <= 3 only). The texture + * function will call alpha in case it finds some texture + colorkeyenable + * combination which needs extra care. */ + if (state->textures[0] && (state->textures[0]->async.color_key_flags & WINED3D_CKEY_SRC_BLT)) + enable_ckey = TRUE; + + if (enable_ckey || context->last_was_ckey) + context_apply_state(context, state, STATE_TEXTURESTAGE(0, WINED3D_TSS_ALPHA_OP)); + context->last_was_ckey = enable_ckey; + + if (state->render_states[WINED3D_RS_ALPHATESTENABLE] + || (state->render_states[WINED3D_RS_COLORKEYENABLE] && enable_ckey)) + { + gl_info->gl_ops.gl.p_glEnable(GL_ALPHA_TEST); + checkGLcall("glEnable GL_ALPHA_TEST"); + } + else + { + gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST); + checkGLcall("glDisable GL_ALPHA_TEST"); + /* Alpha test is disabled, don't bother setting the params - it will happen on the next + * enable call + */ + return; + } + + if (state->render_states[WINED3D_RS_COLORKEYENABLE] && enable_ckey) + { + glParm = GL_NOTEQUAL; + ref = 0.0f; + } + else + { + ref = wined3d_alpha_ref(state); + glParm = wined3d_gl_compare_func(state->render_states[WINED3D_RS_ALPHAFUNC]); + } + if (glParm) + { + gl_info->gl_ops.gl.p_glAlphaFunc(glParm, ref); + checkGLcall("glAlphaFunc"); + } +} + +void state_clipping(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + uint32_t enable_mask; + + if (use_vs(state) && !context->d3d_info->vs_clipping) + { + static BOOL warned; + + /* The OpenGL spec says that clipping planes are disabled when using + * shaders. Direct3D planes aren't, so that is an issue. The MacOS ATI + * driver keeps clipping planes activated with shaders in some + * conditions I got sick of tracking down. The shader state handler + * disables all clip planes because of that - don't do anything here + * and keep them disabled. */ + if (state->render_states[WINED3D_RS_CLIPPLANEENABLE] && !warned++) + FIXME("Clipping not supported with vertex shaders.\n"); + return; + } + + /* glEnable(GL_CLIP_PLANEx) doesn't apply to (ARB backend) vertex shaders. + * The enabled / disabled planes are hardcoded into the shader. Update the + * shader to update the enabled clipplanes. In case of fixed function, we + * need to update the clipping field from ffp_vertex_settings. */ + context->shader_update_mask |= 1u << WINED3D_SHADER_TYPE_VERTEX; + + /* If enabling / disabling all + * TODO: Is this correct? Doesn't D3DRS_CLIPPING disable clipping on the viewport frustrum? + */ + enable_mask = state->render_states[WINED3D_RS_CLIPPING] ? + state->render_states[WINED3D_RS_CLIPPLANEENABLE] : 0; + wined3d_context_gl_enable_clip_distances(context_gl, enable_mask); +} + +static void state_specularenable(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl(context)->gl_info; + + /* Originally this used glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL,GL_SEPARATE_SPECULAR_COLOR) + * and (GL_LIGHT_MODEL_COLOR_CONTROL,GL_SINGLE_COLOR) to swap between enabled/disabled + * specular color. This is wrong: + * Separate specular color means the specular colour is maintained separately, whereas + * single color means it is merged in. However in both cases they are being used to + * some extent. + * To disable specular color, set it explicitly to black and turn off GL_COLOR_SUM_EXT + * NOTE: If not supported don't give FIXMEs the impact is really minimal and very few people are + * running 1.4 yet! + * + * + * If register combiners are enabled, enabling / disabling GL_COLOR_SUM has no effect. + * Instead, we need to setup the FinalCombiner properly. + * + * The default setup for the FinalCombiner is: + * + * + * GL_VARIABLE_A_NV GL_FOG, GL_UNSIGNED_IDENTITY_NV GL_ALPHA + * GL_VARIABLE_B_NV GL_SPARE0_PLUS_SECONDARY_COLOR_NV GL_UNSIGNED_IDENTITY_NV GL_RGB + * GL_VARIABLE_C_NV GL_FOG GL_UNSIGNED_IDENTITY_NV GL_RGB + * GL_VARIABLE_D_NV GL_ZERO GL_UNSIGNED_IDENTITY_NV GL_RGB + * GL_VARIABLE_E_NV GL_ZERO GL_UNSIGNED_IDENTITY_NV GL_RGB + * GL_VARIABLE_F_NV GL_ZERO GL_UNSIGNED_IDENTITY_NV GL_RGB + * GL_VARIABLE_G_NV GL_SPARE0_NV GL_UNSIGNED_IDENTITY_NV GL_ALPHA + * + * That's pretty much fine as it is, except for variable B, which needs to take + * either GL_SPARE0_PLUS_SECONDARY_COLOR_NV or GL_SPARE0_NV, depending on + * whether WINED3D_RS_SPECULARENABLE is enabled or not. + */ + + TRACE("Setting specular enable state and materials\n"); + if (state->render_states[WINED3D_RS_SPECULARENABLE]) + { + gl_info->gl_ops.gl.p_glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, (float *)&state->material.specular); + checkGLcall("glMaterialfv"); + + if (state->material.power > gl_info->limits.shininess) + { + /* glMaterialf man page says that the material says that GL_SHININESS must be between 0.0 + * and 128.0, although in d3d neither -1 nor 129 produce an error. GL_NV_max_light_exponent + * allows bigger values. If the extension is supported, gl_info->limits.shininess contains the + * value reported by the extension, otherwise 128. For values > gl_info->limits.shininess clamp + * them, it should be safe to do so without major visual distortions. + */ + WARN("Material power = %.8e, limit %.8e\n", state->material.power, gl_info->limits.shininess); + gl_info->gl_ops.gl.p_glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, gl_info->limits.shininess); + } + else + { + gl_info->gl_ops.gl.p_glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, state->material.power); + } + checkGLcall("glMaterialf(GL_SHININESS)"); + + if (gl_info->supported[EXT_SECONDARY_COLOR]) + gl_info->gl_ops.gl.p_glEnable(GL_COLOR_SUM_EXT); + else + TRACE("Specular colors cannot be enabled in this version of opengl\n"); + checkGLcall("glEnable(GL_COLOR_SUM)"); + + if (gl_info->supported[NV_REGISTER_COMBINERS]) + { + GL_EXTCALL(glFinalCombinerInputNV(GL_VARIABLE_B_NV, GL_SPARE0_PLUS_SECONDARY_COLOR_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB)); + checkGLcall("glFinalCombinerInputNV()"); + } + } else { + static const GLfloat black[] = {0.0f, 0.0f, 0.0f, 0.0f}; + + /* for the case of enabled lighting: */ + gl_info->gl_ops.gl.p_glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, &black[0]); + checkGLcall("glMaterialfv"); + + /* for the case of disabled lighting: */ + if (gl_info->supported[EXT_SECONDARY_COLOR]) + gl_info->gl_ops.gl.p_glDisable(GL_COLOR_SUM_EXT); + else + TRACE("Specular colors cannot be disabled in this version of opengl\n"); + checkGLcall("glDisable(GL_COLOR_SUM)"); + + if (gl_info->supported[NV_REGISTER_COMBINERS]) + { + GL_EXTCALL(glFinalCombinerInputNV(GL_VARIABLE_B_NV, GL_SPARE0_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB)); + checkGLcall("glFinalCombinerInputNV()"); + } + } + + TRACE("diffuse %s\n", debug_color(&state->material.diffuse)); + TRACE("ambient %s\n", debug_color(&state->material.ambient)); + TRACE("specular %s\n", debug_color(&state->material.specular)); + TRACE("emissive %s\n", debug_color(&state->material.emissive)); + + gl_info->gl_ops.gl.p_glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, (float *)&state->material.ambient); + checkGLcall("glMaterialfv(GL_AMBIENT)"); + gl_info->gl_ops.gl.p_glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, (float *)&state->material.diffuse); + checkGLcall("glMaterialfv(GL_DIFFUSE)"); + gl_info->gl_ops.gl.p_glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, (float *)&state->material.emissive); + checkGLcall("glMaterialfv(GL_EMISSION)"); +} + +static void state_texfactor(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + struct wined3d_color color; + unsigned int i; + + /* Note the texture color applies to all textures whereas + * GL_TEXTURE_ENV_COLOR applies to active only. */ + wined3d_color_from_d3dcolor(&color, state->render_states[WINED3D_RS_TEXTUREFACTOR]); + + /* And now the default texture color as well */ + for (i = 0; i < context->d3d_info->limits.ffp_blend_stages; ++i) + { + /* Note the WINED3D_RS value applies to all textures, but GL has one + * per texture, so apply it now ready to be used! */ + wined3d_context_gl_active_texture(context_gl, gl_info, i); + + gl_info->gl_ops.gl.p_glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, &color.r); + checkGLcall("glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color);"); + } +} + +static void renderstate_stencil_twosided(struct wined3d_context *context, GLint face, + GLint func, GLint ref, GLuint mask, GLint stencilFail, GLint depthFail, GLint stencilPass) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl(context)->gl_info; + + gl_info->gl_ops.gl.p_glEnable(GL_STENCIL_TEST_TWO_SIDE_EXT); + checkGLcall("glEnable(GL_STENCIL_TEST_TWO_SIDE_EXT)"); + GL_EXTCALL(glActiveStencilFaceEXT(face)); + checkGLcall("glActiveStencilFaceEXT(...)"); + gl_info->gl_ops.gl.p_glStencilFunc(func, ref, mask); + checkGLcall("glStencilFunc(...)"); + gl_info->gl_ops.gl.p_glStencilOp(stencilFail, depthFail, stencilPass); + checkGLcall("glStencilOp(...)"); +} + +static GLenum gl_stencil_op(enum wined3d_stencil_op op) +{ + switch (op) + { + case WINED3D_STENCIL_OP_KEEP: + return GL_KEEP; + case WINED3D_STENCIL_OP_ZERO: + return GL_ZERO; + case WINED3D_STENCIL_OP_REPLACE: + return GL_REPLACE; + case WINED3D_STENCIL_OP_INCR_SAT: + return GL_INCR; + case WINED3D_STENCIL_OP_DECR_SAT: + return GL_DECR; + case WINED3D_STENCIL_OP_INVERT: + return GL_INVERT; + case WINED3D_STENCIL_OP_INCR: + return GL_INCR_WRAP; + case WINED3D_STENCIL_OP_DECR: + return GL_DECR_WRAP; + default: + if (!op) + WARN("Unrecognized stencil op %#x.\n", op); + else + FIXME("Unrecognized stencil op %#x.\n", op); + return GL_KEEP; + } +} + +static void state_stencil(struct wined3d_context *context, const struct wined3d_state *state) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl(context)->gl_info; + const struct wined3d_depth_stencil_state *d = state->depth_stencil_state; + GLint func; + GLint func_back; + GLint ref; + GLuint mask; + GLint stencilFail; + GLint stencilFail_back; + GLint stencilPass; + GLint stencilPass_back; + GLint depthFail; + GLint depthFail_back; + + /* No stencil test without a stencil buffer. */ + if (!state->fb.depth_stencil || !d || !d->desc.stencil) + { + gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST); + checkGLcall("glDisable GL_STENCIL_TEST"); + return; + } + + if (!(func = wined3d_gl_compare_func(d->desc.front.func))) + func = GL_ALWAYS; + if (!(func_back = wined3d_gl_compare_func(d->desc.back.func))) + func_back = GL_ALWAYS; + mask = d->desc.stencil_read_mask; + ref = state->stencil_ref & ((1 << state->fb.depth_stencil->format->stencil_size) - 1); + stencilFail = gl_stencil_op(d->desc.front.fail_op); + depthFail = gl_stencil_op(d->desc.front.depth_fail_op); + stencilPass = gl_stencil_op(d->desc.front.pass_op); + stencilFail_back = gl_stencil_op(d->desc.back.fail_op); + depthFail_back = gl_stencil_op(d->desc.back.depth_fail_op); + stencilPass_back = gl_stencil_op(d->desc.back.pass_op); + + TRACE("(ref %x, mask %x, " + "GL_FRONT: func: %x, fail %x, zfail %x, zpass %x " + "GL_BACK: func: %x, fail %x, zfail %x, zpass %x)\n", + ref, mask, + func, stencilFail, depthFail, stencilPass, + func_back, stencilFail_back, depthFail_back, stencilPass_back); + + if (memcmp(&d->desc.front, &d->desc.back, sizeof(d->desc.front))) + { + gl_info->gl_ops.gl.p_glEnable(GL_STENCIL_TEST); + checkGLcall("glEnable GL_STENCIL_TEST"); + + if (gl_info->supported[WINED3D_GL_VERSION_2_0]) + { + GL_EXTCALL(glStencilFuncSeparate(GL_FRONT, func, ref, mask)); + GL_EXTCALL(glStencilOpSeparate(GL_FRONT, stencilFail, depthFail, stencilPass)); + GL_EXTCALL(glStencilFuncSeparate(GL_BACK, func_back, ref, mask)); + GL_EXTCALL(glStencilOpSeparate(GL_BACK, stencilFail_back, depthFail_back, stencilPass_back)); + checkGLcall("setting two sided stencil state"); + } + else if (gl_info->supported[EXT_STENCIL_TWO_SIDE]) + { + /* Apply back first, then front. This function calls glActiveStencilFaceEXT, + * which has an effect on the code below too. If we apply the front face + * afterwards, we are sure that the active stencil face is set to front, + * and other stencil functions which do not use two sided stencil do not have + * to set it back + */ + renderstate_stencil_twosided(context, GL_BACK, + func_back, ref, mask, stencilFail_back, depthFail_back, stencilPass_back); + renderstate_stencil_twosided(context, GL_FRONT, + func, ref, mask, stencilFail, depthFail, stencilPass); + } + else if (gl_info->supported[ATI_SEPARATE_STENCIL]) + { + GL_EXTCALL(glStencilFuncSeparateATI(func, func_back, ref, mask)); + checkGLcall("glStencilFuncSeparateATI(...)"); + GL_EXTCALL(glStencilOpSeparateATI(GL_FRONT, stencilFail, depthFail, stencilPass)); + checkGLcall("glStencilOpSeparateATI(GL_FRONT, ...)"); + GL_EXTCALL(glStencilOpSeparateATI(GL_BACK, stencilFail_back, depthFail_back, stencilPass_back)); + checkGLcall("glStencilOpSeparateATI(GL_BACK, ...)"); + } + else + { + FIXME("Separate (two sided) stencil not supported on this version of OpenGL. Caps weren't honored?\n"); + } + } + else + { + if (gl_info->supported[EXT_STENCIL_TWO_SIDE]) + { + gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT); + checkGLcall("glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT)"); + } + + /* This code disables the ATI extension as well, since the standard stencil functions are equal + * to calling the ATI functions with GL_FRONT_AND_BACK as face parameter + */ + gl_info->gl_ops.gl.p_glEnable(GL_STENCIL_TEST); + checkGLcall("glEnable GL_STENCIL_TEST"); + gl_info->gl_ops.gl.p_glStencilFunc(func, ref, mask); + checkGLcall("glStencilFunc(...)"); + gl_info->gl_ops.gl.p_glStencilOp(stencilFail, depthFail, stencilPass); + checkGLcall("glStencilOp(...)"); + } +} + +static void depth(struct wined3d_context *context, const struct wined3d_state *state) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl(context)->gl_info; + const struct wined3d_depth_stencil_state *d = state->depth_stencil_state; + BOOL enable_depth = d ? d->desc.depth : TRUE; + GLenum depth_func = GL_LESS; + + if (!state->fb.depth_stencil) + { + TRACE("No depth/stencil buffer is attached; disabling depth test.\n"); + enable_depth = FALSE; + } + + if (enable_depth) + { + gl_info->gl_ops.gl.p_glEnable(GL_DEPTH_TEST); + checkGLcall("glEnable GL_DEPTH_TEST"); + } + else + { + gl_info->gl_ops.gl.p_glDisable(GL_DEPTH_TEST); + checkGLcall("glDisable GL_DEPTH_TEST"); + } + + if (!d || d->desc.depth_write) + { + gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE); + checkGLcall("glDepthMask(GL_TRUE)"); + } + else + { + gl_info->gl_ops.gl.p_glDepthMask(GL_FALSE); + checkGLcall("glDepthMask(GL_FALSE)"); + } + + if (d) + depth_func = wined3d_gl_compare_func(d->desc.depth_func); + if (depth_func) + { + gl_info->gl_ops.gl.p_glDepthFunc(depth_func); + checkGLcall("glDepthFunc"); + } + + if (context->last_was_rhw && !isStateDirty(context, STATE_TRANSFORM(WINED3D_TS_PROJECTION))) + context_apply_state(context, state, STATE_TRANSFORM(WINED3D_TS_PROJECTION)); +} + +static void depth_stencil(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl(context)->gl_info; + const struct wined3d_depth_stencil_state *d = state->depth_stencil_state; + GLuint stencil_write_mask = 0; + + depth(context, state); + state_stencil(context, state); + + if (state->fb.depth_stencil) + stencil_write_mask = d ? d->desc.stencil_write_mask : ~0u; + + gl_info->gl_ops.gl.p_glStencilMask(stencil_write_mask); + checkGLcall("glStencilMask"); +} + +static void depth_stencil_2s(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl(context)->gl_info; + const struct wined3d_depth_stencil_state *d = state->depth_stencil_state; + GLuint stencil_write_mask = 0; + + depth(context, state); + state_stencil(context, state); + + if (state->fb.depth_stencil) + stencil_write_mask = d ? d->desc.stencil_write_mask : ~0u; + + GL_EXTCALL(glActiveStencilFaceEXT(GL_BACK)); + checkGLcall("glActiveStencilFaceEXT(GL_BACK)"); + gl_info->gl_ops.gl.p_glStencilMask(stencil_write_mask); + checkGLcall("glStencilMask"); + GL_EXTCALL(glActiveStencilFaceEXT(GL_FRONT)); + checkGLcall("glActiveStencilFaceEXT(GL_FRONT)"); + gl_info->gl_ops.gl.p_glStencilMask(stencil_write_mask); +} + +static void state_fog_vertexpart(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl(context)->gl_info; + + TRACE("context %p, state %p, state_id %#x.\n", context, state, state_id); + + if (!state->render_states[WINED3D_RS_FOGENABLE]) + return; + + /* Table fog on: Never use fog coords, and use per-fragment fog */ + if (state->render_states[WINED3D_RS_FOGTABLEMODE] != WINED3D_FOG_NONE) + { + gl_info->gl_ops.gl.p_glHint(GL_FOG_HINT, GL_NICEST); + if (context->fog_coord) + { + gl_info->gl_ops.gl.p_glFogi(GL_FOG_COORDINATE_SOURCE_EXT, GL_FRAGMENT_DEPTH_EXT); + checkGLcall("glFogi(GL_FOG_COORDINATE_SOURCE_EXT, GL_FRAGMENT_DEPTH_EXT)"); + context->fog_coord = FALSE; + } + + /* Range fog is only used with per-vertex fog in d3d */ + if (gl_info->supported[NV_FOG_DISTANCE]) + { + gl_info->gl_ops.gl.p_glFogi(GL_FOG_DISTANCE_MODE_NV, GL_EYE_PLANE_ABSOLUTE_NV); + checkGLcall("glFogi(GL_FOG_DISTANCE_MODE_NV, GL_EYE_PLANE_ABSOLUTE_NV)"); + } + return; + } + + /* Otherwise use per-vertex fog in any case */ + gl_info->gl_ops.gl.p_glHint(GL_FOG_HINT, GL_FASTEST); + + if (state->render_states[WINED3D_RS_FOGVERTEXMODE] == WINED3D_FOG_NONE || context->last_was_rhw) + { + /* No fog at all, or transformed vertices: Use fog coord */ + if (!context->fog_coord) + { + gl_info->gl_ops.gl.p_glFogi(GL_FOG_COORDINATE_SOURCE_EXT, GL_FOG_COORDINATE_EXT); + checkGLcall("glFogi(GL_FOG_COORDINATE_SOURCE_EXT, GL_FOG_COORDINATE_EXT)"); + context->fog_coord = TRUE; + } + } + else + { + /* Otherwise, use the fragment depth */ + if (context->fog_coord) + { + gl_info->gl_ops.gl.p_glFogi(GL_FOG_COORDINATE_SOURCE_EXT, GL_FRAGMENT_DEPTH_EXT); + checkGLcall("glFogi(GL_FOG_COORDINATE_SOURCE_EXT, GL_FRAGMENT_DEPTH_EXT)"); + context->fog_coord = FALSE; + } + + if (state->render_states[WINED3D_RS_RANGEFOGENABLE]) + { + if (gl_info->supported[NV_FOG_DISTANCE]) + { + gl_info->gl_ops.gl.p_glFogi(GL_FOG_DISTANCE_MODE_NV, GL_EYE_RADIAL_NV); + checkGLcall("glFogi(GL_FOG_DISTANCE_MODE_NV, GL_EYE_RADIAL_NV)"); + } + else + { + WARN("Range fog enabled, but not supported by this GL implementation.\n"); + } + } + else if (gl_info->supported[NV_FOG_DISTANCE]) + { + gl_info->gl_ops.gl.p_glFogi(GL_FOG_DISTANCE_MODE_NV, GL_EYE_PLANE_ABSOLUTE_NV); + checkGLcall("glFogi(GL_FOG_DISTANCE_MODE_NV, GL_EYE_PLANE_ABSOLUTE_NV)"); + } + } +} + +void state_fogstartend(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl(context)->gl_info; + float fogstart, fogend; + + get_fog_start_end(context, state, &fogstart, &fogend); + + gl_info->gl_ops.gl.p_glFogf(GL_FOG_START, fogstart); + checkGLcall("glFogf(GL_FOG_START, fogstart)"); + TRACE("Fog Start == %f\n", fogstart); + + gl_info->gl_ops.gl.p_glFogf(GL_FOG_END, fogend); + checkGLcall("glFogf(GL_FOG_END, fogend)"); + TRACE("Fog End == %f\n", fogend); +} + +void state_fog_fragpart(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl(context)->gl_info; + enum fogsource new_source; + DWORD fogstart = state->render_states[WINED3D_RS_FOGSTART]; + DWORD fogend = state->render_states[WINED3D_RS_FOGEND]; + + TRACE("context %p, state %p, state_id %#x.\n", context, state, state_id); + + if (!state->render_states[WINED3D_RS_FOGENABLE]) + { + /* No fog? Disable it, and we're done :-) */ + gl_info->p_glDisableWINE(GL_FOG); + checkGLcall("glDisable GL_FOG"); + return; + } + + /* Fog Rules: + * + * With fixed function vertex processing, Direct3D knows 2 different fog input sources. + * It can use the Z value of the vertex, or the alpha component of the specular color. + * This depends on the fog vertex, fog table and the vertex declaration. If the Z value + * is used, fogstart, fogend and the equation type are used, otherwise linear fog with + * start = 255, end = 0 is used. Obviously the msdn is not very clear on that. + * + * FOGTABLEMODE != NONE: + * The Z value is used, with the equation specified, no matter what vertex type. + * + * FOGTABLEMODE == NONE, FOGVERTEXMODE != NONE, untransformed: + * Per vertex fog is calculated using the specified fog equation and the parameters + * + * FOGTABLEMODE == NONE, FOGVERTEXMODE != NONE, transformed, OR + * FOGTABLEMODE == NONE, FOGVERTEXMODE == NONE, untransformed: + * Linear fog with start = 255.0, end = 0.0, input comes from the specular color + * + * + * Rules for vertex fog with shaders: + * + * When mixing fixed function functionality with the programmable pipeline, D3D expects + * the fog computation to happen during transformation while openGL expects it to happen + * during rasterization. Also, prior to pixel shader 3.0 D3D handles fog blending after + * the pixel shader while openGL always expects the pixel shader to handle the blending. + * To solve this problem, WineD3D does: + * 1) implement a linear fog equation and fog blending at the end of every pre 3.0 pixel + * shader, + * and 2) disables the fog computation (in either the fixed function or programmable + * rasterizer) if using a vertex program. + * + * D3D shaders can provide an explicit fog coordinate. This fog coordinate is used with + * D3DRS_FOGTABLEMODE==D3DFOG_NONE. The FOGVERTEXMODE is ignored, d3d always uses linear + * fog with start=1.0 and end=0.0 in this case. This is similar to fog coordinates in + * the specular color, a vertex shader counts as pretransformed geometry in this case. + * There are some GL differences between specular fog coords and vertex shaders though. + * + * With table fog the vertex shader fog coordinate is ignored. + * + * If a fogtablemode and a fogvertexmode are specified, table fog is applied (with or + * without shaders). + */ + + /* DX 7 sdk: "If both render states(vertex and table fog) are set to valid modes, + * the system will apply only pixel(=table) fog effects." + */ + if (state->render_states[WINED3D_RS_FOGTABLEMODE] == WINED3D_FOG_NONE) + { + if (use_vs(state)) + { + gl_info->gl_ops.gl.p_glFogi(GL_FOG_MODE, GL_LINEAR); + checkGLcall("glFogi(GL_FOG_MODE, GL_LINEAR)"); + new_source = FOGSOURCE_VS; + } + else + { + switch (state->render_states[WINED3D_RS_FOGVERTEXMODE]) + { + /* If processed vertices are used, fall through to the NONE case */ + case WINED3D_FOG_EXP: + if (!context->last_was_rhw) + { + gl_info->gl_ops.gl.p_glFogi(GL_FOG_MODE, GL_EXP); + checkGLcall("glFogi(GL_FOG_MODE, GL_EXP)"); + new_source = FOGSOURCE_FFP; + break; + } + /* drop through */ + + case WINED3D_FOG_EXP2: + if (!context->last_was_rhw) + { + gl_info->gl_ops.gl.p_glFogi(GL_FOG_MODE, GL_EXP2); + checkGLcall("glFogi(GL_FOG_MODE, GL_EXP2)"); + new_source = FOGSOURCE_FFP; + break; + } + /* drop through */ + + case WINED3D_FOG_LINEAR: + if (!context->last_was_rhw) + { + gl_info->gl_ops.gl.p_glFogi(GL_FOG_MODE, GL_LINEAR); + checkGLcall("glFogi(GL_FOG_MODE, GL_LINEAR)"); + new_source = FOGSOURCE_FFP; + break; + } + /* drop through */ + + case WINED3D_FOG_NONE: + /* Both are none? According to msdn the alpha channel of + * the specular colour contains a fog factor. Set it in + * draw_primitive_immediate_mode(). Same happens with + * vertex fog on transformed vertices. */ + new_source = FOGSOURCE_COORD; + gl_info->gl_ops.gl.p_glFogi(GL_FOG_MODE, GL_LINEAR); + checkGLcall("glFogi(GL_FOG_MODE, GL_LINEAR)"); + break; + + default: + FIXME("Unexpected WINED3D_RS_FOGVERTEXMODE %#x.\n", + state->render_states[WINED3D_RS_FOGVERTEXMODE]); + new_source = FOGSOURCE_FFP; /* Make the compiler happy */ + } + } + } else { + new_source = FOGSOURCE_FFP; + + switch (state->render_states[WINED3D_RS_FOGTABLEMODE]) + { + case WINED3D_FOG_EXP: + gl_info->gl_ops.gl.p_glFogi(GL_FOG_MODE, GL_EXP); + checkGLcall("glFogi(GL_FOG_MODE, GL_EXP)"); + break; + + case WINED3D_FOG_EXP2: + gl_info->gl_ops.gl.p_glFogi(GL_FOG_MODE, GL_EXP2); + checkGLcall("glFogi(GL_FOG_MODE, GL_EXP2)"); + break; + + case WINED3D_FOG_LINEAR: + gl_info->gl_ops.gl.p_glFogi(GL_FOG_MODE, GL_LINEAR); + checkGLcall("glFogi(GL_FOG_MODE, GL_LINEAR)"); + break; + + case WINED3D_FOG_NONE: /* Won't happen */ + default: + FIXME("Unexpected WINED3D_RS_FOGTABLEMODE %#x.\n", + state->render_states[WINED3D_RS_FOGTABLEMODE]); + } + } + + gl_info->p_glEnableWINE(GL_FOG); + checkGLcall("glEnable GL_FOG"); + if (new_source != context->fog_source || fogstart == fogend) + { + context->fog_source = new_source; + state_fogstartend(context, state, STATE_RENDER(WINED3D_RS_FOGSTART)); + } +} + +void state_fogcolor(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl(context)->gl_info; + struct wined3d_color color; + + wined3d_color_from_d3dcolor(&color, state->render_states[WINED3D_RS_FOGCOLOR]); + gl_info->gl_ops.gl.p_glFogfv(GL_FOG_COLOR, &color.r); + checkGLcall("glFog GL_FOG_COLOR"); +} + +void state_fogdensity(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl(context)->gl_info; + union { + DWORD d; + float f; + } tmpvalue; + + tmpvalue.d = state->render_states[WINED3D_RS_FOGDENSITY]; + gl_info->gl_ops.gl.p_glFogfv(GL_FOG_DENSITY, &tmpvalue.f); + checkGLcall("glFogf(GL_FOG_DENSITY, (float) Value)"); +} + +static void state_colormat(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + GLenum Parm = 0; + + /* Depends on the decoded vertex declaration to read the existence of + * diffuse data. The vertex declaration will call this function if the + * fixed function pipeline is used. */ + if (isStateDirty(&context_gl->c, STATE_VDECL)) + return; + + context_gl->untracked_material_count = 0; + if ((context_gl->c.stream_info.use_map & (1u << WINED3D_FFP_DIFFUSE)) + && state->render_states[WINED3D_RS_COLORVERTEX]) + { + TRACE("diff %d, amb %d, emis %d, spec %d\n", + state->render_states[WINED3D_RS_DIFFUSEMATERIALSOURCE], + state->render_states[WINED3D_RS_AMBIENTMATERIALSOURCE], + state->render_states[WINED3D_RS_EMISSIVEMATERIALSOURCE], + state->render_states[WINED3D_RS_SPECULARMATERIALSOURCE]); + + if (state->render_states[WINED3D_RS_DIFFUSEMATERIALSOURCE] == WINED3D_MCS_COLOR1) + { + if (state->render_states[WINED3D_RS_AMBIENTMATERIALSOURCE] == WINED3D_MCS_COLOR1) + Parm = GL_AMBIENT_AND_DIFFUSE; + else + Parm = GL_DIFFUSE; + if (state->render_states[WINED3D_RS_EMISSIVEMATERIALSOURCE] == WINED3D_MCS_COLOR1) + context_gl->untracked_materials[context_gl->untracked_material_count++] = GL_EMISSION; + if (state->render_states[WINED3D_RS_SPECULARMATERIALSOURCE] == WINED3D_MCS_COLOR1) + context_gl->untracked_materials[context_gl->untracked_material_count++] = GL_SPECULAR; + } + else if (state->render_states[WINED3D_RS_AMBIENTMATERIALSOURCE] == WINED3D_MCS_COLOR1) + { + Parm = GL_AMBIENT; + if (state->render_states[WINED3D_RS_EMISSIVEMATERIALSOURCE] == WINED3D_MCS_COLOR1) + context_gl->untracked_materials[context_gl->untracked_material_count++] = GL_EMISSION; + if (state->render_states[WINED3D_RS_SPECULARMATERIALSOURCE] == WINED3D_MCS_COLOR1) + context_gl->untracked_materials[context_gl->untracked_material_count++] = GL_SPECULAR; + } + else if (state->render_states[WINED3D_RS_EMISSIVEMATERIALSOURCE] == WINED3D_MCS_COLOR1) + { + Parm = GL_EMISSION; + if (state->render_states[WINED3D_RS_SPECULARMATERIALSOURCE] == WINED3D_MCS_COLOR1) + context_gl->untracked_materials[context_gl->untracked_material_count++] = GL_SPECULAR; + } + else if (state->render_states[WINED3D_RS_SPECULARMATERIALSOURCE] == WINED3D_MCS_COLOR1) + { + Parm = GL_SPECULAR; + } + } + + /* Nothing changed, return. */ + if (Parm == context_gl->tracking_parm) + return; + + if (!Parm) + { + gl_info->gl_ops.gl.p_glDisable(GL_COLOR_MATERIAL); + checkGLcall("glDisable GL_COLOR_MATERIAL"); + } + else + { + gl_info->gl_ops.gl.p_glColorMaterial(GL_FRONT_AND_BACK, Parm); + checkGLcall("glColorMaterial(GL_FRONT_AND_BACK, Parm)"); + gl_info->gl_ops.gl.p_glEnable(GL_COLOR_MATERIAL); + checkGLcall("glEnable(GL_COLOR_MATERIAL)"); + } + + /* Apparently calls to glMaterialfv are ignored for properties we're + * tracking with glColorMaterial, so apply those here. */ + switch (context_gl->tracking_parm) + { + case GL_AMBIENT_AND_DIFFUSE: + gl_info->gl_ops.gl.p_glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, (float *)&state->material.ambient); + gl_info->gl_ops.gl.p_glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, (float *)&state->material.diffuse); + checkGLcall("glMaterialfv"); + break; + + case GL_DIFFUSE: + gl_info->gl_ops.gl.p_glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, (float *)&state->material.diffuse); + checkGLcall("glMaterialfv"); + break; + + case GL_AMBIENT: + gl_info->gl_ops.gl.p_glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, (float *)&state->material.ambient); + checkGLcall("glMaterialfv"); + break; + + case GL_EMISSION: + gl_info->gl_ops.gl.p_glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, (float *)&state->material.emissive); + checkGLcall("glMaterialfv"); + break; + + case GL_SPECULAR: + /* Only change material color if specular is enabled, otherwise it is set to black */ + if (state->render_states[WINED3D_RS_SPECULARENABLE]) + { + gl_info->gl_ops.gl.p_glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, (float *)&state->material.specular); + checkGLcall("glMaterialfv"); + } + else + { + static const GLfloat black[] = {0.0f, 0.0f, 0.0f, 0.0f}; + gl_info->gl_ops.gl.p_glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, &black[0]); + checkGLcall("glMaterialfv"); + } + break; + } + + context_gl->tracking_parm = Parm; +} + +static void state_linepattern(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl(context)->gl_info; + union + { + DWORD d; + struct wined3d_line_pattern lp; + } tmppattern; + tmppattern.d = state->render_states[WINED3D_RS_LINEPATTERN]; + + TRACE("Line pattern: repeat %d bits %x.\n", tmppattern.lp.repeat_factor, tmppattern.lp.line_pattern); + + if (tmppattern.lp.repeat_factor) + { + gl_info->gl_ops.gl.p_glLineStipple(tmppattern.lp.repeat_factor, tmppattern.lp.line_pattern); + checkGLcall("glLineStipple(repeat, linepattern)"); + gl_info->gl_ops.gl.p_glEnable(GL_LINE_STIPPLE); + checkGLcall("glEnable(GL_LINE_STIPPLE);"); + } + else + { + gl_info->gl_ops.gl.p_glDisable(GL_LINE_STIPPLE); + checkGLcall("glDisable(GL_LINE_STIPPLE);"); + } +} + +static void state_linepattern_w(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + static unsigned int once; + + if (!once++) + FIXME("Setting line patterns is not supported in OpenGL core contexts.\n"); +} + +static void state_normalize(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl(context)->gl_info; + + if (isStateDirty(context, STATE_VDECL)) + return; + + /* Without vertex normals, we set the current normal to 0/0/0 to remove the diffuse factor + * from the opengl lighting equation, as d3d does. Normalization of 0/0/0 can lead to a division + * by zero and is not properly defined in opengl, so avoid it + */ + if (state->render_states[WINED3D_RS_NORMALIZENORMALS] + && (context->stream_info.use_map & (1u << WINED3D_FFP_NORMAL))) + { + gl_info->gl_ops.gl.p_glEnable(GL_NORMALIZE); + checkGLcall("glEnable(GL_NORMALIZE);"); + } + else + { + gl_info->gl_ops.gl.p_glDisable(GL_NORMALIZE); + checkGLcall("glDisable(GL_NORMALIZE);"); + } +} + +static void state_psizemin_w(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + float min, max; + + get_pointsize_minmax(context, state, &min, &max); + + if (min != 1.0f) + FIXME("WINED3D_RS_POINTSIZE_MIN value %.8e not supported on this OpenGL implementation.\n", min); + if (max != 64.0f) + FIXME("WINED3D_RS_POINTSIZE_MAX value %.8e not supported on this OpenGL implementation.\n", max); +} + +static void state_psizemin_ext(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl(context)->gl_info; + float min, max; + + get_pointsize_minmax(context, state, &min, &max); + + GL_EXTCALL(glPointParameterfEXT)(GL_POINT_SIZE_MIN_EXT, min); + checkGLcall("glPointParameterfEXT(...)"); + GL_EXTCALL(glPointParameterfEXT)(GL_POINT_SIZE_MAX_EXT, max); + checkGLcall("glPointParameterfEXT(...)"); +} + +static void state_psizemin_arb(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl(context)->gl_info; + float min, max; + + get_pointsize_minmax(context, state, &min, &max); + + GL_EXTCALL(glPointParameterfARB)(GL_POINT_SIZE_MIN_ARB, min); + checkGLcall("glPointParameterfARB(...)"); + GL_EXTCALL(glPointParameterfARB)(GL_POINT_SIZE_MAX_ARB, max); + checkGLcall("glPointParameterfARB(...)"); +} + +static void state_pscale(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl(context)->gl_info; + float att[3]; + float pointsize; + + get_pointsize(context, state, &pointsize, att); + + if (gl_info->supported[ARB_POINT_PARAMETERS]) + { + GL_EXTCALL(glPointParameterfvARB)(GL_POINT_DISTANCE_ATTENUATION_ARB, att); + checkGLcall("glPointParameterfvARB(GL_DISTANCE_ATTENUATION_ARB, ...)"); + } + else if (gl_info->supported[EXT_POINT_PARAMETERS]) + { + GL_EXTCALL(glPointParameterfvEXT)(GL_DISTANCE_ATTENUATION_EXT, att); + checkGLcall("glPointParameterfvEXT(GL_DISTANCE_ATTENUATION_EXT, ...)"); + } + else if (state->render_states[WINED3D_RS_POINTSCALEENABLE]) + { + WARN("POINT_PARAMETERS not supported in this version of opengl\n"); + } + + gl_info->gl_ops.gl.p_glPointSize(max(pointsize, FLT_MIN)); + checkGLcall("glPointSize(...);"); +} + +static void state_debug_monitor(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + WARN("token: %#x.\n", state->render_states[WINED3D_RS_DEBUGMONITORTOKEN]); +} + +static void state_localviewer(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl(context)->gl_info; + + if (state->render_states[WINED3D_RS_LOCALVIEWER]) + { + gl_info->gl_ops.gl.p_glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, 1); + checkGLcall("glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, 1)"); + } + else + { + gl_info->gl_ops.gl.p_glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, 0); + checkGLcall("glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, 0)"); + } +} + +static void state_lastpixel(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + if (state->render_states[WINED3D_RS_LASTPIXEL]) + { + TRACE("Last Pixel Drawing Enabled\n"); + } + else + { + static BOOL warned; + if (!warned) { + FIXME("Last Pixel Drawing Disabled, not handled yet\n"); + warned = TRUE; + } else { + TRACE("Last Pixel Drawing Disabled, not handled yet\n"); + } + } +} + +void state_pointsprite_w(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + static BOOL warned; + + /* TODO: NV_POINT_SPRITE */ + if (!warned && state->render_states[WINED3D_RS_POINTSPRITEENABLE]) + { + /* A FIXME, not a WARN because point sprites should be software emulated if not supported by HW */ + FIXME("Point sprites not supported\n"); + warned = TRUE; + } +} + +void state_pointsprite(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl(context)->gl_info; + + if (state->render_states[WINED3D_RS_POINTSPRITEENABLE]) + { + gl_info->gl_ops.gl.p_glEnable(GL_POINT_SPRITE_ARB); + checkGLcall("glEnable(GL_POINT_SPRITE_ARB)"); + } + else + { + gl_info->gl_ops.gl.p_glDisable(GL_POINT_SPRITE_ARB); + checkGLcall("glDisable(GL_POINT_SPRITE_ARB)"); + } +} + +static void state_wrap(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + static unsigned int once; + + if ((state->render_states[WINED3D_RS_WRAP0] + || state->render_states[WINED3D_RS_WRAP1] + || state->render_states[WINED3D_RS_WRAP2] + || state->render_states[WINED3D_RS_WRAP3] + || state->render_states[WINED3D_RS_WRAP4] + || state->render_states[WINED3D_RS_WRAP5] + || state->render_states[WINED3D_RS_WRAP6] + || state->render_states[WINED3D_RS_WRAP7] + || state->render_states[WINED3D_RS_WRAP8] + || state->render_states[WINED3D_RS_WRAP9] + || state->render_states[WINED3D_RS_WRAP10] + || state->render_states[WINED3D_RS_WRAP11] + || state->render_states[WINED3D_RS_WRAP12] + || state->render_states[WINED3D_RS_WRAP13] + || state->render_states[WINED3D_RS_WRAP14] + || state->render_states[WINED3D_RS_WRAP15]) + && !once++) + FIXME("(WINED3D_RS_WRAP0) Texture wrapping not yet supported.\n"); +} + +static void state_msaa_w(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + if (state->render_states[WINED3D_RS_MULTISAMPLEANTIALIAS]) + WARN("Multisample antialiasing not supported by GL.\n"); +} + +static void state_msaa(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl(context)->gl_info; + + if (state->render_states[WINED3D_RS_MULTISAMPLEANTIALIAS]) + { + gl_info->gl_ops.gl.p_glEnable(GL_MULTISAMPLE_ARB); + checkGLcall("glEnable(GL_MULTISAMPLE_ARB)"); + } + else + { + gl_info->gl_ops.gl.p_glDisable(GL_MULTISAMPLE_ARB); + checkGLcall("glDisable(GL_MULTISAMPLE_ARB)"); + } +} + +static void line_antialias(const struct wined3d_rasterizer_state *r, const struct wined3d_gl_info *gl_info) +{ + if (r && r->desc.line_antialias) + { + gl_info->gl_ops.gl.p_glEnable(GL_LINE_SMOOTH); + checkGLcall("glEnable(GL_LINE_SMOOTH)"); + } + else + { + gl_info->gl_ops.gl.p_glDisable(GL_LINE_SMOOTH); + checkGLcall("glDisable(GL_LINE_SMOOTH)"); + } +} + +static void scissor(const struct wined3d_rasterizer_state *r, const struct wined3d_gl_info *gl_info) +{ + if (r && r->desc.scissor) + { + gl_info->gl_ops.gl.p_glEnable(GL_SCISSOR_TEST); + checkGLcall("glEnable(GL_SCISSOR_TEST)"); + } + else + { + gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST); + checkGLcall("glDisable(GL_SCISSOR_TEST)"); + } +} + +/* The Direct3D depth bias is specified in normalized depth coordinates. In + * OpenGL the bias is specified in units of "the smallest value that is + * guaranteed to produce a resolvable offset for a given implementation". To + * convert from D3D to GL we need to divide the D3D depth bias by that value. + * We try to detect the value from GL with test draws. On most drivers (r300g, + * 600g, Nvidia, i965 on Mesa) the value is 2^23 for fixed point depth buffers, + * for r200 and i965 on OSX it is 2^24, for r500 on OSX it is 2^22. For floating + * point buffers it is 2^22, 2^23 or 2^24 depending on the GPU. The value does + * not depend on the depth buffer precision on any driver. + * + * Two games that are picky regarding depth bias are Mass Effect 2 (flickering + * decals) and F.E.A.R and F.E.A.R. 2 (semi-transparent guns). + * + * Note that SLOPESCALEDEPTHBIAS is a scaling factor for the depth slope, and + * doesn't need to be scaled to account for GL vs D3D differences. */ +static void depthbias(struct wined3d_context *context, const struct wined3d_state *state) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl(context)->gl_info; + const struct wined3d_rasterizer_state *r = state->rasterizer_state; + float scale_bias = r ? r->desc.scale_bias : 0.0f; + union + { + DWORD d; + float f; + } const_bias; + + const_bias.f = r ? r->desc.depth_bias : 0.0f; + + if (scale_bias || const_bias.f) + { + const struct wined3d_rendertarget_view *depth = state->fb.depth_stencil; + float factor, units, scale, clamp; + + clamp = r ? r->desc.depth_bias_clamp : 0.0f; + + if (context->d3d_info->wined3d_creation_flags & WINED3D_LEGACY_DEPTH_BIAS) + { + factor = units = -(float)const_bias.d; + } + else + { + if (depth) + { + scale = depth->format->depth_bias_scale; + + TRACE("Depth format %s, using depthbias scale of %.8e.\n", + debug_d3dformat(depth->format->id), scale); + } + else + { + /* The context manager will reapply this state on a depth stencil change */ + TRACE("No depth stencil, using depth bias scale of 0.0.\n"); + scale = 0.0f; + } + + factor = scale_bias; + units = const_bias.f * scale; + } + + gl_info->gl_ops.gl.p_glEnable(GL_POLYGON_OFFSET_FILL); + if (gl_info->supported[ARB_POLYGON_OFFSET_CLAMP]) + { + gl_info->gl_ops.ext.p_glPolygonOffsetClamp(factor, units, clamp); + } + else + { + if (clamp != 0.0f) + WARN("Ignoring depth bias clamp %.8e.\n", clamp); + gl_info->gl_ops.gl.p_glPolygonOffset(factor, units); + } + } + else + { + gl_info->gl_ops.gl.p_glDisable(GL_POLYGON_OFFSET_FILL); + } + + checkGLcall("depth bias"); +} + +static void state_zvisible(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + if (state->render_states[WINED3D_RS_ZVISIBLE]) + FIXME("WINED3D_RS_ZVISIBLE not implemented.\n"); +} + +static void state_stippledalpha(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + if (state->render_states[WINED3D_RS_STIPPLEDALPHA]) + FIXME("Stippled Alpha not supported yet.\n"); +} + +static void state_antialias(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + if (state->render_states[WINED3D_RS_ANTIALIAS]) + FIXME("Antialias not supported yet.\n"); +} + +static void state_sample_mask(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl(context)->gl_info; + unsigned int sample_mask = state->sample_mask; + + TRACE("Setting sample mask to %#x.\n", sample_mask); + if (sample_mask != 0xffffffff) + { + gl_info->gl_ops.gl.p_glEnable(GL_SAMPLE_MASK); + checkGLcall("glEnable GL_SAMPLE_MASK"); + GL_EXTCALL(glSampleMaski(0, sample_mask)); + checkGLcall("glSampleMaski"); + } + else + { + gl_info->gl_ops.gl.p_glDisable(GL_SAMPLE_MASK); + checkGLcall("glDisable GL_SAMPLE_MASK"); + } +} + +static void state_sample_mask_w(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + WARN("Unsupported in local OpenGL implementation: glSampleMaski.\n"); +} + +static void state_patchedgestyle(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + if (state->render_states[WINED3D_RS_PATCHEDGESTYLE] != WINED3D_PATCH_EDGE_DISCRETE) + FIXME("WINED3D_RS_PATCHEDGESTYLE %#x not yet implemented.\n", + state->render_states[WINED3D_RS_PATCHEDGESTYLE]); +} + +static void state_patchsegments(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + union { + DWORD d; + float f; + } tmpvalue; + tmpvalue.f = 1.0f; + + if (state->render_states[WINED3D_RS_PATCHSEGMENTS] != tmpvalue.d) + { + static BOOL displayed = FALSE; + + tmpvalue.d = state->render_states[WINED3D_RS_PATCHSEGMENTS]; + if(!displayed) + FIXME("(WINED3D_RS_PATCHSEGMENTS,%f) not yet implemented\n", tmpvalue.f); + + displayed = TRUE; + } +} + +static void state_positiondegree(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + if (state->render_states[WINED3D_RS_POSITIONDEGREE] != WINED3D_DEGREE_CUBIC) + FIXME("WINED3D_RS_POSITIONDEGREE %#x not yet implemented.\n", + state->render_states[WINED3D_RS_POSITIONDEGREE]); +} + +static void state_normaldegree(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + if (state->render_states[WINED3D_RS_NORMALDEGREE] != WINED3D_DEGREE_LINEAR) + FIXME("WINED3D_RS_NORMALDEGREE %#x not yet implemented.\n", + state->render_states[WINED3D_RS_NORMALDEGREE]); +} + +static void state_tessellation(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + if (state->render_states[WINED3D_RS_ENABLEADAPTIVETESSELLATION]) + FIXME("WINED3D_RS_ENABLEADAPTIVETESSELLATION %#x not yet implemented.\n", + state->render_states[WINED3D_RS_ENABLEADAPTIVETESSELLATION]); +} + +static void state_nvdb(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl(context)->gl_info; + union + { + uint32_t d; + float f; + } zmin, zmax; + + if (state->render_states[WINED3D_RS_ADAPTIVETESS_X] == WINED3DFMT_NVDB) + { + zmin.d = state->render_states[WINED3D_RS_ADAPTIVETESS_Z]; + zmax.d = state->render_states[WINED3D_RS_ADAPTIVETESS_W]; + + /* If zmin is larger than zmax INVALID_VALUE error is generated. + * In d3d9 test is not performed in this case*/ + if (zmin.f <= zmax.f) + { + gl_info->gl_ops.gl.p_glEnable(GL_DEPTH_BOUNDS_TEST_EXT); + checkGLcall("glEnable(GL_DEPTH_BOUNDS_TEST_EXT)"); + GL_EXTCALL(glDepthBoundsEXT(zmin.f, zmax.f)); + checkGLcall("glDepthBoundsEXT(...)"); + } + else + { + gl_info->gl_ops.gl.p_glDisable(GL_DEPTH_BOUNDS_TEST_EXT); + checkGLcall("glDisable(GL_DEPTH_BOUNDS_TEST_EXT)"); + } + } + else + { + gl_info->gl_ops.gl.p_glDisable(GL_DEPTH_BOUNDS_TEST_EXT); + checkGLcall("glDisable(GL_DEPTH_BOUNDS_TEST_EXT)"); + } + + state_tessellation(context, state, STATE_RENDER(WINED3D_RS_ENABLEADAPTIVETESSELLATION)); +} + +static void state_wrapu(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + if (state->render_states[WINED3D_RS_WRAPU]) + FIXME("Render state WINED3D_RS_WRAPU not implemented yet.\n"); +} + +static void state_wrapv(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + if (state->render_states[WINED3D_RS_WRAPV]) + FIXME("Render state WINED3D_RS_WRAPV not implemented yet.\n"); +} + +static void state_monoenable(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + if (state->render_states[WINED3D_RS_MONOENABLE]) + FIXME("Render state WINED3D_RS_MONOENABLE not implemented yet.\n"); +} + +static void state_rop2(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + if (state->render_states[WINED3D_RS_ROP2]) + FIXME("Render state WINED3D_RS_ROP2 not implemented yet.\n"); +} + +static void state_planemask(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + if (state->render_states[WINED3D_RS_PLANEMASK]) + FIXME("Render state WINED3D_RS_PLANEMASK not implemented yet.\n"); +} + +static void state_subpixel(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + if (state->render_states[WINED3D_RS_SUBPIXEL]) + FIXME("Render state WINED3D_RS_SUBPIXEL not implemented yet.\n"); +} + +static void state_subpixelx(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + if (state->render_states[WINED3D_RS_SUBPIXELX]) + FIXME("Render state WINED3D_RS_SUBPIXELX not implemented yet.\n"); +} + +static void state_stippleenable(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + if (state->render_states[WINED3D_RS_STIPPLEENABLE]) + FIXME("Render state WINED3D_RS_STIPPLEENABLE not implemented yet.\n"); +} + +static void state_mipmaplodbias(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + if (state->render_states[WINED3D_RS_MIPMAPLODBIAS]) + FIXME("Render state WINED3D_RS_MIPMAPLODBIAS not implemented yet.\n"); +} + +static void state_anisotropy(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + if (state->render_states[WINED3D_RS_ANISOTROPY]) + FIXME("Render state WINED3D_RS_ANISOTROPY not implemented yet.\n"); +} + +static void state_flushbatch(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + if (state->render_states[WINED3D_RS_FLUSHBATCH]) + FIXME("Render state WINED3D_RS_FLUSHBATCH not implemented yet.\n"); +} + +static void state_translucentsi(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + if (state->render_states[WINED3D_RS_TRANSLUCENTSORTINDEPENDENT]) + FIXME("Render state WINED3D_RS_TRANSLUCENTSORTINDEPENDENT not implemented yet.\n"); +} + +static void state_extents(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + if (state->render_states[WINED3D_RS_EXTENTS]) + FIXME("Render state WINED3D_RS_EXTENTS not implemented yet.\n"); +} + +static void state_ckeyblend(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + if (state->render_states[WINED3D_RS_COLORKEYBLENDENABLE]) + FIXME("Render state WINED3D_RS_COLORKEYBLENDENABLE not implemented yet.\n"); +} + +static void state_swvp(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + static int once; + if (state->render_states[WINED3D_RS_SOFTWAREVERTEXPROCESSING]) + { + if (!once++) + FIXME("Software vertex processing not implemented.\n"); + } +} + +static void get_src_and_opr(DWORD arg, BOOL is_alpha, GLenum* source, GLenum* operand) { + /* The WINED3DTA_ALPHAREPLICATE flag specifies the alpha component of the + * input should be used for all input components. The WINED3DTA_COMPLEMENT + * flag specifies the complement of the input should be used. */ + BOOL from_alpha = is_alpha || arg & WINED3DTA_ALPHAREPLICATE; + BOOL complement = arg & WINED3DTA_COMPLEMENT; + + /* Calculate the operand */ + if (complement) { + if (from_alpha) *operand = GL_ONE_MINUS_SRC_ALPHA; + else *operand = GL_ONE_MINUS_SRC_COLOR; + } else { + if (from_alpha) *operand = GL_SRC_ALPHA; + else *operand = GL_SRC_COLOR; + } + + /* Calculate the source */ + switch (arg & WINED3DTA_SELECTMASK) { + case WINED3DTA_CURRENT: *source = GL_PREVIOUS_EXT; break; + case WINED3DTA_DIFFUSE: *source = GL_PRIMARY_COLOR_EXT; break; + case WINED3DTA_TEXTURE: *source = GL_TEXTURE; break; + case WINED3DTA_TFACTOR: *source = GL_CONSTANT_EXT; break; + case WINED3DTA_SPECULAR: + /* + * According to the GL_ARB_texture_env_combine specs, SPECULAR is + * 'Secondary color' and isn't supported until base GL supports it + * There is no concept of temp registers as far as I can tell + */ + FIXME("Unhandled texture arg WINED3DTA_SPECULAR\n"); + *source = GL_TEXTURE; + break; + default: + FIXME("Unrecognized texture arg %#x\n", arg); + *source = GL_TEXTURE; + break; + } +} + +/* Setup the texture operations texture stage states */ +static void set_tex_op(const struct wined3d_gl_info *gl_info, const struct wined3d_state *state, + BOOL isAlpha, int Stage, enum wined3d_texture_op op, DWORD arg1, DWORD arg2, DWORD arg3) +{ + GLenum src1, src2, src3; + GLenum opr1, opr2, opr3; + GLenum comb_target; + GLenum src0_target, src1_target, src2_target; + GLenum opr0_target, opr1_target, opr2_target; + GLenum scal_target; + GLenum opr=0, invopr, src3_target, opr3_target; + BOOL Handled = FALSE; + + TRACE("Alpha?(%d), Stage:%d Op(%s), a1(%d), a2(%d), a3(%d)\n", isAlpha, Stage, debug_d3dtop(op), arg1, arg2, arg3); + + /* Operations usually involve two args, src0 and src1 and are operations + * of the form (a1 a2). However, some of the more complex + * operations take 3 parameters. Instead of the (sensible) addition of a3, + * Microsoft added in a third parameter called a0. Therefore these are + * operations of the form a0 a1 a2. I.e., the new + * parameter goes to the front. + * + * However, below we treat the new (a0) parameter as src2/opr2, so in the + * actual functions below, expect their syntax to differ slightly to those + * listed in the manuals. I.e., replace arg1 with arg3, arg2 with arg1 and + * arg3 with arg2. This affects WINED3DTOP_MULTIPLYADD and WINED3DTOP_LERP. */ + + if (isAlpha) + { + comb_target = GL_COMBINE_ALPHA; + src0_target = GL_SOURCE0_ALPHA; + src1_target = GL_SOURCE1_ALPHA; + src2_target = GL_SOURCE2_ALPHA; + opr0_target = GL_OPERAND0_ALPHA; + opr1_target = GL_OPERAND1_ALPHA; + opr2_target = GL_OPERAND2_ALPHA; + scal_target = GL_ALPHA_SCALE; + } + else + { + comb_target = GL_COMBINE_RGB; + src0_target = GL_SOURCE0_RGB; + src1_target = GL_SOURCE1_RGB; + src2_target = GL_SOURCE2_RGB; + opr0_target = GL_OPERAND0_RGB; + opr1_target = GL_OPERAND1_RGB; + opr2_target = GL_OPERAND2_RGB; + scal_target = GL_RGB_SCALE; + } + + /* If a texture stage references an invalid texture unit the stage just + * passes through the result from the previous stage */ + if (is_invalid_op(state, Stage, op, arg1, arg2, arg3)) + { + arg1 = WINED3DTA_CURRENT; + op = WINED3D_TOP_SELECT_ARG1; + } + + if (isAlpha && !state->textures[Stage] && arg1 == WINED3DTA_TEXTURE) + { + get_src_and_opr(WINED3DTA_DIFFUSE, isAlpha, &src1, &opr1); + } else { + get_src_and_opr(arg1, isAlpha, &src1, &opr1); + } + get_src_and_opr(arg2, isAlpha, &src2, &opr2); + get_src_and_opr(arg3, isAlpha, &src3, &opr3); + + TRACE("ct(%x), 1:(%x,%x), 2:(%x,%x), 3:(%x,%x)\n", comb_target, src1, opr1, src2, opr2, src3, opr3); + + Handled = TRUE; /* Assume will be handled */ + + /* Other texture operations require special extensions: */ + if (gl_info->supported[NV_TEXTURE_ENV_COMBINE4]) + { + if (isAlpha) { + opr = GL_SRC_ALPHA; + invopr = GL_ONE_MINUS_SRC_ALPHA; + src3_target = GL_SOURCE3_ALPHA_NV; + opr3_target = GL_OPERAND3_ALPHA_NV; + } else { + opr = GL_SRC_COLOR; + invopr = GL_ONE_MINUS_SRC_COLOR; + src3_target = GL_SOURCE3_RGB_NV; + opr3_target = GL_OPERAND3_RGB_NV; + } + switch (op) + { + case WINED3D_TOP_DISABLE: /* Only for alpha */ + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, comb_target, GL_ADD); + checkGLcall("GL_TEXTURE_ENV, comb_target, GL_REPLACE"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src0_target, GL_PREVIOUS_EXT); + checkGLcall("GL_TEXTURE_ENV, src0_target, src1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr0_target, GL_SRC_ALPHA); + checkGLcall("GL_TEXTURE_ENV, opr0_target, opr1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src1_target, GL_ZERO); + checkGLcall("GL_TEXTURE_ENV, src1_target, GL_ZERO"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr1_target, invopr); + checkGLcall("GL_TEXTURE_ENV, opr1_target, invopr"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src2_target, GL_ZERO); + checkGLcall("GL_TEXTURE_ENV, src2_target, GL_ZERO"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr2_target, opr); + checkGLcall("GL_TEXTURE_ENV, opr2_target, opr"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src3_target, GL_ZERO); + checkGLcall("GL_TEXTURE_ENV, src3_target, GL_ZERO"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr3_target, opr); + checkGLcall("GL_TEXTURE_ENV, opr3_target, opr"); + break; + + case WINED3D_TOP_SELECT_ARG1: /* = a1 * 1 + 0 * 0 */ + case WINED3D_TOP_SELECT_ARG2: /* = a2 * 1 + 0 * 0 */ + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, comb_target, GL_ADD); + checkGLcall("GL_TEXTURE_ENV, comb_target, GL_ADD"); + if (op == WINED3D_TOP_SELECT_ARG1) + { + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src0_target, src1); + checkGLcall("GL_TEXTURE_ENV, src0_target, src1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr0_target, opr1); + checkGLcall("GL_TEXTURE_ENV, opr0_target, opr1"); + } + else + { + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src0_target, src2); + checkGLcall("GL_TEXTURE_ENV, src0_target, src2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr0_target, opr2); + checkGLcall("GL_TEXTURE_ENV, opr0_target, opr2"); + } + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src1_target, GL_ZERO); + checkGLcall("GL_TEXTURE_ENV, src1_target, GL_ZERO"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr1_target, invopr); + checkGLcall("GL_TEXTURE_ENV, opr1_target, invopr"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src2_target, GL_ZERO); + checkGLcall("GL_TEXTURE_ENV, src2_target, GL_ZERO"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr2_target, opr); + checkGLcall("GL_TEXTURE_ENV, opr2_target, opr"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src3_target, GL_ZERO); + checkGLcall("GL_TEXTURE_ENV, src3_target, GL_ZERO"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr3_target, opr); + checkGLcall("GL_TEXTURE_ENV, opr3_target, opr"); + break; + + case WINED3D_TOP_MODULATE: + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, comb_target, GL_ADD); + checkGLcall("GL_TEXTURE_ENV, comb_target, GL_ADD"); /* Add = a0*a1 + a2*a3 */ + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src0_target, src1); + checkGLcall("GL_TEXTURE_ENV, src0_target, src1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr0_target, opr1); + checkGLcall("GL_TEXTURE_ENV, opr0_target, opr1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src1_target, src2); + checkGLcall("GL_TEXTURE_ENV, src1_target, GL_ZERO"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr1_target, opr2); + checkGLcall("GL_TEXTURE_ENV, opr1_target, invopr"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src2_target, GL_ZERO); + checkGLcall("GL_TEXTURE_ENV, src2_target, src2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr2_target, opr); + checkGLcall("GL_TEXTURE_ENV, opr2_target, opr2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src3_target, GL_ZERO); + checkGLcall("GL_TEXTURE_ENV, src3_target, GL_ZERO"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr3_target, opr); + checkGLcall("GL_TEXTURE_ENV, opr3_target, opr1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, scal_target, 1); + checkGLcall("GL_TEXTURE_ENV, scal_target, 1"); + break; + case WINED3D_TOP_MODULATE_2X: + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, comb_target, GL_ADD); + checkGLcall("GL_TEXTURE_ENV, comb_target, GL_ADD"); /* Add = a0*a1 + a2*a3 */ + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src0_target, src1); + checkGLcall("GL_TEXTURE_ENV, src0_target, src1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr0_target, opr1); + checkGLcall("GL_TEXTURE_ENV, opr0_target, opr1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src1_target, src2); + checkGLcall("GL_TEXTURE_ENV, src1_target, GL_ZERO"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr1_target, opr2); + checkGLcall("GL_TEXTURE_ENV, opr1_target, invopr"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src2_target, GL_ZERO); + checkGLcall("GL_TEXTURE_ENV, src2_target, src2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr2_target, opr); + checkGLcall("GL_TEXTURE_ENV, opr2_target, opr2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src3_target, GL_ZERO); + checkGLcall("GL_TEXTURE_ENV, src3_target, GL_ZERO"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr3_target, opr); + checkGLcall("GL_TEXTURE_ENV, opr3_target, opr1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, scal_target, 2); + checkGLcall("GL_TEXTURE_ENV, scal_target, 2"); + break; + case WINED3D_TOP_MODULATE_4X: + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, comb_target, GL_ADD); + checkGLcall("GL_TEXTURE_ENV, comb_target, GL_ADD"); /* Add = a0*a1 + a2*a3 */ + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src0_target, src1); + checkGLcall("GL_TEXTURE_ENV, src0_target, src1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr0_target, opr1); + checkGLcall("GL_TEXTURE_ENV, opr0_target, opr1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src1_target, src2); + checkGLcall("GL_TEXTURE_ENV, src1_target, GL_ZERO"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr1_target, opr2); + checkGLcall("GL_TEXTURE_ENV, opr1_target, invopr"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src2_target, GL_ZERO); + checkGLcall("GL_TEXTURE_ENV, src2_target, src2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr2_target, opr); + checkGLcall("GL_TEXTURE_ENV, opr2_target, opr2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src3_target, GL_ZERO); + checkGLcall("GL_TEXTURE_ENV, src3_target, GL_ZERO"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr3_target, opr); + checkGLcall("GL_TEXTURE_ENV, opr3_target, opr1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, scal_target, 4); + checkGLcall("GL_TEXTURE_ENV, scal_target, 4"); + break; + + case WINED3D_TOP_ADD: + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, comb_target, GL_ADD); + checkGLcall("GL_TEXTURE_ENV, comb_target, GL_ADD"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src0_target, src1); + checkGLcall("GL_TEXTURE_ENV, src0_target, src1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr0_target, opr1); + checkGLcall("GL_TEXTURE_ENV, opr0_target, opr1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src1_target, GL_ZERO); + checkGLcall("GL_TEXTURE_ENV, src1_target, GL_ZERO"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr1_target, invopr); + checkGLcall("GL_TEXTURE_ENV, opr1_target, invopr"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src2_target, src2); + checkGLcall("GL_TEXTURE_ENV, src2_target, src2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr2_target, opr2); + checkGLcall("GL_TEXTURE_ENV, opr2_target, opr2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src3_target, GL_ZERO); + checkGLcall("GL_TEXTURE_ENV, src3_target, GL_ZERO"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr3_target, invopr); + checkGLcall("GL_TEXTURE_ENV, opr3_target, invopr"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, scal_target, 1); + checkGLcall("GL_TEXTURE_ENV, scal_target, 1"); + break; + + case WINED3D_TOP_ADD_SIGNED: + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, comb_target, GL_ADD_SIGNED); + checkGLcall("GL_TEXTURE_ENV, comb_target, GL_ADD_SIGNED"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src0_target, src1); + checkGLcall("GL_TEXTURE_ENV, src0_target, src1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr0_target, opr1); + checkGLcall("GL_TEXTURE_ENV, opr0_target, opr1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src1_target, GL_ZERO); + checkGLcall("GL_TEXTURE_ENV, src1_target, GL_ZERO"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr1_target, invopr); + checkGLcall("GL_TEXTURE_ENV, opr1_target, invopr"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src2_target, src2); + checkGLcall("GL_TEXTURE_ENV, src2_target, src2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr2_target, opr2); + checkGLcall("GL_TEXTURE_ENV, opr2_target, opr2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src3_target, GL_ZERO); + checkGLcall("GL_TEXTURE_ENV, src3_target, GL_ZERO"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr3_target, invopr); + checkGLcall("GL_TEXTURE_ENV, opr3_target, invopr"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, scal_target, 1); + checkGLcall("GL_TEXTURE_ENV, scal_target, 1"); + break; + + case WINED3D_TOP_ADD_SIGNED_2X: + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, comb_target, GL_ADD_SIGNED); + checkGLcall("GL_TEXTURE_ENV, comb_target, GL_ADD_SIGNED"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src0_target, src1); + checkGLcall("GL_TEXTURE_ENV, src0_target, src1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr0_target, opr1); + checkGLcall("GL_TEXTURE_ENV, opr0_target, opr1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src1_target, GL_ZERO); + checkGLcall("GL_TEXTURE_ENV, src1_target, GL_ZERO"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr1_target, invopr); + checkGLcall("GL_TEXTURE_ENV, opr1_target, invopr"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src2_target, src2); + checkGLcall("GL_TEXTURE_ENV, src2_target, src2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr2_target, opr2); + checkGLcall("GL_TEXTURE_ENV, opr2_target, opr2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src3_target, GL_ZERO); + checkGLcall("GL_TEXTURE_ENV, src3_target, GL_ZERO"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr3_target, invopr); + checkGLcall("GL_TEXTURE_ENV, opr3_target, invopr"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, scal_target, 2); + checkGLcall("GL_TEXTURE_ENV, scal_target, 2"); + break; + + case WINED3D_TOP_ADD_SMOOTH: + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, comb_target, GL_ADD); + checkGLcall("GL_TEXTURE_ENV, comb_target, GL_ADD"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src0_target, src1); + checkGLcall("GL_TEXTURE_ENV, src0_target, src1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr0_target, opr1); + checkGLcall("GL_TEXTURE_ENV, opr0_target, opr1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src1_target, GL_ZERO); + checkGLcall("GL_TEXTURE_ENV, src1_target, GL_ZERO"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr1_target, invopr); + checkGLcall("GL_TEXTURE_ENV, opr1_target, invopr"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src2_target, src2); + checkGLcall("GL_TEXTURE_ENV, src2_target, src2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr2_target, opr2); + checkGLcall("GL_TEXTURE_ENV, opr2_target, opr2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src3_target, src1); + checkGLcall("GL_TEXTURE_ENV, src3_target, src1"); + switch (opr1) { + case GL_SRC_COLOR: opr = GL_ONE_MINUS_SRC_COLOR; break; + case GL_ONE_MINUS_SRC_COLOR: opr = GL_SRC_COLOR; break; + case GL_SRC_ALPHA: opr = GL_ONE_MINUS_SRC_ALPHA; break; + case GL_ONE_MINUS_SRC_ALPHA: opr = GL_SRC_ALPHA; break; + } + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr3_target, opr); + checkGLcall("GL_TEXTURE_ENV, opr3_target, opr"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, scal_target, 1); + checkGLcall("GL_TEXTURE_ENV, scal_target, 1"); + break; + + case WINED3D_TOP_BLEND_DIFFUSE_ALPHA: + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, comb_target, GL_ADD); + checkGLcall("GL_TEXTURE_ENV, comb_target, GL_ADD"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src0_target, src1); + checkGLcall("GL_TEXTURE_ENV, src0_target, src1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr0_target, opr1); + checkGLcall("GL_TEXTURE_ENV, opr0_target, opr1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src1_target, GL_PRIMARY_COLOR); + checkGLcall("GL_TEXTURE_ENV, src1_target, GL_PRIMARY_COLOR"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr1_target, invopr); + checkGLcall("GL_TEXTURE_ENV, opr1_target, invopr"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src2_target, src2); + checkGLcall("GL_TEXTURE_ENV, src2_target, src2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr2_target, opr2); + checkGLcall("GL_TEXTURE_ENV, opr2_target, opr2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src3_target, GL_PRIMARY_COLOR); + checkGLcall("GL_TEXTURE_ENV, src3_target, GL_PRIMARY_COLOR"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr3_target, GL_ONE_MINUS_SRC_ALPHA); + checkGLcall("GL_TEXTURE_ENV, opr3_target, GL_ONE_MINUS_SRC_ALPHA"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, scal_target, 1); + checkGLcall("GL_TEXTURE_ENV, scal_target, 1"); + break; + case WINED3D_TOP_BLEND_TEXTURE_ALPHA: + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, comb_target, GL_ADD); + checkGLcall("GL_TEXTURE_ENV, comb_target, GL_ADD"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src0_target, src1); + checkGLcall("GL_TEXTURE_ENV, src0_target, src1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr0_target, opr1); + checkGLcall("GL_TEXTURE_ENV, opr0_target, opr1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src1_target, GL_TEXTURE); + checkGLcall("GL_TEXTURE_ENV, src1_target, GL_TEXTURE"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr1_target, invopr); + checkGLcall("GL_TEXTURE_ENV, opr1_target, invopr"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src2_target, src2); + checkGLcall("GL_TEXTURE_ENV, src2_target, src2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr2_target, opr2); + checkGLcall("GL_TEXTURE_ENV, opr2_target, opr2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src3_target, GL_TEXTURE); + checkGLcall("GL_TEXTURE_ENV, src3_target, GL_TEXTURE"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr3_target, GL_ONE_MINUS_SRC_ALPHA); + checkGLcall("GL_TEXTURE_ENV, opr3_target, GL_ONE_MINUS_SRC_ALPHA"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, scal_target, 1); + checkGLcall("GL_TEXTURE_ENV, scal_target, 1"); + break; + case WINED3D_TOP_BLEND_FACTOR_ALPHA: + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, comb_target, GL_ADD); + checkGLcall("GL_TEXTURE_ENV, comb_target, GL_ADD"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src0_target, src1); + checkGLcall("GL_TEXTURE_ENV, src0_target, src1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr0_target, opr1); + checkGLcall("GL_TEXTURE_ENV, opr0_target, opr1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src1_target, GL_CONSTANT); + checkGLcall("GL_TEXTURE_ENV, src1_target, GL_CONSTANT"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr1_target, invopr); + checkGLcall("GL_TEXTURE_ENV, opr1_target, invopr"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src2_target, src2); + checkGLcall("GL_TEXTURE_ENV, src2_target, src2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr2_target, opr2); + checkGLcall("GL_TEXTURE_ENV, opr2_target, opr2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src3_target, GL_CONSTANT); + checkGLcall("GL_TEXTURE_ENV, src3_target, GL_CONSTANT"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr3_target, GL_ONE_MINUS_SRC_ALPHA); + checkGLcall("GL_TEXTURE_ENV, opr3_target, GL_ONE_MINUS_SRC_ALPHA"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, scal_target, 1); + checkGLcall("GL_TEXTURE_ENV, scal_target, 1"); + break; + case WINED3D_TOP_BLEND_TEXTURE_ALPHA_PM: + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, comb_target, GL_ADD); + checkGLcall("GL_TEXTURE_ENV, comb_target, GL_ADD"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src0_target, src1); + checkGLcall("GL_TEXTURE_ENV, src0_target, src1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr0_target, opr1); + checkGLcall("GL_TEXTURE_ENV, opr0_target, opr1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src1_target, GL_ZERO); + checkGLcall("GL_TEXTURE_ENV, src1_target, GL_ZERO"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr1_target, invopr); + checkGLcall("GL_TEXTURE_ENV, opr1_target, invopr"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src2_target, src2); + checkGLcall("GL_TEXTURE_ENV, src2_target, src2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr2_target, opr2); + checkGLcall("GL_TEXTURE_ENV, opr2_target, opr2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src3_target, GL_TEXTURE); + checkGLcall("GL_TEXTURE_ENV, src3_target, GL_TEXTURE"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr3_target, GL_ONE_MINUS_SRC_ALPHA); + checkGLcall("GL_TEXTURE_ENV, opr3_target, GL_ONE_MINUS_SRC_ALPHA"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, scal_target, 1); + checkGLcall("GL_TEXTURE_ENV, scal_target, 1"); + break; + case WINED3D_TOP_MODULATE_ALPHA_ADD_COLOR: + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, comb_target, GL_ADD); + checkGLcall("GL_TEXTURE_ENV, comb_target, GL_ADD"); /* Add = a0*a1 + a2*a3 */ + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src0_target, src1); /* a0 = src1/opr1 */ + checkGLcall("GL_TEXTURE_ENV, src0_target, src1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr0_target, opr1); + checkGLcall("GL_TEXTURE_ENV, opr0_target, opr1"); /* a1 = 1 (see docs) */ + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src1_target, GL_ZERO); + checkGLcall("GL_TEXTURE_ENV, src1_target, GL_ZERO"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr1_target, invopr); + checkGLcall("GL_TEXTURE_ENV, opr1_target, invopr"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src2_target, src2); /* a2 = arg2 */ + checkGLcall("GL_TEXTURE_ENV, src2_target, src2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr2_target, opr2); + checkGLcall("GL_TEXTURE_ENV, opr2_target, opr2"); /* a3 = src1 alpha */ + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src3_target, src1); + checkGLcall("GL_TEXTURE_ENV, src3_target, src1"); + switch (opr) { + case GL_SRC_COLOR: opr = GL_SRC_ALPHA; break; + case GL_ONE_MINUS_SRC_COLOR: opr = GL_ONE_MINUS_SRC_ALPHA; break; + } + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr3_target, opr); + checkGLcall("GL_TEXTURE_ENV, opr3_target, opr"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, scal_target, 1); + checkGLcall("GL_TEXTURE_ENV, scal_target, 1"); + break; + case WINED3D_TOP_MODULATE_COLOR_ADD_ALPHA: + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, comb_target, GL_ADD); + checkGLcall("GL_TEXTURE_ENV, comb_target, GL_ADD"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src0_target, src1); + checkGLcall("GL_TEXTURE_ENV, src0_target, src1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr0_target, opr1); + checkGLcall("GL_TEXTURE_ENV, opr0_target, opr1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src1_target, src2); + checkGLcall("GL_TEXTURE_ENV, src1_target, src2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr1_target, opr2); + checkGLcall("GL_TEXTURE_ENV, opr1_target, opr2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src2_target, src1); + checkGLcall("GL_TEXTURE_ENV, src2_target, src1"); + switch (opr1) { + case GL_SRC_COLOR: opr = GL_SRC_ALPHA; break; + case GL_ONE_MINUS_SRC_COLOR: opr = GL_ONE_MINUS_SRC_ALPHA; break; + } + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr2_target, opr); + checkGLcall("GL_TEXTURE_ENV, opr2_target, opr"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src3_target, GL_ZERO); + checkGLcall("GL_TEXTURE_ENV, src3_target, GL_ZERO"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr3_target, invopr); + checkGLcall("GL_TEXTURE_ENV, opr3_target, invopr"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, scal_target, 1); + checkGLcall("GL_TEXTURE_ENV, scal_target, 1"); + break; + case WINED3D_TOP_MODULATE_INVALPHA_ADD_COLOR: + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, comb_target, GL_ADD); + checkGLcall("GL_TEXTURE_ENV, comb_target, GL_ADD"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src0_target, src1); + checkGLcall("GL_TEXTURE_ENV, src0_target, src1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr0_target, opr1); + checkGLcall("GL_TEXTURE_ENV, opr0_target, opr1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src1_target, GL_ZERO); + checkGLcall("GL_TEXTURE_ENV, src1_target, GL_ZERO"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr1_target, invopr); + checkGLcall("GL_TEXTURE_ENV, opr1_target, invopr"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src2_target, src2); + checkGLcall("GL_TEXTURE_ENV, src2_target, src2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr2_target, opr2); + checkGLcall("GL_TEXTURE_ENV, opr2_target, opr2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src3_target, src1); + checkGLcall("GL_TEXTURE_ENV, src3_target, src1"); + switch (opr1) { + case GL_SRC_COLOR: opr = GL_ONE_MINUS_SRC_ALPHA; break; + case GL_ONE_MINUS_SRC_COLOR: opr = GL_SRC_ALPHA; break; + case GL_SRC_ALPHA: opr = GL_ONE_MINUS_SRC_ALPHA; break; + case GL_ONE_MINUS_SRC_ALPHA: opr = GL_SRC_ALPHA; break; + } + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr3_target, opr); + checkGLcall("GL_TEXTURE_ENV, opr3_target, opr"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, scal_target, 1); + checkGLcall("GL_TEXTURE_ENV, scal_target, 1"); + break; + case WINED3D_TOP_MODULATE_INVCOLOR_ADD_ALPHA: + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, comb_target, GL_ADD); + checkGLcall("GL_TEXTURE_ENV, comb_target, GL_ADD"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src0_target, src1); + checkGLcall("GL_TEXTURE_ENV, src0_target, src1"); + switch (opr1) { + case GL_SRC_COLOR: opr = GL_ONE_MINUS_SRC_COLOR; break; + case GL_ONE_MINUS_SRC_COLOR: opr = GL_SRC_COLOR; break; + case GL_SRC_ALPHA: opr = GL_ONE_MINUS_SRC_ALPHA; break; + case GL_ONE_MINUS_SRC_ALPHA: opr = GL_SRC_ALPHA; break; + } + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr0_target, opr); + checkGLcall("GL_TEXTURE_ENV, opr0_target, opr"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src1_target, src2); + checkGLcall("GL_TEXTURE_ENV, src1_target, src2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr1_target, opr2); + checkGLcall("GL_TEXTURE_ENV, opr1_target, opr2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src2_target, src1); + checkGLcall("GL_TEXTURE_ENV, src2_target, src1"); + switch (opr1) { + case GL_SRC_COLOR: opr = GL_SRC_ALPHA; break; + case GL_ONE_MINUS_SRC_COLOR: opr = GL_ONE_MINUS_SRC_ALPHA; break; + } + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr2_target, opr); + checkGLcall("GL_TEXTURE_ENV, opr2_target, opr"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src3_target, GL_ZERO); + checkGLcall("GL_TEXTURE_ENV, src3_target, GL_ZERO"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr3_target, invopr); + checkGLcall("GL_TEXTURE_ENV, opr3_target, invopr"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, scal_target, 1); + checkGLcall("GL_TEXTURE_ENV, scal_target, 1"); + break; + case WINED3D_TOP_MULTIPLY_ADD: + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, comb_target, GL_ADD); + checkGLcall("GL_TEXTURE_ENV, comb_target, GL_ADD"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src0_target, src3); + checkGLcall("GL_TEXTURE_ENV, src0_target, src1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr0_target, opr3); + checkGLcall("GL_TEXTURE_ENV, opr0_target, opr1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src1_target, GL_ZERO); + checkGLcall("GL_TEXTURE_ENV, src1_target, GL_ZERO"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr1_target, invopr); + checkGLcall("GL_TEXTURE_ENV, opr1_target, invopr"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src2_target, src1); + checkGLcall("GL_TEXTURE_ENV, src2_target, src2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr2_target, opr1); + checkGLcall("GL_TEXTURE_ENV, opr2_target, opr2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src3_target, src2); + checkGLcall("GL_TEXTURE_ENV, src3_target, src3"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr3_target, opr2); + checkGLcall("GL_TEXTURE_ENV, opr3_target, opr3"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, scal_target, 1); + checkGLcall("GL_TEXTURE_ENV, scal_target, 1"); + break; + + case WINED3D_TOP_BUMPENVMAP: + case WINED3D_TOP_BUMPENVMAP_LUMINANCE: + FIXME("Implement bump environment mapping in GL_NV_texture_env_combine4 path\n"); + Handled = FALSE; + break; + + default: + Handled = FALSE; + } + if (Handled) + { + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE4_NV); + checkGLcall("GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE4_NV"); + + return; + } + } /* GL_NV_texture_env_combine4 */ + + Handled = TRUE; /* Again, assume handled */ + switch (op) { + case WINED3D_TOP_DISABLE: /* Only for alpha */ + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, comb_target, GL_REPLACE); + checkGLcall("GL_TEXTURE_ENV, comb_target, GL_REPLACE"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src0_target, GL_PREVIOUS_EXT); + checkGLcall("GL_TEXTURE_ENV, src0_target, GL_PREVIOUS_EXT"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr0_target, GL_SRC_ALPHA); + checkGLcall("GL_TEXTURE_ENV, opr0_target, GL_SRC_ALPHA"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, scal_target, 1); + checkGLcall("GL_TEXTURE_ENV, scal_target, 1"); + break; + case WINED3D_TOP_SELECT_ARG1: + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, comb_target, GL_REPLACE); + checkGLcall("GL_TEXTURE_ENV, comb_target, GL_REPLACE"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src0_target, src1); + checkGLcall("GL_TEXTURE_ENV, src0_target, src1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr0_target, opr1); + checkGLcall("GL_TEXTURE_ENV, opr0_target, opr1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, scal_target, 1); + checkGLcall("GL_TEXTURE_ENV, scal_target, 1"); + break; + case WINED3D_TOP_SELECT_ARG2: + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, comb_target, GL_REPLACE); + checkGLcall("GL_TEXTURE_ENV, comb_target, GL_REPLACE"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src0_target, src2); + checkGLcall("GL_TEXTURE_ENV, src0_target, src2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr0_target, opr2); + checkGLcall("GL_TEXTURE_ENV, opr0_target, opr2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, scal_target, 1); + checkGLcall("GL_TEXTURE_ENV, scal_target, 1"); + break; + case WINED3D_TOP_MODULATE: + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, comb_target, GL_MODULATE); + checkGLcall("GL_TEXTURE_ENV, comb_target, GL_MODULATE"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src0_target, src1); + checkGLcall("GL_TEXTURE_ENV, src0_target, src1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr0_target, opr1); + checkGLcall("GL_TEXTURE_ENV, opr0_target, opr1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src1_target, src2); + checkGLcall("GL_TEXTURE_ENV, src1_target, src2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr1_target, opr2); + checkGLcall("GL_TEXTURE_ENV, opr1_target, opr2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, scal_target, 1); + checkGLcall("GL_TEXTURE_ENV, scal_target, 1"); + break; + case WINED3D_TOP_MODULATE_2X: + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, comb_target, GL_MODULATE); + checkGLcall("GL_TEXTURE_ENV, comb_target, GL_MODULATE"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src0_target, src1); + checkGLcall("GL_TEXTURE_ENV, src0_target, src1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr0_target, opr1); + checkGLcall("GL_TEXTURE_ENV, opr0_target, opr1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src1_target, src2); + checkGLcall("GL_TEXTURE_ENV, src1_target, src2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr1_target, opr2); + checkGLcall("GL_TEXTURE_ENV, opr1_target, opr2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, scal_target, 2); + checkGLcall("GL_TEXTURE_ENV, scal_target, 2"); + break; + case WINED3D_TOP_MODULATE_4X: + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, comb_target, GL_MODULATE); + checkGLcall("GL_TEXTURE_ENV, comb_target, GL_MODULATE"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src0_target, src1); + checkGLcall("GL_TEXTURE_ENV, src0_target, src1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr0_target, opr1); + checkGLcall("GL_TEXTURE_ENV, opr0_target, opr1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src1_target, src2); + checkGLcall("GL_TEXTURE_ENV, src1_target, src2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr1_target, opr2); + checkGLcall("GL_TEXTURE_ENV, opr1_target, opr2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, scal_target, 4); + checkGLcall("GL_TEXTURE_ENV, scal_target, 4"); + break; + case WINED3D_TOP_ADD: + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, comb_target, GL_ADD); + checkGLcall("GL_TEXTURE_ENV, comb_target, GL_ADD"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src0_target, src1); + checkGLcall("GL_TEXTURE_ENV, src0_target, src1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr0_target, opr1); + checkGLcall("GL_TEXTURE_ENV, opr0_target, opr1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src1_target, src2); + checkGLcall("GL_TEXTURE_ENV, src1_target, src2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr1_target, opr2); + checkGLcall("GL_TEXTURE_ENV, opr1_target, opr2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, scal_target, 1); + checkGLcall("GL_TEXTURE_ENV, scal_target, 1"); + break; + case WINED3D_TOP_ADD_SIGNED: + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, comb_target, GL_ADD_SIGNED); + checkGLcall("GL_TEXTURE_ENV, comb_target, GL_ADD_SIGNED"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src0_target, src1); + checkGLcall("GL_TEXTURE_ENV, src0_target, src1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr0_target, opr1); + checkGLcall("GL_TEXTURE_ENV, opr0_target, opr1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src1_target, src2); + checkGLcall("GL_TEXTURE_ENV, src1_target, src2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr1_target, opr2); + checkGLcall("GL_TEXTURE_ENV, opr1_target, opr2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, scal_target, 1); + checkGLcall("GL_TEXTURE_ENV, scal_target, 1"); + break; + case WINED3D_TOP_ADD_SIGNED_2X: + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, comb_target, GL_ADD_SIGNED); + checkGLcall("GL_TEXTURE_ENV, comb_target, GL_ADD_SIGNED"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src0_target, src1); + checkGLcall("GL_TEXTURE_ENV, src0_target, src1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr0_target, opr1); + checkGLcall("GL_TEXTURE_ENV, opr0_target, opr1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src1_target, src2); + checkGLcall("GL_TEXTURE_ENV, src1_target, src2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr1_target, opr2); + checkGLcall("GL_TEXTURE_ENV, opr1_target, opr2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, scal_target, 2); + checkGLcall("GL_TEXTURE_ENV, scal_target, 2"); + break; + case WINED3D_TOP_SUBTRACT: + if (gl_info->supported[ARB_TEXTURE_ENV_COMBINE]) + { + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, comb_target, GL_SUBTRACT); + checkGLcall("GL_TEXTURE_ENV, comb_target, GL_SUBTRACT"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src0_target, src1); + checkGLcall("GL_TEXTURE_ENV, src0_target, src1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr0_target, opr1); + checkGLcall("GL_TEXTURE_ENV, opr0_target, opr1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src1_target, src2); + checkGLcall("GL_TEXTURE_ENV, src1_target, src2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr1_target, opr2); + checkGLcall("GL_TEXTURE_ENV, opr1_target, opr2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, scal_target, 1); + checkGLcall("GL_TEXTURE_ENV, scal_target, 1"); + } else { + FIXME("This version of opengl does not support GL_SUBTRACT\n"); + } + break; + + case WINED3D_TOP_BLEND_DIFFUSE_ALPHA: + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, comb_target, GL_INTERPOLATE); + checkGLcall("GL_TEXTURE_ENV, comb_target, GL_INTERPOLATE"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src0_target, src1); + checkGLcall("GL_TEXTURE_ENV, src0_target, src1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr0_target, opr1); + checkGLcall("GL_TEXTURE_ENV, opr0_target, opr1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src1_target, src2); + checkGLcall("GL_TEXTURE_ENV, src1_target, src2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr1_target, opr2); + checkGLcall("GL_TEXTURE_ENV, opr1_target, opr2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src2_target, GL_PRIMARY_COLOR); + checkGLcall("GL_TEXTURE_ENV, src2_target, GL_PRIMARY_COLOR"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr2_target, GL_SRC_ALPHA); + checkGLcall("GL_TEXTURE_ENV, opr2_target, GL_SRC_ALPHA"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, scal_target, 1); + checkGLcall("GL_TEXTURE_ENV, scal_target, 1"); + break; + case WINED3D_TOP_BLEND_TEXTURE_ALPHA: + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, comb_target, GL_INTERPOLATE); + checkGLcall("GL_TEXTURE_ENV, comb_target, GL_INTERPOLATE"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src0_target, src1); + checkGLcall("GL_TEXTURE_ENV, src0_target, src1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr0_target, opr1); + checkGLcall("GL_TEXTURE_ENV, opr0_target, opr1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src1_target, src2); + checkGLcall("GL_TEXTURE_ENV, src1_target, src2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr1_target, opr2); + checkGLcall("GL_TEXTURE_ENV, opr1_target, opr2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src2_target, GL_TEXTURE); + checkGLcall("GL_TEXTURE_ENV, src2_target, GL_TEXTURE"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr2_target, GL_SRC_ALPHA); + checkGLcall("GL_TEXTURE_ENV, opr2_target, GL_SRC_ALPHA"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, scal_target, 1); + checkGLcall("GL_TEXTURE_ENV, scal_target, 1"); + break; + case WINED3D_TOP_BLEND_FACTOR_ALPHA: + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, comb_target, GL_INTERPOLATE); + checkGLcall("GL_TEXTURE_ENV, comb_target, GL_INTERPOLATE"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src0_target, src1); + checkGLcall("GL_TEXTURE_ENV, src0_target, src1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr0_target, opr1); + checkGLcall("GL_TEXTURE_ENV, opr0_target, opr1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src1_target, src2); + checkGLcall("GL_TEXTURE_ENV, src1_target, src2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr1_target, opr2); + checkGLcall("GL_TEXTURE_ENV, opr1_target, opr2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src2_target, GL_CONSTANT); + checkGLcall("GL_TEXTURE_ENV, src2_target, GL_CONSTANT"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr2_target, GL_SRC_ALPHA); + checkGLcall("GL_TEXTURE_ENV, opr2_target, GL_SRC_ALPHA"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, scal_target, 1); + checkGLcall("GL_TEXTURE_ENV, scal_target, 1"); + break; + case WINED3D_TOP_BLEND_CURRENT_ALPHA: + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, comb_target, GL_INTERPOLATE); + checkGLcall("GL_TEXTURE_ENV, comb_target, GL_INTERPOLATE"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src0_target, src1); + checkGLcall("GL_TEXTURE_ENV, src0_target, src1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr0_target, opr1); + checkGLcall("GL_TEXTURE_ENV, opr0_target, opr1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src1_target, src2); + checkGLcall("GL_TEXTURE_ENV, src1_target, src2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr1_target, opr2); + checkGLcall("GL_TEXTURE_ENV, opr1_target, opr2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src2_target, GL_PREVIOUS); + checkGLcall("GL_TEXTURE_ENV, src2_target, GL_PREVIOUS"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr2_target, GL_SRC_ALPHA); + checkGLcall("GL_TEXTURE_ENV, opr2_target, GL_SRC_ALPHA"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, scal_target, 1); + checkGLcall("GL_TEXTURE_ENV, scal_target, 1"); + break; + case WINED3D_TOP_DOTPRODUCT3: + if (gl_info->supported[ARB_TEXTURE_ENV_DOT3]) + { + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, comb_target, GL_DOT3_RGBA_ARB); + checkGLcall("GL_TEXTURE_ENV, comb_target, GL_DOT3_RGBA_ARB"); + } + else if (gl_info->supported[EXT_TEXTURE_ENV_DOT3]) + { + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, comb_target, GL_DOT3_RGBA_EXT); + checkGLcall("GL_TEXTURE_ENV, comb_target, GL_DOT3_RGBA_EXT"); + } else { + FIXME("This version of opengl does not support GL_DOT3\n"); + } + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src0_target, src1); + checkGLcall("GL_TEXTURE_ENV, src0_target, src1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr0_target, opr1); + checkGLcall("GL_TEXTURE_ENV, opr0_target, opr1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src1_target, src2); + checkGLcall("GL_TEXTURE_ENV, src1_target, src2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr1_target, opr2); + checkGLcall("GL_TEXTURE_ENV, opr1_target, opr2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, scal_target, 1); + checkGLcall("GL_TEXTURE_ENV, scal_target, 1"); + break; + case WINED3D_TOP_LERP: + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, comb_target, GL_INTERPOLATE); + checkGLcall("GL_TEXTURE_ENV, comb_target, GL_INTERPOLATE"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src0_target, src1); + checkGLcall("GL_TEXTURE_ENV, src0_target, src1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr0_target, opr1); + checkGLcall("GL_TEXTURE_ENV, opr0_target, opr1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src1_target, src2); + checkGLcall("GL_TEXTURE_ENV, src1_target, src2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr1_target, opr2); + checkGLcall("GL_TEXTURE_ENV, opr1_target, opr2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src2_target, src3); + checkGLcall("GL_TEXTURE_ENV, src2_target, src3"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr2_target, opr3); + checkGLcall("GL_TEXTURE_ENV, opr2_target, opr3"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, scal_target, 1); + checkGLcall("GL_TEXTURE_ENV, scal_target, 1"); + break; + case WINED3D_TOP_ADD_SMOOTH: + if (gl_info->supported[ATI_TEXTURE_ENV_COMBINE3]) + { + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, comb_target, GL_MODULATE_ADD_ATI); + checkGLcall("GL_TEXTURE_ENV, comb_target, GL_MODULATE_ADD_ATI"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src0_target, src1); + checkGLcall("GL_TEXTURE_ENV, src0_target, src1"); + switch (opr1) { + case GL_SRC_COLOR: opr = GL_ONE_MINUS_SRC_COLOR; break; + case GL_ONE_MINUS_SRC_COLOR: opr = GL_SRC_COLOR; break; + case GL_SRC_ALPHA: opr = GL_ONE_MINUS_SRC_ALPHA; break; + case GL_ONE_MINUS_SRC_ALPHA: opr = GL_SRC_ALPHA; break; + } + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr0_target, opr); + checkGLcall("GL_TEXTURE_ENV, opr0_target, opr"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src1_target, src1); + checkGLcall("GL_TEXTURE_ENV, src1_target, src1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr1_target, opr1); + checkGLcall("GL_TEXTURE_ENV, opr1_target, opr1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src2_target, src2); + checkGLcall("GL_TEXTURE_ENV, src2_target, src2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr2_target, opr2); + checkGLcall("GL_TEXTURE_ENV, opr2_target, opr2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, scal_target, 1); + checkGLcall("GL_TEXTURE_ENV, scal_target, 1"); + } else + Handled = FALSE; + break; + case WINED3D_TOP_BLEND_TEXTURE_ALPHA_PM: + if (gl_info->supported[ATI_TEXTURE_ENV_COMBINE3]) + { + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, comb_target, GL_MODULATE_ADD_ATI); + checkGLcall("GL_TEXTURE_ENV, comb_target, GL_MODULATE_ADD_ATI"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src0_target, GL_TEXTURE); + checkGLcall("GL_TEXTURE_ENV, src0_target, GL_TEXTURE"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr0_target, GL_ONE_MINUS_SRC_ALPHA); + checkGLcall("GL_TEXTURE_ENV, opr0_target, GL_ONE_MINUS_SRC_APHA"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src1_target, src1); + checkGLcall("GL_TEXTURE_ENV, src1_target, src1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr1_target, opr1); + checkGLcall("GL_TEXTURE_ENV, opr1_target, opr1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src2_target, src2); + checkGLcall("GL_TEXTURE_ENV, src2_target, src2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr2_target, opr2); + checkGLcall("GL_TEXTURE_ENV, opr2_target, opr2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, scal_target, 1); + checkGLcall("GL_TEXTURE_ENV, scal_target, 1"); + } else + Handled = FALSE; + break; + case WINED3D_TOP_MODULATE_ALPHA_ADD_COLOR: + if (gl_info->supported[ATI_TEXTURE_ENV_COMBINE3]) + { + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, comb_target, GL_MODULATE_ADD_ATI); + checkGLcall("GL_TEXTURE_ENV, comb_target, GL_MODULATE_ADD_ATI"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src0_target, src1); + checkGLcall("GL_TEXTURE_ENV, src0_target, src1"); + switch (opr1) { + case GL_SRC_COLOR: opr = GL_SRC_ALPHA; break; + case GL_ONE_MINUS_SRC_COLOR: opr = GL_ONE_MINUS_SRC_ALPHA; break; + case GL_SRC_ALPHA: opr = GL_SRC_ALPHA; break; + case GL_ONE_MINUS_SRC_ALPHA: opr = GL_ONE_MINUS_SRC_ALPHA; break; + } + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr0_target, opr); + checkGLcall("GL_TEXTURE_ENV, opr0_target, opr"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src1_target, src1); + checkGLcall("GL_TEXTURE_ENV, src1_target, src1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr1_target, opr1); + checkGLcall("GL_TEXTURE_ENV, opr1_target, opr1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src2_target, src2); + checkGLcall("GL_TEXTURE_ENV, src2_target, src2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr2_target, opr2); + checkGLcall("GL_TEXTURE_ENV, opr2_target, opr2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, scal_target, 1); + checkGLcall("GL_TEXTURE_ENV, scal_target, 1"); + } else + Handled = FALSE; + break; + case WINED3D_TOP_MODULATE_COLOR_ADD_ALPHA: + if (gl_info->supported[ATI_TEXTURE_ENV_COMBINE3]) + { + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, comb_target, GL_MODULATE_ADD_ATI); + checkGLcall("GL_TEXTURE_ENV, comb_target, GL_MODULATE_ADD_ATI"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src0_target, src1); + checkGLcall("GL_TEXTURE_ENV, src0_target, src1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr0_target, opr1); + checkGLcall("GL_TEXTURE_ENV, opr0_target, opr1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src1_target, src1); + checkGLcall("GL_TEXTURE_ENV, src1_target, src1"); + switch (opr1) { + case GL_SRC_COLOR: opr = GL_SRC_ALPHA; break; + case GL_ONE_MINUS_SRC_COLOR: opr = GL_ONE_MINUS_SRC_ALPHA; break; + case GL_SRC_ALPHA: opr = GL_SRC_ALPHA; break; + case GL_ONE_MINUS_SRC_ALPHA: opr = GL_ONE_MINUS_SRC_ALPHA; break; + } + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr1_target, opr); + checkGLcall("GL_TEXTURE_ENV, opr1_target, opr"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src2_target, src2); + checkGLcall("GL_TEXTURE_ENV, src2_target, src2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr2_target, opr2); + checkGLcall("GL_TEXTURE_ENV, opr2_target, opr2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, scal_target, 1); + checkGLcall("GL_TEXTURE_ENV, scal_target, 1"); + } else + Handled = FALSE; + break; + case WINED3D_TOP_MODULATE_INVALPHA_ADD_COLOR: + if (gl_info->supported[ATI_TEXTURE_ENV_COMBINE3]) + { + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, comb_target, GL_MODULATE_ADD_ATI); + checkGLcall("GL_TEXTURE_ENV, comb_target, GL_MODULATE_ADD_ATI"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src0_target, src1); + checkGLcall("GL_TEXTURE_ENV, src0_target, src1"); + switch (opr1) { + case GL_SRC_COLOR: opr = GL_ONE_MINUS_SRC_ALPHA; break; + case GL_ONE_MINUS_SRC_COLOR: opr = GL_SRC_ALPHA; break; + case GL_SRC_ALPHA: opr = GL_ONE_MINUS_SRC_ALPHA; break; + case GL_ONE_MINUS_SRC_ALPHA: opr = GL_SRC_ALPHA; break; + } + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr0_target, opr); + checkGLcall("GL_TEXTURE_ENV, opr0_target, opr"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src1_target, src1); + checkGLcall("GL_TEXTURE_ENV, src1_target, src1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr1_target, opr1); + checkGLcall("GL_TEXTURE_ENV, opr1_target, opr1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src2_target, src2); + checkGLcall("GL_TEXTURE_ENV, src2_target, src2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr2_target, opr2); + checkGLcall("GL_TEXTURE_ENV, opr2_target, opr2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, scal_target, 1); + checkGLcall("GL_TEXTURE_ENV, scal_target, 1"); + } else + Handled = FALSE; + break; + case WINED3D_TOP_MODULATE_INVCOLOR_ADD_ALPHA: + if (gl_info->supported[ATI_TEXTURE_ENV_COMBINE3]) + { + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, comb_target, GL_MODULATE_ADD_ATI); + checkGLcall("GL_TEXTURE_ENV, comb_target, GL_MODULATE_ADD_ATI"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src0_target, src1); + checkGLcall("GL_TEXTURE_ENV, src0_target, src1"); + switch (opr1) { + case GL_SRC_COLOR: opr = GL_ONE_MINUS_SRC_COLOR; break; + case GL_ONE_MINUS_SRC_COLOR: opr = GL_SRC_COLOR; break; + case GL_SRC_ALPHA: opr = GL_ONE_MINUS_SRC_ALPHA; break; + case GL_ONE_MINUS_SRC_ALPHA: opr = GL_SRC_ALPHA; break; + } + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr0_target, opr); + checkGLcall("GL_TEXTURE_ENV, opr0_target, opr"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src1_target, src1); + checkGLcall("GL_TEXTURE_ENV, src1_target, src1"); + switch (opr1) { + case GL_SRC_COLOR: opr = GL_SRC_ALPHA; break; + case GL_ONE_MINUS_SRC_COLOR: opr = GL_ONE_MINUS_SRC_ALPHA; break; + case GL_SRC_ALPHA: opr = GL_SRC_ALPHA; break; + case GL_ONE_MINUS_SRC_ALPHA: opr = GL_ONE_MINUS_SRC_ALPHA; break; + } + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr1_target, opr); + checkGLcall("GL_TEXTURE_ENV, opr1_target, opr"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src2_target, src2); + checkGLcall("GL_TEXTURE_ENV, src2_target, src2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr2_target, opr2); + checkGLcall("GL_TEXTURE_ENV, opr2_target, opr2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, scal_target, 1); + checkGLcall("GL_TEXTURE_ENV, scal_target, 1"); + } else + Handled = FALSE; + break; + case WINED3D_TOP_MULTIPLY_ADD: + if (gl_info->supported[ATI_TEXTURE_ENV_COMBINE3]) + { + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, comb_target, GL_MODULATE_ADD_ATI); + checkGLcall("GL_TEXTURE_ENV, comb_target, GL_MODULATE_ADD_ATI"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src0_target, src1); + checkGLcall("GL_TEXTURE_ENV, src0_target, src1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr0_target, opr1); + checkGLcall("GL_TEXTURE_ENV, opr0_target, opr1"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src1_target, src3); + checkGLcall("GL_TEXTURE_ENV, src1_target, src3"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr1_target, opr3); + checkGLcall("GL_TEXTURE_ENV, opr1_target, opr3"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, src2_target, src2); + checkGLcall("GL_TEXTURE_ENV, src2_target, src2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, opr2_target, opr2); + checkGLcall("GL_TEXTURE_ENV, opr2_target, opr2"); + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, scal_target, 1); + checkGLcall("GL_TEXTURE_ENV, scal_target, 1"); + } else + Handled = FALSE; + break; + case WINED3D_TOP_BUMPENVMAP_LUMINANCE: + case WINED3D_TOP_BUMPENVMAP: + if (gl_info->supported[NV_TEXTURE_SHADER2]) + { + /* Technically texture shader support without register combiners is possible, but not expected to occur + * on real world cards, so for now a fixme should be enough + */ + FIXME("Implement bump mapping with GL_NV_texture_shader in non register combiner path\n"); + } + Handled = FALSE; + break; + + default: + Handled = FALSE; + } + + if (Handled) { + BOOL combineOK = TRUE; + if (gl_info->supported[NV_TEXTURE_ENV_COMBINE4]) + { + DWORD op2; + + if (isAlpha) + op2 = state->texture_states[Stage][WINED3D_TSS_COLOR_OP]; + else + op2 = state->texture_states[Stage][WINED3D_TSS_ALPHA_OP]; + + /* Note: If COMBINE4 in effect can't go back to combine! */ + switch (op2) + { + case WINED3D_TOP_ADD_SMOOTH: + case WINED3D_TOP_BLEND_TEXTURE_ALPHA_PM: + case WINED3D_TOP_MODULATE_ALPHA_ADD_COLOR: + case WINED3D_TOP_MODULATE_COLOR_ADD_ALPHA: + case WINED3D_TOP_MODULATE_INVALPHA_ADD_COLOR: + case WINED3D_TOP_MODULATE_INVCOLOR_ADD_ALPHA: + case WINED3D_TOP_MULTIPLY_ADD: + /* Ignore those implemented in both cases */ + switch (op) + { + case WINED3D_TOP_SELECT_ARG1: + case WINED3D_TOP_SELECT_ARG2: + combineOK = FALSE; + Handled = FALSE; + break; + default: + FIXME("Can't use COMBINE4 and COMBINE together, thisop=%s, otherop=%s, isAlpha(%d)\n", debug_d3dtop(op), debug_d3dtop(op2), isAlpha); + return; + } + } + } + + if (combineOK) + { + gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + checkGLcall("GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE"); + + return; + } + } + + /* After all the extensions, if still unhandled, report fixme */ + FIXME("Unhandled texture operation %s\n", debug_d3dtop(op)); +} + + +static void tex_colorop(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + unsigned int stage = (state_id - STATE_TEXTURESTAGE(0, 0)) / (WINED3D_HIGHEST_TEXTURE_STATE + 1); + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + BOOL tex_used = context->fixed_function_usage_map & (1u << stage); + unsigned int mapped_stage = context_gl->tex_unit_map[stage]; + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + + TRACE("Setting color op for stage %d\n", stage); + + /* Using a pixel shader? Don't care for anything here, the shader applying does it */ + if (use_ps(state)) return; + + if (stage != mapped_stage) WARN("Using non 1:1 mapping: %d -> %d!\n", stage, mapped_stage); + + if (mapped_stage != WINED3D_UNMAPPED_STAGE) + { + if (tex_used && mapped_stage >= gl_info->limits.textures) + { + FIXME("Attempt to enable unsupported stage!\n"); + return; + } + wined3d_context_gl_active_texture(context_gl, gl_info, mapped_stage); + } + + if (stage >= context->lowest_disabled_stage) + { + TRACE("Stage disabled\n"); + if (mapped_stage != WINED3D_UNMAPPED_STAGE) + { + /* Disable everything here */ + gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_2D); + checkGLcall("glDisable(GL_TEXTURE_2D)"); + gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_3D); + checkGLcall("glDisable(GL_TEXTURE_3D)"); + if (gl_info->supported[ARB_TEXTURE_CUBE_MAP]) + { + gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_CUBE_MAP_ARB); + checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)"); + } + if (gl_info->supported[ARB_TEXTURE_RECTANGLE]) + { + gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_RECTANGLE_ARB); + checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)"); + } + } + /* All done */ + return; + } + + /* The sampler will also activate the correct texture dimensions, so no + * need to do it here if the sampler for this stage is dirty. */ + if (!isStateDirty(context, STATE_SAMPLER(stage)) && tex_used) + texture_activate_dimensions(state->textures[stage], gl_info); + + set_tex_op(gl_info, state, FALSE, stage, + state->texture_states[stage][WINED3D_TSS_COLOR_OP], + state->texture_states[stage][WINED3D_TSS_COLOR_ARG1], + state->texture_states[stage][WINED3D_TSS_COLOR_ARG2], + state->texture_states[stage][WINED3D_TSS_COLOR_ARG0]); +} + +void tex_alphaop(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + unsigned int stage = (state_id - STATE_TEXTURESTAGE(0, 0)) / (WINED3D_HIGHEST_TEXTURE_STATE + 1); + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + BOOL tex_used = context->fixed_function_usage_map & (1u << stage); + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + unsigned int mapped_stage = context_gl->tex_unit_map[stage]; + DWORD op, arg1, arg2, arg0; + + TRACE("Setting alpha op for stage %d\n", stage); + /* Do not care for enabled / disabled stages, just assign the settings. colorop disables / enables required stuff */ + if (mapped_stage != WINED3D_UNMAPPED_STAGE) + { + if (tex_used && mapped_stage >= gl_info->limits.textures) + { + FIXME("Attempt to enable unsupported stage!\n"); + return; + } + wined3d_context_gl_active_texture(context_gl, gl_info, mapped_stage); + } + + op = state->texture_states[stage][WINED3D_TSS_ALPHA_OP]; + arg1 = state->texture_states[stage][WINED3D_TSS_ALPHA_ARG1]; + arg2 = state->texture_states[stage][WINED3D_TSS_ALPHA_ARG2]; + arg0 = state->texture_states[stage][WINED3D_TSS_ALPHA_ARG0]; + + if (state->render_states[WINED3D_RS_COLORKEYENABLE] && !stage && state->textures[0]) + { + struct wined3d_texture_gl *texture_gl = wined3d_texture_gl(state->textures[0]); + GLenum texture_dimensions = texture_gl->target; + + if (texture_dimensions == GL_TEXTURE_2D || texture_dimensions == GL_TEXTURE_RECTANGLE_ARB) + { + if (texture_gl->t.async.color_key_flags & WINED3D_CKEY_SRC_BLT + && !texture_gl->t.resource.format->alpha_size) + { + /* Color keying needs to pass alpha values from the texture through to have the alpha test work + * properly. On the other hand applications can still use texture combiners apparently. This code + * takes care that apps cannot remove the texture's alpha channel entirely. + * + * The fixup is required for Prince of Persia 3D(prison bars), while Moto racer 2 requires + * D3DTOP_MODULATE to work on color keyed surfaces. Aliens vs Predator 1 uses color keyed textures + * and alpha component of diffuse color to draw things like translucent text and perform other + * blending effects. + * + * Aliens vs Predator 1 relies on diffuse alpha having an effect, so it cannot be ignored. To + * provide the behavior expected by the game, while emulating the colorkey, diffuse alpha must be + * modulated with texture alpha. OTOH, Moto racer 2 at some points sets alphaop/alphaarg to + * SELECTARG/CURRENT, yet puts garbage in diffuse alpha (zeroes). This works on native, because the + * game disables alpha test and alpha blending. Alpha test is overwritten by wine's for purposes of + * color-keying though, so this will lead to missing geometry if texture alpha is modulated (pixels + * fail alpha test). To get around this, blend state is checked: if the app enables alpha blending, + * it can be expected to provide meaningful values in diffuse alpha, so it should be modulated with + * texture alpha; otherwise, selecting diffuse alpha is ignored in favour of texture alpha. + * + * What to do with multitexturing? So far no app has been found that uses color keying with + * multitexturing */ + if (op == WINED3D_TOP_DISABLE) + { + arg1 = WINED3DTA_TEXTURE; + op = WINED3D_TOP_SELECT_ARG1; + } + else if (op == WINED3D_TOP_SELECT_ARG1 && arg1 != WINED3DTA_TEXTURE) + { + if (state->blend_state && state->blend_state->desc.rt[0].enable) + { + arg2 = WINED3DTA_TEXTURE; + op = WINED3D_TOP_MODULATE; + } + else arg1 = WINED3DTA_TEXTURE; + } + else if (op == WINED3D_TOP_SELECT_ARG2 && arg2 != WINED3DTA_TEXTURE) + { + if (state->blend_state && state->blend_state->desc.rt[0].enable) + { + arg1 = WINED3DTA_TEXTURE; + op = WINED3D_TOP_MODULATE; + } + else arg2 = WINED3DTA_TEXTURE; + } + } + } + } + + /* tex_alphaop is shared between the ffp and nvrc because the difference only comes down to + * this if block here, and the other code(color keying, texture unit selection) are the same + */ + TRACE("Setting alpha op for stage %d\n", stage); + if (gl_info->supported[NV_REGISTER_COMBINERS]) + { + set_tex_op_nvrc(gl_info, state, TRUE, stage, op, arg1, arg2, arg0, + mapped_stage, state->texture_states[stage][WINED3D_TSS_RESULT_ARG]); + } + else + { + set_tex_op(gl_info, state, TRUE, stage, op, arg1, arg2, arg0); + } +} + +static void transform_texture(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + unsigned int tex = (state_id - STATE_TEXTURESTAGE(0, 0)) / (WINED3D_HIGHEST_TEXTURE_STATE + 1); + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + unsigned int mapped_stage = context_gl->tex_unit_map[tex]; + struct wined3d_matrix mat; + + /* Ignore this when a vertex shader is used, or if the streams aren't sorted out yet */ + if (use_vs(state) || isStateDirty(context, STATE_VDECL)) + { + TRACE("Using a vertex shader, or stream sources not sorted out yet, skipping\n"); + return; + } + + if (mapped_stage == WINED3D_UNMAPPED_STAGE) return; + if (mapped_stage >= gl_info->limits.textures) return; + + wined3d_context_gl_active_texture(context_gl, gl_info, mapped_stage); + gl_info->gl_ops.gl.p_glMatrixMode(GL_TEXTURE); + checkGLcall("glMatrixMode(GL_TEXTURE)"); + + get_texture_matrix(context, state, mapped_stage, &mat); + + gl_info->gl_ops.gl.p_glLoadMatrixf(&mat._11); + checkGLcall("glLoadMatrixf"); +} + +static void tex_coordindex(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + unsigned int stage = (state_id - STATE_TEXTURESTAGE(0, 0)) / (WINED3D_HIGHEST_TEXTURE_STATE + 1); + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + unsigned int mapped_stage = context_gl->tex_unit_map[stage]; + + static const GLfloat s_plane[] = { 1.0f, 0.0f, 0.0f, 0.0f }; + static const GLfloat t_plane[] = { 0.0f, 1.0f, 0.0f, 0.0f }; + static const GLfloat r_plane[] = { 0.0f, 0.0f, 1.0f, 0.0f }; + static const GLfloat q_plane[] = { 0.0f, 0.0f, 0.0f, 1.0f }; + + if (mapped_stage == WINED3D_UNMAPPED_STAGE) + { + TRACE("No texture unit mapped to stage %d. Skipping texture coordinates.\n", stage); + return; + } + + if (mapped_stage >= min(gl_info->limits.samplers[WINED3D_SHADER_TYPE_PIXEL], WINED3D_MAX_FRAGMENT_SAMPLERS)) + { + WARN("stage %u not mapped to a valid texture unit (%u)\n", stage, mapped_stage); + return; + } + wined3d_context_gl_active_texture(context_gl, gl_info, mapped_stage); + + /* Values 0-7 are indexes into the FVF tex coords - See comments in DrawPrimitive + * + * FIXME: When using generated texture coordinates, the index value is used to specify the wrapping mode. + * eg. SetTextureStageState( 0, WINED3D_TSS_TEXCOORDINDEX, WINED3D_TSS_TCI_CAMERASPACEPOSITION | 1 ); + * means use the vertex position (camera-space) as the input texture coordinates + * for this texture stage, and the wrap mode set in the WINED3D_RS_WRAP1 render + * state. We do not (yet) support the WINED3DRENDERSTATE_WRAPx values, nor tie them up + * to the TEXCOORDINDEX value + */ + switch (state->texture_states[stage][WINED3D_TSS_TEXCOORD_INDEX] & 0xffff0000) + { + case WINED3DTSS_TCI_PASSTHRU: + /* Use the specified texture coordinates contained within the + * vertex format. This value resolves to zero. */ + gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_GEN_S); + gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_GEN_T); + gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_GEN_R); + gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_GEN_Q); + checkGLcall("WINED3DTSS_TCI_PASSTHRU - Disable texgen."); + break; + + case WINED3DTSS_TCI_CAMERASPACEPOSITION: + /* CameraSpacePosition means use the vertex position, transformed to camera space, + * as the input texture coordinates for this stage's texture transformation. This + * equates roughly to EYE_LINEAR */ + + gl_info->gl_ops.gl.p_glMatrixMode(GL_MODELVIEW); + gl_info->gl_ops.gl.p_glPushMatrix(); + gl_info->gl_ops.gl.p_glLoadIdentity(); + gl_info->gl_ops.gl.p_glTexGenfv(GL_S, GL_EYE_PLANE, s_plane); + gl_info->gl_ops.gl.p_glTexGenfv(GL_T, GL_EYE_PLANE, t_plane); + gl_info->gl_ops.gl.p_glTexGenfv(GL_R, GL_EYE_PLANE, r_plane); + gl_info->gl_ops.gl.p_glTexGenfv(GL_Q, GL_EYE_PLANE, q_plane); + gl_info->gl_ops.gl.p_glPopMatrix(); + checkGLcall("WINED3DTSS_TCI_CAMERASPACEPOSITION - Set eye plane."); + + gl_info->gl_ops.gl.p_glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR); + gl_info->gl_ops.gl.p_glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR); + gl_info->gl_ops.gl.p_glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR); + checkGLcall("WINED3DTSS_TCI_CAMERASPACEPOSITION - Set texgen mode."); + + gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_GEN_S); + gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_GEN_T); + gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_GEN_R); + checkGLcall("WINED3DTSS_TCI_CAMERASPACEPOSITION - Enable texgen."); + + break; + + case WINED3DTSS_TCI_CAMERASPACENORMAL: + /* Note that NV_TEXGEN_REFLECTION support is implied when + * ARB_TEXTURE_CUBE_MAP is supported */ + if (!gl_info->supported[NV_TEXGEN_REFLECTION]) + { + FIXME("WINED3DTSS_TCI_CAMERASPACENORMAL not supported.\n"); + break; + } + + gl_info->gl_ops.gl.p_glMatrixMode(GL_MODELVIEW); + gl_info->gl_ops.gl.p_glPushMatrix(); + gl_info->gl_ops.gl.p_glLoadIdentity(); + gl_info->gl_ops.gl.p_glTexGenfv(GL_S, GL_EYE_PLANE, s_plane); + gl_info->gl_ops.gl.p_glTexGenfv(GL_T, GL_EYE_PLANE, t_plane); + gl_info->gl_ops.gl.p_glTexGenfv(GL_R, GL_EYE_PLANE, r_plane); + gl_info->gl_ops.gl.p_glTexGenfv(GL_Q, GL_EYE_PLANE, q_plane); + gl_info->gl_ops.gl.p_glPopMatrix(); + checkGLcall("WINED3DTSS_TCI_CAMERASPACENORMAL - Set eye plane."); + + gl_info->gl_ops.gl.p_glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_NV); + gl_info->gl_ops.gl.p_glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_NV); + gl_info->gl_ops.gl.p_glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_NV); + checkGLcall("WINED3DTSS_TCI_CAMERASPACENORMAL - Set texgen mode."); + + gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_GEN_S); + gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_GEN_T); + gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_GEN_R); + checkGLcall("WINED3DTSS_TCI_CAMERASPACENORMAL - Enable texgen."); + + break; + + case WINED3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR: + /* Note that NV_TEXGEN_REFLECTION support is implied when + * ARB_TEXTURE_CUBE_MAP is supported */ + if (!gl_info->supported[NV_TEXGEN_REFLECTION]) + { + FIXME("WINED3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR not supported.\n"); + break; + } + + gl_info->gl_ops.gl.p_glMatrixMode(GL_MODELVIEW); + gl_info->gl_ops.gl.p_glPushMatrix(); + gl_info->gl_ops.gl.p_glLoadIdentity(); + gl_info->gl_ops.gl.p_glTexGenfv(GL_S, GL_EYE_PLANE, s_plane); + gl_info->gl_ops.gl.p_glTexGenfv(GL_T, GL_EYE_PLANE, t_plane); + gl_info->gl_ops.gl.p_glTexGenfv(GL_R, GL_EYE_PLANE, r_plane); + gl_info->gl_ops.gl.p_glTexGenfv(GL_Q, GL_EYE_PLANE, q_plane); + gl_info->gl_ops.gl.p_glPopMatrix(); + checkGLcall("WINED3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR - Set eye plane."); + + gl_info->gl_ops.gl.p_glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_NV); + gl_info->gl_ops.gl.p_glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_NV); + gl_info->gl_ops.gl.p_glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_NV); + checkGLcall("WINED3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR - Set texgen mode."); + + gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_GEN_S); + gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_GEN_T); + gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_GEN_R); + checkGLcall("WINED3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR - Enable texgen."); + + break; + + case WINED3DTSS_TCI_SPHEREMAP: + gl_info->gl_ops.gl.p_glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); + gl_info->gl_ops.gl.p_glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); + checkGLcall("WINED3DTSS_TCI_SPHEREMAP - Set texgen mode."); + + gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_GEN_S); + gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_GEN_T); + gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_GEN_R); + checkGLcall("WINED3DTSS_TCI_SPHEREMAP - Enable texgen."); + + break; + + default: + FIXME("Unhandled WINED3D_TSS_TEXCOORD_INDEX %#x.\n", + state->texture_states[stage][WINED3D_TSS_TEXCOORD_INDEX]); + gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_GEN_S); + gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_GEN_T); + gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_GEN_R); + gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_GEN_Q); + checkGLcall("Disable texgen."); + + break; + } + + /* Update the texture matrix. */ + if (!isStateDirty(context, STATE_TRANSFORM(WINED3D_TS_TEXTURE0 + stage))) + transform_texture(context, state, STATE_TEXTURESTAGE(stage, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS)); + + if (!isStateDirty(context, STATE_VDECL) && context->namedArraysLoaded) + { + /* Reload the arrays if we are using fixed function arrays to reflect the selected coord input + * source. Call loadTexCoords directly because there is no need to reparse the vertex declaration + * and do all the things linked to it + * TODO: Tidy that up to reload only the arrays of the changed unit + */ + GLuint curVBO = gl_info->supported[ARB_VERTEX_BUFFER_OBJECT] ? ~0U : 0; + + wined3d_context_gl_unload_tex_coords(context_gl); + wined3d_context_gl_load_tex_coords(context_gl, &context->stream_info, &curVBO, state); + } +} + +static void sampler_texmatrix(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + const DWORD sampler = state_id - STATE_SAMPLER(0); + const struct wined3d_texture *texture = state->textures[sampler]; + + TRACE("context %p, state %p, state_id %#x.\n", context, state, state_id); + + if (!texture) + return; + + /* The fixed function np2 texture emulation uses the texture matrix to fix up the coordinates + * wined3d_texture_apply_state_changes() multiplies the set matrix with a fixup matrix. Before the + * scaling is reapplied or removed, the texture matrix has to be reapplied. + */ + if (sampler < WINED3D_MAX_TEXTURES) + { + const BOOL tex_is_pow2 = !(texture->flags & WINED3D_TEXTURE_POW2_MAT_IDENT); + + if (tex_is_pow2 || (context->lastWasPow2Texture & (1u << sampler))) + { + if (tex_is_pow2) + context->lastWasPow2Texture |= 1u << sampler; + else + context->lastWasPow2Texture &= ~(1u << sampler); + + transform_texture(context, state, STATE_TEXTURESTAGE(sampler, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS)); + } + } +} + +static enum wined3d_texture_address wined3d_texture_gl_address_mode(const struct wined3d_texture_gl *texture_gl, + enum wined3d_texture_address t) +{ + if (t < WINED3D_TADDRESS_WRAP || t > WINED3D_TADDRESS_MIRROR_ONCE) + { + FIXME("Unrecognized or unsupported texture address mode %#x.\n", t); + return WINED3D_TADDRESS_WRAP; + } + + /* Cubemaps are always set to clamp, regardless of the sampler state. */ + if (texture_gl->target == GL_TEXTURE_CUBE_MAP_ARB || ((texture_gl->t.flags & WINED3D_TEXTURE_COND_NP2) + && t == WINED3D_TADDRESS_WRAP)) + return WINED3D_TADDRESS_CLAMP; + + return t; +} + +static void wined3d_sampler_desc_from_sampler_states(struct wined3d_sampler_desc *desc, + const struct wined3d_context_gl *context_gl, const DWORD *sampler_states, + const struct wined3d_texture_gl *texture_gl) +{ + union + { + float f; + DWORD d; + } lod_bias; + + desc->address_u = wined3d_texture_gl_address_mode(texture_gl, sampler_states[WINED3D_SAMP_ADDRESS_U]); + desc->address_v = wined3d_texture_gl_address_mode(texture_gl, sampler_states[WINED3D_SAMP_ADDRESS_V]); + desc->address_w = wined3d_texture_gl_address_mode(texture_gl, sampler_states[WINED3D_SAMP_ADDRESS_W]); + wined3d_color_from_d3dcolor((struct wined3d_color *)desc->border_color, + sampler_states[WINED3D_SAMP_BORDER_COLOR]); + if (sampler_states[WINED3D_SAMP_MAG_FILTER] > WINED3D_TEXF_ANISOTROPIC) + FIXME("Unrecognized or unsupported WINED3D_SAMP_MAG_FILTER %#x.\n", + sampler_states[WINED3D_SAMP_MAG_FILTER]); + desc->mag_filter = min(max(sampler_states[WINED3D_SAMP_MAG_FILTER], WINED3D_TEXF_POINT), WINED3D_TEXF_LINEAR); + if (sampler_states[WINED3D_SAMP_MIN_FILTER] > WINED3D_TEXF_ANISOTROPIC) + FIXME("Unrecognized or unsupported WINED3D_SAMP_MIN_FILTER %#x.\n", + sampler_states[WINED3D_SAMP_MIN_FILTER]); + desc->min_filter = min(max(sampler_states[WINED3D_SAMP_MIN_FILTER], WINED3D_TEXF_POINT), WINED3D_TEXF_LINEAR); + if (sampler_states[WINED3D_SAMP_MIP_FILTER] > WINED3D_TEXF_ANISOTROPIC) + FIXME("Unrecognized or unsupported WINED3D_SAMP_MIP_FILTER %#x.\n", + sampler_states[WINED3D_SAMP_MIP_FILTER]); + desc->mip_filter = min(max(sampler_states[WINED3D_SAMP_MIP_FILTER], WINED3D_TEXF_NONE), WINED3D_TEXF_LINEAR); + lod_bias.d = sampler_states[WINED3D_SAMP_MIPMAP_LOD_BIAS]; + desc->lod_bias = lod_bias.f; + desc->min_lod = -1000.0f; + desc->max_lod = 1000.0f; + desc->mip_base_level = sampler_states[WINED3D_SAMP_MAX_MIP_LEVEL]; + desc->max_anisotropy = sampler_states[WINED3D_SAMP_MAX_ANISOTROPY]; + if ((sampler_states[WINED3D_SAMP_MAG_FILTER] != WINED3D_TEXF_ANISOTROPIC + && sampler_states[WINED3D_SAMP_MIN_FILTER] != WINED3D_TEXF_ANISOTROPIC + && sampler_states[WINED3D_SAMP_MIP_FILTER] != WINED3D_TEXF_ANISOTROPIC) + || (texture_gl->t.flags & WINED3D_TEXTURE_COND_NP2)) + desc->max_anisotropy = 1; + desc->compare = texture_gl->t.resource.format_flags & WINED3DFMT_FLAG_SHADOW; + desc->comparison_func = WINED3D_CMP_LESSEQUAL; + desc->srgb_decode = is_srgb_enabled(sampler_states); + + if (!(texture_gl->t.resource.format_flags & WINED3DFMT_FLAG_FILTERING)) + { + desc->mag_filter = WINED3D_TEXF_POINT; + desc->min_filter = WINED3D_TEXF_POINT; + desc->mip_filter = WINED3D_TEXF_NONE; + } + + if (texture_gl->t.flags & WINED3D_TEXTURE_COND_NP2) + { + desc->mip_filter = WINED3D_TEXF_NONE; + if (context_gl->gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT]) + desc->min_filter = WINED3D_TEXF_POINT; + } +} + +/* Enabling and disabling texture dimensions is done by texture stage state / + * pixel shader setup, this function only has to bind textures and set the per + * texture states. */ +static void sampler(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + unsigned int sampler_idx = state_id - STATE_SAMPLER(0); + unsigned int mapped_stage = context_gl->tex_unit_map[sampler_idx]; + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + + TRACE("Sampler %u.\n", sampler_idx); + + if (mapped_stage == WINED3D_UNMAPPED_STAGE) + { + TRACE("No sampler mapped to stage %u. Returning.\n", sampler_idx); + return; + } + + if (mapped_stage >= gl_info->limits.graphics_samplers) + return; + wined3d_context_gl_active_texture(context_gl, gl_info, mapped_stage); + + if (state->textures[sampler_idx]) + { + struct wined3d_texture_gl *texture_gl = wined3d_texture_gl(state->textures[sampler_idx]); + const DWORD *sampler_states = state->sampler_states[sampler_idx]; + struct wined3d_device *device = context->device; + BOOL srgb = is_srgb_enabled(sampler_states); + struct wined3d_sampler_desc desc; + struct wined3d_sampler *sampler; + struct wine_rb_entry *entry; + + wined3d_sampler_desc_from_sampler_states(&desc, context_gl, sampler_states, texture_gl); + + wined3d_texture_gl_bind(texture_gl, context_gl, srgb); + + if ((entry = wine_rb_get(&device->samplers, &desc))) + { + sampler = WINE_RB_ENTRY_VALUE(entry, struct wined3d_sampler, entry); + } + else + { + if (FAILED(wined3d_sampler_create(device, &desc, NULL, &wined3d_null_parent_ops, &sampler))) + { + ERR("Failed to create sampler.\n"); + return; + } + if (wine_rb_put(&device->samplers, &desc, &sampler->entry) == -1) + { + ERR("Failed to insert sampler.\n"); + wined3d_sampler_decref(sampler); + return; + } + } + + wined3d_sampler_gl_bind(wined3d_sampler_gl(sampler), mapped_stage, texture_gl, context_gl); + + /* Trigger shader constant reloading (for NP2 texcoord fixup) */ + if (!(texture_gl->t.flags & WINED3D_TEXTURE_POW2_MAT_IDENT)) + context->constant_update_mask |= WINED3D_SHADER_CONST_PS_NP2_FIXUP; + } + else + { + wined3d_context_gl_bind_texture(context_gl, GL_NONE, 0); + if (gl_info->supported[ARB_SAMPLER_OBJECTS]) + { + GL_EXTCALL(glBindSampler(mapped_stage, 0)); + checkGLcall("glBindSampler"); + } + } +} + +void apply_pixelshader(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + unsigned int i; + + if (use_ps(state)) + { + if (!context->last_was_pshader) + { + /* Former draw without a pixel shader, some samplers may be + * disabled because of WINED3D_TSS_COLOR_OP = WINED3DTOP_DISABLE + * make sure to enable them. */ + for (i = 0; i < WINED3D_MAX_FRAGMENT_SAMPLERS; ++i) + { + if (!isStateDirty(context, STATE_SAMPLER(i))) + sampler(context, state, STATE_SAMPLER(i)); + } + context->last_was_pshader = TRUE; + } + else + { + /* Otherwise all samplers were activated by the code above in + * earlier draws, or by sampler() if a different texture was + * bound. I don't have to do anything. */ + } + } + else + { + /* Disabled the pixel shader - color ops weren't applied while it was + * enabled, so re-apply them. */ + for (i = 0; i < context->d3d_info->limits.ffp_blend_stages; ++i) + { + if (!isStateDirty(context, STATE_TEXTURESTAGE(i, WINED3D_TSS_COLOR_OP))) + context_apply_state(context, state, STATE_TEXTURESTAGE(i, WINED3D_TSS_COLOR_OP)); + } + context->last_was_pshader = FALSE; + } + + context->shader_update_mask |= 1u << WINED3D_SHADER_TYPE_PIXEL; +} + +static void state_compute_shader(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + context->shader_update_mask |= 1u << WINED3D_SHADER_TYPE_COMPUTE; +} + +static void state_shader(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + enum wined3d_shader_type shader_type = state_id - STATE_SHADER(0); + context->shader_update_mask |= 1u << shader_type; +} + +static void shader_bumpenv(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + context->constant_update_mask |= WINED3D_SHADER_CONST_PS_BUMP_ENV; +} + +static void transform_world(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl(context)->gl_info; + struct wined3d_matrix mat; + + /* This function is called by transform_view below if the view matrix was changed too + * + * Deliberately no check if the vertex declaration is dirty because the vdecl state + * does not always update the world matrix, only on a switch between transformed + * and untransformed draws. It *may* happen that the world matrix is set 2 times during one + * draw, but that should be rather rare and cheaper in total. + */ + gl_info->gl_ops.gl.p_glMatrixMode(GL_MODELVIEW); + checkGLcall("glMatrixMode"); + + get_modelview_matrix(context, state, 0, &mat); + + gl_info->gl_ops.gl.p_glLoadMatrixf((GLfloat *)&mat); + checkGLcall("glLoadMatrixf"); +} + +void clipplane(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl(context)->gl_info; + UINT index = state_id - STATE_CLIPPLANE(0); + GLdouble plane[4]; + + if (isStateDirty(context, STATE_TRANSFORM(WINED3D_TS_VIEW)) || index >= gl_info->limits.user_clip_distances) + return; + + gl_info->gl_ops.gl.p_glMatrixMode(GL_MODELVIEW); + gl_info->gl_ops.gl.p_glPushMatrix(); + + /* Clip Plane settings are affected by the model view in OpenGL, the View transform in direct3d */ + if (!use_vs(state)) + gl_info->gl_ops.gl.p_glLoadMatrixf(&state->transforms[WINED3D_TS_VIEW]._11); + else + /* With vertex shaders, clip planes are not transformed in Direct3D, + * while in OpenGL they are still transformed by the model view matrix. */ + gl_info->gl_ops.gl.p_glLoadIdentity(); + + plane[0] = state->clip_planes[index].x; + plane[1] = state->clip_planes[index].y; + plane[2] = state->clip_planes[index].z; + plane[3] = state->clip_planes[index].w; + + TRACE("Clipplane [%.8e, %.8e, %.8e, %.8e]\n", + plane[0], plane[1], plane[2], plane[3]); + gl_info->gl_ops.gl.p_glClipPlane(GL_CLIP_PLANE0 + index, plane); + checkGLcall("glClipPlane"); + + gl_info->gl_ops.gl.p_glPopMatrix(); +} + +static void transform_worldex(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + unsigned int matrix = state_id - STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(0)); + + WARN("Unsupported world matrix %u set.\n", matrix); +} + +static void state_vertexblend_w(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + enum wined3d_vertex_blend_flags f = state->render_states[WINED3D_RS_VERTEXBLEND]; + static unsigned int once; + + if (f == WINED3D_VBF_DISABLE) + return; + + if (!once++) FIXME("Vertex blend flags %#x not supported.\n", f); + else WARN("Vertex blend flags %#x not supported.\n", f); +} + +static void transform_view(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl(context)->gl_info; + const struct wined3d_light_info *light = NULL; + unsigned int k; + + /* If we are changing the View matrix, reset the light and clipping planes to the new view + * NOTE: We have to reset the positions even if the light/plane is not currently + * enabled, since the call to enable it will not reset the position. + * NOTE2: Apparently texture transforms do NOT need reapplying + */ + + gl_info->gl_ops.gl.p_glMatrixMode(GL_MODELVIEW); + checkGLcall("glMatrixMode(GL_MODELVIEW)"); + gl_info->gl_ops.gl.p_glLoadMatrixf(&state->transforms[WINED3D_TS_VIEW]._11); + checkGLcall("glLoadMatrixf(...)"); + + /* Reset lights. TODO: Call light apply func */ + for (k = 0; k < gl_info->limits.lights; ++k) + { + if (!(light = state->light_state.lights[k])) + continue; + if (light->OriginalParms.type == WINED3D_LIGHT_DIRECTIONAL) + gl_info->gl_ops.gl.p_glLightfv(GL_LIGHT0 + light->glIndex, GL_POSITION, &light->direction.x); + else + gl_info->gl_ops.gl.p_glLightfv(GL_LIGHT0 + light->glIndex, GL_POSITION, &light->position.x); + checkGLcall("glLightfv posn"); + gl_info->gl_ops.gl.p_glLightfv(GL_LIGHT0 + light->glIndex, GL_SPOT_DIRECTION, &light->direction.x); + checkGLcall("glLightfv dirn"); + } + + /* Reset Clipping Planes */ + for (k = 0; k < gl_info->limits.user_clip_distances; ++k) + { + if (!isStateDirty(context, STATE_CLIPPLANE(k))) + clipplane(context, state, STATE_CLIPPLANE(k)); + } + + if (context->last_was_rhw) + { + gl_info->gl_ops.gl.p_glLoadIdentity(); + checkGLcall("glLoadIdentity()"); + /* No need to update the world matrix, the identity is fine */ + return; + } + + /* Call the world matrix state, this will apply the combined WORLD + VIEW matrix + * No need to do it here if the state is scheduled for update. */ + if (!isStateDirty(context, STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(0)))) + transform_world(context, state, STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(0))); +} + +static void transform_projection(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl(context)->gl_info; + struct wined3d_matrix projection; + + gl_info->gl_ops.gl.p_glMatrixMode(GL_PROJECTION); + checkGLcall("glMatrixMode(GL_PROJECTION)"); + + get_projection_matrix(context, state, &projection); + gl_info->gl_ops.gl.p_glLoadMatrixf(&projection._11); + checkGLcall("glLoadMatrixf"); +} + +static void streamsrc(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + if (isStateDirty(context, STATE_VDECL)) + return; + wined3d_context_gl_update_stream_sources(wined3d_context_gl(context), state); +} + +static void vdecl_miscpart(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + if (isStateDirty(context, STATE_STREAMSRC)) + return; + wined3d_context_gl_update_stream_sources(wined3d_context_gl(context), state); +} + +static void vertexdeclaration(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + BOOL useVertexShaderFunction = use_vs(state); + BOOL updateFog = FALSE; + BOOL transformed; + BOOL wasrhw = context->last_was_rhw; + unsigned int i; + + transformed = context->stream_info.position_transformed; + if (transformed != context->last_was_rhw && !useVertexShaderFunction) + updateFog = TRUE; + + context->last_was_rhw = transformed; + + if (context->stream_info.swizzle_map != context->last_swizzle_map) + context->shader_update_mask |= 1u << WINED3D_SHADER_TYPE_VERTEX; + + context->last_swizzle_map = context->stream_info.swizzle_map; + + /* Don't have to apply the matrices when vertex shaders are used. When + * vshaders are turned off this function will be called again anyway to + * make sure they're properly set. */ + if (!useVertexShaderFunction) + { + /* TODO: Move this mainly to the viewport state and only apply when + * the vp has changed or transformed / untransformed was switched. */ + if (wasrhw != context->last_was_rhw + && !isStateDirty(context, STATE_TRANSFORM(WINED3D_TS_PROJECTION)) + && !isStateDirty(context, STATE_VIEWPORT)) + transform_projection(context, state, STATE_TRANSFORM(WINED3D_TS_PROJECTION)); + /* World matrix needs reapplication here only if we're switching between rhw and non-rhw + * mode. + * + * If a vertex shader is used, the world matrix changed and then vertex shader unbound + * this check will fail and the matrix not applied again. This is OK because a simple + * world matrix change reapplies the matrix - These checks here are only to satisfy the + * needs of the vertex declaration. + * + * World and view matrix go into the same gl matrix, so only apply them when neither is + * dirty + */ + if (transformed != wasrhw && !isStateDirty(context, STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(0))) + && !isStateDirty(context, STATE_TRANSFORM(WINED3D_TS_VIEW))) + transform_world(context, state, STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(0))); + if (!isStateDirty(context, STATE_RENDER(WINED3D_RS_COLORVERTEX))) + context_apply_state(context, state, STATE_RENDER(WINED3D_RS_COLORVERTEX)); + if (!isStateDirty(context, STATE_RENDER(WINED3D_RS_LIGHTING))) + state_lighting(context, state, STATE_RENDER(WINED3D_RS_LIGHTING)); + + if (context->last_was_vshader) + { + updateFog = TRUE; + + if (!context->d3d_info->vs_clipping + && !isStateDirty(context, STATE_RENDER(WINED3D_RS_CLIPPLANEENABLE))) + { + state_clipping(context, state, STATE_RENDER(WINED3D_RS_CLIPPLANEENABLE)); + } + + for (i = 0; i < gl_info->limits.user_clip_distances; ++i) + { + clipplane(context, state, STATE_CLIPPLANE(i)); + } + } + if (!isStateDirty(context, STATE_RENDER(WINED3D_RS_NORMALIZENORMALS))) + state_normalize(context, state, STATE_RENDER(WINED3D_RS_NORMALIZENORMALS)); + } + else + { + if (!context->last_was_vshader) + { + static BOOL warned = FALSE; + if (!context->d3d_info->vs_clipping) + { + /* Disable all clip planes to get defined results on all drivers. See comment in the + * state_clipping state handler + */ + wined3d_context_gl_enable_clip_distances(context_gl, 0); + + if (!warned && state->render_states[WINED3D_RS_CLIPPLANEENABLE]) + { + FIXME("Clipping not supported with vertex shaders.\n"); + warned = TRUE; + } + } + if (wasrhw) + { + /* Apply the transform matrices when switching from rhw + * drawing to vertex shaders. Vertex shaders themselves do + * not need it, but the matrices are not reapplied + * automatically when switching back from vertex shaders to + * fixed function processing. So make sure we leave the fixed + * function vertex processing states back in a sane state + * before switching to shaders. */ + if (!isStateDirty(context, STATE_TRANSFORM(WINED3D_TS_PROJECTION))) + transform_projection(context, state, STATE_TRANSFORM(WINED3D_TS_PROJECTION)); + if (!isStateDirty(context, STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(0)))) + transform_world(context, state, STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(0))); + } + updateFog = TRUE; + + /* Vertex shader clipping ignores the view matrix. Update all clipplanes + * (Note: ARB shaders can read the clip planes for clipping emulation even if + * device->vs_clipping is false. + */ + for (i = 0; i < gl_info->limits.user_clip_distances; ++i) + { + clipplane(context, state, STATE_CLIPPLANE(i)); + } + } + } + + context->last_was_vshader = useVertexShaderFunction; + context->shader_update_mask |= 1u << WINED3D_SHADER_TYPE_VERTEX; + + if (updateFog) + context_apply_state(context, state, STATE_RENDER(WINED3D_RS_FOGVERTEXMODE)); + + if (!useVertexShaderFunction) + { + unsigned int i; + + for (i = 0; i < WINED3D_MAX_TEXTURES; ++i) + { + if (!isStateDirty(context, STATE_TRANSFORM(WINED3D_TS_TEXTURE0 + i))) + transform_texture(context, state, STATE_TEXTURESTAGE(i, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS)); + } + + if (use_ps(state) && state->shader[WINED3D_SHADER_TYPE_PIXEL]->reg_maps.shader_version.major == 1 + && state->shader[WINED3D_SHADER_TYPE_PIXEL]->reg_maps.shader_version.minor <= 3) + context->shader_update_mask |= 1u << WINED3D_SHADER_TYPE_PIXEL; + } +} + +static void get_viewports(struct wined3d_context *context, const struct wined3d_state *state, + unsigned int viewport_count, struct wined3d_viewport *viewports) +{ + const struct wined3d_rendertarget_view *depth_stencil = state->fb.depth_stencil; + const struct wined3d_rendertarget_view *target = state->fb.render_targets[0]; + unsigned int width, height, i; + + for (i = 0; i < viewport_count; ++i) + viewports[i] = state->viewports[i]; + + /* Note: GL uses a lower left origin while DirectX uses upper left. This + * is reversed when using offscreen rendering. */ + if (context->render_offscreen) + return; + + if (target) + { + wined3d_rendertarget_view_get_drawable_size(target, context, &width, &height); + } + else if (depth_stencil) + { + height = depth_stencil->height; + } + else + { + FIXME("Could not get the height of render targets.\n"); + return; + } + + for (i = 0; i < viewport_count; ++i) + viewports[i].y = height - (viewports[i].y + viewports[i].height); +} + +static void viewport_miscpart(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl(context)->gl_info; + struct wined3d_viewport vp[WINED3D_MAX_VIEWPORTS]; + float min_z, max_z; + + if (gl_info->supported[ARB_VIEWPORT_ARRAY]) + { + GLdouble depth_ranges[2 * WINED3D_MAX_VIEWPORTS]; + GLfloat viewports[4 * WINED3D_MAX_VIEWPORTS]; + + unsigned int i, reset_count = 0; + + get_viewports(context, state, state->viewport_count, vp); + for (i = 0; i < state->viewport_count; ++i) + { + wined3d_viewport_get_z_range(&vp[i], &min_z, &max_z); + depth_ranges[i * 2] = min_z; + depth_ranges[i * 2 + 1] = max_z; + + viewports[i * 4] = vp[i].x; + viewports[i * 4 + 1] = vp[i].y; + viewports[i * 4 + 2] = vp[i].width; + viewports[i * 4 + 3] = vp[i].height; + } + + if (context->viewport_count > state->viewport_count) + reset_count = context->viewport_count - state->viewport_count; + + if (reset_count) + { + memset(&depth_ranges[state->viewport_count * 2], 0, reset_count * 2 * sizeof(*depth_ranges)); + memset(&viewports[state->viewport_count * 4], 0, reset_count * 4 * sizeof(*viewports)); + } + + GL_EXTCALL(glDepthRangeArrayv(0, state->viewport_count + reset_count, depth_ranges)); + GL_EXTCALL(glViewportArrayv(0, state->viewport_count + reset_count, viewports)); + context->viewport_count = state->viewport_count; + } + else + { + get_viewports(context, state, 1, vp); + wined3d_viewport_get_z_range(&vp[0], &min_z, &max_z); + gl_info->gl_ops.gl.p_glDepthRange(min_z, max_z); + gl_info->gl_ops.gl.p_glViewport(vp[0].x, vp[0].y, vp[0].width, vp[0].height); + } + checkGLcall("setting clip space and viewport"); +} + +static void viewport_miscpart_cc(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl(context)->gl_info; + /* See get_projection_matrix() in utils.c for a discussion about those values. */ + float pixel_center_offset = context->d3d_info->wined3d_creation_flags + & WINED3D_PIXEL_CENTER_INTEGER ? 63.0f / 128.0f : -1.0f / 128.0f; + struct wined3d_viewport vp[WINED3D_MAX_VIEWPORTS]; + GLdouble depth_ranges[2 * WINED3D_MAX_VIEWPORTS]; + GLfloat viewports[4 * WINED3D_MAX_VIEWPORTS]; + unsigned int i, reset_count = 0; + float min_z, max_z; + + get_viewports(context, state, state->viewport_count, vp); + + GL_EXTCALL(glClipControl(context->render_offscreen ? GL_UPPER_LEFT : GL_LOWER_LEFT, GL_ZERO_TO_ONE)); + + for (i = 0; i < state->viewport_count; ++i) + { + wined3d_viewport_get_z_range(&vp[i], &min_z, &max_z); + depth_ranges[i * 2] = min_z; + depth_ranges[i * 2 + 1] = max_z; + + viewports[i * 4] = vp[i].x + pixel_center_offset; + viewports[i * 4 + 1] = vp[i].y + pixel_center_offset; + viewports[i * 4 + 2] = vp[i].width; + viewports[i * 4 + 3] = vp[i].height; + } + + if (context->viewport_count > state->viewport_count) + reset_count = context->viewport_count - state->viewport_count; + + if (reset_count) + { + memset(&depth_ranges[state->viewport_count * 2], 0, reset_count * 2 * sizeof(*depth_ranges)); + memset(&viewports[state->viewport_count * 4], 0, reset_count * 4 * sizeof(*viewports)); + } + + GL_EXTCALL(glDepthRangeArrayv(0, state->viewport_count + reset_count, depth_ranges)); + GL_EXTCALL(glViewportArrayv(0, state->viewport_count + reset_count, viewports)); + context->viewport_count = state->viewport_count; + + checkGLcall("setting clip space and viewport"); +} + +static void viewport_vertexpart(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + if (!isStateDirty(context, STATE_TRANSFORM(WINED3D_TS_PROJECTION))) + transform_projection(context, state, STATE_TRANSFORM(WINED3D_TS_PROJECTION)); + if (!isStateDirty(context, STATE_RENDER(WINED3D_RS_POINTSCALEENABLE)) + && state->render_states[WINED3D_RS_POINTSCALEENABLE]) + state_pscale(context, state, STATE_RENDER(WINED3D_RS_POINTSCALEENABLE)); + /* Update the position fixup. */ + context->constant_update_mask |= WINED3D_SHADER_CONST_POS_FIXUP; +} + +static void light(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl(context)->gl_info; + UINT Index = state_id - STATE_ACTIVELIGHT(0); + const struct wined3d_light_info *lightInfo = state->light_state.lights[Index]; + + if (!lightInfo) + { + gl_info->gl_ops.gl.p_glDisable(GL_LIGHT0 + Index); + checkGLcall("glDisable(GL_LIGHT0 + Index)"); + } + else + { + float quad_att; + + /* Light settings are affected by the model view in OpenGL, the View transform in direct3d*/ + gl_info->gl_ops.gl.p_glMatrixMode(GL_MODELVIEW); + gl_info->gl_ops.gl.p_glPushMatrix(); + gl_info->gl_ops.gl.p_glLoadMatrixf(&state->transforms[WINED3D_TS_VIEW]._11); + + gl_info->gl_ops.gl.p_glLightfv(GL_LIGHT0 + Index, GL_DIFFUSE, &lightInfo->OriginalParms.diffuse.r); + gl_info->gl_ops.gl.p_glLightfv(GL_LIGHT0 + Index, GL_SPECULAR, &lightInfo->OriginalParms.specular.r); + gl_info->gl_ops.gl.p_glLightfv(GL_LIGHT0 + Index, GL_AMBIENT, &lightInfo->OriginalParms.ambient.r); + checkGLcall("glLightfv"); + + if ((lightInfo->OriginalParms.range * lightInfo->OriginalParms.range) >= FLT_MIN) + quad_att = 1.4f / (lightInfo->OriginalParms.range * lightInfo->OriginalParms.range); + else + quad_att = 0.0f; /* 0 or MAX? (0 seems to be ok) */ + + /* Do not assign attenuation values for lights that do not use them. D3D apps are free to pass any junk, + * but gl drivers use them and may crash due to bad Attenuation values. Need for Speed most wanted sets + * Attenuation0 to NaN and crashes in the gl lib + */ + + switch (lightInfo->OriginalParms.type) + { + case WINED3D_LIGHT_POINT: + /* Position */ + gl_info->gl_ops.gl.p_glLightfv(GL_LIGHT0 + Index, GL_POSITION, &lightInfo->position.x); + checkGLcall("glLightfv"); + gl_info->gl_ops.gl.p_glLightf(GL_LIGHT0 + Index, GL_SPOT_CUTOFF, lightInfo->cutoff); + checkGLcall("glLightf"); + gl_info->gl_ops.gl.p_glLightf(GL_LIGHT0 + Index, GL_CONSTANT_ATTENUATION, + lightInfo->OriginalParms.attenuation0); + checkGLcall("glLightf"); + gl_info->gl_ops.gl.p_glLightf(GL_LIGHT0 + Index, GL_LINEAR_ATTENUATION, + lightInfo->OriginalParms.attenuation1); + checkGLcall("glLightf"); + if (quad_att < lightInfo->OriginalParms.attenuation2) + quad_att = lightInfo->OriginalParms.attenuation2; + gl_info->gl_ops.gl.p_glLightf(GL_LIGHT0 + Index, GL_QUADRATIC_ATTENUATION, quad_att); + checkGLcall("glLightf"); + /* FIXME: Range */ + break; + + case WINED3D_LIGHT_SPOT: + /* Position */ + gl_info->gl_ops.gl.p_glLightfv(GL_LIGHT0 + Index, GL_POSITION, &lightInfo->position.x); + checkGLcall("glLightfv"); + /* Direction */ + gl_info->gl_ops.gl.p_glLightfv(GL_LIGHT0 + Index, GL_SPOT_DIRECTION, &lightInfo->direction.x); + checkGLcall("glLightfv"); + gl_info->gl_ops.gl.p_glLightf(GL_LIGHT0 + Index, GL_SPOT_EXPONENT, lightInfo->exponent); + checkGLcall("glLightf"); + gl_info->gl_ops.gl.p_glLightf(GL_LIGHT0 + Index, GL_SPOT_CUTOFF, lightInfo->cutoff); + checkGLcall("glLightf"); + gl_info->gl_ops.gl.p_glLightf(GL_LIGHT0 + Index, GL_CONSTANT_ATTENUATION, + lightInfo->OriginalParms.attenuation0); + checkGLcall("glLightf"); + gl_info->gl_ops.gl.p_glLightf(GL_LIGHT0 + Index, GL_LINEAR_ATTENUATION, + lightInfo->OriginalParms.attenuation1); + checkGLcall("glLightf"); + if (quad_att < lightInfo->OriginalParms.attenuation2) + quad_att = lightInfo->OriginalParms.attenuation2; + gl_info->gl_ops.gl.p_glLightf(GL_LIGHT0 + Index, GL_QUADRATIC_ATTENUATION, quad_att); + checkGLcall("glLightf"); + /* FIXME: Range */ + break; + + case WINED3D_LIGHT_DIRECTIONAL: + /* Direction */ + /* Note GL uses w position of 0 for direction! */ + gl_info->gl_ops.gl.p_glLightfv(GL_LIGHT0 + Index, GL_POSITION, &lightInfo->direction.x); + checkGLcall("glLightfv"); + gl_info->gl_ops.gl.p_glLightf(GL_LIGHT0 + Index, GL_SPOT_CUTOFF, lightInfo->cutoff); + checkGLcall("glLightf"); + gl_info->gl_ops.gl.p_glLightf(GL_LIGHT0 + Index, GL_SPOT_EXPONENT, 0.0f); + checkGLcall("glLightf"); + break; + + default: + FIXME("Unrecognized light type %#x.\n", lightInfo->OriginalParms.type); + } + + /* Restore the modelview matrix */ + gl_info->gl_ops.gl.p_glPopMatrix(); + + gl_info->gl_ops.gl.p_glEnable(GL_LIGHT0 + Index); + checkGLcall("glEnable(GL_LIGHT0 + Index)"); + } +} + +static void scissorrect(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl(context)->gl_info; + unsigned int height = 0; + const RECT *r; + + /* Warning: glScissor uses window coordinates, not viewport coordinates, + * so our viewport correction does not apply. Warning2: Even in windowed + * mode the coords are relative to the window, not the screen. */ + + if (!context->render_offscreen) + { + const struct wined3d_rendertarget_view *target = state->fb.render_targets[0]; + unsigned int width; + + wined3d_rendertarget_view_get_drawable_size(target, context, &width, &height); + } + + if (gl_info->supported[ARB_VIEWPORT_ARRAY]) + { + GLint sr[4 * WINED3D_MAX_VIEWPORTS]; + unsigned int i, reset_count = 0; + + for (i = 0; i < state->scissor_rect_count; ++i) + { + r = &state->scissor_rects[i]; + + sr[i * 4] = r->left; + sr[i * 4 + 1] = height ? height - r->top : r->top; + sr[i * 4 + 2] = r->right - r->left; + sr[i * 4 + 3] = r->bottom - r->top; + } + + if (context->scissor_rect_count > state->scissor_rect_count) + reset_count = context->scissor_rect_count - state->scissor_rect_count; + + if (reset_count) + memset(&sr[state->scissor_rect_count * 4], 0, reset_count * 4 * sizeof(GLint)); + + GL_EXTCALL(glScissorArrayv(0, state->scissor_rect_count + reset_count, sr)); + checkGLcall("glScissorArrayv"); + context->scissor_rect_count = state->scissor_rect_count; + } + else + { + r = &state->scissor_rects[0]; + gl_info->gl_ops.gl.p_glScissor(r->left, height ? height - r->top : r->top, + r->right - r->left, r->bottom - r->top); + checkGLcall("glScissor"); + } +} + +static void indexbuffer(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl(context)->gl_info; + const struct wined3d_stream_info *stream_info = &context->stream_info; + struct wined3d_buffer_gl *buffer_gl; + + if (!state->index_buffer || !stream_info->all_vbo) + { + GL_EXTCALL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + return; + } + + buffer_gl = wined3d_buffer_gl(state->index_buffer); + GL_EXTCALL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer_gl->bo.id)); + buffer_gl->bo_user.valid = true; +} + +static void depth_clip(const struct wined3d_rasterizer_state *r, const struct wined3d_gl_info *gl_info) +{ + if (!gl_info->supported[ARB_DEPTH_CLAMP]) + { + if (r && !r->desc.depth_clip) + FIXME("Depth clamp not supported by this GL implementation.\n"); + return; + } + + if (r && !r->desc.depth_clip) + gl_info->gl_ops.gl.p_glEnable(GL_DEPTH_CLAMP); + else + gl_info->gl_ops.gl.p_glDisable(GL_DEPTH_CLAMP); + checkGLcall("depth clip"); +} + +static void rasterizer(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl(context)->gl_info; + const struct wined3d_rasterizer_state *r = state->rasterizer_state; + GLenum mode; + + mode = r && r->desc.front_ccw ? GL_CCW : GL_CW; + if (context->render_offscreen) + mode = (mode == GL_CW) ? GL_CCW : GL_CW; + + gl_info->gl_ops.gl.p_glFrontFace(mode); + checkGLcall("glFrontFace"); + depthbias(context, state); + fillmode(r, gl_info); + cullmode(r, gl_info); + depth_clip(r, gl_info); + scissor(r, gl_info); + line_antialias(r, gl_info); +} + +static void rasterizer_cc(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl(context)->gl_info; + const struct wined3d_rasterizer_state *r = state->rasterizer_state; + GLenum mode; + + mode = r && r->desc.front_ccw ? GL_CCW : GL_CW; + + gl_info->gl_ops.gl.p_glFrontFace(mode); + checkGLcall("glFrontFace"); + depthbias(context, state); + fillmode(r, gl_info); + cullmode(r, gl_info); + depth_clip(r, gl_info); + scissor(r, gl_info); + line_antialias(r, gl_info); +} + +static void psorigin_w(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + static BOOL warned; + + if (!warned) + { + WARN("Point sprite coordinate origin switching not supported.\n"); + warned = TRUE; + } +} + +static void psorigin(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl(context)->gl_info; + GLint origin = context->render_offscreen ? GL_LOWER_LEFT : GL_UPPER_LEFT; + + GL_EXTCALL(glPointParameteri(GL_POINT_SPRITE_COORD_ORIGIN, origin)); + checkGLcall("glPointParameteri(GL_POINT_SPRITE_COORD_ORIGIN, ...)"); +} + +void state_srgbwrite(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl(context)->gl_info; + + TRACE("context %p, state %p, state_id %#x.\n", context, state, state_id); + + if (needs_srgb_write(context->d3d_info, state, &state->fb)) + gl_info->gl_ops.gl.p_glEnable(GL_FRAMEBUFFER_SRGB); + else + gl_info->gl_ops.gl.p_glDisable(GL_FRAMEBUFFER_SRGB); +} + +static void state_cb(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + const struct wined3d_gl_info *gl_info = wined3d_context_gl(context)->gl_info; + enum wined3d_shader_type shader_type; + struct wined3d_buffer_gl *buffer_gl; + unsigned int i, base, count; + + TRACE("context %p, state %p, state_id %#x.\n", context, state, state_id); + + if (STATE_IS_GRAPHICS_CONSTANT_BUFFER(state_id)) + shader_type = state_id - STATE_GRAPHICS_CONSTANT_BUFFER(0); + else + shader_type = WINED3D_SHADER_TYPE_COMPUTE; + + wined3d_gl_limits_get_uniform_block_range(&gl_info->limits, shader_type, &base, &count); + for (i = 0; i < count; ++i) + { + if (!state->cb[shader_type][i]) + { + GL_EXTCALL(glBindBufferBase(GL_UNIFORM_BUFFER, base + i, 0)); + continue; + } + + buffer_gl = wined3d_buffer_gl(state->cb[shader_type][i]); + GL_EXTCALL(glBindBufferBase(GL_UNIFORM_BUFFER, base + i, buffer_gl->bo.id)); + buffer_gl->bo_user.valid = true; + } + checkGLcall("bind constant buffers"); +} + +static void state_cb_warn(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + TRACE("context %p, state %p, state_id %#x.\n", context, state, state_id); + + WARN("Constant buffers (%s) no supported.\n", debug_d3dstate(state_id)); +} + +static void state_shader_resource_binding(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) +{ + TRACE("context %p, state %p, state_id %#x.\n", context, state, state_id); + + context->update_shader_resource_bindings = 1; +} + +static void state_cs_resource_binding(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) +{ + TRACE("context %p, state %p, state_id %#x.\n", context, state, state_id); + context->update_compute_shader_resource_bindings = 1; +} + +static void state_uav_binding(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) +{ + TRACE("context %p, state %p, state_id %#x.\n", context, state, state_id); + context->update_unordered_access_view_bindings = 1; +} + +static void state_cs_uav_binding(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) +{ + TRACE("context %p, state %p, state_id %#x.\n", context, state, state_id); + context->update_compute_unordered_access_view_bindings = 1; +} + +static void state_uav_warn(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + WARN("ARB_image_load_store is not supported by OpenGL implementation.\n"); +} + +static void state_so(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + struct wined3d_buffer_gl *buffer_gl; + unsigned int offset, size, i; + + TRACE("context %p, state %p, state_id %#x.\n", context, state, state_id); + + wined3d_context_gl_end_transform_feedback(context_gl); + + for (i = 0; i < ARRAY_SIZE(state->stream_output); ++i) + { + if (!state->stream_output[i].buffer) + { + GL_EXTCALL(glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, i, 0)); + continue; + } + + buffer_gl = wined3d_buffer_gl(state->stream_output[i].buffer); + offset = state->stream_output[i].offset; + if (offset == ~0u) + { + FIXME("Appending to stream output buffers not implemented.\n"); + offset = 0; + } + size = buffer_gl->b.resource.size - offset; + GL_EXTCALL(glBindBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, i, buffer_gl->bo.id, offset, size)); + buffer_gl->bo_user.valid = true; + } + checkGLcall("bind transform feedback buffers"); +} + +static void state_so_warn(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + WARN("Transform feedback not supported.\n"); +} + +const struct wined3d_state_entry_template misc_state_template_gl[] = +{ + { STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_VERTEX), { STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_VERTEX), state_cb, }, ARB_UNIFORM_BUFFER_OBJECT }, + { STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_VERTEX), { STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_VERTEX), state_cb_warn, }, WINED3D_GL_EXT_NONE }, + { STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_HULL), { STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_HULL), state_cb, }, ARB_UNIFORM_BUFFER_OBJECT }, + { STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_HULL), { STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_HULL), state_cb_warn, }, WINED3D_GL_EXT_NONE }, + { STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_DOMAIN), { STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_DOMAIN), state_cb, }, ARB_UNIFORM_BUFFER_OBJECT }, + { STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_DOMAIN), { STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_DOMAIN), state_cb_warn, }, WINED3D_GL_EXT_NONE }, + { STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_GEOMETRY),{ STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_GEOMETRY),state_cb, }, ARB_UNIFORM_BUFFER_OBJECT }, + { STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_GEOMETRY),{ STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_GEOMETRY),state_cb_warn, }, WINED3D_GL_EXT_NONE }, + { STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_PIXEL), { STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_PIXEL), state_cb, }, ARB_UNIFORM_BUFFER_OBJECT }, + { STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_PIXEL), { STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_PIXEL), state_cb_warn, }, WINED3D_GL_EXT_NONE }, + { STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_COMPUTE), { STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_COMPUTE), state_cb, }, ARB_UNIFORM_BUFFER_OBJECT }, + { STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_COMPUTE), { STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_COMPUTE), state_cb_warn, }, WINED3D_GL_EXT_NONE }, + { STATE_GRAPHICS_SHADER_RESOURCE_BINDING, { STATE_GRAPHICS_SHADER_RESOURCE_BINDING, state_shader_resource_binding}, WINED3D_GL_EXT_NONE }, + { STATE_GRAPHICS_UNORDERED_ACCESS_VIEW_BINDING, { STATE_GRAPHICS_UNORDERED_ACCESS_VIEW_BINDING, state_uav_binding }, ARB_SHADER_IMAGE_LOAD_STORE }, + { STATE_GRAPHICS_UNORDERED_ACCESS_VIEW_BINDING, { STATE_GRAPHICS_UNORDERED_ACCESS_VIEW_BINDING, state_uav_warn }, WINED3D_GL_EXT_NONE }, + { STATE_COMPUTE_SHADER_RESOURCE_BINDING, { STATE_COMPUTE_SHADER_RESOURCE_BINDING, state_cs_resource_binding}, WINED3D_GL_EXT_NONE }, + { STATE_COMPUTE_UNORDERED_ACCESS_VIEW_BINDING, { STATE_COMPUTE_UNORDERED_ACCESS_VIEW_BINDING, state_cs_uav_binding}, ARB_SHADER_IMAGE_LOAD_STORE }, + { STATE_COMPUTE_UNORDERED_ACCESS_VIEW_BINDING, { STATE_COMPUTE_UNORDERED_ACCESS_VIEW_BINDING, state_uav_warn }, WINED3D_GL_EXT_NONE }, + { STATE_STREAM_OUTPUT, { STATE_STREAM_OUTPUT, state_so, }, WINED3D_GL_VERSION_3_2 }, + { STATE_STREAM_OUTPUT, { STATE_STREAM_OUTPUT, state_so_warn, }, WINED3D_GL_EXT_NONE }, + { STATE_BLEND, { STATE_BLEND, blend_dbb }, ARB_DRAW_BUFFERS_BLEND }, + { STATE_BLEND, { STATE_BLEND, blend_db2 }, EXT_DRAW_BUFFERS2 }, + { STATE_BLEND, { STATE_BLEND, blend }, WINED3D_GL_EXT_NONE }, + { STATE_BLEND_FACTOR, { STATE_BLEND_FACTOR, state_blend_factor }, EXT_BLEND_COLOR }, + { STATE_BLEND_FACTOR, { STATE_BLEND_FACTOR, state_blend_factor_w}, WINED3D_GL_EXT_NONE }, + { STATE_SAMPLE_MASK, { STATE_SAMPLE_MASK, state_sample_mask }, ARB_TEXTURE_MULTISAMPLE }, + { STATE_SAMPLE_MASK, { STATE_SAMPLE_MASK, state_sample_mask_w }, WINED3D_GL_EXT_NONE }, + { STATE_DEPTH_STENCIL, { STATE_DEPTH_STENCIL, depth_stencil_2s }, EXT_STENCIL_TWO_SIDE }, + { STATE_DEPTH_STENCIL, { STATE_DEPTH_STENCIL, depth_stencil }, WINED3D_GL_EXT_NONE }, + { STATE_STENCIL_REF, { STATE_DEPTH_STENCIL, NULL }, WINED3D_GL_EXT_NONE }, + { STATE_STREAMSRC, { STATE_STREAMSRC, streamsrc }, WINED3D_GL_EXT_NONE }, + { STATE_VDECL, { STATE_VDECL, vdecl_miscpart }, WINED3D_GL_EXT_NONE }, + { STATE_RASTERIZER, { STATE_RASTERIZER, rasterizer_cc }, ARB_CLIP_CONTROL }, + { STATE_RASTERIZER, { STATE_RASTERIZER, rasterizer }, WINED3D_GL_EXT_NONE }, + { STATE_SCISSORRECT, { STATE_SCISSORRECT, scissorrect }, WINED3D_GL_EXT_NONE }, + { STATE_POINTSPRITECOORDORIGIN, { STATE_POINTSPRITECOORDORIGIN, state_nop }, ARB_CLIP_CONTROL }, + { STATE_POINTSPRITECOORDORIGIN, { STATE_POINTSPRITECOORDORIGIN, psorigin }, WINED3D_GL_VERSION_2_0 }, + { STATE_POINTSPRITECOORDORIGIN, { STATE_POINTSPRITECOORDORIGIN, psorigin_w }, WINED3D_GL_EXT_NONE }, + + /* TODO: Move shader constant loading to vertex and fragment pipeline respectively, as soon as the pshader and + * vshader loadings are untied from each other + */ + { STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_MAT00), { STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_MAT00), shader_bumpenv }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_MAT01), { STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_MAT10), { STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_MAT11), { STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_MAT00), { STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_MAT00), shader_bumpenv }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_MAT01), { STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_MAT10), { STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_MAT11), { STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_MAT00), { STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_MAT00), shader_bumpenv }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_MAT01), { STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_MAT10), { STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_MAT11), { STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_MAT00), { STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_MAT00), shader_bumpenv }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_MAT01), { STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_MAT10), { STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_MAT11), { STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_MAT00), { STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_MAT00), shader_bumpenv }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_MAT01), { STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_MAT10), { STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_MAT11), { STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_MAT00), { STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_MAT00), shader_bumpenv }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_MAT01), { STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_MAT10), { STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_MAT11), { STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_MAT00), { STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_MAT00), shader_bumpenv }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_MAT01), { STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_MAT10), { STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_MAT11), { STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_MAT00), { STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_MAT00), shader_bumpenv }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_MAT01), { STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_MAT10), { STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_MAT11), { STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_MAT00), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_LSCALE), { STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_LSCALE), shader_bumpenv }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_LOFFSET), { STATE_TEXTURESTAGE(0, WINED3D_TSS_BUMPENV_LSCALE), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_LSCALE), { STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_LSCALE), shader_bumpenv }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_LOFFSET), { STATE_TEXTURESTAGE(1, WINED3D_TSS_BUMPENV_LSCALE), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_LSCALE), { STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_LSCALE), shader_bumpenv }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_LOFFSET), { STATE_TEXTURESTAGE(2, WINED3D_TSS_BUMPENV_LSCALE), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_LSCALE), { STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_LSCALE), shader_bumpenv }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_LOFFSET), { STATE_TEXTURESTAGE(3, WINED3D_TSS_BUMPENV_LSCALE), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_LSCALE), { STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_LSCALE), shader_bumpenv }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_LOFFSET), { STATE_TEXTURESTAGE(4, WINED3D_TSS_BUMPENV_LSCALE), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_LSCALE), { STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_LSCALE), shader_bumpenv }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_LOFFSET), { STATE_TEXTURESTAGE(5, WINED3D_TSS_BUMPENV_LSCALE), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_LSCALE), { STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_LSCALE), shader_bumpenv }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_LOFFSET), { STATE_TEXTURESTAGE(6, WINED3D_TSS_BUMPENV_LSCALE), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_LSCALE), { STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_LSCALE), shader_bumpenv }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_LOFFSET), { STATE_TEXTURESTAGE(7, WINED3D_TSS_BUMPENV_LSCALE), NULL }, WINED3D_GL_EXT_NONE }, + + { STATE_VIEWPORT, { STATE_VIEWPORT, viewport_miscpart_cc}, ARB_CLIP_CONTROL }, + { STATE_VIEWPORT, { STATE_VIEWPORT, viewport_miscpart }, WINED3D_GL_EXT_NONE }, + { STATE_INDEXBUFFER, { STATE_INDEXBUFFER, indexbuffer }, ARB_VERTEX_BUFFER_OBJECT }, + { STATE_INDEXBUFFER, { STATE_INDEXBUFFER, state_nop }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_ANTIALIAS), { STATE_RENDER(WINED3D_RS_ANTIALIAS), state_antialias }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_TEXTUREPERSPECTIVE), { STATE_RENDER(WINED3D_RS_TEXTUREPERSPECTIVE), state_nop }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_WRAPU), { STATE_RENDER(WINED3D_RS_WRAPU), state_wrapu }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_WRAPV), { STATE_RENDER(WINED3D_RS_WRAPV), state_wrapv }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_LINEPATTERN), { STATE_RENDER(WINED3D_RS_LINEPATTERN), state_linepattern }, WINED3D_GL_LEGACY_CONTEXT }, + { STATE_RENDER(WINED3D_RS_LINEPATTERN), { STATE_RENDER(WINED3D_RS_LINEPATTERN), state_linepattern_w }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_MONOENABLE), { STATE_RENDER(WINED3D_RS_MONOENABLE), state_monoenable }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_ROP2), { STATE_RENDER(WINED3D_RS_ROP2), state_rop2 }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_PLANEMASK), { STATE_RENDER(WINED3D_RS_PLANEMASK), state_planemask }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_LASTPIXEL), { STATE_RENDER(WINED3D_RS_LASTPIXEL), state_lastpixel }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_DITHERENABLE), { STATE_RENDER(WINED3D_RS_DITHERENABLE), state_ditherenable }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_SUBPIXEL), { STATE_RENDER(WINED3D_RS_SUBPIXEL), state_subpixel }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_SUBPIXELX), { STATE_RENDER(WINED3D_RS_SUBPIXELX), state_subpixelx }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_STIPPLEDALPHA), { STATE_RENDER(WINED3D_RS_STIPPLEDALPHA), state_stippledalpha }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_STIPPLEENABLE), { STATE_RENDER(WINED3D_RS_STIPPLEENABLE), state_stippleenable }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_MIPMAPLODBIAS), { STATE_RENDER(WINED3D_RS_MIPMAPLODBIAS), state_mipmaplodbias }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_ANISOTROPY), { STATE_RENDER(WINED3D_RS_ANISOTROPY), state_anisotropy }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_FLUSHBATCH), { STATE_RENDER(WINED3D_RS_FLUSHBATCH), state_flushbatch }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_TRANSLUCENTSORTINDEPENDENT),{ STATE_RENDER(WINED3D_RS_TRANSLUCENTSORTINDEPENDENT),state_translucentsi }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_WRAP0), { STATE_RENDER(WINED3D_RS_WRAP0), state_wrap }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_WRAP1), { STATE_RENDER(WINED3D_RS_WRAP0), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_WRAP2), { STATE_RENDER(WINED3D_RS_WRAP0), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_WRAP3), { STATE_RENDER(WINED3D_RS_WRAP0), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_WRAP4), { STATE_RENDER(WINED3D_RS_WRAP0), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_WRAP5), { STATE_RENDER(WINED3D_RS_WRAP0), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_WRAP6), { STATE_RENDER(WINED3D_RS_WRAP0), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_WRAP7), { STATE_RENDER(WINED3D_RS_WRAP0), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_WRAP8), { STATE_RENDER(WINED3D_RS_WRAP0), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_WRAP9), { STATE_RENDER(WINED3D_RS_WRAP0), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_WRAP10), { STATE_RENDER(WINED3D_RS_WRAP0), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_WRAP11), { STATE_RENDER(WINED3D_RS_WRAP0), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_WRAP12), { STATE_RENDER(WINED3D_RS_WRAP0), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_WRAP13), { STATE_RENDER(WINED3D_RS_WRAP0), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_WRAP14), { STATE_RENDER(WINED3D_RS_WRAP0), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_WRAP15), { STATE_RENDER(WINED3D_RS_WRAP0), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_EXTENTS), { STATE_RENDER(WINED3D_RS_EXTENTS), state_extents }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_COLORKEYBLENDENABLE), { STATE_RENDER(WINED3D_RS_COLORKEYBLENDENABLE), state_ckeyblend }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_SOFTWAREVERTEXPROCESSING), { STATE_RENDER(WINED3D_RS_SOFTWAREVERTEXPROCESSING), state_swvp }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_PATCHEDGESTYLE), { STATE_RENDER(WINED3D_RS_PATCHEDGESTYLE), state_patchedgestyle}, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_PATCHSEGMENTS), { STATE_RENDER(WINED3D_RS_PATCHSEGMENTS), state_patchsegments }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_POSITIONDEGREE), { STATE_RENDER(WINED3D_RS_POSITIONDEGREE), state_positiondegree}, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_NORMALDEGREE), { STATE_RENDER(WINED3D_RS_NORMALDEGREE), state_normaldegree }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_MINTESSELLATIONLEVEL), { STATE_RENDER(WINED3D_RS_ENABLEADAPTIVETESSELLATION),NULL }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_MAXTESSELLATIONLEVEL), { STATE_RENDER(WINED3D_RS_ENABLEADAPTIVETESSELLATION),NULL }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_ADAPTIVETESS_X), { STATE_RENDER(WINED3D_RS_ENABLEADAPTIVETESSELLATION),NULL }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_ADAPTIVETESS_Y), { STATE_RENDER(WINED3D_RS_ENABLEADAPTIVETESSELLATION),NULL }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_ADAPTIVETESS_Z), { STATE_RENDER(WINED3D_RS_ENABLEADAPTIVETESSELLATION),NULL }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_ADAPTIVETESS_W), { STATE_RENDER(WINED3D_RS_ENABLEADAPTIVETESSELLATION),NULL }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_ENABLEADAPTIVETESSELLATION),{ STATE_RENDER(WINED3D_RS_ENABLEADAPTIVETESSELLATION),state_nvdb }, EXT_DEPTH_BOUNDS_TEST }, + { STATE_RENDER(WINED3D_RS_ENABLEADAPTIVETESSELLATION),{ STATE_RENDER(WINED3D_RS_ENABLEADAPTIVETESSELLATION),state_tessellation }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_MULTISAMPLEANTIALIAS), { STATE_RENDER(WINED3D_RS_MULTISAMPLEANTIALIAS), state_msaa }, ARB_MULTISAMPLE }, + { STATE_RENDER(WINED3D_RS_MULTISAMPLEANTIALIAS), { STATE_RENDER(WINED3D_RS_MULTISAMPLEANTIALIAS), state_msaa_w }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_DEBUGMONITORTOKEN), { STATE_RENDER(WINED3D_RS_DEBUGMONITORTOKEN), state_debug_monitor }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_ZVISIBLE), { STATE_RENDER(WINED3D_RS_ZVISIBLE), state_zvisible }, WINED3D_GL_EXT_NONE }, + /* Samplers */ + { STATE_SAMPLER(0), { STATE_SAMPLER(0), sampler }, WINED3D_GL_EXT_NONE }, + { STATE_SAMPLER(1), { STATE_SAMPLER(1), sampler }, WINED3D_GL_EXT_NONE }, + { STATE_SAMPLER(2), { STATE_SAMPLER(2), sampler }, WINED3D_GL_EXT_NONE }, + { STATE_SAMPLER(3), { STATE_SAMPLER(3), sampler }, WINED3D_GL_EXT_NONE }, + { STATE_SAMPLER(4), { STATE_SAMPLER(4), sampler }, WINED3D_GL_EXT_NONE }, + { STATE_SAMPLER(5), { STATE_SAMPLER(5), sampler }, WINED3D_GL_EXT_NONE }, + { STATE_SAMPLER(6), { STATE_SAMPLER(6), sampler }, WINED3D_GL_EXT_NONE }, + { STATE_SAMPLER(7), { STATE_SAMPLER(7), sampler }, WINED3D_GL_EXT_NONE }, + { STATE_SAMPLER(8), { STATE_SAMPLER(8), sampler }, WINED3D_GL_EXT_NONE }, + { STATE_SAMPLER(9), { STATE_SAMPLER(9), sampler }, WINED3D_GL_EXT_NONE }, + { STATE_SAMPLER(10), { STATE_SAMPLER(10), sampler }, WINED3D_GL_EXT_NONE }, + { STATE_SAMPLER(11), { STATE_SAMPLER(11), sampler }, WINED3D_GL_EXT_NONE }, + { STATE_SAMPLER(12), { STATE_SAMPLER(12), sampler }, WINED3D_GL_EXT_NONE }, + { STATE_SAMPLER(13), { STATE_SAMPLER(13), sampler }, WINED3D_GL_EXT_NONE }, + { STATE_SAMPLER(14), { STATE_SAMPLER(14), sampler }, WINED3D_GL_EXT_NONE }, + { STATE_SAMPLER(15), { STATE_SAMPLER(15), sampler }, WINED3D_GL_EXT_NONE }, + { STATE_SAMPLER(16), /* Vertex sampler 0 */ { STATE_SAMPLER(16), sampler }, WINED3D_GL_EXT_NONE }, + { STATE_SAMPLER(17), /* Vertex sampler 1 */ { STATE_SAMPLER(17), sampler }, WINED3D_GL_EXT_NONE }, + { STATE_SAMPLER(18), /* Vertex sampler 2 */ { STATE_SAMPLER(18), sampler }, WINED3D_GL_EXT_NONE }, + { STATE_SAMPLER(19), /* Vertex sampler 3 */ { STATE_SAMPLER(19), sampler }, WINED3D_GL_EXT_NONE }, + { STATE_BASEVERTEXINDEX, { STATE_BASEVERTEXINDEX, state_nop, }, ARB_DRAW_ELEMENTS_BASE_VERTEX }, + { STATE_BASEVERTEXINDEX, { STATE_STREAMSRC, NULL, }, WINED3D_GL_EXT_NONE }, + { STATE_FRAMEBUFFER, { STATE_FRAMEBUFFER, context_state_fb }, WINED3D_GL_EXT_NONE }, + { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), context_state_drawbuf},WINED3D_GL_EXT_NONE }, + { STATE_SHADER(WINED3D_SHADER_TYPE_HULL), { STATE_SHADER(WINED3D_SHADER_TYPE_HULL), state_shader }, WINED3D_GL_EXT_NONE }, + { STATE_SHADER(WINED3D_SHADER_TYPE_DOMAIN), { STATE_SHADER(WINED3D_SHADER_TYPE_DOMAIN), state_shader }, WINED3D_GL_EXT_NONE }, + { STATE_SHADER(WINED3D_SHADER_TYPE_GEOMETRY), { STATE_SHADER(WINED3D_SHADER_TYPE_GEOMETRY), state_shader }, WINED3D_GL_EXT_NONE }, + { STATE_SHADER(WINED3D_SHADER_TYPE_COMPUTE), { STATE_SHADER(WINED3D_SHADER_TYPE_COMPUTE), state_compute_shader}, WINED3D_GL_EXT_NONE }, + {0 /* Terminate */, { 0, 0 }, WINED3D_GL_EXT_NONE }, +}; + +static const struct wined3d_state_entry_template vp_ffp_states[] = +{ + { STATE_VDECL, { STATE_VDECL, vertexdeclaration }, WINED3D_GL_EXT_NONE }, + { STATE_SHADER(WINED3D_SHADER_TYPE_VERTEX), { STATE_VDECL, NULL }, WINED3D_GL_EXT_NONE }, + { STATE_MATERIAL, { STATE_RENDER(WINED3D_RS_SPECULARENABLE), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_SPECULARENABLE), { STATE_RENDER(WINED3D_RS_SPECULARENABLE), state_specularenable}, WINED3D_GL_EXT_NONE }, + /* Clip planes */ + { STATE_CLIPPLANE(0), { STATE_CLIPPLANE(0), clipplane }, WINED3D_GL_EXT_NONE }, + { STATE_CLIPPLANE(1), { STATE_CLIPPLANE(1), clipplane }, WINED3D_GL_EXT_NONE }, + { STATE_CLIPPLANE(2), { STATE_CLIPPLANE(2), clipplane }, WINED3D_GL_EXT_NONE }, + { STATE_CLIPPLANE(3), { STATE_CLIPPLANE(3), clipplane }, WINED3D_GL_EXT_NONE }, + { STATE_CLIPPLANE(4), { STATE_CLIPPLANE(4), clipplane }, WINED3D_GL_EXT_NONE }, + { STATE_CLIPPLANE(5), { STATE_CLIPPLANE(5), clipplane }, WINED3D_GL_EXT_NONE }, + { STATE_CLIPPLANE(6), { STATE_CLIPPLANE(6), clipplane }, WINED3D_GL_EXT_NONE }, + { STATE_CLIPPLANE(7), { STATE_CLIPPLANE(7), clipplane }, WINED3D_GL_EXT_NONE }, + /* Lights */ + { STATE_LIGHT_TYPE, { STATE_LIGHT_TYPE, state_nop }, WINED3D_GL_EXT_NONE }, + { STATE_ACTIVELIGHT(0), { STATE_ACTIVELIGHT(0), light }, WINED3D_GL_EXT_NONE }, + { STATE_ACTIVELIGHT(1), { STATE_ACTIVELIGHT(1), light }, WINED3D_GL_EXT_NONE }, + { STATE_ACTIVELIGHT(2), { STATE_ACTIVELIGHT(2), light }, WINED3D_GL_EXT_NONE }, + { STATE_ACTIVELIGHT(3), { STATE_ACTIVELIGHT(3), light }, WINED3D_GL_EXT_NONE }, + { STATE_ACTIVELIGHT(4), { STATE_ACTIVELIGHT(4), light }, WINED3D_GL_EXT_NONE }, + { STATE_ACTIVELIGHT(5), { STATE_ACTIVELIGHT(5), light }, WINED3D_GL_EXT_NONE }, + { STATE_ACTIVELIGHT(6), { STATE_ACTIVELIGHT(6), light }, WINED3D_GL_EXT_NONE }, + { STATE_ACTIVELIGHT(7), { STATE_ACTIVELIGHT(7), light }, WINED3D_GL_EXT_NONE }, + /* Viewport */ + { STATE_VIEWPORT, { STATE_VIEWPORT, viewport_vertexpart }, WINED3D_GL_EXT_NONE }, + /* Transform states follow */ + { STATE_TRANSFORM(WINED3D_TS_VIEW), { STATE_TRANSFORM(WINED3D_TS_VIEW), transform_view }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_PROJECTION), { STATE_TRANSFORM(WINED3D_TS_PROJECTION), transform_projection}, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_TEXTURE0), { STATE_TEXTURESTAGE(0, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_TEXTURE1), { STATE_TEXTURESTAGE(1, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_TEXTURE2), { STATE_TEXTURESTAGE(2, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_TEXTURE3), { STATE_TEXTURESTAGE(3, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_TEXTURE4), { STATE_TEXTURESTAGE(4, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_TEXTURE5), { STATE_TEXTURESTAGE(5, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_TEXTURE6), { STATE_TEXTURESTAGE(6, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_TEXTURE7), { STATE_TEXTURESTAGE(7, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 0)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 0)), transform_world }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 1)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 1)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 2)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 2)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 3)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 3)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 4)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 4)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 5)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 5)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 6)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 6)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 7)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 7)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 8)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 8)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 9)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 9)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 10)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 10)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 11)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 11)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 12)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 12)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 13)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 13)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 14)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 14)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 15)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 15)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 16)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 16)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 17)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 17)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 18)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 18)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 19)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 19)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 20)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 20)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 21)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 21)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 22)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 22)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 23)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 23)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 24)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 24)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 25)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 25)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 26)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 26)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 27)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 27)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 28)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 28)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 29)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 29)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 30)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 30)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 31)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 31)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 32)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 32)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 33)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 33)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 34)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 34)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 35)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 35)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 36)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 36)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 37)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 37)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 38)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 38)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 39)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 39)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 40)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 40)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 41)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 41)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 42)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 42)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 43)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 43)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 44)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 44)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 45)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 45)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 46)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 46)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 47)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 47)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 48)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 48)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 49)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 49)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 50)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 50)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 51)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 51)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 52)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 52)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 53)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 53)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 54)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 54)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 55)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 55)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 56)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 56)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 57)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 57)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 58)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 58)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 59)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 59)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 60)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 60)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 61)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 61)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 62)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 62)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 63)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 63)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 64)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 64)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 65)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 65)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 66)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 66)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 67)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 67)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 68)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 68)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 69)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 69)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 70)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 70)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 71)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 71)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 72)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 72)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 73)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 73)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 74)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 74)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 75)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 75)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 76)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 76)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 77)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 77)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 78)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 78)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 79)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 79)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 80)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 80)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 81)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 81)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 82)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 82)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 83)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 83)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 84)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 84)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 85)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 85)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 86)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 86)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 87)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 87)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 88)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 88)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 89)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 89)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 90)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 90)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 91)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 91)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 92)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 92)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 93)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 93)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 94)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 94)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 95)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 95)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 96)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 96)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 97)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 97)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 98)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 98)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 99)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX( 99)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(100)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(100)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(101)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(101)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(102)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(102)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(103)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(103)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(104)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(104)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(105)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(105)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(106)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(106)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(107)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(107)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(108)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(108)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(109)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(109)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(110)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(110)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(111)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(111)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(112)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(112)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(113)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(113)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(114)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(114)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(115)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(115)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(116)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(116)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(117)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(117)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(118)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(118)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(119)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(119)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(120)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(120)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(121)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(121)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(122)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(122)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(123)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(123)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(124)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(124)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(125)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(125)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(126)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(126)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(127)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(127)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(128)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(128)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(129)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(129)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(130)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(130)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(131)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(131)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(132)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(132)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(133)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(133)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(134)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(134)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(135)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(135)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(136)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(136)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(137)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(137)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(138)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(138)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(139)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(139)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(140)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(140)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(141)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(141)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(142)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(142)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(143)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(143)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(144)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(144)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(145)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(145)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(146)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(146)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(147)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(147)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(148)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(148)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(149)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(149)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(150)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(150)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(151)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(151)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(152)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(152)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(153)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(153)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(154)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(154)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(155)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(155)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(156)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(156)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(157)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(157)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(158)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(158)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(159)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(159)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(160)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(160)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(161)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(161)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(162)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(162)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(163)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(163)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(164)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(164)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(165)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(165)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(166)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(166)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(167)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(167)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(168)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(168)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(169)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(169)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(170)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(170)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(171)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(171)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(172)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(172)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(173)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(173)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(174)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(174)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(175)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(175)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(176)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(176)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(177)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(177)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(178)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(178)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(179)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(179)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(180)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(180)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(181)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(181)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(182)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(182)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(183)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(183)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(184)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(184)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(185)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(185)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(186)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(186)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(187)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(187)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(188)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(188)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(189)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(189)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(190)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(190)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(191)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(191)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(192)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(192)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(193)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(193)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(194)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(194)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(195)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(195)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(196)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(196)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(197)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(197)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(198)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(198)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(199)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(199)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(200)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(200)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(201)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(201)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(202)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(202)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(203)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(203)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(204)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(204)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(205)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(205)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(206)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(206)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(207)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(207)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(208)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(208)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(209)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(209)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(210)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(210)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(211)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(211)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(212)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(212)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(213)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(213)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(214)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(214)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(215)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(215)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(216)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(216)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(217)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(217)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(218)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(218)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(219)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(219)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(220)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(220)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(221)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(221)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(222)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(222)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(223)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(223)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(224)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(224)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(225)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(225)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(226)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(226)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(227)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(227)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(228)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(228)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(229)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(229)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(230)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(230)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(231)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(231)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(232)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(232)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(233)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(233)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(234)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(234)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(235)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(235)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(236)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(236)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(237)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(237)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(238)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(238)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(239)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(239)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(240)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(240)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(241)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(241)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(242)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(242)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(243)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(243)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(244)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(244)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(245)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(245)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(246)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(246)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(247)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(247)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(248)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(248)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(249)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(249)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(250)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(250)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(251)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(251)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(252)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(252)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(253)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(253)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(254)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(254)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(255)), { STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(255)), transform_worldex }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(0, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), {STATE_TEXTURESTAGE(0, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), transform_texture }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(1, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), {STATE_TEXTURESTAGE(1, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), transform_texture }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(2, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), {STATE_TEXTURESTAGE(2, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), transform_texture }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(3, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), {STATE_TEXTURESTAGE(3, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), transform_texture }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(4, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), {STATE_TEXTURESTAGE(4, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), transform_texture }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(5, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), {STATE_TEXTURESTAGE(5, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), transform_texture }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(6, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), {STATE_TEXTURESTAGE(6, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), transform_texture }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(7, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), {STATE_TEXTURESTAGE(7, WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS), transform_texture }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(0, WINED3D_TSS_TEXCOORD_INDEX), { STATE_TEXTURESTAGE(0, WINED3D_TSS_TEXCOORD_INDEX), tex_coordindex }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(1, WINED3D_TSS_TEXCOORD_INDEX), { STATE_TEXTURESTAGE(1, WINED3D_TSS_TEXCOORD_INDEX), tex_coordindex }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(2, WINED3D_TSS_TEXCOORD_INDEX), { STATE_TEXTURESTAGE(2, WINED3D_TSS_TEXCOORD_INDEX), tex_coordindex }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(3, WINED3D_TSS_TEXCOORD_INDEX), { STATE_TEXTURESTAGE(3, WINED3D_TSS_TEXCOORD_INDEX), tex_coordindex }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(4, WINED3D_TSS_TEXCOORD_INDEX), { STATE_TEXTURESTAGE(4, WINED3D_TSS_TEXCOORD_INDEX), tex_coordindex }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(5, WINED3D_TSS_TEXCOORD_INDEX), { STATE_TEXTURESTAGE(5, WINED3D_TSS_TEXCOORD_INDEX), tex_coordindex }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(6, WINED3D_TSS_TEXCOORD_INDEX), { STATE_TEXTURESTAGE(6, WINED3D_TSS_TEXCOORD_INDEX), tex_coordindex }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(7, WINED3D_TSS_TEXCOORD_INDEX), { STATE_TEXTURESTAGE(7, WINED3D_TSS_TEXCOORD_INDEX), tex_coordindex }, WINED3D_GL_EXT_NONE }, + /* Fog */ + { STATE_RENDER(WINED3D_RS_FOGENABLE), { STATE_RENDER(WINED3D_RS_FOGENABLE), state_fog_vertexpart}, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_FOGTABLEMODE), { STATE_RENDER(WINED3D_RS_FOGENABLE), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_FOGVERTEXMODE), { STATE_RENDER(WINED3D_RS_FOGENABLE), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_RANGEFOGENABLE), { STATE_RENDER(WINED3D_RS_FOGENABLE), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_CLIPPING), { STATE_RENDER(WINED3D_RS_CLIPPING), state_clipping }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_CLIPPLANEENABLE), { STATE_RENDER(WINED3D_RS_CLIPPING), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_LIGHTING), { STATE_RENDER(WINED3D_RS_LIGHTING), state_lighting }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_AMBIENT), { STATE_RENDER(WINED3D_RS_AMBIENT), state_ambient }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_COLORVERTEX), { STATE_RENDER(WINED3D_RS_COLORVERTEX), state_colormat }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_LOCALVIEWER), { STATE_RENDER(WINED3D_RS_LOCALVIEWER), state_localviewer }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_NORMALIZENORMALS), { STATE_RENDER(WINED3D_RS_NORMALIZENORMALS), state_normalize }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_DIFFUSEMATERIALSOURCE), { STATE_RENDER(WINED3D_RS_COLORVERTEX), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_SPECULARMATERIALSOURCE), { STATE_RENDER(WINED3D_RS_COLORVERTEX), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_AMBIENTMATERIALSOURCE), { STATE_RENDER(WINED3D_RS_COLORVERTEX), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_EMISSIVEMATERIALSOURCE), { STATE_RENDER(WINED3D_RS_COLORVERTEX), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_VERTEXBLEND), { STATE_RENDER(WINED3D_RS_VERTEXBLEND), state_vertexblend_w }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_POINTSIZE), { STATE_RENDER(WINED3D_RS_POINTSCALEENABLE), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_POINTSIZE_MIN), { STATE_RENDER(WINED3D_RS_POINTSIZE_MIN), state_psizemin_arb }, ARB_POINT_PARAMETERS }, + { STATE_RENDER(WINED3D_RS_POINTSIZE_MIN), { STATE_RENDER(WINED3D_RS_POINTSIZE_MIN), state_psizemin_ext }, EXT_POINT_PARAMETERS }, + { STATE_RENDER(WINED3D_RS_POINTSIZE_MIN), { STATE_RENDER(WINED3D_RS_POINTSIZE_MIN), state_psizemin_w }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_POINTSPRITEENABLE), { STATE_RENDER(WINED3D_RS_POINTSPRITEENABLE), state_pointsprite }, ARB_POINT_SPRITE }, + { STATE_RENDER(WINED3D_RS_POINTSPRITEENABLE), { STATE_RENDER(WINED3D_RS_POINTSPRITEENABLE), state_pointsprite_w }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_POINTSCALEENABLE), { STATE_RENDER(WINED3D_RS_POINTSCALEENABLE), state_pscale }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_POINTSCALE_A), { STATE_RENDER(WINED3D_RS_POINTSCALEENABLE), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_POINTSCALE_B), { STATE_RENDER(WINED3D_RS_POINTSCALEENABLE), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_POINTSCALE_C), { STATE_RENDER(WINED3D_RS_POINTSCALEENABLE), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_POINTSIZE_MAX), { STATE_RENDER(WINED3D_RS_POINTSIZE_MIN), NULL }, ARB_POINT_PARAMETERS }, + { STATE_RENDER(WINED3D_RS_POINTSIZE_MAX), { STATE_RENDER(WINED3D_RS_POINTSIZE_MIN), NULL }, EXT_POINT_PARAMETERS }, + { STATE_RENDER(WINED3D_RS_POINTSIZE_MAX), { STATE_RENDER(WINED3D_RS_POINTSIZE_MIN), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_TWEENFACTOR), { STATE_RENDER(WINED3D_RS_VERTEXBLEND), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_INDEXEDVERTEXBLENDENABLE), { STATE_RENDER(WINED3D_RS_VERTEXBLEND), NULL }, WINED3D_GL_EXT_NONE }, + + /* Samplers for NP2 texture matrix adjustions. They are not needed if GL_ARB_texture_non_power_of_two is supported, + * so register a NULL state handler in that case to get the vertex part of sampler() skipped(VTF is handled in the misc states. + * otherwise, register sampler_texmatrix, which takes care of updating the texture matrix + */ + { STATE_SAMPLER(0), { 0, NULL }, ARB_TEXTURE_NON_POWER_OF_TWO }, + { STATE_SAMPLER(0), { 0, NULL }, WINED3D_GL_NORMALIZED_TEXRECT }, + { STATE_SAMPLER(0), { STATE_SAMPLER(0), sampler_texmatrix }, WINED3D_GL_EXT_NONE }, + { STATE_SAMPLER(1), { 0, NULL }, ARB_TEXTURE_NON_POWER_OF_TWO }, + { STATE_SAMPLER(1), { 0, NULL }, WINED3D_GL_NORMALIZED_TEXRECT }, + { STATE_SAMPLER(1), { STATE_SAMPLER(1), sampler_texmatrix }, WINED3D_GL_EXT_NONE }, + { STATE_SAMPLER(2), { 0, NULL }, ARB_TEXTURE_NON_POWER_OF_TWO }, + { STATE_SAMPLER(2), { 0, NULL }, WINED3D_GL_NORMALIZED_TEXRECT }, + { STATE_SAMPLER(2), { STATE_SAMPLER(2), sampler_texmatrix }, WINED3D_GL_EXT_NONE }, + { STATE_SAMPLER(3), { 0, NULL }, ARB_TEXTURE_NON_POWER_OF_TWO }, + { STATE_SAMPLER(3), { 0, NULL }, WINED3D_GL_NORMALIZED_TEXRECT }, + { STATE_SAMPLER(3), { STATE_SAMPLER(3), sampler_texmatrix }, WINED3D_GL_EXT_NONE }, + { STATE_SAMPLER(4), { 0, NULL }, ARB_TEXTURE_NON_POWER_OF_TWO }, + { STATE_SAMPLER(4), { 0, NULL }, WINED3D_GL_NORMALIZED_TEXRECT }, + { STATE_SAMPLER(4), { STATE_SAMPLER(4), sampler_texmatrix }, WINED3D_GL_EXT_NONE }, + { STATE_SAMPLER(5), { 0, NULL }, ARB_TEXTURE_NON_POWER_OF_TWO }, + { STATE_SAMPLER(5), { 0, NULL }, WINED3D_GL_NORMALIZED_TEXRECT }, + { STATE_SAMPLER(5), { STATE_SAMPLER(5), sampler_texmatrix }, WINED3D_GL_EXT_NONE }, + { STATE_SAMPLER(6), { 0, NULL }, ARB_TEXTURE_NON_POWER_OF_TWO }, + { STATE_SAMPLER(6), { 0, NULL }, WINED3D_GL_NORMALIZED_TEXRECT }, + { STATE_SAMPLER(6), { STATE_SAMPLER(6), sampler_texmatrix }, WINED3D_GL_EXT_NONE }, + { STATE_SAMPLER(7), { 0, NULL }, ARB_TEXTURE_NON_POWER_OF_TWO }, + { STATE_SAMPLER(7), { 0, NULL }, WINED3D_GL_NORMALIZED_TEXRECT }, + { STATE_SAMPLER(7), { STATE_SAMPLER(7), sampler_texmatrix }, WINED3D_GL_EXT_NONE }, + { STATE_POINT_ENABLE, { STATE_POINT_ENABLE, state_nop }, WINED3D_GL_EXT_NONE }, + {0 /* Terminate */, { 0, 0 }, WINED3D_GL_EXT_NONE }, +}; + +static const struct wined3d_state_entry_template ffp_fragmentstate_template[] = { + { STATE_TEXTURESTAGE(0, WINED3D_TSS_COLOR_OP), { STATE_TEXTURESTAGE(0, WINED3D_TSS_COLOR_OP), tex_colorop }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(0, WINED3D_TSS_COLOR_ARG1), { STATE_TEXTURESTAGE(0, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(0, WINED3D_TSS_COLOR_ARG2), { STATE_TEXTURESTAGE(0, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(0, WINED3D_TSS_ALPHA_OP), { STATE_TEXTURESTAGE(0, WINED3D_TSS_ALPHA_OP), tex_alphaop }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(0, WINED3D_TSS_ALPHA_ARG1), { STATE_TEXTURESTAGE(0, WINED3D_TSS_ALPHA_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(0, WINED3D_TSS_ALPHA_ARG2), { STATE_TEXTURESTAGE(0, WINED3D_TSS_ALPHA_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(0, WINED3D_TSS_COLOR_ARG0), { STATE_TEXTURESTAGE(0, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(0, WINED3D_TSS_ALPHA_ARG0), { STATE_TEXTURESTAGE(0, WINED3D_TSS_ALPHA_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(0, WINED3D_TSS_RESULT_ARG), { STATE_TEXTURESTAGE(0, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(0, WINED3D_TSS_CONSTANT), { 0 /* As long as we don't support D3DTA_CONSTANT */, NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(1, WINED3D_TSS_COLOR_OP), { STATE_TEXTURESTAGE(1, WINED3D_TSS_COLOR_OP), tex_colorop }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(1, WINED3D_TSS_COLOR_ARG1), { STATE_TEXTURESTAGE(1, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(1, WINED3D_TSS_COLOR_ARG2), { STATE_TEXTURESTAGE(1, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(1, WINED3D_TSS_ALPHA_OP), { STATE_TEXTURESTAGE(1, WINED3D_TSS_ALPHA_OP), tex_alphaop }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(1, WINED3D_TSS_ALPHA_ARG1), { STATE_TEXTURESTAGE(1, WINED3D_TSS_ALPHA_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(1, WINED3D_TSS_ALPHA_ARG2), { STATE_TEXTURESTAGE(1, WINED3D_TSS_ALPHA_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(1, WINED3D_TSS_COLOR_ARG0), { STATE_TEXTURESTAGE(1, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(1, WINED3D_TSS_ALPHA_ARG0), { STATE_TEXTURESTAGE(1, WINED3D_TSS_ALPHA_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(1, WINED3D_TSS_RESULT_ARG), { STATE_TEXTURESTAGE(1, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(1, WINED3D_TSS_CONSTANT), { 0 /* As long as we don't support D3DTA_CONSTANT */, NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(2, WINED3D_TSS_COLOR_OP), { STATE_TEXTURESTAGE(2, WINED3D_TSS_COLOR_OP), tex_colorop }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(2, WINED3D_TSS_COLOR_ARG1), { STATE_TEXTURESTAGE(2, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(2, WINED3D_TSS_COLOR_ARG2), { STATE_TEXTURESTAGE(2, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(2, WINED3D_TSS_ALPHA_OP), { STATE_TEXTURESTAGE(2, WINED3D_TSS_ALPHA_OP), tex_alphaop }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(2, WINED3D_TSS_ALPHA_ARG1), { STATE_TEXTURESTAGE(2, WINED3D_TSS_ALPHA_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(2, WINED3D_TSS_ALPHA_ARG2), { STATE_TEXTURESTAGE(2, WINED3D_TSS_ALPHA_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(2, WINED3D_TSS_COLOR_ARG0), { STATE_TEXTURESTAGE(2, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(2, WINED3D_TSS_ALPHA_ARG0), { STATE_TEXTURESTAGE(2, WINED3D_TSS_ALPHA_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(2, WINED3D_TSS_RESULT_ARG), { STATE_TEXTURESTAGE(2, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(2, WINED3D_TSS_CONSTANT), { 0 /* As long as we don't support D3DTA_CONSTANT */, NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(3, WINED3D_TSS_COLOR_OP), { STATE_TEXTURESTAGE(3, WINED3D_TSS_COLOR_OP), tex_colorop }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(3, WINED3D_TSS_COLOR_ARG1), { STATE_TEXTURESTAGE(3, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(3, WINED3D_TSS_COLOR_ARG2), { STATE_TEXTURESTAGE(3, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(3, WINED3D_TSS_ALPHA_OP), { STATE_TEXTURESTAGE(3, WINED3D_TSS_ALPHA_OP), tex_alphaop }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(3, WINED3D_TSS_ALPHA_ARG1), { STATE_TEXTURESTAGE(3, WINED3D_TSS_ALPHA_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(3, WINED3D_TSS_ALPHA_ARG2), { STATE_TEXTURESTAGE(3, WINED3D_TSS_ALPHA_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(3, WINED3D_TSS_COLOR_ARG0), { STATE_TEXTURESTAGE(3, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(3, WINED3D_TSS_ALPHA_ARG0), { STATE_TEXTURESTAGE(3, WINED3D_TSS_ALPHA_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(3, WINED3D_TSS_RESULT_ARG), { STATE_TEXTURESTAGE(3, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(3, WINED3D_TSS_CONSTANT), { 0 /* As long as we don't support D3DTA_CONSTANT */, NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(4, WINED3D_TSS_COLOR_OP), { STATE_TEXTURESTAGE(4, WINED3D_TSS_COLOR_OP), tex_colorop }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(4, WINED3D_TSS_COLOR_ARG1), { STATE_TEXTURESTAGE(4, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(4, WINED3D_TSS_COLOR_ARG2), { STATE_TEXTURESTAGE(4, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(4, WINED3D_TSS_ALPHA_OP), { STATE_TEXTURESTAGE(4, WINED3D_TSS_ALPHA_OP), tex_alphaop }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(4, WINED3D_TSS_ALPHA_ARG1), { STATE_TEXTURESTAGE(4, WINED3D_TSS_ALPHA_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(4, WINED3D_TSS_ALPHA_ARG2), { STATE_TEXTURESTAGE(4, WINED3D_TSS_ALPHA_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(4, WINED3D_TSS_COLOR_ARG0), { STATE_TEXTURESTAGE(4, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(4, WINED3D_TSS_ALPHA_ARG0), { STATE_TEXTURESTAGE(4, WINED3D_TSS_ALPHA_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(4, WINED3D_TSS_RESULT_ARG), { STATE_TEXTURESTAGE(4, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(4, WINED3D_TSS_CONSTANT), { 0 /* As long as we don't support D3DTA_CONSTANT */, NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(5, WINED3D_TSS_COLOR_OP), { STATE_TEXTURESTAGE(5, WINED3D_TSS_COLOR_OP), tex_colorop }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(5, WINED3D_TSS_COLOR_ARG1), { STATE_TEXTURESTAGE(5, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(5, WINED3D_TSS_COLOR_ARG2), { STATE_TEXTURESTAGE(5, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(5, WINED3D_TSS_ALPHA_OP), { STATE_TEXTURESTAGE(5, WINED3D_TSS_ALPHA_OP), tex_alphaop }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(5, WINED3D_TSS_ALPHA_ARG1), { STATE_TEXTURESTAGE(5, WINED3D_TSS_ALPHA_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(5, WINED3D_TSS_ALPHA_ARG2), { STATE_TEXTURESTAGE(5, WINED3D_TSS_ALPHA_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(5, WINED3D_TSS_COLOR_ARG0), { STATE_TEXTURESTAGE(5, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(5, WINED3D_TSS_ALPHA_ARG0), { STATE_TEXTURESTAGE(5, WINED3D_TSS_ALPHA_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(5, WINED3D_TSS_RESULT_ARG), { STATE_TEXTURESTAGE(5, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(5, WINED3D_TSS_CONSTANT), { 0 /* As long as we don't support D3DTA_CONSTANT */, NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(6, WINED3D_TSS_COLOR_OP), { STATE_TEXTURESTAGE(6, WINED3D_TSS_COLOR_OP), tex_colorop }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(6, WINED3D_TSS_COLOR_ARG1), { STATE_TEXTURESTAGE(6, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(6, WINED3D_TSS_COLOR_ARG2), { STATE_TEXTURESTAGE(6, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(6, WINED3D_TSS_ALPHA_OP), { STATE_TEXTURESTAGE(6, WINED3D_TSS_ALPHA_OP), tex_alphaop }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(6, WINED3D_TSS_ALPHA_ARG1), { STATE_TEXTURESTAGE(6, WINED3D_TSS_ALPHA_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(6, WINED3D_TSS_ALPHA_ARG2), { STATE_TEXTURESTAGE(6, WINED3D_TSS_ALPHA_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(6, WINED3D_TSS_COLOR_ARG0), { STATE_TEXTURESTAGE(6, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(6, WINED3D_TSS_ALPHA_ARG0), { STATE_TEXTURESTAGE(6, WINED3D_TSS_ALPHA_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(6, WINED3D_TSS_RESULT_ARG), { STATE_TEXTURESTAGE(6, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(6, WINED3D_TSS_CONSTANT), { 0 /* As long as we don't support D3DTA_CONSTANT */, NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(7, WINED3D_TSS_COLOR_OP), { STATE_TEXTURESTAGE(7, WINED3D_TSS_COLOR_OP), tex_colorop }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(7, WINED3D_TSS_COLOR_ARG1), { STATE_TEXTURESTAGE(7, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(7, WINED3D_TSS_COLOR_ARG2), { STATE_TEXTURESTAGE(7, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(7, WINED3D_TSS_ALPHA_OP), { STATE_TEXTURESTAGE(7, WINED3D_TSS_ALPHA_OP), tex_alphaop }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(7, WINED3D_TSS_ALPHA_ARG1), { STATE_TEXTURESTAGE(7, WINED3D_TSS_ALPHA_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(7, WINED3D_TSS_ALPHA_ARG2), { STATE_TEXTURESTAGE(7, WINED3D_TSS_ALPHA_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(7, WINED3D_TSS_COLOR_ARG0), { STATE_TEXTURESTAGE(7, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(7, WINED3D_TSS_ALPHA_ARG0), { STATE_TEXTURESTAGE(7, WINED3D_TSS_ALPHA_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(7, WINED3D_TSS_RESULT_ARG), { STATE_TEXTURESTAGE(7, WINED3D_TSS_COLOR_OP), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_TEXTURESTAGE(7, WINED3D_TSS_CONSTANT), { 0 /* As long as we don't support D3DTA_CONSTANT */, NULL }, WINED3D_GL_EXT_NONE }, + { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), apply_pixelshader }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_ALPHAFUNC), { STATE_RENDER(WINED3D_RS_ALPHATESTENABLE), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_ALPHAREF), { STATE_RENDER(WINED3D_RS_ALPHATESTENABLE), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_ALPHATESTENABLE), { STATE_RENDER(WINED3D_RS_ALPHATESTENABLE), state_alpha_test }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_COLORKEYENABLE), { STATE_RENDER(WINED3D_RS_ALPHATESTENABLE), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_COLOR_KEY, { STATE_COLOR_KEY, state_nop }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_SRGBWRITEENABLE), { STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_TEXTUREFACTOR), { STATE_RENDER(WINED3D_RS_TEXTUREFACTOR), state_texfactor }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_FOGCOLOR), { STATE_RENDER(WINED3D_RS_FOGCOLOR), state_fogcolor }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_FOGDENSITY), { STATE_RENDER(WINED3D_RS_FOGDENSITY), state_fogdensity }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_FOGENABLE), { STATE_RENDER(WINED3D_RS_FOGENABLE), state_fog_fragpart }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_FOGTABLEMODE), { STATE_RENDER(WINED3D_RS_FOGENABLE), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_FOGVERTEXMODE), { STATE_RENDER(WINED3D_RS_FOGENABLE), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_FOGSTART), { STATE_RENDER(WINED3D_RS_FOGSTART), state_fogstartend }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_FOGEND), { STATE_RENDER(WINED3D_RS_FOGSTART), NULL }, WINED3D_GL_EXT_NONE }, + { STATE_RENDER(WINED3D_RS_SHADEMODE), { STATE_RENDER(WINED3D_RS_SHADEMODE), state_shademode }, WINED3D_GL_EXT_NONE }, + { STATE_SAMPLER(0), { STATE_SAMPLER(0), sampler_texdim }, WINED3D_GL_EXT_NONE }, + { STATE_SAMPLER(1), { STATE_SAMPLER(1), sampler_texdim }, WINED3D_GL_EXT_NONE }, + { STATE_SAMPLER(2), { STATE_SAMPLER(2), sampler_texdim }, WINED3D_GL_EXT_NONE }, + { STATE_SAMPLER(3), { STATE_SAMPLER(3), sampler_texdim }, WINED3D_GL_EXT_NONE }, + { STATE_SAMPLER(4), { STATE_SAMPLER(4), sampler_texdim }, WINED3D_GL_EXT_NONE }, + { STATE_SAMPLER(5), { STATE_SAMPLER(5), sampler_texdim }, WINED3D_GL_EXT_NONE }, + { STATE_SAMPLER(6), { STATE_SAMPLER(6), sampler_texdim }, WINED3D_GL_EXT_NONE }, + { STATE_SAMPLER(7), { STATE_SAMPLER(7), sampler_texdim }, WINED3D_GL_EXT_NONE }, + {0 /* Terminate */, { 0, 0 }, WINED3D_GL_EXT_NONE }, +}; + +/* Context activation is done by the caller. */ +static void ffp_pipe_enable(const struct wined3d_context *context, BOOL enable) {} + +static void *ffp_alloc(const struct wined3d_shader_backend_ops *shader_backend, void *shader_priv) +{ + return shader_priv; +} + +static void ffp_free(struct wined3d_device *device, struct wined3d_context *context) {} + +static void vp_ffp_get_caps(const struct wined3d_adapter *adapter, struct wined3d_vertex_caps *caps) +{ + const struct wined3d_gl_info *gl_info = &adapter->gl_info; + + caps->xyzrhw = FALSE; + caps->ffp_generic_attributes = FALSE; + caps->max_active_lights = gl_info->limits.lights; + caps->max_vertex_blend_matrices = 1; + caps->max_vertex_blend_matrix_index = 0; + caps->vertex_processing_caps = WINED3DVTXPCAPS_DIRECTIONALLIGHTS + | WINED3DVTXPCAPS_MATERIALSOURCE7 + | WINED3DVTXPCAPS_POSITIONALLIGHTS + | WINED3DVTXPCAPS_LOCALVIEWER + | WINED3DVTXPCAPS_VERTEXFOG + | WINED3DVTXPCAPS_TEXGEN + | WINED3DVTXPCAPS_TEXGEN_SPHEREMAP; + caps->fvf_caps = WINED3DFVFCAPS_PSIZE | 0x0008; /* 8 texture coords */ + caps->max_user_clip_planes = gl_info->limits.user_clip_distances; + caps->raster_caps = 0; + if (gl_info->supported[NV_FOG_DISTANCE]) + caps->raster_caps |= WINED3DPRASTERCAPS_FOGRANGE; +} + +static DWORD vp_ffp_get_emul_mask(const struct wined3d_gl_info *gl_info) +{ + return GL_EXT_EMUL_ARB_MULTITEXTURE | GL_EXT_EMUL_EXT_FOG_COORD; +} + +const struct wined3d_vertex_pipe_ops ffp_vertex_pipe = +{ + ffp_pipe_enable, + vp_ffp_get_caps, + vp_ffp_get_emul_mask, + ffp_alloc, + ffp_free, + vp_ffp_states, +}; + +static void ffp_fragment_get_caps(const struct wined3d_adapter *adapter, struct fragment_caps *caps) +{ + const struct wined3d_gl_info *gl_info = &adapter->gl_info; + + caps->wined3d_caps = 0; + caps->PrimitiveMiscCaps = 0; + caps->TextureOpCaps = WINED3DTEXOPCAPS_ADD + | WINED3DTEXOPCAPS_ADDSIGNED + | WINED3DTEXOPCAPS_ADDSIGNED2X + | WINED3DTEXOPCAPS_MODULATE + | WINED3DTEXOPCAPS_MODULATE2X + | WINED3DTEXOPCAPS_MODULATE4X + | WINED3DTEXOPCAPS_SELECTARG1 + | WINED3DTEXOPCAPS_SELECTARG2 + | WINED3DTEXOPCAPS_DISABLE; + + if (gl_info->supported[ARB_TEXTURE_ENV_COMBINE] + || gl_info->supported[EXT_TEXTURE_ENV_COMBINE] + || gl_info->supported[NV_TEXTURE_ENV_COMBINE4]) + { + caps->TextureOpCaps |= WINED3DTEXOPCAPS_BLENDDIFFUSEALPHA + | WINED3DTEXOPCAPS_BLENDTEXTUREALPHA + | WINED3DTEXOPCAPS_BLENDFACTORALPHA + | WINED3DTEXOPCAPS_BLENDCURRENTALPHA + | WINED3DTEXOPCAPS_LERP + | WINED3DTEXOPCAPS_SUBTRACT; + } + if (gl_info->supported[ATI_TEXTURE_ENV_COMBINE3] + || gl_info->supported[NV_TEXTURE_ENV_COMBINE4]) + { + caps->TextureOpCaps |= WINED3DTEXOPCAPS_ADDSMOOTH + | WINED3DTEXOPCAPS_MULTIPLYADD + | WINED3DTEXOPCAPS_MODULATEALPHA_ADDCOLOR + | WINED3DTEXOPCAPS_MODULATECOLOR_ADDALPHA + | WINED3DTEXOPCAPS_BLENDTEXTUREALPHAPM; + } + if (gl_info->supported[ARB_TEXTURE_ENV_DOT3]) + caps->TextureOpCaps |= WINED3DTEXOPCAPS_DOTPRODUCT3; + + caps->MaxTextureBlendStages = gl_info->limits.textures; + caps->MaxSimultaneousTextures = gl_info->limits.textures; +} + +static DWORD ffp_fragment_get_emul_mask(const struct wined3d_gl_info *gl_info) +{ + return GL_EXT_EMUL_ARB_MULTITEXTURE | GL_EXT_EMUL_EXT_FOG_COORD; +} + +static BOOL ffp_color_fixup_supported(struct color_fixup_desc fixup) +{ + /* We only support identity conversions. */ + return is_identity_fixup(fixup); +} + +static BOOL ffp_none_context_alloc(struct wined3d_context *context) +{ + return TRUE; +} + +static void ffp_none_context_free(struct wined3d_context *context) +{ +} + +const struct wined3d_fragment_pipe_ops ffp_fragment_pipeline = +{ + ffp_pipe_enable, + ffp_fragment_get_caps, + ffp_fragment_get_emul_mask, + ffp_alloc, + ffp_free, + ffp_none_context_alloc, + ffp_none_context_free, + ffp_color_fixup_supported, + ffp_fragmentstate_template, +}; + +static void none_pipe_enable(const struct wined3d_context *context, BOOL enable) {} + +static void *none_alloc(const struct wined3d_shader_backend_ops *shader_backend, void *shader_priv) +{ + return shader_priv; +} + +static void none_free(struct wined3d_device *device, struct wined3d_context *context) {} + +static void vp_none_get_caps(const struct wined3d_adapter *adapter, struct wined3d_vertex_caps *caps) +{ + memset(caps, 0, sizeof(*caps)); +} + +static DWORD vp_none_get_emul_mask(const struct wined3d_gl_info *gl_info) +{ + return 0; +} + +const struct wined3d_vertex_pipe_ops none_vertex_pipe = +{ + none_pipe_enable, + vp_none_get_caps, + vp_none_get_emul_mask, + none_alloc, + none_free, + NULL, +}; + +static void fp_none_get_caps(const struct wined3d_adapter *adapter, struct fragment_caps *caps) +{ + memset(caps, 0, sizeof(*caps)); +} + +static DWORD fp_none_get_emul_mask(const struct wined3d_gl_info *gl_info) +{ + return 0; +} + +static BOOL fp_none_color_fixup_supported(struct color_fixup_desc fixup) +{ + return is_identity_fixup(fixup); +} + +const struct wined3d_fragment_pipe_ops none_fragment_pipe = +{ + none_pipe_enable, + fp_none_get_caps, + fp_none_get_emul_mask, + none_alloc, + none_free, + ffp_none_context_alloc, + ffp_none_context_free, + fp_none_color_fixup_supported, + NULL, +}; + +static unsigned int num_handlers(const APPLYSTATEFUNC *funcs) +{ + unsigned int i; + for(i = 0; funcs[i]; i++); + return i; +} + +static void multistate_apply_2(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + context->device->multistate_funcs[state_id][0](context, state, state_id); + context->device->multistate_funcs[state_id][1](context, state, state_id); +} + +static void multistate_apply_3(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + context->device->multistate_funcs[state_id][0](context, state, state_id); + context->device->multistate_funcs[state_id][1](context, state, state_id); + context->device->multistate_funcs[state_id][2](context, state, state_id); +} + +static void prune_invalid_states(struct wined3d_state_entry *state_table, const struct wined3d_d3d_info *d3d_info) +{ + unsigned int start, last, i; + + start = STATE_TEXTURESTAGE(d3d_info->limits.ffp_blend_stages, 0); + last = STATE_TEXTURESTAGE(WINED3D_MAX_TEXTURES - 1, WINED3D_HIGHEST_TEXTURE_STATE); + for (i = start; i <= last; ++i) + { + state_table[i].representative = 0; + state_table[i].apply = state_undefined; + } + + start = STATE_TRANSFORM(WINED3D_TS_TEXTURE0 + d3d_info->limits.ffp_blend_stages); + last = STATE_TRANSFORM(WINED3D_TS_TEXTURE0 + WINED3D_MAX_TEXTURES - 1); + for (i = start; i <= last; ++i) + { + state_table[i].representative = 0; + state_table[i].apply = state_undefined; + } + + start = STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(d3d_info->limits.ffp_vertex_blend_matrices)); + last = STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(255)); + for (i = start; i <= last; ++i) + { + state_table[i].representative = 0; + state_table[i].apply = state_undefined; + } +} + +static void validate_state_table(struct wined3d_state_entry *state_table) +{ + static const struct + { + DWORD first; + DWORD last; + } + rs_holes[] = + { + { 1, 1}, + { 3, 3}, + { 7, 8}, + { 14, 14}, + { 17, 23}, + { 27, 27}, + { 40, 40}, + { 42, 45}, + { 47, 47}, + { 52, 59}, + { 61, 127}, + {149, 150}, + {162, 162}, + {168, 169}, + {171, 171}, + {174, 177}, + {185, 193}, + {195, 197}, + {206, 209}, + { 0, 0}, + }; + static const DWORD simple_states[] = + { + STATE_MATERIAL, + STATE_VDECL, + STATE_STREAMSRC, + STATE_INDEXBUFFER, + STATE_SHADER(WINED3D_SHADER_TYPE_VERTEX), + STATE_SHADER(WINED3D_SHADER_TYPE_HULL), + STATE_SHADER(WINED3D_SHADER_TYPE_DOMAIN), + STATE_SHADER(WINED3D_SHADER_TYPE_GEOMETRY), + STATE_SHADER(WINED3D_SHADER_TYPE_PIXEL), + STATE_SHADER(WINED3D_SHADER_TYPE_COMPUTE), + STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_VERTEX), + STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_HULL), + STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_DOMAIN), + STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_GEOMETRY), + STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_PIXEL), + STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_COMPUTE), + STATE_COMPUTE_SHADER_RESOURCE_BINDING, + STATE_GRAPHICS_SHADER_RESOURCE_BINDING, + STATE_COMPUTE_UNORDERED_ACCESS_VIEW_BINDING, + STATE_GRAPHICS_UNORDERED_ACCESS_VIEW_BINDING, + STATE_VIEWPORT, + STATE_LIGHT_TYPE, + STATE_SCISSORRECT, + STATE_RASTERIZER, + STATE_POINTSPRITECOORDORIGIN, + STATE_BASEVERTEXINDEX, + STATE_FRAMEBUFFER, + STATE_POINT_ENABLE, + STATE_COLOR_KEY, + STATE_BLEND, + STATE_BLEND_FACTOR, + STATE_DEPTH_STENCIL, + STATE_STENCIL_REF, + }; + unsigned int i, current; + + for (i = STATE_RENDER(1), current = 0; i <= STATE_RENDER(WINEHIGHEST_RENDER_STATE); ++i) + { + if (!rs_holes[current].first || i < STATE_RENDER(rs_holes[current].first)) + { + if (!state_table[i].representative) + ERR("State %s (%#x) should have a representative.\n", debug_d3dstate(i), i); + } + else if (state_table[i].representative) + ERR("State %s (%#x) shouldn't have a representative.\n", debug_d3dstate(i), i); + + if (i == STATE_RENDER(rs_holes[current].last)) ++current; + } + + for (i = 0; i < ARRAY_SIZE(simple_states); ++i) + { + if (!state_table[simple_states[i]].representative) + ERR("State %s (%#x) should have a representative.\n", + debug_d3dstate(simple_states[i]), simple_states[i]); + } + + for (i = 0; i < STATE_HIGHEST + 1; ++i) + { + DWORD rep = state_table[i].representative; + if (rep) + { + if (state_table[rep].representative != rep) + { + ERR("State %s (%#x) has invalid representative %s (%#x).\n", + debug_d3dstate(i), i, debug_d3dstate(rep), rep); + state_table[i].representative = 0; + } + + if (rep != i) + { + if (state_table[i].apply) + ERR("State %s (%#x) has both a handler and representative.\n", debug_d3dstate(i), i); + } + else if (!state_table[i].apply) + { + ERR("Self representing state %s (%#x) has no handler.\n", debug_d3dstate(i), i); + } + } + } +} + +HRESULT compile_state_table(struct wined3d_state_entry *state_table, APPLYSTATEFUNC **dev_multistate_funcs, + const struct wined3d_d3d_info *d3d_info, const BOOL *supported_extensions, + const struct wined3d_vertex_pipe_ops *vertex, const struct wined3d_fragment_pipe_ops *fragment, + const struct wined3d_state_entry_template *misc) +{ + APPLYSTATEFUNC multistate_funcs[STATE_HIGHEST + 1][3]; + const struct wined3d_state_entry_template *cur; + unsigned int i, type, handlers; + BOOL set[STATE_HIGHEST + 1]; + + memset(multistate_funcs, 0, sizeof(multistate_funcs)); + + for (i = 0; i < STATE_HIGHEST + 1; ++i) + { + state_table[i].representative = 0; + state_table[i].apply = state_undefined; + } + + for (type = 0; type < 3; ++type) + { + /* This switch decides the order in which the states are applied */ + switch (type) + { + case 0: cur = misc; break; + case 1: cur = fragment->states; break; + case 2: cur = vertex->vp_states; break; + default: cur = NULL; /* Stupid compiler */ + } + if (!cur) continue; + + /* GL extension filtering should not prevent multiple handlers being applied from different + * pipeline parts + */ + memset(set, 0, sizeof(set)); + + for (i = 0; cur[i].state; ++i) + { + APPLYSTATEFUNC *funcs_array; + + /* Only use the first matching state with the available extension from one template. + * e.g. + * {D3DRS_FOOBAR, {D3DRS_FOOBAR, func1}, XYZ_FANCY}, + * {D3DRS_FOOBAR, {D3DRS_FOOBAR, func2}, 0 } + * + * if GL_XYZ_fancy is supported, ignore the 2nd line + */ + if (set[cur[i].state]) continue; + /* Skip state lines depending on unsupported extensions */ + if (!supported_extensions[cur[i].extension]) continue; + set[cur[i].state] = TRUE; + /* In some cases having an extension means that nothing has to be + * done for a state, e.g. if GL_ARB_texture_non_power_of_two is + * supported, the texture coordinate fixup can be ignored. If the + * apply function is used, mark the state set(done above) to prevent + * applying later lines, but do not record anything in the state + * table + */ + if (!cur[i].content.representative) continue; + + handlers = num_handlers(multistate_funcs[cur[i].state]); + multistate_funcs[cur[i].state][handlers] = cur[i].content.apply; + switch (handlers) + { + case 0: + state_table[cur[i].state].apply = cur[i].content.apply; + break; + case 1: + state_table[cur[i].state].apply = multistate_apply_2; + if (!(dev_multistate_funcs[cur[i].state] = heap_calloc(2, sizeof(**dev_multistate_funcs)))) + goto out_of_mem; + + dev_multistate_funcs[cur[i].state][0] = multistate_funcs[cur[i].state][0]; + dev_multistate_funcs[cur[i].state][1] = multistate_funcs[cur[i].state][1]; + break; + case 2: + state_table[cur[i].state].apply = multistate_apply_3; + if (!(funcs_array = heap_realloc(dev_multistate_funcs[cur[i].state], + sizeof(**dev_multistate_funcs) * 3))) + goto out_of_mem; + + dev_multistate_funcs[cur[i].state] = funcs_array; + dev_multistate_funcs[cur[i].state][2] = multistate_funcs[cur[i].state][2]; + break; + default: + ERR("Unexpected amount of state handlers for state %u: %u.\n", + cur[i].state, handlers + 1); + } + + if (state_table[cur[i].state].representative + && state_table[cur[i].state].representative != cur[i].content.representative) + { + FIXME("State %s (%#x) has different representatives in different pipeline parts.\n", + debug_d3dstate(cur[i].state), cur[i].state); + } + state_table[cur[i].state].representative = cur[i].content.representative; + } + } + + prune_invalid_states(state_table, d3d_info); + validate_state_table(state_table); + + return WINED3D_OK; + +out_of_mem: + for (i = 0; i <= STATE_HIGHEST; ++i) + { + heap_free(dev_multistate_funcs[i]); + } + + memset(dev_multistate_funcs, 0, (STATE_HIGHEST + 1) * sizeof(*dev_multistate_funcs)); + + return E_OUTOFMEMORY; +} diff --git a/wrappers/directx/d3dwine_wrapper/stateblock.c b/wrappers/directx/d3dwine_wrapper/stateblock.c new file mode 100644 index 00000000000..46588fa4246 --- /dev/null +++ b/wrappers/directx/d3dwine_wrapper/stateblock.c @@ -0,0 +1,2006 @@ +/* + * state block implementation + * + * Copyright 2002 Raphael Junqueira + * Copyright 2004 Jason Edmeades + * Copyright 2005 Oliver Stieber + * Copyright 2007 Stefan Dösinger for CodeWeavers + * Copyright 2009 Henri Verbeet for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "config.h" +#include "wine/port.h" +#include "wined3d_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(d3d); + +static const DWORD pixel_states_render[] = +{ + WINED3D_RS_ALPHABLENDENABLE, + WINED3D_RS_ALPHAFUNC, + WINED3D_RS_ALPHAREF, + WINED3D_RS_ALPHATESTENABLE, + WINED3D_RS_ANTIALIASEDLINEENABLE, + WINED3D_RS_BLENDFACTOR, + WINED3D_RS_BLENDOP, + WINED3D_RS_BLENDOPALPHA, + WINED3D_RS_BACK_STENCILFAIL, + WINED3D_RS_BACK_STENCILPASS, + WINED3D_RS_BACK_STENCILZFAIL, + WINED3D_RS_COLORWRITEENABLE, + WINED3D_RS_COLORWRITEENABLE1, + WINED3D_RS_COLORWRITEENABLE2, + WINED3D_RS_COLORWRITEENABLE3, + WINED3D_RS_DEPTHBIAS, + WINED3D_RS_DESTBLEND, + WINED3D_RS_DESTBLENDALPHA, + WINED3D_RS_DITHERENABLE, + WINED3D_RS_FILLMODE, + WINED3D_RS_FOGDENSITY, + WINED3D_RS_FOGEND, + WINED3D_RS_FOGSTART, + WINED3D_RS_LASTPIXEL, + WINED3D_RS_SCISSORTESTENABLE, + WINED3D_RS_SEPARATEALPHABLENDENABLE, + WINED3D_RS_SHADEMODE, + WINED3D_RS_SLOPESCALEDEPTHBIAS, + WINED3D_RS_SRCBLEND, + WINED3D_RS_SRCBLENDALPHA, + WINED3D_RS_SRGBWRITEENABLE, + WINED3D_RS_STENCILENABLE, + WINED3D_RS_STENCILFAIL, + WINED3D_RS_STENCILFUNC, + WINED3D_RS_STENCILMASK, + WINED3D_RS_STENCILPASS, + WINED3D_RS_STENCILREF, + WINED3D_RS_STENCILWRITEMASK, + WINED3D_RS_STENCILZFAIL, + WINED3D_RS_TEXTUREFACTOR, + WINED3D_RS_TWOSIDEDSTENCILMODE, + WINED3D_RS_WRAP0, + WINED3D_RS_WRAP1, + WINED3D_RS_WRAP10, + WINED3D_RS_WRAP11, + WINED3D_RS_WRAP12, + WINED3D_RS_WRAP13, + WINED3D_RS_WRAP14, + WINED3D_RS_WRAP15, + WINED3D_RS_WRAP2, + WINED3D_RS_WRAP3, + WINED3D_RS_WRAP4, + WINED3D_RS_WRAP5, + WINED3D_RS_WRAP6, + WINED3D_RS_WRAP7, + WINED3D_RS_WRAP8, + WINED3D_RS_WRAP9, + WINED3D_RS_ZENABLE, + WINED3D_RS_ZFUNC, + WINED3D_RS_ZWRITEENABLE, +}; + +static const DWORD pixel_states_texture[] = +{ + WINED3D_TSS_ALPHA_ARG0, + WINED3D_TSS_ALPHA_ARG1, + WINED3D_TSS_ALPHA_ARG2, + WINED3D_TSS_ALPHA_OP, + WINED3D_TSS_BUMPENV_LOFFSET, + WINED3D_TSS_BUMPENV_LSCALE, + WINED3D_TSS_BUMPENV_MAT00, + WINED3D_TSS_BUMPENV_MAT01, + WINED3D_TSS_BUMPENV_MAT10, + WINED3D_TSS_BUMPENV_MAT11, + WINED3D_TSS_COLOR_ARG0, + WINED3D_TSS_COLOR_ARG1, + WINED3D_TSS_COLOR_ARG2, + WINED3D_TSS_COLOR_OP, + WINED3D_TSS_RESULT_ARG, + WINED3D_TSS_TEXCOORD_INDEX, + WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS, +}; + +static const DWORD pixel_states_sampler[] = +{ + WINED3D_SAMP_ADDRESS_U, + WINED3D_SAMP_ADDRESS_V, + WINED3D_SAMP_ADDRESS_W, + WINED3D_SAMP_BORDER_COLOR, + WINED3D_SAMP_MAG_FILTER, + WINED3D_SAMP_MIN_FILTER, + WINED3D_SAMP_MIP_FILTER, + WINED3D_SAMP_MIPMAP_LOD_BIAS, + WINED3D_SAMP_MAX_MIP_LEVEL, + WINED3D_SAMP_MAX_ANISOTROPY, + WINED3D_SAMP_SRGB_TEXTURE, + WINED3D_SAMP_ELEMENT_INDEX, +}; + +static const DWORD vertex_states_render[] = +{ + WINED3D_RS_ADAPTIVETESS_W, + WINED3D_RS_ADAPTIVETESS_X, + WINED3D_RS_ADAPTIVETESS_Y, + WINED3D_RS_ADAPTIVETESS_Z, + WINED3D_RS_AMBIENT, + WINED3D_RS_AMBIENTMATERIALSOURCE, + WINED3D_RS_CLIPPING, + WINED3D_RS_CLIPPLANEENABLE, + WINED3D_RS_COLORVERTEX, + WINED3D_RS_CULLMODE, + WINED3D_RS_DIFFUSEMATERIALSOURCE, + WINED3D_RS_EMISSIVEMATERIALSOURCE, + WINED3D_RS_ENABLEADAPTIVETESSELLATION, + WINED3D_RS_FOGCOLOR, + WINED3D_RS_FOGDENSITY, + WINED3D_RS_FOGENABLE, + WINED3D_RS_FOGEND, + WINED3D_RS_FOGSTART, + WINED3D_RS_FOGTABLEMODE, + WINED3D_RS_FOGVERTEXMODE, + WINED3D_RS_INDEXEDVERTEXBLENDENABLE, + WINED3D_RS_LIGHTING, + WINED3D_RS_LOCALVIEWER, + WINED3D_RS_MAXTESSELLATIONLEVEL, + WINED3D_RS_MINTESSELLATIONLEVEL, + WINED3D_RS_MULTISAMPLEANTIALIAS, + WINED3D_RS_MULTISAMPLEMASK, + WINED3D_RS_NORMALDEGREE, + WINED3D_RS_NORMALIZENORMALS, + WINED3D_RS_PATCHEDGESTYLE, + WINED3D_RS_POINTSCALE_A, + WINED3D_RS_POINTSCALE_B, + WINED3D_RS_POINTSCALE_C, + WINED3D_RS_POINTSCALEENABLE, + WINED3D_RS_POINTSIZE, + WINED3D_RS_POINTSIZE_MAX, + WINED3D_RS_POINTSIZE_MIN, + WINED3D_RS_POINTSPRITEENABLE, + WINED3D_RS_POSITIONDEGREE, + WINED3D_RS_RANGEFOGENABLE, + WINED3D_RS_SHADEMODE, + WINED3D_RS_SPECULARENABLE, + WINED3D_RS_SPECULARMATERIALSOURCE, + WINED3D_RS_TWEENFACTOR, + WINED3D_RS_VERTEXBLEND, +}; + +static const DWORD vertex_states_texture[] = +{ + WINED3D_TSS_TEXCOORD_INDEX, + WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS, +}; + +static const DWORD vertex_states_sampler[] = +{ + WINED3D_SAMP_DMAP_OFFSET, +}; + +static inline void stateblock_set_all_bits(DWORD *map, UINT map_size) +{ + DWORD mask = (1u << (map_size & 0x1f)) - 1; + memset(map, 0xff, (map_size >> 5) * sizeof(*map)); + if (mask) map[map_size >> 5] = mask; +} + +/* Set all members of a stateblock savedstate to the given value */ +static void stateblock_savedstates_set_all(struct wined3d_saved_states *states, DWORD vs_consts, DWORD ps_consts) +{ + unsigned int i; + + states->indices = 1; + states->material = 1; + states->viewport = 1; + states->vertexDecl = 1; + states->pixelShader = 1; + states->vertexShader = 1; + states->scissorRect = 1; + states->alpha_to_coverage = 1; + states->lights = 1; + states->transforms = 1; + + states->streamSource = 0xffff; + states->streamFreq = 0xffff; + states->textures = 0xfffff; + stateblock_set_all_bits(states->transform, WINED3D_HIGHEST_TRANSFORM_STATE + 1); + stateblock_set_all_bits(states->renderState, WINEHIGHEST_RENDER_STATE + 1); + for (i = 0; i < WINED3D_MAX_TEXTURES; ++i) states->textureState[i] = 0x3ffff; + for (i = 0; i < WINED3D_MAX_COMBINED_SAMPLERS; ++i) states->samplerState[i] = 0x3ffe; + states->clipplane = (1u << WINED3D_MAX_CLIP_DISTANCES) - 1; + states->pixelShaderConstantsB = 0xffff; + states->pixelShaderConstantsI = 0xffff; + states->vertexShaderConstantsB = 0xffff; + states->vertexShaderConstantsI = 0xffff; + + memset(states->ps_consts_f, 0xffu, sizeof(states->ps_consts_f)); + memset(states->vs_consts_f, 0xffu, sizeof(states->vs_consts_f)); +} + +static void stateblock_savedstates_set_pixel(struct wined3d_saved_states *states, const DWORD num_constants) +{ + DWORD texture_mask = 0; + WORD sampler_mask = 0; + unsigned int i; + + states->pixelShader = 1; + + for (i = 0; i < ARRAY_SIZE(pixel_states_render); ++i) + { + DWORD rs = pixel_states_render[i]; + states->renderState[rs >> 5] |= 1u << (rs & 0x1f); + } + + for (i = 0; i < ARRAY_SIZE(pixel_states_texture); ++i) + texture_mask |= 1u << pixel_states_texture[i]; + for (i = 0; i < WINED3D_MAX_TEXTURES; ++i) states->textureState[i] = texture_mask; + for (i = 0; i < ARRAY_SIZE(pixel_states_sampler); ++i) + sampler_mask |= 1u << pixel_states_sampler[i]; + for (i = 0; i < WINED3D_MAX_COMBINED_SAMPLERS; ++i) states->samplerState[i] = sampler_mask; + states->pixelShaderConstantsB = 0xffff; + states->pixelShaderConstantsI = 0xffff; + + memset(states->ps_consts_f, 0xffu, sizeof(states->ps_consts_f)); +} + +static void stateblock_savedstates_set_vertex(struct wined3d_saved_states *states, const DWORD num_constants) +{ + DWORD texture_mask = 0; + WORD sampler_mask = 0; + unsigned int i; + + states->vertexDecl = 1; + states->vertexShader = 1; + states->alpha_to_coverage = 1; + states->lights = 1; + + for (i = 0; i < ARRAY_SIZE(vertex_states_render); ++i) + { + DWORD rs = vertex_states_render[i]; + states->renderState[rs >> 5] |= 1u << (rs & 0x1f); + } + + for (i = 0; i < ARRAY_SIZE(vertex_states_texture); ++i) + texture_mask |= 1u << vertex_states_texture[i]; + for (i = 0; i < WINED3D_MAX_TEXTURES; ++i) states->textureState[i] = texture_mask; + for (i = 0; i < ARRAY_SIZE(vertex_states_sampler); ++i) + sampler_mask |= 1u << vertex_states_sampler[i]; + for (i = 0; i < WINED3D_MAX_COMBINED_SAMPLERS; ++i) states->samplerState[i] = sampler_mask; + states->vertexShaderConstantsB = 0xffff; + states->vertexShaderConstantsI = 0xffff; + + memset(states->vs_consts_f, 0xffu, sizeof(states->vs_consts_f)); +} + +void CDECL wined3d_stateblock_init_contained_states(struct wined3d_stateblock *stateblock) +{ + unsigned int i, j; + + for (i = 0; i <= WINEHIGHEST_RENDER_STATE >> 5; ++i) + { + DWORD map = stateblock->changed.renderState[i]; + for (j = 0; map; map >>= 1, ++j) + { + if (!(map & 1)) continue; + + stateblock->contained_render_states[stateblock->num_contained_render_states] = (i << 5) | j; + ++stateblock->num_contained_render_states; + } + } + + for (i = 0; i <= WINED3D_HIGHEST_TRANSFORM_STATE >> 5; ++i) + { + DWORD map = stateblock->changed.transform[i]; + for (j = 0; map; map >>= 1, ++j) + { + if (!(map & 1)) continue; + + stateblock->contained_transform_states[stateblock->num_contained_transform_states] = (i << 5) | j; + ++stateblock->num_contained_transform_states; + } + } + + for (i = 0; i < WINED3D_MAX_TEXTURES; ++i) + { + DWORD map = stateblock->changed.textureState[i]; + + for(j = 0; map; map >>= 1, ++j) + { + if (!(map & 1)) continue; + + stateblock->contained_tss_states[stateblock->num_contained_tss_states].stage = i; + stateblock->contained_tss_states[stateblock->num_contained_tss_states].state = j; + ++stateblock->num_contained_tss_states; + } + } + + for (i = 0; i < WINED3D_MAX_COMBINED_SAMPLERS; ++i) + { + DWORD map = stateblock->changed.samplerState[i]; + + for (j = 0; map; map >>= 1, ++j) + { + if (!(map & 1)) continue; + + stateblock->contained_sampler_states[stateblock->num_contained_sampler_states].stage = i; + stateblock->contained_sampler_states[stateblock->num_contained_sampler_states].state = j; + ++stateblock->num_contained_sampler_states; + } + } +} + +static void stateblock_init_lights(struct list *dst_map, const struct list *src_map) +{ + unsigned int i; + + for (i = 0; i < LIGHTMAP_SIZE; ++i) + { + const struct wined3d_light_info *src_light; + + LIST_FOR_EACH_ENTRY(src_light, &src_map[i], struct wined3d_light_info, entry) + { + struct wined3d_light_info *dst_light = heap_alloc(sizeof(*dst_light)); + + *dst_light = *src_light; + list_add_tail(&dst_map[i], &dst_light->entry); + } + } +} + +ULONG CDECL wined3d_stateblock_incref(struct wined3d_stateblock *stateblock) +{ + ULONG refcount = InterlockedIncrement(&stateblock->ref); + + TRACE("%p increasing refcount to %u.\n", stateblock, refcount); + + return refcount; +} + +void state_unbind_resources(struct wined3d_state *state) +{ + struct wined3d_unordered_access_view *uav; + struct wined3d_shader_resource_view *srv; + struct wined3d_vertex_declaration *decl; + struct wined3d_blend_state *blend_state; + struct wined3d_sampler *sampler; + struct wined3d_texture *texture; + struct wined3d_buffer *buffer; + struct wined3d_shader *shader; + unsigned int i, j; + + if ((decl = state->vertex_declaration)) + { + state->vertex_declaration = NULL; + wined3d_vertex_declaration_decref(decl); + } + + for (i = 0; i < WINED3D_MAX_COMBINED_SAMPLERS; ++i) + { + if ((texture = state->textures[i])) + { + state->textures[i] = NULL; + wined3d_texture_decref(texture); + } + } + + for (i = 0; i < WINED3D_MAX_STREAM_OUTPUT_BUFFERS; ++i) + { + if ((buffer = state->stream_output[i].buffer)) + { + state->stream_output[i].buffer = NULL; + wined3d_buffer_decref(buffer); + } + } + + for (i = 0; i < WINED3D_MAX_STREAMS; ++i) + { + if ((buffer = state->streams[i].buffer)) + { + state->streams[i].buffer = NULL; + wined3d_buffer_decref(buffer); + } + } + + if ((buffer = state->index_buffer)) + { + state->index_buffer = NULL; + wined3d_buffer_decref(buffer); + } + + for (i = 0; i < WINED3D_SHADER_TYPE_COUNT; ++i) + { + if ((shader = state->shader[i])) + { + state->shader[i] = NULL; + wined3d_shader_decref(shader); + } + + for (j = 0; j < MAX_CONSTANT_BUFFERS; ++j) + { + if ((buffer = state->cb[i][j])) + { + state->cb[i][j] = NULL; + wined3d_buffer_decref(buffer); + } + } + + for (j = 0; j < MAX_SAMPLER_OBJECTS; ++j) + { + if ((sampler = state->sampler[i][j])) + { + state->sampler[i][j] = NULL; + wined3d_sampler_decref(sampler); + } + } + + for (j = 0; j < MAX_SHADER_RESOURCE_VIEWS; ++j) + { + if ((srv = state->shader_resource_view[i][j])) + { + state->shader_resource_view[i][j] = NULL; + wined3d_srv_bind_count_dec(srv); + wined3d_shader_resource_view_decref(srv); + } + } + } + + for (i = 0; i < WINED3D_PIPELINE_COUNT; ++i) + { + for (j = 0; j < MAX_UNORDERED_ACCESS_VIEWS; ++j) + { + if ((uav = state->unordered_access_view[i][j])) + { + state->unordered_access_view[i][j] = NULL; + wined3d_unordered_access_view_decref(uav); + } + } + } + + if ((blend_state = state->blend_state)) + { + state->blend_state = NULL; + wined3d_blend_state_decref(blend_state); + } +} + +void wined3d_stateblock_state_cleanup(struct wined3d_stateblock_state *state) +{ + struct wined3d_light_info *light, *cursor; + struct wined3d_vertex_declaration *decl; + struct wined3d_texture *texture; + struct wined3d_buffer *buffer; + struct wined3d_shader *shader; + unsigned int i; + + if ((decl = state->vertex_declaration)) + { + state->vertex_declaration = NULL; + wined3d_vertex_declaration_decref(decl); + } + + for (i = 0; i < WINED3D_MAX_STREAMS; ++i) + { + if ((buffer = state->streams[i].buffer)) + { + state->streams[i].buffer = NULL; + wined3d_buffer_decref(buffer); + } + } + + if ((buffer = state->index_buffer)) + { + state->index_buffer = NULL; + wined3d_buffer_decref(buffer); + } + + if ((shader = state->vs)) + { + state->vs = NULL; + wined3d_shader_decref(shader); + } + + if ((shader = state->ps)) + { + state->ps = NULL; + wined3d_shader_decref(shader); + } + + for (i = 0; i < WINED3D_MAX_COMBINED_SAMPLERS; ++i) + { + if ((texture = state->textures[i])) + { + state->textures[i] = NULL; + wined3d_texture_decref(texture); + } + } + + for (i = 0; i < LIGHTMAP_SIZE; ++i) + { + LIST_FOR_EACH_ENTRY_SAFE(light, cursor, &state->light_state->light_map[i], struct wined3d_light_info, entry) + { + list_remove(&light->entry); + heap_free(light); + } + } +} + +void state_cleanup(struct wined3d_state *state) +{ + unsigned int counter; + + if (!(state->flags & WINED3D_STATE_NO_REF)) + state_unbind_resources(state); + + for (counter = 0; counter < WINED3D_MAX_ACTIVE_LIGHTS; ++counter) + { + state->light_state.lights[counter] = NULL; + } + + for (counter = 0; counter < LIGHTMAP_SIZE; ++counter) + { + struct list *e1, *e2; + LIST_FOR_EACH_SAFE(e1, e2, &state->light_state.light_map[counter]) + { + struct wined3d_light_info *light = LIST_ENTRY(e1, struct wined3d_light_info, entry); + list_remove(&light->entry); + heap_free(light); + } + } +} + +ULONG CDECL wined3d_stateblock_decref(struct wined3d_stateblock *stateblock) +{ + ULONG refcount = InterlockedDecrement(&stateblock->ref); + + TRACE("%p decreasing refcount to %u\n", stateblock, refcount); + + if (!refcount) + { + wined3d_stateblock_state_cleanup(&stateblock->stateblock_state); + heap_free(stateblock); + } + + return refcount; +} + +struct wined3d_light_info *wined3d_light_state_get_light(const struct wined3d_light_state *state, unsigned int idx) +{ + struct wined3d_light_info *light_info; + unsigned int hash_idx; + + hash_idx = LIGHTMAP_HASHFUNC(idx); + LIST_FOR_EACH_ENTRY(light_info, &state->light_map[hash_idx], struct wined3d_light_info, entry) + { + if (light_info->OriginalIndex == idx) + return light_info; + } + + return NULL; +} + +HRESULT wined3d_light_state_set_light(struct wined3d_light_state *state, DWORD light_idx, + const struct wined3d_light *params, struct wined3d_light_info **light_info) +{ + struct wined3d_light_info *object; + unsigned int hash_idx; + + if (!(object = wined3d_light_state_get_light(state, light_idx))) + { + TRACE("Adding new light.\n"); + if (!(object = heap_alloc_zero(sizeof(*object)))) + { + ERR("Failed to allocate light info.\n"); + return E_OUTOFMEMORY; + } + + hash_idx = LIGHTMAP_HASHFUNC(light_idx); + list_add_head(&state->light_map[hash_idx], &object->entry); + object->glIndex = -1; + object->OriginalIndex = light_idx; + } + + object->OriginalParms = *params; + + *light_info = object; + return WINED3D_OK; +} + +void wined3d_light_state_enable_light(struct wined3d_light_state *state, const struct wined3d_d3d_info *d3d_info, + struct wined3d_light_info *light_info, BOOL enable) +{ + unsigned int light_count, i; + + if (!(light_info->enabled = enable)) + { + if (light_info->glIndex == -1) + { + TRACE("Light already disabled, nothing to do.\n"); + return; + } + + state->lights[light_info->glIndex] = NULL; + light_info->glIndex = -1; + return; + } + + if (light_info->glIndex != -1) + { + TRACE("Light already enabled, nothing to do.\n"); + return; + } + + /* Find a free light. */ + light_count = d3d_info->limits.active_light_count; + for (i = 0; i < light_count; ++i) + { + if (state->lights[i]) + continue; + + state->lights[i] = light_info; + light_info->glIndex = i; + return; + } + + /* Our tests show that Windows returns D3D_OK in this situation, even with + * D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_PUREDEVICE devices. + * This is consistent among ddraw, d3d8 and d3d9. GetLightEnable returns + * TRUE * as well for those lights. + * + * TODO: Test how this affects rendering. */ + WARN("Too many concurrently active lights.\n"); +} + +static void wined3d_state_record_lights(struct wined3d_light_state *dst_state, + const struct wined3d_light_state *src_state) +{ + const struct wined3d_light_info *src; + struct wined3d_light_info *dst; + UINT i; + + /* Lights... For a recorded state block, we just had a chain of actions + * to perform, so we need to walk that chain and update any actions which + * differ. */ + for (i = 0; i < LIGHTMAP_SIZE; ++i) + { + LIST_FOR_EACH_ENTRY(dst, &dst_state->light_map[i], struct wined3d_light_info, entry) + { + if ((src = wined3d_light_state_get_light(src_state, dst->OriginalIndex))) + { + dst->OriginalParms = src->OriginalParms; + + if (src->glIndex == -1 && dst->glIndex != -1) + { + /* Light disabled. */ + dst_state->lights[dst->glIndex] = NULL; + } + else if (src->glIndex != -1 && dst->glIndex == -1) + { + /* Light enabled. */ + dst_state->lights[src->glIndex] = dst; + } + dst->glIndex = src->glIndex; + } + else + { + /* This can happen if the light was originally created as a + * default light for SetLightEnable() while recording. */ + WARN("Light %u in dst_state %p does not exist in src_state %p.\n", + dst->OriginalIndex, dst_state, src_state); + + dst->OriginalParms = WINED3D_default_light; + if (dst->glIndex != -1) + { + dst_state->lights[dst->glIndex] = NULL; + dst->glIndex = -1; + } + } + } + } +} + +void CDECL wined3d_stateblock_capture(struct wined3d_stateblock *stateblock, + const struct wined3d_stateblock *device_state) +{ + const struct wined3d_stateblock_state *state = &device_state->stateblock_state; + struct wined3d_range range; + unsigned int i, start; + DWORD map; + + TRACE("stateblock %p, device_state %p.\n", stateblock, device_state); + + if (stateblock->changed.vertexShader && stateblock->stateblock_state.vs != state->vs) + { + TRACE("Updating vertex shader from %p to %p.\n", stateblock->stateblock_state.vs, state->vs); + + if (state->vs) + wined3d_shader_incref(state->vs); + if (stateblock->stateblock_state.vs) + wined3d_shader_decref(stateblock->stateblock_state.vs); + stateblock->stateblock_state.vs = state->vs; + } + + for (start = 0; ; start = range.offset + range.size) + { + if (!wined3d_bitmap_get_range(stateblock->changed.vs_consts_f, WINED3D_MAX_VS_CONSTS_F, start, &range)) + break; + + memcpy(&stateblock->stateblock_state.vs_consts_f[range.offset], &state->vs_consts_f[range.offset], + sizeof(*state->vs_consts_f) * range.size); + } + map = stateblock->changed.vertexShaderConstantsI; + for (start = 0; ; start = range.offset + range.size) + { + if (!wined3d_bitmap_get_range(&map, WINED3D_MAX_CONSTS_I, start, &range)) + break; + + memcpy(&stateblock->stateblock_state.vs_consts_i[range.offset], &state->vs_consts_i[range.offset], + sizeof(*state->vs_consts_i) * range.size); + } + map = stateblock->changed.vertexShaderConstantsB; + for (start = 0; ; start = range.offset + range.size) + { + if (!wined3d_bitmap_get_range(&map, WINED3D_MAX_CONSTS_B, start, &range)) + break; + + memcpy(&stateblock->stateblock_state.vs_consts_b[range.offset], &state->vs_consts_b[range.offset], + sizeof(*state->vs_consts_b) * range.size); + } + + for (start = 0; ; start = range.offset + range.size) + { + if (!wined3d_bitmap_get_range(stateblock->changed.ps_consts_f, WINED3D_MAX_PS_CONSTS_F, start, &range)) + break; + + memcpy(&stateblock->stateblock_state.ps_consts_f[range.offset], &state->ps_consts_f[range.offset], + sizeof(*state->ps_consts_f) * range.size); + } + map = stateblock->changed.pixelShaderConstantsI; + for (start = 0; ; start = range.offset + range.size) + { + if (!wined3d_bitmap_get_range(&map, WINED3D_MAX_CONSTS_I, start, &range)) + break; + + memcpy(&stateblock->stateblock_state.ps_consts_i[range.offset], &state->ps_consts_i[range.offset], + sizeof(*state->ps_consts_i) * range.size); + } + map = stateblock->changed.pixelShaderConstantsB; + for (start = 0; ; start = range.offset + range.size) + { + if (!wined3d_bitmap_get_range(&map, WINED3D_MAX_CONSTS_B, start, &range)) + break; + + memcpy(&stateblock->stateblock_state.ps_consts_b[range.offset], &state->ps_consts_b[range.offset], + sizeof(*state->ps_consts_b) * range.size); + } + + if (stateblock->changed.transforms) + { + for (i = 0; i < stateblock->num_contained_transform_states; ++i) + { + enum wined3d_transform_state transform = stateblock->contained_transform_states[i]; + + TRACE("Updating transform %#x.\n", transform); + + stateblock->stateblock_state.transforms[transform] = state->transforms[transform]; + } + } + + if (stateblock->changed.indices + && ((stateblock->stateblock_state.index_buffer != state->index_buffer) + || (stateblock->stateblock_state.base_vertex_index != state->base_vertex_index) + || (stateblock->stateblock_state.index_format != state->index_format))) + { + TRACE("Updating index buffer to %p, base vertex index to %d.\n", + state->index_buffer, state->base_vertex_index); + + if (state->index_buffer) + wined3d_buffer_incref(state->index_buffer); + if (stateblock->stateblock_state.index_buffer) + wined3d_buffer_decref(stateblock->stateblock_state.index_buffer); + stateblock->stateblock_state.index_buffer = state->index_buffer; + stateblock->stateblock_state.base_vertex_index = state->base_vertex_index; + stateblock->stateblock_state.index_format = state->index_format; + } + + if (stateblock->changed.vertexDecl && stateblock->stateblock_state.vertex_declaration != state->vertex_declaration) + { + TRACE("Updating vertex declaration from %p to %p.\n", + stateblock->stateblock_state.vertex_declaration, state->vertex_declaration); + + if (state->vertex_declaration) + wined3d_vertex_declaration_incref(state->vertex_declaration); + if (stateblock->stateblock_state.vertex_declaration) + wined3d_vertex_declaration_decref(stateblock->stateblock_state.vertex_declaration); + stateblock->stateblock_state.vertex_declaration = state->vertex_declaration; + } + + if (stateblock->changed.material + && memcmp(&state->material, &stateblock->stateblock_state.material, + sizeof(stateblock->stateblock_state.material))) + { + TRACE("Updating material.\n"); + + stateblock->stateblock_state.material = state->material; + } + + if (stateblock->changed.viewport + && memcmp(&state->viewport, &stateblock->stateblock_state.viewport, sizeof(state->viewport))) + { + TRACE("Updating viewport.\n"); + + stateblock->stateblock_state.viewport = state->viewport; + } + + if (stateblock->changed.scissorRect + && memcmp(&state->scissor_rect, &stateblock->stateblock_state.scissor_rect, sizeof(state->scissor_rect))) + { + TRACE("Updating scissor rect.\n"); + + stateblock->stateblock_state.scissor_rect = state->scissor_rect; + } + + map = stateblock->changed.streamSource; + while (map) + { + i = wined3d_bit_scan(&map); + + if (stateblock->stateblock_state.streams[i].stride != state->streams[i].stride + || stateblock->stateblock_state.streams[i].offset != state->streams[i].offset + || stateblock->stateblock_state.streams[i].buffer != state->streams[i].buffer) + { + TRACE("stateblock %p, stream source %u, buffer %p, stride %u, offset %u.\n", + stateblock, i, state->streams[i].buffer, state->streams[i].stride, + state->streams[i].offset); + + stateblock->stateblock_state.streams[i].stride = state->streams[i].stride; + if (stateblock->changed.store_stream_offset) + stateblock->stateblock_state.streams[i].offset = state->streams[i].offset; + + if (state->streams[i].buffer) + wined3d_buffer_incref(state->streams[i].buffer); + if (stateblock->stateblock_state.streams[i].buffer) + wined3d_buffer_decref(stateblock->stateblock_state.streams[i].buffer); + stateblock->stateblock_state.streams[i].buffer = state->streams[i].buffer; + } + } + + map = stateblock->changed.streamFreq; + while (map) + { + i = wined3d_bit_scan(&map); + + if (stateblock->stateblock_state.streams[i].frequency != state->streams[i].frequency + || stateblock->stateblock_state.streams[i].flags != state->streams[i].flags) + { + TRACE("Updating stream frequency %u to %u flags to %#x.\n", + i, state->streams[i].frequency, state->streams[i].flags); + + stateblock->stateblock_state.streams[i].frequency = state->streams[i].frequency; + stateblock->stateblock_state.streams[i].flags = state->streams[i].flags; + } + } + + map = stateblock->changed.clipplane; + while (map) + { + i = wined3d_bit_scan(&map); + + if (memcmp(&stateblock->stateblock_state.clip_planes[i], &state->clip_planes[i], sizeof(state->clip_planes[i]))) + { + TRACE("Updating clipplane %u.\n", i); + stateblock->stateblock_state.clip_planes[i] = state->clip_planes[i]; + } + } + + /* Render */ + for (i = 0; i < stateblock->num_contained_render_states; ++i) + { + enum wined3d_render_state rs = stateblock->contained_render_states[i]; + + TRACE("Updating render state %#x to %u.\n", rs, state->rs[rs]); + + stateblock->stateblock_state.rs[rs] = state->rs[rs]; + } + + /* Texture states */ + for (i = 0; i < stateblock->num_contained_tss_states; ++i) + { + DWORD stage = stateblock->contained_tss_states[i].stage; + DWORD texture_state = stateblock->contained_tss_states[i].state; + + TRACE("Updating texturestage state %u, %u to %#x (was %#x).\n", stage, texture_state, + state->texture_states[stage][texture_state], + stateblock->stateblock_state.texture_states[stage][texture_state]); + + stateblock->stateblock_state.texture_states[stage][texture_state] = state->texture_states[stage][texture_state]; + } + + /* Samplers */ + map = stateblock->changed.textures; + while (map) + { + i = wined3d_bit_scan(&map); + + TRACE("Updating texture %u to %p (was %p).\n", + i, state->textures[i], stateblock->stateblock_state.textures[i]); + + if (state->textures[i]) + wined3d_texture_incref(state->textures[i]); + if (stateblock->stateblock_state.textures[i]) + wined3d_texture_decref(stateblock->stateblock_state.textures[i]); + stateblock->stateblock_state.textures[i] = state->textures[i]; + } + + for (i = 0; i < stateblock->num_contained_sampler_states; ++i) + { + DWORD stage = stateblock->contained_sampler_states[i].stage; + DWORD sampler_state = stateblock->contained_sampler_states[i].state; + + TRACE("Updating sampler state %u, %u to %#x (was %#x).\n", stage, sampler_state, + state->sampler_states[stage][sampler_state], + stateblock->stateblock_state.sampler_states[stage][sampler_state]); + + stateblock->stateblock_state.sampler_states[stage][sampler_state] = state->sampler_states[stage][sampler_state]; + } + + if (stateblock->changed.pixelShader && stateblock->stateblock_state.ps != state->ps) + { + if (state->ps) + wined3d_shader_incref(state->ps); + if (stateblock->stateblock_state.ps) + wined3d_shader_decref(stateblock->stateblock_state.ps); + stateblock->stateblock_state.ps = state->ps; + } + + if (stateblock->changed.lights) + wined3d_state_record_lights(stateblock->stateblock_state.light_state, state->light_state); + + if (stateblock->changed.alpha_to_coverage) + stateblock->stateblock_state.alpha_to_coverage = state->alpha_to_coverage; + + TRACE("Capture done.\n"); +} + +void CDECL wined3d_stateblock_apply(const struct wined3d_stateblock *stateblock, + struct wined3d_stateblock *device_state) +{ + const struct wined3d_stateblock_state *state = &stateblock->stateblock_state; + struct wined3d_range range; + unsigned int i, start; + DWORD map; + + TRACE("stateblock %p, device_state %p.\n", stateblock, device_state); + + if (stateblock->changed.vertexShader) + wined3d_stateblock_set_vertex_shader(device_state, state->vs); + if (stateblock->changed.pixelShader) + wined3d_stateblock_set_pixel_shader(device_state, state->ps); + + for (start = 0; ; start = range.offset + range.size) + { + if (!wined3d_bitmap_get_range(stateblock->changed.vs_consts_f, WINED3D_MAX_VS_CONSTS_F, start, &range)) + break; + wined3d_stateblock_set_vs_consts_f(device_state, range.offset, range.size, &state->vs_consts_f[range.offset]); + } + map = stateblock->changed.vertexShaderConstantsI; + for (start = 0; ; start = range.offset + range.size) + { + if (!wined3d_bitmap_get_range(&map, WINED3D_MAX_CONSTS_I, start, &range)) + break; + wined3d_stateblock_set_vs_consts_i(device_state, range.offset, range.size, &state->vs_consts_i[range.offset]); + } + map = stateblock->changed.vertexShaderConstantsB; + for (start = 0; ; start = range.offset + range.size) + { + if (!wined3d_bitmap_get_range(&map, WINED3D_MAX_CONSTS_B, start, &range)) + break; + wined3d_stateblock_set_vs_consts_b(device_state, range.offset, range.size, &state->vs_consts_b[range.offset]); + } + + for (start = 0; ; start = range.offset + range.size) + { + if (!wined3d_bitmap_get_range(stateblock->changed.ps_consts_f, WINED3D_MAX_PS_CONSTS_F, start, &range)) + break; + wined3d_stateblock_set_ps_consts_f(device_state, range.offset, range.size, &state->ps_consts_f[range.offset]); + } + map = stateblock->changed.pixelShaderConstantsI; + for (start = 0; ; start = range.offset + range.size) + { + if (!wined3d_bitmap_get_range(&map, WINED3D_MAX_CONSTS_I, start, &range)) + break; + wined3d_stateblock_set_ps_consts_i(device_state, range.offset, range.size, &state->ps_consts_i[range.offset]); + } + map = stateblock->changed.pixelShaderConstantsB; + for (start = 0; ; start = range.offset + range.size) + { + if (!wined3d_bitmap_get_range(&map, WINED3D_MAX_CONSTS_B, start, &range)) + break; + wined3d_stateblock_set_ps_consts_b(device_state, range.offset, range.size, &state->ps_consts_b[range.offset]); + } + + if (stateblock->changed.transforms) + { + for (i = 0; i < stateblock->num_contained_transform_states; ++i) + { + enum wined3d_transform_state transform = stateblock->contained_transform_states[i]; + + wined3d_stateblock_set_transform(device_state, transform, &state->transforms[transform]); + } + } + + if (stateblock->changed.lights) + { + for (i = 0; i < ARRAY_SIZE(state->light_state->light_map); ++i) + { + const struct wined3d_light_info *light; + + LIST_FOR_EACH_ENTRY(light, &state->light_state->light_map[i], struct wined3d_light_info, entry) + { + wined3d_stateblock_set_light(device_state, light->OriginalIndex, &light->OriginalParms); + wined3d_stateblock_set_light_enable(device_state, light->OriginalIndex, light->glIndex != -1); + } + } + } + + if (stateblock->changed.alpha_to_coverage) + { + device_state->stateblock_state.alpha_to_coverage = state->alpha_to_coverage; + device_state->changed.alpha_to_coverage = 1; + } + + /* Render states. */ + for (i = 0; i < stateblock->num_contained_render_states; ++i) + { + enum wined3d_render_state rs = stateblock->contained_render_states[i]; + + wined3d_stateblock_set_render_state(device_state, rs, state->rs[rs]); + } + + /* Texture states. */ + for (i = 0; i < stateblock->num_contained_tss_states; ++i) + { + DWORD stage = stateblock->contained_tss_states[i].stage; + DWORD texture_state = stateblock->contained_tss_states[i].state; + + wined3d_stateblock_set_texture_stage_state(device_state, stage, texture_state, + state->texture_states[stage][texture_state]); + } + + /* Sampler states. */ + for (i = 0; i < stateblock->num_contained_sampler_states; ++i) + { + DWORD stage = stateblock->contained_sampler_states[i].stage; + DWORD sampler_state = stateblock->contained_sampler_states[i].state; + + wined3d_stateblock_set_sampler_state(device_state, stage, sampler_state, + state->sampler_states[stage][sampler_state]); + } + + if (stateblock->changed.indices) + { + wined3d_stateblock_set_index_buffer(device_state, state->index_buffer, state->index_format); + wined3d_stateblock_set_base_vertex_index(device_state, state->base_vertex_index); + } + + if (stateblock->changed.vertexDecl && state->vertex_declaration) + wined3d_stateblock_set_vertex_declaration(device_state, state->vertex_declaration); + + if (stateblock->changed.material) + wined3d_stateblock_set_material(device_state, &state->material); + + if (stateblock->changed.viewport) + wined3d_stateblock_set_viewport(device_state, &state->viewport); + + if (stateblock->changed.scissorRect) + wined3d_stateblock_set_scissor_rect(device_state, &state->scissor_rect); + + map = stateblock->changed.streamSource; + while (map) + { + i = wined3d_bit_scan(&map); + wined3d_stateblock_set_stream_source(device_state, i, state->streams[i].buffer, + state->streams[i].offset, state->streams[i].stride); + } + + map = stateblock->changed.streamFreq; + while (map) + { + i = wined3d_bit_scan(&map); + wined3d_stateblock_set_stream_source_freq(device_state, i, + state->streams[i].frequency | state->streams[i].flags); + } + + map = stateblock->changed.textures; + while (map) + { + i = wined3d_bit_scan(&map); + wined3d_stateblock_set_texture(device_state, i, state->textures[i]); + } + + map = stateblock->changed.clipplane; + while (map) + { + i = wined3d_bit_scan(&map); + wined3d_stateblock_set_clip_plane(device_state, i, &state->clip_planes[i]); + } + + TRACE("Applied stateblock %p.\n", stateblock); +} + +void CDECL wined3d_stateblock_set_vertex_shader(struct wined3d_stateblock *stateblock, struct wined3d_shader *shader) +{ + TRACE("stateblock %p, shader %p.\n", stateblock, shader); + + if (shader) + wined3d_shader_incref(shader); + if (stateblock->stateblock_state.vs) + wined3d_shader_decref(stateblock->stateblock_state.vs); + stateblock->stateblock_state.vs = shader; + stateblock->changed.vertexShader = TRUE; +} + +static void wined3d_bitmap_set_bits(uint32_t *bitmap, unsigned int start, unsigned int count) +{ + const unsigned int word_bit_count = sizeof(*bitmap) * CHAR_BIT; + const unsigned int shift = start % word_bit_count; + uint32_t mask, last_mask; + unsigned int mask_size; + + bitmap += start / word_bit_count; + mask = ~0u << shift; + mask_size = word_bit_count - shift; + last_mask = (1u << (start + count) % word_bit_count) - 1; + if (mask_size <= count) + { + *bitmap |= mask; + ++bitmap; + count -= mask_size; + mask = ~0u; + } + if (count >= word_bit_count) + { + memset(bitmap, 0xffu, count / word_bit_count * sizeof(*bitmap)); + bitmap += count / word_bit_count; + count = count % word_bit_count; + } + if (count) + *bitmap |= mask & last_mask; +} + +HRESULT CDECL wined3d_stateblock_set_vs_consts_f(struct wined3d_stateblock *stateblock, + unsigned int start_idx, unsigned int count, const struct wined3d_vec4 *constants) +{ + const struct wined3d_d3d_info *d3d_info = &stateblock->device->adapter->d3d_info; + + TRACE("stateblock %p, start_idx %u, count %u, constants %p.\n", + stateblock, start_idx, count, constants); + + if (!constants || start_idx >= d3d_info->limits.vs_uniform_count + || count > d3d_info->limits.vs_uniform_count - start_idx) + return WINED3DERR_INVALIDCALL; + + memcpy(&stateblock->stateblock_state.vs_consts_f[start_idx], constants, count * sizeof(*constants)); + wined3d_bitmap_set_bits(stateblock->changed.vs_consts_f, start_idx, count); + return WINED3D_OK; +} + +HRESULT CDECL wined3d_stateblock_set_vs_consts_i(struct wined3d_stateblock *stateblock, + unsigned int start_idx, unsigned int count, const struct wined3d_ivec4 *constants) +{ + unsigned int i; + + TRACE("stateblock %p, start_idx %u, count %u, constants %p.\n", + stateblock, start_idx, count, constants); + + if (!constants || start_idx >= WINED3D_MAX_CONSTS_I) + return WINED3DERR_INVALIDCALL; + + if (count > WINED3D_MAX_CONSTS_I - start_idx) + count = WINED3D_MAX_CONSTS_I - start_idx; + + memcpy(&stateblock->stateblock_state.vs_consts_i[start_idx], constants, count * sizeof(*constants)); + for (i = start_idx; i < count + start_idx; ++i) + stateblock->changed.vertexShaderConstantsI |= (1u << i); + return WINED3D_OK; +} + +HRESULT CDECL wined3d_stateblock_set_vs_consts_b(struct wined3d_stateblock *stateblock, + unsigned int start_idx, unsigned int count, const BOOL *constants) +{ + unsigned int i; + + TRACE("stateblock %p, start_idx %u, count %u, constants %p.\n", + stateblock, start_idx, count, constants); + + if (!constants || start_idx >= WINED3D_MAX_CONSTS_B) + return WINED3DERR_INVALIDCALL; + + if (count > WINED3D_MAX_CONSTS_B - start_idx) + count = WINED3D_MAX_CONSTS_B - start_idx; + + memcpy(&stateblock->stateblock_state.vs_consts_b[start_idx], constants, count * sizeof(*constants)); + for (i = start_idx; i < count + start_idx; ++i) + stateblock->changed.vertexShaderConstantsB |= (1u << i); + return WINED3D_OK; +} + +void CDECL wined3d_stateblock_set_pixel_shader(struct wined3d_stateblock *stateblock, struct wined3d_shader *shader) +{ + TRACE("stateblock %p, shader %p.\n", stateblock, shader); + + if (shader) + wined3d_shader_incref(shader); + if (stateblock->stateblock_state.ps) + wined3d_shader_decref(stateblock->stateblock_state.ps); + stateblock->stateblock_state.ps = shader; + stateblock->changed.pixelShader = TRUE; +} + +HRESULT CDECL wined3d_stateblock_set_ps_consts_f(struct wined3d_stateblock *stateblock, + unsigned int start_idx, unsigned int count, const struct wined3d_vec4 *constants) +{ + const struct wined3d_d3d_info *d3d_info = &stateblock->device->adapter->d3d_info; + + TRACE("stateblock %p, start_idx %u, count %u, constants %p.\n", + stateblock, start_idx, count, constants); + + if (!constants || start_idx >= d3d_info->limits.ps_uniform_count + || count > d3d_info->limits.ps_uniform_count - start_idx) + return WINED3DERR_INVALIDCALL; + + memcpy(&stateblock->stateblock_state.ps_consts_f[start_idx], constants, count * sizeof(*constants)); + wined3d_bitmap_set_bits(stateblock->changed.ps_consts_f, start_idx, count); + return WINED3D_OK; +} + +HRESULT CDECL wined3d_stateblock_set_ps_consts_i(struct wined3d_stateblock *stateblock, + unsigned int start_idx, unsigned int count, const struct wined3d_ivec4 *constants) +{ + unsigned int i; + + TRACE("stateblock %p, start_idx %u, count %u, constants %p.\n", + stateblock, start_idx, count, constants); + + if (!constants || start_idx >= WINED3D_MAX_CONSTS_I) + return WINED3DERR_INVALIDCALL; + + if (count > WINED3D_MAX_CONSTS_I - start_idx) + count = WINED3D_MAX_CONSTS_I - start_idx; + + memcpy(&stateblock->stateblock_state.ps_consts_i[start_idx], constants, count * sizeof(*constants)); + for (i = start_idx; i < count + start_idx; ++i) + stateblock->changed.pixelShaderConstantsI |= (1u << i); + return WINED3D_OK; +} + +HRESULT CDECL wined3d_stateblock_set_ps_consts_b(struct wined3d_stateblock *stateblock, + unsigned int start_idx, unsigned int count, const BOOL *constants) +{ + unsigned int i; + + TRACE("stateblock %p, start_idx %u, count %u, constants %p.\n", + stateblock, start_idx, count, constants); + + if (!constants || start_idx >= WINED3D_MAX_CONSTS_B) + return WINED3DERR_INVALIDCALL; + + if (count > WINED3D_MAX_CONSTS_B - start_idx) + count = WINED3D_MAX_CONSTS_B - start_idx; + + memcpy(&stateblock->stateblock_state.ps_consts_b[start_idx], constants, count * sizeof(*constants)); + for (i = start_idx; i < count + start_idx; ++i) + stateblock->changed.pixelShaderConstantsB |= (1u << i); + return WINED3D_OK; +} + +void CDECL wined3d_stateblock_set_vertex_declaration(struct wined3d_stateblock *stateblock, + struct wined3d_vertex_declaration *declaration) +{ + TRACE("stateblock %p, declaration %p.\n", stateblock, declaration); + + if (declaration) + wined3d_vertex_declaration_incref(declaration); + if (stateblock->stateblock_state.vertex_declaration) + wined3d_vertex_declaration_decref(stateblock->stateblock_state.vertex_declaration); + stateblock->stateblock_state.vertex_declaration = declaration; + stateblock->changed.vertexDecl = TRUE; +} + +void CDECL wined3d_stateblock_set_render_state(struct wined3d_stateblock *stateblock, + enum wined3d_render_state state, DWORD value) +{ + TRACE("stateblock %p, state %s (%#x), value %#x.\n", stateblock, debug_d3drenderstate(state), state, value); + + if (state > WINEHIGHEST_RENDER_STATE) + { + WARN("Unhandled render state %#x.\n", state); + return; + } + + stateblock->stateblock_state.rs[state] = value; + stateblock->changed.renderState[state >> 5] |= 1u << (state & 0x1f); + + if (state == WINED3D_RS_POINTSIZE + && (value == WINED3D_ALPHA_TO_COVERAGE_ENABLE || value == WINED3D_ALPHA_TO_COVERAGE_DISABLE)) + { + stateblock->changed.alpha_to_coverage = 1; + stateblock->stateblock_state.alpha_to_coverage = (value == WINED3D_ALPHA_TO_COVERAGE_ENABLE); + } +} + +void CDECL wined3d_stateblock_set_sampler_state(struct wined3d_stateblock *stateblock, + UINT sampler_idx, enum wined3d_sampler_state state, DWORD value) +{ + TRACE("stateblock %p, sampler_idx %u, state %s, value %#x.\n", + stateblock, sampler_idx, debug_d3dsamplerstate(state), value); + + if (sampler_idx >= ARRAY_SIZE(stateblock->stateblock_state.sampler_states)) + { + WARN("Invalid sampler %u.\n", sampler_idx); + return; + } + + stateblock->stateblock_state.sampler_states[sampler_idx][state] = value; + stateblock->changed.samplerState[sampler_idx] |= 1u << state; +} + +void CDECL wined3d_stateblock_set_texture_stage_state(struct wined3d_stateblock *stateblock, + UINT stage, enum wined3d_texture_stage_state state, DWORD value) +{ + TRACE("stateblock %p, stage %u, state %s, value %#x.\n", + stateblock, stage, debug_d3dtexturestate(state), value); + + if (state > WINED3D_HIGHEST_TEXTURE_STATE) + { + WARN("Invalid state %#x passed.\n", state); + return; + } + + if (stage >= WINED3D_MAX_TEXTURES) + { + WARN("Attempting to set stage %u which is higher than the max stage %u, ignoring.\n", + stage, WINED3D_MAX_TEXTURES - 1); + return; + } + + stateblock->stateblock_state.texture_states[stage][state] = value; + stateblock->changed.textureState[stage] |= 1u << state; +} + +void CDECL wined3d_stateblock_set_texture(struct wined3d_stateblock *stateblock, + UINT stage, struct wined3d_texture *texture) +{ + TRACE("stateblock %p, stage %u, texture %p.\n", stateblock, stage, texture); + + if (stage >= ARRAY_SIZE(stateblock->stateblock_state.textures)) + { + WARN("Ignoring invalid stage %u.\n", stage); + return; + } + + if (texture) + wined3d_texture_incref(texture); + if (stateblock->stateblock_state.textures[stage]) + wined3d_texture_decref(stateblock->stateblock_state.textures[stage]); + stateblock->stateblock_state.textures[stage] = texture; + stateblock->changed.textures |= 1u << stage; +} + +void CDECL wined3d_stateblock_set_transform(struct wined3d_stateblock *stateblock, + enum wined3d_transform_state d3dts, const struct wined3d_matrix *matrix) +{ + TRACE("stateblock %p, state %s, matrix %p.\n", stateblock, debug_d3dtstype(d3dts), matrix); + TRACE("%.8e %.8e %.8e %.8e\n", matrix->_11, matrix->_12, matrix->_13, matrix->_14); + TRACE("%.8e %.8e %.8e %.8e\n", matrix->_21, matrix->_22, matrix->_23, matrix->_24); + TRACE("%.8e %.8e %.8e %.8e\n", matrix->_31, matrix->_32, matrix->_33, matrix->_34); + TRACE("%.8e %.8e %.8e %.8e\n", matrix->_41, matrix->_42, matrix->_43, matrix->_44); + + stateblock->stateblock_state.transforms[d3dts] = *matrix; + stateblock->changed.transform[d3dts >> 5] |= 1u << (d3dts & 0x1f); + stateblock->changed.transforms = 1; +} + +void CDECL wined3d_stateblock_multiply_transform(struct wined3d_stateblock *stateblock, + enum wined3d_transform_state d3dts, const struct wined3d_matrix *matrix) +{ + struct wined3d_matrix *mat = &stateblock->stateblock_state.transforms[d3dts]; + + TRACE("stateblock %p, state %s, matrix %p.\n", stateblock, debug_d3dtstype(d3dts), matrix); + TRACE("%.8e %.8e %.8e %.8e\n", matrix->_11, matrix->_12, matrix->_13, matrix->_14); + TRACE("%.8e %.8e %.8e %.8e\n", matrix->_21, matrix->_22, matrix->_23, matrix->_24); + TRACE("%.8e %.8e %.8e %.8e\n", matrix->_31, matrix->_32, matrix->_33, matrix->_34); + TRACE("%.8e %.8e %.8e %.8e\n", matrix->_41, matrix->_42, matrix->_43, matrix->_44); + + multiply_matrix(mat, mat, matrix); + stateblock->changed.transform[d3dts >> 5] |= 1u << (d3dts & 0x1f); + stateblock->changed.transforms = 1; +} + +HRESULT CDECL wined3d_stateblock_set_clip_plane(struct wined3d_stateblock *stateblock, + UINT plane_idx, const struct wined3d_vec4 *plane) +{ + TRACE("stateblock %p, plane_idx %u, plane %p.\n", stateblock, plane_idx, plane); + + if (plane_idx >= stateblock->device->adapter->d3d_info.limits.max_clip_distances) + { + TRACE("Application has requested clipplane this device doesn't support.\n"); + return WINED3DERR_INVALIDCALL; + } + + stateblock->stateblock_state.clip_planes[plane_idx] = *plane; + stateblock->changed.clipplane |= 1u << plane_idx; + return S_OK; +} + +void CDECL wined3d_stateblock_set_material(struct wined3d_stateblock *stateblock, + const struct wined3d_material *material) +{ + TRACE("stateblock %p, material %p.\n", stateblock, material); + + stateblock->stateblock_state.material = *material; + stateblock->changed.material = TRUE; +} + +void CDECL wined3d_stateblock_set_viewport(struct wined3d_stateblock *stateblock, + const struct wined3d_viewport *viewport) +{ + TRACE("stateblock %p, viewport %p.\n", stateblock, viewport); + + stateblock->stateblock_state.viewport = *viewport; + stateblock->changed.viewport = TRUE; +} + +void CDECL wined3d_stateblock_set_scissor_rect(struct wined3d_stateblock *stateblock, const RECT *rect) +{ + TRACE("stateblock %p, rect %s.\n", stateblock, wine_dbgstr_rect(rect)); + + stateblock->stateblock_state.scissor_rect = *rect; + stateblock->changed.scissorRect = TRUE; +} + +void CDECL wined3d_stateblock_set_index_buffer(struct wined3d_stateblock *stateblock, + struct wined3d_buffer *buffer, enum wined3d_format_id format_id) +{ + TRACE("stateblock %p, buffer %p, format %s.\n", stateblock, buffer, debug_d3dformat(format_id)); + + if (buffer) + wined3d_buffer_incref(buffer); + if (stateblock->stateblock_state.index_buffer) + wined3d_buffer_decref(stateblock->stateblock_state.index_buffer); + stateblock->stateblock_state.index_buffer = buffer; + stateblock->stateblock_state.index_format = format_id; + stateblock->changed.indices = TRUE; +} + +void CDECL wined3d_stateblock_set_base_vertex_index(struct wined3d_stateblock *stateblock, INT base_index) +{ + TRACE("stateblock %p, base_index %d.\n", stateblock, base_index); + + stateblock->stateblock_state.base_vertex_index = base_index; +} + +HRESULT CDECL wined3d_stateblock_set_stream_source(struct wined3d_stateblock *stateblock, + UINT stream_idx, struct wined3d_buffer *buffer, UINT offset, UINT stride) +{ + struct wined3d_stream_state *stream; + + TRACE("stateblock %p, stream_idx %u, buffer %p, stride %u.\n", + stateblock, stream_idx, buffer, stride); + + if (stream_idx >= WINED3D_MAX_STREAMS) + { + WARN("Stream index %u out of range.\n", stream_idx); + return WINED3DERR_INVALIDCALL; + } + + stream = &stateblock->stateblock_state.streams[stream_idx]; + + if (buffer) + wined3d_buffer_incref(buffer); + if (stream->buffer) + wined3d_buffer_decref(stream->buffer); + stream->buffer = buffer; + stream->stride = stride; + stream->offset = offset; + stateblock->changed.streamSource |= 1u << stream_idx; + return WINED3D_OK; +} + +HRESULT CDECL wined3d_stateblock_set_stream_source_freq(struct wined3d_stateblock *stateblock, + UINT stream_idx, UINT divider) +{ + struct wined3d_stream_state *stream; + + TRACE("stateblock %p, stream_idx %u, divider %#x.\n", stateblock, stream_idx, divider); + + if ((divider & WINED3DSTREAMSOURCE_INSTANCEDATA) && (divider & WINED3DSTREAMSOURCE_INDEXEDDATA)) + { + WARN("INSTANCEDATA and INDEXEDDATA were set, returning D3DERR_INVALIDCALL.\n"); + return WINED3DERR_INVALIDCALL; + } + if ((divider & WINED3DSTREAMSOURCE_INSTANCEDATA) && !stream_idx) + { + WARN("INSTANCEDATA used on stream 0, returning D3DERR_INVALIDCALL.\n"); + return WINED3DERR_INVALIDCALL; + } + if (!divider) + { + WARN("Divider is 0, returning D3DERR_INVALIDCALL.\n"); + return WINED3DERR_INVALIDCALL; + } + + stream = &stateblock->stateblock_state.streams[stream_idx]; + stream->flags = divider & (WINED3DSTREAMSOURCE_INSTANCEDATA | WINED3DSTREAMSOURCE_INDEXEDDATA); + stream->frequency = divider & 0x7fffff; + stateblock->changed.streamFreq |= 1u << stream_idx; + return WINED3D_OK; +} + +HRESULT CDECL wined3d_stateblock_set_light(struct wined3d_stateblock *stateblock, + UINT light_idx, const struct wined3d_light *light) +{ + struct wined3d_light_info *object = NULL; + + TRACE("stateblock %p, light_idx %u, light %p.\n", stateblock, light_idx, light); + + /* Check the parameter range. Need for speed most wanted sets junk lights + * which confuse the GL driver. */ + if (!light) + return WINED3DERR_INVALIDCALL; + + switch (light->type) + { + case WINED3D_LIGHT_POINT: + case WINED3D_LIGHT_SPOT: + case WINED3D_LIGHT_GLSPOT: + /* Incorrect attenuation values can cause the gl driver to crash. + * Happens with Need for speed most wanted. */ + if (light->attenuation0 < 0.0f || light->attenuation1 < 0.0f || light->attenuation2 < 0.0f) + { + WARN("Attenuation is negative, returning WINED3DERR_INVALIDCALL.\n"); + return WINED3DERR_INVALIDCALL; + } + break; + + case WINED3D_LIGHT_DIRECTIONAL: + case WINED3D_LIGHT_PARALLELPOINT: + /* Ignores attenuation */ + break; + + default: + WARN("Light type out of range, returning WINED3DERR_INVALIDCALL.\n"); + return WINED3DERR_INVALIDCALL; + } + + stateblock->changed.lights = 1; + return wined3d_light_state_set_light(stateblock->stateblock_state.light_state, light_idx, light, &object); +} + +HRESULT CDECL wined3d_stateblock_set_light_enable(struct wined3d_stateblock *stateblock, UINT light_idx, BOOL enable) +{ + struct wined3d_light_state *light_state = stateblock->stateblock_state.light_state; + struct wined3d_light_info *light_info; + HRESULT hr; + + TRACE("stateblock %p, light_idx %u, enable %#x.\n", stateblock, light_idx, enable); + + if (!(light_info = wined3d_light_state_get_light(light_state, light_idx))) + { + if (FAILED(hr = wined3d_light_state_set_light(light_state, light_idx, &WINED3D_default_light, &light_info))) + return hr; + } + wined3d_light_state_enable_light(light_state, &stateblock->device->adapter->d3d_info, light_info, enable); + stateblock->changed.lights = 1; + return S_OK; +} + +const struct wined3d_stateblock_state * CDECL wined3d_stateblock_get_state(const struct wined3d_stateblock *stateblock) +{ + return &stateblock->stateblock_state; +} + +HRESULT CDECL wined3d_stateblock_get_light(const struct wined3d_stateblock *stateblock, + UINT light_idx, struct wined3d_light *light, BOOL *enabled) +{ + struct wined3d_light_info *light_info; + + if (!(light_info = wined3d_light_state_get_light(&stateblock->light_state, light_idx))) + { + TRACE("Light %u is not defined.\n", light_idx); + return WINED3DERR_INVALIDCALL; + } + *light = light_info->OriginalParms; + *enabled = light_info->enabled ? 128 : 0; + return WINED3D_OK; +} + +static void init_default_render_states(DWORD rs[WINEHIGHEST_RENDER_STATE + 1], const struct wined3d_d3d_info *d3d_info) +{ + union + { + struct wined3d_line_pattern lp; + DWORD d; + } lp; + union + { + float f; + DWORD d; + } tmpfloat; + + rs[WINED3D_RS_ZENABLE] = WINED3D_ZB_TRUE; + rs[WINED3D_RS_FILLMODE] = WINED3D_FILL_SOLID; + rs[WINED3D_RS_SHADEMODE] = WINED3D_SHADE_GOURAUD; + lp.lp.repeat_factor = 0; + lp.lp.line_pattern = 0; + rs[WINED3D_RS_LINEPATTERN] = lp.d; + rs[WINED3D_RS_ZWRITEENABLE] = TRUE; + rs[WINED3D_RS_ALPHATESTENABLE] = FALSE; + rs[WINED3D_RS_LASTPIXEL] = TRUE; + rs[WINED3D_RS_SRCBLEND] = WINED3D_BLEND_ONE; + rs[WINED3D_RS_DESTBLEND] = WINED3D_BLEND_ZERO; + rs[WINED3D_RS_CULLMODE] = WINED3D_CULL_BACK; + rs[WINED3D_RS_ZFUNC] = WINED3D_CMP_LESSEQUAL; + rs[WINED3D_RS_ALPHAFUNC] = WINED3D_CMP_ALWAYS; + rs[WINED3D_RS_ALPHAREF] = 0; + rs[WINED3D_RS_DITHERENABLE] = FALSE; + rs[WINED3D_RS_ALPHABLENDENABLE] = FALSE; + rs[WINED3D_RS_FOGENABLE] = FALSE; + rs[WINED3D_RS_SPECULARENABLE] = FALSE; + rs[WINED3D_RS_ZVISIBLE] = 0; + rs[WINED3D_RS_FOGCOLOR] = 0; + rs[WINED3D_RS_FOGTABLEMODE] = WINED3D_FOG_NONE; + tmpfloat.f = 0.0f; + rs[WINED3D_RS_FOGSTART] = tmpfloat.d; + tmpfloat.f = 1.0f; + rs[WINED3D_RS_FOGEND] = tmpfloat.d; + tmpfloat.f = 1.0f; + rs[WINED3D_RS_FOGDENSITY] = tmpfloat.d; + rs[WINED3D_RS_RANGEFOGENABLE] = FALSE; + rs[WINED3D_RS_STENCILENABLE] = FALSE; + rs[WINED3D_RS_STENCILFAIL] = WINED3D_STENCIL_OP_KEEP; + rs[WINED3D_RS_STENCILZFAIL] = WINED3D_STENCIL_OP_KEEP; + rs[WINED3D_RS_STENCILPASS] = WINED3D_STENCIL_OP_KEEP; + rs[WINED3D_RS_STENCILREF] = 0; + rs[WINED3D_RS_STENCILMASK] = 0xffffffff; + rs[WINED3D_RS_STENCILFUNC] = WINED3D_CMP_ALWAYS; + rs[WINED3D_RS_STENCILWRITEMASK] = 0xffffffff; + rs[WINED3D_RS_TEXTUREFACTOR] = 0xffffffff; + rs[WINED3D_RS_WRAP0] = 0; + rs[WINED3D_RS_WRAP1] = 0; + rs[WINED3D_RS_WRAP2] = 0; + rs[WINED3D_RS_WRAP3] = 0; + rs[WINED3D_RS_WRAP4] = 0; + rs[WINED3D_RS_WRAP5] = 0; + rs[WINED3D_RS_WRAP6] = 0; + rs[WINED3D_RS_WRAP7] = 0; + rs[WINED3D_RS_CLIPPING] = TRUE; + rs[WINED3D_RS_LIGHTING] = TRUE; + rs[WINED3D_RS_AMBIENT] = 0; + rs[WINED3D_RS_FOGVERTEXMODE] = WINED3D_FOG_NONE; + rs[WINED3D_RS_COLORVERTEX] = TRUE; + rs[WINED3D_RS_LOCALVIEWER] = TRUE; + rs[WINED3D_RS_NORMALIZENORMALS] = FALSE; + rs[WINED3D_RS_DIFFUSEMATERIALSOURCE] = WINED3D_MCS_COLOR1; + rs[WINED3D_RS_SPECULARMATERIALSOURCE] = WINED3D_MCS_COLOR2; + rs[WINED3D_RS_AMBIENTMATERIALSOURCE] = WINED3D_MCS_MATERIAL; + rs[WINED3D_RS_EMISSIVEMATERIALSOURCE] = WINED3D_MCS_MATERIAL; + rs[WINED3D_RS_VERTEXBLEND] = WINED3D_VBF_DISABLE; + rs[WINED3D_RS_CLIPPLANEENABLE] = 0; + rs[WINED3D_RS_SOFTWAREVERTEXPROCESSING] = FALSE; + tmpfloat.f = 1.0f; + rs[WINED3D_RS_POINTSIZE] = tmpfloat.d; + tmpfloat.f = 1.0f; + rs[WINED3D_RS_POINTSIZE_MIN] = tmpfloat.d; + rs[WINED3D_RS_POINTSPRITEENABLE] = FALSE; + rs[WINED3D_RS_POINTSCALEENABLE] = FALSE; + tmpfloat.f = 1.0f; + rs[WINED3D_RS_POINTSCALE_A] = tmpfloat.d; + tmpfloat.f = 0.0f; + rs[WINED3D_RS_POINTSCALE_B] = tmpfloat.d; + tmpfloat.f = 0.0f; + rs[WINED3D_RS_POINTSCALE_C] = tmpfloat.d; + rs[WINED3D_RS_MULTISAMPLEANTIALIAS] = TRUE; + rs[WINED3D_RS_MULTISAMPLEMASK] = 0xffffffff; + rs[WINED3D_RS_PATCHEDGESTYLE] = WINED3D_PATCH_EDGE_DISCRETE; + tmpfloat.f = 1.0f; + rs[WINED3D_RS_PATCHSEGMENTS] = tmpfloat.d; + rs[WINED3D_RS_DEBUGMONITORTOKEN] = 0xbaadcafe; + tmpfloat.f = d3d_info->limits.pointsize_max; + rs[WINED3D_RS_POINTSIZE_MAX] = tmpfloat.d; + rs[WINED3D_RS_INDEXEDVERTEXBLENDENABLE] = FALSE; + rs[WINED3D_RS_COLORWRITEENABLE] = 0x0000000f; + tmpfloat.f = 0.0f; + rs[WINED3D_RS_TWEENFACTOR] = tmpfloat.d; + rs[WINED3D_RS_BLENDOP] = WINED3D_BLEND_OP_ADD; + rs[WINED3D_RS_POSITIONDEGREE] = WINED3D_DEGREE_CUBIC; + rs[WINED3D_RS_NORMALDEGREE] = WINED3D_DEGREE_LINEAR; + /* states new in d3d9 */ + rs[WINED3D_RS_SCISSORTESTENABLE] = FALSE; + rs[WINED3D_RS_SLOPESCALEDEPTHBIAS] = 0; + tmpfloat.f = 1.0f; + rs[WINED3D_RS_MINTESSELLATIONLEVEL] = tmpfloat.d; + rs[WINED3D_RS_MAXTESSELLATIONLEVEL] = tmpfloat.d; + rs[WINED3D_RS_ANTIALIASEDLINEENABLE] = FALSE; + tmpfloat.f = 0.0f; + rs[WINED3D_RS_ADAPTIVETESS_X] = tmpfloat.d; + rs[WINED3D_RS_ADAPTIVETESS_Y] = tmpfloat.d; + tmpfloat.f = 1.0f; + rs[WINED3D_RS_ADAPTIVETESS_Z] = tmpfloat.d; + tmpfloat.f = 0.0f; + rs[WINED3D_RS_ADAPTIVETESS_W] = tmpfloat.d; + rs[WINED3D_RS_ENABLEADAPTIVETESSELLATION] = FALSE; + rs[WINED3D_RS_TWOSIDEDSTENCILMODE] = FALSE; + rs[WINED3D_RS_BACK_STENCILFAIL] = WINED3D_STENCIL_OP_KEEP; + rs[WINED3D_RS_BACK_STENCILZFAIL] = WINED3D_STENCIL_OP_KEEP; + rs[WINED3D_RS_BACK_STENCILPASS] = WINED3D_STENCIL_OP_KEEP; + rs[WINED3D_RS_BACK_STENCILFUNC] = WINED3D_CMP_ALWAYS; + rs[WINED3D_RS_COLORWRITEENABLE1] = 0x0000000f; + rs[WINED3D_RS_COLORWRITEENABLE2] = 0x0000000f; + rs[WINED3D_RS_COLORWRITEENABLE3] = 0x0000000f; + rs[WINED3D_RS_BLENDFACTOR] = 0xffffffff; + rs[WINED3D_RS_SRGBWRITEENABLE] = 0; + rs[WINED3D_RS_DEPTHBIAS] = 0; + rs[WINED3D_RS_WRAP8] = 0; + rs[WINED3D_RS_WRAP9] = 0; + rs[WINED3D_RS_WRAP10] = 0; + rs[WINED3D_RS_WRAP11] = 0; + rs[WINED3D_RS_WRAP12] = 0; + rs[WINED3D_RS_WRAP13] = 0; + rs[WINED3D_RS_WRAP14] = 0; + rs[WINED3D_RS_WRAP15] = 0; + rs[WINED3D_RS_SEPARATEALPHABLENDENABLE] = FALSE; + rs[WINED3D_RS_SRCBLENDALPHA] = WINED3D_BLEND_ONE; + rs[WINED3D_RS_DESTBLENDALPHA] = WINED3D_BLEND_ZERO; + rs[WINED3D_RS_BLENDOPALPHA] = WINED3D_BLEND_OP_ADD; +} + +static void init_default_texture_state(unsigned int i, DWORD stage[WINED3D_HIGHEST_TEXTURE_STATE + 1]) +{ + stage[WINED3D_TSS_COLOR_OP] = i ? WINED3D_TOP_DISABLE : WINED3D_TOP_MODULATE; + stage[WINED3D_TSS_COLOR_ARG1] = WINED3DTA_TEXTURE; + stage[WINED3D_TSS_COLOR_ARG2] = WINED3DTA_CURRENT; + stage[WINED3D_TSS_ALPHA_OP] = i ? WINED3D_TOP_DISABLE : WINED3D_TOP_SELECT_ARG1; + stage[WINED3D_TSS_ALPHA_ARG1] = WINED3DTA_TEXTURE; + stage[WINED3D_TSS_ALPHA_ARG2] = WINED3DTA_CURRENT; + stage[WINED3D_TSS_BUMPENV_MAT00] = 0; + stage[WINED3D_TSS_BUMPENV_MAT01] = 0; + stage[WINED3D_TSS_BUMPENV_MAT10] = 0; + stage[WINED3D_TSS_BUMPENV_MAT11] = 0; + stage[WINED3D_TSS_TEXCOORD_INDEX] = i; + stage[WINED3D_TSS_BUMPENV_LSCALE] = 0; + stage[WINED3D_TSS_BUMPENV_LOFFSET] = 0; + stage[WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS] = WINED3D_TTFF_DISABLE; + stage[WINED3D_TSS_COLOR_ARG0] = WINED3DTA_CURRENT; + stage[WINED3D_TSS_ALPHA_ARG0] = WINED3DTA_CURRENT; + stage[WINED3D_TSS_RESULT_ARG] = WINED3DTA_CURRENT; +} + +static void init_default_sampler_states(DWORD states[WINED3D_MAX_COMBINED_SAMPLERS][WINED3D_HIGHEST_SAMPLER_STATE + 1]) +{ + unsigned int i; + + for (i = 0 ; i < WINED3D_MAX_COMBINED_SAMPLERS; ++i) + { + TRACE("Setting up default samplers states for sampler %u.\n", i); + states[i][WINED3D_SAMP_ADDRESS_U] = WINED3D_TADDRESS_WRAP; + states[i][WINED3D_SAMP_ADDRESS_V] = WINED3D_TADDRESS_WRAP; + states[i][WINED3D_SAMP_ADDRESS_W] = WINED3D_TADDRESS_WRAP; + states[i][WINED3D_SAMP_BORDER_COLOR] = 0; + states[i][WINED3D_SAMP_MAG_FILTER] = WINED3D_TEXF_POINT; + states[i][WINED3D_SAMP_MIN_FILTER] = WINED3D_TEXF_POINT; + states[i][WINED3D_SAMP_MIP_FILTER] = WINED3D_TEXF_NONE; + states[i][WINED3D_SAMP_MIPMAP_LOD_BIAS] = 0; + states[i][WINED3D_SAMP_MAX_MIP_LEVEL] = 0; + states[i][WINED3D_SAMP_MAX_ANISOTROPY] = 1; + states[i][WINED3D_SAMP_SRGB_TEXTURE] = 0; + /* TODO: Indicates which element of a multielement texture to use. */ + states[i][WINED3D_SAMP_ELEMENT_INDEX] = 0; + /* TODO: Vertex offset in the presampled displacement map. */ + states[i][WINED3D_SAMP_DMAP_OFFSET] = 0; + } +} + +static void state_init_default(struct wined3d_state *state, const struct wined3d_d3d_info *d3d_info) +{ + unsigned int i; + struct wined3d_matrix identity; + + TRACE("state %p, d3d_info %p.\n", state, d3d_info); + + get_identity_matrix(&identity); + state->primitive_type = WINED3D_PT_UNDEFINED; + state->patch_vertex_count = 0; + + /* Set some of the defaults for lights, transforms etc */ + state->transforms[WINED3D_TS_PROJECTION] = identity; + state->transforms[WINED3D_TS_VIEW] = identity; + for (i = 0; i < 256; ++i) + { + state->transforms[WINED3D_TS_WORLD_MATRIX(i)] = identity; + } + + init_default_render_states(state->render_states, d3d_info); + + /* Texture Stage States - Put directly into state block, we will call function below */ + for (i = 0; i < WINED3D_MAX_TEXTURES; ++i) + { + TRACE("Setting up default texture states for texture Stage %u.\n", i); + state->transforms[WINED3D_TS_TEXTURE0 + i] = identity; + init_default_texture_state(i, state->texture_states[i]); + } + + init_default_sampler_states(state->sampler_states); + + state->blend_factor.r = 1.0f; + state->blend_factor.g = 1.0f; + state->blend_factor.b = 1.0f; + state->blend_factor.a = 1.0f; + + state->sample_mask = 0xffffffff; + + for (i = 0; i < WINED3D_MAX_STREAMS; ++i) + state->streams[i].frequency = 1; +} + +void state_init(struct wined3d_state *state, const struct wined3d_d3d_info *d3d_info, DWORD flags) +{ + unsigned int i; + + state->flags = flags; + + for (i = 0; i < LIGHTMAP_SIZE; i++) + { + list_init(&state->light_state.light_map[i]); + } + + if (flags & WINED3D_STATE_INIT_DEFAULT) + state_init_default(state, d3d_info); +} + +static void stateblock_state_init_default(struct wined3d_stateblock_state *state, + const struct wined3d_d3d_info *d3d_info) +{ + struct wined3d_matrix identity; + unsigned int i; + + get_identity_matrix(&identity); + + state->transforms[WINED3D_TS_PROJECTION] = identity; + state->transforms[WINED3D_TS_VIEW] = identity; + for (i = 0; i < 256; ++i) + { + state->transforms[WINED3D_TS_WORLD_MATRIX(i)] = identity; + } + + init_default_render_states(state->rs, d3d_info); + + for (i = 0; i < WINED3D_MAX_TEXTURES; ++i) + { + state->transforms[WINED3D_TS_TEXTURE0 + i] = identity; + init_default_texture_state(i, state->texture_states[i]); + } + + init_default_sampler_states(state->sampler_states); + + for (i = 0; i < WINED3D_MAX_STREAMS; ++i) + state->streams[i].frequency = 1; +} + +void wined3d_stateblock_state_init(struct wined3d_stateblock_state *state, + const struct wined3d_device *device, DWORD flags) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(state->light_state->light_map); i++) + { + list_init(&state->light_state->light_map[i]); + } + + if (flags & WINED3D_STATE_INIT_DEFAULT) + stateblock_state_init_default(state, &device->adapter->d3d_info); + +} + +static HRESULT stateblock_init(struct wined3d_stateblock *stateblock, const struct wined3d_stateblock *device_state, + struct wined3d_device *device, enum wined3d_stateblock_type type) +{ + const struct wined3d_d3d_info *d3d_info = &device->adapter->d3d_info; + + stateblock->ref = 1; + stateblock->device = device; + stateblock->stateblock_state.light_state = &stateblock->light_state; + wined3d_stateblock_state_init(&stateblock->stateblock_state, device, + type == WINED3D_SBT_PRIMARY ? WINED3D_STATE_INIT_DEFAULT : 0); + + stateblock->changed.store_stream_offset = 1; + + if (type == WINED3D_SBT_RECORDED || type == WINED3D_SBT_PRIMARY) + return WINED3D_OK; + + TRACE("Updating changed flags appropriate for type %#x.\n", type); + + switch (type) + { + case WINED3D_SBT_ALL: + stateblock_init_lights(stateblock->stateblock_state.light_state->light_map, + device_state->stateblock_state.light_state->light_map); + stateblock_savedstates_set_all(&stateblock->changed, + d3d_info->limits.vs_uniform_count, d3d_info->limits.ps_uniform_count); + break; + + case WINED3D_SBT_PIXEL_STATE: + stateblock_savedstates_set_pixel(&stateblock->changed, + d3d_info->limits.ps_uniform_count); + break; + + case WINED3D_SBT_VERTEX_STATE: + stateblock_init_lights(stateblock->stateblock_state.light_state->light_map, + device_state->stateblock_state.light_state->light_map); + stateblock_savedstates_set_vertex(&stateblock->changed, + d3d_info->limits.vs_uniform_count); + break; + + default: + FIXME("Unrecognized state block type %#x.\n", type); + break; + } + + wined3d_stateblock_init_contained_states(stateblock); + wined3d_stateblock_capture(stateblock, device_state); + + /* According to the tests, stream offset is not updated in the captured state if + * the state was captured on state block creation. This is not the case for + * state blocks initialized with BeginStateBlock / EndStateBlock, multiple + * captures get stream offsets updated. */ + stateblock->changed.store_stream_offset = 0; + + return WINED3D_OK; +} + +HRESULT CDECL wined3d_stateblock_create(struct wined3d_device *device, const struct wined3d_stateblock *device_state, + enum wined3d_stateblock_type type, struct wined3d_stateblock **stateblock) +{ + struct wined3d_stateblock *object; + HRESULT hr; + + TRACE("device %p, device_state %p, type %#x, stateblock %p.\n", + device, device_state, type, stateblock); + + if (!(object = heap_alloc_zero(sizeof(*object)))) + return E_OUTOFMEMORY; + + hr = stateblock_init(object, device_state, device, type); + if (FAILED(hr)) + { + WARN("Failed to initialize stateblock, hr %#x.\n", hr); + heap_free(object); + return hr; + } + + TRACE("Created stateblock %p.\n", object); + *stateblock = object; + + return WINED3D_OK; +} + +void CDECL wined3d_stateblock_reset(struct wined3d_stateblock *stateblock) +{ + TRACE("stateblock %p.\n", stateblock); + + wined3d_stateblock_state_cleanup(&stateblock->stateblock_state); + memset(&stateblock->stateblock_state, 0, sizeof(stateblock->stateblock_state)); + stateblock->stateblock_state.light_state = &stateblock->light_state; + wined3d_stateblock_state_init(&stateblock->stateblock_state, stateblock->device, WINED3D_STATE_INIT_DEFAULT); +} diff --git a/wrappers/directx/d3dwine_wrapper/surface.c b/wrappers/directx/d3dwine_wrapper/surface.c new file mode 100644 index 00000000000..d5d07d8c401 --- /dev/null +++ b/wrappers/directx/d3dwine_wrapper/surface.c @@ -0,0 +1,1730 @@ +/* + * Copyright 1997-2000 Marcus Meissner + * Copyright 1998-2000 Lionel Ulmer + * Copyright 2000-2001 TransGaming Technologies Inc. + * Copyright 2002-2005 Jason Edmeades + * Copyright 2002-2003 Raphael Junqueira + * Copyright 2004 Christian Costa + * Copyright 2005 Oliver Stieber + * Copyright 2006-2011, 2013-2014 Stefan Dösinger for CodeWeavers + * Copyright 2007-2008 Henri Verbeet + * Copyright 2006-2008 Roderick Colenbrander + * Copyright 2009-2011 Henri Verbeet for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "config.h" +#include "wine/port.h" +#include "wined3d_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(d3d); +WINE_DECLARE_DEBUG_CHANNEL(d3d_perf); + +static const DWORD surface_simple_locations = WINED3D_LOCATION_SYSMEM | WINED3D_LOCATION_BUFFER; + +/* Works correctly only for <= 4 bpp formats. */ +static void get_color_masks(const struct wined3d_format *format, DWORD *masks) +{ + masks[0] = ((1u << format->red_size) - 1) << format->red_offset; + masks[1] = ((1u << format->green_size) - 1) << format->green_offset; + masks[2] = ((1u << format->blue_size) - 1) << format->blue_offset; +} + +/* See also float_16_to_32() in wined3d_private.h */ +static inline unsigned short float_32_to_16(const float *in) +{ + int exp = 0; + float tmp = fabsf(*in); + unsigned int mantissa; + unsigned short ret; + + /* Deal with special numbers */ + if (*in == 0.0f) + return 0x0000; + if (isnan(*in)) + return 0x7c01; + if (isinf(*in)) + return (*in < 0.0f ? 0xfc00 : 0x7c00); + + if (tmp < (float)(1u << 10)) + { + do + { + tmp = tmp * 2.0f; + exp--; + } while (tmp < (float)(1u << 10)); + } + else if (tmp >= (float)(1u << 11)) + { + do + { + tmp /= 2.0f; + exp++; + } while (tmp >= (float)(1u << 11)); + } + + mantissa = (unsigned int)tmp; + if (tmp - mantissa >= 0.5f) + ++mantissa; /* Round to nearest, away from zero. */ + + exp += 10; /* Normalize the mantissa. */ + exp += 15; /* Exponent is encoded with excess 15. */ + + if (exp > 30) /* too big */ + { + ret = 0x7c00; /* INF */ + } + else if (exp <= 0) + { + /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */ + while (exp <= 0) + { + mantissa = mantissa >> 1; + ++exp; + } + ret = mantissa & 0x3ff; + } + else + { + ret = (exp << 10) | (mantissa & 0x3ff); + } + + ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */ + return ret; +} + +static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst, + DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h) +{ + unsigned short *dst_s; + const float *src_f; + unsigned int x, y; + + TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out); + + for (y = 0; y < h; ++y) + { + src_f = (const float *)(src + y * pitch_in); + dst_s = (unsigned short *) (dst + y * pitch_out); + for (x = 0; x < w; ++x) + { + dst_s[x] = float_32_to_16(src_f + x); + } + } +} + +static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst, + DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h) +{ + static const unsigned char convert_5to8[] = + { + 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a, + 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b, + 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd, + 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff, + }; + static const unsigned char convert_6to8[] = + { + 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c, + 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d, + 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d, + 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d, + 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e, + 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe, + 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf, + 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff, + }; + unsigned int x, y; + + TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out); + + for (y = 0; y < h; ++y) + { + const WORD *src_line = (const WORD *)(src + y * pitch_in); + DWORD *dst_line = (DWORD *)(dst + y * pitch_out); + for (x = 0; x < w; ++x) + { + WORD pixel = src_line[x]; + dst_line[x] = 0xff000000u + | convert_5to8[(pixel & 0xf800u) >> 11] << 16 + | convert_6to8[(pixel & 0x07e0u) >> 5] << 8 + | convert_5to8[(pixel & 0x001fu)]; + } + } +} + +/* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since + * in both cases we're just setting the X / Alpha channel to 0xff. */ +static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst, + DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h) +{ + unsigned int x, y; + + TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out); + + for (y = 0; y < h; ++y) + { + const DWORD *src_line = (const DWORD *)(src + y * pitch_in); + DWORD *dst_line = (DWORD *)(dst + y * pitch_out); + + for (x = 0; x < w; ++x) + { + dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff); + } + } +} + +static inline BYTE cliptobyte(int x) +{ + return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x)); +} + +static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst, + DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h) +{ + int c2, d, e, r2 = 0, g2 = 0, b2 = 0; + unsigned int x, y; + + TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out); + + for (y = 0; y < h; ++y) + { + const BYTE *src_line = src + y * pitch_in; + DWORD *dst_line = (DWORD *)(dst + y * pitch_out); + for (x = 0; x < w; ++x) + { + /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV: + * C = Y - 16; D = U - 128; E = V - 128; + * R = cliptobyte((298 * C + 409 * E + 128) >> 8); + * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8); + * B = cliptobyte((298 * C + 516 * D + 128) >> 8); + * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V . + * U and V are shared between the pixels. */ + if (!(x & 1)) /* For every even pixel, read new U and V. */ + { + d = (int) src_line[1] - 128; + e = (int) src_line[3] - 128; + r2 = 409 * e + 128; + g2 = - 100 * d - 208 * e + 128; + b2 = 516 * d + 128; + } + c2 = 298 * ((int) src_line[0] - 16); + dst_line[x] = 0xff000000 + | cliptobyte((c2 + r2) >> 8) << 16 /* red */ + | cliptobyte((c2 + g2) >> 8) << 8 /* green */ + | cliptobyte((c2 + b2) >> 8); /* blue */ + /* Scale RGB values to 0..255 range, + * then clip them if still not in range (may be negative), + * then shift them within DWORD if necessary. */ + src_line += 2; + } + } +} + +static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst, + DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h) +{ + unsigned int x, y; + int c2, d, e, r2 = 0, g2 = 0, b2 = 0; + + TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out); + + for (y = 0; y < h; ++y) + { + const BYTE *src_line = src + y * pitch_in; + WORD *dst_line = (WORD *)(dst + y * pitch_out); + for (x = 0; x < w; ++x) + { + /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV: + * C = Y - 16; D = U - 128; E = V - 128; + * R = cliptobyte((298 * C + 409 * E + 128) >> 8); + * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8); + * B = cliptobyte((298 * C + 516 * D + 128) >> 8); + * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V . + * U and V are shared between the pixels. */ + if (!(x & 1)) /* For every even pixel, read new U and V. */ + { + d = (int) src_line[1] - 128; + e = (int) src_line[3] - 128; + r2 = 409 * e + 128; + g2 = - 100 * d - 208 * e + 128; + b2 = 516 * d + 128; + } + c2 = 298 * ((int) src_line[0] - 16); + dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */ + | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */ + | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */ + /* Scale RGB values to 0..255 range, + * then clip them if still not in range (may be negative), + * then shift them within DWORD if necessary. */ + src_line += 2; + } + } +} + +struct d3dfmt_converter_desc +{ + enum wined3d_format_id from, to; + void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h); +}; + +static const struct d3dfmt_converter_desc converters[] = +{ + {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float}, + {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8}, + {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8}, + {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8}, + {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8}, + {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5}, +}; + +static inline const struct d3dfmt_converter_desc *find_converter(enum wined3d_format_id from, + enum wined3d_format_id to) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(converters); ++i) + { + if (converters[i].from == from && converters[i].to == to) + return &converters[i]; + } + + return NULL; +} + +static struct wined3d_texture *surface_convert_format(struct wined3d_texture *src_texture, + unsigned int sub_resource_idx, const struct wined3d_format *dst_format) +{ + unsigned int texture_level = sub_resource_idx % src_texture->level_count; + const struct wined3d_format *src_format = src_texture->resource.format; + struct wined3d_device *device = src_texture->resource.device; + const struct d3dfmt_converter_desc *conv = NULL; + unsigned int src_row_pitch, src_slice_pitch; + struct wined3d_texture *dst_texture; + struct wined3d_bo_address src_data; + struct wined3d_resource_desc desc; + struct wined3d_context *context; + DWORD map_binding; + + if (!(conv = find_converter(src_format->id, dst_format->id)) && ((device->wined3d->flags & WINED3D_NO3D) + || !is_identity_fixup(src_format->color_fixup) || src_format->conv_byte_count + || !is_identity_fixup(dst_format->color_fixup) || dst_format->conv_byte_count + || ((src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED) + && !src_format->decompress))) + { + FIXME("Cannot find a conversion function from format %s to %s.\n", + debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id)); + return NULL; + } + + /* FIXME: Multisampled conversion? */ + desc.resource_type = WINED3D_RTYPE_TEXTURE_2D; + desc.format = dst_format->id; + desc.multisample_type = WINED3D_MULTISAMPLE_NONE; + desc.multisample_quality = 0; + desc.usage = WINED3DUSAGE_SCRATCH | WINED3DUSAGE_PRIVATE; + desc.bind_flags = 0; + desc.access = WINED3D_RESOURCE_ACCESS_CPU | WINED3D_RESOURCE_ACCESS_MAP_R | WINED3D_RESOURCE_ACCESS_MAP_W; + desc.width = wined3d_texture_get_level_width(src_texture, texture_level); + desc.height = wined3d_texture_get_level_height(src_texture, texture_level); + desc.depth = 1; + desc.size = 0; + if (FAILED(wined3d_texture_create(device, &desc, 1, 1, WINED3D_TEXTURE_CREATE_DISCARD, + NULL, NULL, &wined3d_null_parent_ops, &dst_texture))) + { + ERR("Failed to create a destination texture for conversion.\n"); + return NULL; + } + + context = context_acquire(device, NULL, 0); + + map_binding = src_texture->resource.map_binding; + if (!wined3d_texture_load_location(src_texture, sub_resource_idx, context, map_binding)) + ERR("Failed to load the source sub-resource into %s.\n", wined3d_debug_location(map_binding)); + wined3d_texture_get_pitch(src_texture, texture_level, &src_row_pitch, &src_slice_pitch); + wined3d_texture_get_memory(src_texture, sub_resource_idx, &src_data, map_binding); + + if (conv) + { + unsigned int dst_row_pitch, dst_slice_pitch; + struct wined3d_bo_address dst_data; + struct wined3d_range range; + const BYTE *src; + BYTE *dst; + + map_binding = dst_texture->resource.map_binding; + if (!wined3d_texture_load_location(dst_texture, 0, context, map_binding)) + ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(map_binding)); + wined3d_texture_get_pitch(dst_texture, 0, &dst_row_pitch, &dst_slice_pitch); + wined3d_texture_get_memory(dst_texture, 0, &dst_data, map_binding); + + src = wined3d_context_map_bo_address(context, &src_data, + src_texture->sub_resources[sub_resource_idx].size, WINED3D_MAP_READ); + dst = wined3d_context_map_bo_address(context, &dst_data, + dst_texture->sub_resources[0].size, WINED3D_MAP_WRITE); + + conv->convert(src, dst, src_row_pitch, dst_row_pitch, desc.width, desc.height); + + range.offset = 0; + range.size = dst_texture->sub_resources[0].size; + wined3d_texture_invalidate_location(dst_texture, 0, ~map_binding); + wined3d_context_unmap_bo_address(context, &dst_data, 1, &range); + wined3d_context_unmap_bo_address(context, &src_data, 0, NULL); + } + else + { + struct wined3d_box src_box = {0, 0, desc.width, desc.height, 0, 1}; + + TRACE("Using upload conversion.\n"); + + wined3d_texture_prepare_location(dst_texture, 0, context, WINED3D_LOCATION_TEXTURE_RGB); + dst_texture->texture_ops->texture_upload_data(context, wined3d_const_bo_address(&src_data), + src_format, &src_box, src_row_pitch, src_slice_pitch, + dst_texture, 0, WINED3D_LOCATION_TEXTURE_RGB, 0, 0, 0); + + wined3d_texture_validate_location(dst_texture, 0, WINED3D_LOCATION_TEXTURE_RGB); + wined3d_texture_invalidate_location(dst_texture, 0, ~WINED3D_LOCATION_TEXTURE_RGB); + } + + context_release(context); + + return dst_texture; +} + +void texture2d_read_from_framebuffer(struct wined3d_texture *texture, unsigned int sub_resource_idx, + struct wined3d_context *context, DWORD src_location, DWORD dst_location) +{ + struct wined3d_resource *resource = &texture->resource; + struct wined3d_device *device = resource->device; + const struct wined3d_format_gl *format_gl; + struct wined3d_texture *restore_texture; + const struct wined3d_gl_info *gl_info; + struct wined3d_context_gl *context_gl; + unsigned int row_pitch, slice_pitch; + unsigned int width, height, level; + struct wined3d_bo_address data; + unsigned int restore_idx; + BYTE *row, *top, *bottom; + BOOL src_is_upside_down; + unsigned int i; + BYTE *mem; + + wined3d_texture_get_memory(texture, sub_resource_idx, &data, dst_location); + + restore_texture = context->current_rt.texture; + restore_idx = context->current_rt.sub_resource_idx; + if (restore_texture != texture || restore_idx != sub_resource_idx) + context = context_acquire(device, texture, sub_resource_idx); + else + restore_texture = NULL; + context_gl = wined3d_context_gl(context); + gl_info = context_gl->gl_info; + + if (src_location != resource->draw_binding) + { + wined3d_context_gl_apply_fbo_state_blit(context_gl, GL_READ_FRAMEBUFFER, + resource, sub_resource_idx, NULL, 0, src_location); + wined3d_context_gl_check_fbo_status(context_gl, GL_READ_FRAMEBUFFER); + context_invalidate_state(context, STATE_FRAMEBUFFER); + } + else + { + wined3d_context_gl_apply_blit_state(context_gl, device); + } + + /* Select the correct read buffer, and give some debug output. + * There is no need to keep track of the current read buffer or reset it, + * every part of the code that reads sets the read buffer as desired. + */ + if (src_location != WINED3D_LOCATION_DRAWABLE || wined3d_resource_is_offscreen(resource)) + { + /* Mapping the primary render target which is not on a swapchain. + * Read from the back buffer. */ + TRACE("Mapping offscreen render target.\n"); + gl_info->gl_ops.gl.p_glReadBuffer(wined3d_context_gl_get_offscreen_gl_buffer(context_gl)); + src_is_upside_down = TRUE; + } + else + { + /* Onscreen surfaces are always part of a swapchain */ + GLenum buffer = wined3d_texture_get_gl_buffer(texture); + TRACE("Mapping %#x buffer.\n", buffer); + gl_info->gl_ops.gl.p_glReadBuffer(buffer); + src_is_upside_down = FALSE; + } + checkGLcall("glReadBuffer"); + + if (data.buffer_object) + { + GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, ((struct wined3d_bo_gl *)data.buffer_object)->id)); + checkGLcall("glBindBuffer"); + } + + level = sub_resource_idx % texture->level_count; + wined3d_texture_get_pitch(texture, level, &row_pitch, &slice_pitch); + format_gl = wined3d_format_gl(resource->format); + + /* Setup pixel store pack state -- to glReadPixels into the correct place */ + gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, row_pitch / format_gl->f.byte_count); + checkGLcall("glPixelStorei"); + + width = wined3d_texture_get_level_width(texture, level); + height = wined3d_texture_get_level_height(texture, level); + gl_info->gl_ops.gl.p_glReadPixels(0, 0, width, height, + format_gl->format, format_gl->type, data.addr); + checkGLcall("glReadPixels"); + + /* Reset previous pixel store pack state */ + gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, 0); + checkGLcall("glPixelStorei"); + + if (!src_is_upside_down) + { + /* glReadPixels returns the image upside down, and there is no way to + * prevent this. Flip the lines in software. */ + + if (!(row = heap_alloc(row_pitch))) + goto error; + + if (data.buffer_object) + { + mem = GL_EXTCALL(glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_WRITE)); + checkGLcall("glMapBuffer"); + } + else + mem = data.addr; + + top = mem; + bottom = mem + row_pitch * (height - 1); + for (i = 0; i < height / 2; i++) + { + memcpy(row, top, row_pitch); + memcpy(top, bottom, row_pitch); + memcpy(bottom, row, row_pitch); + top += row_pitch; + bottom -= row_pitch; + } + heap_free(row); + + if (data.buffer_object) + GL_EXTCALL(glUnmapBuffer(GL_PIXEL_PACK_BUFFER)); + } + +error: + if (data.buffer_object) + { + GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0)); + wined3d_context_gl_reference_bo(context_gl, (struct wined3d_bo_gl *)data.buffer_object); + checkGLcall("glBindBuffer"); + } + + if (restore_texture) + context_restore(context, restore_texture, restore_idx); +} + +/* Read the framebuffer contents into a texture. Note that this function + * doesn't do any kind of flipping. Using this on an onscreen surface will + * result in a flipped D3D texture. + * + * Context activation is done by the caller. This function may temporarily + * switch to a different context and restore the original one before return. */ +void texture2d_load_fb_texture(struct wined3d_texture_gl *texture_gl, + unsigned int sub_resource_idx, BOOL srgb, struct wined3d_context *context) +{ + struct wined3d_texture *restore_texture; + const struct wined3d_gl_info *gl_info; + struct wined3d_context_gl *context_gl; + struct wined3d_resource *resource; + unsigned int restore_idx, level; + struct wined3d_device *device; + GLenum target; + + resource = &texture_gl->t.resource; + device = resource->device; + restore_texture = context->current_rt.texture; + restore_idx = context->current_rt.sub_resource_idx; + if (restore_texture != &texture_gl->t || restore_idx != sub_resource_idx) + context = context_acquire(device, &texture_gl->t, sub_resource_idx); + else + restore_texture = NULL; + context_gl = wined3d_context_gl(context); + + gl_info = context_gl->gl_info; + device_invalidate_state(device, STATE_FRAMEBUFFER); + + wined3d_texture_gl_prepare_texture(texture_gl, context_gl, srgb); + wined3d_texture_gl_bind_and_dirtify(texture_gl, context_gl, srgb); + + TRACE("Reading back offscreen render target %p, %u.\n", texture_gl, sub_resource_idx); + + if (wined3d_resource_is_offscreen(resource)) + gl_info->gl_ops.gl.p_glReadBuffer(wined3d_context_gl_get_offscreen_gl_buffer(context_gl)); + else + gl_info->gl_ops.gl.p_glReadBuffer(wined3d_texture_get_gl_buffer(&texture_gl->t)); + checkGLcall("glReadBuffer"); + + level = sub_resource_idx % texture_gl->t.level_count; + target = wined3d_texture_gl_get_sub_resource_target(texture_gl, sub_resource_idx); + gl_info->gl_ops.gl.p_glCopyTexSubImage2D(target, level, 0, 0, 0, 0, + wined3d_texture_get_level_width(&texture_gl->t, level), + wined3d_texture_get_level_height(&texture_gl->t, level)); + checkGLcall("glCopyTexSubImage2D"); + + if (restore_texture) + context_restore(context, restore_texture, restore_idx); +} + +/* Context activation is done by the caller. */ +static void cpu_blitter_destroy(struct wined3d_blitter *blitter, struct wined3d_context *context) +{ + struct wined3d_blitter *next; + + if ((next = blitter->next)) + next->ops->blitter_destroy(next, context); + + heap_free(blitter); +} + +static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data, + UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h, + const struct wined3d_format *format, DWORD flags, const struct wined3d_blt_fx *fx) +{ + UINT row_block_count; + const BYTE *src_row; + BYTE *dst_row; + UINT x, y; + + src_row = src_data; + dst_row = dst_data; + + row_block_count = (update_w + format->block_width - 1) / format->block_width; + + if (!flags) + { + for (y = 0; y < update_h; y += format->block_height) + { + memcpy(dst_row, src_row, row_block_count * format->block_byte_count); + src_row += src_pitch; + dst_row += dst_pitch; + } + + return WINED3D_OK; + } + + if (flags == WINED3D_BLT_FX && fx->fx == WINEDDBLTFX_MIRRORUPDOWN) + { + src_row += (((update_h / format->block_height) - 1) * src_pitch); + + switch (format->id) + { + case WINED3DFMT_DXT1: + for (y = 0; y < update_h; y += format->block_height) + { + struct block + { + WORD color[2]; + BYTE control_row[4]; + }; + + const struct block *s = (const struct block *)src_row; + struct block *d = (struct block *)dst_row; + + for (x = 0; x < row_block_count; ++x) + { + d[x].color[0] = s[x].color[0]; + d[x].color[1] = s[x].color[1]; + d[x].control_row[0] = s[x].control_row[3]; + d[x].control_row[1] = s[x].control_row[2]; + d[x].control_row[2] = s[x].control_row[1]; + d[x].control_row[3] = s[x].control_row[0]; + } + src_row -= src_pitch; + dst_row += dst_pitch; + } + return WINED3D_OK; + + case WINED3DFMT_DXT2: + case WINED3DFMT_DXT3: + for (y = 0; y < update_h; y += format->block_height) + { + struct block + { + WORD alpha_row[4]; + WORD color[2]; + BYTE control_row[4]; + }; + + const struct block *s = (const struct block *)src_row; + struct block *d = (struct block *)dst_row; + + for (x = 0; x < row_block_count; ++x) + { + d[x].alpha_row[0] = s[x].alpha_row[3]; + d[x].alpha_row[1] = s[x].alpha_row[2]; + d[x].alpha_row[2] = s[x].alpha_row[1]; + d[x].alpha_row[3] = s[x].alpha_row[0]; + d[x].color[0] = s[x].color[0]; + d[x].color[1] = s[x].color[1]; + d[x].control_row[0] = s[x].control_row[3]; + d[x].control_row[1] = s[x].control_row[2]; + d[x].control_row[2] = s[x].control_row[1]; + d[x].control_row[3] = s[x].control_row[0]; + } + src_row -= src_pitch; + dst_row += dst_pitch; + } + return WINED3D_OK; + + default: + FIXME("Compressed flip not implemented for format %s.\n", + debug_d3dformat(format->id)); + return E_NOTIMPL; + } + } + + FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n", + debug_d3dformat(format->id), flags, flags & WINED3D_BLT_FX ? fx->fx : 0); + + return E_NOTIMPL; +} + +static HRESULT surface_cpu_blt(struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx, + const struct wined3d_box *dst_box, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx, + const struct wined3d_box *src_box, DWORD flags, const struct wined3d_blt_fx *fx, + enum wined3d_texture_filter_type filter) +{ + unsigned int bpp, src_height, src_width, dst_height, dst_width, row_byte_count; + struct wined3d_device *device = dst_texture->resource.device; + const struct wined3d_format *src_format, *dst_format; + struct wined3d_texture *converted_texture = NULL; + struct wined3d_bo_address src_data, dst_data; + unsigned int src_fmt_flags, dst_fmt_flags; + struct wined3d_map_desc dst_map, src_map; + unsigned int x, sx, xinc, y, sy, yinc; + struct wined3d_context *context; + struct wined3d_range dst_range; + unsigned int texture_level; + HRESULT hr = WINED3D_OK; + BOOL same_sub_resource; + BOOL upload = FALSE; + DWORD map_binding; + const BYTE *sbase; + const BYTE *sbuf; + BYTE *dbuf; + + TRACE("dst_texture %p, dst_sub_resource_idx %u, dst_box %s, src_texture %p, " + "src_sub_resource_idx %u, src_box %s, flags %#x, fx %p, filter %s.\n", + dst_texture, dst_sub_resource_idx, debug_box(dst_box), src_texture, + src_sub_resource_idx, debug_box(src_box), flags, fx, debug_d3dtexturefiltertype(filter)); + + context = context_acquire(device, NULL, 0); + + src_format = src_texture->resource.format; + dst_format = dst_texture->resource.format; + + if (wined3d_format_is_typeless(src_format) && src_format->id == dst_format->typeless_id) + src_format = dst_format; + if (wined3d_format_is_typeless(dst_format) && dst_format->id == src_format->typeless_id) + dst_format = src_format; + + src_height = src_box->bottom - src_box->top; + src_width = src_box->right - src_box->left; + dst_height = dst_box->bottom - dst_box->top; + dst_width = dst_box->right - dst_box->left; + + dst_range.offset = 0; + dst_range.size = dst_texture->sub_resources[dst_sub_resource_idx].size; + if (src_texture == dst_texture && src_sub_resource_idx == dst_sub_resource_idx) + { + same_sub_resource = TRUE; + + map_binding = dst_texture->resource.map_binding; + texture_level = dst_sub_resource_idx % dst_texture->level_count; + if (!wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, map_binding)) + ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(map_binding)); + wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~map_binding); + wined3d_texture_get_pitch(dst_texture, texture_level, &dst_map.row_pitch, &dst_map.slice_pitch); + wined3d_texture_get_memory(dst_texture, dst_sub_resource_idx, &dst_data, map_binding); + dst_map.data = wined3d_context_map_bo_address(context, &dst_data, + dst_texture->sub_resources[dst_sub_resource_idx].size, WINED3D_MAP_READ | WINED3D_MAP_WRITE); + + src_map = dst_map; + } + else + { + same_sub_resource = FALSE; + upload = dst_format->flags[dst_texture->resource.gl_type] & WINED3DFMT_FLAG_BLOCKS + && (dst_width != src_width || dst_height != src_height); + + if (upload) + { + dst_format = src_format->flags[dst_texture->resource.gl_type] & WINED3DFMT_FLAG_BLOCKS + ? wined3d_get_format(device->adapter, WINED3DFMT_B8G8R8A8_UNORM, 0) : src_format; + } + + if (!(flags & WINED3D_BLT_RAW) && dst_format->id != src_format->id) + { + if (!(converted_texture = surface_convert_format(src_texture, src_sub_resource_idx, dst_format))) + { + FIXME("Cannot convert %s to %s.\n", debug_d3dformat(src_format->id), + debug_d3dformat(dst_format->id)); + context_release(context); + return WINED3DERR_NOTAVAILABLE; + } + src_texture = converted_texture; + src_sub_resource_idx = 0; + src_format = src_texture->resource.format; + } + + map_binding = src_texture->resource.map_binding; + texture_level = src_sub_resource_idx % src_texture->level_count; + if (!wined3d_texture_load_location(src_texture, src_sub_resource_idx, context, map_binding)) + ERR("Failed to load the source sub-resource into %s.\n", wined3d_debug_location(map_binding)); + wined3d_texture_get_pitch(src_texture, texture_level, &src_map.row_pitch, &src_map.slice_pitch); + wined3d_texture_get_memory(src_texture, src_sub_resource_idx, &src_data, map_binding); + src_map.data = wined3d_context_map_bo_address(context, &src_data, + src_texture->sub_resources[src_sub_resource_idx].size, WINED3D_MAP_READ); + + if (upload) + { + wined3d_format_calculate_pitch(dst_format, 1, dst_box->right, dst_box->bottom, + &dst_map.row_pitch, &dst_map.slice_pitch); + dst_map.data = heap_alloc(dst_map.slice_pitch); + } + else + { + map_binding = dst_texture->resource.map_binding; + texture_level = dst_sub_resource_idx % dst_texture->level_count; + if (!wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, map_binding)) + ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(map_binding)); + + wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~map_binding); + wined3d_texture_get_pitch(dst_texture, texture_level, &dst_map.row_pitch, &dst_map.slice_pitch); + wined3d_texture_get_memory(dst_texture, dst_sub_resource_idx, &dst_data, map_binding); + dst_map.data = wined3d_context_map_bo_address(context, &dst_data, + dst_texture->sub_resources[dst_sub_resource_idx].size, WINED3D_MAP_WRITE); + } + } + src_fmt_flags = src_format->flags[src_texture->resource.gl_type]; + dst_fmt_flags = dst_format->flags[dst_texture->resource.gl_type]; + flags &= ~WINED3D_BLT_RAW; + + bpp = dst_format->byte_count; + row_byte_count = dst_width * bpp; + + sbase = (BYTE *)src_map.data + + ((src_box->top / src_format->block_height) * src_map.row_pitch) + + ((src_box->left / src_format->block_width) * src_format->block_byte_count); + dbuf = (BYTE *)dst_map.data + + ((dst_box->top / dst_format->block_height) * dst_map.row_pitch) + + ((dst_box->left / dst_format->block_width) * dst_format->block_byte_count); + + if (src_fmt_flags & dst_fmt_flags & WINED3DFMT_FLAG_BLOCKS) + { + TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id)); + + if (same_sub_resource) + { + FIXME("Only plain blits supported on compressed surfaces.\n"); + hr = E_NOTIMPL; + goto release; + } + + hr = surface_cpu_blt_compressed(sbase, dbuf, + src_map.row_pitch, dst_map.row_pitch, dst_width, dst_height, + src_format, flags, fx); + goto release; + } + + if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT + && (src_width != dst_width || src_height != dst_height)) + { + /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */ + FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter)); + } + + xinc = (src_width << 16) / dst_width; + yinc = (src_height << 16) / dst_height; + + if (!flags) + { + /* No effects, we can cheat here. */ + if (dst_width == src_width) + { + if (dst_height == src_height) + { + /* No stretching in either direction. This needs to be as fast + * as possible. */ + sbuf = sbase; + + /* Check for overlapping surfaces. */ + if (!same_sub_resource || dst_box->top < src_box->top + || dst_box->right <= src_box->left || src_box->right <= dst_box->left) + { + /* No overlap, or dst above src, so copy from top downwards. */ + for (y = 0; y < dst_height; ++y) + { + memcpy(dbuf, sbuf, row_byte_count); + sbuf += src_map.row_pitch; + dbuf += dst_map.row_pitch; + } + } + else if (dst_box->top > src_box->top) + { + /* Copy from bottom upwards. */ + sbuf += src_map.row_pitch * dst_height; + dbuf += dst_map.row_pitch * dst_height; + for (y = 0; y < dst_height; ++y) + { + sbuf -= src_map.row_pitch; + dbuf -= dst_map.row_pitch; + memcpy(dbuf, sbuf, row_byte_count); + } + } + else + { + /* Src and dst overlapping on the same line, use memmove. */ + for (y = 0; y < dst_height; ++y) + { + memmove(dbuf, sbuf, row_byte_count); + sbuf += src_map.row_pitch; + dbuf += dst_map.row_pitch; + } + } + } + else + { + /* Stretching in y direction only. */ + for (y = sy = 0; y < dst_height; ++y, sy += yinc) + { + sbuf = sbase + (sy >> 16) * src_map.row_pitch; + memcpy(dbuf, sbuf, row_byte_count); + dbuf += dst_map.row_pitch; + } + } + } + else + { + /* Stretching in X direction. */ + unsigned int last_sy = ~0u; + for (y = sy = 0; y < dst_height; ++y, sy += yinc) + { + sbuf = sbase + (sy >> 16) * src_map.row_pitch; + + if ((sy >> 16) == (last_sy >> 16)) + { + /* This source row is the same as last source row - + * Copy the already stretched row. */ + memcpy(dbuf, dbuf - dst_map.row_pitch, row_byte_count); + } + else + { +#define STRETCH_ROW(type) \ +do { \ + const type *s = (const type *)sbuf; \ + type *d = (type *)dbuf; \ + for (x = sx = 0; x < dst_width; ++x, sx += xinc) \ + d[x] = s[sx >> 16]; \ +} while(0) + + switch(bpp) + { + case 1: + STRETCH_ROW(BYTE); + break; + case 2: + STRETCH_ROW(WORD); + break; + case 4: + STRETCH_ROW(DWORD); + break; + case 3: + { + const BYTE *s; + BYTE *d = dbuf; + for (x = sx = 0; x < dst_width; x++, sx+= xinc) + { + DWORD pixel; + + s = sbuf + 3 * (sx >> 16); + pixel = s[0] | (s[1] << 8) | (s[2] << 16); + d[0] = (pixel ) & 0xff; + d[1] = (pixel >> 8) & 0xff; + d[2] = (pixel >> 16) & 0xff; + d += 3; + } + break; + } + default: + FIXME("Stretched blit not implemented for bpp %u.\n", bpp * 8); + hr = WINED3DERR_NOTAVAILABLE; + goto error; + } +#undef STRETCH_ROW + } + dbuf += dst_map.row_pitch; + last_sy = sy; + } + } + } + else + { + LONG dstyinc = dst_map.row_pitch, dstxinc = bpp; + DWORD keylow = 0xffffffff, keyhigh = 0, keymask = 0xffffffff; + DWORD destkeylow = 0x0, destkeyhigh = 0xffffffff, destkeymask = 0xffffffff; + if (flags & (WINED3D_BLT_SRC_CKEY | WINED3D_BLT_DST_CKEY + | WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_DST_CKEY_OVERRIDE)) + { + /* The color keying flags are checked for correctness in ddraw. */ + if (flags & WINED3D_BLT_SRC_CKEY) + { + keylow = src_texture->async.src_blt_color_key.color_space_low_value; + keyhigh = src_texture->async.src_blt_color_key.color_space_high_value; + } + else if (flags & WINED3D_BLT_SRC_CKEY_OVERRIDE) + { + keylow = fx->src_color_key.color_space_low_value; + keyhigh = fx->src_color_key.color_space_high_value; + } + + if (flags & WINED3D_BLT_DST_CKEY) + { + /* Destination color keys are taken from the source surface! */ + destkeylow = src_texture->async.dst_blt_color_key.color_space_low_value; + destkeyhigh = src_texture->async.dst_blt_color_key.color_space_high_value; + } + else if (flags & WINED3D_BLT_DST_CKEY_OVERRIDE) + { + destkeylow = fx->dst_color_key.color_space_low_value; + destkeyhigh = fx->dst_color_key.color_space_high_value; + } + + if (bpp == 1) + { + keymask = 0xff; + } + else + { + DWORD masks[3]; + get_color_masks(src_format, masks); + keymask = masks[0] | masks[1] | masks[2]; + } + flags &= ~(WINED3D_BLT_SRC_CKEY | WINED3D_BLT_DST_CKEY + | WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_DST_CKEY_OVERRIDE); + } + + if (flags & WINED3D_BLT_FX) + { + BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp; + LONG tmpxy; + dTopLeft = dbuf; + dTopRight = dbuf + ((dst_width - 1) * bpp); + dBottomLeft = dTopLeft + ((dst_height - 1) * dst_map.row_pitch); + dBottomRight = dBottomLeft + ((dst_width - 1) * bpp); + + if (fx->fx & WINEDDBLTFX_ARITHSTRETCHY) + { + /* I don't think we need to do anything about this flag. */ + WARN("Nothing done for WINEDDBLTFX_ARITHSTRETCHY.\n"); + } + if (fx->fx & WINEDDBLTFX_MIRRORLEFTRIGHT) + { + tmp = dTopRight; + dTopRight = dTopLeft; + dTopLeft = tmp; + tmp = dBottomRight; + dBottomRight = dBottomLeft; + dBottomLeft = tmp; + dstxinc = dstxinc * -1; + } + if (fx->fx & WINEDDBLTFX_MIRRORUPDOWN) + { + tmp = dTopLeft; + dTopLeft = dBottomLeft; + dBottomLeft = tmp; + tmp = dTopRight; + dTopRight = dBottomRight; + dBottomRight = tmp; + dstyinc = dstyinc * -1; + } + if (fx->fx & WINEDDBLTFX_NOTEARING) + { + /* I don't think we need to do anything about this flag. */ + WARN("Nothing done for WINEDDBLTFX_NOTEARING.\n"); + } + if (fx->fx & WINEDDBLTFX_ROTATE180) + { + tmp = dBottomRight; + dBottomRight = dTopLeft; + dTopLeft = tmp; + tmp = dBottomLeft; + dBottomLeft = dTopRight; + dTopRight = tmp; + dstxinc = dstxinc * -1; + dstyinc = dstyinc * -1; + } + if (fx->fx & WINEDDBLTFX_ROTATE270) + { + tmp = dTopLeft; + dTopLeft = dBottomLeft; + dBottomLeft = dBottomRight; + dBottomRight = dTopRight; + dTopRight = tmp; + tmpxy = dstxinc; + dstxinc = dstyinc; + dstyinc = tmpxy; + dstxinc = dstxinc * -1; + } + if (fx->fx & WINEDDBLTFX_ROTATE90) + { + tmp = dTopLeft; + dTopLeft = dTopRight; + dTopRight = dBottomRight; + dBottomRight = dBottomLeft; + dBottomLeft = tmp; + tmpxy = dstxinc; + dstxinc = dstyinc; + dstyinc = tmpxy; + dstyinc = dstyinc * -1; + } + if (fx->fx & WINEDDBLTFX_ZBUFFERBASEDEST) + { + /* I don't think we need to do anything about this flag. */ + WARN("Nothing done for WINEDDBLTFX_ZBUFFERBASEDEST.\n"); + } + dbuf = dTopLeft; + flags &= ~(WINED3D_BLT_FX); + } + +#define COPY_COLORKEY_FX(type) \ +do { \ + const type *s; \ + type *d = (type *)dbuf, *dx, tmp; \ + for (y = sy = 0; y < dst_height; ++y, sy += yinc) \ + { \ + s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \ + dx = d; \ + for (x = sx = 0; x < dst_width; ++x, sx += xinc) \ + { \ + tmp = s[sx >> 16]; \ + if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \ + && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \ + { \ + dx[0] = tmp; \ + } \ + dx = (type *)(((BYTE *)dx) + dstxinc); \ + } \ + d = (type *)(((BYTE *)d) + dstyinc); \ + } \ +} while(0) + + switch (bpp) + { + case 1: + COPY_COLORKEY_FX(BYTE); + break; + case 2: + COPY_COLORKEY_FX(WORD); + break; + case 4: + COPY_COLORKEY_FX(DWORD); + break; + case 3: + { + const BYTE *s; + BYTE *d = dbuf, *dx; + for (y = sy = 0; y < dst_height; ++y, sy += yinc) + { + sbuf = sbase + (sy >> 16) * src_map.row_pitch; + dx = d; + for (x = sx = 0; x < dst_width; ++x, sx+= xinc) + { + DWORD pixel, dpixel = 0; + s = sbuf + 3 * (sx>>16); + pixel = s[0] | (s[1] << 8) | (s[2] << 16); + dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16); + if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh) + && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh)) + { + dx[0] = (pixel ) & 0xff; + dx[1] = (pixel >> 8) & 0xff; + dx[2] = (pixel >> 16) & 0xff; + } + dx += dstxinc; + } + d += dstyinc; + } + break; + } + default: + FIXME("%s color-keyed blit not implemented for bpp %u.\n", + (flags & WINED3D_BLT_SRC_CKEY) ? "Source" : "Destination", bpp * 8); + hr = WINED3DERR_NOTAVAILABLE; + goto error; +#undef COPY_COLORKEY_FX + } + } + +error: + if (flags) + FIXME(" Unsupported flags %#x.\n", flags); + +release: + if (upload && hr == WINED3D_OK) + { + struct wined3d_bo_address data; + + data.buffer_object = 0; + data.addr = dst_map.data; + + texture_level = dst_sub_resource_idx % dst_texture->level_count; + + wined3d_texture_prepare_location(dst_texture, texture_level, context, WINED3D_LOCATION_TEXTURE_RGB); + dst_texture->texture_ops->texture_upload_data(context, wined3d_const_bo_address(&data), dst_format, + dst_box, dst_map.row_pitch, dst_map.slice_pitch, dst_texture, texture_level, + WINED3D_LOCATION_TEXTURE_RGB, dst_box->left, dst_box->top, 0); + + wined3d_texture_validate_location(dst_texture, texture_level, WINED3D_LOCATION_TEXTURE_RGB); + wined3d_texture_invalidate_location(dst_texture, texture_level, ~WINED3D_LOCATION_TEXTURE_RGB); + } + + if (upload) + { + heap_free(dst_map.data); + } + else + { + wined3d_context_unmap_bo_address(context, &dst_data, 1, &dst_range); + } + + if (!same_sub_resource) + wined3d_context_unmap_bo_address(context, &src_data, 0, NULL); + if (SUCCEEDED(hr) && dst_texture->swapchain && dst_texture->swapchain->front_buffer == dst_texture) + { + SetRect(&dst_texture->swapchain->front_buffer_update, + dst_box->left, dst_box->top, dst_box->right, dst_box->bottom); + dst_texture->swapchain->swapchain_ops->swapchain_frontbuffer_updated(dst_texture->swapchain); + } + if (converted_texture) + wined3d_texture_decref(converted_texture); + context_release(context); + + return hr; +} + +static void surface_cpu_blt_colour_fill(struct wined3d_rendertarget_view *view, + const struct wined3d_box *box, const struct wined3d_color *colour) +{ + struct wined3d_device *device = view->resource->device; + unsigned int x, y, z, w, h, d, bpp, level; + struct wined3d_context *context; + struct wined3d_texture *texture; + struct wined3d_bo_address data; + struct wined3d_map_desc map; + struct wined3d_range range; + DWORD map_binding; + uint8_t *dst; + DWORD c; + + TRACE("view %p, box %s, colour %s.\n", view, debug_box(box), debug_color(colour)); + + if (view->format_flags & WINED3DFMT_FLAG_BLOCKS) + { + FIXME("Not implemented for format %s.\n", debug_d3dformat(view->format->id)); + return; + } + + if (view->format->id != view->resource->format->id) + FIXME("View format %s doesn't match resource format %s.\n", + debug_d3dformat(view->format->id), debug_d3dformat(view->resource->format->id)); + + if (view->resource->type == WINED3D_RTYPE_BUFFER) + { + FIXME("Not implemented for buffers.\n"); + return; + } + + context = context_acquire(device, NULL, 0); + + texture = texture_from_resource(view->resource); + level = view->sub_resource_idx % texture->level_count; + + c = wined3d_format_convert_from_float(view->format, colour); + bpp = view->format->byte_count; + w = min(box->right, view->width) - min(box->left, view->width); + h = min(box->bottom, view->height) - min(box->top, view->height); + if (view->resource->type != WINED3D_RTYPE_TEXTURE_3D) + { + d = 1; + } + else + { + d = wined3d_texture_get_level_depth(texture, level); + d = min(box->back, d) - min(box->front, d); + } + + map_binding = texture->resource.map_binding; + if (!wined3d_texture_load_location(texture, view->sub_resource_idx, context, map_binding)) + ERR("Failed to load the sub-resource into %s.\n", wined3d_debug_location(map_binding)); + wined3d_texture_invalidate_location(texture, view->sub_resource_idx, ~map_binding); + wined3d_texture_get_pitch(texture, level, &map.row_pitch, &map.slice_pitch); + wined3d_texture_get_memory(texture, view->sub_resource_idx, &data, map_binding); + map.data = wined3d_context_map_bo_address(context, &data, + texture->sub_resources[view->sub_resource_idx].size, WINED3D_MAP_WRITE); + map.data = (BYTE *)map.data + + (box->front * map.slice_pitch) + + ((box->top / view->format->block_height) * map.row_pitch) + + ((box->left / view->format->block_width) * view->format->block_byte_count); + range.offset = 0; + range.size = texture->sub_resources[view->sub_resource_idx].size; + + switch (bpp) + { + case 1: + for (x = 0; x < w; ++x) + { + ((BYTE *)map.data)[x] = c; + } + break; + + case 2: + for (x = 0; x < w; ++x) + { + ((WORD *)map.data)[x] = c; + } + break; + + case 3: + { + dst = map.data; + for (x = 0; x < w; ++x, dst += 3) + { + dst[0] = (c ) & 0xff; + dst[1] = (c >> 8) & 0xff; + dst[2] = (c >> 16) & 0xff; + } + break; + } + case 4: + for (x = 0; x < w; ++x) + { + ((DWORD *)map.data)[x] = c; + } + break; + + default: + FIXME("Not implemented for bpp %u.\n", bpp); + wined3d_resource_unmap(view->resource, view->sub_resource_idx); + return; + } + + dst = map.data; + for (y = 1; y < h; ++y) + { + dst += map.row_pitch; + memcpy(dst, map.data, w * bpp); + } + + dst = map.data; + for (z = 1; z < d; ++z) + { + dst += map.slice_pitch; + memcpy(dst, map.data, w * h * bpp); + } + + wined3d_context_unmap_bo_address(context, &data, 1, &range); + context_release(context); +} + +static void cpu_blitter_clear(struct wined3d_blitter *blitter, struct wined3d_device *device, + unsigned int rt_count, const struct wined3d_fb_state *fb, unsigned int rect_count, const RECT *clear_rects, + const RECT *draw_rect, DWORD flags, const struct wined3d_color *colour, float depth, DWORD stencil) +{ + struct wined3d_color c = {depth, 0.0f, 0.0f, 0.0f}; + struct wined3d_rendertarget_view *view; + struct wined3d_box box; + unsigned int i, j; + + if (!rect_count) + { + rect_count = 1; + clear_rects = draw_rect; + } + + for (i = 0; i < rect_count; ++i) + { + box.left = max(clear_rects[i].left, draw_rect->left); + box.top = max(clear_rects[i].top, draw_rect->top); + box.right = min(clear_rects[i].right, draw_rect->right); + box.bottom = min(clear_rects[i].bottom, draw_rect->bottom); + box.front = 0; + box.back = ~0u; + + if (box.left >= box.right || box.top >= box.bottom) + continue; + + if (flags & WINED3DCLEAR_TARGET) + { + for (j = 0; j < rt_count; ++j) + { + if ((view = fb->render_targets[j])) + surface_cpu_blt_colour_fill(view, &box, colour); + } + } + + if ((flags & (WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL)) && (view = fb->depth_stencil)) + { + if ((view->format->depth_size && !(flags & WINED3DCLEAR_ZBUFFER)) + || (view->format->stencil_size && !(flags & WINED3DCLEAR_STENCIL))) + FIXME("Clearing %#x on %s.\n", flags, debug_d3dformat(view->format->id)); + + surface_cpu_blt_colour_fill(view, &box, &c); + } + } +} + +static DWORD cpu_blitter_blit(struct wined3d_blitter *blitter, enum wined3d_blit_op op, + struct wined3d_context *context, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx, + DWORD src_location, const RECT *src_rect, struct wined3d_texture *dst_texture, + unsigned int dst_sub_resource_idx, DWORD dst_location, const RECT *dst_rect, + const struct wined3d_color_key *color_key, enum wined3d_texture_filter_type filter) +{ + struct wined3d_box dst_box = {dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, 0, 1}; + struct wined3d_box src_box = {src_rect->left, src_rect->top, src_rect->right, src_rect->bottom, 0, 1}; + struct wined3d_blt_fx fx; + DWORD flags = 0; + + memset(&fx, 0, sizeof(fx)); + switch (op) + { + case WINED3D_BLIT_OP_COLOR_BLIT: + case WINED3D_BLIT_OP_DEPTH_BLIT: + break; + case WINED3D_BLIT_OP_RAW_BLIT: + flags |= WINED3D_BLT_RAW; + break; + case WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST: + flags |= WINED3D_BLT_ALPHA_TEST; + break; + case WINED3D_BLIT_OP_COLOR_BLIT_CKEY: + flags |= WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_FX; + fx.src_color_key = *color_key; + break; + default: + FIXME("Unhandled op %#x.\n", op); + break; + } + + if (FAILED(surface_cpu_blt(dst_texture, dst_sub_resource_idx, &dst_box, + src_texture, src_sub_resource_idx, &src_box, flags, &fx, filter))) + ERR("Failed to blit.\n"); + wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, dst_location); + + return dst_location | (dst_texture->sub_resources[dst_sub_resource_idx].locations + & dst_texture->resource.map_binding); +} + +static const struct wined3d_blitter_ops cpu_blitter_ops = +{ + cpu_blitter_destroy, + cpu_blitter_clear, + cpu_blitter_blit, +}; + +struct wined3d_blitter *wined3d_cpu_blitter_create(void) +{ + struct wined3d_blitter *blitter; + + if (!(blitter = heap_alloc(sizeof(*blitter)))) + return NULL; + + TRACE("Created blitter %p.\n", blitter); + + blitter->ops = &cpu_blitter_ops; + blitter->next = NULL; + + return blitter; +} + +static bool wined3d_is_colour_blit(enum wined3d_blit_op blit_op) +{ + switch (blit_op) + { + case WINED3D_BLIT_OP_COLOR_BLIT: + case WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST: + case WINED3D_BLIT_OP_COLOR_BLIT_CKEY: + return true; + + default: + return false; + } +} + +HRESULT texture2d_blt(struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx, + const struct wined3d_box *dst_box, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx, + const struct wined3d_box *src_box, DWORD flags, const struct wined3d_blt_fx *fx, + enum wined3d_texture_filter_type filter) +{ + struct wined3d_texture_sub_resource *src_sub_resource, *dst_sub_resource; + struct wined3d_device *device = dst_texture->resource.device; + struct wined3d_swapchain *src_swapchain, *dst_swapchain; + const struct wined3d_color_key *colour_key = NULL; + DWORD src_location, dst_location, valid_locations; + struct wined3d_context *context; + enum wined3d_blit_op blit_op; + BOOL scale, convert, resolve; + RECT src_rect, dst_rect; + bool src_ds, dst_ds; + + static const DWORD simple_blit = WINED3D_BLT_SRC_CKEY + | WINED3D_BLT_SRC_CKEY_OVERRIDE + | WINED3D_BLT_ALPHA_TEST + | WINED3D_BLT_RAW; + + TRACE("dst_texture %p, dst_sub_resource_idx %u, dst_box %s, src_texture %p, " + "src_sub_resource_idx %u, src_box %s, flags %#x, fx %p, filter %s.\n", + dst_texture, dst_sub_resource_idx, debug_box(dst_box), src_texture, src_sub_resource_idx, + debug_box(src_box), flags, fx, debug_d3dtexturefiltertype(filter)); + TRACE("Usage is %s.\n", debug_d3dusage(dst_texture->resource.usage)); + + if (fx) + { + TRACE("fx %#x.\n", fx->fx); + TRACE("dst_color_key {0x%08x, 0x%08x}.\n", + fx->dst_color_key.color_space_low_value, + fx->dst_color_key.color_space_high_value); + TRACE("src_color_key {0x%08x, 0x%08x}.\n", + fx->src_color_key.color_space_low_value, + fx->src_color_key.color_space_high_value); + } + + dst_sub_resource = &dst_texture->sub_resources[dst_sub_resource_idx]; + src_sub_resource = &src_texture->sub_resources[src_sub_resource_idx]; + + if (src_sub_resource->locations & WINED3D_LOCATION_DISCARDED) + { + WARN("Source sub-resource is discarded, nothing to do.\n"); + return WINED3D_OK; + } + + SetRect(&src_rect, src_box->left, src_box->top, src_box->right, src_box->bottom); + SetRect(&dst_rect, dst_box->left, dst_box->top, dst_box->right, dst_box->bottom); + + if (!fx || !(fx->fx)) + flags &= ~WINED3D_BLT_FX; + + /* WINED3D_BLT_DO_NOT_WAIT appeared in DX7. */ + if (flags & WINED3D_BLT_DO_NOT_WAIT) + { + static unsigned int once; + + if (!once++) + FIXME("Can't handle WINED3D_BLT_DO_NOT_WAIT flag.\n"); + } + + flags &= ~(WINED3D_BLT_SYNCHRONOUS | WINED3D_BLT_DO_NOT_WAIT | WINED3D_BLT_WAIT); + + if (flags & ~simple_blit) + { + WARN_(d3d_perf)("Using CPU fallback for complex blit (%#x).\n", flags); + goto cpu; + } + + src_swapchain = src_texture->swapchain; + dst_swapchain = dst_texture->swapchain; + + if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain + && (wined3d_settings.offscreen_rendering_mode != ORM_FBO + || src_texture == src_swapchain->front_buffer)) + { + /* TODO: We could support cross-swapchain blits by first downloading + * the source to a texture. */ + FIXME("Cross-swapchain blit not supported.\n"); + return WINED3DERR_INVALIDCALL; + } + + scale = src_box->right - src_box->left != dst_box->right - dst_box->left + || src_box->bottom - src_box->top != dst_box->bottom - dst_box->top; + convert = src_texture->resource.format->id != dst_texture->resource.format->id; + resolve = src_texture->resource.multisample_type != dst_texture->resource.multisample_type; + + dst_ds = dst_texture->resource.format->depth_size || dst_texture->resource.format->stencil_size; + src_ds = src_texture->resource.format->depth_size || src_texture->resource.format->stencil_size; + + if (src_ds || dst_ds) + { + TRACE("Depth/stencil blit.\n"); + + if (dst_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU) + dst_location = dst_texture->resource.draw_binding; + else + dst_location = dst_texture->resource.map_binding; + + if ((flags & WINED3D_BLT_RAW) || (!scale && !convert && !resolve)) + blit_op = WINED3D_BLIT_OP_RAW_BLIT; + else + blit_op = WINED3D_BLIT_OP_DEPTH_BLIT; + + context = context_acquire(device, dst_texture, dst_sub_resource_idx); + valid_locations = device->blitter->ops->blitter_blit(device->blitter, blit_op, context, + src_texture, src_sub_resource_idx, src_texture->resource.draw_binding, &src_rect, + dst_texture, dst_sub_resource_idx, dst_location, &dst_rect, NULL, filter); + context_release(context); + + wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, valid_locations); + wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~valid_locations); + + return WINED3D_OK; + } + + TRACE("Colour blit.\n"); + + /* In principle this would apply to depth blits as well, but we don't + * implement those in the CPU blitter at the moment. */ + if ((dst_sub_resource->locations & dst_texture->resource.map_binding) + && (src_sub_resource->locations & src_texture->resource.map_binding)) + { + if (scale) + TRACE("Not doing sysmem blit because of scaling.\n"); + else if (convert) + TRACE("Not doing sysmem blit because of format conversion.\n"); + else + goto cpu; + } + + blit_op = WINED3D_BLIT_OP_COLOR_BLIT; + if (flags & WINED3D_BLT_SRC_CKEY_OVERRIDE) + { + colour_key = &fx->src_color_key; + blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY; + } + else if (flags & WINED3D_BLT_SRC_CKEY) + { + colour_key = &src_texture->async.src_blt_color_key; + blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY; + } + else if (flags & WINED3D_BLT_ALPHA_TEST) + { + blit_op = WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST; + } + else if ((src_sub_resource->locations & surface_simple_locations) + && !(dst_sub_resource->locations & surface_simple_locations) + && (dst_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU)) + { + /* Upload */ + if (scale) + TRACE("Not doing upload because of scaling.\n"); + else if (convert) + TRACE("Not doing upload because of format conversion.\n"); + else if (dst_texture->resource.format->conv_byte_count) + TRACE("Not doing upload because the destination format needs conversion.\n"); + else + { + wined3d_texture_upload_from_texture(dst_texture, dst_sub_resource_idx, dst_box->left, + dst_box->top, dst_box->front, src_texture, src_sub_resource_idx, src_box); + if (!wined3d_resource_is_offscreen(&dst_texture->resource)) + { + context = context_acquire(device, dst_texture, dst_sub_resource_idx); + wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, + context, dst_texture->resource.draw_binding); + context_release(context); + } + return WINED3D_OK; + } + } + else if (!(src_sub_resource->locations & surface_simple_locations) + && (dst_sub_resource->locations & dst_texture->resource.map_binding) + && !(dst_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU)) + { + /* Download */ + if (scale) + TRACE("Not doing download because of scaling.\n"); + else if (convert) + TRACE("Not doing download because of format conversion.\n"); + else if (src_texture->resource.format->conv_byte_count) + TRACE("Not doing download because the source format needs conversion.\n"); + else if (!(src_texture->flags & WINED3D_TEXTURE_DOWNLOADABLE)) + TRACE("Not doing download because texture is not downloadable.\n"); + else if (!wined3d_texture_is_full_rect(src_texture, src_sub_resource_idx % src_texture->level_count, &src_rect)) + TRACE("Not doing download because of partial download (src).\n"); + else if (!wined3d_texture_is_full_rect(dst_texture, dst_sub_resource_idx % dst_texture->level_count, &dst_rect)) + TRACE("Not doing download because of partial download (dst).\n"); + else + { + wined3d_texture_download_from_texture(dst_texture, dst_sub_resource_idx, src_texture, + src_sub_resource_idx); + return WINED3D_OK; + } + } + else if (dst_swapchain && dst_swapchain->back_buffers + && dst_texture == dst_swapchain->front_buffer + && src_texture == dst_swapchain->back_buffers[0]) + { + /* Use present for back -> front blits. The idea behind this is that + * present is potentially faster than a blit, in particular when FBO + * blits aren't available. Some ddraw applications like Half-Life and + * Prince of Persia 3D use Blt() from the backbuffer to the + * frontbuffer instead of doing a Flip(). D3d8 and d3d9 applications + * can't blit directly to the frontbuffer. */ + enum wined3d_swap_effect swap_effect = dst_swapchain->state.desc.swap_effect; + + TRACE("Using present for backbuffer -> frontbuffer blit.\n"); + + /* Set the swap effect to COPY, we don't want the backbuffer to become + * undefined. */ + dst_swapchain->state.desc.swap_effect = WINED3D_SWAP_EFFECT_COPY; + wined3d_swapchain_present(dst_swapchain, NULL, NULL, + dst_swapchain->win_handle, dst_swapchain->swap_interval, 0); + dst_swapchain->state.desc.swap_effect = swap_effect; + + return WINED3D_OK; + } + + if ((flags & WINED3D_BLT_RAW) || (blit_op == WINED3D_BLIT_OP_COLOR_BLIT && !scale && !convert && !resolve)) + blit_op = WINED3D_BLIT_OP_RAW_BLIT; + + context = context_acquire(device, dst_texture, dst_sub_resource_idx); + + if (src_texture->resource.multisample_type != WINED3D_MULTISAMPLE_NONE + && ((scale && !context->d3d_info->scaled_resolve) + || convert || !wined3d_is_colour_blit(blit_op))) + src_location = WINED3D_LOCATION_RB_RESOLVED; + else + src_location = src_texture->resource.draw_binding; + + if (!(dst_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU)) + dst_location = dst_texture->resource.map_binding; + else if (dst_texture->resource.multisample_type != WINED3D_MULTISAMPLE_NONE + && (scale || convert || !wined3d_is_colour_blit(blit_op))) + dst_location = WINED3D_LOCATION_RB_RESOLVED; + else + dst_location = dst_texture->resource.draw_binding; + + valid_locations = device->blitter->ops->blitter_blit(device->blitter, blit_op, context, + src_texture, src_sub_resource_idx, src_location, &src_rect, + dst_texture, dst_sub_resource_idx, dst_location, &dst_rect, colour_key, filter); + + context_release(context); + + wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, valid_locations); + wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~valid_locations); + + return WINED3D_OK; + +cpu: + return surface_cpu_blt(dst_texture, dst_sub_resource_idx, dst_box, + src_texture, src_sub_resource_idx, src_box, flags, fx, filter); +} diff --git a/wrappers/directx/d3dwine_wrapper/swapchain.c b/wrappers/directx/d3dwine_wrapper/swapchain.c new file mode 100644 index 00000000000..5fae34c8af5 --- /dev/null +++ b/wrappers/directx/d3dwine_wrapper/swapchain.c @@ -0,0 +1,2350 @@ +/* + * Copyright 2002-2003 Jason Edmeades + * Copyright 2002-2003 Raphael Junqueira + * Copyright 2005 Oliver Stieber + * Copyright 2007-2008 Stefan Dösinger for CodeWeavers + * Copyright 2011 Henri Verbeet for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "config.h" +#include "wine/port.h" +#include "wined3d_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(d3d); + +void wined3d_swapchain_cleanup(struct wined3d_swapchain *swapchain) +{ + HRESULT hr; + UINT i; + + TRACE("Destroying swapchain %p.\n", swapchain); + + wined3d_swapchain_state_cleanup(&swapchain->state); + wined3d_swapchain_set_gamma_ramp(swapchain, 0, &swapchain->orig_gamma); + + /* Release the swapchain's draw buffers. Make sure swapchain->back_buffers[0] + * is the last buffer to be destroyed, FindContext() depends on that. */ + if (swapchain->front_buffer) + { + wined3d_texture_set_swapchain(swapchain->front_buffer, NULL); + if (wined3d_texture_decref(swapchain->front_buffer)) + WARN("Something's still holding the front buffer (%p).\n", swapchain->front_buffer); + swapchain->front_buffer = NULL; + } + + if (swapchain->back_buffers) + { + i = swapchain->state.desc.backbuffer_count; + + while (i--) + { + wined3d_texture_set_swapchain(swapchain->back_buffers[i], NULL); + if (wined3d_texture_decref(swapchain->back_buffers[i])) + WARN("Something's still holding back buffer %u (%p).\n", i, swapchain->back_buffers[i]); + } + heap_free(swapchain->back_buffers); + swapchain->back_buffers = NULL; + } + + /* Restore the screen resolution if we rendered in fullscreen. + * This will restore the screen resolution to what it was before creating + * the swapchain. In case of d3d8 and d3d9 this will be the original + * desktop resolution. In case of d3d7 this will be a NOP because ddraw + * sets the resolution before starting up Direct3D, thus orig_width and + * orig_height will be equal to the modes in the presentation params. */ + if (!swapchain->state.desc.windowed) + { + if (swapchain->state.desc.auto_restore_display_mode) + { + if (FAILED(hr = wined3d_restore_display_modes(swapchain->device->wined3d))) + ERR("Failed to restore display mode, hr %#x.\n", hr); + + if (swapchain->state.desc.flags & WINED3D_SWAPCHAIN_RESTORE_WINDOW_RECT) + { + wined3d_swapchain_state_restore_from_fullscreen(&swapchain->state, + swapchain->state.device_window, &swapchain->state.original_window_rect); + wined3d_device_release_focus_window(swapchain->device); + } + } + else + { + wined3d_swapchain_state_restore_from_fullscreen(&swapchain->state, swapchain->state.device_window, NULL); + } + } +} + +static void wined3d_swapchain_gl_destroy_object(void *object) +{ + wined3d_swapchain_gl_destroy_contexts(object); +} + +void wined3d_swapchain_gl_cleanup(struct wined3d_swapchain_gl *swapchain_gl) +{ + struct wined3d_cs *cs = swapchain_gl->s.device->cs; + + wined3d_swapchain_cleanup(&swapchain_gl->s); + + wined3d_cs_destroy_object(cs, wined3d_swapchain_gl_destroy_object, swapchain_gl); + wined3d_cs_finish(cs, WINED3D_CS_QUEUE_DEFAULT); + + if (swapchain_gl->backup_dc) + { + TRACE("Destroying backup wined3d window %p, dc %p.\n", swapchain_gl->backup_wnd, swapchain_gl->backup_dc); + + wined3d_release_dc(swapchain_gl->backup_wnd, swapchain_gl->backup_dc); + DestroyWindow(swapchain_gl->backup_wnd); + } +} + +static void wined3d_swapchain_vk_destroy_vulkan_swapchain(struct wined3d_swapchain_vk *swapchain_vk) +{ + struct wined3d_device_vk *device_vk = wined3d_device_vk(swapchain_vk->s.device); + const struct wined3d_vk_info *vk_info; + unsigned int i; + VkResult vr; + + TRACE("swapchain_vk %p.\n", swapchain_vk); + + vk_info = &wined3d_adapter_vk(device_vk->d.adapter)->vk_info; + + if ((vr = VK_CALL(vkQueueWaitIdle(device_vk->vk_queue))) < 0) + ERR("Failed to wait on queue, vr %s.\n", wined3d_debug_vkresult(vr)); + heap_free(swapchain_vk->vk_images); + for (i = 0; i < swapchain_vk->image_count; ++i) + { + VK_CALL(vkDestroySemaphore(device_vk->vk_device, swapchain_vk->vk_semaphores[i].available, NULL)); + VK_CALL(vkDestroySemaphore(device_vk->vk_device, swapchain_vk->vk_semaphores[i].presentable, NULL)); + } + heap_free(swapchain_vk->vk_semaphores); + VK_CALL(vkDestroySwapchainKHR(device_vk->vk_device, swapchain_vk->vk_swapchain, NULL)); + VK_CALL(vkDestroySurfaceKHR(vk_info->instance, swapchain_vk->vk_surface, NULL)); +} + +static void wined3d_swapchain_vk_destroy_object(void *object) +{ + wined3d_swapchain_vk_destroy_vulkan_swapchain(object); +} + +void wined3d_swapchain_vk_cleanup(struct wined3d_swapchain_vk *swapchain_vk) +{ + struct wined3d_cs *cs = swapchain_vk->s.device->cs; + + wined3d_cs_destroy_object(cs, wined3d_swapchain_vk_destroy_object, swapchain_vk); + wined3d_cs_finish(cs, WINED3D_CS_QUEUE_DEFAULT); + + wined3d_swapchain_cleanup(&swapchain_vk->s); +} + +ULONG CDECL wined3d_swapchain_incref(struct wined3d_swapchain *swapchain) +{ + ULONG refcount = InterlockedIncrement(&swapchain->ref); + + TRACE("%p increasing refcount to %u.\n", swapchain, refcount); + + return refcount; +} + +ULONG CDECL wined3d_swapchain_decref(struct wined3d_swapchain *swapchain) +{ + ULONG refcount = InterlockedDecrement(&swapchain->ref); + + TRACE("%p decreasing refcount to %u.\n", swapchain, refcount); + + if (!refcount) + { + struct wined3d_device *device; + + wined3d_mutex_lock(); + + device = swapchain->device; + if (device->swapchain_count && device->swapchains[0] == swapchain) + wined3d_device_uninit_3d(device); + wined3d_cs_finish(device->cs, WINED3D_CS_QUEUE_DEFAULT); + + swapchain->parent_ops->wined3d_object_destroyed(swapchain->parent); + swapchain->device->adapter->adapter_ops->adapter_destroy_swapchain(swapchain); + + wined3d_mutex_unlock(); + } + + return refcount; +} + +void * CDECL wined3d_swapchain_get_parent(const struct wined3d_swapchain *swapchain) +{ + TRACE("swapchain %p.\n", swapchain); + + return swapchain->parent; +} + +void CDECL wined3d_swapchain_set_window(struct wined3d_swapchain *swapchain, HWND window) +{ + if (!window) + window = swapchain->state.device_window; + if (window == swapchain->win_handle) + return; + + TRACE("Setting swapchain %p window from %p to %p.\n", + swapchain, swapchain->win_handle, window); + + wined3d_cs_finish(swapchain->device->cs, WINED3D_CS_QUEUE_DEFAULT); + + swapchain->win_handle = window; +} + +HRESULT CDECL wined3d_swapchain_present(struct wined3d_swapchain *swapchain, + const RECT *src_rect, const RECT *dst_rect, HWND dst_window_override, + unsigned int swap_interval, DWORD flags) +{ + RECT s, d; + + TRACE("swapchain %p, src_rect %s, dst_rect %s, dst_window_override %p, swap_interval %u, flags %#x.\n", + swapchain, wine_dbgstr_rect(src_rect), wine_dbgstr_rect(dst_rect), + dst_window_override, swap_interval, flags); + + if (flags) + FIXME("Ignoring flags %#x.\n", flags); + + wined3d_mutex_lock(); + + if (!swapchain->back_buffers) + { + WARN("Swapchain doesn't have a backbuffer, returning WINED3DERR_INVALIDCALL.\n"); + wined3d_mutex_unlock(); + return WINED3DERR_INVALIDCALL; + } + + if (!src_rect) + { + SetRect(&s, 0, 0, swapchain->state.desc.backbuffer_width, + swapchain->state.desc.backbuffer_height); + src_rect = &s; + } + + if (!dst_rect) + { + GetClientRect(swapchain->win_handle, &d); + dst_rect = &d; + } + + wined3d_cs_emit_present(swapchain->device->cs, swapchain, src_rect, + dst_rect, dst_window_override, swap_interval, flags); + + wined3d_mutex_unlock(); + + return WINED3D_OK; +} + +HRESULT CDECL wined3d_swapchain_get_front_buffer_data(const struct wined3d_swapchain *swapchain, + struct wined3d_texture *dst_texture, unsigned int sub_resource_idx) +{ + RECT src_rect, dst_rect; + + TRACE("swapchain %p, dst_texture %p, sub_resource_idx %u.\n", swapchain, dst_texture, sub_resource_idx); + + SetRect(&src_rect, 0, 0, swapchain->front_buffer->resource.width, swapchain->front_buffer->resource.height); + dst_rect = src_rect; + + if (swapchain->state.desc.windowed) + { + MapWindowPoints(swapchain->win_handle, NULL, (POINT *)&dst_rect, 2); + FIXME("Using destination rect %s in windowed mode, this is likely wrong.\n", + wine_dbgstr_rect(&dst_rect)); + } + + return wined3d_texture_blt(dst_texture, sub_resource_idx, &dst_rect, + swapchain->front_buffer, 0, &src_rect, 0, NULL, WINED3D_TEXF_POINT); +} + +struct wined3d_texture * CDECL wined3d_swapchain_get_back_buffer(const struct wined3d_swapchain *swapchain, + UINT back_buffer_idx) +{ + TRACE("swapchain %p, back_buffer_idx %u.\n", + swapchain, back_buffer_idx); + + /* Return invalid if there is no backbuffer array, otherwise it will + * crash when ddraw is used (there swapchain->back_buffers is always + * NULL). We need this because this function is called from + * stateblock_init_default_state() to get the default scissorrect + * dimensions. */ + if (!swapchain->back_buffers || back_buffer_idx >= swapchain->state.desc.backbuffer_count) + { + WARN("Invalid back buffer index.\n"); + /* Native d3d9 doesn't set NULL here, just as wine's d3d9. But set it + * here in wined3d to avoid problems in other libs. */ + return NULL; + } + + TRACE("Returning back buffer %p.\n", swapchain->back_buffers[back_buffer_idx]); + + return swapchain->back_buffers[back_buffer_idx]; +} + +struct wined3d_output * wined3d_swapchain_get_output(const struct wined3d_swapchain *swapchain) +{ + TRACE("swapchain %p.\n", swapchain); + + return swapchain->state.desc.output; +} + +HRESULT CDECL wined3d_swapchain_get_raster_status(const struct wined3d_swapchain *swapchain, + struct wined3d_raster_status *raster_status) +{ + struct wined3d_output *output; + + TRACE("swapchain %p, raster_status %p.\n", swapchain, raster_status); + + output = wined3d_swapchain_get_output(swapchain); + if (!output) + { + ERR("Failed to get output from swapchain %p.\n", swapchain); + return E_FAIL; + } + + return wined3d_output_get_raster_status(output, raster_status); +} + +struct wined3d_swapchain_state * CDECL wined3d_swapchain_get_state(struct wined3d_swapchain *swapchain) +{ + return &swapchain->state; +} + +HRESULT CDECL wined3d_swapchain_get_display_mode(const struct wined3d_swapchain *swapchain, + struct wined3d_display_mode *mode, enum wined3d_display_rotation *rotation) +{ + struct wined3d_output *output; + HRESULT hr; + + TRACE("swapchain %p, mode %p, rotation %p.\n", swapchain, mode, rotation); + + if (!(output = wined3d_swapchain_get_output(swapchain))) + { + ERR("Failed to get output from swapchain %p.\n", swapchain); + return E_FAIL; + } + + hr = wined3d_output_get_display_mode(output, mode, rotation); + + TRACE("Returning w %u, h %u, refresh rate %u, format %s.\n", + mode->width, mode->height, mode->refresh_rate, debug_d3dformat(mode->format_id)); + + return hr; +} + +struct wined3d_device * CDECL wined3d_swapchain_get_device(const struct wined3d_swapchain *swapchain) +{ + TRACE("swapchain %p.\n", swapchain); + + return swapchain->device; +} + +void CDECL wined3d_swapchain_get_desc(const struct wined3d_swapchain *swapchain, + struct wined3d_swapchain_desc *desc) +{ + TRACE("swapchain %p, desc %p.\n", swapchain, desc); + + *desc = swapchain->state.desc; +} + +HRESULT CDECL wined3d_swapchain_set_gamma_ramp(const struct wined3d_swapchain *swapchain, + DWORD flags, const struct wined3d_gamma_ramp *ramp) +{ + HDC dc; + + TRACE("swapchain %p, flags %#x, ramp %p.\n", swapchain, flags, ramp); + + if (flags) + FIXME("Ignoring flags %#x.\n", flags); + + dc = GetDCEx(swapchain->state.device_window, 0, DCX_USESTYLE | DCX_CACHE); + SetDeviceGammaRamp(dc, (void *)ramp); + ReleaseDC(swapchain->state.device_window, dc); + + return WINED3D_OK; +} + +void CDECL wined3d_swapchain_set_palette(struct wined3d_swapchain *swapchain, struct wined3d_palette *palette) +{ + TRACE("swapchain %p, palette %p.\n", swapchain, palette); + + wined3d_cs_finish(swapchain->device->cs, WINED3D_CS_QUEUE_DEFAULT); + + swapchain->palette = palette; +} + +HRESULT CDECL wined3d_swapchain_get_gamma_ramp(const struct wined3d_swapchain *swapchain, + struct wined3d_gamma_ramp *ramp) +{ + HDC dc; + + TRACE("swapchain %p, ramp %p.\n", swapchain, ramp); + + dc = GetDCEx(swapchain->state.device_window, 0, DCX_USESTYLE | DCX_CACHE); + GetDeviceGammaRamp(dc, ramp); + ReleaseDC(swapchain->state.device_window, dc); + + return WINED3D_OK; +} + +/* A GL context is provided by the caller */ +static void swapchain_blit(const struct wined3d_swapchain *swapchain, + struct wined3d_context *context, const RECT *src_rect, const RECT *dst_rect) +{ + struct wined3d_texture *texture = swapchain->back_buffers[0]; + struct wined3d_device *device = swapchain->device; + enum wined3d_texture_filter_type filter; + DWORD location; + + TRACE("swapchain %p, context %p, src_rect %s, dst_rect %s.\n", + swapchain, context, wine_dbgstr_rect(src_rect), wine_dbgstr_rect(dst_rect)); + + if ((src_rect->right - src_rect->left == dst_rect->right - dst_rect->left + && src_rect->bottom - src_rect->top == dst_rect->bottom - dst_rect->top) + || is_complex_fixup(texture->resource.format->color_fixup)) + filter = WINED3D_TEXF_NONE; + else + filter = WINED3D_TEXF_LINEAR; + + location = WINED3D_LOCATION_TEXTURE_RGB; + if (texture->resource.multisample_type) + location = WINED3D_LOCATION_RB_RESOLVED; + + wined3d_texture_validate_location(texture, 0, WINED3D_LOCATION_DRAWABLE); + device->blitter->ops->blitter_blit(device->blitter, WINED3D_BLIT_OP_COLOR_BLIT, context, texture, 0, + location, src_rect, texture, 0, WINED3D_LOCATION_DRAWABLE, dst_rect, NULL, filter); + wined3d_texture_invalidate_location(texture, 0, WINED3D_LOCATION_DRAWABLE); +} + +static void swapchain_gl_set_swap_interval(struct wined3d_swapchain *swapchain, + struct wined3d_context_gl *context_gl, unsigned int swap_interval) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + + swap_interval = swap_interval <= 4 ? swap_interval : 1; + if (swapchain->swap_interval == swap_interval) + return; + + swapchain->swap_interval = swap_interval; + + if (!gl_info->supported[WGL_EXT_SWAP_CONTROL]) + return; + + if (!GL_EXTCALL(wglSwapIntervalEXT(swap_interval))) + { + ERR("Failed to set swap interval %u for context %p, last error %#x.\n", + swap_interval, context_gl, GetLastError()); + } +} + +/* Context activation is done by the caller. */ +static void wined3d_swapchain_gl_rotate(struct wined3d_swapchain *swapchain, struct wined3d_context *context) +{ + struct wined3d_texture_sub_resource *sub_resource; + struct wined3d_texture_gl *texture, *texture_prev; + struct gl_texture tex0; + GLuint rb0; + DWORD locations0; + unsigned int i; + static const DWORD supported_locations = WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_RB_MULTISAMPLE; + + if (swapchain->state.desc.backbuffer_count < 2 || !swapchain->render_to_fbo) + return; + + texture_prev = wined3d_texture_gl(swapchain->back_buffers[0]); + + /* Back buffer 0 is already in the draw binding. */ + tex0 = texture_prev->texture_rgb; + rb0 = texture_prev->rb_multisample; + locations0 = texture_prev->t.sub_resources[0].locations; + + for (i = 1; i < swapchain->state.desc.backbuffer_count; ++i) + { + texture = wined3d_texture_gl(swapchain->back_buffers[i]); + sub_resource = &texture->t.sub_resources[0]; + + if (!(sub_resource->locations & supported_locations)) + wined3d_texture_load_location(&texture->t, 0, context, texture->t.resource.draw_binding); + + texture_prev->texture_rgb = texture->texture_rgb; + texture_prev->rb_multisample = texture->rb_multisample; + + wined3d_texture_validate_location(&texture_prev->t, 0, sub_resource->locations & supported_locations); + wined3d_texture_invalidate_location(&texture_prev->t, 0, ~(sub_resource->locations & supported_locations)); + + texture_prev = texture; + } + + texture_prev->texture_rgb = tex0; + texture_prev->rb_multisample = rb0; + + wined3d_texture_validate_location(&texture_prev->t, 0, locations0 & supported_locations); + wined3d_texture_invalidate_location(&texture_prev->t, 0, ~(locations0 & supported_locations)); + + device_invalidate_state(swapchain->device, STATE_FRAMEBUFFER); +} + +static void swapchain_gl_present(struct wined3d_swapchain *swapchain, + const RECT *src_rect, const RECT *dst_rect, unsigned int swap_interval, DWORD flags) +{ + struct wined3d_swapchain_gl *swapchain_gl = wined3d_swapchain_gl(swapchain); + const struct wined3d_swapchain_desc *desc = &swapchain->state.desc; + struct wined3d_texture *back_buffer = swapchain->back_buffers[0]; + const struct wined3d_gl_info *gl_info; + struct wined3d_context_gl *context_gl; + struct wined3d_context *context; + BOOL render_to_fbo; + + context = context_acquire(swapchain->device, swapchain->front_buffer, 0); + context_gl = wined3d_context_gl(context); + if (!context_gl->valid) + { + context_release(context); + WARN("Invalid context, skipping present.\n"); + return; + } + + gl_info = context_gl->gl_info; + + swapchain_gl_set_swap_interval(swapchain, context_gl, swap_interval); + + TRACE("Presenting DC %p.\n", context_gl->dc); + + if (!(render_to_fbo = swapchain->render_to_fbo) + && (src_rect->left || src_rect->top + || src_rect->right != desc->backbuffer_width + || src_rect->bottom != desc->backbuffer_height + || dst_rect->left || dst_rect->top + || dst_rect->right != desc->backbuffer_width + || dst_rect->bottom != desc->backbuffer_height)) + render_to_fbo = TRUE; + + /* Rendering to a window of different size, presenting partial rectangles, + * or rendering to a different window needs help from FBO_blit or a textured + * draw. Render the swapchain to a FBO in the future. + * + * Note that FBO_blit from the backbuffer to the frontbuffer cannot solve + * all these issues - this fails if the window is smaller than the backbuffer. + */ + if (!swapchain->render_to_fbo && render_to_fbo && wined3d_settings.offscreen_rendering_mode == ORM_FBO) + { + wined3d_texture_load_location(back_buffer, 0, context, WINED3D_LOCATION_TEXTURE_RGB); + wined3d_texture_invalidate_location(back_buffer, 0, WINED3D_LOCATION_DRAWABLE); + swapchain->render_to_fbo = TRUE; + swapchain_update_draw_bindings(swapchain); + } + else + { + wined3d_texture_load_location(back_buffer, 0, context, back_buffer->resource.draw_binding); + } + + if (swapchain->render_to_fbo) + swapchain_blit(swapchain, context, src_rect, dst_rect); + + if (swapchain_gl->context_count > 1) + gl_info->gl_ops.gl.p_glFinish(); + + /* call wglSwapBuffers through the gl table to avoid confusing the Steam overlay */ + gl_info->gl_ops.wgl.p_wglSwapBuffers(context_gl->dc); + wined3d_context_gl_submit_command_fence(context_gl); + + wined3d_swapchain_gl_rotate(swapchain, context); + + TRACE("SwapBuffers called, Starting new frame\n"); + + wined3d_texture_validate_location(swapchain->front_buffer, 0, WINED3D_LOCATION_DRAWABLE); + wined3d_texture_invalidate_location(swapchain->front_buffer, 0, ~WINED3D_LOCATION_DRAWABLE); + + context_release(context); +} + +static void swapchain_frontbuffer_updated(struct wined3d_swapchain *swapchain) +{ + struct wined3d_texture *front_buffer = swapchain->front_buffer; + struct wined3d_context *context; + + context = context_acquire(swapchain->device, front_buffer, 0); + wined3d_texture_load_location(front_buffer, 0, context, front_buffer->resource.draw_binding); + context_release(context); + SetRectEmpty(&swapchain->front_buffer_update); +} + +static const struct wined3d_swapchain_ops swapchain_gl_ops = +{ + swapchain_gl_present, + swapchain_frontbuffer_updated, +}; + +static bool wined3d_swapchain_vk_present_mode_supported(struct wined3d_swapchain_vk *swapchain_vk, + VkPresentModeKHR vk_present_mode) +{ + struct wined3d_device_vk *device_vk = wined3d_device_vk(swapchain_vk->s.device); + const struct wined3d_vk_info *vk_info; + struct wined3d_adapter_vk *adapter_vk; + VkPhysicalDevice vk_physical_device; + VkPresentModeKHR *vk_modes; + bool supported = false; + uint32_t count, i; + VkResult vr; + + adapter_vk = wined3d_adapter_vk(device_vk->d.adapter); + vk_physical_device = adapter_vk->physical_device; + vk_info = &adapter_vk->vk_info; + + if ((vr = VK_CALL(vkGetPhysicalDeviceSurfacePresentModesKHR(vk_physical_device, + swapchain_vk->vk_surface, &count, NULL))) < 0) + { + ERR("Failed to get supported present mode count, vr %s.\n", wined3d_debug_vkresult(vr)); + return false; + } + + if (!(vk_modes = heap_calloc(count, sizeof(*vk_modes)))) + return false; + + if ((vr = VK_CALL(vkGetPhysicalDeviceSurfacePresentModesKHR(vk_physical_device, + swapchain_vk->vk_surface, &count, vk_modes))) < 0) + { + ERR("Failed to get supported present modes, vr %s.\n", wined3d_debug_vkresult(vr)); + goto done; + } + + for (i = 0; i < count; ++i) + { + if (vk_modes[i] == vk_present_mode) + { + supported = true; + goto done; + } + } + +done: + heap_free(vk_modes); + return supported; +} + +static VkFormat get_swapchain_fallback_format(VkFormat vk_format) +{ + switch (vk_format) + { + case VK_FORMAT_R8G8B8A8_SRGB: + return VK_FORMAT_B8G8R8A8_SRGB; + case VK_FORMAT_R8G8B8A8_UNORM: + case VK_FORMAT_A2B10G10R10_UNORM_PACK32: + case VK_FORMAT_R16G16B16A16_SFLOAT: + return VK_FORMAT_B8G8R8A8_UNORM; + default: + WARN("Unhandled format %#x.\n", vk_format); + return VK_FORMAT_UNDEFINED; + } +} + +static VkFormat wined3d_swapchain_vk_select_vk_format(struct wined3d_swapchain_vk *swapchain_vk, + VkSurfaceKHR vk_surface) +{ + struct wined3d_device_vk *device_vk = wined3d_device_vk(swapchain_vk->s.device); + const struct wined3d_swapchain_desc *desc = &swapchain_vk->s.state.desc; + const struct wined3d_vk_info *vk_info; + struct wined3d_adapter_vk *adapter_vk; + const struct wined3d_format *format; + VkPhysicalDevice vk_physical_device; + VkSurfaceFormatKHR *vk_formats; + uint32_t format_count, i; + VkFormat vk_format; + VkResult vr; + + adapter_vk = wined3d_adapter_vk(device_vk->d.adapter); + vk_physical_device = adapter_vk->physical_device; + vk_info = &adapter_vk->vk_info; + + if ((format = wined3d_get_format(&adapter_vk->a, desc->backbuffer_format, WINED3D_BIND_RENDER_TARGET))) + vk_format = wined3d_format_vk(format)->vk_format; + else + vk_format = VK_FORMAT_B8G8R8A8_UNORM; + + vr = VK_CALL(vkGetPhysicalDeviceSurfaceFormatsKHR(vk_physical_device, vk_surface, &format_count, NULL)); + if (vr < 0 || !format_count) + { + WARN("Failed to get supported surface format count, vr %s.\n", wined3d_debug_vkresult(vr)); + return VK_FORMAT_UNDEFINED; + } + + if (!(vk_formats = heap_calloc(format_count, sizeof(*vk_formats)))) + return VK_FORMAT_UNDEFINED; + + if ((vr = VK_CALL(vkGetPhysicalDeviceSurfaceFormatsKHR(vk_physical_device, + vk_surface, &format_count, vk_formats))) < 0) + { + WARN("Failed to get supported surface formats, vr %s.\n", wined3d_debug_vkresult(vr)); + heap_free(vk_formats); + return VK_FORMAT_UNDEFINED; + } + + for (i = 0; i < format_count; ++i) + { + if (vk_formats[i].format == vk_format && vk_formats[i].colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) + break; + } + if (i == format_count) + { + /* Try to create a swapchain with format conversion. */ + vk_format = get_swapchain_fallback_format(vk_format); + WARN("Failed to find Vulkan swapchain format for %s.\n", debug_d3dformat(desc->backbuffer_format)); + for (i = 0; i < format_count; ++i) + { + if (vk_formats[i].format == vk_format && vk_formats[i].colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) + break; + } + } + heap_free(vk_formats); + if (i == format_count) + { + FIXME("Failed to find Vulkan swapchain format for %s.\n", debug_d3dformat(desc->backbuffer_format)); + return VK_FORMAT_UNDEFINED; + } + + TRACE("Using Vulkan swapchain format %#x.\n", vk_format); + + return vk_format; +} + +static bool wined3d_swapchain_vk_create_vulkan_swapchain_images(struct wined3d_swapchain_vk *swapchain_vk, + VkSwapchainKHR vk_swapchain) +{ + struct wined3d_device_vk *device_vk = wined3d_device_vk(swapchain_vk->s.device); + const struct wined3d_vk_info *vk_info; + VkSemaphoreCreateInfo semaphore_info; + uint32_t image_count, i; + VkResult vr; + + vk_info = &wined3d_adapter_vk(device_vk->d.adapter)->vk_info; + + if ((vr = VK_CALL(vkGetSwapchainImagesKHR(device_vk->vk_device, vk_swapchain, &image_count, NULL))) < 0) + { + ERR("Failed to get image count, vr %s\n", wined3d_debug_vkresult(vr)); + return false; + } + + if (!(swapchain_vk->vk_images = heap_calloc(image_count, sizeof(*swapchain_vk->vk_images)))) + { + ERR("Failed to allocate images array.\n"); + return false; + } + + if ((vr = VK_CALL(vkGetSwapchainImagesKHR(device_vk->vk_device, + vk_swapchain, &image_count, swapchain_vk->vk_images))) < 0) + { + ERR("Failed to get swapchain images, vr %s.\n", wined3d_debug_vkresult(vr)); + heap_free(swapchain_vk->vk_images); + return false; + } + + if (!(swapchain_vk->vk_semaphores = heap_calloc(image_count, sizeof(*swapchain_vk->vk_semaphores)))) + { + ERR("Failed to allocate semaphores array.\n"); + heap_free(swapchain_vk->vk_images); + return false; + } + + semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + semaphore_info.pNext = NULL; + semaphore_info.flags = 0; + for (i = 0; i < image_count; ++i) + { + if ((vr = VK_CALL(vkCreateSemaphore(device_vk->vk_device, + &semaphore_info, NULL, &swapchain_vk->vk_semaphores[i].available))) < 0) + { + ERR("Failed to create semaphore, vr %s.\n", wined3d_debug_vkresult(vr)); + goto fail; + } + + if ((vr = VK_CALL(vkCreateSemaphore(device_vk->vk_device, + &semaphore_info, NULL, &swapchain_vk->vk_semaphores[i].presentable))) < 0) + { + ERR("Failed to create semaphore, vr %s.\n", wined3d_debug_vkresult(vr)); + goto fail; + } + } + swapchain_vk->image_count = image_count; + + return true; + +fail: + for (i = 0; i < image_count; ++i) + { + if (swapchain_vk->vk_semaphores[i].available) + VK_CALL(vkDestroySemaphore(device_vk->vk_device, swapchain_vk->vk_semaphores[i].available, NULL)); + if (swapchain_vk->vk_semaphores[i].presentable) + VK_CALL(vkDestroySemaphore(device_vk->vk_device, swapchain_vk->vk_semaphores[i].presentable, NULL)); + } + heap_free(swapchain_vk->vk_semaphores); + heap_free(swapchain_vk->vk_images); + return false; +} + +static HRESULT wined3d_swapchain_vk_create_vulkan_swapchain(struct wined3d_swapchain_vk *swapchain_vk) +{ + struct wined3d_device_vk *device_vk = wined3d_device_vk(swapchain_vk->s.device); + const struct wined3d_swapchain_desc *desc = &swapchain_vk->s.state.desc; + VkSwapchainCreateInfoKHR vk_swapchain_desc; + VkWin32SurfaceCreateInfoKHR surface_desc; + unsigned int width, height, image_count; + const struct wined3d_vk_info *vk_info; + VkSurfaceCapabilitiesKHR surface_caps; + struct wined3d_adapter_vk *adapter_vk; + VkPresentModeKHR vk_present_mode; + VkSwapchainKHR vk_swapchain; + VkImageUsageFlags usage; + VkSurfaceKHR vk_surface; + VkBool32 supported; + VkFormat vk_format; + VkResult vr; + + adapter_vk = wined3d_adapter_vk(device_vk->d.adapter); + vk_info = &adapter_vk->vk_info; + + surface_desc.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; + surface_desc.pNext = NULL; + surface_desc.flags = 0; + surface_desc.hinstance = (HINSTANCE)GetWindowLongPtrW(swapchain_vk->s.win_handle, GWLP_HINSTANCE); + surface_desc.hwnd = swapchain_vk->s.win_handle; + if ((vr = VK_CALL(vkCreateWin32SurfaceKHR(vk_info->instance, &surface_desc, NULL, &vk_surface))) < 0) + { + ERR("Failed to create Vulkan surface, vr %s.\n", wined3d_debug_vkresult(vr)); + return E_FAIL; + } + swapchain_vk->vk_surface = vk_surface; + + if ((vr = VK_CALL(vkGetPhysicalDeviceSurfaceSupportKHR(adapter_vk->physical_device, + device_vk->vk_queue_family_index, vk_surface, &supported))) < 0 || !supported) + { + ERR("Queue family does not support presentation on this surface, vr %s.\n", wined3d_debug_vkresult(vr)); + goto fail; + } + + if ((vk_format = wined3d_swapchain_vk_select_vk_format(swapchain_vk, vk_surface)) == VK_FORMAT_UNDEFINED) + { + ERR("Failed to select swapchain format.\n"); + goto fail; + } + + if ((vr = VK_CALL(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(adapter_vk->physical_device, + swapchain_vk->vk_surface, &surface_caps))) < 0) + { + ERR("Failed to get surface capabilities, vr %s.\n", wined3d_debug_vkresult(vr)); + goto fail; + } + + image_count = desc->backbuffer_count; + if (image_count < surface_caps.minImageCount) + image_count = surface_caps.minImageCount; + else if (surface_caps.maxImageCount && image_count > surface_caps.maxImageCount) + image_count = surface_caps.maxImageCount; + + if (image_count != desc->backbuffer_count) + WARN("Image count %u is not supported (%u-%u).\n", desc->backbuffer_count, + surface_caps.minImageCount, surface_caps.maxImageCount); + + width = desc->backbuffer_width; + if (width < surface_caps.minImageExtent.width) + width = surface_caps.minImageExtent.width; + else if (width > surface_caps.maxImageExtent.width) + width = surface_caps.maxImageExtent.width; + + height = desc->backbuffer_height; + if (height < surface_caps.minImageExtent.height) + height = surface_caps.minImageExtent.height; + else if (height > surface_caps.maxImageExtent.height) + height = surface_caps.maxImageExtent.height; + + if (width != desc->backbuffer_width || height != desc->backbuffer_height) + WARN("Swapchain dimensions %ux%u are not supported (%u-%u x %u-%u).\n", + desc->backbuffer_width, desc->backbuffer_height, + surface_caps.minImageExtent.width, surface_caps.maxImageExtent.width, + surface_caps.minImageExtent.height, surface_caps.maxImageExtent.height); + + usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + usage |= surface_caps.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + usage |= surface_caps.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_DST_BIT; + if (!(usage & VK_IMAGE_USAGE_TRANSFER_SRC_BIT) || !(usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT)) + WARN("Transfer not supported for swapchain images.\n"); + + if (!(surface_caps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR)) + { + FIXME("Unsupported alpha mode, %#x.\n", surface_caps.supportedCompositeAlpha); + goto fail; + } + + vk_present_mode = VK_PRESENT_MODE_FIFO_KHR; + if (!swapchain_vk->s.swap_interval) + { + if (wined3d_swapchain_vk_present_mode_supported(swapchain_vk, VK_PRESENT_MODE_IMMEDIATE_KHR)) + vk_present_mode = VK_PRESENT_MODE_IMMEDIATE_KHR; + else + FIXME("Unsupported swap interval %u.\n", swapchain_vk->s.swap_interval); + } + + vk_swapchain_desc.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + vk_swapchain_desc.pNext = NULL; + vk_swapchain_desc.flags = 0; + vk_swapchain_desc.surface = vk_surface; + vk_swapchain_desc.minImageCount = image_count; + vk_swapchain_desc.imageFormat = vk_format; + vk_swapchain_desc.imageColorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; + vk_swapchain_desc.imageExtent.width = width; + vk_swapchain_desc.imageExtent.height = height; + vk_swapchain_desc.imageArrayLayers = 1; + vk_swapchain_desc.imageUsage = usage; + vk_swapchain_desc.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + vk_swapchain_desc.queueFamilyIndexCount = 0; + vk_swapchain_desc.pQueueFamilyIndices = NULL; + vk_swapchain_desc.preTransform = surface_caps.currentTransform; + vk_swapchain_desc.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + vk_swapchain_desc.presentMode = vk_present_mode; + vk_swapchain_desc.clipped = VK_TRUE; + vk_swapchain_desc.oldSwapchain = VK_NULL_HANDLE; + if ((vr = VK_CALL(vkCreateSwapchainKHR(device_vk->vk_device, &vk_swapchain_desc, NULL, &vk_swapchain))) < 0) + { + ERR("Failed to create Vulkan swapchain, vr %s.\n", wined3d_debug_vkresult(vr)); + goto fail; + } + swapchain_vk->vk_swapchain = vk_swapchain; + + if (!wined3d_swapchain_vk_create_vulkan_swapchain_images(swapchain_vk, vk_swapchain)) + { + VK_CALL(vkDestroySwapchainKHR(device_vk->vk_device, vk_swapchain, NULL)); + goto fail; + } + + return WINED3D_OK; + +fail: + VK_CALL(vkDestroySurfaceKHR(vk_info->instance, vk_surface, NULL)); + return E_FAIL; +} + +static HRESULT wined3d_swapchain_vk_recreate(struct wined3d_swapchain_vk *swapchain_vk) +{ + TRACE("swapchain_vk %p.\n", swapchain_vk); + + wined3d_swapchain_vk_destroy_vulkan_swapchain(swapchain_vk); + + return wined3d_swapchain_vk_create_vulkan_swapchain(swapchain_vk); +} + +static void wined3d_swapchain_vk_set_swap_interval(struct wined3d_swapchain_vk *swapchain_vk, + unsigned int swap_interval) +{ + if (swap_interval > 1) + { + if (swap_interval <= 4) + FIXME("Unsupported swap interval %u.\n", swap_interval); + swap_interval = 1; + } + + if (swapchain_vk->s.swap_interval == swap_interval) + return; + + swapchain_vk->s.swap_interval = swap_interval; + wined3d_swapchain_vk_recreate(swapchain_vk); +} + +static void wined3d_swapchain_vk_blit(struct wined3d_swapchain_vk *swapchain_vk, + struct wined3d_context_vk *context_vk, const RECT *src_rect, const RECT *dst_rect, unsigned int swap_interval) +{ + struct wined3d_texture_vk *back_buffer_vk = wined3d_texture_vk(swapchain_vk->s.back_buffers[0]); + struct wined3d_device_vk *device_vk = wined3d_device_vk(swapchain_vk->s.device); + const struct wined3d_swapchain_desc *desc = &swapchain_vk->s.state.desc; + const struct wined3d_vk_info *vk_info = context_vk->vk_info; + VkCommandBuffer vk_command_buffer; + VkPresentInfoKHR present_desc; + unsigned int present_idx; + VkImageLayout vk_layout; + uint32_t image_idx; + VkImageBlit blit; + VkResult vr; + HRESULT hr; + + static const VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + + wined3d_swapchain_vk_set_swap_interval(swapchain_vk, swap_interval); + + present_idx = swapchain_vk->current++ % swapchain_vk->image_count; + wined3d_context_vk_wait_command_buffer(context_vk, swapchain_vk->vk_semaphores[present_idx].command_buffer_id); + vr = VK_CALL(vkAcquireNextImageKHR(device_vk->vk_device, swapchain_vk->vk_swapchain, UINT64_MAX, + swapchain_vk->vk_semaphores[present_idx].available, VK_NULL_HANDLE, &image_idx)); + if (vr == VK_ERROR_OUT_OF_DATE_KHR) + { + if (FAILED(hr = wined3d_swapchain_vk_recreate(swapchain_vk))) + { + ERR("Failed to recreate swapchain, hr %#x.\n", hr); + return; + } + vr = VK_CALL(vkAcquireNextImageKHR(device_vk->vk_device, swapchain_vk->vk_swapchain, UINT64_MAX, + swapchain_vk->vk_semaphores[present_idx].available, VK_NULL_HANDLE, &image_idx)); + } + if (vr < 0) + { + ERR("Failed to acquire next Vulkan image, vr %s.\n", wined3d_debug_vkresult(vr)); + return; + } + + vk_command_buffer = wined3d_context_vk_get_command_buffer(context_vk); + + wined3d_context_vk_end_current_render_pass(context_vk); + + wined3d_context_vk_image_barrier(context_vk, vk_command_buffer, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, + vk_access_mask_from_bind_flags(back_buffer_vk->t.resource.bind_flags), + VK_ACCESS_TRANSFER_READ_BIT, + back_buffer_vk->layout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + back_buffer_vk->vk_image, VK_IMAGE_ASPECT_COLOR_BIT); + + wined3d_context_vk_image_barrier(context_vk, vk_command_buffer, + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, VK_ACCESS_TRANSFER_WRITE_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + swapchain_vk->vk_images[image_idx], VK_IMAGE_ASPECT_COLOR_BIT); + + blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + blit.srcSubresource.mipLevel = 0; + blit.srcSubresource.baseArrayLayer = 0; + blit.srcSubresource.layerCount = 1; + blit.srcOffsets[0].x = src_rect->left; + blit.srcOffsets[0].y = src_rect->top; + blit.srcOffsets[0].z = 0; + blit.srcOffsets[1].x = src_rect->right; + blit.srcOffsets[1].y = src_rect->bottom; + blit.srcOffsets[1].z = 1; + blit.dstSubresource = blit.srcSubresource; + blit.dstOffsets[0].x = dst_rect->left; + blit.dstOffsets[0].y = dst_rect->top; + blit.dstOffsets[0].z = 0; + blit.dstOffsets[1].x = dst_rect->right; + blit.dstOffsets[1].y = dst_rect->bottom; + blit.dstOffsets[1].z = 1; + VK_CALL(vkCmdBlitImage(vk_command_buffer, + back_buffer_vk->vk_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + swapchain_vk->vk_images[image_idx], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, &blit, VK_FILTER_NEAREST)); + + wined3d_context_vk_reference_texture(context_vk, back_buffer_vk); + wined3d_context_vk_image_barrier(context_vk, vk_command_buffer, + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, + VK_ACCESS_TRANSFER_WRITE_BIT, 0, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, + swapchain_vk->vk_images[image_idx], VK_IMAGE_ASPECT_COLOR_BIT); + + if (desc->swap_effect == WINED3D_SWAP_EFFECT_DISCARD || desc->swap_effect == WINED3D_SWAP_EFFECT_FLIP_DISCARD) + vk_layout = VK_IMAGE_LAYOUT_UNDEFINED; + else + vk_layout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + wined3d_context_vk_image_barrier(context_vk, vk_command_buffer, + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + VK_ACCESS_TRANSFER_READ_BIT, + vk_access_mask_from_bind_flags(back_buffer_vk->t.resource.bind_flags), + vk_layout, back_buffer_vk->layout, + back_buffer_vk->vk_image, VK_IMAGE_ASPECT_COLOR_BIT); + back_buffer_vk->bind_mask = 0; + + swapchain_vk->vk_semaphores[present_idx].command_buffer_id = context_vk->current_command_buffer.id; + wined3d_context_vk_submit_command_buffer(context_vk, + 1, &swapchain_vk->vk_semaphores[present_idx].available, &wait_stage, + 1, &swapchain_vk->vk_semaphores[present_idx].presentable); + + present_desc.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + present_desc.pNext = NULL; + present_desc.waitSemaphoreCount = 1; + present_desc.pWaitSemaphores = &swapchain_vk->vk_semaphores[present_idx].presentable; + present_desc.swapchainCount = 1; + present_desc.pSwapchains = &swapchain_vk->vk_swapchain; + present_desc.pImageIndices = &image_idx; + present_desc.pResults = NULL; + if ((vr = VK_CALL(vkQueuePresentKHR(device_vk->vk_queue, &present_desc)))) + ERR("Present returned vr %s.\n", wined3d_debug_vkresult(vr)); +} + +static void wined3d_swapchain_vk_rotate(struct wined3d_swapchain *swapchain, struct wined3d_context_vk *context_vk) +{ + struct wined3d_texture_sub_resource *sub_resource; + struct wined3d_texture_vk *texture, *texture_prev; + struct wined3d_allocator_block *memory0; + VkDescriptorImageInfo vk_info0; + VkDeviceMemory vk_memory0; + VkImageLayout vk_layout0; + VkImage vk_image0; + DWORD locations0; + unsigned int i; + uint64_t id0; + + static const DWORD supported_locations = WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_RB_MULTISAMPLE; + + if (swapchain->state.desc.backbuffer_count < 2) + return; + + texture_prev = wined3d_texture_vk(swapchain->back_buffers[0]); + + /* Back buffer 0 is already in the draw binding. */ + vk_image0 = texture_prev->vk_image; + memory0 = texture_prev->memory; + vk_memory0 = texture_prev->vk_memory; + vk_layout0 = texture_prev->layout; + id0 = texture_prev->command_buffer_id; + vk_info0 = texture_prev->default_image_info; + locations0 = texture_prev->t.sub_resources[0].locations; + + for (i = 1; i < swapchain->state.desc.backbuffer_count; ++i) + { + texture = wined3d_texture_vk(swapchain->back_buffers[i]); + sub_resource = &texture->t.sub_resources[0]; + + if (!(sub_resource->locations & supported_locations)) + wined3d_texture_load_location(&texture->t, 0, &context_vk->c, texture->t.resource.draw_binding); + + texture_prev->vk_image = texture->vk_image; + texture_prev->memory = texture->memory; + texture_prev->vk_memory = texture->vk_memory; + texture_prev->layout = texture->layout; + texture_prev->command_buffer_id = texture->command_buffer_id; + texture_prev->default_image_info = texture->default_image_info; + + wined3d_texture_validate_location(&texture_prev->t, 0, sub_resource->locations & supported_locations); + wined3d_texture_invalidate_location(&texture_prev->t, 0, ~(sub_resource->locations & supported_locations)); + + texture_prev = texture; + } + + texture_prev->vk_image = vk_image0; + texture_prev->memory = memory0; + texture_prev->vk_memory = vk_memory0; + texture_prev->layout = vk_layout0; + texture_prev->command_buffer_id = id0; + texture_prev->default_image_info = vk_info0; + + wined3d_texture_validate_location(&texture_prev->t, 0, locations0 & supported_locations); + wined3d_texture_invalidate_location(&texture_prev->t, 0, ~(locations0 & supported_locations)); + + device_invalidate_state(swapchain->device, STATE_FRAMEBUFFER); +} + +static void swapchain_vk_present(struct wined3d_swapchain *swapchain, const RECT *src_rect, + const RECT *dst_rect, unsigned int swap_interval, uint32_t flags) +{ + struct wined3d_swapchain_vk *swapchain_vk = wined3d_swapchain_vk(swapchain); + struct wined3d_texture *back_buffer = swapchain->back_buffers[0]; + struct wined3d_context_vk *context_vk; + + context_vk = wined3d_context_vk(context_acquire(swapchain->device, back_buffer, 0)); + + wined3d_texture_load_location(back_buffer, 0, &context_vk->c, back_buffer->resource.draw_binding); + + if (swapchain_vk->vk_swapchain) + wined3d_swapchain_vk_blit(swapchain_vk, context_vk, src_rect, dst_rect, swap_interval); + + wined3d_swapchain_vk_rotate(swapchain, context_vk); + + wined3d_texture_validate_location(swapchain->front_buffer, 0, WINED3D_LOCATION_DRAWABLE); + wined3d_texture_invalidate_location(swapchain->front_buffer, 0, ~WINED3D_LOCATION_DRAWABLE); + + TRACE("Starting new frame.\n"); + + context_release(&context_vk->c); +} + +static const struct wined3d_swapchain_ops swapchain_vk_ops = +{ + swapchain_vk_present, + swapchain_frontbuffer_updated, +}; + +static void swapchain_gdi_frontbuffer_updated(struct wined3d_swapchain *swapchain) +{ + struct wined3d_dc_info *front; + POINT offset = {0, 0}; + HDC src_dc, dst_dc; + RECT draw_rect; + HWND window; + + TRACE("swapchain %p.\n", swapchain); + + front = &swapchain->front_buffer->dc_info[0]; + if (swapchain->palette) + wined3d_palette_apply_to_dc(swapchain->palette, front->dc); + + if (swapchain->front_buffer->resource.map_count) + ERR("Trying to blit a mapped surface.\n"); + + TRACE("Copying surface %p to screen.\n", front); + + src_dc = front->dc; + window = swapchain->win_handle; + dst_dc = GetDCEx(window, 0, DCX_CLIPSIBLINGS | DCX_CACHE); + + /* Front buffer coordinates are screen coordinates. Map them to the + * destination window if not fullscreened. */ + if (swapchain->state.desc.windowed) + ClientToScreen(window, &offset); + + TRACE("offset %s.\n", wine_dbgstr_point(&offset)); + + SetRect(&draw_rect, 0, 0, swapchain->front_buffer->resource.width, + swapchain->front_buffer->resource.height); + IntersectRect(&draw_rect, &draw_rect, &swapchain->front_buffer_update); + + BitBlt(dst_dc, draw_rect.left - offset.x, draw_rect.top - offset.y, + draw_rect.right - draw_rect.left, draw_rect.bottom - draw_rect.top, + src_dc, draw_rect.left, draw_rect.top, SRCCOPY); + ReleaseDC(window, dst_dc); + + SetRectEmpty(&swapchain->front_buffer_update); +} + +static void swapchain_gdi_present(struct wined3d_swapchain *swapchain, + const RECT *src_rect, const RECT *dst_rect, unsigned int swap_interval, DWORD flags) +{ + struct wined3d_dc_info *front, *back; + HBITMAP bitmap; + void *data; + HDC dc; + + front = &swapchain->front_buffer->dc_info[0]; + back = &swapchain->back_buffers[0]->dc_info[0]; + + /* Flip the surface data. */ + dc = front->dc; + bitmap = front->bitmap; + data = swapchain->front_buffer->resource.heap_memory; + + front->dc = back->dc; + front->bitmap = back->bitmap; + swapchain->front_buffer->resource.heap_memory = swapchain->back_buffers[0]->resource.heap_memory; + + back->dc = dc; + back->bitmap = bitmap; + swapchain->back_buffers[0]->resource.heap_memory = data; + + SetRect(&swapchain->front_buffer_update, 0, 0, + swapchain->front_buffer->resource.width, + swapchain->front_buffer->resource.height); + swapchain_gdi_frontbuffer_updated(swapchain); +} + +static const struct wined3d_swapchain_ops swapchain_no3d_ops = +{ + swapchain_gdi_present, + swapchain_gdi_frontbuffer_updated, +}; + +static void swapchain_update_render_to_fbo(struct wined3d_swapchain *swapchain) +{ + if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) + return; + + if (!swapchain->state.desc.backbuffer_count) + { + TRACE("Single buffered rendering.\n"); + swapchain->render_to_fbo = FALSE; + return; + } + + TRACE("Rendering to FBO.\n"); + swapchain->render_to_fbo = TRUE; +} + +static void wined3d_swapchain_apply_sample_count_override(const struct wined3d_swapchain *swapchain, + enum wined3d_format_id format_id, enum wined3d_multisample_type *type, DWORD *quality) +{ + const struct wined3d_adapter *adapter; + const struct wined3d_gl_info *gl_info; + const struct wined3d_format *format; + enum wined3d_multisample_type t; + + if (wined3d_settings.sample_count == ~0u) + return; + + adapter = swapchain->device->adapter; + gl_info = &adapter->gl_info; + if (!(format = wined3d_get_format(adapter, format_id, WINED3D_BIND_RENDER_TARGET))) + return; + + if ((t = min(wined3d_settings.sample_count, gl_info->limits.samples))) + while (!(format->multisample_types & 1u << (t - 1))) + ++t; + TRACE("Using sample count %u.\n", t); + *type = t; + *quality = 0; +} + +void swapchain_set_max_frame_latency(struct wined3d_swapchain *swapchain, const struct wined3d_device *device) +{ + /* Subtract 1 for the implicit OpenGL latency. */ + swapchain->max_frame_latency = device->max_frame_latency >= 2 ? device->max_frame_latency - 1 : 1; +} + +static enum wined3d_format_id adapter_format_from_backbuffer_format(const struct wined3d_adapter *adapter, + enum wined3d_format_id format_id) +{ + const struct wined3d_format *backbuffer_format; + + backbuffer_format = wined3d_get_format(adapter, format_id, WINED3D_BIND_RENDER_TARGET); + return pixelformat_for_depth(backbuffer_format->byte_count * CHAR_BIT); +} + +static HRESULT wined3d_swapchain_state_init(struct wined3d_swapchain_state *state, + const struct wined3d_swapchain_desc *desc, HWND window, struct wined3d *wined3d, + struct wined3d_swapchain_state_parent *parent) +{ + HRESULT hr; + + state->desc = *desc; + + if (FAILED(hr = wined3d_output_get_display_mode(desc->output, &state->original_mode, NULL))) + { + ERR("Failed to get current display mode, hr %#x.\n", hr); + return hr; + } + + if (!desc->windowed) + { + if (desc->flags & WINED3D_SWAPCHAIN_ALLOW_MODE_SWITCH) + { + state->d3d_mode.width = desc->backbuffer_width; + state->d3d_mode.height = desc->backbuffer_height; + state->d3d_mode.format_id = adapter_format_from_backbuffer_format(desc->output->adapter, + desc->backbuffer_format); + state->d3d_mode.refresh_rate = desc->refresh_rate; + state->d3d_mode.scanline_ordering = WINED3D_SCANLINE_ORDERING_UNKNOWN; + } + else + { + state->d3d_mode = state->original_mode; + } + } + + GetWindowRect(window, &state->original_window_rect); + state->wined3d = wined3d; + state->device_window = window; + state->parent = parent; + + if (desc->flags & WINED3D_SWAPCHAIN_REGISTER_STATE) + wined3d_swapchain_state_register(state); + + return hr; +} + +static HRESULT wined3d_swapchain_init(struct wined3d_swapchain *swapchain, struct wined3d_device *device, + struct wined3d_swapchain_desc *desc, struct wined3d_swapchain_state_parent *state_parent, + void *parent, const struct wined3d_parent_ops *parent_ops, + const struct wined3d_swapchain_ops *swapchain_ops) +{ + struct wined3d_resource_desc texture_desc; + struct wined3d_output_desc output_desc; + BOOL displaymode_set = FALSE; + DWORD texture_flags = 0; + HRESULT hr = E_FAIL; + RECT client_rect; + unsigned int i; + HWND window; + + wined3d_mutex_lock(); + + if (desc->backbuffer_count > 1) + { + FIXME("The application requested more than one back buffer, this is not properly supported.\n" + "Please configure the application to use double buffering (1 back buffer) if possible.\n"); + } + + if (desc->swap_effect != WINED3D_SWAP_EFFECT_DISCARD + && desc->swap_effect != WINED3D_SWAP_EFFECT_SEQUENTIAL + && desc->swap_effect != WINED3D_SWAP_EFFECT_COPY) + FIXME("Unimplemented swap effect %#x.\n", desc->swap_effect); + + window = desc->device_window ? desc->device_window : device->create_parms.focus_window; + if (FAILED(hr = wined3d_swapchain_state_init(&swapchain->state, desc, window, device->wined3d, state_parent))) + { + ERR("Failed to initialise swapchain state, hr %#x.\n", hr); + wined3d_mutex_unlock(); + return hr; + } + + swapchain->swapchain_ops = swapchain_ops; + swapchain->device = device; + swapchain->parent = parent; + swapchain->parent_ops = parent_ops; + swapchain->ref = 1; + swapchain->win_handle = window; + swapchain->swap_interval = WINED3D_SWAP_INTERVAL_DEFAULT; + swapchain_set_max_frame_latency(swapchain, device); + + GetClientRect(window, &client_rect); + if (desc->windowed) + { + TRACE("Client rect %s.\n", wine_dbgstr_rect(&client_rect)); + + if (!desc->backbuffer_width) + { + desc->backbuffer_width = client_rect.right ? client_rect.right : 8; + TRACE("Updating width to %u.\n", desc->backbuffer_width); + } + if (!desc->backbuffer_height) + { + desc->backbuffer_height = client_rect.bottom ? client_rect.bottom : 8; + TRACE("Updating height to %u.\n", desc->backbuffer_height); + } + + if (desc->backbuffer_format == WINED3DFMT_UNKNOWN) + { + desc->backbuffer_format = swapchain->state.original_mode.format_id; + TRACE("Updating format to %s.\n", debug_d3dformat(swapchain->state.original_mode.format_id)); + } + } + else + { + if (FAILED(hr = wined3d_output_get_desc(desc->output, &output_desc))) + { + ERR("Failed to get output description, hr %#x.\n", hr); + goto err; + } + + wined3d_swapchain_state_setup_fullscreen(&swapchain->state, window, + output_desc.desktop_rect.left, output_desc.desktop_rect.top, desc->backbuffer_width, + desc->backbuffer_height); + } + swapchain->state.desc = *desc; + wined3d_swapchain_apply_sample_count_override(swapchain, swapchain->state.desc.backbuffer_format, + &swapchain->state.desc.multisample_type, &swapchain->state.desc.multisample_quality); + swapchain_update_render_to_fbo(swapchain); + + TRACE("Creating front buffer.\n"); + + texture_desc.resource_type = WINED3D_RTYPE_TEXTURE_2D; + texture_desc.format = swapchain->state.desc.backbuffer_format; + texture_desc.multisample_type = swapchain->state.desc.multisample_type; + texture_desc.multisample_quality = swapchain->state.desc.multisample_quality; + texture_desc.usage = 0; + if (device->wined3d->flags & WINED3D_NO3D) + texture_desc.usage |= WINED3DUSAGE_OWNDC; + texture_desc.bind_flags = 0; + if (device->wined3d->flags & WINED3D_NO3D) + texture_desc.access = WINED3D_RESOURCE_ACCESS_CPU; + else + texture_desc.access = WINED3D_RESOURCE_ACCESS_GPU; + if (swapchain->state.desc.flags & WINED3D_SWAPCHAIN_LOCKABLE_BACKBUFFER) + texture_desc.access |= WINED3D_RESOURCE_ACCESS_MAP_R | WINED3D_RESOURCE_ACCESS_MAP_W; + texture_desc.width = swapchain->state.desc.backbuffer_width; + texture_desc.height = swapchain->state.desc.backbuffer_height; + texture_desc.depth = 1; + texture_desc.size = 0; + + if (swapchain->state.desc.flags & WINED3D_SWAPCHAIN_GDI_COMPATIBLE) + texture_flags |= WINED3D_TEXTURE_CREATE_GET_DC; + + if (FAILED(hr = device->device_parent->ops->create_swapchain_texture(device->device_parent, + parent, &texture_desc, texture_flags, &swapchain->front_buffer))) + { + WARN("Failed to create front buffer, hr %#x.\n", hr); + goto err; + } + + wined3d_texture_set_swapchain(swapchain->front_buffer, swapchain); + if (!(device->wined3d->flags & WINED3D_NO3D)) + { + wined3d_texture_validate_location(swapchain->front_buffer, 0, WINED3D_LOCATION_DRAWABLE); + wined3d_texture_invalidate_location(swapchain->front_buffer, 0, ~WINED3D_LOCATION_DRAWABLE); + } + + /* MSDN says we're only allowed a single fullscreen swapchain per device, + * so we should really check to see if there is a fullscreen swapchain + * already. Does a single head count as full screen? */ + if (!desc->windowed && desc->flags & WINED3D_SWAPCHAIN_ALLOW_MODE_SWITCH) + { + /* Change the display settings */ + if (FAILED(hr = wined3d_output_set_display_mode(desc->output, + &swapchain->state.d3d_mode))) + { + WARN("Failed to set display mode, hr %#x.\n", hr); + goto err; + } + displaymode_set = TRUE; + } + + if (swapchain->state.desc.backbuffer_count > 0) + { + if (!(swapchain->back_buffers = heap_calloc(swapchain->state.desc.backbuffer_count, + sizeof(*swapchain->back_buffers)))) + { + ERR("Failed to allocate backbuffer array memory.\n"); + hr = E_OUTOFMEMORY; + goto err; + } + + texture_desc.bind_flags = swapchain->state.desc.backbuffer_bind_flags; + texture_desc.usage = 0; + if (device->wined3d->flags & WINED3D_NO3D) + texture_desc.usage |= WINED3DUSAGE_OWNDC; + for (i = 0; i < swapchain->state.desc.backbuffer_count; ++i) + { + TRACE("Creating back buffer %u.\n", i); + if (FAILED(hr = device->device_parent->ops->create_swapchain_texture(device->device_parent, + parent, &texture_desc, texture_flags, &swapchain->back_buffers[i]))) + { + WARN("Failed to create back buffer %u, hr %#x.\n", i, hr); + swapchain->state.desc.backbuffer_count = i; + goto err; + } + wined3d_texture_set_swapchain(swapchain->back_buffers[i], swapchain); + } + } + + /* Swapchains share the depth/stencil buffer, so only create a single depthstencil surface. */ + if (desc->enable_auto_depth_stencil) + { + TRACE("Creating depth/stencil buffer.\n"); + if (!device->auto_depth_stencil_view) + { + struct wined3d_view_desc desc; + struct wined3d_texture *ds; + + texture_desc.format = swapchain->state.desc.auto_depth_stencil_format; + texture_desc.usage = 0; + texture_desc.bind_flags = WINED3D_BIND_DEPTH_STENCIL; + if (device->wined3d->flags & WINED3D_NO3D) + texture_desc.access = WINED3D_RESOURCE_ACCESS_CPU; + else + texture_desc.access = WINED3D_RESOURCE_ACCESS_GPU; + + if (FAILED(hr = device->device_parent->ops->create_swapchain_texture(device->device_parent, + device->device_parent, &texture_desc, 0, &ds))) + { + WARN("Failed to create the auto depth/stencil surface, hr %#x.\n", hr); + goto err; + } + + desc.format_id = ds->resource.format->id; + desc.flags = 0; + desc.u.texture.level_idx = 0; + desc.u.texture.level_count = 1; + desc.u.texture.layer_idx = 0; + desc.u.texture.layer_count = 1; + hr = wined3d_rendertarget_view_create(&desc, &ds->resource, NULL, &wined3d_null_parent_ops, + &device->auto_depth_stencil_view); + wined3d_texture_decref(ds); + if (FAILED(hr)) + { + ERR("Failed to create rendertarget view, hr %#x.\n", hr); + goto err; + } + } + } + + wined3d_swapchain_get_gamma_ramp(swapchain, &swapchain->orig_gamma); + + wined3d_mutex_unlock(); + + return WINED3D_OK; + +err: + if (displaymode_set) + { + if (FAILED(wined3d_restore_display_modes(device->wined3d))) + ERR("Failed to restore display mode.\n"); + } + + if (swapchain->back_buffers) + { + for (i = 0; i < swapchain->state.desc.backbuffer_count; ++i) + { + if (swapchain->back_buffers[i]) + { + wined3d_texture_set_swapchain(swapchain->back_buffers[i], NULL); + wined3d_texture_decref(swapchain->back_buffers[i]); + } + } + heap_free(swapchain->back_buffers); + } + + if (swapchain->front_buffer) + { + wined3d_texture_set_swapchain(swapchain->front_buffer, NULL); + wined3d_texture_decref(swapchain->front_buffer); + } + + wined3d_swapchain_state_cleanup(&swapchain->state); + wined3d_mutex_unlock(); + + return hr; +} + +HRESULT wined3d_swapchain_no3d_init(struct wined3d_swapchain *swapchain_no3d, struct wined3d_device *device, + struct wined3d_swapchain_desc *desc, struct wined3d_swapchain_state_parent *state_parent, + void *parent, const struct wined3d_parent_ops *parent_ops) +{ + TRACE("swapchain_no3d %p, device %p, desc %p, state_parent %p, parent %p, parent_ops %p.\n", + swapchain_no3d, device, desc, state_parent, parent, parent_ops); + + return wined3d_swapchain_init(swapchain_no3d, device, desc, state_parent, parent, parent_ops, + &swapchain_no3d_ops); +} + +HRESULT wined3d_swapchain_gl_init(struct wined3d_swapchain_gl *swapchain_gl, struct wined3d_device *device, + struct wined3d_swapchain_desc *desc, struct wined3d_swapchain_state_parent *state_parent, + void *parent, const struct wined3d_parent_ops *parent_ops) +{ + HRESULT hr; + + TRACE("swapchain_gl %p, device %p, desc %p, state_parent %p, parent %p, parent_ops %p.\n", + swapchain_gl, device, desc, state_parent, parent, parent_ops); + + if (FAILED(hr = wined3d_swapchain_init(&swapchain_gl->s, device, desc, state_parent, parent, + parent_ops, &swapchain_gl_ops))) + { + /* Cleanup any context that may have been created for the swapchain. */ + wined3d_cs_destroy_object(device->cs, wined3d_swapchain_gl_destroy_object, swapchain_gl); + wined3d_cs_finish(device->cs, WINED3D_CS_QUEUE_DEFAULT); + } + + return hr; +} + +HRESULT wined3d_swapchain_vk_init(struct wined3d_swapchain_vk *swapchain_vk, struct wined3d_device *device, + struct wined3d_swapchain_desc *desc, struct wined3d_swapchain_state_parent *state_parent, + void *parent, const struct wined3d_parent_ops *parent_ops) +{ + HRESULT hr; + + TRACE("swapchain_vk %p, device %p, desc %p, parent %p, parent_ops %p.\n", + swapchain_vk, device, desc, parent, parent_ops); + + if (FAILED(hr = wined3d_swapchain_init(&swapchain_vk->s, device, desc, state_parent, parent, + parent_ops, &swapchain_vk_ops))) + return hr; + + if (swapchain_vk->s.win_handle == GetDesktopWindow()) + { + WARN("Creating a desktop window swapchain.\n"); + return hr; + } + + if (FAILED(hr = wined3d_swapchain_vk_create_vulkan_swapchain(swapchain_vk))) + { + wined3d_swapchain_cleanup(&swapchain_vk->s); + return hr; + } + + return hr; +} + +HRESULT CDECL wined3d_swapchain_create(struct wined3d_device *device, + struct wined3d_swapchain_desc *desc, struct wined3d_swapchain_state_parent *state_parent, + void *parent, const struct wined3d_parent_ops *parent_ops, + struct wined3d_swapchain **swapchain) +{ + struct wined3d_swapchain *object; + HRESULT hr; + + if (FAILED(hr = device->adapter->adapter_ops->adapter_create_swapchain(device, + desc, state_parent, parent, parent_ops, &object))) + return hr; + + if (desc->flags & WINED3D_SWAPCHAIN_IMPLICIT) + { + wined3d_mutex_lock(); + if (FAILED(hr = wined3d_device_set_implicit_swapchain(device, object))) + { + wined3d_cs_finish(device->cs, WINED3D_CS_QUEUE_DEFAULT); + device->adapter->adapter_ops->adapter_destroy_swapchain(object); + wined3d_mutex_unlock(); + return hr; + } + wined3d_mutex_unlock(); + } + + *swapchain = object; + + return hr; +} + +static struct wined3d_context_gl *wined3d_swapchain_gl_create_context(struct wined3d_swapchain_gl *swapchain_gl) +{ + struct wined3d_device *device = swapchain_gl->s.device; + struct wined3d_context_gl *context_gl; + + TRACE("Creating a new context for swapchain %p, thread %u.\n", swapchain_gl, GetCurrentThreadId()); + + wined3d_from_cs(device->cs); + + if (!(context_gl = heap_alloc_zero(sizeof(*context_gl)))) + { + ERR("Failed to allocate context memory.\n"); + return NULL; + } + + if (FAILED(wined3d_context_gl_init(context_gl, swapchain_gl))) + { + WARN("Failed to initialise context.\n"); + heap_free(context_gl); + return NULL; + } + + if (!device_context_add(device, &context_gl->c)) + { + ERR("Failed to add the newly created context to the context list.\n"); + wined3d_context_gl_destroy(context_gl); + return NULL; + } + + TRACE("Created context %p.\n", context_gl); + + context_release(&context_gl->c); + + if (!wined3d_array_reserve((void **)&swapchain_gl->contexts, &swapchain_gl->contexts_size, + swapchain_gl->context_count + 1, sizeof(*swapchain_gl->contexts))) + { + ERR("Failed to allocate new context array memory.\n"); + wined3d_context_gl_destroy(context_gl); + return NULL; + } + swapchain_gl->contexts[swapchain_gl->context_count++] = context_gl; + + return context_gl; +} + +void wined3d_swapchain_gl_destroy_contexts(struct wined3d_swapchain_gl *swapchain_gl) +{ + unsigned int i; + + TRACE("swapchain_gl %p.\n", swapchain_gl); + + for (i = 0; i < swapchain_gl->context_count; ++i) + { + wined3d_context_gl_destroy(swapchain_gl->contexts[i]); + } + heap_free(swapchain_gl->contexts); + swapchain_gl->contexts_size = 0; + swapchain_gl->context_count = 0; + swapchain_gl->contexts = NULL; +} + +struct wined3d_context_gl *wined3d_swapchain_gl_get_context(struct wined3d_swapchain_gl *swapchain_gl) +{ + DWORD tid = GetCurrentThreadId(); + unsigned int i; + + for (i = 0; i < swapchain_gl->context_count; ++i) + { + if (swapchain_gl->contexts[i]->tid == tid) + return swapchain_gl->contexts[i]; + } + + /* Create a new context for the thread. */ + return wined3d_swapchain_gl_create_context(swapchain_gl); +} + +HDC wined3d_swapchain_gl_get_backup_dc(struct wined3d_swapchain_gl *swapchain_gl) +{ + if (!swapchain_gl->backup_dc) + { + TRACE("Creating the backup window for swapchain %p.\n", swapchain_gl); + + if (!(swapchain_gl->backup_wnd = CreateWindowA(WINED3D_OPENGL_WINDOW_CLASS_NAME, "WineD3D fake window", + WS_OVERLAPPEDWINDOW, 10, 10, 10, 10, NULL, NULL, NULL, NULL))) + { + ERR("Failed to create a window.\n"); + return NULL; + } + + if (!(swapchain_gl->backup_dc = GetDC(swapchain_gl->backup_wnd))) + { + ERR("Failed to get a DC.\n"); + DestroyWindow(swapchain_gl->backup_wnd); + swapchain_gl->backup_wnd = NULL; + return NULL; + } + } + + return swapchain_gl->backup_dc; +} + +void swapchain_update_draw_bindings(struct wined3d_swapchain *swapchain) +{ + UINT i; + + wined3d_resource_update_draw_binding(&swapchain->front_buffer->resource); + + for (i = 0; i < swapchain->state.desc.backbuffer_count; ++i) + { + wined3d_resource_update_draw_binding(&swapchain->back_buffers[i]->resource); + } +} + +void wined3d_swapchain_activate(struct wined3d_swapchain *swapchain, BOOL activate) +{ + struct wined3d_device *device = swapchain->device; + HWND window = swapchain->state.device_window; + struct wined3d_output_desc output_desc; + unsigned int screensaver_active; + struct wined3d_output *output; + BOOL focus_messages, filter; + HRESULT hr; + + /* This code is not protected by the wined3d mutex, so it may run while + * wined3d_device_reset is active. Testing on Windows shows that changing + * focus during resets and resetting during focus change events causes + * the application to crash with an invalid memory access. */ + + if (!(focus_messages = device->wined3d->flags & WINED3D_FOCUS_MESSAGES)) + filter = wined3d_filter_messages(window, TRUE); + + if (activate) + { + SystemParametersInfoW(SPI_GETSCREENSAVEACTIVE, 0, &screensaver_active, 0); + if ((device->restore_screensaver = !!screensaver_active)) + SystemParametersInfoW(SPI_SETSCREENSAVEACTIVE, FALSE, NULL, 0); + + if (!(device->create_parms.flags & WINED3DCREATE_NOWINDOWCHANGES)) + { + /* The d3d versions do not agree on the exact messages here. D3d8 restores + * the window but leaves the size untouched, d3d9 sets the size on an + * invisible window, generates messages but doesn't change the window + * properties. The implementation follows d3d9. + * + * Guild Wars 1 wants a WINDOWPOSCHANGED message on the device window to + * resume drawing after a focus loss. */ + output = wined3d_swapchain_get_output(swapchain); + if (!output) + { + ERR("Failed to get output from swapchain %p.\n", swapchain); + return; + } + + if (SUCCEEDED(hr = wined3d_output_get_desc(output, &output_desc))) + SetWindowPos(window, NULL, output_desc.desktop_rect.left, + output_desc.desktop_rect.top, swapchain->state.desc.backbuffer_width, + swapchain->state.desc.backbuffer_height, SWP_NOACTIVATE | SWP_NOZORDER); + else + ERR("Failed to get output description, hr %#x.\n", hr); + } + + if (device->wined3d->flags & WINED3D_RESTORE_MODE_ON_ACTIVATE) + { + output = wined3d_swapchain_get_output(swapchain); + if (!output) + { + ERR("Failed to get output from swapchain %p.\n", swapchain); + return; + } + + if (FAILED(hr = wined3d_output_set_display_mode(output, + &swapchain->state.d3d_mode))) + ERR("Failed to set display mode, hr %#x.\n", hr); + } + + if (swapchain == device->swapchains[0]) + device->device_parent->ops->activate(device->device_parent, TRUE); + } + else + { + if (device->restore_screensaver) + { + SystemParametersInfoW(SPI_SETSCREENSAVEACTIVE, TRUE, NULL, 0); + device->restore_screensaver = FALSE; + } + + if (FAILED(hr = wined3d_restore_display_modes(device->wined3d))) + ERR("Failed to restore display modes, hr %#x.\n", hr); + + swapchain->reapply_mode = TRUE; + + /* Some DDraw apps (Deus Ex: GOTY, and presumably all UT 1 based games) destroy the device + * during window minimization. Do our housekeeping now, as the device may not exist after + * the ShowWindow call. + * + * In d3d9, the device is marked lost after the window is minimized. If we find an app + * that needs this behavior (e.g. because it calls TestCooperativeLevel in the window proc) + * we'll have to control this via a create flag. Note that the device and swapchain are not + * safe to access after the ShowWindow call. */ + if (swapchain == device->swapchains[0]) + device->device_parent->ops->activate(device->device_parent, FALSE); + + if (!(device->create_parms.flags & WINED3DCREATE_NOWINDOWCHANGES) && IsWindowVisible(window)) + ShowWindow(window, SW_MINIMIZE); + } + + if (!focus_messages) + wined3d_filter_messages(window, filter); +} + +HRESULT CDECL wined3d_swapchain_resize_buffers(struct wined3d_swapchain *swapchain, unsigned int buffer_count, + unsigned int width, unsigned int height, enum wined3d_format_id format_id, + enum wined3d_multisample_type multisample_type, unsigned int multisample_quality) +{ + struct wined3d_swapchain_desc *desc = &swapchain->state.desc; + BOOL update_desc = FALSE; + + TRACE("swapchain %p, buffer_count %u, width %u, height %u, format %s, " + "multisample_type %#x, multisample_quality %#x.\n", + swapchain, buffer_count, width, height, debug_d3dformat(format_id), + multisample_type, multisample_quality); + + wined3d_swapchain_apply_sample_count_override(swapchain, format_id, &multisample_type, &multisample_quality); + + if (buffer_count && buffer_count != desc->backbuffer_count) + FIXME("Cannot change the back buffer count yet.\n"); + + wined3d_cs_finish(swapchain->device->cs, WINED3D_CS_QUEUE_DEFAULT); + + if (!width || !height) + { + /* The application is requesting that either the swapchain width or + * height be set to the corresponding dimension in the window's + * client rect. */ + + RECT client_rect; + + if (!desc->windowed) + return WINED3DERR_INVALIDCALL; + + if (!GetClientRect(swapchain->state.device_window, &client_rect)) + { + ERR("Failed to get client rect, last error %#x.\n", GetLastError()); + return WINED3DERR_INVALIDCALL; + } + + if (!width) + width = client_rect.right; + + if (!height) + height = client_rect.bottom; + } + + if (width != desc->backbuffer_width || height != desc->backbuffer_height) + { + desc->backbuffer_width = width; + desc->backbuffer_height = height; + update_desc = TRUE; + } + + if (format_id == WINED3DFMT_UNKNOWN) + { + if (!desc->windowed) + return WINED3DERR_INVALIDCALL; + format_id = swapchain->state.original_mode.format_id; + } + + if (format_id != desc->backbuffer_format) + { + desc->backbuffer_format = format_id; + update_desc = TRUE; + } + + if (multisample_type != desc->multisample_type + || multisample_quality != desc->multisample_quality) + { + desc->multisample_type = multisample_type; + desc->multisample_quality = multisample_quality; + update_desc = TRUE; + } + + if (update_desc) + { + HRESULT hr; + UINT i; + + if (FAILED(hr = wined3d_texture_update_desc(swapchain->front_buffer, 0, desc->backbuffer_width, + desc->backbuffer_height, desc->backbuffer_format, + desc->multisample_type, desc->multisample_quality, NULL, 0))) + return hr; + + for (i = 0; i < desc->backbuffer_count; ++i) + { + if (FAILED(hr = wined3d_texture_update_desc(swapchain->back_buffers[i], 0, desc->backbuffer_width, + desc->backbuffer_height, desc->backbuffer_format, + desc->multisample_type, desc->multisample_quality, NULL, 0))) + return hr; + } + } + + swapchain_update_render_to_fbo(swapchain); + swapchain_update_draw_bindings(swapchain); + + return WINED3D_OK; +} + +static HRESULT wined3d_swapchain_state_set_display_mode(struct wined3d_swapchain_state *state, + struct wined3d_output *output, struct wined3d_display_mode *mode) +{ + HRESULT hr; + + if (state->desc.flags & WINED3D_SWAPCHAIN_USE_CLOSEST_MATCHING_MODE) + { + if (FAILED(hr = wined3d_output_find_closest_matching_mode(output, mode))) + { + WARN("Failed to find closest matching mode, hr %#x.\n", hr); + } + } + + if (output != state->desc.output) + { + if (FAILED(hr = wined3d_restore_display_modes(state->wined3d))) + { + WARN("Failed to restore display modes, hr %#x.\n", hr); + return hr; + } + + if (FAILED(hr = wined3d_output_get_display_mode(output, &state->original_mode, NULL))) + { + WARN("Failed to get current display mode, hr %#x.\n", hr); + return hr; + } + } + + if (FAILED(hr = wined3d_output_set_display_mode(output, mode))) + { + WARN("Failed to set display mode, hr %#x.\n", hr); + return WINED3DERR_INVALIDCALL; + } + + return WINED3D_OK; +} + +HRESULT CDECL wined3d_swapchain_state_resize_target(struct wined3d_swapchain_state *state, + const struct wined3d_display_mode *mode) +{ + struct wined3d_display_mode actual_mode; + struct wined3d_output_desc output_desc; + RECT original_window_rect, window_rect; + int x, y, width, height; + HWND window; + HRESULT hr; + + TRACE("state %p, mode %p.\n", state, mode); + + wined3d_mutex_lock(); + + window = state->device_window; + + if (state->desc.windowed) + { + SetRect(&window_rect, 0, 0, mode->width, mode->height); + AdjustWindowRectEx(&window_rect, + GetWindowLongW(window, GWL_STYLE), FALSE, + GetWindowLongW(window, GWL_EXSTYLE)); + GetWindowRect(window, &original_window_rect); + + x = original_window_rect.left; + y = original_window_rect.top; + width = window_rect.right - window_rect.left; + height = window_rect.bottom - window_rect.top; + } + else + { + if (state->desc.flags & WINED3D_SWAPCHAIN_ALLOW_MODE_SWITCH) + { + actual_mode = *mode; + if (FAILED(hr = wined3d_swapchain_state_set_display_mode(state, state->desc.output, + &actual_mode))) + { + ERR("Failed to set display mode, hr %#x.\n", hr); + wined3d_mutex_unlock(); + return hr; + } + } + + if (FAILED(hr = wined3d_output_get_desc(state->desc.output, &output_desc))) + { + ERR("Failed to get output description, hr %#x.\n", hr); + wined3d_mutex_unlock(); + return hr; + } + + x = output_desc.desktop_rect.left; + y = output_desc.desktop_rect.top; + width = output_desc.desktop_rect.right - output_desc.desktop_rect.left; + height = output_desc.desktop_rect.bottom - output_desc.desktop_rect.top; + } + + wined3d_mutex_unlock(); + + MoveWindow(window, x, y, width, height, TRUE); + + return WINED3D_OK; +} + +static LONG fullscreen_style(LONG style) +{ + /* Make sure the window is managed, otherwise we won't get keyboard input. */ + style |= WS_POPUP | WS_SYSMENU; + style &= ~(WS_CAPTION | WS_THICKFRAME); + + return style; +} + +static LONG fullscreen_exstyle(LONG exstyle) +{ + /* Filter out window decorations. */ + exstyle &= ~(WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE); + + return exstyle; +} + +HRESULT wined3d_swapchain_state_setup_fullscreen(struct wined3d_swapchain_state *state, + HWND window, int x, int y, int width, int height) +{ + unsigned int window_pos_flags = SWP_FRAMECHANGED | SWP_NOACTIVATE; + LONG style, exstyle; + BOOL filter; + + TRACE("Setting up window %p for fullscreen mode.\n", window); + + if (!IsWindow(window)) + { + WARN("%p is not a valid window.\n", window); + return WINED3DERR_NOTAVAILABLE; + } + + if (state->style || state->exstyle) + { + ERR("Changing the window style for window %p, but another style (%08x, %08x) is already stored.\n", + window, state->style, state->exstyle); + } + + if (state->desc.flags & WINED3D_SWAPCHAIN_NO_WINDOW_CHANGES) + window_pos_flags |= SWP_NOZORDER; + else + window_pos_flags |= SWP_SHOWWINDOW; + + state->style = GetWindowLongW(window, GWL_STYLE); + state->exstyle = GetWindowLongW(window, GWL_EXSTYLE); + + style = fullscreen_style(state->style); + exstyle = fullscreen_exstyle(state->exstyle); + + TRACE("Old style was %08x, %08x, setting to %08x, %08x.\n", + state->style, state->exstyle, style, exstyle); + + filter = wined3d_filter_messages(window, TRUE); + + SetWindowLongW(window, GWL_STYLE, style); + SetWindowLongW(window, GWL_EXSTYLE, exstyle); + SetWindowPos(window, HWND_TOPMOST, x, y, width, height, window_pos_flags); + + wined3d_filter_messages(window, filter); + + return WINED3D_OK; +} + +void wined3d_swapchain_state_restore_from_fullscreen(struct wined3d_swapchain_state *state, + HWND window, const RECT *window_rect) +{ + unsigned int window_pos_flags = SWP_FRAMECHANGED | SWP_NOZORDER | SWP_NOACTIVATE; + HWND window_pos_after = NULL; + LONG style, exstyle; + RECT rect = {0}; + BOOL filter; + + if (!state->style && !state->exstyle) + return; + + if ((state->desc.flags & WINED3D_SWAPCHAIN_RESTORE_WINDOW_STATE) + && !(state->desc.flags & WINED3D_SWAPCHAIN_NO_WINDOW_CHANGES)) + { + window_pos_after = (state->exstyle & WS_EX_TOPMOST) ? HWND_TOPMOST : HWND_NOTOPMOST; + window_pos_flags |= (state->style & WS_VISIBLE) ? SWP_SHOWWINDOW : SWP_HIDEWINDOW; + window_pos_flags &= ~SWP_NOZORDER; + } + + style = GetWindowLongW(window, GWL_STYLE); + exstyle = GetWindowLongW(window, GWL_EXSTYLE); + + /* These flags are set by wined3d_device_setup_fullscreen_window, not the + * application, and we want to ignore them in the test below, since it's + * not the application's fault that they changed. Additionally, we want to + * preserve the current status of these flags (i.e. don't restore them) to + * more closely emulate the behavior of Direct3D, which leaves these flags + * alone when returning to windowed mode. */ + state->style ^= (state->style ^ style) & WS_VISIBLE; + state->exstyle ^= (state->exstyle ^ exstyle) & WS_EX_TOPMOST; + + TRACE("Restoring window style of window %p to %08x, %08x.\n", + window, state->style, state->exstyle); + + filter = wined3d_filter_messages(window, TRUE); + + /* Only restore the style if the application didn't modify it during the + * fullscreen phase. Some applications change it before calling Reset() + * when switching between windowed and fullscreen modes (HL2), some + * depend on the original style (Eve Online). */ + if (style == fullscreen_style(state->style) && exstyle == fullscreen_exstyle(state->exstyle)) + { + SetWindowLongW(window, GWL_STYLE, state->style); + SetWindowLongW(window, GWL_EXSTYLE, state->exstyle); + } + + if (window_rect) + rect = *window_rect; + else + window_pos_flags |= (SWP_NOMOVE | SWP_NOSIZE); + SetWindowPos(window, window_pos_after, rect.left, rect.top, + rect.right - rect.left, rect.bottom - rect.top, window_pos_flags); + + wined3d_filter_messages(window, filter); + + /* Delete the old values. */ + state->style = 0; + state->exstyle = 0; +} + +HRESULT CDECL wined3d_swapchain_state_set_fullscreen(struct wined3d_swapchain_state *state, + const struct wined3d_swapchain_desc *swapchain_desc, + const struct wined3d_display_mode *mode) +{ + struct wined3d_display_mode actual_mode; + struct wined3d_output_desc output_desc; + BOOL windowed = state->desc.windowed; + HRESULT hr; + + TRACE("state %p, swapchain_desc %p, mode %p.\n", state, swapchain_desc, mode); + + if (state->desc.flags & WINED3D_SWAPCHAIN_ALLOW_MODE_SWITCH) + { + if (mode) + { + actual_mode = *mode; + if (FAILED(hr = wined3d_swapchain_state_set_display_mode(state, swapchain_desc->output, + &actual_mode))) + return hr; + } + else + { + if (!swapchain_desc->windowed) + { + actual_mode.width = swapchain_desc->backbuffer_width; + actual_mode.height = swapchain_desc->backbuffer_height; + actual_mode.refresh_rate = swapchain_desc->refresh_rate; + actual_mode.format_id = adapter_format_from_backbuffer_format(swapchain_desc->output->adapter, + swapchain_desc->backbuffer_format); + actual_mode.scanline_ordering = WINED3D_SCANLINE_ORDERING_UNKNOWN; + if (FAILED(hr = wined3d_swapchain_state_set_display_mode(state, swapchain_desc->output, + &actual_mode))) + return hr; + } + else + { + if (FAILED(hr = wined3d_restore_display_modes(state->wined3d))) + { + WARN("Failed to restore display modes for all outputs, hr %#x.\n", hr); + return hr; + } + } + } + } + else + { + if (mode) + WARN("WINED3D_SWAPCHAIN_ALLOW_MODE_SWITCH is not set, ignoring mode.\n"); + + if (FAILED(hr = wined3d_output_get_display_mode(swapchain_desc->output, &actual_mode, + NULL))) + { + ERR("Failed to get display mode, hr %#x.\n", hr); + return WINED3DERR_INVALIDCALL; + } + } + + if (!swapchain_desc->windowed) + { + unsigned int width = actual_mode.width; + unsigned int height = actual_mode.height; + + if (FAILED(hr = wined3d_output_get_desc(swapchain_desc->output, &output_desc))) + { + ERR("Failed to get output description, hr %#x.\n", hr); + return hr; + } + + if (state->desc.windowed) + { + /* Switch from windowed to fullscreen */ + if (FAILED(hr = wined3d_swapchain_state_setup_fullscreen(state, state->device_window, + output_desc.desktop_rect.left, output_desc.desktop_rect.top, width, height))) + return hr; + } + else + { + HWND window = state->device_window; + BOOL filter; + + /* Fullscreen -> fullscreen mode change */ + filter = wined3d_filter_messages(window, TRUE); + MoveWindow(window, output_desc.desktop_rect.left, output_desc.desktop_rect.top, width, + height, TRUE); + ShowWindow(window, SW_SHOW); + wined3d_filter_messages(window, filter); + } + state->d3d_mode = actual_mode; + } + else if (!state->desc.windowed) + { + /* Fullscreen -> windowed switch */ + RECT *window_rect = NULL; + if (state->desc.flags & WINED3D_SWAPCHAIN_RESTORE_WINDOW_RECT) + window_rect = &state->original_window_rect; + wined3d_swapchain_state_restore_from_fullscreen(state, state->device_window, window_rect); + } + + state->desc.output = swapchain_desc->output; + state->desc.windowed = swapchain_desc->windowed; + + if (windowed != state->desc.windowed) + state->parent->ops->windowed_state_changed(state->parent, state->desc.windowed); + + return WINED3D_OK; +} + +BOOL CDECL wined3d_swapchain_state_is_windowed(const struct wined3d_swapchain_state *state) +{ + TRACE("state %p.\n", state); + + return state->desc.windowed; +} + +void CDECL wined3d_swapchain_state_destroy(struct wined3d_swapchain_state *state) +{ + wined3d_swapchain_state_cleanup(state); + heap_free(state); +} + +HRESULT CDECL wined3d_swapchain_state_create(const struct wined3d_swapchain_desc *desc, + HWND window, struct wined3d *wined3d, struct wined3d_swapchain_state_parent *state_parent, + struct wined3d_swapchain_state **state) +{ + struct wined3d_swapchain_state *s; + HRESULT hr; + + TRACE("desc %p, window %p, wined3d %p, state %p.\n", desc, window, wined3d, state); + + if (!(s = heap_alloc_zero(sizeof(*s)))) + return E_OUTOFMEMORY; + + if (FAILED(hr = wined3d_swapchain_state_init(s, desc, window, wined3d, state_parent))) + { + heap_free(s); + return hr; + } + + *state = s; + + return hr; +} diff --git a/wrappers/directx/d3dwine_wrapper/texture.c b/wrappers/directx/d3dwine_wrapper/texture.c new file mode 100644 index 00000000000..f4f929db2ca --- /dev/null +++ b/wrappers/directx/d3dwine_wrapper/texture.c @@ -0,0 +1,6631 @@ +/* + * Copyright 2002-2005 Jason Edmeades + * Copyright 2002-2005 Raphael Junqueira + * Copyright 2005 Oliver Stieber + * Copyright 2007-2009, 2013 Stefan Dösinger for CodeWeavers + * Copyright 2009-2011 Henri Verbeet for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "config.h" +#include "wine/port.h" +#include "wined3d_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(d3d); +WINE_DECLARE_DEBUG_CHANNEL(d3d_perf); +WINE_DECLARE_DEBUG_CHANNEL(winediag); + +#define WINED3D_TEXTURE_DYNAMIC_MAP_THRESHOLD 50 + +static const uint32_t wined3d_texture_sysmem_locations = WINED3D_LOCATION_SYSMEM | WINED3D_LOCATION_BUFFER; +static const struct wined3d_texture_ops texture_gl_ops; + +struct wined3d_texture_idx +{ + struct wined3d_texture *texture; + unsigned int sub_resource_idx; +}; + +struct wined3d_rect_f +{ + float l; + float t; + float r; + float b; +}; + +static BOOL wined3d_texture_use_pbo(const struct wined3d_texture *texture, const struct wined3d_gl_info *gl_info) +{ + if (!gl_info->supported[ARB_PIXEL_BUFFER_OBJECT] + || texture->resource.format->conv_byte_count + || (texture->flags & (WINED3D_TEXTURE_PIN_SYSMEM | WINED3D_TEXTURE_COND_NP2_EMULATED))) + return FALSE; + + /* Use a PBO for dynamic textures and read-only staging textures. */ + return (!(texture->resource.access & WINED3D_RESOURCE_ACCESS_CPU) + && texture->resource.usage & WINED3DUSAGE_DYNAMIC) + || texture->resource.access == (WINED3D_RESOURCE_ACCESS_CPU | WINED3D_RESOURCE_ACCESS_MAP_R); +} + +static BOOL wined3d_texture_use_immutable_storage(const struct wined3d_texture *texture, + const struct wined3d_gl_info *gl_info) +{ + /* We don't expect to create texture views for textures with height-scaled formats. + * Besides, ARB_texture_storage doesn't allow specifying exact sizes for all levels. */ + return gl_info->supported[ARB_TEXTURE_STORAGE] + && !(texture->resource.format_flags & WINED3DFMT_FLAG_HEIGHT_SCALE); +} + +/* Front buffer coordinates are always full screen coordinates, but our GL + * drawable is limited to the window's client area. The sysmem and texture + * copies do have the full screen size. Note that GL has a bottom-left + * origin, while D3D has a top-left origin. */ +void wined3d_texture_translate_drawable_coords(const struct wined3d_texture *texture, HWND window, RECT *rect) +{ + unsigned int drawable_height; + POINT offset = {0, 0}; + RECT windowsize; + + if (!texture->swapchain) + return; + + if (texture == texture->swapchain->front_buffer) + { + ScreenToClient(window, &offset); + OffsetRect(rect, offset.x, offset.y); + } + + GetClientRect(window, &windowsize); + drawable_height = windowsize.bottom - windowsize.top; + + rect->top = drawable_height - rect->top; + rect->bottom = drawable_height - rect->bottom; +} + +GLenum wined3d_texture_get_gl_buffer(const struct wined3d_texture *texture) +{ + const struct wined3d_swapchain *swapchain = texture->swapchain; + + TRACE("texture %p.\n", texture); + + if (!swapchain) + { + ERR("Texture %p is not part of a swapchain.\n", texture); + return GL_NONE; + } + + if (texture == swapchain->front_buffer) + { + TRACE("Returning GL_FRONT.\n"); + return GL_FRONT; + } + + if (texture == swapchain->back_buffers[0]) + { + TRACE("Returning GL_BACK.\n"); + return GL_BACK; + } + + FIXME("Higher back buffer, returning GL_BACK.\n"); + return GL_BACK; +} + +static DWORD wined3d_resource_access_from_location(DWORD location) +{ + switch (location) + { + case WINED3D_LOCATION_DISCARDED: + return 0; + + case WINED3D_LOCATION_SYSMEM: + return WINED3D_RESOURCE_ACCESS_CPU; + + case WINED3D_LOCATION_BUFFER: + case WINED3D_LOCATION_DRAWABLE: + case WINED3D_LOCATION_TEXTURE_RGB: + case WINED3D_LOCATION_TEXTURE_SRGB: + case WINED3D_LOCATION_RB_MULTISAMPLE: + case WINED3D_LOCATION_RB_RESOLVED: + return WINED3D_RESOURCE_ACCESS_GPU; + + default: + FIXME("Unhandled location %#x.\n", location); + return 0; + } +} + +static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct wined3d_rect_f *f) +{ + f->l = ((r->left * 2.0f) / w) - 1.0f; + f->t = ((r->top * 2.0f) / h) - 1.0f; + f->r = ((r->right * 2.0f) / w) - 1.0f; + f->b = ((r->bottom * 2.0f) / h) - 1.0f; +} + +void texture2d_get_blt_info(const struct wined3d_texture_gl *texture_gl, + unsigned int sub_resource_idx, const RECT *rect, struct wined3d_blt_info *info) +{ + struct wined3d_vec3 *coords = info->texcoords; + struct wined3d_rect_f f; + unsigned int level; + GLenum target; + GLsizei w, h; + + level = sub_resource_idx % texture_gl->t.level_count; + w = wined3d_texture_get_level_pow2_width(&texture_gl->t, level); + h = wined3d_texture_get_level_pow2_height(&texture_gl->t, level); + target = wined3d_texture_gl_get_sub_resource_target(texture_gl, sub_resource_idx); + + switch (target) + { + default: + FIXME("Unsupported texture target %#x.\n", target); + /* Fall back to GL_TEXTURE_2D */ + case GL_TEXTURE_2D: + info->bind_target = GL_TEXTURE_2D; + coords[0].x = (float)rect->left / w; + coords[0].y = (float)rect->top / h; + coords[0].z = 0.0f; + + coords[1].x = (float)rect->right / w; + coords[1].y = (float)rect->top / h; + coords[1].z = 0.0f; + + coords[2].x = (float)rect->left / w; + coords[2].y = (float)rect->bottom / h; + coords[2].z = 0.0f; + + coords[3].x = (float)rect->right / w; + coords[3].y = (float)rect->bottom / h; + coords[3].z = 0.0f; + break; + + case GL_TEXTURE_RECTANGLE_ARB: + info->bind_target = GL_TEXTURE_RECTANGLE_ARB; + coords[0].x = rect->left; coords[0].y = rect->top; coords[0].z = 0.0f; + coords[1].x = rect->right; coords[1].y = rect->top; coords[1].z = 0.0f; + coords[2].x = rect->left; coords[2].y = rect->bottom; coords[2].z = 0.0f; + coords[3].x = rect->right; coords[3].y = rect->bottom; coords[3].z = 0.0f; + break; + + case GL_TEXTURE_CUBE_MAP_POSITIVE_X: + info->bind_target = GL_TEXTURE_CUBE_MAP_ARB; + cube_coords_float(rect, w, h, &f); + + coords[0].x = 1.0f; coords[0].y = -f.t; coords[0].z = -f.l; + coords[1].x = 1.0f; coords[1].y = -f.t; coords[1].z = -f.r; + coords[2].x = 1.0f; coords[2].y = -f.b; coords[2].z = -f.l; + coords[3].x = 1.0f; coords[3].y = -f.b; coords[3].z = -f.r; + break; + + case GL_TEXTURE_CUBE_MAP_NEGATIVE_X: + info->bind_target = GL_TEXTURE_CUBE_MAP_ARB; + cube_coords_float(rect, w, h, &f); + + coords[0].x = -1.0f; coords[0].y = -f.t; coords[0].z = f.l; + coords[1].x = -1.0f; coords[1].y = -f.t; coords[1].z = f.r; + coords[2].x = -1.0f; coords[2].y = -f.b; coords[2].z = f.l; + coords[3].x = -1.0f; coords[3].y = -f.b; coords[3].z = f.r; + break; + + case GL_TEXTURE_CUBE_MAP_POSITIVE_Y: + info->bind_target = GL_TEXTURE_CUBE_MAP_ARB; + cube_coords_float(rect, w, h, &f); + + coords[0].x = f.l; coords[0].y = 1.0f; coords[0].z = f.t; + coords[1].x = f.r; coords[1].y = 1.0f; coords[1].z = f.t; + coords[2].x = f.l; coords[2].y = 1.0f; coords[2].z = f.b; + coords[3].x = f.r; coords[3].y = 1.0f; coords[3].z = f.b; + break; + + case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: + info->bind_target = GL_TEXTURE_CUBE_MAP_ARB; + cube_coords_float(rect, w, h, &f); + + coords[0].x = f.l; coords[0].y = -1.0f; coords[0].z = -f.t; + coords[1].x = f.r; coords[1].y = -1.0f; coords[1].z = -f.t; + coords[2].x = f.l; coords[2].y = -1.0f; coords[2].z = -f.b; + coords[3].x = f.r; coords[3].y = -1.0f; coords[3].z = -f.b; + break; + + case GL_TEXTURE_CUBE_MAP_POSITIVE_Z: + info->bind_target = GL_TEXTURE_CUBE_MAP_ARB; + cube_coords_float(rect, w, h, &f); + + coords[0].x = f.l; coords[0].y = -f.t; coords[0].z = 1.0f; + coords[1].x = f.r; coords[1].y = -f.t; coords[1].z = 1.0f; + coords[2].x = f.l; coords[2].y = -f.b; coords[2].z = 1.0f; + coords[3].x = f.r; coords[3].y = -f.b; coords[3].z = 1.0f; + break; + + case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: + info->bind_target = GL_TEXTURE_CUBE_MAP_ARB; + cube_coords_float(rect, w, h, &f); + + coords[0].x = -f.l; coords[0].y = -f.t; coords[0].z = -1.0f; + coords[1].x = -f.r; coords[1].y = -f.t; coords[1].z = -1.0f; + coords[2].x = -f.l; coords[2].y = -f.b; coords[2].z = -1.0f; + coords[3].x = -f.r; coords[3].y = -f.b; coords[3].z = -1.0f; + break; + } +} + +static bool fbo_blitter_supported(enum wined3d_blit_op blit_op, const struct wined3d_gl_info *gl_info, + const struct wined3d_resource *src_resource, DWORD src_location, + const struct wined3d_resource *dst_resource, DWORD dst_location) +{ + const struct wined3d_format *src_format = src_resource->format; + const struct wined3d_format *dst_format = dst_resource->format; + + if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer) + return false; + + /* Source and/or destination need to be on the GL side. */ + if (!(src_resource->access & dst_resource->access & WINED3D_RESOURCE_ACCESS_GPU)) + return false; + + if (src_resource->type != WINED3D_RTYPE_TEXTURE_2D) + return false; + + switch (blit_op) + { + case WINED3D_BLIT_OP_COLOR_BLIT: + if (!((src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE) + || (src_resource->bind_flags & WINED3D_BIND_RENDER_TARGET))) + return false; + if (!((dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE) + || (dst_resource->bind_flags & WINED3D_BIND_RENDER_TARGET))) + return false; + if ((src_format->id != dst_format->id || dst_location == WINED3D_LOCATION_DRAWABLE) + && (!is_identity_fixup(src_format->color_fixup) || !is_identity_fixup(dst_format->color_fixup))) + return false; + break; + + case WINED3D_BLIT_OP_DEPTH_BLIT: + if (!(src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_DEPTH_STENCIL)) + return false; + if (!(dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_DEPTH_STENCIL)) + return false; + /* Accept pure swizzle fixups for depth formats. In general we + * ignore the stencil component (if present) at the moment and the + * swizzle is not relevant with just the depth component. */ + if (is_complex_fixup(src_format->color_fixup) || is_complex_fixup(dst_format->color_fixup) + || is_scaling_fixup(src_format->color_fixup) || is_scaling_fixup(dst_format->color_fixup)) + return false; + break; + + default: + return false; + } + + return true; +} + +/* Blit between surface locations. Onscreen on different swapchains is not supported. + * Depth / stencil is not supported. Context activation is done by the caller. */ +static void texture2d_blt_fbo(struct wined3d_device *device, struct wined3d_context *context, + enum wined3d_texture_filter_type filter, struct wined3d_texture *src_texture, + unsigned int src_sub_resource_idx, DWORD src_location, const RECT *src_rect, + struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx, DWORD dst_location, + const RECT *dst_rect) +{ + struct wined3d_texture *required_texture, *restore_texture; + const struct wined3d_gl_info *gl_info; + struct wined3d_context_gl *context_gl; + unsigned int restore_idx; + bool scaled_resolve; + GLenum gl_filter; + GLenum buffer; + RECT s, d; + + TRACE("device %p, context %p, filter %s, src_texture %p, src_sub_resource_idx %u, src_location %s, " + "src_rect %s, dst_texture %p, dst_sub_resource_idx %u, dst_location %s, dst_rect %s.\n", + device, context, debug_d3dtexturefiltertype(filter), src_texture, src_sub_resource_idx, + wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect), dst_texture, + dst_sub_resource_idx, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect)); + + scaled_resolve = wined3d_texture_gl_is_multisample_location(wined3d_texture_gl(src_texture), src_location) + && (abs(src_rect->bottom - src_rect->top) != abs(dst_rect->bottom - dst_rect->top) + || abs(src_rect->right - src_rect->left) != abs(dst_rect->right - dst_rect->left)); + + if (filter == WINED3D_TEXF_LINEAR) + gl_filter = scaled_resolve ? GL_SCALED_RESOLVE_NICEST_EXT : GL_LINEAR; + else + gl_filter = scaled_resolve ? GL_SCALED_RESOLVE_FASTEST_EXT : GL_NEAREST; + + /* Make sure the locations are up-to-date. Loading the destination + * surface isn't required if the entire surface is overwritten. (And is + * in fact harmful if we're being called by surface_load_location() with + * the purpose of loading the destination surface.) */ + wined3d_texture_load_location(src_texture, src_sub_resource_idx, context, src_location); + if (!wined3d_texture_is_full_rect(dst_texture, dst_sub_resource_idx % dst_texture->level_count, dst_rect)) + wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, dst_location); + else + wined3d_texture_prepare_location(dst_texture, dst_sub_resource_idx, context, dst_location); + + /* Acquire a context for the front-buffer, even though we may be blitting + * to/from a back-buffer. Since context_acquire() doesn't take the + * resource location into account, it may consider the back-buffer to be + * offscreen. */ + if (src_location == WINED3D_LOCATION_DRAWABLE) + required_texture = src_texture->swapchain->front_buffer; + else if (dst_location == WINED3D_LOCATION_DRAWABLE) + required_texture = dst_texture->swapchain->front_buffer; + else + required_texture = NULL; + + restore_texture = context->current_rt.texture; + restore_idx = context->current_rt.sub_resource_idx; + if (restore_texture != required_texture) + context = context_acquire(device, required_texture, 0); + else + restore_texture = NULL; + + context_gl = wined3d_context_gl(context); + if (!context_gl->valid) + { + context_release(context); + WARN("Invalid context, skipping blit.\n"); + return; + } + + gl_info = context_gl->gl_info; + + if (src_location == WINED3D_LOCATION_DRAWABLE) + { + TRACE("Source texture %p is onscreen.\n", src_texture); + buffer = wined3d_texture_get_gl_buffer(src_texture); + s = *src_rect; + wined3d_texture_translate_drawable_coords(src_texture, context_gl->window, &s); + src_rect = &s; + } + else + { + TRACE("Source texture %p is offscreen.\n", src_texture); + buffer = GL_COLOR_ATTACHMENT0; + } + + wined3d_context_gl_apply_fbo_state_blit(context_gl, GL_READ_FRAMEBUFFER, + &src_texture->resource, src_sub_resource_idx, NULL, 0, src_location); + gl_info->gl_ops.gl.p_glReadBuffer(buffer); + checkGLcall("glReadBuffer()"); + wined3d_context_gl_check_fbo_status(context_gl, GL_READ_FRAMEBUFFER); + + if (dst_location == WINED3D_LOCATION_DRAWABLE) + { + TRACE("Destination texture %p is onscreen.\n", dst_texture); + buffer = wined3d_texture_get_gl_buffer(dst_texture); + d = *dst_rect; + wined3d_texture_translate_drawable_coords(dst_texture, context_gl->window, &d); + dst_rect = &d; + } + else + { + TRACE("Destination texture %p is offscreen.\n", dst_texture); + buffer = GL_COLOR_ATTACHMENT0; + } + + wined3d_context_gl_apply_fbo_state_blit(context_gl, GL_DRAW_FRAMEBUFFER, + &dst_texture->resource, dst_sub_resource_idx, NULL, 0, dst_location); + wined3d_context_gl_set_draw_buffer(context_gl, buffer); + wined3d_context_gl_check_fbo_status(context_gl, GL_DRAW_FRAMEBUFFER); + context_invalidate_state(context, STATE_FRAMEBUFFER); + + gl_info->gl_ops.gl.p_glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + context_invalidate_state(context, STATE_BLEND); + + gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST); + context_invalidate_state(context, STATE_RASTERIZER); + + gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom, + dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, GL_COLOR_BUFFER_BIT, gl_filter); + checkGLcall("glBlitFramebuffer()"); + + if (dst_location == WINED3D_LOCATION_DRAWABLE && dst_texture->swapchain->front_buffer == dst_texture) + gl_info->gl_ops.gl.p_glFlush(); + + if (restore_texture) + context_restore(context, restore_texture, restore_idx); +} + +static void texture2d_depth_blt_fbo(const struct wined3d_device *device, struct wined3d_context *context, + struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx, DWORD src_location, + const RECT *src_rect, struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx, + DWORD dst_location, const RECT *dst_rect) +{ + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + GLbitfield src_mask, dst_mask; + GLbitfield gl_mask; + + TRACE("device %p, src_texture %p, src_sub_resource_idx %u, src_location %s, src_rect %s, " + "dst_texture %p, dst_sub_resource_idx %u, dst_location %s, dst_rect %s.\n", device, + src_texture, src_sub_resource_idx, wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect), + dst_texture, dst_sub_resource_idx, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect)); + + src_mask = 0; + if (src_texture->resource.format->depth_size) + src_mask |= GL_DEPTH_BUFFER_BIT; + if (src_texture->resource.format->stencil_size) + src_mask |= GL_STENCIL_BUFFER_BIT; + + dst_mask = 0; + if (dst_texture->resource.format->depth_size) + dst_mask |= GL_DEPTH_BUFFER_BIT; + if (dst_texture->resource.format->stencil_size) + dst_mask |= GL_STENCIL_BUFFER_BIT; + + if (src_mask != dst_mask) + { + ERR("Incompatible formats %s and %s.\n", + debug_d3dformat(src_texture->resource.format->id), + debug_d3dformat(dst_texture->resource.format->id)); + return; + } + + if (!src_mask) + { + ERR("Not a depth / stencil format: %s.\n", + debug_d3dformat(src_texture->resource.format->id)); + return; + } + gl_mask = src_mask; + + /* Make sure the locations are up-to-date. Loading the destination + * surface isn't required if the entire surface is overwritten. */ + wined3d_texture_load_location(src_texture, src_sub_resource_idx, context, src_location); + if (!wined3d_texture_is_full_rect(dst_texture, dst_sub_resource_idx % dst_texture->level_count, dst_rect)) + wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, dst_location); + else + wined3d_texture_prepare_location(dst_texture, dst_sub_resource_idx, context, dst_location); + + wined3d_context_gl_apply_fbo_state_blit(context_gl, GL_READ_FRAMEBUFFER, NULL, 0, + &src_texture->resource, src_sub_resource_idx, src_location); + wined3d_context_gl_check_fbo_status(context_gl, GL_READ_FRAMEBUFFER); + + wined3d_context_gl_apply_fbo_state_blit(context_gl, GL_DRAW_FRAMEBUFFER, NULL, 0, + &dst_texture->resource, dst_sub_resource_idx, dst_location); + wined3d_context_gl_set_draw_buffer(context_gl, GL_NONE); + wined3d_context_gl_check_fbo_status(context_gl, GL_DRAW_FRAMEBUFFER); + context_invalidate_state(context, STATE_FRAMEBUFFER); + + if (gl_mask & GL_DEPTH_BUFFER_BIT) + { + gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE); + context_invalidate_state(context, STATE_DEPTH_STENCIL); + } + if (gl_mask & GL_STENCIL_BUFFER_BIT) + { + if (gl_info->supported[EXT_STENCIL_TWO_SIDE]) + gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT); + gl_info->gl_ops.gl.p_glStencilMask(~0U); + context_invalidate_state(context, STATE_DEPTH_STENCIL); + } + + gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST); + context_invalidate_state(context, STATE_RASTERIZER); + + gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom, + dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, gl_mask, GL_NEAREST); + checkGLcall("glBlitFramebuffer()"); +} + +static void wined3d_texture_evict_sysmem(struct wined3d_texture *texture) +{ + struct wined3d_texture_sub_resource *sub_resource; + unsigned int i, sub_count; + + if (texture->flags & (WINED3D_TEXTURE_CONVERTED | WINED3D_TEXTURE_PIN_SYSMEM) + || texture->download_count > WINED3D_TEXTURE_DYNAMIC_MAP_THRESHOLD) + { + TRACE("Not evicting system memory for texture %p.\n", texture); + return; + } + + TRACE("Evicting system memory for texture %p.\n", texture); + + sub_count = texture->level_count * texture->layer_count; + for (i = 0; i < sub_count; ++i) + { + sub_resource = &texture->sub_resources[i]; + if (sub_resource->locations == WINED3D_LOCATION_SYSMEM) + ERR("WINED3D_LOCATION_SYSMEM is the only location for sub-resource %u of texture %p.\n", + i, texture); + sub_resource->locations &= ~WINED3D_LOCATION_SYSMEM; + } + wined3d_resource_free_sysmem(&texture->resource); +} + +void wined3d_texture_validate_location(struct wined3d_texture *texture, + unsigned int sub_resource_idx, DWORD location) +{ + struct wined3d_texture_sub_resource *sub_resource; + DWORD previous_locations; + + TRACE("texture %p, sub_resource_idx %u, location %s.\n", + texture, sub_resource_idx, wined3d_debug_location(location)); + + sub_resource = &texture->sub_resources[sub_resource_idx]; + previous_locations = sub_resource->locations; + sub_resource->locations |= location; + if (previous_locations == WINED3D_LOCATION_SYSMEM && location != WINED3D_LOCATION_SYSMEM + && !--texture->sysmem_count) + wined3d_texture_evict_sysmem(texture); + + TRACE("New locations flags are %s.\n", wined3d_debug_location(sub_resource->locations)); +} + +static void wined3d_texture_set_dirty(struct wined3d_texture *texture) +{ + texture->flags &= ~(WINED3D_TEXTURE_RGB_VALID | WINED3D_TEXTURE_SRGB_VALID); +} + +void wined3d_texture_invalidate_location(struct wined3d_texture *texture, + unsigned int sub_resource_idx, DWORD location) +{ + struct wined3d_texture_sub_resource *sub_resource; + DWORD previous_locations; + + TRACE("texture %p, sub_resource_idx %u, location %s.\n", + texture, sub_resource_idx, wined3d_debug_location(location)); + + if (location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB)) + wined3d_texture_set_dirty(texture); + + sub_resource = &texture->sub_resources[sub_resource_idx]; + previous_locations = sub_resource->locations; + sub_resource->locations &= ~location; + if (previous_locations != WINED3D_LOCATION_SYSMEM && sub_resource->locations == WINED3D_LOCATION_SYSMEM) + ++texture->sysmem_count; + + TRACE("New locations flags are %s.\n", wined3d_debug_location(sub_resource->locations)); + + if (!sub_resource->locations) + ERR("Sub-resource %u of texture %p does not have any up to date location.\n", + sub_resource_idx, texture); +} + +void wined3d_texture_clear_dirty_regions(struct wined3d_texture *texture) +{ + unsigned int i; + + TRACE("texture %p\n", texture); + + if (!texture->dirty_regions) + return; + + for (i = 0; i < texture->layer_count; ++i) + { + texture->dirty_regions[i].box_count = 0; + } +} + +static BOOL wined3d_texture_copy_sysmem_location(struct wined3d_texture *texture, + unsigned int sub_resource_idx, struct wined3d_context *context, DWORD location) +{ + unsigned int size = texture->sub_resources[sub_resource_idx].size; + struct wined3d_device *device = texture->resource.device; + const struct wined3d_gl_info *gl_info; + struct wined3d_bo_gl *src_bo, *dst_bo; + struct wined3d_bo_address dst, src; + + if (!wined3d_texture_prepare_location(texture, sub_resource_idx, context, location)) + return FALSE; + + wined3d_texture_get_memory(texture, sub_resource_idx, &dst, location); + wined3d_texture_get_memory(texture, sub_resource_idx, &src, + texture->sub_resources[sub_resource_idx].locations); + + if ((dst_bo = (struct wined3d_bo_gl *)dst.buffer_object)) + { + context = context_acquire(device, NULL, 0); + gl_info = wined3d_context_gl(context)->gl_info; + GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, dst_bo->id)); + GL_EXTCALL(glBufferSubData(GL_PIXEL_UNPACK_BUFFER, 0, size, src.addr)); + GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0)); + wined3d_context_gl_reference_bo(wined3d_context_gl(context), dst_bo); + checkGLcall("PBO upload"); + context_release(context); + return TRUE; + } + + if ((src_bo = (struct wined3d_bo_gl *)src.buffer_object)) + { + context = context_acquire(device, NULL, 0); + gl_info = wined3d_context_gl(context)->gl_info; + GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, src_bo->id)); + GL_EXTCALL(glGetBufferSubData(GL_PIXEL_PACK_BUFFER, 0, size, dst.addr)); + GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0)); + wined3d_context_gl_reference_bo(wined3d_context_gl(context), src_bo); + checkGLcall("PBO download"); + context_release(context); + return TRUE; + } + + memcpy(dst.addr, src.addr, size); + return TRUE; +} + +/* Context activation is done by the caller. Context may be NULL in + * WINED3D_NO3D mode. */ +BOOL wined3d_texture_load_location(struct wined3d_texture *texture, + unsigned int sub_resource_idx, struct wined3d_context *context, DWORD location) +{ + DWORD current = texture->sub_resources[sub_resource_idx].locations; + BOOL ret; + + TRACE("texture %p, sub_resource_idx %u, context %p, location %s.\n", + texture, sub_resource_idx, context, wined3d_debug_location(location)); + + TRACE("Current resource location %s.\n", wined3d_debug_location(current)); + + if (current & location) + { + TRACE("Location %s is already up to date.\n", wined3d_debug_location(location)); + return TRUE; + } + + if (WARN_ON(d3d)) + { + DWORD required_access = wined3d_resource_access_from_location(location); + if ((texture->resource.access & required_access) != required_access) + WARN("Operation requires %#x access, but texture only has %#x.\n", + required_access, texture->resource.access); + } + + if (current & WINED3D_LOCATION_DISCARDED) + { + TRACE("Sub-resource previously discarded, nothing to do.\n"); + if (!wined3d_texture_prepare_location(texture, sub_resource_idx, context, location)) + return FALSE; + wined3d_texture_validate_location(texture, sub_resource_idx, location); + wined3d_texture_invalidate_location(texture, sub_resource_idx, WINED3D_LOCATION_DISCARDED); + return TRUE; + } + + if (!current) + { + ERR("Sub-resource %u of texture %p does not have any up to date location.\n", + sub_resource_idx, texture); + wined3d_texture_validate_location(texture, sub_resource_idx, WINED3D_LOCATION_DISCARDED); + return wined3d_texture_load_location(texture, sub_resource_idx, context, location); + } + + if ((location & wined3d_texture_sysmem_locations) && (current & wined3d_texture_sysmem_locations)) + ret = wined3d_texture_copy_sysmem_location(texture, sub_resource_idx, context, location); + else + ret = texture->texture_ops->texture_load_location(texture, sub_resource_idx, context, location); + + if (ret) + wined3d_texture_validate_location(texture, sub_resource_idx, location); + + return ret; +} + +void wined3d_texture_get_memory(struct wined3d_texture *texture, unsigned int sub_resource_idx, + struct wined3d_bo_address *data, DWORD locations) +{ + struct wined3d_texture_sub_resource *sub_resource; + + TRACE("texture %p, sub_resource_idx %u, data %p, locations %s.\n", + texture, sub_resource_idx, data, wined3d_debug_location(locations)); + + sub_resource = &texture->sub_resources[sub_resource_idx]; + if (locations & WINED3D_LOCATION_BUFFER) + { + data->addr = NULL; + data->buffer_object = (uintptr_t)&sub_resource->bo; + return; + } + + if (locations & WINED3D_LOCATION_SYSMEM) + { + if (texture->sub_resources[sub_resource_idx].user_memory) + { + data->addr = texture->sub_resources[sub_resource_idx].user_memory; + } + else + { + data->addr = texture->resource.heap_memory; + data->addr += sub_resource->offset; + } + data->buffer_object = 0; + return; + } + + ERR("Unexpected locations %s.\n", wined3d_debug_location(locations)); + data->addr = NULL; + data->buffer_object = 0; +} + +/* Context activation is done by the caller. */ +static void wined3d_texture_remove_buffer_object(struct wined3d_texture *texture, + unsigned int sub_resource_idx, struct wined3d_context_gl *context_gl) +{ + struct wined3d_bo_gl *bo = &texture->sub_resources[sub_resource_idx].bo; + + TRACE("texture %p, sub_resource_idx %u, context_gl %p.\n", texture, sub_resource_idx, context_gl); + + wined3d_context_gl_destroy_bo(context_gl, bo); + wined3d_texture_invalidate_location(texture, sub_resource_idx, WINED3D_LOCATION_BUFFER); +} + +static void wined3d_texture_update_map_binding(struct wined3d_texture *texture) +{ + unsigned int sub_count = texture->level_count * texture->layer_count; + struct wined3d_device *device = texture->resource.device; + DWORD map_binding = texture->update_map_binding; + struct wined3d_context *context; + unsigned int i; + + context = context_acquire(device, NULL, 0); + + for (i = 0; i < sub_count; ++i) + { + if (texture->sub_resources[i].locations == texture->resource.map_binding + && !wined3d_texture_load_location(texture, i, context, map_binding)) + ERR("Failed to load location %s.\n", wined3d_debug_location(map_binding)); + if (texture->resource.map_binding == WINED3D_LOCATION_BUFFER) + wined3d_texture_remove_buffer_object(texture, i, wined3d_context_gl(context)); + } + + context_release(context); + + texture->resource.map_binding = map_binding; + texture->update_map_binding = 0; +} + +void wined3d_texture_set_map_binding(struct wined3d_texture *texture, DWORD map_binding) +{ + texture->update_map_binding = map_binding; + if (!texture->resource.map_count) + wined3d_texture_update_map_binding(texture); +} + +/* A GL context is provided by the caller */ +static void gltexture_delete(struct wined3d_device *device, const struct wined3d_gl_info *gl_info, + struct gl_texture *tex) +{ + context_gl_resource_released(device, tex->name, FALSE); + gl_info->gl_ops.gl.p_glDeleteTextures(1, &tex->name); + tex->name = 0; +} + +/* Context activation is done by the caller. */ +/* The caller is responsible for binding the correct texture. */ +static void wined3d_texture_gl_allocate_mutable_storage(struct wined3d_texture_gl *texture_gl, + GLenum gl_internal_format, const struct wined3d_format_gl *format, + const struct wined3d_gl_info *gl_info) +{ + unsigned int level, level_count, layer, layer_count; + GLsizei width, height, depth; + GLenum target; + + level_count = texture_gl->t.level_count; + if (texture_gl->target == GL_TEXTURE_1D_ARRAY || texture_gl->target == GL_TEXTURE_2D_ARRAY) + layer_count = 1; + else + layer_count = texture_gl->t.layer_count; + + for (layer = 0; layer < layer_count; ++layer) + { + target = wined3d_texture_gl_get_sub_resource_target(texture_gl, layer * level_count); + + for (level = 0; level < level_count; ++level) + { + width = wined3d_texture_get_level_pow2_width(&texture_gl->t, level); + height = wined3d_texture_get_level_pow2_height(&texture_gl->t, level); + if (texture_gl->t.resource.format_flags & WINED3DFMT_FLAG_HEIGHT_SCALE) + { + height *= format->f.height_scale.numerator; + height /= format->f.height_scale.denominator; + } + + TRACE("texture_gl %p, layer %u, level %u, target %#x, width %u, height %u.\n", + texture_gl, layer, level, target, width, height); + + if (target == GL_TEXTURE_3D || target == GL_TEXTURE_2D_ARRAY) + { + depth = wined3d_texture_get_level_depth(&texture_gl->t, level); + GL_EXTCALL(glTexImage3D(target, level, gl_internal_format, width, height, + target == GL_TEXTURE_2D_ARRAY ? texture_gl->t.layer_count : depth, 0, + format->format, format->type, NULL)); + checkGLcall("glTexImage3D"); + } + else if (target == GL_TEXTURE_1D) + { + gl_info->gl_ops.gl.p_glTexImage1D(target, level, gl_internal_format, + width, 0, format->format, format->type, NULL); + } + else + { + gl_info->gl_ops.gl.p_glTexImage2D(target, level, gl_internal_format, width, + target == GL_TEXTURE_1D_ARRAY ? texture_gl->t.layer_count : height, 0, + format->format, format->type, NULL); + checkGLcall("glTexImage2D"); + } + } + } +} + +/* Context activation is done by the caller. */ +/* The caller is responsible for binding the correct texture. */ +static void wined3d_texture_gl_allocate_immutable_storage(struct wined3d_texture_gl *texture_gl, + GLenum gl_internal_format, const struct wined3d_gl_info *gl_info) +{ + unsigned int samples = wined3d_resource_get_sample_count(&texture_gl->t.resource); + GLsizei height = wined3d_texture_get_level_pow2_height(&texture_gl->t, 0); + GLsizei width = wined3d_texture_get_level_pow2_width(&texture_gl->t, 0); + GLboolean standard_pattern = texture_gl->t.resource.multisample_type != WINED3D_MULTISAMPLE_NON_MASKABLE + && texture_gl->t.resource.multisample_quality == WINED3D_STANDARD_MULTISAMPLE_PATTERN; + + switch (texture_gl->target) + { + case GL_TEXTURE_3D: + GL_EXTCALL(glTexStorage3D(texture_gl->target, texture_gl->t.level_count, + gl_internal_format, width, height, wined3d_texture_get_level_depth(&texture_gl->t, 0))); + break; + case GL_TEXTURE_2D_ARRAY: + GL_EXTCALL(glTexStorage3D(texture_gl->target, texture_gl->t.level_count, + gl_internal_format, width, height, texture_gl->t.layer_count)); + break; + case GL_TEXTURE_2D_MULTISAMPLE: + GL_EXTCALL(glTexStorage2DMultisample(texture_gl->target, samples, + gl_internal_format, width, height, standard_pattern)); + break; + case GL_TEXTURE_2D_MULTISAMPLE_ARRAY: + GL_EXTCALL(glTexStorage3DMultisample(texture_gl->target, samples, + gl_internal_format, width, height, texture_gl->t.layer_count, standard_pattern)); + break; + case GL_TEXTURE_1D_ARRAY: + GL_EXTCALL(glTexStorage2D(texture_gl->target, texture_gl->t.level_count, + gl_internal_format, width, texture_gl->t.layer_count)); + break; + case GL_TEXTURE_1D: + GL_EXTCALL(glTexStorage1D(texture_gl->target, texture_gl->t.level_count, gl_internal_format, width)); + break; + default: + GL_EXTCALL(glTexStorage2D(texture_gl->target, texture_gl->t.level_count, + gl_internal_format, width, height)); + break; + } + + checkGLcall("allocate immutable storage"); +} + +void wined3d_texture_sub_resources_destroyed(struct wined3d_texture *texture) +{ + unsigned int sub_count = texture->level_count * texture->layer_count; + struct wined3d_texture_sub_resource *sub_resource; + unsigned int i; + + for (i = 0; i < sub_count; ++i) + { + sub_resource = &texture->sub_resources[i]; + if (sub_resource->parent) + { + TRACE("sub-resource %u.\n", i); + sub_resource->parent_ops->wined3d_object_destroyed(sub_resource->parent); + sub_resource->parent = NULL; + } + } +} + +static void wined3d_texture_create_dc(void *object) +{ + const struct wined3d_texture_idx *idx = object; + struct wined3d_context *context = NULL; + unsigned int sub_resource_idx, level; + const struct wined3d_format *format; + unsigned int row_pitch, slice_pitch; + struct wined3d_texture *texture; + struct wined3d_dc_info *dc_info; + struct wined3d_bo_address data; + D3DKMT_CREATEDCFROMMEMORY desc; + struct wined3d_device *device; + NTSTATUS status; + + TRACE("texture %p, sub_resource_idx %u.\n", idx->texture, idx->sub_resource_idx); + + texture = idx->texture; + sub_resource_idx = idx->sub_resource_idx; + level = sub_resource_idx % texture->level_count; + device = texture->resource.device; + + format = texture->resource.format; + if (!format->ddi_format) + { + WARN("Cannot create a DC for format %s.\n", debug_d3dformat(format->id)); + return; + } + + if (!texture->dc_info) + { + unsigned int sub_count = texture->level_count * texture->layer_count; + + if (!(texture->dc_info = heap_calloc(sub_count, sizeof(*texture->dc_info)))) + { + ERR("Failed to allocate DC info.\n"); + return; + } + } + + if (!(texture->sub_resources[sub_resource_idx].locations & texture->resource.map_binding)) + { + context = context_acquire(device, NULL, 0); + wined3d_texture_load_location(texture, sub_resource_idx, context, texture->resource.map_binding); + } + wined3d_texture_invalidate_location(texture, sub_resource_idx, ~texture->resource.map_binding); + wined3d_texture_get_pitch(texture, level, &row_pitch, &slice_pitch); + wined3d_texture_get_memory(texture, sub_resource_idx, &data, texture->resource.map_binding); + if (data.buffer_object) + { + if (!context) + context = context_acquire(device, NULL, 0); + desc.pMemory = wined3d_context_map_bo_address(context, &data, + texture->sub_resources[sub_resource_idx].size, WINED3D_MAP_READ | WINED3D_MAP_WRITE); + } + else + { + desc.pMemory = data.addr; + } + + if (context) + context_release(context); + + desc.Format = format->ddi_format; + desc.Width = wined3d_texture_get_level_width(texture, level); + desc.Height = wined3d_texture_get_level_height(texture, level); + desc.Pitch = row_pitch; + desc.hDeviceDc = CreateCompatibleDC(NULL); + desc.pColorTable = NULL; + + status = D3DKMTCreateDCFromMemory(&desc); + DeleteDC(desc.hDeviceDc); + if (status) + { + WARN("Failed to create DC, status %#x.\n", status); + return; + } + + dc_info = &texture->dc_info[sub_resource_idx]; + dc_info->dc = desc.hDc; + dc_info->bitmap = desc.hBitmap; + + TRACE("Created DC %p, bitmap %p for texture %p, %u.\n", dc_info->dc, dc_info->bitmap, texture, sub_resource_idx); +} + +static void wined3d_texture_destroy_dc(void *object) +{ + const struct wined3d_texture_idx *idx = object; + D3DKMT_DESTROYDCFROMMEMORY destroy_desc; + struct wined3d_context *context; + struct wined3d_texture *texture; + struct wined3d_dc_info *dc_info; + struct wined3d_bo_address data; + unsigned int sub_resource_idx; + struct wined3d_device *device; + struct wined3d_range range; + NTSTATUS status; + + TRACE("texture %p, sub_resource_idx %u.\n", idx->texture, idx->sub_resource_idx); + + texture = idx->texture; + sub_resource_idx = idx->sub_resource_idx; + device = texture->resource.device; + dc_info = &texture->dc_info[sub_resource_idx]; + + if (!dc_info->dc) + { + ERR("Sub-resource {%p, %u} has no DC.\n", texture, sub_resource_idx); + return; + } + + TRACE("dc %p, bitmap %p.\n", dc_info->dc, dc_info->bitmap); + + destroy_desc.hDc = dc_info->dc; + destroy_desc.hBitmap = dc_info->bitmap; + if ((status = D3DKMTDestroyDCFromMemory(&destroy_desc))) + ERR("Failed to destroy dc, status %#x.\n", status); + dc_info->dc = NULL; + dc_info->bitmap = NULL; + + wined3d_texture_get_memory(texture, sub_resource_idx, &data, texture->resource.map_binding); + if (data.buffer_object) + { + context = context_acquire(device, NULL, 0); + range.offset = 0; + range.size = texture->sub_resources[sub_resource_idx].size; + wined3d_context_unmap_bo_address(context, &data, 1, &range); + context_release(context); + } +} + +void wined3d_texture_set_swapchain(struct wined3d_texture *texture, struct wined3d_swapchain *swapchain) +{ + texture->swapchain = swapchain; + wined3d_resource_update_draw_binding(&texture->resource); +} + +void wined3d_gl_texture_swizzle_from_color_fixup(GLint swizzle[4], struct color_fixup_desc fixup) +{ + static const GLenum swizzle_source[] = + { + GL_ZERO, /* CHANNEL_SOURCE_ZERO */ + GL_ONE, /* CHANNEL_SOURCE_ONE */ + GL_RED, /* CHANNEL_SOURCE_X */ + GL_GREEN, /* CHANNEL_SOURCE_Y */ + GL_BLUE, /* CHANNEL_SOURCE_Z */ + GL_ALPHA, /* CHANNEL_SOURCE_W */ + }; + + swizzle[0] = swizzle_source[fixup.x_source]; + swizzle[1] = swizzle_source[fixup.y_source]; + swizzle[2] = swizzle_source[fixup.z_source]; + swizzle[3] = swizzle_source[fixup.w_source]; +} + +/* Context activation is done by the caller. */ +void wined3d_texture_gl_bind(struct wined3d_texture_gl *texture_gl, + struct wined3d_context_gl *context_gl, BOOL srgb) +{ + const struct wined3d_format *format = texture_gl->t.resource.format; + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + const struct color_fixup_desc fixup = format->color_fixup; + struct gl_texture *gl_tex; + GLenum target; + + TRACE("texture_gl %p, context_gl %p, srgb %#x.\n", texture_gl, context_gl, srgb); + + if (!needs_separate_srgb_gl_texture(&context_gl->c, &texture_gl->t)) + srgb = FALSE; + + /* sRGB mode cache for preload() calls outside drawprim. */ + if (srgb) + texture_gl->t.flags |= WINED3D_TEXTURE_IS_SRGB; + else + texture_gl->t.flags &= ~WINED3D_TEXTURE_IS_SRGB; + + gl_tex = wined3d_texture_gl_get_gl_texture(texture_gl, srgb); + target = texture_gl->target; + + if (gl_tex->name) + { + wined3d_context_gl_bind_texture(context_gl, target, gl_tex->name); + return; + } + + gl_info->gl_ops.gl.p_glGenTextures(1, &gl_tex->name); + checkGLcall("glGenTextures"); + TRACE("Generated texture %d.\n", gl_tex->name); + + if (!gl_tex->name) + { + ERR("Failed to generate a texture name.\n"); + return; + } + + /* Initialise the state of the texture object to the OpenGL defaults, not + * the wined3d defaults. */ + gl_tex->sampler_desc.address_u = WINED3D_TADDRESS_WRAP; + gl_tex->sampler_desc.address_v = WINED3D_TADDRESS_WRAP; + gl_tex->sampler_desc.address_w = WINED3D_TADDRESS_WRAP; + memset(gl_tex->sampler_desc.border_color, 0, sizeof(gl_tex->sampler_desc.border_color)); + gl_tex->sampler_desc.mag_filter = WINED3D_TEXF_LINEAR; + gl_tex->sampler_desc.min_filter = WINED3D_TEXF_POINT; /* GL_NEAREST_MIPMAP_LINEAR */ + gl_tex->sampler_desc.mip_filter = WINED3D_TEXF_LINEAR; /* GL_NEAREST_MIPMAP_LINEAR */ + gl_tex->sampler_desc.lod_bias = 0.0f; + gl_tex->sampler_desc.min_lod = -1000.0f; + gl_tex->sampler_desc.max_lod = 1000.0f; + gl_tex->sampler_desc.max_anisotropy = 1; + gl_tex->sampler_desc.compare = FALSE; + gl_tex->sampler_desc.comparison_func = WINED3D_CMP_LESSEQUAL; + if (gl_info->supported[EXT_TEXTURE_SRGB_DECODE]) + gl_tex->sampler_desc.srgb_decode = TRUE; + else + gl_tex->sampler_desc.srgb_decode = srgb; + gl_tex->base_level = 0; + wined3d_texture_set_dirty(&texture_gl->t); + + wined3d_context_gl_bind_texture(context_gl, target, gl_tex->name); + + /* For a new texture we have to set the texture levels after binding the + * texture. Beware that texture rectangles do not support mipmapping, but + * set the maxmiplevel if we're relying on the partial + * GL_ARB_texture_non_power_of_two emulation with texture rectangles. + * (I.e., do not care about cond_np2 here, just look for + * GL_TEXTURE_RECTANGLE_ARB.) */ + if (target != GL_TEXTURE_RECTANGLE_ARB) + { + TRACE("Setting GL_TEXTURE_MAX_LEVEL to %u.\n", texture_gl->t.level_count - 1); + gl_info->gl_ops.gl.p_glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, texture_gl->t.level_count - 1); + checkGLcall("glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, texture->level_count)"); + } + + if (target == GL_TEXTURE_CUBE_MAP_ARB) + { + /* Cubemaps are always set to clamp, regardless of the sampler state. */ + gl_info->gl_ops.gl.p_glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + gl_info->gl_ops.gl.p_glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + gl_info->gl_ops.gl.p_glTexParameteri(target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + } + + if (texture_gl->t.flags & WINED3D_TEXTURE_COND_NP2) + { + /* Conditinal non power of two textures use a different clamping + * default. If we're using the GL_WINE_normalized_texrect partial + * driver emulation, we're dealing with a GL_TEXTURE_2D texture which + * has the address mode set to repeat - something that prevents us + * from hitting the accelerated codepath. Thus manually set the GL + * state. The same applies to filtering. Even if the texture has only + * one mip level, the default LINEAR_MIPMAP_LINEAR filter causes a SW + * fallback on macos. */ + gl_info->gl_ops.gl.p_glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + gl_info->gl_ops.gl.p_glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + gl_info->gl_ops.gl.p_glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + gl_info->gl_ops.gl.p_glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + checkGLcall("glTexParameteri"); + gl_tex->sampler_desc.address_u = WINED3D_TADDRESS_CLAMP; + gl_tex->sampler_desc.address_v = WINED3D_TADDRESS_CLAMP; + gl_tex->sampler_desc.mag_filter = WINED3D_TEXF_POINT; + gl_tex->sampler_desc.min_filter = WINED3D_TEXF_POINT; + gl_tex->sampler_desc.mip_filter = WINED3D_TEXF_NONE; + } + + if (gl_info->supported[WINED3D_GL_LEGACY_CONTEXT] && gl_info->supported[ARB_DEPTH_TEXTURE]) + { + gl_info->gl_ops.gl.p_glTexParameteri(target, GL_DEPTH_TEXTURE_MODE_ARB, GL_INTENSITY); + checkGLcall("glTexParameteri(GL_DEPTH_TEXTURE_MODE_ARB, GL_INTENSITY)"); + } + + if (!is_identity_fixup(fixup) && can_use_texture_swizzle(context_gl->c.d3d_info, format)) + { + GLint swizzle[4]; + + wined3d_gl_texture_swizzle_from_color_fixup(swizzle, fixup); + gl_info->gl_ops.gl.p_glTexParameteriv(target, GL_TEXTURE_SWIZZLE_RGBA, swizzle); + checkGLcall("set format swizzle"); + } +} + +/* Context activation is done by the caller. */ +void wined3d_texture_gl_bind_and_dirtify(struct wined3d_texture_gl *texture_gl, + struct wined3d_context_gl *context_gl, BOOL srgb) +{ + /* We don't need a specific texture unit, but after binding the texture + * the current unit is dirty. Read the unit back instead of switching to + * 0, this avoids messing around with the state manager's GL states. The + * current texture unit should always be a valid one. + * + * To be more specific, this is tricky because we can implicitly be + * called from sampler() in state.c. This means we can't touch anything + * other than whatever happens to be the currently active texture, or we + * would risk marking already applied sampler states dirty again. */ + if (context_gl->active_texture < ARRAY_SIZE(context_gl->rev_tex_unit_map)) + { + unsigned int active_sampler = context_gl->rev_tex_unit_map[context_gl->active_texture]; + if (active_sampler != WINED3D_UNMAPPED_STAGE) + context_invalidate_state(&context_gl->c, STATE_SAMPLER(active_sampler)); + } + /* FIXME: Ideally we'd only do this when touching a binding that's used by + * a shader. */ + context_invalidate_compute_state(&context_gl->c, STATE_COMPUTE_SHADER_RESOURCE_BINDING); + context_invalidate_state(&context_gl->c, STATE_GRAPHICS_SHADER_RESOURCE_BINDING); + + wined3d_texture_gl_bind(texture_gl, context_gl, srgb); +} + +/* Context activation is done by the caller (state handler). */ +/* This function relies on the correct texture being bound and loaded. */ +void wined3d_texture_gl_apply_sampler_desc(struct wined3d_texture_gl *texture_gl, + const struct wined3d_sampler_desc *sampler_desc, const struct wined3d_context_gl *context_gl) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + GLenum target = texture_gl->target; + struct gl_texture *gl_tex; + DWORD state; + + TRACE("texture_gl %p, sampler_desc %p, context_gl %p.\n", texture_gl, sampler_desc, context_gl); + + gl_tex = wined3d_texture_gl_get_gl_texture(texture_gl, texture_gl->t.flags & WINED3D_TEXTURE_IS_SRGB); + + state = sampler_desc->address_u; + if (state != gl_tex->sampler_desc.address_u) + { + gl_info->gl_ops.gl.p_glTexParameteri(target, GL_TEXTURE_WRAP_S, + gl_info->wrap_lookup[state - WINED3D_TADDRESS_WRAP]); + gl_tex->sampler_desc.address_u = state; + } + + state = sampler_desc->address_v; + if (state != gl_tex->sampler_desc.address_v) + { + gl_info->gl_ops.gl.p_glTexParameteri(target, GL_TEXTURE_WRAP_T, + gl_info->wrap_lookup[state - WINED3D_TADDRESS_WRAP]); + gl_tex->sampler_desc.address_v = state; + } + + state = sampler_desc->address_w; + if (state != gl_tex->sampler_desc.address_w) + { + gl_info->gl_ops.gl.p_glTexParameteri(target, GL_TEXTURE_WRAP_R, + gl_info->wrap_lookup[state - WINED3D_TADDRESS_WRAP]); + gl_tex->sampler_desc.address_w = state; + } + + if (memcmp(gl_tex->sampler_desc.border_color, sampler_desc->border_color, + sizeof(gl_tex->sampler_desc.border_color))) + { + gl_info->gl_ops.gl.p_glTexParameterfv(target, GL_TEXTURE_BORDER_COLOR, &sampler_desc->border_color[0]); + memcpy(gl_tex->sampler_desc.border_color, sampler_desc->border_color, + sizeof(gl_tex->sampler_desc.border_color)); + } + + state = sampler_desc->mag_filter; + if (state != gl_tex->sampler_desc.mag_filter) + { + gl_info->gl_ops.gl.p_glTexParameteri(target, GL_TEXTURE_MAG_FILTER, wined3d_gl_mag_filter(state)); + gl_tex->sampler_desc.mag_filter = state; + } + + if (sampler_desc->min_filter != gl_tex->sampler_desc.min_filter + || sampler_desc->mip_filter != gl_tex->sampler_desc.mip_filter) + { + gl_info->gl_ops.gl.p_glTexParameteri(target, GL_TEXTURE_MIN_FILTER, + wined3d_gl_min_mip_filter(sampler_desc->min_filter, sampler_desc->mip_filter)); + gl_tex->sampler_desc.min_filter = sampler_desc->min_filter; + gl_tex->sampler_desc.mip_filter = sampler_desc->mip_filter; + } + + state = sampler_desc->max_anisotropy; + if (state != gl_tex->sampler_desc.max_anisotropy) + { + if (gl_info->supported[ARB_TEXTURE_FILTER_ANISOTROPIC]) + gl_info->gl_ops.gl.p_glTexParameteri(target, GL_TEXTURE_MAX_ANISOTROPY, state); + else + WARN("Anisotropic filtering not supported.\n"); + gl_tex->sampler_desc.max_anisotropy = state; + } + + if (!sampler_desc->srgb_decode != !gl_tex->sampler_desc.srgb_decode + && (context_gl->c.d3d_info->wined3d_creation_flags & WINED3D_SRGB_READ_WRITE_CONTROL) + && gl_info->supported[EXT_TEXTURE_SRGB_DECODE]) + { + gl_info->gl_ops.gl.p_glTexParameteri(target, GL_TEXTURE_SRGB_DECODE_EXT, + sampler_desc->srgb_decode ? GL_DECODE_EXT : GL_SKIP_DECODE_EXT); + gl_tex->sampler_desc.srgb_decode = sampler_desc->srgb_decode; + } + + if (!sampler_desc->compare != !gl_tex->sampler_desc.compare) + { + if (sampler_desc->compare) + gl_info->gl_ops.gl.p_glTexParameteri(target, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB); + else + gl_info->gl_ops.gl.p_glTexParameteri(target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE); + gl_tex->sampler_desc.compare = sampler_desc->compare; + } + + checkGLcall("Texture parameter application"); + + if (gl_info->supported[EXT_TEXTURE_LOD_BIAS]) + { + gl_info->gl_ops.gl.p_glTexEnvf(GL_TEXTURE_FILTER_CONTROL_EXT, + GL_TEXTURE_LOD_BIAS_EXT, sampler_desc->lod_bias); + checkGLcall("glTexEnvf(GL_TEXTURE_LOD_BIAS_EXT, ...)"); + } +} + +ULONG CDECL wined3d_texture_incref(struct wined3d_texture *texture) +{ + ULONG refcount; + + TRACE("texture %p, swapchain %p.\n", texture, texture->swapchain); + + if (texture->swapchain) + return wined3d_swapchain_incref(texture->swapchain); + + refcount = InterlockedIncrement(&texture->resource.ref); + TRACE("%p increasing refcount to %u.\n", texture, refcount); + + return refcount; +} + +static void wined3d_texture_destroy_object(void *object) +{ + struct wined3d_texture *texture = object; + struct wined3d_resource *resource; + struct wined3d_dc_info *dc_info; + unsigned int sub_count; + unsigned int i; + + TRACE("texture %p.\n", texture); + + resource = &texture->resource; + sub_count = texture->level_count * texture->layer_count; + + if ((dc_info = texture->dc_info)) + { + for (i = 0; i < sub_count; ++i) + { + if (dc_info[i].dc) + { + struct wined3d_texture_idx texture_idx = {texture, i}; + + wined3d_texture_destroy_dc(&texture_idx); + } + } + heap_free(dc_info); + } + + if (texture->overlay_info) + { + for (i = 0; i < sub_count; ++i) + { + struct wined3d_overlay_info *info = &texture->overlay_info[i]; + struct wined3d_overlay_info *overlay, *cur; + + list_remove(&info->entry); + LIST_FOR_EACH_ENTRY_SAFE(overlay, cur, &info->overlays, struct wined3d_overlay_info, entry) + { + list_remove(&overlay->entry); + } + } + heap_free(texture->overlay_info); + } + + if (texture->dirty_regions) + { + for (i = 0; i < texture->layer_count; ++i) + { + heap_free(texture->dirty_regions[i].boxes); + } + heap_free(texture->dirty_regions); + } + + resource->resource_ops->resource_unload(resource); +} + +void wined3d_texture_cleanup(struct wined3d_texture *texture) +{ + wined3d_cs_destroy_object(texture->resource.device->cs, wined3d_texture_destroy_object, texture); + resource_cleanup(&texture->resource); +} + +static void wined3d_texture_cleanup_sync(struct wined3d_texture *texture) +{ + wined3d_texture_sub_resources_destroyed(texture); + wined3d_texture_cleanup(texture); + wined3d_resource_wait_idle(&texture->resource); +} + +ULONG CDECL wined3d_texture_decref(struct wined3d_texture *texture) +{ + unsigned int i, sub_resource_count; + ULONG refcount; + + TRACE("texture %p, swapchain %p.\n", texture, texture->swapchain); + + if (texture->swapchain) + return wined3d_swapchain_decref(texture->swapchain); + + refcount = InterlockedDecrement(&texture->resource.ref); + TRACE("%p decreasing refcount to %u.\n", texture, refcount); + + if (!refcount) + { + /* Wait for the texture to become idle if it's using user memory, + * since the application is allowed to free that memory once the + * texture is destroyed. Note that this implies that + * the destroy handler can't access that memory either. */ + sub_resource_count = texture->layer_count * texture->level_count; + for (i = 0; i < sub_resource_count; ++i) + { + if (texture->sub_resources[i].user_memory) + { + wined3d_resource_wait_idle(&texture->resource); + break; + } + } + texture->resource.device->adapter->adapter_ops->adapter_destroy_texture(texture); + } + + return refcount; +} + +struct wined3d_resource * CDECL wined3d_texture_get_resource(struct wined3d_texture *texture) +{ + TRACE("texture %p.\n", texture); + + return &texture->resource; +} + +static BOOL color_key_equal(const struct wined3d_color_key *c1, struct wined3d_color_key *c2) +{ + return c1->color_space_low_value == c2->color_space_low_value + && c1->color_space_high_value == c2->color_space_high_value; +} + +/* Context activation is done by the caller */ +void wined3d_texture_load(struct wined3d_texture *texture, + struct wined3d_context *context, BOOL srgb) +{ + UINT sub_count = texture->level_count * texture->layer_count; + const struct wined3d_d3d_info *d3d_info = context->d3d_info; + DWORD flag; + UINT i; + + TRACE("texture %p, context %p, srgb %#x.\n", texture, context, srgb); + + if (!needs_separate_srgb_gl_texture(context, texture)) + srgb = FALSE; + + if (srgb) + flag = WINED3D_TEXTURE_SRGB_VALID; + else + flag = WINED3D_TEXTURE_RGB_VALID; + + if (!d3d_info->shader_color_key + && (!(texture->async.flags & WINED3D_TEXTURE_ASYNC_COLOR_KEY) + != !(texture->async.color_key_flags & WINED3D_CKEY_SRC_BLT) + || (texture->async.flags & WINED3D_TEXTURE_ASYNC_COLOR_KEY + && !color_key_equal(&texture->async.gl_color_key, &texture->async.src_blt_color_key)))) + { + unsigned int sub_count = texture->level_count * texture->layer_count; + unsigned int i; + + TRACE("Reloading because of color key value change.\n"); + for (i = 0; i < sub_count; i++) + { + if (!wined3d_texture_load_location(texture, i, context, texture->resource.map_binding)) + ERR("Failed to load location %s.\n", wined3d_debug_location(texture->resource.map_binding)); + else + wined3d_texture_invalidate_location(texture, i, ~texture->resource.map_binding); + } + + texture->async.gl_color_key = texture->async.src_blt_color_key; + } + + if (texture->flags & flag) + { + TRACE("Texture %p not dirty, nothing to do.\n", texture); + return; + } + + /* Reload the surfaces if the texture is marked dirty. */ + for (i = 0; i < sub_count; ++i) + { + if (!wined3d_texture_load_location(texture, i, context, + srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB)) + ERR("Failed to load location (srgb %#x).\n", srgb); + } + texture->flags |= flag; +} + +void * CDECL wined3d_texture_get_parent(const struct wined3d_texture *texture) +{ + TRACE("texture %p.\n", texture); + + return texture->resource.parent; +} + +HRESULT wined3d_texture_check_box_dimensions(const struct wined3d_texture *texture, + unsigned int level, const struct wined3d_box *box) +{ + const struct wined3d_format *format = texture->resource.format; + unsigned int width_mask, height_mask, width, height, depth; + + width = wined3d_texture_get_level_width(texture, level); + height = wined3d_texture_get_level_height(texture, level); + depth = wined3d_texture_get_level_depth(texture, level); + + if (box->left >= box->right || box->right > width + || box->top >= box->bottom || box->bottom > height + || box->front >= box->back || box->back > depth) + { + WARN("Box %s is invalid.\n", debug_box(box)); + return WINEDDERR_INVALIDRECT; + } + + if (texture->resource.format_flags & WINED3DFMT_FLAG_BLOCKS) + { + /* This assumes power of two block sizes, but NPOT block sizes would + * be silly anyway. + * + * This also assumes that the format's block depth is 1. */ + width_mask = format->block_width - 1; + height_mask = format->block_height - 1; + + if ((box->left & width_mask) || (box->top & height_mask) + || (box->right & width_mask && box->right != width) + || (box->bottom & height_mask && box->bottom != height)) + { + WARN("Box %s is misaligned for %ux%u blocks.\n", + debug_box(box), format->block_width, format->block_height); + return WINED3DERR_INVALIDCALL; + } + } + + return WINED3D_OK; +} + +void CDECL wined3d_texture_get_pitch(const struct wined3d_texture *texture, + unsigned int level, unsigned int *row_pitch, unsigned int *slice_pitch) +{ + const struct wined3d_resource *resource = &texture->resource; + unsigned int width = wined3d_texture_get_level_width(texture, level); + unsigned int height = wined3d_texture_get_level_height(texture, level); + + if (texture->row_pitch) + { + *row_pitch = texture->row_pitch; + *slice_pitch = texture->slice_pitch; + return; + } + + wined3d_format_calculate_pitch(resource->format, resource->device->surface_alignment, + width, height, row_pitch, slice_pitch); +} + +DWORD CDECL wined3d_texture_set_lod(struct wined3d_texture *texture, DWORD lod) +{ + struct wined3d_resource *resource; + DWORD old = texture->lod; + + TRACE("texture %p, lod %u.\n", texture, lod); + + /* The d3d9:texture test shows that SetLOD is ignored on non-managed + * textures. The call always returns 0, and GetLOD always returns 0. */ + resource = &texture->resource; + if (!wined3d_resource_access_is_managed(resource->access)) + { + TRACE("Ignoring LOD on texture with resource access %s.\n", + wined3d_debug_resource_access(resource->access)); + return 0; + } + + if (lod >= texture->level_count) + lod = texture->level_count - 1; + + if (texture->lod != lod) + { + struct wined3d_device *device = resource->device; + + wined3d_resource_wait_idle(resource); + texture->lod = lod; + + wined3d_texture_gl(texture)->texture_rgb.base_level = ~0u; + wined3d_texture_gl(texture)->texture_srgb.base_level = ~0u; + if (resource->bind_count) + wined3d_cs_emit_set_sampler_state(device->cs, texture->sampler, WINED3D_SAMP_MAX_MIP_LEVEL, + device->state.sampler_states[texture->sampler][WINED3D_SAMP_MAX_MIP_LEVEL]); + } + + return old; +} + +DWORD CDECL wined3d_texture_get_lod(const struct wined3d_texture *texture) +{ + TRACE("texture %p, returning %u.\n", texture, texture->lod); + + return texture->lod; +} + +DWORD CDECL wined3d_texture_get_level_count(const struct wined3d_texture *texture) +{ + TRACE("texture %p, returning %u.\n", texture, texture->level_count); + + return texture->level_count; +} + +HRESULT CDECL wined3d_texture_set_color_key(struct wined3d_texture *texture, + DWORD flags, const struct wined3d_color_key *color_key) +{ + struct wined3d_device *device = texture->resource.device; + static const DWORD all_flags = WINED3D_CKEY_DST_BLT | WINED3D_CKEY_DST_OVERLAY + | WINED3D_CKEY_SRC_BLT | WINED3D_CKEY_SRC_OVERLAY; + + TRACE("texture %p, flags %#x, color_key %p.\n", texture, flags, color_key); + + if (flags & ~all_flags) + { + WARN("Invalid flags passed, returning WINED3DERR_INVALIDCALL.\n"); + return WINED3DERR_INVALIDCALL; + } + + wined3d_cs_emit_set_color_key(device->cs, texture, flags, color_key); + + return WINED3D_OK; +} + +/* In D3D the depth stencil dimensions have to be greater than or equal to the + * render target dimensions. With FBOs, the dimensions have to be an exact match. */ +/* TODO: We should synchronize the renderbuffer's content with the texture's content. */ +/* Context activation is done by the caller. */ +void wined3d_texture_gl_set_compatible_renderbuffer(struct wined3d_texture_gl *texture_gl, + struct wined3d_context_gl *context_gl, unsigned int level, const struct wined3d_rendertarget_info *rt) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + struct wined3d_renderbuffer_entry *entry; + unsigned int src_width, src_height; + unsigned int width, height; + GLuint renderbuffer = 0; + + if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]) + return; + + if (rt && rt->resource->format->id != WINED3DFMT_NULL) + { + struct wined3d_texture *rt_texture; + unsigned int rt_level; + + if (rt->resource->type == WINED3D_RTYPE_BUFFER) + { + FIXME("Unsupported resource type %s.\n", debug_d3dresourcetype(rt->resource->type)); + return; + } + rt_texture = wined3d_texture_from_resource(rt->resource); + rt_level = rt->sub_resource_idx % rt_texture->level_count; + + width = wined3d_texture_get_level_pow2_width(rt_texture, rt_level); + height = wined3d_texture_get_level_pow2_height(rt_texture, rt_level); + } + else + { + width = wined3d_texture_get_level_pow2_width(&texture_gl->t, level); + height = wined3d_texture_get_level_pow2_height(&texture_gl->t, level); + } + + src_width = wined3d_texture_get_level_pow2_width(&texture_gl->t, level); + src_height = wined3d_texture_get_level_pow2_height(&texture_gl->t, level); + + /* A depth stencil smaller than the render target is not valid */ + if (width > src_width || height > src_height) + return; + + /* Remove any renderbuffer set if the sizes match */ + if (width == src_width && height == src_height) + { + texture_gl->current_renderbuffer = NULL; + return; + } + + /* Look if we've already got a renderbuffer of the correct dimensions */ + LIST_FOR_EACH_ENTRY(entry, &texture_gl->renderbuffers, struct wined3d_renderbuffer_entry, entry) + { + if (entry->width == width && entry->height == height) + { + renderbuffer = entry->id; + texture_gl->current_renderbuffer = entry; + break; + } + } + + if (!renderbuffer) + { + const struct wined3d_format_gl *format_gl; + + format_gl = wined3d_format_gl(texture_gl->t.resource.format); + gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer); + gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer); + gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, format_gl->internal, width, height); + + entry = heap_alloc(sizeof(*entry)); + entry->width = width; + entry->height = height; + entry->id = renderbuffer; + list_add_head(&texture_gl->renderbuffers, &entry->entry); + + texture_gl->current_renderbuffer = entry; + } + + checkGLcall("set compatible renderbuffer"); +} + +HRESULT CDECL wined3d_texture_update_desc(struct wined3d_texture *texture, unsigned int sub_resource_idx, + UINT width, UINT height, enum wined3d_format_id format_id, + enum wined3d_multisample_type multisample_type, UINT multisample_quality, void *mem, UINT pitch) +{ + struct wined3d_texture_sub_resource *sub_resource; + unsigned int i, level, sub_resource_count; + const struct wined3d_d3d_info *d3d_info; + const struct wined3d_gl_info *gl_info; + const struct wined3d_format *format; + struct wined3d_device *device; + unsigned int resource_size; + const struct wined3d *d3d; + unsigned int slice_pitch; + bool update_memory_only; + bool create_dib = false; + + TRACE("texture %p, width %u, height %u, format %s, multisample_type %#x, multisample_quality %u, " + "mem %p, pitch %u, sub_resource_idx %u.\n", + texture, width, height, debug_d3dformat(format_id), multisample_type, multisample_quality, mem, pitch, + sub_resource_idx); + + device = texture->resource.device; + d3d = device->wined3d; + gl_info = &device->adapter->gl_info; + d3d_info = &device->adapter->d3d_info; + format = wined3d_get_format(device->adapter, format_id, texture->resource.bind_flags); + resource_size = wined3d_format_calculate_size(format, device->surface_alignment, width, height, 1); + level = sub_resource_idx % texture->level_count; + sub_resource_count = texture->level_count * texture->layer_count; + + update_memory_only = width == wined3d_texture_get_level_width(texture, level) + && height == wined3d_texture_get_level_height(texture, level) + && format_id == texture->resource.format->id && multisample_type == texture->resource.multisample_type + && multisample_quality == texture->resource.multisample_quality; + + if (pitch) + slice_pitch = height * pitch; + else + wined3d_format_calculate_pitch(format, 1, width, height, &pitch, &slice_pitch); + + if (update_memory_only) + { + unsigned int current_row_pitch, current_slice_pitch; + + wined3d_texture_get_pitch(texture, level, ¤t_row_pitch, ¤t_slice_pitch); + update_memory_only = pitch == current_row_pitch && slice_pitch == current_slice_pitch; + } + + if (!resource_size) + return WINED3DERR_INVALIDCALL; + + if (sub_resource_count > 1 && !update_memory_only) + { + FIXME("Texture has multiple sub-resources, not supported.\n"); + return WINED3DERR_INVALIDCALL; + } + + if (texture->resource.type != WINED3D_RTYPE_TEXTURE_2D) + { + WARN("Not supported on %s.\n", debug_d3dresourcetype(texture->resource.type)); + return WINED3DERR_INVALIDCALL; + } + + if (texture->resource.map_count) + { + WARN("Texture is mapped.\n"); + return WINED3DERR_INVALIDCALL; + } + + /* We have no way of supporting a pitch that is not a multiple of the pixel + * byte width short of uploading the texture row-by-row. + * Fortunately that's not an issue since D3D9Ex doesn't allow a custom pitch + * for user-memory textures (it always expects packed data) while DirectDraw + * requires a 4-byte aligned pitch and doesn't support texture formats + * larger than 4 bytes per pixel nor any format using 3 bytes per pixel. + * This check is here to verify that the assumption holds. */ + if (pitch % texture->resource.format->byte_count) + { + WARN("Pitch unsupported, not a multiple of the texture format byte width.\n"); + return WINED3DERR_INVALIDCALL; + } + + if (device->d3d_initialized) + wined3d_cs_emit_unload_resource(device->cs, &texture->resource); + wined3d_resource_wait_idle(&texture->resource); + + if (texture->dc_info && texture->dc_info[0].dc) + { + struct wined3d_texture_idx texture_idx = {texture, sub_resource_idx}; + + wined3d_cs_destroy_object(device->cs, wined3d_texture_destroy_dc, &texture_idx); + wined3d_cs_finish(device->cs, WINED3D_CS_QUEUE_DEFAULT); + create_dib = true; + } + + texture->sub_resources[sub_resource_idx].user_memory = mem; + + if (update_memory_only) + { + for (i = 0; i < sub_resource_count; ++i) + if (!texture->sub_resources[i].user_memory) + break; + + if (i == sub_resource_count) + wined3d_resource_free_sysmem(&texture->resource); + } + else + { + wined3d_resource_free_sysmem(&texture->resource); + + sub_resource = &texture->sub_resources[sub_resource_idx]; + + texture->row_pitch = pitch; + texture->slice_pitch = slice_pitch; + + texture->resource.format = format; + texture->resource.multisample_type = multisample_type; + texture->resource.multisample_quality = multisample_quality; + texture->resource.width = width; + texture->resource.height = height; + if (!(texture->resource.access & WINED3D_RESOURCE_ACCESS_CPU) && d3d->flags & WINED3D_VIDMEM_ACCOUNTING) + adapter_adjust_memory(device->adapter, (INT64)texture->slice_pitch - texture->resource.size); + texture->resource.size = texture->slice_pitch; + sub_resource->size = texture->slice_pitch; + sub_resource->locations = WINED3D_LOCATION_DISCARDED; + + if (texture->texture_ops == &texture_gl_ops) + { + if (multisample_type && gl_info->supported[ARB_TEXTURE_MULTISAMPLE]) + { + wined3d_texture_gl(texture)->target = GL_TEXTURE_2D_MULTISAMPLE; + texture->flags &= ~WINED3D_TEXTURE_DOWNLOADABLE; + } + else + { + wined3d_texture_gl(texture)->target = GL_TEXTURE_2D; + texture->flags |= WINED3D_TEXTURE_DOWNLOADABLE; + } + } + + if (((width & (width - 1)) || (height & (height - 1))) && !d3d_info->texture_npot + && !d3d_info->texture_npot_conditional) + { + texture->flags |= WINED3D_TEXTURE_COND_NP2_EMULATED; + texture->pow2_width = texture->pow2_height = 1; + while (texture->pow2_width < width) + texture->pow2_width <<= 1; + while (texture->pow2_height < height) + texture->pow2_height <<= 1; + } + else + { + texture->flags &= ~WINED3D_TEXTURE_COND_NP2_EMULATED; + texture->pow2_width = width; + texture->pow2_height = height; + } + } + + if (!mem && !wined3d_resource_prepare_sysmem(&texture->resource)) + ERR("Failed to allocate resource memory.\n"); + + /* The format might be changed to a format that needs conversion. + * If the surface didn't use PBOs previously but could now, don't + * change it - whatever made us not use PBOs might come back, e.g. + * color keys. */ + if (texture->resource.map_binding == WINED3D_LOCATION_BUFFER && !wined3d_texture_use_pbo(texture, gl_info)) + texture->resource.map_binding = WINED3D_LOCATION_SYSMEM; + + wined3d_texture_validate_location(texture, sub_resource_idx, WINED3D_LOCATION_SYSMEM); + wined3d_texture_invalidate_location(texture, sub_resource_idx, ~WINED3D_LOCATION_SYSMEM); + + if (create_dib) + { + struct wined3d_texture_idx texture_idx = {texture, sub_resource_idx}; + + wined3d_cs_init_object(device->cs, wined3d_texture_create_dc, &texture_idx); + wined3d_cs_finish(device->cs, WINED3D_CS_QUEUE_DEFAULT); + } + + return WINED3D_OK; +} + +/* Context activation is done by the caller. */ +static void wined3d_texture_gl_prepare_buffer_object(struct wined3d_texture_gl *texture_gl, + unsigned int sub_resource_idx, struct wined3d_context_gl *context_gl) +{ + struct wined3d_texture_sub_resource *sub_resource; + struct wined3d_bo_gl *bo; + + sub_resource = &texture_gl->t.sub_resources[sub_resource_idx]; + bo = &sub_resource->bo; + if (bo->id) + return; + + if (!wined3d_context_gl_create_bo(context_gl, sub_resource->size, GL_PIXEL_UNPACK_BUFFER, + GL_STREAM_DRAW, true, GL_MAP_READ_BIT | GL_MAP_WRITE_BIT | GL_CLIENT_STORAGE_BIT, bo)) + return; + + TRACE("Created buffer object %u for texture %p, sub-resource %u.\n", bo->id, texture_gl, sub_resource_idx); +} + +static void wined3d_texture_force_reload(struct wined3d_texture *texture) +{ + unsigned int sub_count = texture->level_count * texture->layer_count; + unsigned int i; + + texture->flags &= ~(WINED3D_TEXTURE_RGB_ALLOCATED | WINED3D_TEXTURE_SRGB_ALLOCATED + | WINED3D_TEXTURE_CONVERTED); + texture->async.flags &= ~WINED3D_TEXTURE_ASYNC_COLOR_KEY; + for (i = 0; i < sub_count; ++i) + { + wined3d_texture_invalidate_location(texture, i, + WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB); + } +} + +/* Context activation is done by the caller. */ +void wined3d_texture_gl_prepare_texture(struct wined3d_texture_gl *texture_gl, + struct wined3d_context_gl *context_gl, BOOL srgb) +{ + DWORD alloc_flag = srgb ? WINED3D_TEXTURE_SRGB_ALLOCATED : WINED3D_TEXTURE_RGB_ALLOCATED; + const struct wined3d_d3d_info *d3d_info = context_gl->c.d3d_info; + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + struct wined3d_resource *resource = &texture_gl->t.resource; + const struct wined3d_device *device = resource->device; + const struct wined3d_format *format = resource->format; + const struct wined3d_color_key_conversion *conversion; + const struct wined3d_format_gl *format_gl; + GLenum internal; + + TRACE("texture_gl %p, context_gl %p, format %s.\n", texture_gl, context_gl, debug_d3dformat(format->id)); + + if (!d3d_info->shader_color_key + && !(texture_gl->t.async.flags & WINED3D_TEXTURE_ASYNC_COLOR_KEY) + != !(texture_gl->t.async.color_key_flags & WINED3D_CKEY_SRC_BLT)) + { + wined3d_texture_force_reload(&texture_gl->t); + + if (texture_gl->t.async.color_key_flags & WINED3D_CKEY_SRC_BLT) + texture_gl->t.async.flags |= WINED3D_TEXTURE_ASYNC_COLOR_KEY; + } + + if (texture_gl->t.flags & alloc_flag) + return; + + if (resource->format_flags & WINED3DFMT_FLAG_DECOMPRESS) + { + TRACE("WINED3DFMT_FLAG_DECOMPRESS set.\n"); + texture_gl->t.flags |= WINED3D_TEXTURE_CONVERTED; + format = wined3d_resource_get_decompress_format(resource); + } + else if (format->conv_byte_count) + { + texture_gl->t.flags |= WINED3D_TEXTURE_CONVERTED; + } + else if ((conversion = wined3d_format_get_color_key_conversion(&texture_gl->t, TRUE))) + { + texture_gl->t.flags |= WINED3D_TEXTURE_CONVERTED; + format = wined3d_get_format(device->adapter, conversion->dst_format, resource->bind_flags); + TRACE("Using format %s for color key conversion.\n", debug_d3dformat(format->id)); + } + format_gl = wined3d_format_gl(format); + + wined3d_texture_gl_bind_and_dirtify(texture_gl, context_gl, srgb); + + if (srgb) + internal = format_gl->srgb_internal; + else if (resource->bind_flags & WINED3D_BIND_RENDER_TARGET && wined3d_resource_is_offscreen(resource)) + internal = format_gl->rt_internal; + else + internal = format_gl->internal; + + if (!internal) + FIXME("No GL internal format for format %s.\n", debug_d3dformat(format->id)); + + TRACE("internal %#x, format %#x, type %#x.\n", internal, format_gl->format, format_gl->type); + + if (wined3d_texture_use_immutable_storage(&texture_gl->t, gl_info)) + wined3d_texture_gl_allocate_immutable_storage(texture_gl, internal, gl_info); + else + wined3d_texture_gl_allocate_mutable_storage(texture_gl, internal, format_gl, gl_info); + texture_gl->t.flags |= alloc_flag; +} + +static void wined3d_texture_gl_prepare_rb(struct wined3d_texture_gl *texture_gl, + const struct wined3d_gl_info *gl_info, BOOL multisample) +{ + const struct wined3d_format_gl *format_gl; + + format_gl = wined3d_format_gl(texture_gl->t.resource.format); + if (multisample) + { + DWORD samples; + + if (texture_gl->rb_multisample) + return; + + samples = wined3d_resource_get_sample_count(&texture_gl->t.resource); + + gl_info->fbo_ops.glGenRenderbuffers(1, &texture_gl->rb_multisample); + gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, texture_gl->rb_multisample); + gl_info->fbo_ops.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, + format_gl->internal, texture_gl->t.resource.width, texture_gl->t.resource.height); + checkGLcall("glRenderbufferStorageMultisample()"); + TRACE("Created multisample rb %u.\n", texture_gl->rb_multisample); + } + else + { + if (texture_gl->rb_resolved) + return; + + gl_info->fbo_ops.glGenRenderbuffers(1, &texture_gl->rb_resolved); + gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, texture_gl->rb_resolved); + gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, format_gl->internal, + texture_gl->t.resource.width, texture_gl->t.resource.height); + checkGLcall("glRenderbufferStorage()"); + TRACE("Created resolved rb %u.\n", texture_gl->rb_resolved); + } +} + +BOOL wined3d_texture_prepare_location(struct wined3d_texture *texture, + unsigned int sub_resource_idx, struct wined3d_context *context, unsigned int location) +{ + return texture->texture_ops->texture_prepare_location(texture, sub_resource_idx, context, location); +} + +static void wined3d_texture_unload_location(struct wined3d_texture *texture, + struct wined3d_context *context, unsigned int location) +{ + texture->texture_ops->texture_unload_location(texture, context, location); +} + +static struct wined3d_texture_sub_resource *wined3d_texture_get_sub_resource(struct wined3d_texture *texture, + unsigned int sub_resource_idx) +{ + UINT sub_count = texture->level_count * texture->layer_count; + + TRACE("texture %p, sub_resource_idx %u.\n", texture, sub_resource_idx); + + if (sub_resource_idx >= sub_count) + { + WARN("sub_resource_idx %u >= sub_count %u.\n", sub_resource_idx, sub_count); + return NULL; + } + + return &texture->sub_resources[sub_resource_idx]; +} + +static void wined3d_texture_dirty_region_add(struct wined3d_texture *texture, + unsigned int layer, const struct wined3d_box *box) +{ + struct wined3d_dirty_regions *regions; + unsigned int count; + + if (!texture->dirty_regions) + return; + + regions = &texture->dirty_regions[layer]; + count = regions->box_count + 1; + if (count >= WINED3D_MAX_DIRTY_REGION_COUNT || !box + || (!box->left && !box->top && !box->front + && box->right == texture->resource.width + && box->bottom == texture->resource.height + && box->back == texture->resource.depth)) + { + regions->box_count = WINED3D_MAX_DIRTY_REGION_COUNT; + return; + } + + if (!wined3d_array_reserve((void **)®ions->boxes, ®ions->boxes_size, count, sizeof(*regions->boxes))) + { + WARN("Failed to grow boxes array, marking entire texture dirty.\n"); + regions->box_count = WINED3D_MAX_DIRTY_REGION_COUNT; + return; + } + + regions->boxes[regions->box_count++] = *box; +} + +HRESULT CDECL wined3d_texture_add_dirty_region(struct wined3d_texture *texture, + UINT layer, const struct wined3d_box *dirty_region) +{ + TRACE("texture %p, layer %u, dirty_region %s.\n", texture, layer, debug_box(dirty_region)); + + if (layer >= texture->layer_count) + { + WARN("Invalid layer %u specified.\n", layer); + return WINED3DERR_INVALIDCALL; + } + + if (dirty_region && FAILED(wined3d_texture_check_box_dimensions(texture, 0, dirty_region))) + { + WARN("Invalid dirty_region %s specified.\n", debug_box(dirty_region)); + return WINED3DERR_INVALIDCALL; + } + + wined3d_texture_dirty_region_add(texture, layer, dirty_region); + wined3d_cs_emit_add_dirty_texture_region(texture->resource.device->cs, texture, layer); + + return WINED3D_OK; +} + +static void wined3d_texture_gl_upload_bo(const struct wined3d_format *src_format, GLenum target, + unsigned int level, unsigned int src_row_pitch, unsigned int dst_x, unsigned int dst_y, + unsigned int dst_z, unsigned int update_w, unsigned int update_h, unsigned int update_d, + const BYTE *addr, BOOL srgb, struct wined3d_texture *dst_texture, + const struct wined3d_gl_info *gl_info) +{ + const struct wined3d_format_gl *format_gl = wined3d_format_gl(src_format); + + if (src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED) + { + unsigned int dst_row_pitch, dst_slice_pitch; + GLenum internal; + + if (srgb) + internal = format_gl->srgb_internal; + else if (dst_texture->resource.bind_flags & WINED3D_BIND_RENDER_TARGET + && wined3d_resource_is_offscreen(&dst_texture->resource)) + internal = format_gl->rt_internal; + else + internal = format_gl->internal; + + wined3d_format_calculate_pitch(src_format, 1, update_w, update_h, &dst_row_pitch, &dst_slice_pitch); + + TRACE("Uploading compressed data, target %#x, level %u, x %u, y %u, z %u, " + "w %u, h %u, d %u, format %#x, image_size %#x, addr %p.\n", + target, level, dst_x, dst_y, dst_z, update_w, update_h, + update_d, internal, dst_slice_pitch, addr); + + if (target == GL_TEXTURE_1D) + { + GL_EXTCALL(glCompressedTexSubImage1D(target, level, dst_x, + update_w, internal, dst_row_pitch, addr)); + } + else if (dst_row_pitch == src_row_pitch) + { + if (target == GL_TEXTURE_2D_ARRAY || target == GL_TEXTURE_3D) + { + GL_EXTCALL(glCompressedTexSubImage3D(target, level, dst_x, dst_y, dst_z, + update_w, update_h, update_d, internal, dst_slice_pitch * update_d, addr)); + } + else + { + GL_EXTCALL(glCompressedTexSubImage2D(target, level, dst_x, dst_y, + update_w, update_h, internal, dst_slice_pitch, addr)); + } + } + else + { + unsigned int row_count = (update_h + src_format->block_height - 1) / src_format->block_height; + unsigned int row, y, z; + + /* glCompressedTexSubImage2D() ignores pixel store state, so we + * can't use the unpack row length like for glTexSubImage2D. */ + for (z = dst_z; z < dst_z + update_d; ++z) + { + for (row = 0, y = dst_y; row < row_count; ++row) + { + if (target == GL_TEXTURE_2D_ARRAY || target == GL_TEXTURE_3D) + { + GL_EXTCALL(glCompressedTexSubImage3D(target, level, dst_x, y, z, + update_w, src_format->block_height, 1, internal, dst_row_pitch, addr)); + } + else + { + GL_EXTCALL(glCompressedTexSubImage2D(target, level, dst_x, y, + update_w, src_format->block_height, internal, dst_row_pitch, addr)); + } + + y += src_format->block_height; + addr += src_row_pitch; + } + } + } + checkGLcall("Upload compressed texture data"); + } + else + { + unsigned int y, y_count; + + TRACE("Uploading data, target %#x, level %u, x %u, y %u, z %u, " + "w %u, h %u, d %u, format %#x, type %#x, addr %p.\n", + target, level, dst_x, dst_y, dst_z, update_w, update_h, + update_d, format_gl->format, format_gl->type, addr); + + if (src_row_pitch) + { + gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, src_row_pitch / src_format->byte_count); + y_count = 1; + } + else + { + y_count = update_h; + update_h = 1; + } + + for (y = 0; y < y_count; ++y) + { + if (target == GL_TEXTURE_2D_ARRAY || target == GL_TEXTURE_3D) + { + GL_EXTCALL(glTexSubImage3D(target, level, dst_x, dst_y + y, dst_z, + update_w, update_h, update_d, format_gl->format, format_gl->type, addr)); + } + else if (target == GL_TEXTURE_1D) + { + gl_info->gl_ops.gl.p_glTexSubImage1D(target, level, dst_x, + update_w, format_gl->format, format_gl->type, addr); + } + else + { + gl_info->gl_ops.gl.p_glTexSubImage2D(target, level, dst_x, dst_y + y, + update_w, update_h, format_gl->format, format_gl->type, addr); + } + } + gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + checkGLcall("Upload texture data"); + } +} + +static const struct d3dfmt_alpha_fixup +{ + enum wined3d_format_id format_id, conv_format_id; +} +formats_src_alpha_fixup[] = +{ + {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM}, + {WINED3DFMT_B5G5R5X1_UNORM, WINED3DFMT_B5G5R5A1_UNORM}, + {WINED3DFMT_B4G4R4X4_UNORM, WINED3DFMT_B4G4R4A4_UNORM}, +}; + +static enum wined3d_format_id wined3d_get_alpha_fixup_format(enum wined3d_format_id format_id, + const struct wined3d_format *dst_format) +{ + unsigned int i; + + if (!(dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED) + && !dst_format->alpha_size) + return WINED3DFMT_UNKNOWN; + + for (i = 0; i < ARRAY_SIZE(formats_src_alpha_fixup); ++i) + { + if (formats_src_alpha_fixup[i].format_id == format_id) + return formats_src_alpha_fixup[i].conv_format_id; + } + + return WINED3DFMT_UNKNOWN; +} + +static void wined3d_fixup_alpha(const struct wined3d_format *format, const uint8_t *src, + unsigned int src_row_pitch, uint8_t *dst, unsigned int dst_row_pitch, + unsigned int width, unsigned int height) +{ + unsigned int byte_count, alpha_mask; + unsigned int x, y; + + byte_count = format->byte_count; + alpha_mask = ((1u << format->alpha_size) - 1) << format->alpha_offset; + + switch (byte_count) + { + case 2: + for (y = 0; y < height; ++y) + { + const uint16_t *src_row = (const uint16_t *)&src[y * src_row_pitch]; + uint16_t *dst_row = (uint16_t *)&dst[y * dst_row_pitch]; + + for (x = 0; x < width; ++x) + { + dst_row[x] = src_row[x] | alpha_mask; + } + } + break; + + case 4: + for (y = 0; y < height; ++y) + { + const uint32_t *src_row = (const uint32_t *)&src[y * src_row_pitch]; + uint32_t *dst_row = (uint32_t *)&dst[y * dst_row_pitch]; + + for (x = 0; x < width; ++x) + { + dst_row[x] = src_row[x] | alpha_mask; + } + } + break; + + default: + ERR("Unsupported byte count %u.\n", byte_count); + break; + } +} + +static void wined3d_texture_gl_upload_data(struct wined3d_context *context, + const struct wined3d_const_bo_address *src_bo_addr, const struct wined3d_format *src_format, + const struct wined3d_box *src_box, unsigned int src_row_pitch, unsigned int src_slice_pitch, + struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx, unsigned int dst_location, + unsigned int dst_x, unsigned int dst_y, unsigned int dst_z) +{ + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + enum wined3d_format_id alpha_fixup_format_id = WINED3DFMT_UNKNOWN; + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + unsigned int update_w = src_box->right - src_box->left; + unsigned int update_h = src_box->bottom - src_box->top; + unsigned int update_d = src_box->back - src_box->front; + struct wined3d_bo_address bo; + unsigned int level; + BOOL srgb = FALSE; + BOOL decompress; + GLenum target; + + TRACE("context %p, src_bo_addr %s, src_format %s, src_box %s, src_row_pitch %u, src_slice_pitch %u, " + "dst_texture %p, dst_sub_resource_idx %u, dst_location %s, dst_x %u, dst_y %u, dst_z %u.\n", + context, debug_const_bo_address(src_bo_addr), debug_d3dformat(src_format->id), debug_box(src_box), + src_row_pitch, src_slice_pitch, dst_texture, dst_sub_resource_idx, + wined3d_debug_location(dst_location), dst_x, dst_y, dst_z); + + if (dst_location == WINED3D_LOCATION_TEXTURE_SRGB) + { + srgb = TRUE; + } + else if (dst_location != WINED3D_LOCATION_TEXTURE_RGB) + { + FIXME("Unhandled location %s.\n", wined3d_debug_location(dst_location)); + return; + } + + wined3d_texture_gl_bind_and_dirtify(wined3d_texture_gl(dst_texture), wined3d_context_gl(context), srgb); + + if (dst_texture->sub_resources[dst_sub_resource_idx].map_count) + { + WARN("Uploading a texture that is currently mapped, setting WINED3D_TEXTURE_PIN_SYSMEM.\n"); + dst_texture->flags |= WINED3D_TEXTURE_PIN_SYSMEM; + } + + if (src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_HEIGHT_SCALE) + { + update_h *= src_format->height_scale.numerator; + update_h /= src_format->height_scale.denominator; + } + + target = wined3d_texture_gl_get_sub_resource_target(wined3d_texture_gl(dst_texture), dst_sub_resource_idx); + level = dst_sub_resource_idx % dst_texture->level_count; + + switch (target) + { + case GL_TEXTURE_1D_ARRAY: + dst_y = dst_sub_resource_idx / dst_texture->level_count; + update_h = 1; + break; + case GL_TEXTURE_2D_ARRAY: + dst_z = dst_sub_resource_idx / dst_texture->level_count; + update_d = 1; + break; + case GL_TEXTURE_2D_MULTISAMPLE: + case GL_TEXTURE_2D_MULTISAMPLE_ARRAY: + FIXME("Not supported for multisample textures.\n"); + return; + } + + bo.buffer_object = src_bo_addr->buffer_object; + bo.addr = (BYTE *)src_bo_addr->addr + src_box->front * src_slice_pitch; + if (dst_texture->resource.format_flags & WINED3DFMT_FLAG_BLOCKS) + { + bo.addr += (src_box->top / src_format->block_height) * src_row_pitch; + bo.addr += (src_box->left / src_format->block_width) * src_format->block_byte_count; + } + else + { + bo.addr += src_box->top * src_row_pitch; + bo.addr += src_box->left * src_format->byte_count; + } + + decompress = (dst_texture->resource.format_flags & WINED3DFMT_FLAG_DECOMPRESS) + || (src_format->decompress && src_format->id != dst_texture->resource.format->id); + + if (src_format->upload || decompress + || (alpha_fixup_format_id = wined3d_get_alpha_fixup_format(src_format->id, + dst_texture->resource.format)) != WINED3DFMT_UNKNOWN) + { + const struct wined3d_format *compressed_format = src_format; + unsigned int dst_row_pitch, dst_slice_pitch; + struct wined3d_format_gl f; + void *converted_mem; + unsigned int z; + BYTE *src_mem; + + if (decompress) + { + src_format = wined3d_resource_get_decompress_format(&dst_texture->resource); + } + else if (alpha_fixup_format_id != WINED3DFMT_UNKNOWN) + { + src_format = wined3d_get_format(context->device->adapter, alpha_fixup_format_id, 0); + assert(!!src_format); + } + else + { + if (dst_texture->resource.format_flags & WINED3DFMT_FLAG_BLOCKS) + ERR("Converting a block-based format.\n"); + + f = *wined3d_format_gl(src_format); + f.f.byte_count = src_format->conv_byte_count; + src_format = &f.f; + } + + wined3d_format_calculate_pitch(src_format, 1, update_w, update_h, &dst_row_pitch, &dst_slice_pitch); + + if (!(converted_mem = heap_alloc(dst_slice_pitch))) + { + ERR("Failed to allocate upload buffer.\n"); + return; + } + + src_mem = wined3d_context_gl_map_bo_address(context_gl, &bo, src_slice_pitch * update_d, WINED3D_MAP_READ); + + for (z = 0; z < update_d; ++z, src_mem += src_slice_pitch) + { + if (decompress) + compressed_format->decompress(src_mem, converted_mem, src_row_pitch, src_slice_pitch, + dst_row_pitch, dst_slice_pitch, update_w, update_h, 1); + else if (alpha_fixup_format_id != WINED3DFMT_UNKNOWN) + wined3d_fixup_alpha(src_format, src_mem, src_row_pitch, converted_mem, dst_row_pitch, + update_w, update_h); + else + src_format->upload(src_mem, converted_mem, src_row_pitch, src_slice_pitch, + dst_row_pitch, dst_slice_pitch, update_w, update_h, 1); + + wined3d_texture_gl_upload_bo(src_format, target, level, dst_row_pitch, dst_x, dst_y, + dst_z + z, update_w, update_h, 1, converted_mem, srgb, dst_texture, gl_info); + } + + wined3d_context_gl_unmap_bo_address(context_gl, &bo, 0, NULL); + heap_free(converted_mem); + } + else + { + if (bo.buffer_object) + { + GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, ((struct wined3d_bo_gl *)bo.buffer_object)->id)); + checkGLcall("glBindBuffer"); + } + + wined3d_texture_gl_upload_bo(src_format, target, level, src_row_pitch, dst_x, dst_y, + dst_z, update_w, update_h, update_d, bo.addr, srgb, dst_texture, gl_info); + + if (bo.buffer_object) + { + GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0)); + wined3d_context_gl_reference_bo(context_gl, (struct wined3d_bo_gl *)bo.buffer_object); + checkGLcall("glBindBuffer"); + } + } + + if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE) + { + struct wined3d_device *device = dst_texture->resource.device; + unsigned int i; + + for (i = 0; i < device->context_count; ++i) + { + wined3d_context_gl_texture_update(wined3d_context_gl(device->contexts[i]), wined3d_texture_gl(dst_texture)); + } + } +} + +static void wined3d_texture_gl_download_data_slow_path(struct wined3d_texture_gl *texture_gl, + unsigned int sub_resource_idx, struct wined3d_context_gl *context_gl, const struct wined3d_bo_address *data) +{ + struct wined3d_bo_gl *bo = (struct wined3d_bo_gl *)data->buffer_object; + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + struct wined3d_texture_sub_resource *sub_resource; + unsigned int dst_row_pitch, dst_slice_pitch; + unsigned int src_row_pitch, src_slice_pitch; + const struct wined3d_format_gl *format_gl; + BYTE *temporary_mem = NULL; + unsigned int level; + GLenum target; + void *mem; + + format_gl = wined3d_format_gl(texture_gl->t.resource.format); + + /* Only support read back of converted P8 textures. */ + if (texture_gl->t.flags & WINED3D_TEXTURE_CONVERTED && format_gl->f.id != WINED3DFMT_P8_UINT + && !format_gl->f.download) + { + ERR("Trying to read back converted texture %p, %u with format %s.\n", + texture_gl, sub_resource_idx, debug_d3dformat(format_gl->f.id)); + return; + } + + sub_resource = &texture_gl->t.sub_resources[sub_resource_idx]; + target = wined3d_texture_gl_get_sub_resource_target(texture_gl, sub_resource_idx); + level = sub_resource_idx % texture_gl->t.level_count; + + if (target == GL_TEXTURE_1D_ARRAY || target == GL_TEXTURE_2D_ARRAY) + { + if (format_gl->f.download) + { + FIXME("Reading back converted array texture %p is not supported.\n", texture_gl); + return; + } + + /* NP2 emulation is not allowed on array textures. */ + if (texture_gl->t.flags & WINED3D_TEXTURE_COND_NP2_EMULATED) + ERR("Array texture %p uses NP2 emulation.\n", texture_gl); + + WARN_(d3d_perf)("Downloading all miplevel layers to get the data for a single sub-resource.\n"); + + if (!(temporary_mem = heap_calloc(texture_gl->t.layer_count, sub_resource->size))) + { + ERR("Out of memory.\n"); + return; + } + } + + if (texture_gl->t.flags & WINED3D_TEXTURE_COND_NP2_EMULATED) + { + if (format_gl->f.download) + { + FIXME("Reading back converted texture %p with NP2 emulation is not supported.\n", texture_gl); + return; + } + + wined3d_texture_get_pitch(&texture_gl->t, level, &dst_row_pitch, &dst_slice_pitch); + wined3d_format_calculate_pitch(&format_gl->f, texture_gl->t.resource.device->surface_alignment, + wined3d_texture_get_level_pow2_width(&texture_gl->t, level), + wined3d_texture_get_level_pow2_height(&texture_gl->t, level), + &src_row_pitch, &src_slice_pitch); + if (!(temporary_mem = heap_alloc(src_slice_pitch))) + { + ERR("Out of memory.\n"); + return; + } + + if (bo) + ERR("NP2 emulated texture uses PBO unexpectedly.\n"); + if (texture_gl->t.resource.format_flags & WINED3DFMT_FLAG_COMPRESSED) + ERR("Unexpected compressed format for NP2 emulated texture.\n"); + } + + if (format_gl->f.download) + { + struct wined3d_format f; + + if (bo) + ERR("Converted texture %p uses PBO unexpectedly.\n", texture_gl); + + WARN_(d3d_perf)("Downloading converted texture %p, %u with format %s.\n", + texture_gl, sub_resource_idx, debug_d3dformat(format_gl->f.id)); + + f = format_gl->f; + f.byte_count = format_gl->f.conv_byte_count; + wined3d_texture_get_pitch(&texture_gl->t, level, &dst_row_pitch, &dst_slice_pitch); + wined3d_format_calculate_pitch(&f, texture_gl->t.resource.device->surface_alignment, + wined3d_texture_get_level_width(&texture_gl->t, level), + wined3d_texture_get_level_height(&texture_gl->t, level), + &src_row_pitch, &src_slice_pitch); + + if (!(temporary_mem = heap_alloc(src_slice_pitch))) + { + ERR("Failed to allocate memory.\n"); + return; + } + } + + if (temporary_mem) + { + mem = temporary_mem; + } + else if (bo) + { + GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, bo->id)); + checkGLcall("glBindBuffer"); + mem = data->addr; + } + else + { + mem = data->addr; + } + + if (texture_gl->t.resource.format_flags & WINED3DFMT_FLAG_COMPRESSED) + { + TRACE("Downloading compressed texture %p, %u, level %u, format %#x, type %#x, data %p.\n", + texture_gl, sub_resource_idx, level, format_gl->format, format_gl->type, mem); + + GL_EXTCALL(glGetCompressedTexImage(target, level, mem)); + checkGLcall("glGetCompressedTexImage"); + } + else + { + TRACE("Downloading texture %p, %u, level %u, format %#x, type %#x, data %p.\n", + texture_gl, sub_resource_idx, level, format_gl->format, format_gl->type, mem); + + gl_info->gl_ops.gl.p_glGetTexImage(target, level, format_gl->format, format_gl->type, mem); + checkGLcall("glGetTexImage"); + } + + if (format_gl->f.download) + { + format_gl->f.download(mem, data->addr, src_row_pitch, src_slice_pitch, dst_row_pitch, dst_slice_pitch, + wined3d_texture_get_level_width(&texture_gl->t, level), + wined3d_texture_get_level_height(&texture_gl->t, level), 1); + } + else if (texture_gl->t.flags & WINED3D_TEXTURE_COND_NP2_EMULATED) + { + const BYTE *src_data; + unsigned int h, y; + BYTE *dst_data; + /* Some games (e.g. Warhammer 40,000) don't properly handle texture + * pitches, preventing us from using the texture pitch to box NPOT + * textures. Instead, we repack the texture's CPU copy so that its + * pitch equals bpp * width instead of bpp * pow2width. + * + * Instead of boxing the texture: + * + * │<── texture width ──>│ pow2 width ──>│ + * ├─────────────────────┼───────────────┼─ + * │111111111111111111111│ │ʌ + * │222222222222222222222│ ││ + * │333333333333333333333│ padding │texture height + * │444444444444444444444│ ││ + * │555555555555555555555│ │v + * ├─────────────────────┘ ├─ + * │ │pow2 height + * │ padding padding ││ + * │ │v + * └─────────────────────────────────────┴─ + * + * we're repacking the data to the expected texture width + * + * │<── texture width ──>│ pow2 width ──>│ + * ├─────────────────────┴───────────────┼─ + * │1111111111111111111112222222222222222│ʌ + * │2222233333333333333333333344444444444││ + * │4444444444555555555555555555555 │texture height + * │ ││ + * │ padding padding │v + * │ ├─ + * │ │pow2 height + * │ padding padding ││ + * │ │v + * └─────────────────────────────────────┴─ + * + * == is the same as + * + * │<── texture width ──>│ + * ├─────────────────────┼─ + * │111111111111111111111│ʌ + * │222222222222222222222││ + * │333333333333333333333│texture height + * │444444444444444444444││ + * │555555555555555555555│v + * └─────────────────────┴─ + * + * This also means that any references to surface memory should work + * with the data as if it were a standard texture with a NPOT width + * instead of a texture boxed up to be a power-of-two texture. */ + src_data = mem; + dst_data = data->addr; + TRACE("Repacking the surface data from pitch %u to pitch %u.\n", src_row_pitch, dst_row_pitch); + h = wined3d_texture_get_level_height(&texture_gl->t, level); + for (y = 0; y < h; ++y) + { + memcpy(dst_data, src_data, dst_row_pitch); + src_data += src_row_pitch; + dst_data += dst_row_pitch; + } + } + else if (temporary_mem) + { + unsigned int layer = sub_resource_idx / texture_gl->t.level_count; + void *src_data = temporary_mem + layer * sub_resource->size; + if (bo) + { + GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, bo->id)); + checkGLcall("glBindBuffer"); + GL_EXTCALL(glBufferSubData(GL_PIXEL_PACK_BUFFER, 0, sub_resource->size, src_data)); + checkGLcall("glBufferSubData"); + } + else + { + memcpy(data->addr, src_data, sub_resource->size); + } + } + + if (bo) + { + GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0)); + wined3d_context_gl_reference_bo(context_gl, bo); + checkGLcall("glBindBuffer"); + } + + heap_free(temporary_mem); +} + +static void wined3d_texture_gl_download_data(struct wined3d_context *context, + struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx, unsigned int src_location, + const struct wined3d_box *src_box, const struct wined3d_bo_address *dst_bo_addr, + const struct wined3d_format *dst_format, unsigned int dst_x, unsigned int dst_y, unsigned int dst_z, + unsigned int dst_row_pitch, unsigned int dst_slice_pitch) +{ + struct wined3d_texture_gl *src_texture_gl = wined3d_texture_gl(src_texture); + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + unsigned int src_level, src_width, src_height, src_depth; + unsigned int src_row_pitch, src_slice_pitch; + const struct wined3d_format_gl *format_gl; + struct wined3d_bo_gl *dst_bo; + BOOL srgb = FALSE; + GLenum target; + + TRACE("context %p, src_texture %p, src_sub_resource_idx %u, src_location %s, src_box %s, dst_bo_addr %s, " + "dst_format %s, dst_x %u, dst_y %u, dst_z %u, dst_row_pitch %u, dst_slice_pitch %u.\n", + context, src_texture, src_sub_resource_idx, wined3d_debug_location(src_location), + debug_box(src_box), debug_bo_address(dst_bo_addr), debug_d3dformat(dst_format->id), + dst_x, dst_y, dst_z, dst_row_pitch, dst_slice_pitch); + + if (src_location == WINED3D_LOCATION_TEXTURE_SRGB) + { + srgb = TRUE; + } + else if (src_location != WINED3D_LOCATION_TEXTURE_RGB) + { + FIXME("Unhandled location %s.\n", wined3d_debug_location(src_location)); + return; + } + + src_level = src_sub_resource_idx % src_texture->level_count; + src_width = wined3d_texture_get_level_width(src_texture, src_level); + src_height = wined3d_texture_get_level_height(src_texture, src_level); + src_depth = wined3d_texture_get_level_depth(src_texture, src_level); + if (src_box->left || src_box->top || src_box->right != src_width || src_box->bottom != src_height + || src_box->front || src_box->back != src_depth) + { + FIXME("Unhandled source box %s.\n", debug_box(src_box)); + return; + } + + if (dst_x || dst_y || dst_z) + { + FIXME("Unhandled destination (%u, %u, %u).\n", dst_x, dst_y, dst_z); + return; + } + + if (dst_format->id != src_texture->resource.format->id) + { + FIXME("Unhandled format conversion (%s -> %s).\n", + debug_d3dformat(src_texture->resource.format->id), + debug_d3dformat(dst_format->id)); + return; + } + + wined3d_texture_get_pitch(src_texture, src_level, &src_row_pitch, &src_slice_pitch); + if (src_row_pitch != dst_row_pitch || src_slice_pitch != dst_slice_pitch) + { + FIXME("Unhandled destination pitches %u/%u (source pitches %u/%u).\n", + dst_row_pitch, dst_slice_pitch, src_row_pitch, src_slice_pitch); + return; + } + + wined3d_texture_gl_bind_and_dirtify(src_texture_gl, context_gl, srgb); + + format_gl = wined3d_format_gl(src_texture->resource.format); + target = wined3d_texture_gl_get_sub_resource_target(src_texture_gl, src_sub_resource_idx); + + if ((src_texture->resource.type == WINED3D_RTYPE_TEXTURE_2D + && (target == GL_TEXTURE_2D_ARRAY || format_gl->f.conv_byte_count + || src_texture->flags & (WINED3D_TEXTURE_CONVERTED | WINED3D_TEXTURE_COND_NP2_EMULATED))) + || target == GL_TEXTURE_1D_ARRAY) + { + wined3d_texture_gl_download_data_slow_path(src_texture_gl, src_sub_resource_idx, context_gl, dst_bo_addr); + return; + } + + if (format_gl->f.conv_byte_count) + { + FIXME("Attempting to download a converted texture, type %s format %s.\n", + debug_d3dresourcetype(src_texture->resource.type), + debug_d3dformat(format_gl->f.id)); + return; + } + + if ((dst_bo = (struct wined3d_bo_gl *)dst_bo_addr->buffer_object)) + { + GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, dst_bo->id)); + checkGLcall("glBindBuffer"); + } + + if (src_texture->resource.format_flags & WINED3DFMT_FLAG_COMPRESSED) + { + TRACE("Downloading compressed texture %p, %u, level %u, format %#x, type %#x, data %p.\n", + src_texture, src_sub_resource_idx, src_level, format_gl->format, format_gl->type, dst_bo_addr->addr); + + GL_EXTCALL(glGetCompressedTexImage(target, src_level, dst_bo_addr->addr)); + checkGLcall("glGetCompressedTexImage"); + } + else + { + TRACE("Downloading texture %p, %u, level %u, format %#x, type %#x, data %p.\n", + src_texture, src_sub_resource_idx, src_level, format_gl->format, format_gl->type, dst_bo_addr->addr); + + gl_info->gl_ops.gl.p_glGetTexImage(target, src_level, format_gl->format, format_gl->type, dst_bo_addr->addr); + checkGLcall("glGetTexImage"); + } + + if (dst_bo) + { + GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0)); + wined3d_context_gl_reference_bo(context_gl, dst_bo); + checkGLcall("glBindBuffer"); + } +} + +/* Context activation is done by the caller. */ +static BOOL wined3d_texture_gl_load_sysmem(struct wined3d_texture_gl *texture_gl, + unsigned int sub_resource_idx, struct wined3d_context_gl *context_gl, DWORD dst_location) +{ + struct wined3d_texture_sub_resource *sub_resource; + + sub_resource = &texture_gl->t.sub_resources[sub_resource_idx]; + + /* We cannot download data from multisample textures directly. */ + if (wined3d_texture_gl_is_multisample_location(texture_gl, WINED3D_LOCATION_TEXTURE_RGB)) + { + wined3d_texture_load_location(&texture_gl->t, sub_resource_idx, &context_gl->c, WINED3D_LOCATION_RB_RESOLVED); + texture2d_read_from_framebuffer(&texture_gl->t, sub_resource_idx, &context_gl->c, + WINED3D_LOCATION_RB_RESOLVED, dst_location); + return TRUE; + } + + if (sub_resource->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED)) + wined3d_texture_load_location(&texture_gl->t, sub_resource_idx, &context_gl->c, WINED3D_LOCATION_TEXTURE_RGB); + + /* Download the sub-resource to system memory. */ + if (sub_resource->locations & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB)) + { + unsigned int row_pitch, slice_pitch, level; + struct wined3d_bo_address data; + struct wined3d_box src_box; + unsigned int src_location; + + level = sub_resource_idx % texture_gl->t.level_count; + wined3d_texture_get_memory(&texture_gl->t, sub_resource_idx, &data, dst_location); + src_location = sub_resource->locations & WINED3D_LOCATION_TEXTURE_RGB + ? WINED3D_LOCATION_TEXTURE_RGB : WINED3D_LOCATION_TEXTURE_SRGB; + wined3d_texture_get_level_box(&texture_gl->t, level, &src_box); + wined3d_texture_get_pitch(&texture_gl->t, level, &row_pitch, &slice_pitch); + wined3d_texture_gl_download_data(&context_gl->c, &texture_gl->t, sub_resource_idx, src_location, + &src_box, &data, texture_gl->t.resource.format, 0, 0, 0, row_pitch, slice_pitch); + + ++texture_gl->t.download_count; + return TRUE; + } + + if (!(texture_gl->t.resource.bind_flags & WINED3D_BIND_DEPTH_STENCIL) + && (sub_resource->locations & WINED3D_LOCATION_DRAWABLE)) + { + texture2d_read_from_framebuffer(&texture_gl->t, sub_resource_idx, &context_gl->c, + texture_gl->t.resource.draw_binding, dst_location); + return TRUE; + } + + FIXME("Can't load texture %p, %u with location flags %s into sysmem.\n", + texture_gl, sub_resource_idx, wined3d_debug_location(sub_resource->locations)); + + return FALSE; +} + +static BOOL wined3d_texture_load_drawable(struct wined3d_texture *texture, + unsigned int sub_resource_idx, struct wined3d_context *context) +{ + struct wined3d_device *device; + unsigned int level; + RECT r; + + if (texture->resource.bind_flags & WINED3D_BIND_DEPTH_STENCIL) + { + DWORD current = texture->sub_resources[sub_resource_idx].locations; + FIXME("Unimplemented copy from %s for depth/stencil buffers.\n", + wined3d_debug_location(current)); + return FALSE; + } + + if (wined3d_settings.offscreen_rendering_mode == ORM_FBO + && wined3d_resource_is_offscreen(&texture->resource)) + { + ERR("Trying to load offscreen texture into WINED3D_LOCATION_DRAWABLE.\n"); + return FALSE; + } + + device = texture->resource.device; + level = sub_resource_idx % texture->level_count; + SetRect(&r, 0, 0, wined3d_texture_get_level_width(texture, level), + wined3d_texture_get_level_height(texture, level)); + wined3d_texture_load_location(texture, sub_resource_idx, context, WINED3D_LOCATION_TEXTURE_RGB); + device->blitter->ops->blitter_blit(device->blitter, WINED3D_BLIT_OP_COLOR_BLIT, context, + texture, sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB, &r, + texture, sub_resource_idx, WINED3D_LOCATION_DRAWABLE, &r, + NULL, WINED3D_TEXF_POINT); + + return TRUE; +} + +static BOOL wined3d_texture_load_renderbuffer(struct wined3d_texture *texture, + unsigned int sub_resource_idx, struct wined3d_context *context, DWORD dst_location) +{ + unsigned int level = sub_resource_idx % texture->level_count; + const RECT rect = {0, 0, + wined3d_texture_get_level_width(texture, level), + wined3d_texture_get_level_height(texture, level)}; + struct wined3d_texture_sub_resource *sub_resource; + DWORD src_location, locations; + + sub_resource = &texture->sub_resources[sub_resource_idx]; + locations = sub_resource->locations; + if (texture->resource.bind_flags & WINED3D_BIND_DEPTH_STENCIL) + { + FIXME("Unimplemented copy from %s for depth/stencil buffers.\n", + wined3d_debug_location(locations)); + return FALSE; + } + + if (locations & WINED3D_LOCATION_RB_MULTISAMPLE) + src_location = WINED3D_LOCATION_RB_MULTISAMPLE; + else if (locations & WINED3D_LOCATION_RB_RESOLVED) + src_location = WINED3D_LOCATION_RB_RESOLVED; + else if (locations & WINED3D_LOCATION_TEXTURE_SRGB) + src_location = WINED3D_LOCATION_TEXTURE_SRGB; + else if (locations & WINED3D_LOCATION_TEXTURE_RGB) + src_location = WINED3D_LOCATION_TEXTURE_RGB; + else if (locations & WINED3D_LOCATION_DRAWABLE) + src_location = WINED3D_LOCATION_DRAWABLE; + else /* texture2d_blt_fbo() will load the source location if necessary. */ + src_location = WINED3D_LOCATION_TEXTURE_RGB; + + texture2d_blt_fbo(texture->resource.device, context, WINED3D_TEXF_POINT, texture, + sub_resource_idx, src_location, &rect, texture, sub_resource_idx, dst_location, &rect); + + return TRUE; +} + +static BOOL wined3d_texture_gl_load_texture(struct wined3d_texture_gl *texture_gl, + unsigned int sub_resource_idx, struct wined3d_context_gl *context_gl, BOOL srgb) +{ + unsigned int width, height, level, src_row_pitch, src_slice_pitch, dst_row_pitch, dst_slice_pitch; + struct wined3d_device *device = texture_gl->t.resource.device; + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + const struct wined3d_color_key_conversion *conversion; + struct wined3d_texture_sub_resource *sub_resource; + const struct wined3d_format *format; + struct wined3d_bo_address data; + BYTE *src_mem, *dst_mem = NULL; + struct wined3d_box src_box; + DWORD dst_location; + BOOL depth; + + depth = texture_gl->t.resource.bind_flags & WINED3D_BIND_DEPTH_STENCIL; + sub_resource = &texture_gl->t.sub_resources[sub_resource_idx]; + + if (!depth && wined3d_settings.offscreen_rendering_mode != ORM_FBO + && wined3d_resource_is_offscreen(&texture_gl->t.resource) + && (sub_resource->locations & WINED3D_LOCATION_DRAWABLE)) + { + texture2d_load_fb_texture(texture_gl, sub_resource_idx, srgb, &context_gl->c); + + return TRUE; + } + + level = sub_resource_idx % texture_gl->t.level_count; + wined3d_texture_get_level_box(&texture_gl->t, level, &src_box); + + if (!depth && sub_resource->locations & (WINED3D_LOCATION_TEXTURE_SRGB | WINED3D_LOCATION_TEXTURE_RGB) + && (texture_gl->t.resource.format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB) + && fbo_blitter_supported(WINED3D_BLIT_OP_COLOR_BLIT, gl_info, + &texture_gl->t.resource, WINED3D_LOCATION_TEXTURE_RGB, + &texture_gl->t.resource, WINED3D_LOCATION_TEXTURE_SRGB)) + { + RECT src_rect; + + SetRect(&src_rect, src_box.left, src_box.top, src_box.right, src_box.bottom); + if (srgb) + texture2d_blt_fbo(device, &context_gl->c, WINED3D_TEXF_POINT, + &texture_gl->t, sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB, &src_rect, + &texture_gl->t, sub_resource_idx, WINED3D_LOCATION_TEXTURE_SRGB, &src_rect); + else + texture2d_blt_fbo(device, &context_gl->c, WINED3D_TEXF_POINT, + &texture_gl->t, sub_resource_idx, WINED3D_LOCATION_TEXTURE_SRGB, &src_rect, + &texture_gl->t, sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB, &src_rect); + + return TRUE; + } + + if (!depth && sub_resource->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED) + && (!srgb || (texture_gl->t.resource.format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB))) + { + DWORD src_location = sub_resource->locations & WINED3D_LOCATION_RB_RESOLVED ? + WINED3D_LOCATION_RB_RESOLVED : WINED3D_LOCATION_RB_MULTISAMPLE; + RECT src_rect; + + SetRect(&src_rect, src_box.left, src_box.top, src_box.right, src_box.bottom); + dst_location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB; + if (fbo_blitter_supported(WINED3D_BLIT_OP_COLOR_BLIT, gl_info, + &texture_gl->t.resource, src_location, &texture_gl->t.resource, dst_location)) + texture2d_blt_fbo(device, &context_gl->c, WINED3D_TEXF_POINT, &texture_gl->t, sub_resource_idx, + src_location, &src_rect, &texture_gl->t, sub_resource_idx, dst_location, &src_rect); + + return TRUE; + } + + /* Upload from system memory */ + + if (srgb) + { + dst_location = WINED3D_LOCATION_TEXTURE_SRGB; + if ((sub_resource->locations & (WINED3D_LOCATION_TEXTURE_RGB | texture_gl->t.resource.map_binding)) + == WINED3D_LOCATION_TEXTURE_RGB) + { + FIXME_(d3d_perf)("Downloading RGB texture %p, %u to reload it as sRGB.\n", texture_gl, sub_resource_idx); + wined3d_texture_load_location(&texture_gl->t, sub_resource_idx, + &context_gl->c, texture_gl->t.resource.map_binding); + } + } + else + { + dst_location = WINED3D_LOCATION_TEXTURE_RGB; + if ((sub_resource->locations & (WINED3D_LOCATION_TEXTURE_SRGB | texture_gl->t.resource.map_binding)) + == WINED3D_LOCATION_TEXTURE_SRGB) + { + FIXME_(d3d_perf)("Downloading sRGB texture %p, %u to reload it as RGB.\n", texture_gl, sub_resource_idx); + wined3d_texture_load_location(&texture_gl->t, sub_resource_idx, + &context_gl->c, texture_gl->t.resource.map_binding); + } + } + + if (!(sub_resource->locations & wined3d_texture_sysmem_locations)) + { + WARN("Trying to load a texture from sysmem, but no simple location is valid.\n"); + /* Lets hope we get it from somewhere... */ + wined3d_texture_load_location(&texture_gl->t, sub_resource_idx, &context_gl->c, WINED3D_LOCATION_SYSMEM); + } + + wined3d_texture_get_pitch(&texture_gl->t, level, &src_row_pitch, &src_slice_pitch); + + format = texture_gl->t.resource.format; + if ((conversion = wined3d_format_get_color_key_conversion(&texture_gl->t, TRUE))) + format = wined3d_get_format(device->adapter, conversion->dst_format, texture_gl->t.resource.bind_flags); + + /* Don't use PBOs for converted surfaces. During PBO conversion we look at + * WINED3D_TEXTURE_CONVERTED but it isn't set (yet) in all cases it is + * getting called. */ + if (conversion && sub_resource->bo.id) + { + TRACE("Removing the pbo attached to texture %p, %u.\n", texture_gl, sub_resource_idx); + + wined3d_texture_load_location(&texture_gl->t, sub_resource_idx, &context_gl->c, WINED3D_LOCATION_SYSMEM); + wined3d_texture_set_map_binding(&texture_gl->t, WINED3D_LOCATION_SYSMEM); + } + + wined3d_texture_get_memory(&texture_gl->t, sub_resource_idx, &data, sub_resource->locations); + if (conversion) + { + width = src_box.right - src_box.left; + height = src_box.bottom - src_box.top; + wined3d_format_calculate_pitch(format, device->surface_alignment, + width, height, &dst_row_pitch, &dst_slice_pitch); + + src_mem = wined3d_context_gl_map_bo_address(context_gl, &data, src_slice_pitch, WINED3D_MAP_READ); + if (!(dst_mem = heap_alloc(dst_slice_pitch))) + { + ERR("Out of memory (%u).\n", dst_slice_pitch); + return FALSE; + } + conversion->convert(src_mem, src_row_pitch, dst_mem, dst_row_pitch, + width, height, &texture_gl->t.async.gl_color_key); + src_row_pitch = dst_row_pitch; + src_slice_pitch = dst_slice_pitch; + wined3d_context_gl_unmap_bo_address(context_gl, &data, 0, NULL); + + data.buffer_object = 0; + data.addr = dst_mem; + } + + wined3d_texture_gl_upload_data(&context_gl->c, wined3d_const_bo_address(&data), format, &src_box, + src_row_pitch, src_slice_pitch, &texture_gl->t, sub_resource_idx, dst_location, 0, 0, 0); + + heap_free(dst_mem); + + return TRUE; +} + +static BOOL wined3d_texture_gl_prepare_location(struct wined3d_texture *texture, + unsigned int sub_resource_idx, struct wined3d_context *context, unsigned int location) +{ + struct wined3d_texture_gl *texture_gl = wined3d_texture_gl(texture); + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + + switch (location) + { + case WINED3D_LOCATION_SYSMEM: + return texture->sub_resources[sub_resource_idx].user_memory ? TRUE + : wined3d_resource_prepare_sysmem(&texture->resource); + + case WINED3D_LOCATION_BUFFER: + wined3d_texture_gl_prepare_buffer_object(texture_gl, sub_resource_idx, context_gl); + return TRUE; + + case WINED3D_LOCATION_TEXTURE_RGB: + wined3d_texture_gl_prepare_texture(texture_gl, context_gl, FALSE); + return TRUE; + + case WINED3D_LOCATION_TEXTURE_SRGB: + wined3d_texture_gl_prepare_texture(texture_gl, context_gl, TRUE); + return TRUE; + + case WINED3D_LOCATION_DRAWABLE: + if (!texture->swapchain && wined3d_settings.offscreen_rendering_mode != ORM_BACKBUFFER) + ERR("Texture %p does not have a drawable.\n", texture); + return TRUE; + + case WINED3D_LOCATION_RB_MULTISAMPLE: + wined3d_texture_gl_prepare_rb(texture_gl, context_gl->gl_info, TRUE); + return TRUE; + + case WINED3D_LOCATION_RB_RESOLVED: + wined3d_texture_gl_prepare_rb(texture_gl, context_gl->gl_info, FALSE); + return TRUE; + + default: + ERR("Invalid location %s.\n", wined3d_debug_location(location)); + return FALSE; + } +} + +/* Context activation is done by the caller. */ +static BOOL wined3d_texture_gl_load_location(struct wined3d_texture *texture, + unsigned int sub_resource_idx, struct wined3d_context *context, DWORD location) +{ + struct wined3d_texture_gl *texture_gl = wined3d_texture_gl(texture); + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + + TRACE("texture %p, sub_resource_idx %u, context %p, location %s.\n", + texture, sub_resource_idx, context, wined3d_debug_location(location)); + + if (!wined3d_texture_gl_prepare_location(texture, sub_resource_idx, context, location)) + return FALSE; + + switch (location) + { + case WINED3D_LOCATION_SYSMEM: + case WINED3D_LOCATION_BUFFER: + return wined3d_texture_gl_load_sysmem(texture_gl, sub_resource_idx, context_gl, location); + + case WINED3D_LOCATION_DRAWABLE: + return wined3d_texture_load_drawable(texture, sub_resource_idx, context); + + case WINED3D_LOCATION_RB_RESOLVED: + case WINED3D_LOCATION_RB_MULTISAMPLE: + return wined3d_texture_load_renderbuffer(texture, sub_resource_idx, context, location); + + case WINED3D_LOCATION_TEXTURE_RGB: + case WINED3D_LOCATION_TEXTURE_SRGB: + return wined3d_texture_gl_load_texture(texture_gl, sub_resource_idx, + context_gl, location == WINED3D_LOCATION_TEXTURE_SRGB); + + default: + FIXME("Unhandled %s load from %s.\n", wined3d_debug_location(location), + wined3d_debug_location(texture->sub_resources[sub_resource_idx].locations)); + return FALSE; + } +} + +static void wined3d_texture_gl_unload_location(struct wined3d_texture *texture, + struct wined3d_context *context, unsigned int location) +{ + struct wined3d_texture_gl *texture_gl = wined3d_texture_gl(texture); + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + struct wined3d_renderbuffer_entry *entry, *entry2; + unsigned int i, sub_count; + + TRACE("texture %p, context %p, location %s.\n", texture, context, wined3d_debug_location(location)); + + switch (location) + { + case WINED3D_LOCATION_BUFFER: + sub_count = texture->level_count * texture->layer_count; + for (i = 0; i < sub_count; ++i) + { + if (texture_gl->t.sub_resources[i].bo.id) + wined3d_texture_remove_buffer_object(&texture_gl->t, i, context_gl); + } + break; + + case WINED3D_LOCATION_TEXTURE_RGB: + if (texture_gl->texture_rgb.name) + gltexture_delete(texture_gl->t.resource.device, context_gl->gl_info, &texture_gl->texture_rgb); + break; + + case WINED3D_LOCATION_TEXTURE_SRGB: + if (texture_gl->texture_srgb.name) + gltexture_delete(texture_gl->t.resource.device, context_gl->gl_info, &texture_gl->texture_srgb); + break; + + case WINED3D_LOCATION_RB_MULTISAMPLE: + if (texture_gl->rb_multisample) + { + TRACE("Deleting multisample renderbuffer %u.\n", texture_gl->rb_multisample); + context_gl_resource_released(texture_gl->t.resource.device, texture_gl->rb_multisample, TRUE); + context_gl->gl_info->fbo_ops.glDeleteRenderbuffers(1, &texture_gl->rb_multisample); + texture_gl->rb_multisample = 0; + } + break; + + case WINED3D_LOCATION_RB_RESOLVED: + LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &texture_gl->renderbuffers, + struct wined3d_renderbuffer_entry, entry) + { + context_gl_resource_released(texture_gl->t.resource.device, entry->id, TRUE); + context_gl->gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id); + list_remove(&entry->entry); + heap_free(entry); + } + list_init(&texture_gl->renderbuffers); + texture_gl->current_renderbuffer = NULL; + + if (texture_gl->rb_resolved) + { + TRACE("Deleting resolved renderbuffer %u.\n", texture_gl->rb_resolved); + context_gl_resource_released(texture_gl->t.resource.device, texture_gl->rb_resolved, TRUE); + context_gl->gl_info->fbo_ops.glDeleteRenderbuffers(1, &texture_gl->rb_resolved); + texture_gl->rb_resolved = 0; + } + break; + + default: + ERR("Unhandled location %s.\n", wined3d_debug_location(location)); + break; + } +} + +static const struct wined3d_texture_ops texture_gl_ops = +{ + wined3d_texture_gl_prepare_location, + wined3d_texture_gl_load_location, + wined3d_texture_gl_unload_location, + wined3d_texture_gl_upload_data, + wined3d_texture_gl_download_data, +}; + +struct wined3d_texture * __cdecl wined3d_texture_from_resource(struct wined3d_resource *resource) +{ + return texture_from_resource(resource); +} + +static ULONG texture_resource_incref(struct wined3d_resource *resource) +{ + return wined3d_texture_incref(texture_from_resource(resource)); +} + +static ULONG texture_resource_decref(struct wined3d_resource *resource) +{ + return wined3d_texture_decref(texture_from_resource(resource)); +} + +static void texture_resource_preload(struct wined3d_resource *resource) +{ + struct wined3d_texture *texture = texture_from_resource(resource); + struct wined3d_context *context; + + context = context_acquire(resource->device, NULL, 0); + wined3d_texture_load(texture, context, texture->flags & WINED3D_TEXTURE_IS_SRGB); + context_release(context); +} + +static void texture_resource_unload(struct wined3d_resource *resource) +{ + struct wined3d_texture *texture = texture_from_resource(resource); + struct wined3d_device *device = resource->device; + unsigned int location = resource->map_binding; + struct wined3d_context *context; + unsigned int sub_count, i; + + TRACE("resource %p.\n", resource); + + /* D3D is not initialised, so no GPU locations should currently exist. + * Moreover, we may not be able to acquire a valid context. */ + if (!device->d3d_initialized) + return; + + context = context_acquire(device, NULL, 0); + + if (location == WINED3D_LOCATION_BUFFER) + location = WINED3D_LOCATION_SYSMEM; + + sub_count = texture->level_count * texture->layer_count; + for (i = 0; i < sub_count; ++i) + { + if (resource->access & WINED3D_RESOURCE_ACCESS_CPU + && wined3d_texture_load_location(texture, i, context, location)) + { + wined3d_texture_invalidate_location(texture, i, ~location); + } + else + { + if (resource->access & WINED3D_RESOURCE_ACCESS_CPU) + ERR("Discarding %s %p sub-resource %u with resource access %s.\n", + debug_d3dresourcetype(resource->type), resource, i, + wined3d_debug_resource_access(resource->access)); + wined3d_texture_validate_location(texture, i, WINED3D_LOCATION_DISCARDED); + wined3d_texture_invalidate_location(texture, i, ~WINED3D_LOCATION_DISCARDED); + } + } + + wined3d_texture_unload_location(texture, context, WINED3D_LOCATION_BUFFER); + wined3d_texture_unload_location(texture, context, WINED3D_LOCATION_TEXTURE_RGB); + wined3d_texture_unload_location(texture, context, WINED3D_LOCATION_TEXTURE_SRGB); + wined3d_texture_unload_location(texture, context, WINED3D_LOCATION_RB_MULTISAMPLE); + wined3d_texture_unload_location(texture, context, WINED3D_LOCATION_RB_RESOLVED); + + context_release(context); + + wined3d_texture_force_reload(texture); + if (texture->resource.bind_count) + device_invalidate_state(device, STATE_SAMPLER(texture->sampler)); + wined3d_texture_set_dirty(texture); + + resource_unload(&texture->resource); +} + +static HRESULT texture_resource_sub_resource_map(struct wined3d_resource *resource, unsigned int sub_resource_idx, + struct wined3d_map_desc *map_desc, const struct wined3d_box *box, DWORD flags) +{ + const struct wined3d_format *format = resource->format; + struct wined3d_texture_sub_resource *sub_resource; + struct wined3d_device *device = resource->device; + unsigned int fmt_flags = resource->format_flags; + struct wined3d_context *context; + struct wined3d_texture *texture; + struct wined3d_bo_address data; + unsigned int texture_level; + BYTE *base_memory; + BOOL ret; + + TRACE("resource %p, sub_resource_idx %u, map_desc %p, box %s, flags %#x.\n", + resource, sub_resource_idx, map_desc, debug_box(box), flags); + + texture = texture_from_resource(resource); + if (!(sub_resource = wined3d_texture_get_sub_resource(texture, sub_resource_idx))) + return E_INVALIDARG; + + texture_level = sub_resource_idx % texture->level_count; + if (box && FAILED(wined3d_texture_check_box_dimensions(texture, texture_level, box))) + { + WARN("Map box is invalid.\n"); + if (((fmt_flags & WINED3DFMT_FLAG_BLOCKS) && !(resource->access & WINED3D_RESOURCE_ACCESS_CPU)) + || resource->type != WINED3D_RTYPE_TEXTURE_2D) + return WINED3DERR_INVALIDCALL; + } + + if (texture->flags & WINED3D_TEXTURE_DC_IN_USE) + { + WARN("DC is in use.\n"); + return WINED3DERR_INVALIDCALL; + } + + if (sub_resource->map_count) + { + WARN("Sub-resource is already mapped.\n"); + return WINED3DERR_INVALIDCALL; + } + + context = context_acquire(device, NULL, 0); + + if (flags & WINED3D_MAP_DISCARD) + { + TRACE("WINED3D_MAP_DISCARD flag passed, marking %s as up to date.\n", + wined3d_debug_location(resource->map_binding)); + if ((ret = wined3d_texture_prepare_location(texture, sub_resource_idx, context, resource->map_binding))) + wined3d_texture_validate_location(texture, sub_resource_idx, resource->map_binding); + } + else + { + if (resource->usage & WINED3DUSAGE_DYNAMIC) + WARN_(d3d_perf)("Mapping a dynamic texture without WINED3D_MAP_DISCARD.\n"); + ret = wined3d_texture_load_location(texture, sub_resource_idx, context, resource->map_binding); + } + + if (!ret) + { + ERR("Failed to prepare location.\n"); + context_release(context); + return E_OUTOFMEMORY; + } + + /* We only record dirty regions for the top-most level. */ + if (texture->dirty_regions && flags & WINED3D_MAP_WRITE + && !(flags & WINED3D_MAP_NO_DIRTY_UPDATE) && !texture_level) + wined3d_texture_dirty_region_add(texture, sub_resource_idx / texture->level_count, box); + + if (flags & WINED3D_MAP_WRITE + && (!(flags & WINED3D_MAP_NO_DIRTY_UPDATE) || (resource->usage & WINED3DUSAGE_DYNAMIC))) + wined3d_texture_invalidate_location(texture, sub_resource_idx, ~resource->map_binding); + + wined3d_texture_get_memory(texture, sub_resource_idx, &data, resource->map_binding); + base_memory = wined3d_context_map_bo_address(context, &data, sub_resource->size, flags); + sub_resource->map_flags = flags; + TRACE("Base memory pointer %p.\n", base_memory); + + context_release(context); + + if (fmt_flags & WINED3DFMT_FLAG_BROKEN_PITCH) + { + map_desc->row_pitch = wined3d_texture_get_level_width(texture, texture_level) * format->byte_count; + map_desc->slice_pitch = wined3d_texture_get_level_height(texture, texture_level) * map_desc->row_pitch; + } + else + { + wined3d_texture_get_pitch(texture, texture_level, &map_desc->row_pitch, &map_desc->slice_pitch); + } + + if (!box) + { + map_desc->data = base_memory; + } + else + { + if ((fmt_flags & (WINED3DFMT_FLAG_BLOCKS | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_BLOCKS) + { + /* Compressed textures are block based, so calculate the offset of + * the block that contains the top-left pixel of the mapped box. */ + map_desc->data = base_memory + + (box->front * map_desc->slice_pitch) + + ((box->top / format->block_height) * map_desc->row_pitch) + + ((box->left / format->block_width) * format->block_byte_count); + } + else + { + map_desc->data = base_memory + + (box->front * map_desc->slice_pitch) + + (box->top * map_desc->row_pitch) + + (box->left * format->byte_count); + } + } + + if (texture->swapchain && texture->swapchain->front_buffer == texture) + { + RECT *r = &texture->swapchain->front_buffer_update; + + if (!box) + SetRect(r, 0, 0, resource->width, resource->height); + else + SetRect(r, box->left, box->top, box->right, box->bottom); + TRACE("Mapped front buffer %s.\n", wine_dbgstr_rect(r)); + } + + ++resource->map_count; + ++sub_resource->map_count; + + TRACE("Returning memory %p, row pitch %u, slice pitch %u.\n", + map_desc->data, map_desc->row_pitch, map_desc->slice_pitch); + + return WINED3D_OK; +} + +static HRESULT texture_resource_sub_resource_unmap(struct wined3d_resource *resource, unsigned int sub_resource_idx) +{ + struct wined3d_texture_sub_resource *sub_resource; + struct wined3d_device *device = resource->device; + struct wined3d_context *context; + struct wined3d_texture *texture; + struct wined3d_bo_address data; + struct wined3d_range range; + + TRACE("resource %p, sub_resource_idx %u.\n", resource, sub_resource_idx); + + texture = texture_from_resource(resource); + if (!(sub_resource = wined3d_texture_get_sub_resource(texture, sub_resource_idx))) + return E_INVALIDARG; + + if (!sub_resource->map_count) + { + WARN("Trying to unmap unmapped sub-resource.\n"); + if (texture->flags & WINED3D_TEXTURE_DC_IN_USE) + return WINED3D_OK; + return WINEDDERR_NOTLOCKED; + } + + context = context_acquire(device, NULL, 0); + + wined3d_texture_get_memory(texture, sub_resource_idx, &data, texture->resource.map_binding); + range.offset = 0; + range.size = sub_resource->size; + wined3d_context_unmap_bo_address(context, &data, !!(sub_resource->map_flags & WINED3D_MAP_WRITE), &range); + + context_release(context); + + if (texture->swapchain && texture->swapchain->front_buffer == texture) + { + if (!(sub_resource->locations & (WINED3D_LOCATION_DRAWABLE | WINED3D_LOCATION_TEXTURE_RGB))) + texture->swapchain->swapchain_ops->swapchain_frontbuffer_updated(texture->swapchain); + } + + --sub_resource->map_count; + if (!--resource->map_count && texture->update_map_binding) + wined3d_texture_update_map_binding(texture); + + return WINED3D_OK; +} + +static const struct wined3d_resource_ops texture_resource_ops = +{ + texture_resource_incref, + texture_resource_decref, + texture_resource_preload, + texture_resource_unload, + texture_resource_sub_resource_map, + texture_resource_sub_resource_unmap, +}; + +static HRESULT wined3d_texture_init(struct wined3d_texture *texture, const struct wined3d_resource_desc *desc, + unsigned int layer_count, unsigned int level_count, DWORD flags, struct wined3d_device *device, + void *parent, const struct wined3d_parent_ops *parent_ops, void *sub_resources, + const struct wined3d_texture_ops *texture_ops) +{ + const struct wined3d_d3d_info *d3d_info = &device->adapter->d3d_info; + struct wined3d_device_parent *device_parent = device->device_parent; + const struct wined3d_gl_info *gl_info = &device->adapter->gl_info; + unsigned int sub_count, i, j, size, offset = 0; + unsigned int pow2_width, pow2_height; + const struct wined3d_format *format; + HRESULT hr; + + TRACE("texture %p, resource_type %s, format %s, multisample_type %#x, multisample_quality %#x, " + "usage %s, access %s, width %u, height %u, depth %u, layer_count %u, level_count %u, " + "flags %#x, device %p, parent %p, parent_ops %p, sub_resources %p, texture_ops %p.\n", + texture, debug_d3dresourcetype(desc->resource_type), debug_d3dformat(desc->format), + desc->multisample_type, desc->multisample_quality, debug_d3dusage(desc->usage), + wined3d_debug_resource_access(desc->access), desc->width, desc->height, desc->depth, + layer_count, level_count, flags, device, parent, parent_ops, sub_resources, texture_ops); + + if (!desc->width || !desc->height || !desc->depth) + return WINED3DERR_INVALIDCALL; + + if (desc->resource_type == WINED3D_RTYPE_TEXTURE_3D && layer_count != 1) + { + ERR("Invalid layer count for volume texture.\n"); + return E_INVALIDARG; + } + + texture->sub_resources = sub_resources; + + /* TODO: It should only be possible to create textures for formats + * that are reported as supported. */ + if (WINED3DFMT_UNKNOWN >= desc->format) + { + WARN("Texture cannot be created with a format of WINED3DFMT_UNKNOWN.\n"); + return WINED3DERR_INVALIDCALL; + } + format = wined3d_get_format(device->adapter, desc->format, desc->bind_flags); + + if (desc->usage & WINED3DUSAGE_DYNAMIC && (wined3d_resource_access_is_managed(desc->access) + || desc->usage & WINED3DUSAGE_SCRATCH)) + { + WARN("Attempted to create a dynamic texture with access %s and usage %s.\n", + wined3d_debug_resource_access(desc->access), debug_d3dusage(desc->usage)); + return WINED3DERR_INVALIDCALL; + } + + pow2_width = desc->width; + pow2_height = desc->height; + if (((desc->width & (desc->width - 1)) || (desc->height & (desc->height - 1)) || (desc->depth & (desc->depth - 1))) + && !d3d_info->texture_npot) + { + /* level_count == 0 returns an error as well. */ + if (level_count != 1 || layer_count != 1 || desc->resource_type == WINED3D_RTYPE_TEXTURE_3D) + { + if (!(desc->usage & WINED3DUSAGE_SCRATCH)) + { + WARN("Attempted to create a mipmapped/cube/array/volume NPOT " + "texture without unconditional NPOT support.\n"); + return WINED3DERR_INVALIDCALL; + } + + WARN("Creating a scratch mipmapped/cube/array NPOT texture despite lack of HW support.\n"); + } + texture->flags |= WINED3D_TEXTURE_COND_NP2; + + if (desc->resource_type != WINED3D_RTYPE_TEXTURE_3D && !d3d_info->texture_npot_conditional) + { + /* TODO: Add support for non-power-of-two compressed textures. */ + if (format->flags[WINED3D_GL_RES_TYPE_TEX_2D] + & (WINED3DFMT_FLAG_COMPRESSED | WINED3DFMT_FLAG_HEIGHT_SCALE)) + { + FIXME("Compressed or height scaled non-power-of-two (%ux%u) textures are not supported.\n", + desc->width, desc->height); + return WINED3DERR_NOTAVAILABLE; + } + + /* Find the nearest pow2 match. */ + pow2_width = pow2_height = 1; + while (pow2_width < desc->width) + pow2_width <<= 1; + while (pow2_height < desc->height) + pow2_height <<= 1; + texture->flags |= WINED3D_TEXTURE_COND_NP2_EMULATED; + } + } + texture->pow2_width = pow2_width; + texture->pow2_height = pow2_height; + + if ((pow2_width > d3d_info->limits.texture_size || pow2_height > d3d_info->limits.texture_size) + && (desc->bind_flags & WINED3D_BIND_SHADER_RESOURCE)) + { + /* One of four options: + * 1: Do the same as we do with NPOT and scale the texture. (Any + * texture ops would require the texture to be scaled which is + * potentially slow.) + * 2: Set the texture to the maximum size (bad idea). + * 3: WARN and return WINED3DERR_NOTAVAILABLE. + * 4: Create the surface, but allow it to be used only for DirectDraw + * Blts. Some apps (e.g. Swat 3) create textures with a height of + * 16 and a width > 3000 and blt 16x16 letter areas from them to + * the render target. */ + if (desc->access & WINED3D_RESOURCE_ACCESS_GPU) + { + WARN("Dimensions (%ux%u) exceed the maximum texture size.\n", pow2_width, pow2_height); + return WINED3DERR_NOTAVAILABLE; + } + + /* We should never use this surface in combination with OpenGL. */ + TRACE("Creating an oversized (%ux%u) surface.\n", pow2_width, pow2_height); + } + + for (i = 0; i < layer_count; ++i) + { + for (j = 0; j < level_count; ++j) + { + unsigned int idx = i * level_count + j; + + size = wined3d_format_calculate_size(format, device->surface_alignment, + max(1, desc->width >> j), max(1, desc->height >> j), max(1, desc->depth >> j)); + texture->sub_resources[idx].offset = offset; + texture->sub_resources[idx].size = size; + offset += size; + } + offset = (offset + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1); + } + + if (!offset) + return WINED3DERR_INVALIDCALL; + + /* Ensure the last mip-level is at least large enough to hold a single + * compressed block. It is questionable how useful these mip-levels are to + * the application with "broken pitch" formats, but we want to avoid + * memory corruption when loading textures into WINED3D_LOCATION_SYSMEM. */ + if (format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_BROKEN_PITCH) + { + unsigned int min_size; + + min_size = texture->sub_resources[level_count * layer_count - 1].offset + format->block_byte_count; + min_size = (min_size + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1); + if (min_size > offset) + offset = min_size; + } + + if (FAILED(hr = resource_init(&texture->resource, device, desc->resource_type, format, + desc->multisample_type, desc->multisample_quality, desc->usage, desc->bind_flags, desc->access, + desc->width, desc->height, desc->depth, offset, parent, parent_ops, &texture_resource_ops))) + { + static unsigned int once; + + /* DXTn 3D textures are not supported. Do not write the ERR for them. */ + if ((desc->format == WINED3DFMT_DXT1 || desc->format == WINED3DFMT_DXT2 || desc->format == WINED3DFMT_DXT3 + || desc->format == WINED3DFMT_DXT4 || desc->format == WINED3DFMT_DXT5) + && !(format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_TEXTURE) + && desc->resource_type != WINED3D_RTYPE_TEXTURE_3D && !once++) + ERR_(winediag)("The application tried to create a DXTn texture, but the driver does not support them.\n"); + + WARN("Failed to initialize resource, returning %#x\n", hr); + return hr; + } + wined3d_resource_update_draw_binding(&texture->resource); + + texture->texture_ops = texture_ops; + + texture->layer_count = layer_count; + texture->level_count = level_count; + texture->lod = 0; + texture->flags |= WINED3D_TEXTURE_POW2_MAT_IDENT | WINED3D_TEXTURE_NORMALIZED_COORDS + | WINED3D_TEXTURE_DOWNLOADABLE; + if (flags & WINED3D_TEXTURE_CREATE_GET_DC_LENIENT) + texture->flags |= WINED3D_TEXTURE_PIN_SYSMEM | WINED3D_TEXTURE_GET_DC_LENIENT; + if (flags & (WINED3D_TEXTURE_CREATE_GET_DC | WINED3D_TEXTURE_CREATE_GET_DC_LENIENT)) + texture->flags |= WINED3D_TEXTURE_GET_DC; + if (flags & WINED3D_TEXTURE_CREATE_DISCARD) + texture->flags |= WINED3D_TEXTURE_DISCARD; + if (flags & WINED3D_TEXTURE_CREATE_GENERATE_MIPMAPS) + { + if (!(texture->resource.format_flags & WINED3DFMT_FLAG_GEN_MIPMAP)) + WARN("Format doesn't support mipmaps generation, " + "ignoring WINED3D_TEXTURE_CREATE_GENERATE_MIPMAPS flag.\n"); + else + texture->flags |= WINED3D_TEXTURE_GENERATE_MIPMAPS; + } + + if (flags & WINED3D_TEXTURE_CREATE_RECORD_DIRTY_REGIONS + && !(texture->dirty_regions = heap_calloc(texture->layer_count, sizeof(*texture->dirty_regions)))) + { + wined3d_texture_cleanup_sync(texture); + return E_OUTOFMEMORY; + } + + /* Precalculated scaling for 'faked' non power of two texture coords. */ + if (texture->resource.gl_type == WINED3D_GL_RES_TYPE_TEX_RECT) + { + texture->pow2_matrix[0] = (float)desc->width; + texture->pow2_matrix[5] = (float)desc->height; + texture->flags &= ~(WINED3D_TEXTURE_POW2_MAT_IDENT | WINED3D_TEXTURE_NORMALIZED_COORDS); + } + else if (texture->flags & WINED3D_TEXTURE_COND_NP2_EMULATED) + { + texture->pow2_matrix[0] = (((float)desc->width) / ((float)pow2_width)); + texture->pow2_matrix[5] = (((float)desc->height) / ((float)pow2_height)); + texture->flags &= ~WINED3D_TEXTURE_POW2_MAT_IDENT; + } + else + { + texture->pow2_matrix[0] = 1.0f; + texture->pow2_matrix[5] = 1.0f; + } + texture->pow2_matrix[10] = 1.0f; + texture->pow2_matrix[15] = 1.0f; + TRACE("x scale %.8e, y scale %.8e.\n", texture->pow2_matrix[0], texture->pow2_matrix[5]); + + if (wined3d_texture_use_pbo(texture, gl_info)) + texture->resource.map_binding = WINED3D_LOCATION_BUFFER; + + if (desc->resource_type != WINED3D_RTYPE_TEXTURE_3D + || !wined3d_texture_use_pbo(texture, gl_info)) + { + if (!wined3d_resource_prepare_sysmem(&texture->resource)) + { + wined3d_texture_cleanup_sync(texture); + return E_OUTOFMEMORY; + } + } + + sub_count = level_count * layer_count; + if (sub_count / layer_count != level_count) + { + wined3d_texture_cleanup_sync(texture); + return E_OUTOFMEMORY; + } + + if (desc->usage & WINED3DUSAGE_OVERLAY) + { + if (!(texture->overlay_info = heap_calloc(sub_count, sizeof(*texture->overlay_info)))) + { + wined3d_texture_cleanup_sync(texture); + return E_OUTOFMEMORY; + } + + for (i = 0; i < sub_count; ++i) + { + list_init(&texture->overlay_info[i].entry); + list_init(&texture->overlay_info[i].overlays); + } + } + + /* Generate all sub-resources. */ + for (i = 0; i < sub_count; ++i) + { + struct wined3d_texture_sub_resource *sub_resource; + + sub_resource = &texture->sub_resources[i]; + sub_resource->locations = WINED3D_LOCATION_DISCARDED; + if (desc->resource_type != WINED3D_RTYPE_TEXTURE_3D) + { + wined3d_texture_validate_location(texture, i, WINED3D_LOCATION_SYSMEM); + wined3d_texture_invalidate_location(texture, i, ~WINED3D_LOCATION_SYSMEM); + } + + if (FAILED(hr = device_parent->ops->texture_sub_resource_created(device_parent, + desc->resource_type, texture, i, &sub_resource->parent, &sub_resource->parent_ops))) + { + WARN("Failed to create sub-resource parent, hr %#x.\n", hr); + sub_resource->parent = NULL; + wined3d_texture_cleanup_sync(texture); + return hr; + } + + TRACE("parent %p, parent_ops %p.\n", sub_resource->parent, sub_resource->parent_ops); + + TRACE("Created sub-resource %u (level %u, layer %u).\n", + i, i % texture->level_count, i / texture->level_count); + + if (desc->usage & WINED3DUSAGE_OWNDC) + { + struct wined3d_texture_idx texture_idx = {texture, i}; + + wined3d_cs_init_object(device->cs, wined3d_texture_create_dc, &texture_idx); + wined3d_cs_finish(device->cs, WINED3D_CS_QUEUE_DEFAULT); + if (!texture->dc_info || !texture->dc_info[i].dc) + { + wined3d_texture_cleanup_sync(texture); + return WINED3DERR_INVALIDCALL; + } + } + } + + return WINED3D_OK; +} + +HRESULT CDECL wined3d_texture_blt(struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx, + const RECT *dst_rect, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx, + const RECT *src_rect, DWORD flags, const struct wined3d_blt_fx *fx, enum wined3d_texture_filter_type filter) +{ + struct wined3d_box src_box = {src_rect->left, src_rect->top, src_rect->right, src_rect->bottom, 0, 1}; + struct wined3d_box dst_box = {dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, 0, 1}; + HRESULT hr; + + TRACE("dst_texture %p, dst_sub_resource_idx %u, dst_rect %s, src_texture %p, " + "src_sub_resource_idx %u, src_rect %s, flags %#x, fx %p, filter %s.\n", + dst_texture, dst_sub_resource_idx, wine_dbgstr_rect(dst_rect), src_texture, + src_sub_resource_idx, wine_dbgstr_rect(src_rect), flags, fx, debug_d3dtexturefiltertype(filter)); + + if (dst_sub_resource_idx >= dst_texture->level_count * dst_texture->layer_count + || dst_texture->resource.type != WINED3D_RTYPE_TEXTURE_2D) + return WINED3DERR_INVALIDCALL; + + if (src_sub_resource_idx >= src_texture->level_count * src_texture->layer_count + || src_texture->resource.type != WINED3D_RTYPE_TEXTURE_2D) + return WINED3DERR_INVALIDCALL; + + if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT + && filter != WINED3D_TEXF_LINEAR) + return WINED3DERR_INVALIDCALL; + + if (FAILED(hr = wined3d_texture_check_box_dimensions(dst_texture, + dst_sub_resource_idx % dst_texture->level_count, &dst_box))) + return hr; + + if (FAILED(hr = wined3d_texture_check_box_dimensions(src_texture, + src_sub_resource_idx % src_texture->level_count, &src_box))) + return hr; + + if (dst_texture->sub_resources[dst_sub_resource_idx].map_count + || src_texture->sub_resources[src_sub_resource_idx].map_count) + { + WARN("Sub-resource is busy, returning WINEDDERR_SURFACEBUSY.\n"); + return WINEDDERR_SURFACEBUSY; + } + + if (!src_texture->resource.format->depth_size != !dst_texture->resource.format->depth_size + || !src_texture->resource.format->stencil_size != !dst_texture->resource.format->stencil_size) + { + WARN("Rejecting depth/stencil blit between incompatible formats.\n"); + return WINED3DERR_INVALIDCALL; + } + + if (dst_texture->resource.device != src_texture->resource.device) + { + FIXME("Rejecting cross-device blit.\n"); + return E_NOTIMPL; + } + + wined3d_cs_emit_blt_sub_resource(dst_texture->resource.device->cs, &dst_texture->resource, dst_sub_resource_idx, + &dst_box, &src_texture->resource, src_sub_resource_idx, &src_box, flags, fx, filter); + + return WINED3D_OK; +} + +HRESULT CDECL wined3d_texture_get_overlay_position(const struct wined3d_texture *texture, + unsigned int sub_resource_idx, LONG *x, LONG *y) +{ + struct wined3d_overlay_info *overlay; + + TRACE("texture %p, sub_resource_idx %u, x %p, y %p.\n", texture, sub_resource_idx, x, y); + + if (!(texture->resource.usage & WINED3DUSAGE_OVERLAY) + || sub_resource_idx >= texture->level_count * texture->layer_count) + { + WARN("Invalid sub-resource specified.\n"); + return WINEDDERR_NOTAOVERLAYSURFACE; + } + + overlay = &texture->overlay_info[sub_resource_idx]; + if (!overlay->dst_texture) + { + TRACE("Overlay not visible.\n"); + *x = 0; + *y = 0; + return WINEDDERR_OVERLAYNOTVISIBLE; + } + + *x = overlay->dst_rect.left; + *y = overlay->dst_rect.top; + + TRACE("Returning position %d, %d.\n", *x, *y); + + return WINED3D_OK; +} + +HRESULT CDECL wined3d_texture_set_overlay_position(struct wined3d_texture *texture, + unsigned int sub_resource_idx, LONG x, LONG y) +{ + struct wined3d_overlay_info *overlay; + LONG w, h; + + TRACE("texture %p, sub_resource_idx %u, x %d, y %d.\n", texture, sub_resource_idx, x, y); + + if (!(texture->resource.usage & WINED3DUSAGE_OVERLAY) + || sub_resource_idx >= texture->level_count * texture->layer_count) + { + WARN("Invalid sub-resource specified.\n"); + return WINEDDERR_NOTAOVERLAYSURFACE; + } + + overlay = &texture->overlay_info[sub_resource_idx]; + w = overlay->dst_rect.right - overlay->dst_rect.left; + h = overlay->dst_rect.bottom - overlay->dst_rect.top; + SetRect(&overlay->dst_rect, x, y, x + w, y + h); + + return WINED3D_OK; +} + +HRESULT CDECL wined3d_texture_update_overlay(struct wined3d_texture *texture, unsigned int sub_resource_idx, + const RECT *src_rect, struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx, + const RECT *dst_rect, DWORD flags) +{ + struct wined3d_overlay_info *overlay; + unsigned int level, dst_level; + + TRACE("texture %p, sub_resource_idx %u, src_rect %s, dst_texture %p, " + "dst_sub_resource_idx %u, dst_rect %s, flags %#x.\n", + texture, sub_resource_idx, wine_dbgstr_rect(src_rect), dst_texture, + dst_sub_resource_idx, wine_dbgstr_rect(dst_rect), flags); + + if (!(texture->resource.usage & WINED3DUSAGE_OVERLAY) || texture->resource.type != WINED3D_RTYPE_TEXTURE_2D + || sub_resource_idx >= texture->level_count * texture->layer_count) + { + WARN("Invalid sub-resource specified.\n"); + return WINEDDERR_NOTAOVERLAYSURFACE; + } + + if (!dst_texture || dst_texture->resource.type != WINED3D_RTYPE_TEXTURE_2D + || dst_sub_resource_idx >= dst_texture->level_count * dst_texture->layer_count) + { + WARN("Invalid destination sub-resource specified.\n"); + return WINED3DERR_INVALIDCALL; + } + + overlay = &texture->overlay_info[sub_resource_idx]; + + level = sub_resource_idx % texture->level_count; + if (src_rect) + overlay->src_rect = *src_rect; + else + SetRect(&overlay->src_rect, 0, 0, + wined3d_texture_get_level_width(texture, level), + wined3d_texture_get_level_height(texture, level)); + + dst_level = dst_sub_resource_idx % dst_texture->level_count; + if (dst_rect) + overlay->dst_rect = *dst_rect; + else + SetRect(&overlay->dst_rect, 0, 0, + wined3d_texture_get_level_width(dst_texture, dst_level), + wined3d_texture_get_level_height(dst_texture, dst_level)); + + if (overlay->dst_texture && (overlay->dst_texture != dst_texture + || overlay->dst_sub_resource_idx != dst_sub_resource_idx || flags & WINEDDOVER_HIDE)) + { + overlay->dst_texture = NULL; + list_remove(&overlay->entry); + } + + if (flags & WINEDDOVER_SHOW) + { + if (overlay->dst_texture != dst_texture || overlay->dst_sub_resource_idx != dst_sub_resource_idx) + { + overlay->dst_texture = dst_texture; + overlay->dst_sub_resource_idx = dst_sub_resource_idx; + list_add_tail(&texture->overlay_info[dst_sub_resource_idx].overlays, &overlay->entry); + } + } + else if (flags & WINEDDOVER_HIDE) + { + /* Tests show that the rectangles are erased on hide. */ + SetRectEmpty(&overlay->src_rect); + SetRectEmpty(&overlay->dst_rect); + overlay->dst_texture = NULL; + } + + return WINED3D_OK; +} + +void * CDECL wined3d_texture_get_sub_resource_parent(struct wined3d_texture *texture, unsigned int sub_resource_idx) +{ + unsigned int sub_count = texture->level_count * texture->layer_count; + + TRACE("texture %p, sub_resource_idx %u.\n", texture, sub_resource_idx); + + if (sub_resource_idx >= sub_count) + { + WARN("sub_resource_idx %u >= sub_count %u.\n", sub_resource_idx, sub_count); + return NULL; + } + + return texture->sub_resources[sub_resource_idx].parent; +} + +void CDECL wined3d_texture_set_sub_resource_parent(struct wined3d_texture *texture, + unsigned int sub_resource_idx, void *parent) +{ + unsigned int sub_count = texture->level_count * texture->layer_count; + + TRACE("texture %p, sub_resource_idx %u, parent %p.\n", texture, sub_resource_idx, parent); + + if (sub_resource_idx >= sub_count) + { + WARN("sub_resource_idx %u >= sub_count %u.\n", sub_resource_idx, sub_count); + return; + } + + texture->sub_resources[sub_resource_idx].parent = parent; +} + +HRESULT CDECL wined3d_texture_get_sub_resource_desc(const struct wined3d_texture *texture, + unsigned int sub_resource_idx, struct wined3d_sub_resource_desc *desc) +{ + unsigned int sub_count = texture->level_count * texture->layer_count; + const struct wined3d_resource *resource; + unsigned int level_idx; + + TRACE("texture %p, sub_resource_idx %u, desc %p.\n", texture, sub_resource_idx, desc); + + if (sub_resource_idx >= sub_count) + { + WARN("sub_resource_idx %u >= sub_count %u.\n", sub_resource_idx, sub_count); + return WINED3DERR_INVALIDCALL; + } + + resource = &texture->resource; + desc->format = resource->format->id; + desc->multisample_type = resource->multisample_type; + desc->multisample_quality = resource->multisample_quality; + desc->usage = resource->usage; + desc->bind_flags = resource->bind_flags; + desc->access = resource->access; + + level_idx = sub_resource_idx % texture->level_count; + desc->width = wined3d_texture_get_level_width(texture, level_idx); + desc->height = wined3d_texture_get_level_height(texture, level_idx); + desc->depth = wined3d_texture_get_level_depth(texture, level_idx); + desc->size = texture->sub_resources[sub_resource_idx].size; + + return WINED3D_OK; +} + +HRESULT wined3d_texture_gl_init(struct wined3d_texture_gl *texture_gl, struct wined3d_device *device, + const struct wined3d_resource_desc *desc, unsigned int layer_count, unsigned int level_count, + uint32_t flags, void *parent, const struct wined3d_parent_ops *parent_ops) +{ + const struct wined3d_gl_info *gl_info = &device->adapter->gl_info; + HRESULT hr; + + TRACE("texture_gl %p, device %p, desc %p, layer_count %u, " + "level_count %u, flags %#x, parent %p, parent_ops %p.\n", + texture_gl, device, desc, layer_count, + level_count, flags, parent, parent_ops); + + if (!(desc->usage & WINED3DUSAGE_LEGACY_CUBEMAP) && layer_count > 1 + && !gl_info->supported[EXT_TEXTURE_ARRAY]) + { + WARN("OpenGL implementation does not support array textures.\n"); + return WINED3DERR_INVALIDCALL; + } + + switch (desc->resource_type) + { + case WINED3D_RTYPE_TEXTURE_1D: + if (layer_count > 1) + texture_gl->target = GL_TEXTURE_1D_ARRAY; + else + texture_gl->target = GL_TEXTURE_1D; + break; + + case WINED3D_RTYPE_TEXTURE_2D: + if (desc->usage & WINED3DUSAGE_LEGACY_CUBEMAP) + { + texture_gl->target = GL_TEXTURE_CUBE_MAP_ARB; + } + else if (desc->multisample_type && gl_info->supported[ARB_TEXTURE_MULTISAMPLE]) + { + if (layer_count > 1) + texture_gl->target = GL_TEXTURE_2D_MULTISAMPLE_ARRAY; + else + texture_gl->target = GL_TEXTURE_2D_MULTISAMPLE; + } + else + { + if (layer_count > 1) + texture_gl->target = GL_TEXTURE_2D_ARRAY; + else + texture_gl->target = GL_TEXTURE_2D; + } + break; + + case WINED3D_RTYPE_TEXTURE_3D: + if (!gl_info->supported[EXT_TEXTURE3D]) + { + WARN("OpenGL implementation does not support 3D textures.\n"); + return WINED3DERR_INVALIDCALL; + } + texture_gl->target = GL_TEXTURE_3D; + break; + + default: + ERR("Invalid resource type %s.\n", debug_d3dresourcetype(desc->resource_type)); + return WINED3DERR_INVALIDCALL; + } + + list_init(&texture_gl->renderbuffers); + + if (FAILED(hr = wined3d_texture_init(&texture_gl->t, desc, layer_count, level_count, + flags, device, parent, parent_ops, &texture_gl[1], &texture_gl_ops))) + return hr; + + if (texture_gl->t.resource.gl_type == WINED3D_GL_RES_TYPE_TEX_RECT) + texture_gl->target = GL_TEXTURE_RECTANGLE_ARB; + + if (texture_gl->target == GL_TEXTURE_2D_MULTISAMPLE_ARRAY || texture_gl->target == GL_TEXTURE_2D_MULTISAMPLE) + texture_gl->t.flags &= ~WINED3D_TEXTURE_DOWNLOADABLE; + + return WINED3D_OK; +} + +HRESULT CDECL wined3d_texture_create(struct wined3d_device *device, const struct wined3d_resource_desc *desc, + UINT layer_count, UINT level_count, DWORD flags, const struct wined3d_sub_resource_data *data, + void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_texture **texture) +{ + unsigned int sub_count = level_count * layer_count; + unsigned int i; + HRESULT hr; + + TRACE("device %p, desc %p, layer_count %u, level_count %u, flags %#x, data %p, " + "parent %p, parent_ops %p, texture %p.\n", + device, desc, layer_count, level_count, flags, data, parent, parent_ops, texture); + + if (!layer_count) + { + WARN("Invalid layer count.\n"); + return E_INVALIDARG; + } + if ((desc->usage & WINED3DUSAGE_LEGACY_CUBEMAP) && layer_count != 6) + { + ERR("Invalid layer count %u for legacy cubemap.\n", layer_count); + layer_count = 6; + } + + if (!level_count) + { + WARN("Invalid level count.\n"); + return WINED3DERR_INVALIDCALL; + } + + if (desc->multisample_type != WINED3D_MULTISAMPLE_NONE) + { + const struct wined3d_format *format = wined3d_get_format(device->adapter, desc->format, desc->bind_flags); + + if (desc->multisample_type == WINED3D_MULTISAMPLE_NON_MASKABLE + && desc->multisample_quality >= wined3d_popcount(format->multisample_types)) + { + WARN("Unsupported quality level %u requested for WINED3D_MULTISAMPLE_NON_MASKABLE.\n", + desc->multisample_quality); + return WINED3DERR_NOTAVAILABLE; + } + if (desc->multisample_type != WINED3D_MULTISAMPLE_NON_MASKABLE + && (!(format->multisample_types & 1u << (desc->multisample_type - 1)) + || (desc->multisample_quality && desc->multisample_quality != WINED3D_STANDARD_MULTISAMPLE_PATTERN))) + { + WARN("Unsupported multisample type %u quality %u requested.\n", desc->multisample_type, + desc->multisample_quality); + return WINED3DERR_NOTAVAILABLE; + } + } + + if (data) + { + for (i = 0; i < sub_count; ++i) + { + if (data[i].data) + continue; + + WARN("Invalid sub-resource data specified for sub-resource %u.\n", i); + return E_INVALIDARG; + } + } + + if (FAILED(hr = device->adapter->adapter_ops->adapter_create_texture(device, desc, + layer_count, level_count, flags, parent, parent_ops, texture))) + return hr; + + /* FIXME: We'd like to avoid ever allocating system memory for the texture + * in this case. */ + if (data) + { + struct wined3d_box box; + + for (i = 0; i < sub_count; ++i) + { + wined3d_texture_get_level_box(*texture, i % (*texture)->level_count, &box); + wined3d_cs_emit_update_sub_resource(device->cs, &(*texture)->resource, + i, &box, data[i].data, data[i].row_pitch, data[i].slice_pitch); + } + } + + TRACE("Created texture %p.\n", *texture); + + return WINED3D_OK; +} + +HRESULT CDECL wined3d_texture_get_dc(struct wined3d_texture *texture, unsigned int sub_resource_idx, HDC *dc) +{ + struct wined3d_device *device = texture->resource.device; + struct wined3d_texture_sub_resource *sub_resource; + struct wined3d_dc_info *dc_info; + + TRACE("texture %p, sub_resource_idx %u, dc %p.\n", texture, sub_resource_idx, dc); + + if (!(texture->flags & WINED3D_TEXTURE_GET_DC)) + { + WARN("Texture does not support GetDC\n"); + /* Don't touch the DC */ + return WINED3DERR_INVALIDCALL; + } + + if (!(sub_resource = wined3d_texture_get_sub_resource(texture, sub_resource_idx))) + return WINED3DERR_INVALIDCALL; + + if (texture->resource.type != WINED3D_RTYPE_TEXTURE_2D) + { + WARN("Not supported on %s resources.\n", debug_d3dresourcetype(texture->resource.type)); + return WINED3DERR_INVALIDCALL; + } + + if (texture->resource.map_count && !(texture->flags & WINED3D_TEXTURE_GET_DC_LENIENT)) + return WINED3DERR_INVALIDCALL; + + if (!(dc_info = texture->dc_info) || !dc_info[sub_resource_idx].dc) + { + struct wined3d_texture_idx texture_idx = {texture, sub_resource_idx}; + + wined3d_cs_init_object(device->cs, wined3d_texture_create_dc, &texture_idx); + wined3d_cs_finish(device->cs, WINED3D_CS_QUEUE_DEFAULT); + if (!(dc_info = texture->dc_info) || !dc_info[sub_resource_idx].dc) + return WINED3DERR_INVALIDCALL; + } + + if (!(texture->flags & WINED3D_TEXTURE_GET_DC_LENIENT)) + texture->flags |= WINED3D_TEXTURE_DC_IN_USE; + ++texture->resource.map_count; + ++sub_resource->map_count; + + *dc = dc_info[sub_resource_idx].dc; + TRACE("Returning dc %p.\n", *dc); + + return WINED3D_OK; +} + +HRESULT CDECL wined3d_texture_release_dc(struct wined3d_texture *texture, unsigned int sub_resource_idx, HDC dc) +{ + struct wined3d_device *device = texture->resource.device; + struct wined3d_texture_sub_resource *sub_resource; + struct wined3d_dc_info *dc_info; + + TRACE("texture %p, sub_resource_idx %u, dc %p.\n", texture, sub_resource_idx, dc); + + if (!(sub_resource = wined3d_texture_get_sub_resource(texture, sub_resource_idx))) + return WINED3DERR_INVALIDCALL; + + if (texture->resource.type != WINED3D_RTYPE_TEXTURE_2D) + { + WARN("Not supported on %s resources.\n", debug_d3dresourcetype(texture->resource.type)); + return WINED3DERR_INVALIDCALL; + } + + if (!(texture->flags & (WINED3D_TEXTURE_GET_DC_LENIENT | WINED3D_TEXTURE_DC_IN_USE))) + return WINED3DERR_INVALIDCALL; + + if (!(dc_info = texture->dc_info) || dc_info[sub_resource_idx].dc != dc) + { + WARN("Application tries to release invalid DC %p, sub-resource DC is %p.\n", + dc, dc_info ? dc_info[sub_resource_idx].dc : NULL); + return WINED3DERR_INVALIDCALL; + } + + if (!(texture->resource.usage & WINED3DUSAGE_OWNDC)) + { + struct wined3d_texture_idx texture_idx = {texture, sub_resource_idx}; + + wined3d_cs_destroy_object(device->cs, wined3d_texture_destroy_dc, &texture_idx); + wined3d_cs_finish(device->cs, WINED3D_CS_QUEUE_DEFAULT); + } + + --sub_resource->map_count; + if (!--texture->resource.map_count && texture->update_map_binding) + wined3d_texture_update_map_binding(texture); + if (!(texture->flags & WINED3D_TEXTURE_GET_DC_LENIENT)) + texture->flags &= ~WINED3D_TEXTURE_DC_IN_USE; + + return WINED3D_OK; +} + +void wined3d_texture_upload_from_texture(struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx, + unsigned int dst_x, unsigned int dst_y, unsigned int dst_z, struct wined3d_texture *src_texture, + unsigned int src_sub_resource_idx, const struct wined3d_box *src_box) +{ + unsigned int src_row_pitch, src_slice_pitch; + unsigned int update_w, update_h, update_d; + unsigned int src_level, dst_level; + struct wined3d_context *context; + struct wined3d_bo_address data; + + TRACE("dst_texture %p, dst_sub_resource_idx %u, dst_x %u, dst_y %u, dst_z %u, " + "src_texture %p, src_sub_resource_idx %u, src_box %s.\n", + dst_texture, dst_sub_resource_idx, dst_x, dst_y, dst_z, + src_texture, src_sub_resource_idx, debug_box(src_box)); + + context = context_acquire(dst_texture->resource.device, NULL, 0); + + /* Only load the sub-resource for partial updates. For newly allocated + * textures the texture wouldn't be the current location, and we'd upload + * zeroes just to overwrite them again. */ + update_w = src_box->right - src_box->left; + update_h = src_box->bottom - src_box->top; + update_d = src_box->back - src_box->front; + dst_level = dst_sub_resource_idx % dst_texture->level_count; + if (update_w == wined3d_texture_get_level_width(dst_texture, dst_level) + && update_h == wined3d_texture_get_level_height(dst_texture, dst_level) + && update_d == wined3d_texture_get_level_depth(dst_texture, dst_level)) + wined3d_texture_prepare_location(dst_texture, dst_sub_resource_idx, context, WINED3D_LOCATION_TEXTURE_RGB); + else + wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, WINED3D_LOCATION_TEXTURE_RGB); + + src_level = src_sub_resource_idx % src_texture->level_count; + wined3d_texture_get_memory(src_texture, src_sub_resource_idx, &data, + src_texture->sub_resources[src_sub_resource_idx].locations); + wined3d_texture_get_pitch(src_texture, src_level, &src_row_pitch, &src_slice_pitch); + + dst_texture->texture_ops->texture_upload_data(context, wined3d_const_bo_address(&data), + src_texture->resource.format, src_box, src_row_pitch, src_slice_pitch, dst_texture, + dst_sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB, dst_x, dst_y, dst_z); + + context_release(context); + + wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB); + wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~WINED3D_LOCATION_TEXTURE_RGB); +} + +/* Partial downloads are not supported. */ +void wined3d_texture_download_from_texture(struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx, + struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx) +{ + unsigned int src_level, dst_level, dst_row_pitch, dst_slice_pitch; + unsigned int dst_location = dst_texture->resource.map_binding; + struct wined3d_context *context; + struct wined3d_bo_address data; + struct wined3d_box src_box; + unsigned int src_location; + + context = context_acquire(src_texture->resource.device, NULL, 0); + + wined3d_texture_prepare_location(dst_texture, dst_sub_resource_idx, context, dst_location); + wined3d_texture_get_memory(dst_texture, dst_sub_resource_idx, &data, dst_location); + + if (src_texture->sub_resources[src_sub_resource_idx].locations & WINED3D_LOCATION_TEXTURE_RGB) + src_location = WINED3D_LOCATION_TEXTURE_RGB; + else + src_location = WINED3D_LOCATION_TEXTURE_SRGB; + src_level = src_sub_resource_idx % src_texture->level_count; + wined3d_texture_get_level_box(src_texture, src_level, &src_box); + + dst_level = dst_sub_resource_idx % dst_texture->level_count; + wined3d_texture_get_pitch(dst_texture, dst_level, &dst_row_pitch, &dst_slice_pitch); + + src_texture->texture_ops->texture_download_data(context, src_texture, src_sub_resource_idx, src_location, + &src_box, &data, dst_texture->resource.format, 0, 0, 0, dst_row_pitch, dst_slice_pitch); + + context_release(context); + + wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, dst_location); + wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~dst_location); +} + +static void wined3d_texture_no3d_upload_data(struct wined3d_context *context, + const struct wined3d_const_bo_address *src_bo_addr, const struct wined3d_format *src_format, + const struct wined3d_box *src_box, unsigned int src_row_pitch, unsigned int src_slice_pitch, + struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx, unsigned int dst_location, + unsigned int dst_x, unsigned int dst_y, unsigned int dst_z) +{ + FIXME("Not implemented.\n"); +} + +static void wined3d_texture_no3d_download_data(struct wined3d_context *context, + struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx, unsigned int src_location, + const struct wined3d_box *src_box, const struct wined3d_bo_address *dst_bo_addr, + const struct wined3d_format *dst_format, unsigned int dst_x, unsigned int dst_y, unsigned int dst_z, + unsigned int dst_row_pitch, unsigned int dst_slice_pitch) +{ + FIXME("Not implemented.\n"); +} + +static BOOL wined3d_texture_no3d_prepare_location(struct wined3d_texture *texture, + unsigned int sub_resource_idx, struct wined3d_context *context, unsigned int location) +{ + if (location == WINED3D_LOCATION_SYSMEM) + return texture->sub_resources[sub_resource_idx].user_memory ? TRUE + : wined3d_resource_prepare_sysmem(&texture->resource); + + FIXME("Unhandled location %s.\n", wined3d_debug_location(location)); + return FALSE; +} + +static BOOL wined3d_texture_no3d_load_location(struct wined3d_texture *texture, + unsigned int sub_resource_idx, struct wined3d_context *context, DWORD location) +{ + TRACE("texture %p, sub_resource_idx %u, context %p, location %s.\n", + texture, sub_resource_idx, context, wined3d_debug_location(location)); + + if (location == WINED3D_LOCATION_SYSMEM) + return TRUE; + + ERR("Unhandled location %s.\n", wined3d_debug_location(location)); + + return FALSE; +} + +static void wined3d_texture_no3d_unload_location(struct wined3d_texture *texture, + struct wined3d_context *context, unsigned int location) +{ + TRACE("texture %p, context %p, location %s.\n", texture, context, wined3d_debug_location(location)); +} + +static const struct wined3d_texture_ops wined3d_texture_no3d_ops = +{ + wined3d_texture_no3d_prepare_location, + wined3d_texture_no3d_load_location, + wined3d_texture_no3d_unload_location, + wined3d_texture_no3d_upload_data, + wined3d_texture_no3d_download_data, +}; + +HRESULT wined3d_texture_no3d_init(struct wined3d_texture *texture_no3d, struct wined3d_device *device, + const struct wined3d_resource_desc *desc, unsigned int layer_count, unsigned int level_count, + uint32_t flags, void *parent, const struct wined3d_parent_ops *parent_ops) +{ + TRACE("texture_no3d %p, device %p, desc %p, layer_count %u, " + "level_count %u, flags %#x, parent %p, parent_ops %p.\n", + texture_no3d, device, desc, layer_count, + level_count, flags, parent, parent_ops); + + return wined3d_texture_init(texture_no3d, desc, layer_count, level_count, + flags, device, parent, parent_ops, &texture_no3d[1], &wined3d_texture_no3d_ops); +} + +void wined3d_vk_swizzle_from_color_fixup(VkComponentMapping *mapping, struct color_fixup_desc fixup) +{ + static const VkComponentSwizzle swizzle_source[] = + { + VK_COMPONENT_SWIZZLE_ZERO, /* CHANNEL_SOURCE_ZERO */ + VK_COMPONENT_SWIZZLE_ONE, /* CHANNEL_SOURCE_ONE */ + VK_COMPONENT_SWIZZLE_R, /* CHANNEL_SOURCE_X */ + VK_COMPONENT_SWIZZLE_G, /* CHANNEL_SOURCE_Y */ + VK_COMPONENT_SWIZZLE_B, /* CHANNEL_SOURCE_Z */ + VK_COMPONENT_SWIZZLE_A, /* CHANNEL_SOURCE_W */ + }; + + mapping->r = swizzle_source[fixup.x_source]; + mapping->g = swizzle_source[fixup.y_source]; + mapping->b = swizzle_source[fixup.z_source]; + mapping->a = swizzle_source[fixup.w_source]; +} + +const VkDescriptorImageInfo *wined3d_texture_vk_get_default_image_info(struct wined3d_texture_vk *texture_vk, + struct wined3d_context_vk *context_vk) +{ + const struct wined3d_format_vk *format_vk; + const struct wined3d_vk_info *vk_info; + struct wined3d_device_vk *device_vk; + VkImageViewCreateInfo create_info; + struct color_fixup_desc fixup; + uint32_t flags = 0; + VkResult vr; + + if (texture_vk->default_image_info.imageView) + return &texture_vk->default_image_info; + + format_vk = wined3d_format_vk(texture_vk->t.resource.format); + device_vk = wined3d_device_vk(texture_vk->t.resource.device); + vk_info = context_vk->vk_info; + + if (texture_vk->t.layer_count > 1) + flags |= WINED3D_VIEW_TEXTURE_ARRAY; + + wined3d_texture_vk_prepare_texture(texture_vk, context_vk); + create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + create_info.pNext = NULL; + create_info.flags = 0; + create_info.image = texture_vk->vk_image; + create_info.viewType = vk_image_view_type_from_wined3d(texture_vk->t.resource.type, flags); + create_info.format = format_vk->vk_format; + fixup = format_vk->f.color_fixup; + if (is_identity_fixup(fixup) || !can_use_texture_swizzle(context_vk->c.d3d_info, &format_vk->f)) + { + create_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + create_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + create_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + create_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + } + else + { + wined3d_vk_swizzle_from_color_fixup(&create_info.components, fixup); + } + create_info.subresourceRange.aspectMask = vk_aspect_mask_from_format(&format_vk->f); + create_info.subresourceRange.baseMipLevel = 0; + create_info.subresourceRange.levelCount = texture_vk->t.level_count; + create_info.subresourceRange.baseArrayLayer = 0; + create_info.subresourceRange.layerCount = texture_vk->t.layer_count; + if ((vr = VK_CALL(vkCreateImageView(device_vk->vk_device, &create_info, + NULL, &texture_vk->default_image_info.imageView))) < 0) + { + ERR("Failed to create Vulkan image view, vr %s.\n", wined3d_debug_vkresult(vr)); + return NULL; + } + + TRACE("Created image view 0x%s.\n", wine_dbgstr_longlong(texture_vk->default_image_info.imageView)); + + texture_vk->default_image_info.sampler = VK_NULL_HANDLE; + texture_vk->default_image_info.imageLayout = texture_vk->layout; + + return &texture_vk->default_image_info; +} + +static void wined3d_texture_vk_upload_data(struct wined3d_context *context, + const struct wined3d_const_bo_address *src_bo_addr, const struct wined3d_format *src_format, + const struct wined3d_box *src_box, unsigned int src_row_pitch, unsigned int src_slice_pitch, + struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx, unsigned int dst_location, + unsigned int dst_x, unsigned int dst_y, unsigned int dst_z) +{ + struct wined3d_texture_vk *dst_texture_vk = wined3d_texture_vk(dst_texture); + struct wined3d_context_vk *context_vk = wined3d_context_vk(context); + unsigned int dst_level, dst_row_pitch, dst_slice_pitch; + struct wined3d_texture_sub_resource *sub_resource; + struct wined3d_bo_address staging_bo_addr; + const struct wined3d_vk_info *vk_info; + VkCommandBuffer vk_command_buffer; + struct wined3d_bo_vk staging_bo; + VkImageAspectFlags aspect_mask; + struct wined3d_range range; + VkBufferImageCopy region; + size_t src_offset; + void *map_ptr; + + TRACE("context %p, src_bo_addr %s, src_format %s, src_box %s, src_row_pitch %u, src_slice_pitch %u, " + "dst_texture %p, dst_sub_resource_idx %u, dst_location %s, dst_x %u, dst_y %u, dst_z %u.\n", + context, debug_const_bo_address(src_bo_addr), debug_d3dformat(src_format->id), debug_box(src_box), + src_row_pitch, src_slice_pitch, dst_texture, dst_sub_resource_idx, + wined3d_debug_location(dst_location), dst_x, dst_y, dst_z); + + if (src_bo_addr->buffer_object) + { + FIXME("Unhandled buffer object %#lx.\n", src_bo_addr->buffer_object); + return; + } + + if (src_format->id != dst_texture->resource.format->id) + { + FIXME("Unhandled format conversion (%s -> %s).\n", + debug_d3dformat(src_format->id), + debug_d3dformat(dst_texture->resource.format->id)); + return; + } + + dst_level = dst_sub_resource_idx % dst_texture->level_count; + wined3d_texture_get_pitch(dst_texture, dst_level, &dst_row_pitch, &dst_slice_pitch); + if (dst_texture->resource.type == WINED3D_RTYPE_TEXTURE_1D) + src_row_pitch = dst_row_pitch = 0; + if (dst_texture->resource.type != WINED3D_RTYPE_TEXTURE_3D) + src_slice_pitch = dst_slice_pitch = 0; + + if (dst_location != WINED3D_LOCATION_TEXTURE_RGB) + { + FIXME("Unhandled location %s.\n", wined3d_debug_location(dst_location)); + return; + } + + if (wined3d_resource_get_sample_count(&dst_texture_vk->t.resource) > 1) + { + FIXME("Not supported for multisample textures.\n"); + return; + } + + aspect_mask = vk_aspect_mask_from_format(dst_texture->resource.format); + if (wined3d_popcount(aspect_mask) > 1) + { + FIXME("Unhandled multi-aspect format %s.\n", debug_d3dformat(dst_texture->resource.format->id)); + return; + } + + sub_resource = &dst_texture_vk->t.sub_resources[dst_sub_resource_idx]; + vk_info = context_vk->vk_info; + + src_offset = src_box->front * src_slice_pitch + + (src_box->top / src_format->block_height) * src_row_pitch + + (src_box->left / src_format->block_width) * src_format->block_byte_count; + + if (!wined3d_context_vk_create_bo(context_vk, sub_resource->size, + VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &staging_bo)) + { + ERR("Failed to create staging bo.\n"); + return; + } + + staging_bo_addr.buffer_object = (uintptr_t)&staging_bo; + staging_bo_addr.addr = NULL; + if (!(map_ptr = wined3d_context_map_bo_address(context, &staging_bo_addr, + sub_resource->size, WINED3D_MAP_DISCARD | WINED3D_MAP_WRITE))) + { + ERR("Failed to map staging bo.\n"); + wined3d_context_vk_destroy_bo(context_vk, &staging_bo); + return; + } + + wined3d_format_copy_data(src_format, src_bo_addr->addr + src_offset, src_row_pitch, + src_slice_pitch, map_ptr, dst_row_pitch, dst_slice_pitch, src_box->right - src_box->left, + src_box->bottom - src_box->top, src_box->back - src_box->front); + + range.offset = 0; + range.size = sub_resource->size; + wined3d_context_unmap_bo_address(context, &staging_bo_addr, 1, &range); + + if (!(vk_command_buffer = wined3d_context_vk_get_command_buffer(context_vk))) + { + ERR("Failed to get command buffer.\n"); + wined3d_context_vk_destroy_bo(context_vk, &staging_bo); + return; + } + + wined3d_context_vk_image_barrier(context_vk, vk_command_buffer, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, + vk_access_mask_from_bind_flags(dst_texture_vk->t.resource.bind_flags), + VK_ACCESS_TRANSFER_WRITE_BIT, + dst_texture_vk->layout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + dst_texture_vk->vk_image, aspect_mask); + + region.bufferOffset = staging_bo.buffer_offset; + region.bufferRowLength = (dst_row_pitch / src_format->block_byte_count) * src_format->block_width; + if (dst_row_pitch) + region.bufferImageHeight = (dst_slice_pitch / dst_row_pitch) * src_format->block_height; + else + region.bufferImageHeight = 1; + region.imageSubresource.aspectMask = aspect_mask; + region.imageSubresource.mipLevel = dst_level; + region.imageSubresource.baseArrayLayer = dst_sub_resource_idx / dst_texture_vk->t.level_count; + region.imageSubresource.layerCount = 1; + region.imageOffset.x = dst_x; + region.imageOffset.y = dst_y; + region.imageOffset.z = dst_z; + region.imageExtent.width = src_box->right - src_box->left; + region.imageExtent.height = src_box->bottom - src_box->top; + region.imageExtent.depth = src_box->back - src_box->front; + + VK_CALL(vkCmdCopyBufferToImage(vk_command_buffer, staging_bo.vk_buffer, + dst_texture_vk->vk_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion)); + + wined3d_context_vk_image_barrier(context_vk, vk_command_buffer, + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + VK_ACCESS_TRANSFER_WRITE_BIT, + vk_access_mask_from_bind_flags(dst_texture_vk->t.resource.bind_flags), + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, dst_texture_vk->layout, + dst_texture_vk->vk_image, aspect_mask); + wined3d_context_vk_reference_texture(context_vk, dst_texture_vk); + wined3d_context_vk_reference_bo(context_vk, &staging_bo); + wined3d_context_vk_destroy_bo(context_vk, &staging_bo); +} + +static void wined3d_texture_vk_download_data(struct wined3d_context *context, + struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx, unsigned int src_location, + const struct wined3d_box *src_box, const struct wined3d_bo_address *dst_bo_addr, + const struct wined3d_format *dst_format, unsigned int dst_x, unsigned int dst_y, unsigned int dst_z, + unsigned int dst_row_pitch, unsigned int dst_slice_pitch) +{ + struct wined3d_texture_vk *src_texture_vk = wined3d_texture_vk(src_texture); + struct wined3d_context_vk *context_vk = wined3d_context_vk(context); + unsigned int src_level, src_width, src_height, src_depth; + struct wined3d_texture_sub_resource *sub_resource; + unsigned int src_row_pitch, src_slice_pitch; + struct wined3d_bo_address staging_bo_addr; + const struct wined3d_vk_info *vk_info; + VkCommandBuffer vk_command_buffer; + struct wined3d_bo_vk staging_bo; + VkImageAspectFlags aspect_mask; + VkBufferImageCopy region; + void *map_ptr; + + TRACE("context %p, src_texture %p, src_sub_resource_idx %u, src_location %s, src_box %s, dst_bo_addr %s, " + "dst_format %s, dst_x %u, dst_y %u, dst_z %u, dst_row_pitch %u, dst_slice_pitch %u.\n", + context, src_texture, src_sub_resource_idx, wined3d_debug_location(src_location), + debug_box(src_box), debug_bo_address(dst_bo_addr), debug_d3dformat(dst_format->id), + dst_x, dst_y, dst_z, dst_row_pitch, dst_slice_pitch); + + if (src_location != WINED3D_LOCATION_TEXTURE_RGB) + { + FIXME("Unhandled location %s.\n", wined3d_debug_location(src_location)); + return; + } + + src_level = src_sub_resource_idx % src_texture->level_count; + src_width = wined3d_texture_get_level_width(src_texture, src_level); + src_height = wined3d_texture_get_level_height(src_texture, src_level); + src_depth = wined3d_texture_get_level_depth(src_texture, src_level); + if (src_box->left || src_box->top || src_box->right != src_width || src_box->bottom != src_height + || src_box->front || src_box->back != src_depth) + { + FIXME("Unhandled source box %s.\n", debug_box(src_box)); + return; + } + + if (dst_bo_addr->buffer_object) + { + FIXME("Unhandled buffer object %#lx.\n", dst_bo_addr->buffer_object); + return; + } + + if (dst_format->id != src_texture->resource.format->id) + { + FIXME("Unhandled format conversion (%s -> %s).\n", + debug_d3dformat(src_texture->resource.format->id), + debug_d3dformat(dst_format->id)); + return; + } + + if (dst_x || dst_y || dst_z) + { + FIXME("Unhandled destination (%u, %u, %u).\n", dst_x, dst_y, dst_z); + return; + } + + if (wined3d_resource_get_sample_count(&src_texture_vk->t.resource) > 1) + { + FIXME("Not supported for multisample textures.\n"); + return; + } + + aspect_mask = vk_aspect_mask_from_format(src_texture->resource.format); + if (wined3d_popcount(aspect_mask) > 1) + { + FIXME("Unhandled multi-aspect format %s.\n", debug_d3dformat(src_texture->resource.format->id)); + return; + } + + wined3d_texture_get_pitch(src_texture, src_level, &src_row_pitch, &src_slice_pitch); + if (src_texture->resource.type == WINED3D_RTYPE_TEXTURE_1D) + src_row_pitch = dst_row_pitch = 0; + if (src_texture->resource.type != WINED3D_RTYPE_TEXTURE_3D) + src_slice_pitch = dst_slice_pitch = 0; + + sub_resource = &src_texture_vk->t.sub_resources[src_sub_resource_idx]; + vk_info = context_vk->vk_info; + if (!(vk_command_buffer = wined3d_context_vk_get_command_buffer(context_vk))) + { + ERR("Failed to get command buffer.\n"); + return; + } + + if (!wined3d_context_vk_create_bo(context_vk, sub_resource->size, + VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &staging_bo)) + { + ERR("Failed to create staging bo.\n"); + return; + } + + wined3d_context_vk_image_barrier(context_vk, vk_command_buffer, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, + vk_access_mask_from_bind_flags(src_texture_vk->t.resource.bind_flags), + VK_ACCESS_TRANSFER_READ_BIT, + src_texture_vk->layout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + src_texture_vk->vk_image, aspect_mask); + + region.bufferOffset = staging_bo.buffer_offset; + region.bufferRowLength = 0; + region.bufferImageHeight = 0; + region.imageSubresource.aspectMask = aspect_mask; + region.imageSubresource.mipLevel = src_level; + region.imageSubresource.baseArrayLayer = src_sub_resource_idx / src_texture_vk->t.level_count; + region.imageSubresource.layerCount = 1; + region.imageOffset.x = 0; + region.imageOffset.y = 0; + region.imageOffset.z = 0; + region.imageExtent.width = src_width; + region.imageExtent.height = src_height; + region.imageExtent.depth = src_depth; + + VK_CALL(vkCmdCopyImageToBuffer(vk_command_buffer, src_texture_vk->vk_image, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, staging_bo.vk_buffer, 1, ®ion)); + + wined3d_context_vk_image_barrier(context_vk, vk_command_buffer, + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + VK_ACCESS_TRANSFER_READ_BIT, + vk_access_mask_from_bind_flags(src_texture_vk->t.resource.bind_flags), + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, src_texture_vk->layout, + src_texture_vk->vk_image, aspect_mask); + + wined3d_context_vk_reference_texture(context_vk, src_texture_vk); + wined3d_context_vk_reference_bo(context_vk, &staging_bo); + wined3d_context_vk_submit_command_buffer(context_vk, 0, NULL, NULL, 0, NULL); + wined3d_context_vk_wait_command_buffer(context_vk, src_texture_vk->command_buffer_id); + + staging_bo_addr.buffer_object = (uintptr_t)&staging_bo; + staging_bo_addr.addr = (uint8_t *)NULL; + if (!(map_ptr = wined3d_context_map_bo_address(context, &staging_bo_addr, + sub_resource->size, WINED3D_MAP_READ))) + { + ERR("Failed to map staging bo.\n"); + wined3d_context_vk_destroy_bo(context_vk, &staging_bo); + return; + } + + wined3d_format_copy_data(dst_format, map_ptr, src_row_pitch, src_slice_pitch, + dst_bo_addr->addr, dst_row_pitch, dst_slice_pitch, src_box->right - src_box->left, + src_box->bottom - src_box->top, src_box->back - src_box->front); + + wined3d_context_unmap_bo_address(context, &staging_bo_addr, 0, NULL); + wined3d_context_vk_destroy_bo(context_vk, &staging_bo); +} + +static BOOL wined3d_texture_vk_load_texture(struct wined3d_texture_vk *texture_vk, + unsigned int sub_resource_idx, struct wined3d_context *context) +{ + struct wined3d_texture_sub_resource *sub_resource; + unsigned int level, row_pitch, slice_pitch; + struct wined3d_bo_address data; + struct wined3d_box src_box; + + sub_resource = &texture_vk->t.sub_resources[sub_resource_idx]; + if (!(sub_resource->locations & WINED3D_LOCATION_SYSMEM)) + { + ERR("Unimplemented load from %s.\n", wined3d_debug_location(sub_resource->locations)); + return FALSE; + } + + level = sub_resource_idx % texture_vk->t.level_count; + wined3d_texture_get_memory(&texture_vk->t, sub_resource_idx, &data, WINED3D_LOCATION_SYSMEM); + wined3d_texture_get_level_box(&texture_vk->t, level, &src_box); + wined3d_texture_get_pitch(&texture_vk->t, level, &row_pitch, &slice_pitch); + wined3d_texture_vk_upload_data(context, wined3d_const_bo_address(&data), texture_vk->t.resource.format, + &src_box, row_pitch, slice_pitch, &texture_vk->t, sub_resource_idx, + WINED3D_LOCATION_TEXTURE_RGB, 0, 0, 0); + + return TRUE; +} + +static BOOL wined3d_texture_vk_load_sysmem(struct wined3d_texture_vk *texture_vk, + unsigned int sub_resource_idx, struct wined3d_context *context) +{ + struct wined3d_texture_sub_resource *sub_resource; + unsigned int level, row_pitch, slice_pitch; + struct wined3d_bo_address data; + struct wined3d_box src_box; + + sub_resource = &texture_vk->t.sub_resources[sub_resource_idx]; + if (!(sub_resource->locations & WINED3D_LOCATION_TEXTURE_RGB)) + { + ERR("Unimplemented load from %s.\n", wined3d_debug_location(sub_resource->locations)); + return FALSE; + } + + level = sub_resource_idx % texture_vk->t.level_count; + wined3d_texture_get_memory(&texture_vk->t, sub_resource_idx, &data, WINED3D_LOCATION_SYSMEM); + wined3d_texture_get_level_box(&texture_vk->t, level, &src_box); + wined3d_texture_get_pitch(&texture_vk->t, level, &row_pitch, &slice_pitch); + wined3d_texture_vk_download_data(context, &texture_vk->t, sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB, + &src_box, &data, texture_vk->t.resource.format, 0, 0, 0, row_pitch, slice_pitch); + + return TRUE; +} + +BOOL wined3d_texture_vk_prepare_texture(struct wined3d_texture_vk *texture_vk, + struct wined3d_context_vk *context_vk) +{ + const struct wined3d_format_vk *format_vk; + VkMemoryRequirements memory_requirements; + const struct wined3d_vk_info *vk_info; + struct wined3d_adapter_vk *adapter_vk; + struct wined3d_device_vk *device_vk; + struct wined3d_resource *resource; + VkCommandBuffer vk_command_buffer; + VkImageCreateInfo create_info; + unsigned int memory_type_idx; + VkResult vr; + + if (texture_vk->t.flags & WINED3D_TEXTURE_RGB_ALLOCATED) + return TRUE; + + if (!(vk_command_buffer = wined3d_context_vk_get_command_buffer(context_vk))) + { + ERR("Failed to get command buffer.\n"); + return FALSE; + } + + resource = &texture_vk->t.resource; + device_vk = wined3d_device_vk(resource->device); + adapter_vk = wined3d_adapter_vk(device_vk->d.adapter); + format_vk = wined3d_format_vk(resource->format); + vk_info = context_vk->vk_info; + + create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + create_info.pNext = NULL; + + create_info.flags = 0; + if (wined3d_format_is_typeless(&format_vk->f) || texture_vk->t.swapchain) + create_info.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT; + + switch (resource->type) + { + case WINED3D_RTYPE_TEXTURE_1D: + create_info.imageType = VK_IMAGE_TYPE_1D; + break; + case WINED3D_RTYPE_TEXTURE_2D: + create_info.imageType = VK_IMAGE_TYPE_2D; + if (texture_vk->t.layer_count >= 6 && resource->width == resource->height) + create_info.flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT; + break; + case WINED3D_RTYPE_TEXTURE_3D: + create_info.imageType = VK_IMAGE_TYPE_3D; + if (resource->bind_flags & (WINED3D_BIND_RENDER_TARGET | WINED3D_BIND_UNORDERED_ACCESS)) + create_info.flags |= VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT; + break; + default: + ERR("Invalid resource type %s.\n", debug_d3dresourcetype(resource->type)); + create_info.imageType = VK_IMAGE_TYPE_2D; + break; + } + + create_info.format = format_vk->vk_format; + create_info.extent.width = resource->width; + create_info.extent.height = resource->height; + create_info.extent.depth = resource->depth; + create_info.mipLevels = texture_vk->t.level_count; + create_info.arrayLayers = texture_vk->t.layer_count; + create_info.samples = max(1, wined3d_resource_get_sample_count(resource)); + create_info.tiling = VK_IMAGE_TILING_OPTIMAL; + + create_info.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; + if (resource->bind_flags & WINED3D_BIND_SHADER_RESOURCE) + create_info.usage |= VK_IMAGE_USAGE_SAMPLED_BIT; + if (resource->bind_flags & WINED3D_BIND_RENDER_TARGET) + create_info.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + if (resource->bind_flags & WINED3D_BIND_DEPTH_STENCIL) + create_info.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + if (resource->bind_flags & WINED3D_BIND_UNORDERED_ACCESS) + create_info.usage |= VK_IMAGE_USAGE_STORAGE_BIT; + + texture_vk->layout = VK_IMAGE_LAYOUT_GENERAL; + if (wined3d_popcount(resource->bind_flags == 1)) + { + switch (resource->bind_flags) + { + case WINED3D_BIND_RENDER_TARGET: + texture_vk->layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + break; + + case WINED3D_BIND_DEPTH_STENCIL: + texture_vk->layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + break; + + case WINED3D_BIND_SHADER_RESOURCE: + texture_vk->layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + break; + + default: + break; + } + } + + create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + create_info.queueFamilyIndexCount = 0; + create_info.pQueueFamilyIndices = NULL; + create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + if ((vr = VK_CALL(vkCreateImage(device_vk->vk_device, &create_info, NULL, &texture_vk->vk_image))) < 0) + { + ERR("Failed to create Vulkan image, vr %s.\n", wined3d_debug_vkresult(vr)); + return FALSE; + } + + VK_CALL(vkGetImageMemoryRequirements(device_vk->vk_device, texture_vk->vk_image, &memory_requirements)); + + memory_type_idx = wined3d_adapter_vk_get_memory_type_index(adapter_vk, + memory_requirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + if (memory_type_idx == ~0u) + { + ERR("Failed to find suitable memory type.\n"); + VK_CALL(vkDestroyImage(device_vk->vk_device, texture_vk->vk_image, NULL)); + texture_vk->vk_image = VK_NULL_HANDLE; + return FALSE; + } + + texture_vk->memory = wined3d_context_vk_allocate_memory(context_vk, + memory_type_idx, memory_requirements.size, &texture_vk->vk_memory); + if (!texture_vk->vk_memory) + { + ERR("Failed to allocate image memory.\n"); + VK_CALL(vkDestroyImage(device_vk->vk_device, texture_vk->vk_image, NULL)); + texture_vk->vk_image = VK_NULL_HANDLE; + return FALSE; + } + + if ((vr = VK_CALL(vkBindImageMemory(device_vk->vk_device, texture_vk->vk_image, + texture_vk->vk_memory, texture_vk->memory ? texture_vk->memory->offset : 0))) < 0) + { + WARN("Failed to bind memory, vr %s.\n", wined3d_debug_vkresult(vr)); + if (texture_vk->memory) + wined3d_allocator_block_free(texture_vk->memory); + else + VK_CALL(vkFreeMemory(device_vk->vk_device, texture_vk->vk_memory, NULL)); + texture_vk->vk_memory = VK_NULL_HANDLE; + VK_CALL(vkDestroyImage(device_vk->vk_device, texture_vk->vk_image, NULL)); + texture_vk->vk_image = VK_NULL_HANDLE; + return FALSE; + } + + wined3d_context_vk_reference_texture(context_vk, texture_vk); + wined3d_context_vk_image_barrier(context_vk, vk_command_buffer, + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, + 0, 0, + VK_IMAGE_LAYOUT_UNDEFINED, texture_vk->layout, + texture_vk->vk_image, vk_aspect_mask_from_format(&format_vk->f)); + + texture_vk->t.flags |= WINED3D_TEXTURE_RGB_ALLOCATED; + + TRACE("Created image 0x%s, memory 0x%s for texture %p.\n", + wine_dbgstr_longlong(texture_vk->vk_image), wine_dbgstr_longlong(texture_vk->vk_memory), texture_vk); + + return TRUE; +} + +static BOOL wined3d_texture_vk_prepare_location(struct wined3d_texture *texture, + unsigned int sub_resource_idx, struct wined3d_context *context, unsigned int location) +{ + switch (location) + { + case WINED3D_LOCATION_SYSMEM: + return texture->sub_resources[sub_resource_idx].user_memory ? TRUE + : wined3d_resource_prepare_sysmem(&texture->resource); + + case WINED3D_LOCATION_TEXTURE_RGB: + return wined3d_texture_vk_prepare_texture(wined3d_texture_vk(texture), wined3d_context_vk(context)); + + default: + FIXME("Unhandled location %s.\n", wined3d_debug_location(location)); + return FALSE; + } +} + +static BOOL wined3d_texture_vk_load_location(struct wined3d_texture *texture, + unsigned int sub_resource_idx, struct wined3d_context *context, DWORD location) +{ + if (!wined3d_texture_vk_prepare_location(texture, sub_resource_idx, context, location)) + return FALSE; + + switch (location) + { + case WINED3D_LOCATION_TEXTURE_RGB: + return wined3d_texture_vk_load_texture(wined3d_texture_vk(texture), sub_resource_idx, context); + + case WINED3D_LOCATION_SYSMEM: + return wined3d_texture_vk_load_sysmem(wined3d_texture_vk(texture), sub_resource_idx, context); + + default: + FIXME("Unimplemented location %s.\n", wined3d_debug_location(location)); + return FALSE; + } +} + +static void wined3d_texture_vk_unload_location(struct wined3d_texture *texture, + struct wined3d_context *context, unsigned int location) +{ + struct wined3d_texture_vk *texture_vk = wined3d_texture_vk(texture); + struct wined3d_context_vk *context_vk = wined3d_context_vk(context); + + TRACE("texture %p, context %p, location %s.\n", texture, context, wined3d_debug_location(location)); + + switch (location) + { + case WINED3D_LOCATION_TEXTURE_RGB: + if (texture_vk->default_image_info.imageView) + { + wined3d_context_vk_destroy_image_view(context_vk, + texture_vk->default_image_info.imageView, texture_vk->command_buffer_id); + texture_vk->default_image_info.imageView = VK_NULL_HANDLE; + } + + if (texture_vk->vk_image) + { + wined3d_context_vk_destroy_image(context_vk, texture_vk->vk_image, texture_vk->command_buffer_id); + texture_vk->vk_image = VK_NULL_HANDLE; + if (texture_vk->memory) + wined3d_context_vk_destroy_allocator_block(context_vk, + texture_vk->memory, texture_vk->command_buffer_id); + else + wined3d_context_vk_destroy_memory(context_vk, + texture_vk->vk_memory, texture_vk->command_buffer_id); + texture_vk->vk_memory = VK_NULL_HANDLE; + texture_vk->memory = NULL; + } + break; + + case WINED3D_LOCATION_BUFFER: + case WINED3D_LOCATION_TEXTURE_SRGB: + case WINED3D_LOCATION_RB_MULTISAMPLE: + case WINED3D_LOCATION_RB_RESOLVED: + break; + + default: + ERR("Unhandled location %s.\n", wined3d_debug_location(location)); + break; + } +} + +static const struct wined3d_texture_ops wined3d_texture_vk_ops = +{ + wined3d_texture_vk_prepare_location, + wined3d_texture_vk_load_location, + wined3d_texture_vk_unload_location, + wined3d_texture_vk_upload_data, + wined3d_texture_vk_download_data, +}; + +HRESULT wined3d_texture_vk_init(struct wined3d_texture_vk *texture_vk, struct wined3d_device *device, + const struct wined3d_resource_desc *desc, unsigned int layer_count, unsigned int level_count, + uint32_t flags, void *parent, const struct wined3d_parent_ops *parent_ops) +{ + TRACE("texture_vk %p, device %p, desc %p, layer_count %u, " + "level_count %u, flags %#x, parent %p, parent_ops %p.\n", + texture_vk, device, desc, layer_count, + level_count, flags, parent, parent_ops); + + return wined3d_texture_init(&texture_vk->t, desc, layer_count, level_count, + flags, device, parent, parent_ops, &texture_vk[1], &wined3d_texture_vk_ops); +} + +void wined3d_texture_vk_barrier(struct wined3d_texture_vk *texture_vk, + struct wined3d_context_vk *context_vk, uint32_t bind_mask) +{ + TRACE("texture_vk %p, context_vk %p, bind_mask %s.\n", + texture_vk, context_vk, wined3d_debug_bind_flags(bind_mask)); + + if (texture_vk->bind_mask && texture_vk->bind_mask != bind_mask) + { + TRACE(" %s -> %s.\n", + wined3d_debug_bind_flags(texture_vk->bind_mask), wined3d_debug_bind_flags(bind_mask)); + wined3d_context_vk_image_barrier(context_vk, wined3d_context_vk_get_command_buffer(context_vk), + vk_pipeline_stage_mask_from_bind_flags(texture_vk->bind_mask), + vk_pipeline_stage_mask_from_bind_flags(bind_mask), + vk_access_mask_from_bind_flags(texture_vk->bind_mask), vk_access_mask_from_bind_flags(bind_mask), + texture_vk->layout, texture_vk->layout, texture_vk->vk_image, + vk_aspect_mask_from_format(texture_vk->t.resource.format)); + } + texture_vk->bind_mask = bind_mask; +} + +static void ffp_blitter_destroy(struct wined3d_blitter *blitter, struct wined3d_context *context) +{ + struct wined3d_blitter *next; + + if ((next = blitter->next)) + next->ops->blitter_destroy(next, context); + + heap_free(blitter); +} + +static bool ffp_blit_supported(enum wined3d_blit_op blit_op, const struct wined3d_context *context, + const struct wined3d_resource *src_resource, DWORD src_location, + const struct wined3d_resource *dst_resource, DWORD dst_location) +{ + const struct wined3d_format *src_format = src_resource->format; + const struct wined3d_format *dst_format = dst_resource->format; + bool decompress; + + if (src_resource->type != WINED3D_RTYPE_TEXTURE_2D) + return false; + + decompress = (src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED) + && !(dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED); + if (!decompress && !(src_resource->access & dst_resource->access & WINED3D_RESOURCE_ACCESS_GPU)) + { + TRACE("Source or destination resource is not GPU accessible.\n"); + return false; + } + + if (blit_op == WINED3D_BLIT_OP_RAW_BLIT && dst_format->id == src_format->id) + { + if (dst_format->depth_size || dst_format->stencil_size) + blit_op = WINED3D_BLIT_OP_DEPTH_BLIT; + else + blit_op = WINED3D_BLIT_OP_COLOR_BLIT; + } + + switch (blit_op) + { + case WINED3D_BLIT_OP_COLOR_BLIT_CKEY: + if (context->d3d_info->shader_color_key) + { + TRACE("Colour keying requires converted textures.\n"); + return false; + } + case WINED3D_BLIT_OP_COLOR_BLIT: + case WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST: + if (!wined3d_context_gl_const(context)->gl_info->supported[WINED3D_GL_LEGACY_CONTEXT]) + return false; + + if (TRACE_ON(d3d)) + { + TRACE("Checking support for fixup:\n"); + dump_color_fixup_desc(src_format->color_fixup); + } + + /* We only support identity conversions. */ + if (!is_identity_fixup(src_format->color_fixup) + || !is_identity_fixup(dst_format->color_fixup)) + { + if (wined3d_settings.offscreen_rendering_mode == ORM_BACKBUFFER + && dst_format->id == src_format->id && dst_location == WINED3D_LOCATION_DRAWABLE) + { + WARN("Claiming fixup support because of ORM_BACKBUFFER.\n"); + } + else + { + TRACE("Fixups are not supported.\n"); + return false; + } + } + + if (!(dst_resource->bind_flags & WINED3D_BIND_RENDER_TARGET)) + { + TRACE("Can only blit to render targets.\n"); + return false; + } + return true; + + default: + TRACE("Unsupported blit operation %#x.\n", blit_op); + return false; + } +} + +static bool is_full_clear(const struct wined3d_rendertarget_view *rtv, const RECT *draw_rect, const RECT *clear_rect) +{ + unsigned int height = rtv->height; + unsigned int width = rtv->width; + + /* partial draw rect */ + if (draw_rect->left || draw_rect->top || draw_rect->right < width || draw_rect->bottom < height) + return false; + + /* partial clear rect */ + if (clear_rect && (clear_rect->left > 0 || clear_rect->top > 0 + || clear_rect->right < width || clear_rect->bottom < height)) + return false; + + return true; +} + +static void ffp_blitter_clear_rendertargets(struct wined3d_device *device, unsigned int rt_count, + const struct wined3d_fb_state *fb, unsigned int rect_count, const RECT *clear_rect, const RECT *draw_rect, + uint32_t flags, const struct wined3d_color *colour, float depth, unsigned int stencil) +{ + struct wined3d_rendertarget_view *rtv = rt_count ? fb->render_targets[0] : NULL; + struct wined3d_rendertarget_view *dsv = fb->depth_stencil; + const struct wined3d_state *state = &device->cs->state; + struct wined3d_texture *depth_stencil = NULL; + unsigned int drawable_width, drawable_height; + const struct wined3d_gl_info *gl_info; + struct wined3d_context_gl *context_gl; + struct wined3d_texture *target = NULL; + struct wined3d_color colour_srgb; + struct wined3d_context *context; + GLbitfield clear_mask = 0; + bool render_offscreen; + unsigned int i; + + if (rtv && rtv->resource->type != WINED3D_RTYPE_BUFFER) + { + target = texture_from_resource(rtv->resource); + context = context_acquire(device, target, rtv->sub_resource_idx); + } + else + { + context = context_acquire(device, NULL, 0); + } + context_gl = wined3d_context_gl(context); + + if (dsv && dsv->resource->type != WINED3D_RTYPE_BUFFER) + depth_stencil = texture_from_resource(dsv->resource); + + if (!context_gl->valid) + { + context_release(context); + WARN("Invalid context, skipping clear.\n"); + return; + } + gl_info = context_gl->gl_info; + + /* When we're clearing parts of the drawable, make sure that the target + * surface is well up to date in the drawable. After the clear we'll mark + * the drawable up to date, so we have to make sure that this is true for + * the cleared parts, and the untouched parts. + * + * If we're clearing the whole target there is no need to copy it into the + * drawable, it will be overwritten anyway. If we're not clearing the + * colour buffer we don't have to copy either since we're not going to set + * the drawable up to date. We have to check all settings that limit the + * clear area though. Do not bother checking all this if the destination + * surface is in the drawable anyway. */ + for (i = 0; i < rt_count; ++i) + { + struct wined3d_rendertarget_view *rtv = fb->render_targets[i]; + + if (rtv && rtv->format->id != WINED3DFMT_NULL) + { + struct wined3d_texture *rt = wined3d_texture_from_resource(rtv->resource); + + if (flags & WINED3DCLEAR_TARGET && !is_full_clear(rtv, draw_rect, rect_count ? clear_rect : NULL)) + wined3d_texture_load_location(rt, rtv->sub_resource_idx, context, rtv->resource->draw_binding); + else + wined3d_texture_prepare_location(rt, rtv->sub_resource_idx, context, rtv->resource->draw_binding); + } + } + + if (target) + { + render_offscreen = context->render_offscreen; + wined3d_rendertarget_view_get_drawable_size(rtv, context, &drawable_width, &drawable_height); + } + else + { + unsigned int ds_level = dsv->sub_resource_idx % depth_stencil->level_count; + + render_offscreen = true; + drawable_width = wined3d_texture_get_level_pow2_width(depth_stencil, ds_level); + drawable_height = wined3d_texture_get_level_pow2_height(depth_stencil, ds_level); + } + + if (depth_stencil) + { + DWORD ds_location = render_offscreen ? dsv->resource->draw_binding : WINED3D_LOCATION_DRAWABLE; + struct wined3d_texture *ds = wined3d_texture_from_resource(dsv->resource); + + if (flags & (WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL) + && !is_full_clear(dsv, draw_rect, rect_count ? clear_rect : NULL)) + wined3d_texture_load_location(ds, dsv->sub_resource_idx, context, ds_location); + else + wined3d_texture_prepare_location(ds, dsv->sub_resource_idx, context, ds_location); + + if (flags & (WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL)) + { + wined3d_texture_validate_location(ds, dsv->sub_resource_idx, ds_location); + wined3d_texture_invalidate_location(ds, dsv->sub_resource_idx, ~ds_location); + } + } + + if (!wined3d_context_gl_apply_clear_state(context_gl, state, rt_count, fb)) + { + context_release(context); + WARN("Failed to apply clear state, skipping clear.\n"); + return; + } + + /* Only set the values up once, as they are not changing. */ + if (flags & WINED3DCLEAR_STENCIL) + { + if (gl_info->supported[EXT_STENCIL_TWO_SIDE]) + gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT); + gl_info->gl_ops.gl.p_glStencilMask(~0u); + context_invalidate_state(context, STATE_DEPTH_STENCIL); + gl_info->gl_ops.gl.p_glClearStencil(stencil); + checkGLcall("glClearStencil"); + clear_mask = clear_mask | GL_STENCIL_BUFFER_BIT; + } + + if (flags & WINED3DCLEAR_ZBUFFER) + { + gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE); + context_invalidate_state(context, STATE_DEPTH_STENCIL); + if (gl_info->supported[ARB_ES2_COMPATIBILITY]) + GL_EXTCALL(glClearDepthf(depth)); + else + gl_info->gl_ops.gl.p_glClearDepth(depth); + checkGLcall("glClearDepth"); + clear_mask = clear_mask | GL_DEPTH_BUFFER_BIT; + } + + if (flags & WINED3DCLEAR_TARGET) + { + for (i = 0; i < rt_count; ++i) + { + struct wined3d_rendertarget_view *rtv = fb->render_targets[i]; + struct wined3d_texture *texture; + + if (!rtv) + continue; + + if (rtv->resource->type == WINED3D_RTYPE_BUFFER) + { + FIXME("Not supported on buffer resources.\n"); + continue; + } + + texture = texture_from_resource(rtv->resource); + wined3d_texture_validate_location(texture, rtv->sub_resource_idx, rtv->resource->draw_binding); + wined3d_texture_invalidate_location(texture, rtv->sub_resource_idx, ~rtv->resource->draw_binding); + } + + if (!gl_info->supported[ARB_FRAMEBUFFER_SRGB] && needs_srgb_write(context->d3d_info, state, fb)) + { + if (rt_count > 1) + WARN("Clearing multiple sRGB render targets without GL_ARB_framebuffer_sRGB " + "support, this might cause graphical issues.\n"); + + wined3d_colour_srgb_from_linear(&colour_srgb, colour); + colour = &colour_srgb; + } + + gl_info->gl_ops.gl.p_glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + context_invalidate_state(context, STATE_BLEND); + gl_info->gl_ops.gl.p_glClearColor(colour->r, colour->g, colour->b, colour->a); + checkGLcall("glClearColor"); + clear_mask = clear_mask | GL_COLOR_BUFFER_BIT; + } + + if (!rect_count) + { + if (render_offscreen) + { + gl_info->gl_ops.gl.p_glScissor(draw_rect->left, draw_rect->top, + draw_rect->right - draw_rect->left, draw_rect->bottom - draw_rect->top); + } + else + { + gl_info->gl_ops.gl.p_glScissor(draw_rect->left, drawable_height - draw_rect->bottom, + draw_rect->right - draw_rect->left, draw_rect->bottom - draw_rect->top); + } + gl_info->gl_ops.gl.p_glClear(clear_mask); + } + else + { + RECT current_rect; + + /* Now process each rect in turn. */ + for (i = 0; i < rect_count; ++i) + { + /* Note that GL uses lower left, width/height. */ + IntersectRect(¤t_rect, draw_rect, &clear_rect[i]); + + TRACE("clear_rect[%u] %s, current_rect %s.\n", i, + wine_dbgstr_rect(&clear_rect[i]), + wine_dbgstr_rect(¤t_rect)); + + /* Tests show that rectangles where x1 > x2 or y1 > y2 are ignored + * silently. The rectangle is not cleared, no error is returned, + * but further rectangles are still cleared if they are valid. */ + if (current_rect.left > current_rect.right || current_rect.top > current_rect.bottom) + { + TRACE("Rectangle with negative dimensions, ignoring.\n"); + continue; + } + + if (render_offscreen) + { + gl_info->gl_ops.gl.p_glScissor(current_rect.left, current_rect.top, + current_rect.right - current_rect.left, current_rect.bottom - current_rect.top); + } + else + { + gl_info->gl_ops.gl.p_glScissor(current_rect.left, drawable_height - current_rect.bottom, + current_rect.right - current_rect.left, current_rect.bottom - current_rect.top); + } + gl_info->gl_ops.gl.p_glClear(clear_mask); + } + } + context->scissor_rect_count = WINED3D_MAX_VIEWPORTS; + checkGLcall("clear"); + + if (flags & WINED3DCLEAR_TARGET && target->swapchain && target->swapchain->front_buffer == target) + gl_info->gl_ops.gl.p_glFlush(); + + context_release(context); +} + +static bool blitter_use_cpu_clear(struct wined3d_rendertarget_view *view) +{ + struct wined3d_resource *resource; + struct wined3d_texture *texture; + DWORD locations; + + resource = view->resource; + if (resource->type == WINED3D_RTYPE_BUFFER) + return !(resource->access & WINED3D_RESOURCE_ACCESS_GPU); + + texture = texture_from_resource(resource); + locations = texture->sub_resources[view->sub_resource_idx].locations; + if (locations & (resource->map_binding | WINED3D_LOCATION_DISCARDED)) + return !(resource->access & WINED3D_RESOURCE_ACCESS_GPU) + || (texture->flags & WINED3D_TEXTURE_PIN_SYSMEM); + + return !(resource->access & WINED3D_RESOURCE_ACCESS_GPU) + && !(texture->flags & WINED3D_TEXTURE_CONVERTED); +} + +static void ffp_blitter_clear(struct wined3d_blitter *blitter, struct wined3d_device *device, + unsigned int rt_count, const struct wined3d_fb_state *fb, unsigned int rect_count, const RECT *clear_rects, + const RECT *draw_rect, DWORD flags, const struct wined3d_color *colour, float depth, DWORD stencil) +{ + struct wined3d_rendertarget_view *view, *previous = NULL; + bool have_identical_size = TRUE; + struct wined3d_fb_state tmp_fb; + unsigned int next_rt_count = 0; + struct wined3d_blitter *next; + DWORD next_flags = 0; + unsigned int i; + + if (flags & WINED3DCLEAR_TARGET) + { + for (i = 0; i < rt_count; ++i) + { + if (!(view = fb->render_targets[i])) + continue; + + if (blitter_use_cpu_clear(view) + || (!(view->resource->bind_flags & WINED3D_BIND_RENDER_TARGET) + && (wined3d_settings.offscreen_rendering_mode != ORM_FBO + || !(view->format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE)))) + { + next_flags |= WINED3DCLEAR_TARGET; + flags &= ~WINED3DCLEAR_TARGET; + next_rt_count = rt_count; + rt_count = 0; + break; + } + + /* FIXME: We should reject colour fills on formats with fixups, + * but this would break P8 colour fills for example. */ + } + } + + if ((flags & (WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL)) && (view = fb->depth_stencil) + && (!view->format->depth_size || (flags & WINED3DCLEAR_ZBUFFER)) + && (!view->format->stencil_size || (flags & WINED3DCLEAR_STENCIL)) + && blitter_use_cpu_clear(view)) + { + next_flags |= flags & (WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL); + flags &= ~(WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL); + } + + if (flags) + { + for (i = 0; i < rt_count; ++i) + { + if (!(view = fb->render_targets[i])) + continue; + + if (previous && (previous->width != view->width || previous->height != view->height)) + have_identical_size = false; + previous = view; + } + if (flags & (WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL)) + { + view = fb->depth_stencil; + + if (previous && (previous->width != view->width || previous->height != view->height)) + have_identical_size = false; + } + + if (have_identical_size) + { + ffp_blitter_clear_rendertargets(device, rt_count, fb, rect_count, + clear_rects, draw_rect, flags, colour, depth, stencil); + } + else + { + for (i = 0; i < rt_count; ++i) + { + if (!(view = fb->render_targets[i])) + continue; + + tmp_fb.render_targets[0] = view; + tmp_fb.depth_stencil = NULL; + ffp_blitter_clear_rendertargets(device, 1, &tmp_fb, rect_count, + clear_rects, draw_rect, WINED3DCLEAR_TARGET, colour, depth, stencil); + } + if (flags & (WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL)) + { + tmp_fb.render_targets[0] = NULL; + tmp_fb.depth_stencil = fb->depth_stencil; + ffp_blitter_clear_rendertargets(device, 0, &tmp_fb, rect_count, + clear_rects, draw_rect, flags & ~WINED3DCLEAR_TARGET, colour, depth, stencil); + } + } + } + + if (next_flags && (next = blitter->next)) + next->ops->blitter_clear(next, device, next_rt_count, fb, rect_count, + clear_rects, draw_rect, next_flags, colour, depth, stencil); +} + +static DWORD ffp_blitter_blit(struct wined3d_blitter *blitter, enum wined3d_blit_op op, + struct wined3d_context *context, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx, + DWORD src_location, const RECT *src_rect, struct wined3d_texture *dst_texture, + unsigned int dst_sub_resource_idx, DWORD dst_location, const RECT *dst_rect, + const struct wined3d_color_key *colour_key, enum wined3d_texture_filter_type filter) +{ + struct wined3d_texture_gl *src_texture_gl = wined3d_texture_gl(src_texture); + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + struct wined3d_resource *src_resource, *dst_resource; + struct wined3d_texture *staging_texture = NULL; + struct wined3d_color_key old_blt_key; + struct wined3d_device *device; + struct wined3d_blitter *next; + DWORD old_colour_key_flags; + RECT r; + + src_resource = &src_texture->resource; + dst_resource = &dst_texture->resource; + device = dst_resource->device; + + if (!ffp_blit_supported(op, context, src_resource, src_location, dst_resource, dst_location)) + { + if ((next = blitter->next)) + return next->ops->blitter_blit(next, op, context, src_texture, src_sub_resource_idx, src_location, + src_rect, dst_texture, dst_sub_resource_idx, dst_location, dst_rect, colour_key, filter); + } + + TRACE("Blt from texture %p, %u to rendertarget %p, %u.\n", + src_texture, src_sub_resource_idx, dst_texture, dst_sub_resource_idx); + + old_blt_key = src_texture->async.src_blt_color_key; + old_colour_key_flags = src_texture->async.color_key_flags; + wined3d_texture_set_color_key(src_texture, WINED3D_CKEY_SRC_BLT, colour_key); + + if (!(src_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU)) + { + struct wined3d_resource_desc desc; + struct wined3d_box upload_box; + unsigned int src_level; + HRESULT hr; + + TRACE("Source texture is not GPU accessible, creating a staging texture.\n"); + + src_level = src_sub_resource_idx % src_texture->level_count; + desc.resource_type = WINED3D_RTYPE_TEXTURE_2D; + desc.format = src_texture->resource.format->id; + desc.multisample_type = src_texture->resource.multisample_type; + desc.multisample_quality = src_texture->resource.multisample_quality; + desc.usage = WINED3DUSAGE_PRIVATE; + desc.bind_flags = 0; + desc.access = WINED3D_RESOURCE_ACCESS_GPU; + desc.width = wined3d_texture_get_level_width(src_texture, src_level); + desc.height = wined3d_texture_get_level_height(src_texture, src_level); + desc.depth = 1; + desc.size = 0; + + if (FAILED(hr = wined3d_texture_create(device, &desc, 1, 1, 0, + NULL, NULL, &wined3d_null_parent_ops, &staging_texture))) + { + ERR("Failed to create staging texture, hr %#x.\n", hr); + return dst_location; + } + + wined3d_box_set(&upload_box, 0, 0, desc.width, desc.height, 0, desc.depth); + wined3d_texture_upload_from_texture(staging_texture, 0, 0, 0, 0, + src_texture, src_sub_resource_idx, &upload_box); + + src_texture = staging_texture; + src_texture_gl = wined3d_texture_gl(src_texture); + src_sub_resource_idx = 0; + } + else + { + /* Make sure the surface is up-to-date. This should probably use + * surface_load_location() and worry about the destination surface + * too, unless we're overwriting it completely. */ + wined3d_texture_load(src_texture, context, FALSE); + } + + wined3d_context_gl_apply_ffp_blit_state(context_gl, device); + + if (dst_location == WINED3D_LOCATION_DRAWABLE) + { + r = *dst_rect; + wined3d_texture_translate_drawable_coords(dst_texture, context_gl->window, &r); + dst_rect = &r; + } + + if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) + { + GLenum buffer; + + if (dst_location == WINED3D_LOCATION_DRAWABLE) + { + TRACE("Destination texture %p is onscreen.\n", dst_texture); + buffer = wined3d_texture_get_gl_buffer(dst_texture); + } + else + { + TRACE("Destination texture %p is offscreen.\n", dst_texture); + buffer = GL_COLOR_ATTACHMENT0; + } + wined3d_context_gl_apply_fbo_state_blit(context_gl, GL_DRAW_FRAMEBUFFER, + dst_resource, dst_sub_resource_idx, NULL, 0, dst_location); + wined3d_context_gl_set_draw_buffer(context_gl, buffer); + wined3d_context_gl_check_fbo_status(context_gl, GL_DRAW_FRAMEBUFFER); + context_invalidate_state(context, STATE_FRAMEBUFFER); + } + + gl_info->gl_ops.gl.p_glEnable(src_texture_gl->target); + checkGLcall("glEnable(target)"); + + if (op == WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST || colour_key) + { + gl_info->gl_ops.gl.p_glEnable(GL_ALPHA_TEST); + checkGLcall("glEnable(GL_ALPHA_TEST)"); + } + + if (colour_key) + { + /* For P8 surfaces, the alpha component contains the palette index. + * Which means that the colourkey is one of the palette entries. In + * other cases pixels that should be masked away have alpha set to 0. */ + if (src_texture->resource.format->id == WINED3DFMT_P8_UINT) + gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL, + (float)src_texture->async.src_blt_color_key.color_space_low_value / 255.0f); + else + gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL, 0.0f); + checkGLcall("glAlphaFunc"); + } + + wined3d_context_gl_draw_textured_quad(context_gl, src_texture_gl, + src_sub_resource_idx, src_rect, dst_rect, filter); + + if (op == WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST || colour_key) + { + gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST); + checkGLcall("glDisable(GL_ALPHA_TEST)"); + } + + gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_2D); + checkGLcall("glDisable(GL_TEXTURE_2D)"); + if (gl_info->supported[ARB_TEXTURE_CUBE_MAP]) + { + gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_CUBE_MAP_ARB); + checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)"); + } + if (gl_info->supported[ARB_TEXTURE_RECTANGLE]) + { + gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_RECTANGLE_ARB); + checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)"); + } + + if (dst_texture->swapchain && dst_texture->swapchain->front_buffer == dst_texture) + gl_info->gl_ops.gl.p_glFlush(); + + /* Restore the colour key parameters */ + wined3d_texture_set_color_key(src_texture, WINED3D_CKEY_SRC_BLT, + (old_colour_key_flags & WINED3D_CKEY_SRC_BLT) ? &old_blt_key : NULL); + + if (staging_texture) + wined3d_texture_decref(staging_texture); + + return dst_location; +} + +static const struct wined3d_blitter_ops ffp_blitter_ops = +{ + ffp_blitter_destroy, + ffp_blitter_clear, + ffp_blitter_blit, +}; + +void wined3d_ffp_blitter_create(struct wined3d_blitter **next, const struct wined3d_gl_info *gl_info) +{ + struct wined3d_blitter *blitter; + + if (!(blitter = heap_alloc(sizeof(*blitter)))) + return; + + TRACE("Created blitter %p.\n", blitter); + + blitter->ops = &ffp_blitter_ops; + blitter->next = *next; + *next = blitter; +} + +static void fbo_blitter_destroy(struct wined3d_blitter *blitter, struct wined3d_context *context) +{ + struct wined3d_blitter *next; + + if ((next = blitter->next)) + next->ops->blitter_destroy(next, context); + + heap_free(blitter); +} + +static void fbo_blitter_clear(struct wined3d_blitter *blitter, struct wined3d_device *device, + unsigned int rt_count, const struct wined3d_fb_state *fb, unsigned int rect_count, const RECT *clear_rects, + const RECT *draw_rect, DWORD flags, const struct wined3d_color *colour, float depth, DWORD stencil) +{ + struct wined3d_blitter *next; + + if ((next = blitter->next)) + next->ops->blitter_clear(next, device, rt_count, fb, rect_count, + clear_rects, draw_rect, flags, colour, depth, stencil); +} + +static DWORD fbo_blitter_blit(struct wined3d_blitter *blitter, enum wined3d_blit_op op, + struct wined3d_context *context, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx, + DWORD src_location, const RECT *src_rect, struct wined3d_texture *dst_texture, + unsigned int dst_sub_resource_idx, DWORD dst_location, const RECT *dst_rect, + const struct wined3d_color_key *colour_key, enum wined3d_texture_filter_type filter) +{ + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + struct wined3d_resource *src_resource, *dst_resource; + enum wined3d_blit_op blit_op = op; + struct wined3d_device *device; + struct wined3d_blitter *next; + + TRACE("blitter %p, op %#x, context %p, src_texture %p, src_sub_resource_idx %u, src_location %s, src_rect %s, " + "dst_texture %p, dst_sub_resource_idx %u, dst_location %s, dst_rect %s, colour_key %p, filter %s.\n", + blitter, op, context, src_texture, src_sub_resource_idx, wined3d_debug_location(src_location), + wine_dbgstr_rect(src_rect), dst_texture, dst_sub_resource_idx, wined3d_debug_location(dst_location), + wine_dbgstr_rect(dst_rect), colour_key, debug_d3dtexturefiltertype(filter)); + + src_resource = &src_texture->resource; + dst_resource = &dst_texture->resource; + + device = dst_resource->device; + + if (blit_op == WINED3D_BLIT_OP_RAW_BLIT && dst_resource->format->id == src_resource->format->id) + { + if (dst_resource->format->depth_size || dst_resource->format->stencil_size) + blit_op = WINED3D_BLIT_OP_DEPTH_BLIT; + else + blit_op = WINED3D_BLIT_OP_COLOR_BLIT; + } + + if (!fbo_blitter_supported(blit_op, context_gl->gl_info, + src_resource, src_location, dst_resource, dst_location)) + { + if (!(next = blitter->next)) + { + ERR("No blitter to handle blit op %#x.\n", op); + return dst_location; + } + + TRACE("Forwarding to blitter %p.\n", next); + return next->ops->blitter_blit(next, op, context, src_texture, src_sub_resource_idx, src_location, + src_rect, dst_texture, dst_sub_resource_idx, dst_location, dst_rect, colour_key, filter); + } + + if (blit_op == WINED3D_BLIT_OP_COLOR_BLIT) + { + TRACE("Colour blit.\n"); + texture2d_blt_fbo(device, context, filter, src_texture, src_sub_resource_idx, src_location, + src_rect, dst_texture, dst_sub_resource_idx, dst_location, dst_rect); + return dst_location; + } + + if (blit_op == WINED3D_BLIT_OP_DEPTH_BLIT) + { + TRACE("Depth/stencil blit.\n"); + texture2d_depth_blt_fbo(device, context, src_texture, src_sub_resource_idx, src_location, + src_rect, dst_texture, dst_sub_resource_idx, dst_location, dst_rect); + return dst_location; + } + + ERR("This blitter does not implement blit op %#x.\n", blit_op); + return dst_location; +} + +static const struct wined3d_blitter_ops fbo_blitter_ops = +{ + fbo_blitter_destroy, + fbo_blitter_clear, + fbo_blitter_blit, +}; + +void wined3d_fbo_blitter_create(struct wined3d_blitter **next, const struct wined3d_gl_info *gl_info) +{ + struct wined3d_blitter *blitter; + + if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer) + return; + + if (!(blitter = heap_alloc(sizeof(*blitter)))) + return; + + TRACE("Created blitter %p.\n", blitter); + + blitter->ops = &fbo_blitter_ops; + blitter->next = *next; + *next = blitter; +} + +static void raw_blitter_destroy(struct wined3d_blitter *blitter, struct wined3d_context *context) +{ + struct wined3d_blitter *next; + + if ((next = blitter->next)) + next->ops->blitter_destroy(next, context); + + heap_free(blitter); +} + +static void raw_blitter_clear(struct wined3d_blitter *blitter, struct wined3d_device *device, + unsigned int rt_count, const struct wined3d_fb_state *fb, unsigned int rect_count, const RECT *clear_rects, + const RECT *draw_rect, DWORD flags, const struct wined3d_color *colour, float depth, DWORD stencil) +{ + struct wined3d_blitter *next; + + if (!(next = blitter->next)) + { + ERR("No blitter to handle clear.\n"); + return; + } + + TRACE("Forwarding to blitter %p.\n", next); + next->ops->blitter_clear(next, device, rt_count, fb, rect_count, + clear_rects, draw_rect, flags, colour, depth, stencil); +} + +static DWORD raw_blitter_blit(struct wined3d_blitter *blitter, enum wined3d_blit_op op, + struct wined3d_context *context, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx, + DWORD src_location, const RECT *src_rect, struct wined3d_texture *dst_texture, + unsigned int dst_sub_resource_idx, DWORD dst_location, const RECT *dst_rect, + const struct wined3d_color_key *colour_key, enum wined3d_texture_filter_type filter) +{ + struct wined3d_texture_gl *src_texture_gl = wined3d_texture_gl(src_texture); + struct wined3d_texture_gl *dst_texture_gl = wined3d_texture_gl(dst_texture); + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + unsigned int src_level, src_layer, dst_level, dst_layer; + struct wined3d_blitter *next; + GLuint src_name, dst_name; + DWORD location; + + /* If we would need to copy from a renderbuffer or drawable, we'd probably + * be better off using the FBO blitter directly, since we'd need to use it + * to copy the resource contents to the texture anyway. */ + if (op != WINED3D_BLIT_OP_RAW_BLIT + || (src_texture->resource.format->id == dst_texture->resource.format->id + && (!(src_location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB)) + || !(dst_location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))))) + { + if (!(next = blitter->next)) + { + ERR("No blitter to handle blit op %#x.\n", op); + return dst_location; + } + + TRACE("Forwarding to blitter %p.\n", next); + return next->ops->blitter_blit(next, op, context, src_texture, src_sub_resource_idx, src_location, + src_rect, dst_texture, dst_sub_resource_idx, dst_location, dst_rect, colour_key, filter); + } + + TRACE("Blit using ARB_copy_image.\n"); + + src_level = src_sub_resource_idx % src_texture->level_count; + src_layer = src_sub_resource_idx / src_texture->level_count; + + dst_level = dst_sub_resource_idx % dst_texture->level_count; + dst_layer = dst_sub_resource_idx / dst_texture->level_count; + + location = src_location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB); + if (!location) + location = src_texture->flags & WINED3D_TEXTURE_IS_SRGB + ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB; + if (!wined3d_texture_load_location(src_texture, src_sub_resource_idx, context, location)) + ERR("Failed to load the source sub-resource into %s.\n", wined3d_debug_location(location)); + src_name = wined3d_texture_gl_get_texture_name(src_texture_gl, + context, location == WINED3D_LOCATION_TEXTURE_SRGB); + + location = dst_location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB); + if (!location) + location = dst_texture->flags & WINED3D_TEXTURE_IS_SRGB + ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB; + if (wined3d_texture_is_full_rect(dst_texture, dst_level, dst_rect)) + { + if (!wined3d_texture_prepare_location(dst_texture, dst_sub_resource_idx, context, location)) + ERR("Failed to prepare the destination sub-resource into %s.\n", wined3d_debug_location(location)); + } + else + { + if (!wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, location)) + ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(location)); + } + dst_name = wined3d_texture_gl_get_texture_name(dst_texture_gl, + context, location == WINED3D_LOCATION_TEXTURE_SRGB); + + GL_EXTCALL(glCopyImageSubData(src_name, src_texture_gl->target, src_level, + src_rect->left, src_rect->top, src_layer, dst_name, dst_texture_gl->target, dst_level, + dst_rect->left, dst_rect->top, dst_layer, src_rect->right - src_rect->left, + src_rect->bottom - src_rect->top, 1)); + checkGLcall("copy image data"); + + wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, location); + wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~location); + if (!wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, dst_location)) + ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(dst_location)); + + return dst_location | location; +} + +static const struct wined3d_blitter_ops raw_blitter_ops = +{ + raw_blitter_destroy, + raw_blitter_clear, + raw_blitter_blit, +}; + +void wined3d_raw_blitter_create(struct wined3d_blitter **next, const struct wined3d_gl_info *gl_info) +{ + struct wined3d_blitter *blitter; + + if (!gl_info->supported[ARB_COPY_IMAGE]) + return; + + if (!(blitter = heap_alloc(sizeof(*blitter)))) + return; + + TRACE("Created blitter %p.\n", blitter); + + blitter->ops = &raw_blitter_ops; + blitter->next = *next; + *next = blitter; +} + +static void vk_blitter_destroy(struct wined3d_blitter *blitter, struct wined3d_context *context) +{ + struct wined3d_blitter *next; + + TRACE("blitter %p, context %p.\n", blitter, context); + + if ((next = blitter->next)) + next->ops->blitter_destroy(next, context); + + heap_free(blitter); +} + +static void vk_blitter_clear_rendertargets(struct wined3d_context_vk *context_vk, unsigned int rt_count, + const struct wined3d_fb_state *fb, unsigned int rect_count, const RECT *clear_rects, const RECT *draw_rect, + uint32_t flags, const struct wined3d_color *colour, float depth, unsigned int stencil) +{ + VkClearValue clear_values[WINED3D_MAX_RENDER_TARGETS + 1]; + VkImageView views[WINED3D_MAX_RENDER_TARGETS + 1]; + struct wined3d_rendertarget_view_vk *rtv_vk; + struct wined3d_rendertarget_view *view; + const struct wined3d_vk_info *vk_info; + struct wined3d_device_vk *device_vk; + VkCommandBuffer vk_command_buffer; + VkRenderPassBeginInfo begin_desc; + unsigned int i, attachment_count; + VkFramebufferCreateInfo fb_desc; + VkFramebuffer vk_framebuffer; + VkRenderPass vk_render_pass; + bool depth_stencil = false; + unsigned int layer_count; + VkClearColorValue *c; + VkResult vr; + RECT r; + + TRACE("context_vk %p, rt_count %u, fb %p, rect_count %u, clear_rects %p, " + "draw_rect %s, flags %#x, colour %s, depth %.8e, stencil %#x.\n", + context_vk, rt_count, fb, rect_count, clear_rects, + wine_dbgstr_rect(draw_rect), flags, debug_color(colour), depth, stencil); + + device_vk = wined3d_device_vk(context_vk->c.device); + vk_info = context_vk->vk_info; + + if (!(flags & WINED3DCLEAR_TARGET)) + rt_count = 0; + + for (i = 0, attachment_count = 0, layer_count = 1; i < rt_count; ++i) + { + if (!(view = fb->render_targets[i])) + continue; + + if (!is_full_clear(view, draw_rect, clear_rects)) + wined3d_rendertarget_view_load_location(view, &context_vk->c, view->resource->draw_binding); + else + wined3d_rendertarget_view_prepare_location(view, &context_vk->c, view->resource->draw_binding); + wined3d_rendertarget_view_validate_location(view, view->resource->draw_binding); + wined3d_rendertarget_view_invalidate_location(view, ~view->resource->draw_binding); + + rtv_vk = wined3d_rendertarget_view_vk(view); + views[attachment_count] = wined3d_rendertarget_view_vk_get_image_view(rtv_vk, context_vk); + wined3d_rendertarget_view_vk_barrier(rtv_vk, context_vk, WINED3D_BIND_RENDER_TARGET); + + c = &clear_values[attachment_count].color; + if (view->format_flags & WINED3DFMT_FLAG_INTEGER) + { + c->int32[0] = colour->r; + c->int32[1] = colour->g; + c->int32[2] = colour->b; + c->int32[3] = colour->a; + } + else + { + c->float32[0] = colour->r; + c->float32[1] = colour->g; + c->float32[2] = colour->b; + c->float32[3] = colour->a; + } + + if (view->layer_count > layer_count) + layer_count = view->layer_count; + + ++attachment_count; + } + + if (flags & (WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL) && (view = fb->depth_stencil)) + { + if (!is_full_clear(view, draw_rect, clear_rects)) + wined3d_rendertarget_view_load_location(view, &context_vk->c, view->resource->draw_binding); + else + wined3d_rendertarget_view_prepare_location(view, &context_vk->c, view->resource->draw_binding); + wined3d_rendertarget_view_validate_location(view, view->resource->draw_binding); + wined3d_rendertarget_view_invalidate_location(view, ~view->resource->draw_binding); + + rtv_vk = wined3d_rendertarget_view_vk(view); + views[attachment_count] = wined3d_rendertarget_view_vk_get_image_view(rtv_vk, context_vk); + wined3d_rendertarget_view_vk_barrier(rtv_vk, context_vk, WINED3D_BIND_DEPTH_STENCIL); + + clear_values[attachment_count].depthStencil.depth = depth; + clear_values[attachment_count].depthStencil.stencil = stencil; + + if (view->layer_count > layer_count) + layer_count = view->layer_count; + + depth_stencil = true; + ++attachment_count; + } + + if (!attachment_count) + return; + + if (!(vk_render_pass = wined3d_context_vk_get_render_pass(context_vk, fb, + rt_count, flags & (WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL), flags))) + { + ERR("Failed to get render pass.\n"); + return; + } + + if (!(vk_command_buffer = wined3d_context_vk_get_command_buffer(context_vk))) + { + ERR("Failed to get command buffer.\n"); + return; + } + + fb_desc.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + fb_desc.pNext = NULL; + fb_desc.flags = 0; + fb_desc.renderPass = vk_render_pass; + fb_desc.attachmentCount = attachment_count; + fb_desc.pAttachments = views; + fb_desc.width = draw_rect->right - draw_rect->left; + fb_desc.height = draw_rect->bottom - draw_rect->top; + fb_desc.layers = layer_count; + if ((vr = VK_CALL(vkCreateFramebuffer(device_vk->vk_device, &fb_desc, NULL, &vk_framebuffer))) < 0) + { + ERR("Failed to create Vulkan framebuffer, vr %s.\n", wined3d_debug_vkresult(vr)); + return; + } + + begin_desc.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + begin_desc.pNext = NULL; + begin_desc.renderPass = vk_render_pass; + begin_desc.framebuffer = vk_framebuffer; + begin_desc.clearValueCount = attachment_count; + begin_desc.pClearValues = clear_values; + + wined3d_context_vk_end_current_render_pass(context_vk); + + for (i = 0; i < rect_count; ++i) + { + r.left = max(clear_rects[i].left, draw_rect->left); + r.top = max(clear_rects[i].top, draw_rect->top); + r.right = min(clear_rects[i].right, draw_rect->right); + r.bottom = min(clear_rects[i].bottom, draw_rect->bottom); + + if (r.left >= r.right || r.top >= r.bottom) + continue; + + begin_desc.renderArea.offset.x = r.left; + begin_desc.renderArea.offset.y = r.top; + begin_desc.renderArea.extent.width = r.right - r.left; + begin_desc.renderArea.extent.height = r.bottom - r.top; + VK_CALL(vkCmdBeginRenderPass(vk_command_buffer, &begin_desc, VK_SUBPASS_CONTENTS_INLINE)); + VK_CALL(vkCmdEndRenderPass(vk_command_buffer)); + } + + wined3d_context_vk_destroy_framebuffer(context_vk, vk_framebuffer, context_vk->current_command_buffer.id); + + for (i = 0; i < rt_count; ++i) + { + if (!(view = fb->render_targets[i])) + continue; + + wined3d_context_vk_reference_rendertarget_view(context_vk, wined3d_rendertarget_view_vk(view)); + } + + if (depth_stencil) + { + view = fb->depth_stencil; + wined3d_context_vk_reference_rendertarget_view(context_vk, wined3d_rendertarget_view_vk(view)); + } +} + +static void vk_blitter_clear(struct wined3d_blitter *blitter, struct wined3d_device *device, + unsigned int rt_count, const struct wined3d_fb_state *fb, unsigned int rect_count, const RECT *clear_rects, + const RECT *draw_rect, DWORD flags, const struct wined3d_color *colour, float depth, DWORD stencil) +{ + struct wined3d_device_vk *device_vk = wined3d_device_vk(device); + struct wined3d_rendertarget_view *view, *previous = NULL; + struct wined3d_context_vk *context_vk; + bool have_identical_size = true; + struct wined3d_fb_state tmp_fb; + unsigned int next_rt_count = 0; + struct wined3d_blitter *next; + uint32_t next_flags = 0; + unsigned int i; + + TRACE("blitter %p, device %p, rt_count %u, fb %p, rect_count %u, clear_rects %p, " + "draw_rect %s, flags %#x, colour %s, depth %.8e, stencil %#x.\n", + blitter, device, rt_count, fb, rect_count, clear_rects, + wine_dbgstr_rect(draw_rect), flags, debug_color(colour), depth, stencil); + + if (!rect_count) + { + rect_count = 1; + clear_rects = draw_rect; + } + + if (flags & WINED3DCLEAR_TARGET) + { + for (i = 0; i < rt_count; ++i) + { + if (!(view = fb->render_targets[i])) + continue; + + if (blitter_use_cpu_clear(view)) + { + next_flags |= WINED3DCLEAR_TARGET; + flags &= ~WINED3DCLEAR_TARGET; + next_rt_count = rt_count; + rt_count = 0; + break; + } + } + } + + if ((flags & (WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL)) && (view = fb->depth_stencil) + && (!view->format->depth_size || (flags & WINED3DCLEAR_ZBUFFER)) + && (!view->format->stencil_size || (flags & WINED3DCLEAR_STENCIL)) + && blitter_use_cpu_clear(view)) + { + next_flags |= flags & (WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL); + flags &= ~(WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL); + } + + if (flags) + { + context_vk = wined3d_context_vk(context_acquire(&device_vk->d, NULL, 0)); + + for (i = 0; i < rt_count; ++i) + { + if (!(view = fb->render_targets[i])) + continue; + + if (previous && (previous->width != view->width || previous->height != view->height)) + have_identical_size = false; + previous = view; + } + if (flags & (WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL)) + { + view = fb->depth_stencil; + + if (previous && (previous->width != view->width || previous->height != view->height)) + have_identical_size = false; + } + + if (have_identical_size) + { + vk_blitter_clear_rendertargets(context_vk, rt_count, fb, rect_count, + clear_rects, draw_rect, flags, colour, depth, stencil); + } + else + { + for (i = 0; i < rt_count; ++i) + { + if (!(view = fb->render_targets[i])) + continue; + + tmp_fb.render_targets[0] = view; + tmp_fb.depth_stencil = NULL; + vk_blitter_clear_rendertargets(context_vk, 1, &tmp_fb, rect_count, + clear_rects, draw_rect, WINED3DCLEAR_TARGET, colour, depth, stencil); + } + if (flags & (WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL)) + { + tmp_fb.render_targets[0] = NULL; + tmp_fb.depth_stencil = fb->depth_stencil; + vk_blitter_clear_rendertargets(context_vk, 0, &tmp_fb, rect_count, + clear_rects, draw_rect, flags & ~WINED3DCLEAR_TARGET, colour, depth, stencil); + } + } + + context_release(&context_vk->c); + } + + if (!next_flags) + return; + + if (!(next = blitter->next)) + { + ERR("No blitter to handle clear.\n"); + return; + } + + TRACE("Forwarding to blitter %p.\n", next); + next->ops->blitter_clear(next, device, next_rt_count, fb, rect_count, + clear_rects, draw_rect, next_flags, colour, depth, stencil); +} + +static bool vk_blitter_blit_supported(enum wined3d_blit_op op, const struct wined3d_context *context, + const struct wined3d_resource *src_resource, const RECT *src_rect, + const struct wined3d_resource *dst_resource, const RECT *dst_rect) +{ + const struct wined3d_format *src_format = src_resource->format; + const struct wined3d_format *dst_format = dst_resource->format; + + if (!(dst_resource->access & WINED3D_RESOURCE_ACCESS_GPU)) + { + TRACE("Destination resource does not have GPU access.\n"); + return false; + } + + if (!(src_resource->access & WINED3D_RESOURCE_ACCESS_GPU)) + { + TRACE("Source resource does not have GPU access.\n"); + return false; + } + + if (dst_format->id != src_format->id) + { + if (!is_identity_fixup(dst_format->color_fixup)) + { + TRACE("Destination fixups are not supported.\n"); + return false; + } + + if (!is_identity_fixup(src_format->color_fixup)) + { + TRACE("Source fixups are not supported.\n"); + return false; + } + + if (op != WINED3D_BLIT_OP_RAW_BLIT + && wined3d_format_vk(src_format)->vk_format != wined3d_format_vk(dst_format)->vk_format) + { + TRACE("Format conversion not supported.\n"); + return false; + } + } + + if (wined3d_resource_get_sample_count(dst_resource) > 1) + { + TRACE("Multi-sample destination resource not supported.\n"); + return false; + } + + if (op == WINED3D_BLIT_OP_RAW_BLIT) + return true; + + if (op != WINED3D_BLIT_OP_COLOR_BLIT) + { + TRACE("Unsupported blit operation %#x.\n", op); + return false; + } + + if ((src_rect->right - src_rect->left != dst_rect->right - dst_rect->left) + || (src_rect->bottom - src_rect->top != dst_rect->bottom - dst_rect->top)) + { + TRACE("Scaling not supported.\n"); + return false; + } + + return true; +} + +static DWORD vk_blitter_blit(struct wined3d_blitter *blitter, enum wined3d_blit_op op, + struct wined3d_context *context, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx, + DWORD src_location, const RECT *src_rect, struct wined3d_texture *dst_texture, + unsigned int dst_sub_resource_idx, DWORD dst_location, const RECT *dst_rect, + const struct wined3d_color_key *colour_key, enum wined3d_texture_filter_type filter) +{ + struct wined3d_texture_vk *src_texture_vk = wined3d_texture_vk(src_texture); + struct wined3d_texture_vk *dst_texture_vk = wined3d_texture_vk(dst_texture); + struct wined3d_context_vk *context_vk = wined3d_context_vk(context); + const struct wined3d_vk_info *vk_info = context_vk->vk_info; + unsigned int src_level, src_layer, dst_level, dst_layer; + VkImageAspectFlags src_aspect, dst_aspect; + VkCommandBuffer vk_command_buffer; + struct wined3d_blitter *next; + bool resolve = false; + + TRACE("blitter %p, op %#x, context %p, src_texture %p, src_sub_resource_idx %u, src_location %s, src_rect %s, " + "dst_texture %p, dst_sub_resource_idx %u, dst_location %s, dst_rect %s, colour_key %p, filter %s.\n", + blitter, op, context, src_texture, src_sub_resource_idx, wined3d_debug_location(src_location), + wine_dbgstr_rect(src_rect), dst_texture, dst_sub_resource_idx, wined3d_debug_location(dst_location), + wine_dbgstr_rect(dst_rect), colour_key, debug_d3dtexturefiltertype(filter)); + + if (!vk_blitter_blit_supported(op, context, &src_texture->resource, src_rect, &dst_texture->resource, dst_rect)) + goto next; + + src_aspect = vk_aspect_mask_from_format(src_texture_vk->t.resource.format); + dst_aspect = vk_aspect_mask_from_format(dst_texture_vk->t.resource.format); + + if (wined3d_resource_get_sample_count(&src_texture_vk->t.resource) > 1) + resolve = true; + + src_level = src_sub_resource_idx % src_texture->level_count; + src_layer = src_sub_resource_idx / src_texture->level_count; + + dst_level = dst_sub_resource_idx % dst_texture->level_count; + dst_layer = dst_sub_resource_idx / dst_texture->level_count; + + if (!wined3d_texture_load_location(src_texture, src_sub_resource_idx, context, WINED3D_LOCATION_TEXTURE_RGB)) + ERR("Failed to load the source sub-resource.\n"); + + if (wined3d_texture_is_full_rect(dst_texture, dst_level, dst_rect)) + { + if (!wined3d_texture_prepare_location(dst_texture, + dst_sub_resource_idx, context, WINED3D_LOCATION_TEXTURE_RGB)) + { + ERR("Failed to prepare the destination sub-resource.\n"); + goto next; + } + } + else + { + if (!wined3d_texture_load_location(dst_texture, + dst_sub_resource_idx, context, WINED3D_LOCATION_TEXTURE_RGB)) + { + ERR("Failed to load the destination sub-resource.\n"); + goto next; + } + } + + if (!(vk_command_buffer = wined3d_context_vk_get_command_buffer(context_vk))) + { + ERR("Failed to get command buffer.\n"); + goto next; + } + + wined3d_context_vk_image_barrier(context_vk, vk_command_buffer, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, + vk_access_mask_from_bind_flags(src_texture_vk->t.resource.bind_flags), + VK_ACCESS_TRANSFER_READ_BIT, + src_texture_vk->layout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + src_texture_vk->vk_image, src_aspect); + wined3d_context_vk_image_barrier(context_vk, vk_command_buffer, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, + vk_access_mask_from_bind_flags(dst_texture_vk->t.resource.bind_flags), + VK_ACCESS_TRANSFER_WRITE_BIT, + dst_texture_vk->layout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + dst_texture_vk->vk_image, dst_aspect); + + if (resolve) + { + VkImageResolve region; + + region.srcSubresource.aspectMask = src_aspect; + region.srcSubresource.mipLevel = src_level; + region.srcSubresource.baseArrayLayer = src_layer; + region.srcSubresource.layerCount = 1; + region.srcOffset.x = src_rect->left; + region.srcOffset.y = src_rect->top; + region.srcOffset.z = 0; + region.dstSubresource.aspectMask = dst_aspect; + region.dstSubresource.mipLevel = dst_level; + region.dstSubresource.baseArrayLayer = dst_layer; + region.dstSubresource.layerCount = 1; + region.dstOffset.x = dst_rect->left; + region.dstOffset.y = dst_rect->top; + region.dstOffset.z = 0; + region.extent.width = src_rect->right - src_rect->left; + region.extent.height = src_rect->bottom - src_rect->top; + region.extent.depth = 1; + + VK_CALL(vkCmdResolveImage(vk_command_buffer, src_texture_vk->vk_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + dst_texture_vk->vk_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion)); + } + else + { + VkImageCopy region; + + region.srcSubresource.aspectMask = src_aspect; + region.srcSubresource.mipLevel = src_level; + region.srcSubresource.baseArrayLayer = src_layer; + region.srcSubresource.layerCount = 1; + region.srcOffset.x = src_rect->left; + region.srcOffset.y = src_rect->top; + region.srcOffset.z = 0; + region.dstSubresource.aspectMask = dst_aspect; + region.dstSubresource.mipLevel = dst_level; + region.dstSubresource.baseArrayLayer = dst_layer; + region.dstSubresource.layerCount = 1; + region.dstOffset.x = dst_rect->left; + region.dstOffset.y = dst_rect->top; + region.dstOffset.z = 0; + region.extent.width = src_rect->right - src_rect->left; + region.extent.height = src_rect->bottom - src_rect->top; + region.extent.depth = 1; + + VK_CALL(vkCmdCopyImage(vk_command_buffer, src_texture_vk->vk_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + dst_texture_vk->vk_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion)); + } + + wined3d_context_vk_image_barrier(context_vk, vk_command_buffer, + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + VK_ACCESS_TRANSFER_WRITE_BIT, + vk_access_mask_from_bind_flags(dst_texture_vk->t.resource.bind_flags), + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, dst_texture_vk->layout, + dst_texture_vk->vk_image, dst_aspect); + wined3d_context_vk_image_barrier(context_vk, vk_command_buffer, + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + VK_ACCESS_TRANSFER_READ_BIT, + vk_access_mask_from_bind_flags(src_texture_vk->t.resource.bind_flags), + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, src_texture_vk->layout, + src_texture_vk->vk_image, src_aspect); + + wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB); + wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~WINED3D_LOCATION_TEXTURE_RGB); + if (!wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, dst_location)) + ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(dst_location)); + + wined3d_context_vk_reference_texture(context_vk, src_texture_vk); + wined3d_context_vk_reference_texture(context_vk, dst_texture_vk); + + return dst_location | WINED3D_LOCATION_TEXTURE_RGB; + +next: + if (!(next = blitter->next)) + { + ERR("No blitter to handle blit op %#x.\n", op); + return dst_location; + } + + TRACE("Forwarding to blitter %p.\n", next); + return next->ops->blitter_blit(next, op, context, src_texture, src_sub_resource_idx, src_location, + src_rect, dst_texture, dst_sub_resource_idx, dst_location, dst_rect, colour_key, filter); +} + +static const struct wined3d_blitter_ops vk_blitter_ops = +{ + .blitter_destroy = vk_blitter_destroy, + .blitter_clear = vk_blitter_clear, + .blitter_blit = vk_blitter_blit, +}; + +void wined3d_vk_blitter_create(struct wined3d_blitter **next) +{ + struct wined3d_blitter *blitter; + + if (!(blitter = heap_alloc(sizeof(*blitter)))) + return; + + TRACE("Created blitter %p.\n", blitter); + + blitter->ops = &vk_blitter_ops; + blitter->next = *next; + *next = blitter; +} diff --git a/wrappers/directx/d3dwine_wrapper/utils.c b/wrappers/directx/d3dwine_wrapper/utils.c new file mode 100644 index 00000000000..6839065365f --- /dev/null +++ b/wrappers/directx/d3dwine_wrapper/utils.c @@ -0,0 +1,7317 @@ +/* + * Utility functions for the WineD3D Library + * + * Copyright 2002-2004 Jason Edmeades + * Copyright 2003-2004 Raphael Junqueira + * Copyright 2004 Christian Costa + * Copyright 2005 Oliver Stieber + * Copyright 2006-2008 Henri Verbeet + * Copyright 2007-2008 Stefan Dösinger for CodeWeavers + * Copyright 2009-2010 Henri Verbeet for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "config.h" +#include "wine/port.h" + +#include + +#include "wined3d_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(d3d); + +#define WINED3D_FORMAT_FOURCC_BASE (WINED3DFMT_BC7_UNORM_SRGB + 1) + +static const struct +{ + enum wined3d_format_id id; + unsigned int idx; +} +format_index_remap[] = +{ + {WINED3DFMT_UYVY, WINED3D_FORMAT_FOURCC_BASE}, + {WINED3DFMT_YUY2, WINED3D_FORMAT_FOURCC_BASE + 1}, + {WINED3DFMT_YV12, WINED3D_FORMAT_FOURCC_BASE + 2}, + {WINED3DFMT_DXT1, WINED3D_FORMAT_FOURCC_BASE + 3}, + {WINED3DFMT_DXT2, WINED3D_FORMAT_FOURCC_BASE + 4}, + {WINED3DFMT_DXT3, WINED3D_FORMAT_FOURCC_BASE + 5}, + {WINED3DFMT_DXT4, WINED3D_FORMAT_FOURCC_BASE + 6}, + {WINED3DFMT_DXT5, WINED3D_FORMAT_FOURCC_BASE + 7}, + {WINED3DFMT_MULTI2_ARGB8, WINED3D_FORMAT_FOURCC_BASE + 8}, + {WINED3DFMT_G8R8_G8B8, WINED3D_FORMAT_FOURCC_BASE + 9}, + {WINED3DFMT_R8G8_B8G8, WINED3D_FORMAT_FOURCC_BASE + 10}, + {WINED3DFMT_ATI1N, WINED3D_FORMAT_FOURCC_BASE + 11}, + {WINED3DFMT_ATI2N, WINED3D_FORMAT_FOURCC_BASE + 12}, + {WINED3DFMT_INST, WINED3D_FORMAT_FOURCC_BASE + 13}, + {WINED3DFMT_NVDB, WINED3D_FORMAT_FOURCC_BASE + 14}, + {WINED3DFMT_NVHU, WINED3D_FORMAT_FOURCC_BASE + 15}, + {WINED3DFMT_NVHS, WINED3D_FORMAT_FOURCC_BASE + 16}, + {WINED3DFMT_INTZ, WINED3D_FORMAT_FOURCC_BASE + 17}, + {WINED3DFMT_RESZ, WINED3D_FORMAT_FOURCC_BASE + 18}, + {WINED3DFMT_NULL, WINED3D_FORMAT_FOURCC_BASE + 19}, + {WINED3DFMT_R16, WINED3D_FORMAT_FOURCC_BASE + 20}, + {WINED3DFMT_AL16, WINED3D_FORMAT_FOURCC_BASE + 21}, + {WINED3DFMT_NV12, WINED3D_FORMAT_FOURCC_BASE + 22}, + {WINED3DFMT_ATOC, WINED3D_FORMAT_FOURCC_BASE + 23}, +}; + +#define WINED3D_FORMAT_COUNT (WINED3D_FORMAT_FOURCC_BASE + ARRAY_SIZE(format_index_remap)) + +struct wined3d_format_channels +{ + enum wined3d_format_id id; + DWORD red_size, green_size, blue_size, alpha_size; + DWORD red_offset, green_offset, blue_offset, alpha_offset; + UINT bpp; + BYTE depth_size, stencil_size; +}; + +static const struct wined3d_format_channels formats[] = +{ + /* size offset + * format id r g b a r g b a bpp depth stencil */ + {WINED3DFMT_UNKNOWN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* FourCC formats */ + {WINED3DFMT_UYVY, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0}, + {WINED3DFMT_YUY2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0}, + {WINED3DFMT_YV12, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, + {WINED3DFMT_NV12, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, + {WINED3DFMT_DXT1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, + {WINED3DFMT_DXT2, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, + {WINED3DFMT_DXT3, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, + {WINED3DFMT_DXT4, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, + {WINED3DFMT_DXT5, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, + {WINED3DFMT_MULTI2_ARGB8, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, + {WINED3DFMT_G8R8_G8B8, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, + {WINED3DFMT_R8G8_B8G8, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, + /* Hmm? */ + {WINED3DFMT_R8G8_SNORM_Cx, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0}, + {WINED3DFMT_R11G11B10_FLOAT, 11, 11, 10, 0, 0, 11, 22, 0, 4, 0, 0}, + /* Palettized formats */ + {WINED3DFMT_P8_UINT_A8_UNORM, 0, 0, 0, 8, 0, 0, 0, 8, 2, 0, 0}, + {WINED3DFMT_P8_UINT, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, + /* Standard ARGB formats. */ + {WINED3DFMT_B8G8R8_UNORM, 8, 8, 8, 0, 16, 8, 0, 0, 3, 0, 0}, + {WINED3DFMT_B5G6R5_UNORM, 5, 6, 5, 0, 11, 5, 0, 0, 2, 0, 0}, + {WINED3DFMT_B5G5R5X1_UNORM, 5, 5, 5, 0, 10, 5, 0, 0, 2, 0, 0}, + {WINED3DFMT_B5G5R5A1_UNORM, 5, 5, 5, 1, 10, 5, 0, 15, 2, 0, 0}, + {WINED3DFMT_B4G4R4A4_UNORM, 4, 4, 4, 4, 8, 4, 0, 12, 2, 0, 0}, + {WINED3DFMT_B2G3R3_UNORM, 3, 3, 2, 0, 5, 2, 0, 0, 1, 0, 0}, + {WINED3DFMT_A8_UNORM, 0, 0, 0, 8, 0, 0, 0, 0, 1, 0, 0}, + {WINED3DFMT_B2G3R3A8_UNORM, 3, 3, 2, 8, 5, 2, 0, 8, 2, 0, 0}, + {WINED3DFMT_B4G4R4X4_UNORM, 4, 4, 4, 0, 8, 4, 0, 0, 2, 0, 0}, + {WINED3DFMT_R8G8B8X8_UNORM, 8, 8, 8, 0, 0, 8, 16, 0, 4, 0, 0}, + {WINED3DFMT_B10G10R10A2_UNORM, 10, 10, 10, 2, 20, 10, 0, 30, 4, 0, 0}, + /* Luminance */ + {WINED3DFMT_L8_UNORM, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, + {WINED3DFMT_L8A8_UNORM, 0, 0, 0, 8, 0, 0, 0, 8, 2, 0, 0}, + {WINED3DFMT_L4A4_UNORM, 0, 0, 0, 4, 0, 0, 0, 4, 1, 0, 0}, + {WINED3DFMT_L16_UNORM, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0}, + /* Bump mapping stuff */ + {WINED3DFMT_R5G5_SNORM_L6_UNORM, 5, 5, 0, 0, 0, 5, 0, 0, 2, 0, 0}, + {WINED3DFMT_R8G8_SNORM_L8X8_UNORM, 8, 8, 0, 0, 0, 8, 0, 0, 4, 0, 0}, + {WINED3DFMT_R10G11B11_SNORM, 10, 11, 11, 0, 0, 10, 21, 0, 4, 0, 0}, + {WINED3DFMT_R10G10B10_SNORM_A2_UNORM, 10, 10, 10, 2, 0, 10, 20, 30, 4, 0, 0}, + /* Depth stencil formats */ + {WINED3DFMT_D16_LOCKABLE, 0, 0, 0, 0, 0, 0, 0, 0, 2, 16, 0}, + {WINED3DFMT_D32_UNORM, 0, 0, 0, 0, 0, 0, 0, 0, 4, 32, 0}, + {WINED3DFMT_S1_UINT_D15_UNORM, 0, 0, 0, 0, 0, 0, 0, 0, 2, 15, 1}, + {WINED3DFMT_X8D24_UNORM, 0, 0, 0, 0, 0, 0, 0, 0, 4, 24, 0}, + {WINED3DFMT_S4X4_UINT_D24_UNORM, 0, 0, 0, 0, 0, 0, 0, 0, 4, 24, 4}, + {WINED3DFMT_S8_UINT_D24_FLOAT, 0, 0, 0, 0, 0, 0, 0, 0, 4, 24, 8}, + /* Vendor-specific formats */ + {WINED3DFMT_ATI1N, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, + {WINED3DFMT_ATI2N, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, + {WINED3DFMT_NVDB, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {WINED3DFMT_ATOC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {WINED3DFMT_INST, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {WINED3DFMT_INTZ, 0, 0, 0, 0, 0, 0, 0, 0, 4, 24, 8}, + {WINED3DFMT_RESZ, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {WINED3DFMT_NVHU, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0}, + {WINED3DFMT_NVHS, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0}, + {WINED3DFMT_NULL, 8, 8, 8, 8, 0, 8, 16, 24, 4, 0, 0}, + /* Unsure about them, could not find a Windows driver that supports them */ + {WINED3DFMT_R16, 16, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0}, + {WINED3DFMT_AL16, 0, 0, 0, 16, 0, 0, 0, 16, 4, 0, 0}, + /* DirectX 10 HDR formats */ + {WINED3DFMT_R9G9B9E5_SHAREDEXP, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0}, + /* Typeless */ + {WINED3DFMT_R32G32B32A32_TYPELESS, 32, 32, 32, 32, 0, 32, 64, 96, 16, 0, 0}, + {WINED3DFMT_R32G32B32_TYPELESS, 32, 32, 32, 0, 0, 32, 64, 0, 12, 0, 0}, + {WINED3DFMT_R16G16B16A16_TYPELESS, 16, 16, 16, 16, 0, 16, 32, 48, 8, 0, 0}, + {WINED3DFMT_R32G32_TYPELESS, 32, 32, 0, 0, 0, 32, 0, 0, 8, 0, 0}, + {WINED3DFMT_R32G8X24_TYPELESS, 32, 8, 0, 0, 0, 0, 0, 0, 8, 0, 0}, + {WINED3DFMT_R10G10B10A2_TYPELESS, 10, 10, 10, 2, 0, 10, 20, 30, 4, 0, 0}, + {WINED3DFMT_R10G10B10X2_TYPELESS, 10, 10, 10, 0, 0, 10, 20, 0, 4, 0, 0}, + {WINED3DFMT_R8G8B8A8_TYPELESS, 8, 8, 8, 8, 0, 8, 16, 24, 4, 0, 0}, + {WINED3DFMT_R16G16_TYPELESS, 16, 16, 0, 0, 0, 16, 0, 0, 4, 0, 0}, + {WINED3DFMT_R32_TYPELESS, 32, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0}, + {WINED3DFMT_R24G8_TYPELESS, 24, 8, 0, 0, 0, 0, 0, 0, 4, 0, 0}, + {WINED3DFMT_R8G8_TYPELESS, 8, 8, 0, 0, 0, 8, 0, 0, 2, 0, 0}, + {WINED3DFMT_R16_TYPELESS, 16, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0}, + {WINED3DFMT_R8_TYPELESS, 8, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, + {WINED3DFMT_BC1_TYPELESS, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, + {WINED3DFMT_BC2_TYPELESS, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, + {WINED3DFMT_BC3_TYPELESS, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, + {WINED3DFMT_BC4_TYPELESS, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, + {WINED3DFMT_BC5_TYPELESS, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, + {WINED3DFMT_BC6H_TYPELESS, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, + {WINED3DFMT_BC7_TYPELESS, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, + {WINED3DFMT_B8G8R8A8_TYPELESS, 8, 8, 8, 8, 16, 8, 0, 24, 4, 0, 0}, + {WINED3DFMT_B8G8R8X8_TYPELESS, 8, 8, 8, 0, 16, 8, 0, 0, 4, 0, 0}, +}; + +enum wined3d_channel_type +{ + WINED3D_CHANNEL_TYPE_NONE, + WINED3D_CHANNEL_TYPE_UNORM, + WINED3D_CHANNEL_TYPE_SNORM, + WINED3D_CHANNEL_TYPE_UINT, + WINED3D_CHANNEL_TYPE_SINT, + WINED3D_CHANNEL_TYPE_FLOAT, + WINED3D_CHANNEL_TYPE_DEPTH, + WINED3D_CHANNEL_TYPE_STENCIL, + WINED3D_CHANNEL_TYPE_UNUSED, +}; + +struct wined3d_typed_format_info +{ + enum wined3d_format_id id; + enum wined3d_format_id typeless_id; + const char *channels; +}; + +/** + * The last entry for a given typeless format defines its internal format. + * + * u - WINED3D_CHANNEL_TYPE_UNORM + * i - WINED3D_CHANNEL_TYPE_SNORM + * U - WINED3D_CHANNEL_TYPE_UINT + * I - WINED3D_CHANNEL_TYPE_SINT + * F - WINED3D_CHANNEL_TYPE_FLOAT + * D - WINED3D_CHANNEL_TYPE_DEPTH + * S - WINED3D_CHANNEL_TYPE_STENCIL + * X - WINED3D_CHANNEL_TYPE_UNUSED + */ +static const struct wined3d_typed_format_info typed_formats[] = +{ + {WINED3DFMT_R32G32B32A32_UINT, WINED3DFMT_R32G32B32A32_TYPELESS, "UUUU"}, + {WINED3DFMT_R32G32B32A32_SINT, WINED3DFMT_R32G32B32A32_TYPELESS, "IIII"}, + {WINED3DFMT_R32G32B32A32_FLOAT, WINED3DFMT_R32G32B32A32_TYPELESS, "FFFF"}, + {WINED3DFMT_R32G32B32_UINT, WINED3DFMT_R32G32B32_TYPELESS, "UUU"}, + {WINED3DFMT_R32G32B32_SINT, WINED3DFMT_R32G32B32_TYPELESS, "III"}, + {WINED3DFMT_R32G32B32_FLOAT, WINED3DFMT_R32G32B32_TYPELESS, "FFF"}, + {WINED3DFMT_R16G16B16A16_UNORM, WINED3DFMT_R16G16B16A16_TYPELESS, "uuuu"}, + {WINED3DFMT_R16G16B16A16_SNORM, WINED3DFMT_R16G16B16A16_TYPELESS, "iiii"}, + {WINED3DFMT_R16G16B16A16_UINT, WINED3DFMT_R16G16B16A16_TYPELESS, "UUUU"}, + {WINED3DFMT_R16G16B16A16_SINT, WINED3DFMT_R16G16B16A16_TYPELESS, "IIII"}, + {WINED3DFMT_R16G16B16A16_FLOAT, WINED3DFMT_R16G16B16A16_TYPELESS, "FFFF"}, + {WINED3DFMT_R32G32_UINT, WINED3DFMT_R32G32_TYPELESS, "UU"}, + {WINED3DFMT_R32G32_SINT, WINED3DFMT_R32G32_TYPELESS, "II"}, + {WINED3DFMT_R32G32_FLOAT, WINED3DFMT_R32G32_TYPELESS, "FF"}, + {WINED3DFMT_R32_FLOAT_X8X24_TYPELESS, WINED3DFMT_R32G8X24_TYPELESS, "FXX"}, + {WINED3DFMT_X32_TYPELESS_G8X24_UINT, WINED3DFMT_R32G8X24_TYPELESS, "XUX"}, + {WINED3DFMT_D32_FLOAT_S8X24_UINT, WINED3DFMT_R32G8X24_TYPELESS, "DSX"}, + {WINED3DFMT_R10G10B10A2_SNORM, WINED3DFMT_R10G10B10A2_TYPELESS, "iiii"}, + {WINED3DFMT_R10G10B10A2_UINT, WINED3DFMT_R10G10B10A2_TYPELESS, "UUUU"}, + {WINED3DFMT_R10G10B10A2_UNORM, WINED3DFMT_R10G10B10A2_TYPELESS, "uuuu"}, + {WINED3DFMT_R10G10B10X2_SNORM, WINED3DFMT_R10G10B10X2_TYPELESS, "iiiX"}, + {WINED3DFMT_R10G10B10X2_UINT, WINED3DFMT_R10G10B10X2_TYPELESS, "UUUX"}, + {WINED3DFMT_R8G8B8A8_UINT, WINED3DFMT_R8G8B8A8_TYPELESS, "UUUU"}, + {WINED3DFMT_R8G8B8A8_SINT, WINED3DFMT_R8G8B8A8_TYPELESS, "IIII"}, + {WINED3DFMT_R8G8B8A8_SNORM, WINED3DFMT_R8G8B8A8_TYPELESS, "iiii"}, + {WINED3DFMT_R8G8B8A8_UNORM_SRGB, WINED3DFMT_R8G8B8A8_TYPELESS, "uuuu"}, + {WINED3DFMT_R8G8B8A8_UNORM, WINED3DFMT_R8G8B8A8_TYPELESS, "uuuu"}, + {WINED3DFMT_R16G16_UNORM, WINED3DFMT_R16G16_TYPELESS, "uu"}, + {WINED3DFMT_R16G16_SNORM, WINED3DFMT_R16G16_TYPELESS, "ii"}, + {WINED3DFMT_R16G16_UINT, WINED3DFMT_R16G16_TYPELESS, "UU"}, + {WINED3DFMT_R16G16_SINT, WINED3DFMT_R16G16_TYPELESS, "II"}, + {WINED3DFMT_R16G16_FLOAT, WINED3DFMT_R16G16_TYPELESS, "FF"}, + {WINED3DFMT_D32_FLOAT, WINED3DFMT_R32_TYPELESS, "D"}, + {WINED3DFMT_R32_FLOAT, WINED3DFMT_R32_TYPELESS, "F"}, + {WINED3DFMT_R32_UINT, WINED3DFMT_R32_TYPELESS, "U"}, + {WINED3DFMT_R32_SINT, WINED3DFMT_R32_TYPELESS, "I"}, + {WINED3DFMT_R24_UNORM_X8_TYPELESS, WINED3DFMT_R24G8_TYPELESS, "uX"}, + {WINED3DFMT_X24_TYPELESS_G8_UINT, WINED3DFMT_R24G8_TYPELESS, "XU"}, + {WINED3DFMT_D24_UNORM_S8_UINT, WINED3DFMT_R24G8_TYPELESS, "DS"}, + {WINED3DFMT_R8G8_SNORM, WINED3DFMT_R8G8_TYPELESS, "ii"}, + {WINED3DFMT_R8G8_UNORM, WINED3DFMT_R8G8_TYPELESS, "uu"}, + {WINED3DFMT_R8G8_UINT, WINED3DFMT_R8G8_TYPELESS, "UU"}, + {WINED3DFMT_R8G8_SINT, WINED3DFMT_R8G8_TYPELESS, "II"}, + {WINED3DFMT_D16_UNORM, WINED3DFMT_R16_TYPELESS, "D"}, + {WINED3DFMT_R16_UNORM, WINED3DFMT_R16_TYPELESS, "u"}, + {WINED3DFMT_R16_SNORM, WINED3DFMT_R16_TYPELESS, "i"}, + {WINED3DFMT_R16_UINT, WINED3DFMT_R16_TYPELESS, "U"}, + {WINED3DFMT_R16_SINT, WINED3DFMT_R16_TYPELESS, "I"}, + {WINED3DFMT_R16_FLOAT, WINED3DFMT_R16_TYPELESS, "F"}, + {WINED3DFMT_R8_UNORM, WINED3DFMT_R8_TYPELESS, "u"}, + {WINED3DFMT_R8_SNORM, WINED3DFMT_R8_TYPELESS, "i"}, + {WINED3DFMT_R8_UINT, WINED3DFMT_R8_TYPELESS, "U"}, + {WINED3DFMT_R8_SINT, WINED3DFMT_R8_TYPELESS, "I"}, + {WINED3DFMT_BC1_UNORM_SRGB, WINED3DFMT_BC1_TYPELESS, ""}, + {WINED3DFMT_BC1_UNORM, WINED3DFMT_BC1_TYPELESS, ""}, + {WINED3DFMT_BC2_UNORM_SRGB, WINED3DFMT_BC2_TYPELESS, ""}, + {WINED3DFMT_BC2_UNORM, WINED3DFMT_BC2_TYPELESS, ""}, + {WINED3DFMT_BC3_UNORM_SRGB, WINED3DFMT_BC3_TYPELESS, ""}, + {WINED3DFMT_BC3_UNORM, WINED3DFMT_BC3_TYPELESS, ""}, + {WINED3DFMT_BC4_UNORM, WINED3DFMT_BC4_TYPELESS, ""}, + {WINED3DFMT_BC4_SNORM, WINED3DFMT_BC4_TYPELESS, ""}, + {WINED3DFMT_BC5_UNORM, WINED3DFMT_BC5_TYPELESS, ""}, + {WINED3DFMT_BC5_SNORM, WINED3DFMT_BC5_TYPELESS, ""}, + {WINED3DFMT_BC6H_UF16, WINED3DFMT_BC6H_TYPELESS, ""}, + {WINED3DFMT_BC6H_SF16, WINED3DFMT_BC6H_TYPELESS, ""}, + {WINED3DFMT_BC7_UNORM_SRGB, WINED3DFMT_BC7_TYPELESS, ""}, + {WINED3DFMT_BC7_UNORM, WINED3DFMT_BC7_TYPELESS, ""}, + {WINED3DFMT_B8G8R8A8_UNORM_SRGB, WINED3DFMT_B8G8R8A8_TYPELESS, "uuuu"}, + {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8A8_TYPELESS, "uuuu"}, + {WINED3DFMT_B8G8R8X8_UNORM_SRGB, WINED3DFMT_B8G8R8X8_TYPELESS, "uuuX"}, + {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8X8_TYPELESS, "uuuX"}, +}; + +struct wined3d_typeless_format_depth_stencil_info +{ + enum wined3d_format_id typeless_id; + enum wined3d_format_id depth_stencil_id; + enum wined3d_format_id depth_view_id; + enum wined3d_format_id stencil_view_id; + BOOL separate_depth_view_format; +}; + +static const struct wined3d_typeless_format_depth_stencil_info typeless_depth_stencil_formats[] = +{ + {WINED3DFMT_R32G8X24_TYPELESS, WINED3DFMT_D32_FLOAT_S8X24_UINT, + WINED3DFMT_R32_FLOAT_X8X24_TYPELESS, WINED3DFMT_X32_TYPELESS_G8X24_UINT, TRUE}, + {WINED3DFMT_R24G8_TYPELESS, WINED3DFMT_D24_UNORM_S8_UINT, + WINED3DFMT_R24_UNORM_X8_TYPELESS, WINED3DFMT_X24_TYPELESS_G8_UINT, TRUE}, + {WINED3DFMT_R32_TYPELESS, WINED3DFMT_D32_FLOAT, WINED3DFMT_R32_FLOAT}, + {WINED3DFMT_R16_TYPELESS, WINED3DFMT_D16_UNORM, WINED3DFMT_R16_UNORM}, +}; + +struct wined3d_format_ddi_info +{ + enum wined3d_format_id id; + D3DDDIFORMAT ddi_format; +}; + +static const struct wined3d_format_ddi_info ddi_formats[] = +{ + {WINED3DFMT_B8G8R8_UNORM, D3DDDIFMT_R8G8B8}, + {WINED3DFMT_B8G8R8A8_UNORM, D3DDDIFMT_A8R8G8B8}, + {WINED3DFMT_B8G8R8X8_UNORM, D3DDDIFMT_X8R8G8B8}, + {WINED3DFMT_B5G6R5_UNORM, D3DDDIFMT_R5G6B5}, + {WINED3DFMT_B5G5R5X1_UNORM, D3DDDIFMT_X1R5G5B5}, + {WINED3DFMT_B5G5R5A1_UNORM, D3DDDIFMT_A1R5G5B5}, + {WINED3DFMT_B4G4R4A4_UNORM, D3DDDIFMT_A4R4G4B4}, + {WINED3DFMT_B4G4R4X4_UNORM, D3DDDIFMT_X4R4G4B4}, + {WINED3DFMT_P8_UINT, D3DDDIFMT_P8}, +}; + +struct wined3d_format_base_flags +{ + enum wined3d_format_id id; + DWORD flags; +}; + +/* The ATI2N format behaves like an uncompressed format in LockRect(), but + * still needs to use the correct block based calculation for e.g. the + * resource size. */ +static const struct wined3d_format_base_flags format_base_flags[] = +{ + {WINED3DFMT_ATI1N, WINED3DFMT_FLAG_MAPPABLE | WINED3DFMT_FLAG_BROKEN_PITCH}, + {WINED3DFMT_ATI2N, WINED3DFMT_FLAG_MAPPABLE | WINED3DFMT_FLAG_BROKEN_PITCH}, + {WINED3DFMT_D16_LOCKABLE, WINED3DFMT_FLAG_MAPPABLE}, + {WINED3DFMT_INTZ, WINED3DFMT_FLAG_MAPPABLE}, + {WINED3DFMT_R11G11B10_FLOAT, WINED3DFMT_FLAG_FLOAT}, + {WINED3DFMT_D32_FLOAT, WINED3DFMT_FLAG_FLOAT}, + {WINED3DFMT_S8_UINT_D24_FLOAT, WINED3DFMT_FLAG_FLOAT}, + {WINED3DFMT_D32_FLOAT_S8X24_UINT, WINED3DFMT_FLAG_FLOAT}, + {WINED3DFMT_INST, WINED3DFMT_FLAG_EXTENSION}, + {WINED3DFMT_NULL, WINED3DFMT_FLAG_EXTENSION}, + {WINED3DFMT_NVDB, WINED3DFMT_FLAG_EXTENSION}, + {WINED3DFMT_ATOC, WINED3DFMT_FLAG_EXTENSION}, + {WINED3DFMT_RESZ, WINED3DFMT_FLAG_EXTENSION}, + {WINED3DFMT_R32G32B32A32_TYPELESS, WINED3DFMT_FLAG_CAST_TO_BLOCK}, + {WINED3DFMT_R32G32B32A32_FLOAT, WINED3DFMT_FLAG_CAST_TO_BLOCK}, + {WINED3DFMT_R32G32B32A32_UINT, WINED3DFMT_FLAG_CAST_TO_BLOCK}, + {WINED3DFMT_R32G32B32A32_SINT, WINED3DFMT_FLAG_CAST_TO_BLOCK}, + {WINED3DFMT_R16G16B16A16_TYPELESS, WINED3DFMT_FLAG_CAST_TO_BLOCK}, + {WINED3DFMT_R16G16B16A16_FLOAT, WINED3DFMT_FLAG_CAST_TO_BLOCK}, + {WINED3DFMT_R16G16B16A16_UNORM, WINED3DFMT_FLAG_CAST_TO_BLOCK}, + {WINED3DFMT_R16G16B16A16_UINT, WINED3DFMT_FLAG_CAST_TO_BLOCK}, + {WINED3DFMT_R16G16B16A16_SNORM, WINED3DFMT_FLAG_CAST_TO_BLOCK}, + {WINED3DFMT_R16G16B16A16_SINT, WINED3DFMT_FLAG_CAST_TO_BLOCK}, + {WINED3DFMT_R32G32_TYPELESS, WINED3DFMT_FLAG_CAST_TO_BLOCK}, + {WINED3DFMT_R32G32_FLOAT, WINED3DFMT_FLAG_CAST_TO_BLOCK}, + {WINED3DFMT_R32G32_UINT, WINED3DFMT_FLAG_CAST_TO_BLOCK}, + {WINED3DFMT_R32G32_SINT, WINED3DFMT_FLAG_CAST_TO_BLOCK}, + {WINED3DFMT_R32_TYPELESS, WINED3DFMT_FLAG_CAST_TO_BLOCK}, + {WINED3DFMT_R32_FLOAT, WINED3DFMT_FLAG_CAST_TO_BLOCK}, + {WINED3DFMT_R32_UINT, WINED3DFMT_FLAG_CAST_TO_BLOCK}, + {WINED3DFMT_R32_SINT, WINED3DFMT_FLAG_CAST_TO_BLOCK}, +}; + +static void rgb888_from_rgb565(WORD rgb565, BYTE *r, BYTE *g, BYTE *b) +{ + BYTE c; + + /* (2⁸ - 1) / (2⁵ - 1) ≈ 2⁸ / 2⁵ + 2⁸ / 2¹⁰ + * (2⁸ - 1) / (2⁶ - 1) ≈ 2⁸ / 2⁶ + 2⁸ / 2¹² */ + c = rgb565 >> 11; + *r = (c << 3) + (c >> 2); + c = (rgb565 >> 5) & 0x3f; + *g = (c << 2) + (c >> 4); + c = rgb565 & 0x1f; + *b = (c << 3) + (c >> 2); +} + +static void build_dxtn_colour_table(WORD colour0, WORD colour1, + DWORD colour_table[4], enum wined3d_format_id format_id) +{ + unsigned int i; + struct + { + BYTE r, g, b; + } c[4]; + + rgb888_from_rgb565(colour0, &c[0].r, &c[0].g, &c[0].b); + rgb888_from_rgb565(colour1, &c[1].r, &c[1].g, &c[1].b); + + if (format_id == WINED3DFMT_BC1_UNORM && colour0 <= colour1) + { + c[2].r = (c[0].r + c[1].r) / 2; + c[2].g = (c[0].g + c[1].g) / 2; + c[2].b = (c[0].b + c[1].b) / 2; + + c[3].r = 0; + c[3].g = 0; + c[3].b = 0; + } + else + { + for (i = 0; i < 2; ++i) + { + c[i + 2].r = (2 * c[i].r + c[1 - i].r) / 3; + c[i + 2].g = (2 * c[i].g + c[1 - i].g) / 3; + c[i + 2].b = (2 * c[i].b + c[1 - i].b) / 3; + } + } + + for (i = 0; i < 4; ++i) + { + colour_table[i] = (c[i].r << 16) | (c[i].g << 8) | c[i].b; + } +} + +static void build_bc3_alpha_table(BYTE alpha0, BYTE alpha1, BYTE alpha_table[8]) +{ + unsigned int i; + + alpha_table[0] = alpha0; + alpha_table[1] = alpha1; + + if (alpha0 > alpha1) + { + for (i = 0; i < 6; ++i) + { + alpha_table[2 + i] = ((6 - i) * alpha0 + (i + 1) * alpha1) / 7; + } + return; + } + else + { + for (i = 0; i < 4; ++i) + { + alpha_table[2 + i] = ((4 - i) * alpha0 + (i + 1) * alpha1) / 5; + } + alpha_table[6] = 0x00; + alpha_table[7] = 0xff; + } +} + +static void decompress_dxtn_block(const BYTE *src, BYTE *dst, unsigned int width, + unsigned int height, unsigned int dst_row_pitch, enum wined3d_format_id format_id) +{ + const UINT64 *s = (const UINT64 *)src; + BOOL bc1_alpha = FALSE; + DWORD colour_table[4]; + BYTE alpha_table[8]; + UINT64 alpha_bits; + DWORD colour_bits; + unsigned int x, y; + BYTE colour_idx; + DWORD *dst_row; + BYTE alpha; + + if (format_id == WINED3DFMT_BC1_UNORM) + { + WORD colour0, colour1; + + alpha_bits = 0; + + colour0 = s[0] & 0xffff; + colour1 = (s[0] >> 16) & 0xffff; + colour_bits = (s[0] >> 32) & 0xffffffff; + build_dxtn_colour_table(colour0, colour1, colour_table, format_id); + if (colour0 <= colour1) + bc1_alpha = TRUE; + } + else + { + alpha_bits = s[0]; + if (format_id == WINED3DFMT_BC3_UNORM) + { + build_bc3_alpha_table(alpha_bits & 0xff, (alpha_bits >> 8) & 0xff, alpha_table); + alpha_bits >>= 16; + } + + colour_bits = (s[1] >> 32) & 0xffffffff; + build_dxtn_colour_table(s[1] & 0xffff, (s[1] >> 16) & 0xffff, colour_table, format_id); + } + + for (y = 0; y < height; ++y) + { + dst_row = (DWORD *)&dst[y * dst_row_pitch]; + for (x = 0; x < width; ++x) + { + colour_idx = (colour_bits >> (y * 8 + x * 2)) & 0x3; + switch (format_id) + { + case WINED3DFMT_BC1_UNORM: + alpha = bc1_alpha && colour_idx == 3 ? 0x00 : 0xff; + break; + + case WINED3DFMT_BC2_UNORM: + alpha = (alpha_bits >> (y * 16 + x * 4)) & 0xf; + /* (2⁸ - 1) / (2⁴ - 1) ≈ 2⁸ / 2⁴ + 2⁸ / 2⁸ */ + alpha |= alpha << 4; + break; + + case WINED3DFMT_BC3_UNORM: + alpha = alpha_table[(alpha_bits >> (y * 12 + x * 3)) & 0x7]; + break; + + default: + alpha = 0xff; + break; + } + dst_row[x] = (alpha << 24) | colour_table[colour_idx]; + } + } +} + +static void decompress_dxtn(const BYTE *src, BYTE *dst, unsigned int src_row_pitch, + unsigned int src_slice_pitch, unsigned int dst_row_pitch, unsigned int dst_slice_pitch, + unsigned int width, unsigned int height, unsigned int depth, enum wined3d_format_id format_id) +{ + unsigned int block_byte_count, block_w, block_h; + const BYTE *src_row, *src_slice = src; + BYTE *dst_row, *dst_slice = dst; + unsigned int x, y, z; + + block_byte_count = format_id == WINED3DFMT_BC1_UNORM ? 8 : 16; + + for (z = 0; z < depth; ++z) + { + src_row = src_slice; + dst_row = dst_slice; + for (y = 0; y < height; y += 4) + { + for (x = 0; x < width; x += 4) + { + block_w = min(width - x, 4); + block_h = min(height - y, 4); + decompress_dxtn_block(&src_row[x * (block_byte_count / 4)], + &dst_row[x * 4], block_w, block_h, dst_row_pitch, format_id); + } + src_row += src_row_pitch; + dst_row += dst_row_pitch * 4; + } + src_slice += src_slice_pitch; + dst_slice += dst_slice_pitch; + } +} + +static void decompress_bc3(const BYTE *src, BYTE *dst, unsigned int src_row_pitch, + unsigned int src_slice_pitch, unsigned int dst_row_pitch, unsigned int dst_slice_pitch, + unsigned int width, unsigned int height, unsigned int depth) +{ + decompress_dxtn(src, dst, src_row_pitch, src_slice_pitch, dst_row_pitch, + dst_slice_pitch, width, height, depth, WINED3DFMT_BC3_UNORM); +} + +static void decompress_bc2(const BYTE *src, BYTE *dst, unsigned int src_row_pitch, + unsigned int src_slice_pitch, unsigned int dst_row_pitch, unsigned int dst_slice_pitch, + unsigned int width, unsigned int height, unsigned int depth) +{ + decompress_dxtn(src, dst, src_row_pitch, src_slice_pitch, dst_row_pitch, + dst_slice_pitch, width, height, depth, WINED3DFMT_BC2_UNORM); +} + +static void decompress_bc1(const BYTE *src, BYTE *dst, unsigned int src_row_pitch, + unsigned int src_slice_pitch, unsigned int dst_row_pitch, unsigned int dst_slice_pitch, + unsigned int width, unsigned int height, unsigned int depth) +{ + decompress_dxtn(src, dst, src_row_pitch, src_slice_pitch, dst_row_pitch, + dst_slice_pitch, width, height, depth, WINED3DFMT_BC1_UNORM); +} + +static const struct wined3d_format_decompress_info +{ + enum wined3d_format_id id; + void (*decompress)(const BYTE *src, BYTE *dst, unsigned int src_row_pitch, unsigned int src_slice_pitch, + unsigned int dst_row_pitch, unsigned int dst_slice_pitch, + unsigned int width, unsigned int height, unsigned int depth); +} +format_decompress_info[] = +{ + {WINED3DFMT_DXT1, decompress_bc1}, + {WINED3DFMT_DXT2, decompress_bc2}, + {WINED3DFMT_DXT3, decompress_bc2}, + {WINED3DFMT_DXT4, decompress_bc3}, + {WINED3DFMT_DXT5, decompress_bc3}, + {WINED3DFMT_BC1_UNORM, decompress_bc1}, + {WINED3DFMT_BC2_UNORM, decompress_bc2}, + {WINED3DFMT_BC3_UNORM, decompress_bc3}, +}; + +struct wined3d_format_block_info +{ + enum wined3d_format_id id; + UINT block_width; + UINT block_height; + UINT block_byte_count; + unsigned int flags; +}; + +static const struct wined3d_format_block_info format_block_info[] = +{ + {WINED3DFMT_DXT1, 4, 4, 8, WINED3DFMT_FLAG_COMPRESSED}, + {WINED3DFMT_DXT2, 4, 4, 16, WINED3DFMT_FLAG_COMPRESSED}, + {WINED3DFMT_DXT3, 4, 4, 16, WINED3DFMT_FLAG_COMPRESSED}, + {WINED3DFMT_DXT4, 4, 4, 16, WINED3DFMT_FLAG_COMPRESSED}, + {WINED3DFMT_DXT5, 4, 4, 16, WINED3DFMT_FLAG_COMPRESSED}, + {WINED3DFMT_BC1_UNORM, 4, 4, 8, WINED3DFMT_FLAG_COMPRESSED}, + {WINED3DFMT_BC2_UNORM, 4, 4, 16, WINED3DFMT_FLAG_COMPRESSED}, + {WINED3DFMT_BC3_UNORM, 4, 4, 16, WINED3DFMT_FLAG_COMPRESSED}, + {WINED3DFMT_BC4_UNORM, 4, 4, 8, WINED3DFMT_FLAG_COMPRESSED}, + {WINED3DFMT_BC4_SNORM, 4, 4, 8, WINED3DFMT_FLAG_COMPRESSED}, + {WINED3DFMT_BC5_UNORM, 4, 4, 16, WINED3DFMT_FLAG_COMPRESSED}, + {WINED3DFMT_BC5_SNORM, 4, 4, 16, WINED3DFMT_FLAG_COMPRESSED}, + {WINED3DFMT_BC6H_UF16, 4, 4, 16, WINED3DFMT_FLAG_COMPRESSED}, + {WINED3DFMT_BC6H_SF16, 4, 4, 16, WINED3DFMT_FLAG_COMPRESSED}, + {WINED3DFMT_BC7_UNORM, 4, 4, 16, WINED3DFMT_FLAG_COMPRESSED}, + {WINED3DFMT_ATI1N, 4, 4, 8, WINED3DFMT_FLAG_COMPRESSED | WINED3DFMT_FLAG_BLOCKS_NO_VERIFY}, + {WINED3DFMT_ATI2N, 4, 4, 16, WINED3DFMT_FLAG_COMPRESSED | WINED3DFMT_FLAG_BLOCKS_NO_VERIFY}, + {WINED3DFMT_YUY2, 2, 1, 4, WINED3DFMT_FLAG_BLOCKS_NO_VERIFY}, + {WINED3DFMT_UYVY, 2, 1, 4, WINED3DFMT_FLAG_BLOCKS_NO_VERIFY}, + {WINED3DFMT_R9G9B9E5_SHAREDEXP, 1, 1, 4}, +}; + +struct wined3d_format_vertex_info +{ + enum wined3d_format_id id; + enum wined3d_ffp_emit_idx emit_idx; + GLenum gl_vtx_type; + enum wined3d_gl_extension extension; +}; + +static const struct wined3d_format_vertex_info format_vertex_info[] = +{ + {WINED3DFMT_R32_FLOAT, WINED3D_FFP_EMIT_FLOAT1, GL_FLOAT}, + {WINED3DFMT_R32G32_FLOAT, WINED3D_FFP_EMIT_FLOAT2, GL_FLOAT}, + {WINED3DFMT_R32G32B32_FLOAT, WINED3D_FFP_EMIT_FLOAT3, GL_FLOAT}, + {WINED3DFMT_R32G32B32A32_FLOAT, WINED3D_FFP_EMIT_FLOAT4, GL_FLOAT}, + {WINED3DFMT_B8G8R8A8_UNORM, WINED3D_FFP_EMIT_D3DCOLOR, GL_UNSIGNED_BYTE}, + {WINED3DFMT_R8G8B8A8_UINT, WINED3D_FFP_EMIT_UBYTE4, GL_UNSIGNED_BYTE}, + {WINED3DFMT_R16G16_UINT, WINED3D_FFP_EMIT_INVALID, GL_UNSIGNED_SHORT}, + {WINED3DFMT_R16G16_SINT, WINED3D_FFP_EMIT_SHORT2, GL_SHORT}, + {WINED3DFMT_R16G16B16A16_SINT, WINED3D_FFP_EMIT_SHORT4, GL_SHORT}, + {WINED3DFMT_R8G8B8A8_UNORM, WINED3D_FFP_EMIT_UBYTE4N, GL_UNSIGNED_BYTE}, + {WINED3DFMT_R16G16_SNORM, WINED3D_FFP_EMIT_SHORT2N, GL_SHORT}, + {WINED3DFMT_R16G16B16A16_SNORM, WINED3D_FFP_EMIT_SHORT4N, GL_SHORT}, + {WINED3DFMT_R16G16_UNORM, WINED3D_FFP_EMIT_USHORT2N, GL_UNSIGNED_SHORT}, + {WINED3DFMT_R16G16B16A16_UNORM, WINED3D_FFP_EMIT_USHORT4N, GL_UNSIGNED_SHORT}, + {WINED3DFMT_R10G10B10X2_UINT, WINED3D_FFP_EMIT_UDEC3, GL_UNSIGNED_SHORT}, + {WINED3DFMT_R10G10B10X2_SNORM, WINED3D_FFP_EMIT_DEC3N, GL_SHORT}, + {WINED3DFMT_R10G10B10A2_UNORM, WINED3D_FFP_EMIT_INVALID, GL_UNSIGNED_INT_2_10_10_10_REV, + ARB_VERTEX_TYPE_2_10_10_10_REV}, + /* Without ARB_half_float_vertex we convert these on upload. */ + {WINED3DFMT_R16G16_FLOAT, WINED3D_FFP_EMIT_FLOAT16_2, GL_FLOAT}, + {WINED3DFMT_R16G16_FLOAT, WINED3D_FFP_EMIT_FLOAT16_2, GL_HALF_FLOAT, ARB_HALF_FLOAT_VERTEX}, + {WINED3DFMT_R16G16B16A16_FLOAT, WINED3D_FFP_EMIT_FLOAT16_4, GL_FLOAT}, + {WINED3DFMT_R16G16B16A16_FLOAT, WINED3D_FFP_EMIT_FLOAT16_4, GL_HALF_FLOAT, ARB_HALF_FLOAT_VERTEX}, + {WINED3DFMT_R8G8B8A8_SNORM, WINED3D_FFP_EMIT_INVALID, GL_BYTE}, + {WINED3DFMT_R8G8B8A8_SINT, WINED3D_FFP_EMIT_INVALID, GL_BYTE}, + {WINED3DFMT_R8G8_UNORM, WINED3D_FFP_EMIT_INVALID, GL_UNSIGNED_BYTE}, + {WINED3DFMT_R16G16B16A16_UINT, WINED3D_FFP_EMIT_INVALID, GL_UNSIGNED_SHORT}, + {WINED3DFMT_R8_UNORM, WINED3D_FFP_EMIT_INVALID, GL_UNSIGNED_BYTE}, + {WINED3DFMT_R8_UINT, WINED3D_FFP_EMIT_INVALID, GL_UNSIGNED_BYTE}, + {WINED3DFMT_R8_SINT, WINED3D_FFP_EMIT_INVALID, GL_BYTE}, + {WINED3DFMT_R16_UINT, WINED3D_FFP_EMIT_INVALID, GL_UNSIGNED_SHORT}, + {WINED3DFMT_R16_SINT, WINED3D_FFP_EMIT_INVALID, GL_SHORT}, + {WINED3DFMT_R32_UINT, WINED3D_FFP_EMIT_INVALID, GL_UNSIGNED_INT}, + {WINED3DFMT_R32_SINT, WINED3D_FFP_EMIT_INVALID, GL_INT}, + {WINED3DFMT_R32G32_UINT, WINED3D_FFP_EMIT_INVALID, GL_UNSIGNED_INT}, + {WINED3DFMT_R32G32_SINT, WINED3D_FFP_EMIT_INVALID, GL_INT}, + {WINED3DFMT_R32G32B32_UINT, WINED3D_FFP_EMIT_INVALID, GL_UNSIGNED_INT}, + {WINED3DFMT_R32G32B32A32_UINT, WINED3D_FFP_EMIT_INVALID, GL_UNSIGNED_INT}, + {WINED3DFMT_R32G32B32A32_SINT, WINED3D_FFP_EMIT_INVALID, GL_INT}, +}; + +struct wined3d_format_texture_info +{ + enum wined3d_format_id id; + GLint gl_internal; + GLint gl_srgb_internal; + GLint gl_rt_internal; + GLint gl_format; + GLint gl_type; + unsigned int conv_byte_count; + unsigned int flags; + enum wined3d_gl_extension extension; + void (*upload)(const BYTE *src, BYTE *dst, unsigned int src_row_pitch, unsigned int src_slice_pitch, + unsigned int dst_row_pitch, unsigned int dst_slice_pitch, + unsigned int width, unsigned int height, unsigned int depth); + void (*download)(const BYTE *src, BYTE *dst, unsigned int src_row_pitch, unsigned int src_slice_pitch, + unsigned int dst_row_pitch, unsigned int dst_slice_pitch, + unsigned int width, unsigned int height, unsigned int depth); + void (*decompress)(const BYTE *src, BYTE *dst, unsigned int src_row_pitch, unsigned int src_slice_pitch, + unsigned int dst_row_pitch, unsigned int dst_slice_pitch, + unsigned int width, unsigned int height, unsigned int depth); +}; + +static void convert_l4a4_unorm(const BYTE *src, BYTE *dst, UINT src_row_pitch, UINT src_slice_pitch, + UINT dst_row_pitch, UINT dst_slice_pitch, UINT width, UINT height, UINT depth) +{ + /* WINED3DFMT_L4A4_UNORM exists as an internal gl format, but for some reason there is not + * format+type combination to load it. Thus convert it to A8L8, then load it + * with A4L4 internal, but A8L8 format+type + */ + unsigned int x, y, z; + const unsigned char *Source; + unsigned char *Dest; + + for (z = 0; z < depth; z++) + { + for (y = 0; y < height; y++) + { + Source = src + z * src_slice_pitch + y * src_row_pitch; + Dest = dst + z * dst_slice_pitch + y * dst_row_pitch; + for (x = 0; x < width; x++ ) + { + unsigned char color = (*Source++); + /* A */ Dest[1] = (color & 0xf0u) << 0; + /* L */ Dest[0] = (color & 0x0fu) << 4; + Dest += 2; + } + } + } +} + +static void convert_r5g5_snorm_l6_unorm(const BYTE *src, BYTE *dst, UINT src_row_pitch, UINT src_slice_pitch, + UINT dst_row_pitch, UINT dst_slice_pitch, UINT width, UINT height, UINT depth) +{ + unsigned int x, y, z; + unsigned char r_in, g_in, l_in; + const unsigned short *texel_in; + unsigned short *texel_out; + + /* Emulating signed 5 bit values with unsigned 5 bit values has some precision problems by design: + * E.g. the signed input value 0 becomes 16. GL normalizes it to 16 / 31 = 0.516. We convert it + * back to a signed value by subtracting 0.5 and multiplying by 2.0. The resulting value is + * ((16 / 31) - 0.5) * 2.0 = 0.032, which is quite different from the intended result 0.000. */ + for (z = 0; z < depth; z++) + { + for (y = 0; y < height; y++) + { + texel_out = (unsigned short *) (dst + z * dst_slice_pitch + y * dst_row_pitch); + texel_in = (const unsigned short *)(src + z * src_slice_pitch + y * src_row_pitch); + for (x = 0; x < width; x++ ) + { + l_in = (*texel_in & 0xfc00u) >> 10; + g_in = (*texel_in & 0x03e0u) >> 5; + r_in = *texel_in & 0x001fu; + + *texel_out = ((r_in + 16) << 11) | (l_in << 5) | (g_in + 16); + texel_out++; + texel_in++; + } + } + } +} + +static void convert_r5g5_snorm_l6_unorm_ext(const BYTE *src, BYTE *dst, UINT src_row_pitch, UINT src_slice_pitch, + UINT dst_row_pitch, UINT dst_slice_pitch, UINT width, UINT height, UINT depth) +{ + unsigned int x, y, z; + unsigned char *texel_out, r_out, g_out, r_in, g_in, l_in; + const unsigned short *texel_in; + + for (z = 0; z < depth; z++) + { + for (y = 0; y < height; y++) + { + texel_in = (const unsigned short *)(src + z * src_slice_pitch + y * src_row_pitch); + texel_out = dst + z * dst_slice_pitch + y * dst_row_pitch; + for (x = 0; x < width; x++ ) + { + l_in = (*texel_in & 0xfc00u) >> 10; + g_in = (*texel_in & 0x03e0u) >> 5; + r_in = *texel_in & 0x001fu; + + r_out = r_in << 3; + if (!(r_in & 0x10)) /* r > 0 */ + r_out |= r_in >> 1; + + g_out = g_in << 3; + if (!(g_in & 0x10)) /* g > 0 */ + g_out |= g_in >> 1; + + texel_out[0] = r_out; + texel_out[1] = g_out; + texel_out[2] = l_in << 1 | l_in >> 5; + texel_out[3] = 0; + + texel_out += 4; + texel_in++; + } + } + } +} + +static void convert_r5g5_snorm_l6_unorm_nv(const BYTE *src, BYTE *dst, UINT src_row_pitch, UINT src_slice_pitch, + UINT dst_row_pitch, UINT dst_slice_pitch, UINT width, UINT height, UINT depth) +{ + unsigned int x, y, z; + unsigned char *texel_out, ds_out, dt_out, r_in, g_in, l_in; + const unsigned short *texel_in; + + /* This makes the gl surface bigger(24 bit instead of 16), but it works with + * fixed function and shaders without further conversion once the surface is + * loaded. + * + * The difference between this function and convert_r5g5_snorm_l6_unorm_ext + * is that convert_r5g5_snorm_l6_unorm_ext creates a 32 bit XRGB texture and + * this function creates a 24 bit DSDT_MAG texture. Trying to load a DSDT_MAG + * internal with a 32 bit DSDT_MAG_INTENSITY or DSDT_MAG_VIB format fails. */ + for (z = 0; z < depth; z++) + { + for (y = 0; y < height; y++) + { + texel_in = (const unsigned short *)(src + z * src_slice_pitch + y * src_row_pitch); + texel_out = dst + z * dst_slice_pitch + y * dst_row_pitch; + for (x = 0; x < width; x++ ) + { + l_in = (*texel_in & 0xfc00u) >> 10; + g_in = (*texel_in & 0x03e0u) >> 5; + r_in = *texel_in & 0x001fu; + + ds_out = r_in << 3; + if (!(r_in & 0x10)) /* r > 0 */ + ds_out |= r_in >> 1; + + dt_out = g_in << 3; + if (!(g_in & 0x10)) /* g > 0 */ + dt_out |= g_in >> 1; + + texel_out[0] = ds_out; + texel_out[1] = dt_out; + texel_out[2] = l_in << 1 | l_in >> 5; + + texel_out += 3; + texel_in++; + } + } + } +} + +static void convert_r8g8_snorm(const BYTE *src, BYTE *dst, UINT src_row_pitch, UINT src_slice_pitch, + UINT dst_row_pitch, UINT dst_slice_pitch, UINT width, UINT height, UINT depth) +{ + unsigned int x, y, z; + const short *Source; + unsigned char *Dest; + + for (z = 0; z < depth; z++) + { + for (y = 0; y < height; y++) + { + Source = (const short *)(src + z * src_slice_pitch + y * src_row_pitch); + Dest = dst + z * dst_slice_pitch + y * dst_row_pitch; + for (x = 0; x < width; x++ ) + { + const short color = (*Source++); + /* B */ Dest[0] = 0xff; + /* G */ Dest[1] = (color >> 8) + 128; /* V */ + /* R */ Dest[2] = (color & 0xff) + 128; /* U */ + Dest += 3; + } + } + } +} + +static void convert_r8g8_snorm_l8x8_unorm(const BYTE *src, BYTE *dst, UINT src_row_pitch, UINT src_slice_pitch, + UINT dst_row_pitch, UINT dst_slice_pitch, UINT width, UINT height, UINT depth) +{ + unsigned int x, y, z; + const DWORD *Source; + unsigned char *Dest; + + /* Doesn't work correctly with the fixed function pipeline, but can work in + * shaders if the shader is adjusted. (There's no use for this format in gl's + * standard fixed function pipeline anyway). + */ + for (z = 0; z < depth; z++) + { + for (y = 0; y < height; y++) + { + Source = (const DWORD *)(src + z * src_slice_pitch + y * src_row_pitch); + Dest = dst + z * dst_slice_pitch + y * dst_row_pitch; + for (x = 0; x < width; x++ ) + { + LONG color = (*Source++); + /* B */ Dest[0] = ((color >> 16) & 0xff); /* L */ + /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */ + /* R */ Dest[2] = (color & 0xff) + 128; /* U */ + Dest += 4; + } + } + } +} + +static void convert_r8g8_snorm_l8x8_unorm_nv(const BYTE *src, BYTE *dst, UINT src_row_pitch, UINT src_slice_pitch, + UINT dst_row_pitch, UINT dst_slice_pitch, UINT width, UINT height, UINT depth) +{ + unsigned int x, y, z; + const DWORD *Source; + unsigned char *Dest; + + /* This implementation works with the fixed function pipeline and shaders + * without further modification after converting the surface. + */ + for (z = 0; z < depth; z++) + { + for (y = 0; y < height; y++) + { + Source = (const DWORD *)(src + z * src_slice_pitch + y * src_row_pitch); + Dest = dst + z * dst_slice_pitch + y * dst_row_pitch; + for (x = 0; x < width; x++ ) + { + LONG color = (*Source++); + /* L */ Dest[2] = ((color >> 16) & 0xff); /* L */ + /* V */ Dest[1] = ((color >> 8 ) & 0xff); /* V */ + /* U */ Dest[0] = (color & 0xff); /* U */ + /* I */ Dest[3] = 255; /* X */ + Dest += 4; + } + } + } +} + +static void convert_r8g8b8a8_snorm(const BYTE *src, BYTE *dst, UINT src_row_pitch, UINT src_slice_pitch, + UINT dst_row_pitch, UINT dst_slice_pitch, UINT width, UINT height, UINT depth) +{ + unsigned int x, y, z; + const DWORD *Source; + unsigned char *Dest; + + for (z = 0; z < depth; z++) + { + for (y = 0; y < height; y++) + { + Source = (const DWORD *)(src + z * src_slice_pitch + y * src_row_pitch); + Dest = dst + z * dst_slice_pitch + y * dst_row_pitch; + for (x = 0; x < width; x++ ) + { + LONG color = (*Source++); + /* B */ Dest[0] = ((color >> 16) & 0xff) + 128; /* W */ + /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */ + /* R */ Dest[2] = (color & 0xff) + 128; /* U */ + /* A */ Dest[3] = ((color >> 24) & 0xff) + 128; /* Q */ + Dest += 4; + } + } + } +} + +static void convert_r16g16_snorm(const BYTE *src, BYTE *dst, UINT src_row_pitch, UINT src_slice_pitch, + UINT dst_row_pitch, UINT dst_slice_pitch, UINT width, UINT height, UINT depth) +{ + unsigned int x, y, z; + const DWORD *Source; + unsigned short *Dest; + + for (z = 0; z < depth; z++) + { + for (y = 0; y < height; y++) + { + Source = (const DWORD *)(src + z * src_slice_pitch + y * src_row_pitch); + Dest = (unsigned short *) (dst + z * dst_slice_pitch + y * dst_row_pitch); + for (x = 0; x < width; x++ ) + { + const DWORD color = (*Source++); + /* B */ Dest[0] = 0xffff; + /* G */ Dest[1] = (color >> 16) + 32768; /* V */ + /* R */ Dest[2] = (color & 0xffff) + 32768; /* U */ + Dest += 3; + } + } + } +} + +static void convert_r16g16(const BYTE *src, BYTE *dst, UINT src_row_pitch, UINT src_slice_pitch, + UINT dst_row_pitch, UINT dst_slice_pitch, UINT width, UINT height, UINT depth) +{ + unsigned int x, y, z; + const WORD *Source; + WORD *Dest; + + for (z = 0; z < depth; z++) + { + for (y = 0; y < height; y++) + { + Source = (const WORD *)(src + z * src_slice_pitch + y * src_row_pitch); + Dest = (WORD *) (dst + z * dst_slice_pitch + y * dst_row_pitch); + for (x = 0; x < width; x++ ) + { + WORD green = (*Source++); + WORD red = (*Source++); + Dest[0] = green; + Dest[1] = red; + /* Strictly speaking not correct for R16G16F, but it doesn't matter because the + * shader overwrites it anyway */ + Dest[2] = 0xffff; + Dest += 3; + } + } + } +} + +static void convert_r32g32_float(const BYTE *src, BYTE *dst, UINT src_row_pitch, UINT src_slice_pitch, + UINT dst_row_pitch, UINT dst_slice_pitch, UINT width, UINT height, UINT depth) +{ + unsigned int x, y, z; + const float *Source; + float *Dest; + + for (z = 0; z < depth; z++) + { + for (y = 0; y < height; y++) + { + Source = (const float *)(src + z * src_slice_pitch + y * src_row_pitch); + Dest = (float *) (dst + z * dst_slice_pitch + y * dst_row_pitch); + for (x = 0; x < width; x++ ) + { + float green = (*Source++); + float red = (*Source++); + Dest[0] = green; + Dest[1] = red; + Dest[2] = 1.0f; + Dest += 3; + } + } + } +} + +static void convert_s8_uint_d24_float(const BYTE *src, BYTE *dst, UINT src_row_pitch, UINT src_slice_pitch, + UINT dst_row_pitch, UINT dst_slice_pitch, UINT width, UINT height, UINT depth) +{ + unsigned int x, y, z; + + for (z = 0; z < depth; z++) + { + for (y = 0; y < height; ++y) + { + const DWORD *source = (const DWORD *)(src + z * src_slice_pitch + y * src_row_pitch); + float *dest_f = (float *)(dst + z * dst_slice_pitch + y * dst_row_pitch); + DWORD *dest_s = (DWORD *)dest_f; + + for (x = 0; x < width; ++x) + { + dest_f[x * 2] = float_24_to_32((source[x] & 0xffffff00u) >> 8); + dest_s[x * 2 + 1] = source[x] & 0xff; + } + } + } +} + +static void x8_d24_unorm_upload(const BYTE *src, BYTE *dst, + unsigned int src_row_pitch, unsigned int src_slice_pitch, + unsigned int dst_row_pitch, unsigned int dst_slice_pitch, + unsigned int width, unsigned int height, unsigned int depth) +{ + unsigned int x, y, z; + + for (z = 0; z < depth; ++z) + { + for (y = 0; y < height; ++y) + { + const DWORD *source = (const DWORD *)(src + z * src_slice_pitch + y * src_row_pitch); + DWORD *dest = (DWORD *)(dst + z * dst_slice_pitch + y * dst_row_pitch); + + for (x = 0; x < width; ++x) + { + dest[x] = source[x] << 8 | ((source[x] >> 16) & 0xff); + } + } + } +} + +static void x8_d24_unorm_download(const BYTE *src, BYTE *dst, + unsigned int src_row_pitch, unsigned int src_slice_pitch, + unsigned int dst_row_pitch, unsigned int dst_slice_pitch, + unsigned int width, unsigned int height, unsigned int depth) +{ + unsigned int x, y, z; + + for (z = 0; z < depth; ++z) + { + for (y = 0; y < height; ++y) + { + const DWORD *source = (const DWORD *)(src + z * src_slice_pitch + y * src_row_pitch); + DWORD *dest = (DWORD *)(dst + z * dst_slice_pitch + y * dst_row_pitch); + + for (x = 0; x < width; ++x) + { + dest[x] = source[x] >> 8; + } + } + } +} + +static BOOL color_in_range(const struct wined3d_color_key *color_key, DWORD color) +{ + /* FIXME: Is this really how color keys are supposed to work? I think it + * makes more sense to compare the individual channels. */ + return color >= color_key->color_space_low_value + && color <= color_key->color_space_high_value; +} + +static void convert_b5g6r5_unorm_b5g5r5a1_unorm_color_key(const BYTE *src, unsigned int src_pitch, + BYTE *dst, unsigned int dst_pitch, unsigned int width, unsigned int height, + const struct wined3d_color_key *color_key) +{ + const WORD *src_row; + unsigned int x, y; + WORD *dst_row; + + for (y = 0; y < height; ++y) + { + src_row = (WORD *)&src[src_pitch * y]; + dst_row = (WORD *)&dst[dst_pitch * y]; + for (x = 0; x < width; ++x) + { + WORD src_color = src_row[x]; + if (!color_in_range(color_key, src_color)) + dst_row[x] = 0x8000u | ((src_color & 0xffc0u) >> 1) | (src_color & 0x1fu); + else + dst_row[x] = ((src_color & 0xffc0u) >> 1) | (src_color & 0x1fu); + } + } +} + +static void convert_b5g5r5x1_unorm_b5g5r5a1_unorm_color_key(const BYTE *src, unsigned int src_pitch, + BYTE *dst, unsigned int dst_pitch, unsigned int width, unsigned int height, + const struct wined3d_color_key *color_key) +{ + const WORD *src_row; + unsigned int x, y; + WORD *dst_row; + + for (y = 0; y < height; ++y) + { + src_row = (WORD *)&src[src_pitch * y]; + dst_row = (WORD *)&dst[dst_pitch * y]; + for (x = 0; x < width; ++x) + { + WORD src_color = src_row[x]; + if (color_in_range(color_key, src_color)) + dst_row[x] = src_color & ~0x8000; + else + dst_row[x] = src_color | 0x8000; + } + } +} + +static void convert_b8g8r8_unorm_b8g8r8a8_unorm_color_key(const BYTE *src, unsigned int src_pitch, + BYTE *dst, unsigned int dst_pitch, unsigned int width, unsigned int height, + const struct wined3d_color_key *color_key) +{ + const BYTE *src_row; + unsigned int x, y; + DWORD *dst_row; + + for (y = 0; y < height; ++y) + { + src_row = &src[src_pitch * y]; + dst_row = (DWORD *)&dst[dst_pitch * y]; + for (x = 0; x < width; ++x) + { + DWORD src_color = (src_row[x * 3 + 2] << 16) | (src_row[x * 3 + 1] << 8) | src_row[x * 3]; + if (!color_in_range(color_key, src_color)) + dst_row[x] = src_color | 0xff000000; + } + } +} + +static void convert_b8g8r8x8_unorm_b8g8r8a8_unorm_color_key(const BYTE *src, unsigned int src_pitch, + BYTE *dst, unsigned int dst_pitch, unsigned int width, unsigned int height, + const struct wined3d_color_key *color_key) +{ + const DWORD *src_row; + unsigned int x, y; + DWORD *dst_row; + + for (y = 0; y < height; ++y) + { + src_row = (DWORD *)&src[src_pitch * y]; + dst_row = (DWORD *)&dst[dst_pitch * y]; + for (x = 0; x < width; ++x) + { + DWORD src_color = src_row[x]; + if (color_in_range(color_key, src_color)) + dst_row[x] = src_color & ~0xff000000; + else + dst_row[x] = src_color | 0xff000000; + } + } +} + +static void convert_b8g8r8a8_unorm_b8g8r8a8_unorm_color_key(const BYTE *src, unsigned int src_pitch, + BYTE *dst, unsigned int dst_pitch, unsigned int width, unsigned int height, + const struct wined3d_color_key *color_key) +{ + const DWORD *src_row; + unsigned int x, y; + DWORD *dst_row; + + for (y = 0; y < height; ++y) + { + src_row = (DWORD *)&src[src_pitch * y]; + dst_row = (DWORD *)&dst[dst_pitch * y]; + for (x = 0; x < width; ++x) + { + DWORD src_color = src_row[x]; + if (color_in_range(color_key, src_color)) + src_color &= ~0xff000000; + dst_row[x] = src_color; + } + } +} + +const struct wined3d_color_key_conversion * wined3d_format_get_color_key_conversion( + const struct wined3d_texture *texture, BOOL need_alpha_ck) +{ + const struct wined3d_format *format = texture->resource.format; + unsigned int i; + + static const struct + { + enum wined3d_format_id src_format; + struct wined3d_color_key_conversion conversion; + } + color_key_info[] = + { + {WINED3DFMT_B5G6R5_UNORM, {WINED3DFMT_B5G5R5A1_UNORM, convert_b5g6r5_unorm_b5g5r5a1_unorm_color_key }}, + {WINED3DFMT_B5G5R5X1_UNORM, {WINED3DFMT_B5G5R5A1_UNORM, convert_b5g5r5x1_unorm_b5g5r5a1_unorm_color_key }}, + {WINED3DFMT_B8G8R8_UNORM, {WINED3DFMT_B8G8R8A8_UNORM, convert_b8g8r8_unorm_b8g8r8a8_unorm_color_key }}, + {WINED3DFMT_B8G8R8X8_UNORM, {WINED3DFMT_B8G8R8A8_UNORM, convert_b8g8r8x8_unorm_b8g8r8a8_unorm_color_key }}, + {WINED3DFMT_B8G8R8A8_UNORM, {WINED3DFMT_B8G8R8A8_UNORM, convert_b8g8r8a8_unorm_b8g8r8a8_unorm_color_key }}, + }; + + if (need_alpha_ck && (texture->async.flags & WINED3D_TEXTURE_ASYNC_COLOR_KEY)) + { + for (i = 0; i < ARRAY_SIZE(color_key_info); ++i) + { + if (color_key_info[i].src_format == format->id) + return &color_key_info[i].conversion; + } + + FIXME("Color-keying not supported with format %s.\n", debug_d3dformat(format->id)); + } + + return NULL; +} + +/* We intentionally don't support WINED3DFMT_D32_UNORM. No hardware driver + * supports it, and applications get confused when we do. + * + * The following formats explicitly don't have WINED3DFMT_FLAG_TEXTURE set: + * + * These are never supported on native. + * WINED3DFMT_B8G8R8_UNORM + * WINED3DFMT_B2G3R3_UNORM + * WINED3DFMT_L4A4_UNORM + * WINED3DFMT_S1_UINT_D15_UNORM + * WINED3DFMT_S4X4_UINT_D24_UNORM + * + * Only some Geforce/Voodoo3/G400 cards offer 8-bit textures in case of ddraw. + * Since it is not widely available, don't offer it. Further no Windows driver + * offers WINED3DFMT_P8_UINT_A8_NORM, so don't offer it either. + * WINED3DFMT_P8_UINT + * WINED3DFMT_P8_UINT_A8_UNORM + * + * These formats seem to be similar to the HILO formats in + * GL_NV_texture_shader. NVHU is said to be GL_UNSIGNED_HILO16, + * NVHS GL_SIGNED_HILO16. Rumours say that D3D computes a 3rd channel + * similarly to D3DFMT_CxV8U8 (So NVHS could be called D3DFMT_CxV16U16). ATI + * refused to support formats which can easily be emulated with pixel shaders, + * so applications have to deal with not having NVHS and NVHU. + * WINED3DFMT_NVHU + * WINED3DFMT_NVHS */ +static const struct wined3d_format_texture_info format_texture_info[] = +{ + /* format id gl_internal gl_srgb_internal gl_rt_internal + gl_format gl_type conv_byte_count + flags + extension upload download */ + /* FourCC formats */ + /* GL_APPLE_ycbcr_422 claims that its '2YUV' format, which is supported via the UNSIGNED_SHORT_8_8_REV_APPLE type + * is equivalent to 'UYVY' format on Windows, and the 'YUVS' via UNSIGNED_SHORT_8_8_APPLE equates to 'YUY2'. The + * d3d9 test however shows that the opposite is true. Since the extension is from 2002, it predates the x86 based + * Macs, so probably the endianness differs. This could be tested as soon as we have a Windows and MacOS on a big + * endian machine + */ + {WINED3DFMT_UYVY, GL_RG8, GL_RG8, 0, + GL_RG, GL_UNSIGNED_BYTE, 0, + WINED3DFMT_FLAG_FILTERING, + ARB_TEXTURE_RG, NULL}, + {WINED3DFMT_UYVY, GL_LUMINANCE8_ALPHA8, GL_LUMINANCE8_ALPHA8, 0, + GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, 0, + WINED3DFMT_FLAG_FILTERING, + WINED3D_GL_LEGACY_CONTEXT, NULL}, + {WINED3DFMT_UYVY, GL_RGB_RAW_422_APPLE, GL_RGB_RAW_422_APPLE, 0, + GL_RGB_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE, 0, + WINED3DFMT_FLAG_FILTERING, + APPLE_RGB_422, NULL}, + {WINED3DFMT_UYVY, GL_RGB, GL_RGB, 0, + GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_FILTERING, + APPLE_YCBCR_422, NULL}, + {WINED3DFMT_YUY2, GL_RG8, GL_RG8, 0, + GL_RG, GL_UNSIGNED_BYTE, 0, + WINED3DFMT_FLAG_FILTERING, + ARB_TEXTURE_RG, NULL}, + {WINED3DFMT_YUY2, GL_LUMINANCE8_ALPHA8, GL_LUMINANCE8_ALPHA8, 0, + GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, 0, + WINED3DFMT_FLAG_FILTERING, + WINED3D_GL_LEGACY_CONTEXT, NULL}, + {WINED3DFMT_YUY2, GL_RGB_RAW_422_APPLE, GL_RGB_RAW_422_APPLE, 0, + GL_RGB_422_APPLE, GL_UNSIGNED_SHORT_8_8_REV_APPLE, 0, + WINED3DFMT_FLAG_FILTERING, + APPLE_RGB_422, NULL}, + {WINED3DFMT_YUY2, GL_RGB, GL_RGB, 0, + GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_REV_APPLE, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_FILTERING, + APPLE_YCBCR_422, NULL}, + {WINED3DFMT_YV12, GL_R8, GL_R8, 0, + GL_RED, GL_UNSIGNED_BYTE, 0, + WINED3DFMT_FLAG_FILTERING, + ARB_TEXTURE_RG, NULL}, + {WINED3DFMT_YV12, GL_ALPHA8, GL_ALPHA8, 0, + GL_ALPHA, GL_UNSIGNED_BYTE, 0, + WINED3DFMT_FLAG_FILTERING, + WINED3D_GL_LEGACY_CONTEXT, NULL}, + {WINED3DFMT_NV12, GL_R8, GL_R8, 0, + GL_RED, GL_UNSIGNED_BYTE, 0, + WINED3DFMT_FLAG_FILTERING, + ARB_TEXTURE_RG, NULL}, + {WINED3DFMT_NV12, GL_ALPHA8, GL_ALPHA8, 0, + GL_ALPHA, GL_UNSIGNED_BYTE, 0, + WINED3DFMT_FLAG_FILTERING, + WINED3D_GL_LEGACY_CONTEXT, NULL}, + {WINED3DFMT_DXT1, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT, 0, + GL_RGBA, GL_UNSIGNED_BYTE, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING + | WINED3DFMT_FLAG_SRGB_READ, + EXT_TEXTURE_COMPRESSION_S3TC, NULL}, + {WINED3DFMT_DXT2, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT, 0, + GL_RGBA, GL_UNSIGNED_BYTE, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING + | WINED3DFMT_FLAG_SRGB_READ, + EXT_TEXTURE_COMPRESSION_S3TC, NULL}, + {WINED3DFMT_DXT3, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT, 0, + GL_RGBA, GL_UNSIGNED_BYTE, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING + | WINED3DFMT_FLAG_SRGB_READ, + EXT_TEXTURE_COMPRESSION_S3TC, NULL}, + {WINED3DFMT_DXT4, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, 0, + GL_RGBA, GL_UNSIGNED_BYTE, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING + | WINED3DFMT_FLAG_SRGB_READ, + EXT_TEXTURE_COMPRESSION_S3TC, NULL}, + {WINED3DFMT_DXT5, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, 0, + GL_RGBA, GL_UNSIGNED_BYTE, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING + | WINED3DFMT_FLAG_SRGB_READ, + EXT_TEXTURE_COMPRESSION_S3TC, NULL}, + {WINED3DFMT_BC1_UNORM, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT, 0, + GL_RGBA, GL_UNSIGNED_BYTE, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING, + EXT_TEXTURE_COMPRESSION_S3TC, NULL}, + {WINED3DFMT_BC2_UNORM, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT, 0, + GL_RGBA, GL_UNSIGNED_BYTE, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING, + EXT_TEXTURE_COMPRESSION_S3TC, NULL}, + {WINED3DFMT_BC3_UNORM, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, 0, + GL_RGBA, GL_UNSIGNED_BYTE, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING, + EXT_TEXTURE_COMPRESSION_S3TC, NULL}, + {WINED3DFMT_BC4_UNORM, GL_COMPRESSED_RED_RGTC1, GL_COMPRESSED_RED_RGTC1, 0, + GL_RED, GL_UNSIGNED_BYTE, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING, + ARB_TEXTURE_COMPRESSION_RGTC, NULL}, + {WINED3DFMT_BC4_SNORM, GL_COMPRESSED_SIGNED_RED_RGTC1, GL_COMPRESSED_SIGNED_RED_RGTC1, 0, + GL_RED, GL_UNSIGNED_BYTE, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING, + ARB_TEXTURE_COMPRESSION_RGTC, NULL}, + {WINED3DFMT_BC5_UNORM, GL_COMPRESSED_RG_RGTC2, GL_COMPRESSED_RG_RGTC2, 0, + GL_RG, GL_UNSIGNED_BYTE, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING, + ARB_TEXTURE_COMPRESSION_RGTC, NULL}, + {WINED3DFMT_BC5_SNORM, GL_COMPRESSED_SIGNED_RG_RGTC2, GL_COMPRESSED_SIGNED_RG_RGTC2, 0, + GL_RG, GL_UNSIGNED_BYTE, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING, + ARB_TEXTURE_COMPRESSION_RGTC, NULL}, + {WINED3DFMT_BC6H_UF16, GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB, GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB, 0, + GL_RGB, GL_UNSIGNED_BYTE, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING, + ARB_TEXTURE_COMPRESSION_BPTC, NULL}, + {WINED3DFMT_BC6H_SF16, GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB, GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB, 0, + GL_RGB, GL_UNSIGNED_BYTE, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING, + ARB_TEXTURE_COMPRESSION_BPTC, NULL}, + {WINED3DFMT_BC7_UNORM, GL_COMPRESSED_RGBA_BPTC_UNORM_ARB, GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB, 0, + GL_RGBA, GL_UNSIGNED_BYTE, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING, + ARB_TEXTURE_COMPRESSION_BPTC, NULL}, + /* IEEE formats */ + {WINED3DFMT_R32_FLOAT, GL_RGB32F_ARB, GL_RGB32F_ARB, 0, + GL_RED, GL_FLOAT, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_RENDERTARGET | WINED3DFMT_FLAG_VTF, + ARB_TEXTURE_FLOAT, NULL}, + {WINED3DFMT_R32_FLOAT, GL_R32F, GL_R32F, 0, + GL_RED, GL_FLOAT, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_RENDERTARGET | WINED3DFMT_FLAG_VTF, + ARB_TEXTURE_RG, NULL}, + {WINED3DFMT_R32G32_FLOAT, GL_RGB32F_ARB, GL_RGB32F_ARB, 0, + GL_RGB, GL_FLOAT, 12, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_RENDERTARGET | WINED3DFMT_FLAG_VTF, + ARB_TEXTURE_FLOAT, convert_r32g32_float}, + {WINED3DFMT_R32G32_FLOAT, GL_RG32F, GL_RG32F, 0, + GL_RG, GL_FLOAT, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_RENDERTARGET | WINED3DFMT_FLAG_VTF, + ARB_TEXTURE_RG, NULL}, + {WINED3DFMT_R32G32B32_FLOAT, GL_RGB32F, GL_RGB32F, 0, + GL_RGB, GL_FLOAT, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_RENDERTARGET, + ARB_TEXTURE_FLOAT, NULL}, + {WINED3DFMT_R32G32B32A32_FLOAT, GL_RGBA32F_ARB, GL_RGBA32F_ARB, 0, + GL_RGBA, GL_FLOAT, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_RENDERTARGET | WINED3DFMT_FLAG_VTF, + ARB_TEXTURE_FLOAT, NULL}, + /* Float */ + {WINED3DFMT_R16_FLOAT, GL_RGB16F_ARB, GL_RGB16F_ARB, 0, + GL_RED, GL_HALF_FLOAT_ARB, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_RENDERTARGET | WINED3DFMT_FLAG_VTF, + ARB_TEXTURE_FLOAT, NULL}, + {WINED3DFMT_R16_FLOAT, GL_R16F, GL_R16F, 0, + GL_RED, GL_HALF_FLOAT_ARB, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_RENDERTARGET | WINED3DFMT_FLAG_VTF, + ARB_TEXTURE_RG, NULL}, + {WINED3DFMT_R16G16_FLOAT, GL_RGB16F_ARB, GL_RGB16F_ARB, 0, + GL_RGB, GL_HALF_FLOAT_ARB, 6, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_RENDERTARGET | WINED3DFMT_FLAG_VTF, + ARB_TEXTURE_FLOAT, convert_r16g16}, + {WINED3DFMT_R16G16_FLOAT, GL_RG16F, GL_RG16F, 0, + GL_RG, GL_HALF_FLOAT_ARB, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_RENDERTARGET | WINED3DFMT_FLAG_VTF, + ARB_TEXTURE_RG, NULL}, + {WINED3DFMT_R16G16B16A16_FLOAT, GL_RGBA16F_ARB, GL_RGBA16F_ARB, 0, + GL_RGBA, GL_HALF_FLOAT_ARB, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_RENDERTARGET + | WINED3DFMT_FLAG_VTF, + ARB_TEXTURE_FLOAT, NULL}, + {WINED3DFMT_R11G11B10_FLOAT, GL_R11F_G11F_B10F, GL_R11F_G11F_B10F, 0, + GL_RGB, GL_UNSIGNED_INT_10F_11F_11F_REV, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_RENDERTARGET, + EXT_PACKED_FLOAT}, + /* Palettized formats */ + {WINED3DFMT_P8_UINT, GL_R8, GL_R8, 0, + GL_RED, GL_UNSIGNED_BYTE, 0, + 0, + ARB_TEXTURE_RG, NULL}, + {WINED3DFMT_P8_UINT, GL_ALPHA8, GL_ALPHA8, 0, + GL_ALPHA, GL_UNSIGNED_BYTE, 0, + 0, + WINED3D_GL_LEGACY_CONTEXT, NULL}, + /* Standard ARGB formats */ + {WINED3DFMT_B8G8R8_UNORM, GL_RGB8, GL_RGB8, 0, + GL_BGR, GL_UNSIGNED_BYTE, 0, + WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING | WINED3DFMT_FLAG_RENDERTARGET, + WINED3D_GL_EXT_NONE, NULL}, + {WINED3DFMT_B8G8R8A8_UNORM, GL_RGBA8, GL_SRGB8_ALPHA8_EXT, 0, + GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING + | WINED3DFMT_FLAG_RENDERTARGET | WINED3DFMT_FLAG_SRGB_READ | WINED3DFMT_FLAG_SRGB_WRITE + | WINED3DFMT_FLAG_VTF, + WINED3D_GL_EXT_NONE, NULL}, + {WINED3DFMT_B8G8R8X8_UNORM, GL_RGB8, GL_SRGB8_EXT, 0, + GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING + | WINED3DFMT_FLAG_RENDERTARGET | WINED3DFMT_FLAG_SRGB_READ | WINED3DFMT_FLAG_SRGB_WRITE, + WINED3D_GL_EXT_NONE, NULL}, + {WINED3DFMT_B5G6R5_UNORM, GL_RGB5, GL_SRGB8_EXT, GL_RGB8, + GL_RGB, GL_UNSIGNED_SHORT_5_6_5, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING + | WINED3DFMT_FLAG_RENDERTARGET | WINED3DFMT_FLAG_SRGB_READ, + WINED3D_GL_EXT_NONE, NULL}, + {WINED3DFMT_B5G6R5_UNORM, GL_RGB565, GL_SRGB8_EXT, GL_RGB8, + GL_RGB, GL_UNSIGNED_SHORT_5_6_5, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING + | WINED3DFMT_FLAG_RENDERTARGET | WINED3DFMT_FLAG_SRGB_READ, + ARB_ES2_COMPATIBILITY, NULL}, + {WINED3DFMT_B5G5R5X1_UNORM, GL_RGB5, GL_RGB5, 0, + GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING + | WINED3DFMT_FLAG_RENDERTARGET, + WINED3D_GL_EXT_NONE, NULL}, + {WINED3DFMT_B5G5R5A1_UNORM, GL_RGB5_A1, GL_RGB5_A1, 0, + GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING, + WINED3D_GL_EXT_NONE, NULL}, + {WINED3DFMT_B4G4R4A4_UNORM, GL_RGBA4, GL_SRGB8_ALPHA8_EXT, 0, + GL_BGRA, GL_UNSIGNED_SHORT_4_4_4_4_REV, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING + | WINED3DFMT_FLAG_SRGB_READ, + WINED3D_GL_EXT_NONE, NULL}, + {WINED3DFMT_B2G3R3_UNORM, GL_R3_G3_B2, GL_R3_G3_B2, 0, + GL_RGB, GL_UNSIGNED_BYTE_3_3_2, 0, + WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING, + WINED3D_GL_EXT_NONE, NULL}, + {WINED3DFMT_R8_UNORM, GL_R8, GL_R8, 0, + GL_RED, GL_UNSIGNED_BYTE, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING + | WINED3DFMT_FLAG_RENDERTARGET | WINED3DFMT_FLAG_VTF, + ARB_TEXTURE_RG, NULL}, + {WINED3DFMT_A8_UNORM, GL_R8, GL_R8, 0, + GL_RED, GL_UNSIGNED_BYTE, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING + | WINED3DFMT_FLAG_RENDERTARGET, + ARB_TEXTURE_RG, NULL}, + {WINED3DFMT_A8_UNORM, GL_ALPHA8, GL_ALPHA8, 0, + GL_ALPHA, GL_UNSIGNED_BYTE, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING + | WINED3DFMT_FLAG_RENDERTARGET, + WINED3D_GL_LEGACY_CONTEXT, NULL}, + {WINED3DFMT_B4G4R4X4_UNORM, GL_RGB4, GL_RGB4, 0, + GL_BGRA, GL_UNSIGNED_SHORT_4_4_4_4_REV, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING, + WINED3D_GL_EXT_NONE, NULL}, + {WINED3DFMT_R10G10B10A2_UINT, GL_RGB10_A2UI, GL_RGB10_A2UI, 0, + GL_RGBA_INTEGER, GL_UNSIGNED_INT_2_10_10_10_REV, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_RENDERTARGET, + ARB_TEXTURE_RGB10_A2UI, NULL}, + {WINED3DFMT_R10G10B10A2_UNORM, GL_RGB10_A2, GL_RGB10_A2, 0, + GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING + | WINED3DFMT_FLAG_RENDERTARGET, + WINED3D_GL_EXT_NONE, NULL}, + {WINED3DFMT_R8G8B8A8_UNORM, GL_RGBA8, GL_SRGB8_ALPHA8_EXT, 0, + GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING + | WINED3DFMT_FLAG_RENDERTARGET | WINED3DFMT_FLAG_SRGB_READ | WINED3DFMT_FLAG_SRGB_WRITE + | WINED3DFMT_FLAG_VTF, + WINED3D_GL_EXT_NONE, NULL}, + {WINED3DFMT_R8G8B8A8_UINT, GL_RGBA8UI, GL_RGBA8UI, 0, + GL_RGBA_INTEGER, GL_UNSIGNED_INT_8_8_8_8_REV, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_RENDERTARGET, + ARB_TEXTURE_RGB10_A2UI, NULL}, + {WINED3DFMT_R8G8B8A8_SINT, GL_RGBA8I, GL_RGBA8I, 0, + GL_RGBA_INTEGER, GL_BYTE, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_RENDERTARGET, + EXT_TEXTURE_INTEGER, NULL}, + {WINED3DFMT_R8G8B8X8_UNORM, GL_RGB8, GL_RGB8, 0, + GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING, + WINED3D_GL_EXT_NONE, NULL}, + {WINED3DFMT_R16G16_UNORM, GL_RGB16, GL_RGB16, GL_RGBA16, + GL_RGB, GL_UNSIGNED_SHORT, 6, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING, + WINED3D_GL_EXT_NONE, convert_r16g16}, + {WINED3DFMT_R16G16_UNORM, GL_RG16, GL_RG16, 0, + GL_RG, GL_UNSIGNED_SHORT, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING + | WINED3DFMT_FLAG_RENDERTARGET, + ARB_TEXTURE_RG, NULL}, + {WINED3DFMT_B10G10R10A2_UNORM, GL_RGB10_A2, GL_RGB10_A2, 0, + GL_BGRA, GL_UNSIGNED_INT_2_10_10_10_REV, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING + | WINED3DFMT_FLAG_RENDERTARGET, + WINED3D_GL_EXT_NONE, NULL}, + {WINED3DFMT_R16G16B16A16_UNORM, GL_RGBA16, GL_RGBA16, 0, + GL_RGBA, GL_UNSIGNED_SHORT, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING + | WINED3DFMT_FLAG_RENDERTARGET, + WINED3D_GL_EXT_NONE, NULL}, + {WINED3DFMT_R8G8_UNORM, GL_RG8, GL_RG8, 0, + GL_RG, GL_UNSIGNED_BYTE, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING + | WINED3DFMT_FLAG_RENDERTARGET, + ARB_TEXTURE_RG, NULL}, + {WINED3DFMT_R8G8_UINT, GL_RG8UI, GL_RG8UI, 0, + GL_RG_INTEGER, GL_UNSIGNED_BYTE, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_RENDERTARGET, + ARB_TEXTURE_RG, NULL}, + {WINED3DFMT_R8G8_SINT, GL_RG8I, GL_RG8I, 0, + GL_RG_INTEGER, GL_BYTE, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_RENDERTARGET, + ARB_TEXTURE_RG, NULL}, + {WINED3DFMT_R16G16B16A16_UINT, GL_RGBA16UI, GL_RGBA16UI, 0, + GL_RGBA_INTEGER, GL_UNSIGNED_SHORT, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_RENDERTARGET, + EXT_TEXTURE_INTEGER, NULL}, + {WINED3DFMT_R16G16B16A16_SINT, GL_RGBA16I, GL_RGBA16I, 0, + GL_RGBA_INTEGER, GL_SHORT, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_RENDERTARGET, + EXT_TEXTURE_INTEGER, NULL}, + {WINED3DFMT_R32G32_UINT, GL_RG32UI, GL_RG32UI, 0, + GL_RG_INTEGER, GL_UNSIGNED_INT, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_RENDERTARGET, + ARB_TEXTURE_RG, NULL}, + {WINED3DFMT_R32G32_SINT, GL_RG32I, GL_RG32I, 0, + GL_RG_INTEGER, GL_INT, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_RENDERTARGET, + ARB_TEXTURE_RG, NULL}, + {WINED3DFMT_R16G16_UINT, GL_RG16UI, GL_RG16UI, 0, + GL_RG_INTEGER, GL_UNSIGNED_SHORT, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_RENDERTARGET, + ARB_TEXTURE_RG, NULL}, + {WINED3DFMT_R16G16_SINT, GL_RG16I, GL_RG16I, 0, + GL_RG_INTEGER, GL_SHORT, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_RENDERTARGET, + ARB_TEXTURE_RG, NULL}, + {WINED3DFMT_R32_UINT, GL_R32UI, GL_R32UI, 0, + GL_RED_INTEGER, GL_UNSIGNED_INT, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_RENDERTARGET, + ARB_TEXTURE_RG, NULL}, + {WINED3DFMT_R32_SINT, GL_R32I, GL_R32I, 0, + GL_RED_INTEGER, GL_INT, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_RENDERTARGET, + ARB_TEXTURE_RG, NULL}, + {WINED3DFMT_R16_UNORM, GL_R16, GL_R16, 0, + GL_RED, GL_UNSIGNED_SHORT, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING + | WINED3DFMT_FLAG_RENDERTARGET, + ARB_TEXTURE_RG, NULL}, + {WINED3DFMT_R16_UINT, GL_R16UI, GL_R16UI, 0, + GL_RED_INTEGER, GL_UNSIGNED_SHORT, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_RENDERTARGET, + ARB_TEXTURE_RG, NULL}, + {WINED3DFMT_R16_SINT, GL_R16I, GL_R16I, 0, + GL_RED_INTEGER, GL_SHORT, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_RENDERTARGET, + ARB_TEXTURE_RG, NULL}, + {WINED3DFMT_R8_UINT, GL_R8UI, GL_R8UI, 0, + GL_RED_INTEGER, GL_UNSIGNED_BYTE, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_RENDERTARGET, + ARB_TEXTURE_RG, NULL}, + {WINED3DFMT_R8_SINT, GL_R8I, GL_R8I, 0, + GL_RED_INTEGER, GL_BYTE, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_RENDERTARGET, + ARB_TEXTURE_RG, NULL}, + /* Luminance */ + {WINED3DFMT_L8_UNORM, GL_LUMINANCE8, GL_SLUMINANCE8_EXT, 0, + GL_LUMINANCE, GL_UNSIGNED_BYTE, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING + | WINED3DFMT_FLAG_SRGB_READ, + WINED3D_GL_LEGACY_CONTEXT, NULL}, + {WINED3DFMT_L8_UNORM, GL_R8, GL_R8, 0, + GL_RED, GL_UNSIGNED_BYTE, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING + | WINED3DFMT_FLAG_RENDERTARGET, + ARB_TEXTURE_RG, NULL}, + {WINED3DFMT_L8A8_UNORM, GL_RG8, GL_RG8, 0, + GL_RG, GL_UNSIGNED_BYTE, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING, + ARB_TEXTURE_RG, NULL}, + {WINED3DFMT_L8A8_UNORM, GL_LUMINANCE8_ALPHA8, GL_SLUMINANCE8_ALPHA8_EXT, 0, + GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING + | WINED3DFMT_FLAG_SRGB_READ, + WINED3D_GL_LEGACY_CONTEXT, NULL}, + {WINED3DFMT_L4A4_UNORM, GL_RG8, GL_RG8, 0, + GL_RG, GL_UNSIGNED_BYTE, 2, + WINED3DFMT_FLAG_FILTERING, + ARB_TEXTURE_RG, convert_l4a4_unorm}, + {WINED3DFMT_L4A4_UNORM, GL_LUMINANCE4_ALPHA4, GL_LUMINANCE4_ALPHA4, 0, + GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, 2, + WINED3DFMT_FLAG_FILTERING, + WINED3D_GL_LEGACY_CONTEXT, convert_l4a4_unorm}, + {WINED3DFMT_L16_UNORM, GL_R16, GL_R16, 0, + GL_RED, GL_UNSIGNED_SHORT, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING, + ARB_TEXTURE_RG, NULL}, + {WINED3DFMT_L16_UNORM, GL_LUMINANCE16, GL_LUMINANCE16, 0, + GL_LUMINANCE, GL_UNSIGNED_SHORT, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING, + WINED3D_GL_LEGACY_CONTEXT, NULL}, + /* Bump mapping stuff */ + {WINED3DFMT_R8G8_SNORM, GL_RGB8, GL_RGB8, 0, + GL_BGR, GL_UNSIGNED_BYTE, 3, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING + | WINED3DFMT_FLAG_BUMPMAP, + WINED3D_GL_EXT_NONE, convert_r8g8_snorm}, + {WINED3DFMT_R8G8_SNORM, GL_DSDT8_NV, GL_DSDT8_NV, 0, + GL_DSDT_NV, GL_BYTE, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING + | WINED3DFMT_FLAG_BUMPMAP, + NV_TEXTURE_SHADER, NULL}, + {WINED3DFMT_R8G8_SNORM, GL_RG8_SNORM, GL_RG8_SNORM, 0, + GL_RG, GL_BYTE, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING + | WINED3DFMT_FLAG_RENDERTARGET | WINED3DFMT_FLAG_BUMPMAP, + EXT_TEXTURE_SNORM, NULL}, + {WINED3DFMT_R5G5_SNORM_L6_UNORM, GL_RGB5, GL_RGB5, 0, + GL_RGB, GL_UNSIGNED_SHORT_5_6_5, 2, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING + | WINED3DFMT_FLAG_BUMPMAP, + WINED3D_GL_EXT_NONE, convert_r5g5_snorm_l6_unorm}, + {WINED3DFMT_R5G5_SNORM_L6_UNORM, GL_DSDT8_MAG8_NV, GL_DSDT8_MAG8_NV, 0, + GL_DSDT_MAG_NV, GL_BYTE, 3, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING + | WINED3DFMT_FLAG_BUMPMAP, + NV_TEXTURE_SHADER, convert_r5g5_snorm_l6_unorm_nv}, + {WINED3DFMT_R5G5_SNORM_L6_UNORM, GL_RGB8_SNORM, GL_RGB8_SNORM, 0, + GL_RGBA, GL_BYTE, 4, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING + | WINED3DFMT_FLAG_BUMPMAP, + EXT_TEXTURE_SNORM, convert_r5g5_snorm_l6_unorm_ext}, + {WINED3DFMT_R8G8_SNORM_L8X8_UNORM, GL_RGB8, GL_RGB8, 0, + GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, 4, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING + | WINED3DFMT_FLAG_BUMPMAP, + WINED3D_GL_EXT_NONE, convert_r8g8_snorm_l8x8_unorm}, + {WINED3DFMT_R8G8_SNORM_L8X8_UNORM, GL_DSDT8_MAG8_INTENSITY8_NV, GL_DSDT8_MAG8_INTENSITY8_NV, 0, + GL_DSDT_MAG_VIB_NV, GL_UNSIGNED_INT_8_8_S8_S8_REV_NV, 4, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING + | WINED3DFMT_FLAG_BUMPMAP, + NV_TEXTURE_SHADER, convert_r8g8_snorm_l8x8_unorm_nv}, + {WINED3DFMT_R8G8B8A8_SNORM, GL_RGBA8, GL_RGBA8, 0, + GL_BGRA, GL_UNSIGNED_BYTE, 4, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING + | WINED3DFMT_FLAG_BUMPMAP, + WINED3D_GL_EXT_NONE, convert_r8g8b8a8_snorm}, + {WINED3DFMT_R8G8B8A8_SNORM, GL_SIGNED_RGBA8_NV, GL_SIGNED_RGBA8_NV, 0, + GL_RGBA, GL_BYTE, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING + | WINED3DFMT_FLAG_BUMPMAP, + NV_TEXTURE_SHADER, NULL}, + {WINED3DFMT_R8G8B8A8_SNORM, GL_RGBA8_SNORM, GL_RGBA8_SNORM, 0, + GL_RGBA, GL_BYTE, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING + | WINED3DFMT_FLAG_RENDERTARGET | WINED3DFMT_FLAG_BUMPMAP, + EXT_TEXTURE_SNORM, NULL}, + {WINED3DFMT_R16G16_SNORM, GL_RGB16, GL_RGB16, 0, + GL_BGR, GL_UNSIGNED_SHORT, 6, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING + | WINED3DFMT_FLAG_BUMPMAP, + WINED3D_GL_EXT_NONE, convert_r16g16_snorm}, + {WINED3DFMT_R16G16_SNORM, GL_SIGNED_HILO16_NV, GL_SIGNED_HILO16_NV, 0, + GL_HILO_NV, GL_SHORT, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING + | WINED3DFMT_FLAG_BUMPMAP, + NV_TEXTURE_SHADER, NULL}, + {WINED3DFMT_R16G16_SNORM, GL_RG16_SNORM, GL_RG16_SNORM, 0, + GL_RG, GL_SHORT, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING + | WINED3DFMT_FLAG_RENDERTARGET | WINED3DFMT_FLAG_BUMPMAP, + EXT_TEXTURE_SNORM, NULL}, + {WINED3DFMT_R16G16B16A16_SNORM, GL_RGBA16_SNORM, GL_RGBA16_SNORM, 0, + GL_RGBA, GL_SHORT, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING + | WINED3DFMT_FLAG_RENDERTARGET, + EXT_TEXTURE_SNORM, NULL}, + {WINED3DFMT_R16_SNORM, GL_R16_SNORM, GL_R16_SNORM, 0, + GL_RED, GL_SHORT, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING + | WINED3DFMT_FLAG_RENDERTARGET, + EXT_TEXTURE_SNORM, NULL}, + {WINED3DFMT_R8_SNORM, GL_R8_SNORM, GL_R8_SNORM, 0, + GL_RED, GL_BYTE, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING + | WINED3DFMT_FLAG_RENDERTARGET, + EXT_TEXTURE_SNORM, NULL}, + /* Depth stencil formats */ + {WINED3DFMT_D16_LOCKABLE, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT, 0, + GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, 0, + WINED3DFMT_FLAG_DEPTH_STENCIL, + WINED3D_GL_EXT_NONE, NULL}, + {WINED3DFMT_D16_LOCKABLE, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT16, 0, + GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_DEPTH_STENCIL | WINED3DFMT_FLAG_SHADOW, + ARB_DEPTH_TEXTURE, NULL}, + {WINED3DFMT_D24_UNORM_S8_UINT, GL_DEPTH_COMPONENT24_ARB, GL_DEPTH_COMPONENT24_ARB, 0, + GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING + | WINED3DFMT_FLAG_DEPTH_STENCIL | WINED3DFMT_FLAG_SHADOW, + ARB_DEPTH_TEXTURE, NULL}, + {WINED3DFMT_D24_UNORM_S8_UINT, GL_DEPTH24_STENCIL8, GL_DEPTH24_STENCIL8, 0, + GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING + | WINED3DFMT_FLAG_DEPTH_STENCIL | WINED3DFMT_FLAG_SHADOW, + EXT_PACKED_DEPTH_STENCIL, NULL}, + {WINED3DFMT_X8D24_UNORM, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT, 0, + GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, 4, + WINED3DFMT_FLAG_DEPTH_STENCIL, + WINED3D_GL_EXT_NONE, x8_d24_unorm_upload, x8_d24_unorm_download}, + {WINED3DFMT_X8D24_UNORM, GL_DEPTH_COMPONENT24_ARB, GL_DEPTH_COMPONENT24_ARB, 0, + GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, 4, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING + | WINED3DFMT_FLAG_DEPTH_STENCIL | WINED3DFMT_FLAG_SHADOW, + ARB_DEPTH_TEXTURE, x8_d24_unorm_upload, x8_d24_unorm_download}, + {WINED3DFMT_D16_UNORM, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT, 0, + GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, 0, + WINED3DFMT_FLAG_DEPTH_STENCIL, + WINED3D_GL_EXT_NONE, NULL}, + {WINED3DFMT_D16_UNORM, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT16, 0, + GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING + | WINED3DFMT_FLAG_DEPTH_STENCIL | WINED3DFMT_FLAG_SHADOW, + ARB_DEPTH_TEXTURE, NULL}, + {WINED3DFMT_D32_FLOAT, GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT32F, 0, + GL_DEPTH_COMPONENT, GL_FLOAT, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_DEPTH_STENCIL | WINED3DFMT_FLAG_SHADOW, + ARB_DEPTH_BUFFER_FLOAT, NULL}, + {WINED3DFMT_D32_FLOAT_S8X24_UINT, GL_DEPTH32F_STENCIL8, GL_DEPTH32F_STENCIL8, 0, + GL_DEPTH_STENCIL, GL_FLOAT_32_UNSIGNED_INT_24_8_REV, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_DEPTH_STENCIL | WINED3DFMT_FLAG_SHADOW, + ARB_DEPTH_BUFFER_FLOAT, NULL}, + {WINED3DFMT_S8_UINT_D24_FLOAT, GL_DEPTH32F_STENCIL8, GL_DEPTH32F_STENCIL8, 0, + GL_DEPTH_STENCIL, GL_FLOAT_32_UNSIGNED_INT_24_8_REV, 8, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_DEPTH_STENCIL | WINED3DFMT_FLAG_SHADOW, + ARB_DEPTH_BUFFER_FLOAT, convert_s8_uint_d24_float}, + {WINED3DFMT_R32G32B32A32_UINT, GL_RGBA32UI, GL_RGBA32UI, 0, + GL_RGBA_INTEGER, GL_UNSIGNED_INT, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_RENDERTARGET, + EXT_TEXTURE_INTEGER, NULL}, + {WINED3DFMT_R32G32B32A32_SINT, GL_RGBA32I, GL_RGBA32I, 0, + GL_RGBA_INTEGER, GL_INT, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_RENDERTARGET, + EXT_TEXTURE_INTEGER, NULL}, + /* Vendor-specific formats */ + {WINED3DFMT_ATI1N, GL_COMPRESSED_RED_RGTC1, GL_COMPRESSED_RED_RGTC1, 0, + GL_RED, GL_UNSIGNED_BYTE, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING, + ARB_TEXTURE_COMPRESSION_RGTC, NULL}, + {WINED3DFMT_ATI2N, GL_COMPRESSED_LUMINANCE_ALPHA_3DC_ATI, GL_COMPRESSED_LUMINANCE_ALPHA_3DC_ATI, 0, + GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING, + ATI_TEXTURE_COMPRESSION_3DC, NULL}, + {WINED3DFMT_ATI2N, GL_COMPRESSED_RG_RGTC2, GL_COMPRESSED_RG_RGTC2, 0, + GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING, + EXT_TEXTURE_COMPRESSION_RGTC, NULL}, + {WINED3DFMT_ATI2N, GL_COMPRESSED_RG_RGTC2, GL_COMPRESSED_RG_RGTC2, 0, + GL_RG, GL_UNSIGNED_BYTE, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING, + ARB_TEXTURE_COMPRESSION_RGTC, NULL}, + {WINED3DFMT_INTZ, GL_DEPTH24_STENCIL8, GL_DEPTH24_STENCIL8, 0, + GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING + | WINED3DFMT_FLAG_DEPTH_STENCIL, + EXT_PACKED_DEPTH_STENCIL, NULL}, + {WINED3DFMT_NULL, 0, 0, 0, + GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_RENDERTARGET | WINED3DFMT_FLAG_FBO_ATTACHABLE, + ARB_FRAMEBUFFER_OBJECT, NULL}, + /* DirectX 10 HDR formats */ + {WINED3DFMT_R9G9B9E5_SHAREDEXP, GL_RGB9_E5_EXT, GL_RGB9_E5_EXT, 0, + GL_RGB, GL_UNSIGNED_INT_5_9_9_9_REV_EXT, 0, + WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING, + EXT_TEXTURE_SHARED_EXPONENT, NULL}, +}; + +struct wined3d_format_srgb_info +{ + enum wined3d_format_id srgb_format_id; + enum wined3d_format_id base_format_id; +}; + +static const struct wined3d_format_srgb_info format_srgb_info[] = +{ + {WINED3DFMT_R8G8B8A8_UNORM_SRGB, WINED3DFMT_R8G8B8A8_UNORM}, + {WINED3DFMT_BC1_UNORM_SRGB, WINED3DFMT_BC1_UNORM}, + {WINED3DFMT_BC2_UNORM_SRGB, WINED3DFMT_BC2_UNORM}, + {WINED3DFMT_BC3_UNORM_SRGB, WINED3DFMT_BC3_UNORM}, + {WINED3DFMT_B8G8R8A8_UNORM_SRGB, WINED3DFMT_B8G8R8A8_UNORM}, + {WINED3DFMT_B8G8R8X8_UNORM_SRGB, WINED3DFMT_B8G8R8X8_UNORM}, + {WINED3DFMT_BC7_UNORM_SRGB, WINED3DFMT_BC7_UNORM}, +}; + +static inline int get_format_idx(enum wined3d_format_id format_id) +{ + unsigned int i; + + if (format_id < WINED3D_FORMAT_FOURCC_BASE) + return format_id; + + for (i = 0; i < ARRAY_SIZE(format_index_remap); ++i) + { + if (format_index_remap[i].id == format_id) + return format_index_remap[i].idx; + } + + return -1; +} + +static struct wined3d_format_gl *wined3d_format_gl_mutable(struct wined3d_format *format) +{ + return CONTAINING_RECORD(format, struct wined3d_format_gl, f); +} + +static struct wined3d_format_vk *wined3d_format_vk_mutable(struct wined3d_format *format) +{ + return CONTAINING_RECORD(format, struct wined3d_format_vk, f); +} + +static struct wined3d_format *get_format_by_idx(const struct wined3d_adapter *adapter, int fmt_idx) +{ + return (struct wined3d_format *)((BYTE *)adapter->formats + fmt_idx * adapter->format_size); +} + +static struct wined3d_format_gl *get_format_gl_by_idx(const struct wined3d_adapter *adapter, int fmt_idx) +{ + return wined3d_format_gl_mutable(get_format_by_idx(adapter, fmt_idx)); +} + +static struct wined3d_format *get_format_internal(const struct wined3d_adapter *adapter, + enum wined3d_format_id format_id) +{ + int fmt_idx; + + if ((fmt_idx = get_format_idx(format_id)) == -1) + { + ERR("Format %s (%#x) not found.\n", debug_d3dformat(format_id), format_id); + return NULL; + } + + return get_format_by_idx(adapter, fmt_idx); +} + +static struct wined3d_format_gl *get_format_gl_internal(const struct wined3d_adapter *adapter, + enum wined3d_format_id format_id) +{ + struct wined3d_format *format; + + if ((format = get_format_internal(adapter, format_id))) + return wined3d_format_gl_mutable(format); + + return NULL; +} + +static void copy_format(const struct wined3d_adapter *adapter, + struct wined3d_format *dst_format, const struct wined3d_format *src_format) +{ + enum wined3d_format_id id = dst_format->id; + memcpy(dst_format, src_format, adapter->format_size); + dst_format->id = id; +} + +static void format_set_flag(struct wined3d_format *format, unsigned int flag) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(format->flags); ++i) + format->flags[i] |= flag; +} + +static void format_clear_flag(struct wined3d_format *format, unsigned int flag) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(format->flags); ++i) + format->flags[i] &= ~flag; +} + +static enum wined3d_channel_type map_channel_type(char t) +{ + switch (t) + { + case 'u': + return WINED3D_CHANNEL_TYPE_UNORM; + case 'i': + return WINED3D_CHANNEL_TYPE_SNORM; + case 'U': + return WINED3D_CHANNEL_TYPE_UINT; + case 'I': + return WINED3D_CHANNEL_TYPE_SINT; + case 'F': + return WINED3D_CHANNEL_TYPE_FLOAT; + case 'D': + return WINED3D_CHANNEL_TYPE_DEPTH; + case 'S': + return WINED3D_CHANNEL_TYPE_STENCIL; + case 'X': + return WINED3D_CHANNEL_TYPE_UNUSED; + default: + ERR("Invalid channel type '%c'.\n", t); + return WINED3D_CHANNEL_TYPE_NONE; + } +} + +static BOOL init_format_base_info(struct wined3d_adapter *adapter) +{ + struct wined3d_format *format; + unsigned int i, j; + + for (i = 0; i < ARRAY_SIZE(formats); ++i) + { + if (!(format = get_format_internal(adapter, formats[i].id))) + return FALSE; + + format->id = formats[i].id; + format->red_size = formats[i].red_size; + format->green_size = formats[i].green_size; + format->blue_size = formats[i].blue_size; + format->alpha_size = formats[i].alpha_size; + format->red_offset = formats[i].red_offset; + format->green_offset = formats[i].green_offset; + format->blue_offset = formats[i].blue_offset; + format->alpha_offset = formats[i].alpha_offset; + format->byte_count = formats[i].bpp; + format->depth_size = formats[i].depth_size; + format->stencil_size = formats[i].stencil_size; + format->block_width = 1; + format->block_height = 1; + format->block_byte_count = formats[i].bpp; + } + + for (i = 0; i < ARRAY_SIZE(typed_formats); ++i) + { + struct wined3d_format *typeless_format; + unsigned int component_count = 0; + DWORD flags = 0; + + if (!(format = get_format_internal(adapter, typed_formats[i].id))) + return FALSE; + + if (!(typeless_format = get_format_internal(adapter, typed_formats[i].typeless_id))) + return FALSE; + + format->id = typed_formats[i].id; + format->red_size = typeless_format->red_size; + format->green_size = typeless_format->green_size; + format->blue_size = typeless_format->blue_size; + format->alpha_size = typeless_format->alpha_size; + format->red_offset = typeless_format->red_offset; + format->green_offset = typeless_format->green_offset; + format->blue_offset = typeless_format->blue_offset; + format->alpha_offset = typeless_format->alpha_offset; + format->byte_count = typeless_format->byte_count; + format->depth_size = typeless_format->depth_size; + format->stencil_size = typeless_format->stencil_size; + format->block_width = typeless_format->block_width; + format->block_height = typeless_format->block_height; + format->block_byte_count = typeless_format->block_byte_count; + format->typeless_id = typeless_format->id; + + typeless_format->typeless_id = typeless_format->id; + + for (j = 0; j < strlen(typed_formats[i].channels); ++j) + { + enum wined3d_channel_type channel_type = map_channel_type(typed_formats[i].channels[j]); + + if (channel_type == WINED3D_CHANNEL_TYPE_UNORM || channel_type == WINED3D_CHANNEL_TYPE_SNORM) + flags |= WINED3DFMT_FLAG_NORMALISED; + if (channel_type == WINED3D_CHANNEL_TYPE_UINT || channel_type == WINED3D_CHANNEL_TYPE_SINT) + flags |= WINED3DFMT_FLAG_INTEGER; + if (channel_type == WINED3D_CHANNEL_TYPE_FLOAT) + flags |= WINED3DFMT_FLAG_FLOAT; + if (channel_type != WINED3D_CHANNEL_TYPE_UNUSED) + ++component_count; + + if (channel_type == WINED3D_CHANNEL_TYPE_DEPTH && !format->depth_size) + { + format->depth_size = format->red_size; + format->red_size = format->red_offset = 0; + } + + if (channel_type == WINED3D_CHANNEL_TYPE_STENCIL && !format->stencil_size) + { + format->stencil_size = format->green_size; + format->green_size = format->green_offset = 0; + } + } + + format->component_count = component_count; + format_set_flag(format, flags); + } + + for (i = 0; i < ARRAY_SIZE(ddi_formats); ++i) + { + if (!(format = get_format_internal(adapter, ddi_formats[i].id))) + return FALSE; + + format->ddi_format = ddi_formats[i].ddi_format; + } + + for (i = 0; i < ARRAY_SIZE(format_base_flags); ++i) + { + if (!(format = get_format_internal(adapter, format_base_flags[i].id))) + return FALSE; + + format_set_flag(format, format_base_flags[i].flags); + } + + return TRUE; +} + +static BOOL init_format_block_info(struct wined3d_adapter *adapter) +{ + struct wined3d_format *format; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(format_block_info); ++i) + { + if (!(format = get_format_internal(adapter, format_block_info[i].id))) + return FALSE; + + format->block_width = format_block_info[i].block_width; + format->block_height = format_block_info[i].block_height; + format->block_byte_count = format_block_info[i].block_byte_count; + format_set_flag(format, WINED3DFMT_FLAG_BLOCKS | format_block_info[i].flags); + } + + return TRUE; +} + +/* Most compressed formats are not supported for 3D textures by OpenGL. + * + * In the case of the S3TC/DXTn formats, NV_texture_compression_vtc provides + * these formats for 3D textures, but unfortunately the block layout is + * different from the one used by Direct3D. + * + * Since applications either don't check format availability at all before + * using these, or refuse to run without them, we decompress them on upload. + * + * Affected applications include "Heroes VI", "From Dust", "Halo Online" and + * "Eldorado". */ +static BOOL init_format_decompress_info(struct wined3d_adapter *adapter) +{ + struct wined3d_format *format; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(format_decompress_info); ++i) + { + if (!(format = get_format_internal(adapter, format_decompress_info[i].id))) + return FALSE; + + format->flags[WINED3D_GL_RES_TYPE_TEX_3D] |= WINED3DFMT_FLAG_DECOMPRESS; + format->decompress = format_decompress_info[i].decompress; + } + + return TRUE; +} + +static BOOL init_srgb_formats(struct wined3d_adapter *adapter) +{ + struct wined3d_format *format, *srgb_format; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(format_srgb_info); ++i) + { + if (!(srgb_format = get_format_internal(adapter, format_srgb_info[i].srgb_format_id))) + return FALSE; + if (!(format = get_format_internal(adapter, format_srgb_info[i].base_format_id))) + return FALSE; + + copy_format(adapter, srgb_format, format); + } + + return TRUE; +} + +static GLenum wined3d_gl_type_to_enum(enum wined3d_gl_resource_type type) +{ + switch (type) + { + case WINED3D_GL_RES_TYPE_TEX_1D: + return GL_TEXTURE_1D; + case WINED3D_GL_RES_TYPE_TEX_2D: + return GL_TEXTURE_2D; + case WINED3D_GL_RES_TYPE_TEX_3D: + return GL_TEXTURE_3D; + case WINED3D_GL_RES_TYPE_TEX_CUBE: + return GL_TEXTURE_CUBE_MAP_ARB; + case WINED3D_GL_RES_TYPE_TEX_RECT: + return GL_TEXTURE_RECTANGLE_ARB; + case WINED3D_GL_RES_TYPE_BUFFER: + return GL_TEXTURE_2D; /* TODO: GL_TEXTURE_BUFFER. */ + case WINED3D_GL_RES_TYPE_RB: + return GL_RENDERBUFFER; + case WINED3D_GL_RES_TYPE_COUNT: + break; + } + ERR("Unexpected GL resource type %u.\n", type); + return 0; +} + +static void delete_fbo_attachment(const struct wined3d_gl_info *gl_info, + enum wined3d_gl_resource_type d3d_type, GLuint object) +{ + switch (d3d_type) + { + case WINED3D_GL_RES_TYPE_TEX_1D: + case WINED3D_GL_RES_TYPE_TEX_2D: + case WINED3D_GL_RES_TYPE_TEX_RECT: + case WINED3D_GL_RES_TYPE_TEX_3D: + case WINED3D_GL_RES_TYPE_TEX_CUBE: + gl_info->gl_ops.gl.p_glDeleteTextures(1, &object); + break; + + case WINED3D_GL_RES_TYPE_RB: + gl_info->fbo_ops.glDeleteRenderbuffers(1, &object); + break; + + case WINED3D_GL_RES_TYPE_BUFFER: + case WINED3D_GL_RES_TYPE_COUNT: + break; + } +} + +static void create_and_bind_fbo_attachment(const struct wined3d_gl_info *gl_info, + const struct wined3d_format_gl *format_gl, + enum wined3d_gl_resource_type d3d_type, GLuint *object, GLenum internal) +{ + GLenum format = format_gl->format; + GLenum type = format_gl->type; + GLenum attach_type; + + attach_type = format_gl->f.depth_size ? GL_DEPTH_ATTACHMENT : GL_COLOR_ATTACHMENT0; + switch (d3d_type) + { + case WINED3D_GL_RES_TYPE_TEX_1D: + gl_info->gl_ops.gl.p_glGenTextures(1, object); + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_1D, *object); + gl_info->gl_ops.gl.p_glTexImage1D(GL_TEXTURE_1D, 0, internal, 16, 0, format, type, NULL); + gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + gl_info->fbo_ops.glFramebufferTexture1D(GL_FRAMEBUFFER, attach_type, GL_TEXTURE_1D, *object, 0); + if (format_gl->f.stencil_size) + gl_info->fbo_ops.glFramebufferTexture1D(GL_FRAMEBUFFER, + GL_STENCIL_ATTACHMENT, GL_TEXTURE_1D, *object, 0); + break; + + case WINED3D_GL_RES_TYPE_TEX_2D: + case WINED3D_GL_RES_TYPE_TEX_RECT: + gl_info->gl_ops.gl.p_glGenTextures(1, object); + gl_info->gl_ops.gl.p_glBindTexture(wined3d_gl_type_to_enum(d3d_type), *object); + gl_info->gl_ops.gl.p_glTexImage2D(wined3d_gl_type_to_enum(d3d_type), 0, internal, 16, 16, 0, + format, type, NULL); + gl_info->gl_ops.gl.p_glTexParameteri(wined3d_gl_type_to_enum(d3d_type), GL_TEXTURE_MIN_FILTER, GL_NEAREST); + gl_info->gl_ops.gl.p_glTexParameteri(wined3d_gl_type_to_enum(d3d_type), GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + gl_info->fbo_ops.glFramebufferTexture2D(GL_FRAMEBUFFER, attach_type, + wined3d_gl_type_to_enum(d3d_type), *object, 0); + if (format_gl->f.stencil_size) + gl_info->fbo_ops.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, + wined3d_gl_type_to_enum(d3d_type), *object, 0); + break; + + case WINED3D_GL_RES_TYPE_TEX_3D: + gl_info->gl_ops.gl.p_glGenTextures(1, object); + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_3D, *object); + GL_EXTCALL(glTexImage3D(GL_TEXTURE_3D, 0, internal, 16, 16, 16, 0, format, type, NULL)); + gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + gl_info->fbo_ops.glFramebufferTexture3D(GL_FRAMEBUFFER, attach_type, GL_TEXTURE_3D, *object, 0, 0); + if (format_gl->f.stencil_size) + gl_info->fbo_ops.glFramebufferTexture3D(GL_FRAMEBUFFER, + GL_STENCIL_ATTACHMENT, GL_TEXTURE_3D, *object, 0, 0); + break; + + case WINED3D_GL_RES_TYPE_TEX_CUBE: + gl_info->gl_ops.gl.p_glGenTextures(1, object); + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, *object); + gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB, 0, internal, 16, 16, 0, + format, type, NULL); + gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB, 0, internal, 16, 16, 0, + format, type, NULL); + gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB, 0, internal, 16, 16, 0, + format, type, NULL); + gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB, 0, internal, 16, 16, 0, + format, type, NULL); + gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB, 0, internal, 16, 16, 0, + format, type, NULL); + gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB, 0, internal, 16, 16, 0, + format, type, NULL); + gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + gl_info->fbo_ops.glFramebufferTexture2D(GL_FRAMEBUFFER, attach_type, + GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB, *object, 0); + if (format_gl->f.stencil_size) + gl_info->fbo_ops.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, + GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB, *object, 0); + break; + + case WINED3D_GL_RES_TYPE_RB: + gl_info->fbo_ops.glGenRenderbuffers(1, object); + gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, *object); + gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, internal, 16, 16); + gl_info->fbo_ops.glFramebufferRenderbuffer(GL_FRAMEBUFFER, attach_type, GL_RENDERBUFFER, *object); + if (format_gl->f.stencil_size) + gl_info->fbo_ops.glFramebufferRenderbuffer(GL_FRAMEBUFFER, + GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, *object); + break; + + case WINED3D_GL_RES_TYPE_BUFFER: + case WINED3D_GL_RES_TYPE_COUNT: + break; + } + + /* Ideally we'd skip all formats already known not to work on textures + * by checking for WINED3DFMT_FLAG_TEXTURE here. However, we want to + * know if we can attach WINED3DFMT_P8_UINT textures to FBOs, and this + * format never has WINED3DFMT_FLAG_TEXTURE set. Instead, swallow GL + * errors generated by invalid formats. */ + while (gl_info->gl_ops.gl.p_glGetError()); +} + +static void draw_test_quad(struct wined3d_caps_gl_ctx *ctx, const struct wined3d_vec3 *geometry, + const struct wined3d_color *color) +{ + const struct wined3d_gl_info *gl_info = ctx->gl_info; + static const struct wined3d_vec3 default_geometry[] = + { + {-1.0f, -1.0f, 0.0f}, + { 1.0f, -1.0f, 0.0f}, + {-1.0f, 1.0f, 0.0f}, + { 1.0f, 1.0f, 0.0f}, + }; + static const char vs_core_header[] = + "#version 150\n" + "in vec4 pos;\n" + "in vec4 color;\n" + "out vec4 out_color;\n" + "\n"; + static const char vs_legacy_header[] = + "#version 120\n" + "attribute vec4 pos;\n" + "attribute vec4 color;\n" + "varying vec4 out_color;\n" + "\n"; + static const char vs_body[] = + "void main()\n" + "{\n" + " gl_Position = pos;\n" + " out_color = color;\n" + "}\n"; + static const char fs_core[] = + "#version 150\n" + "in vec4 out_color;\n" + "out vec4 fragment_color;\n" + "\n" + "void main()\n" + "{\n" + " fragment_color = out_color;\n" + "}\n"; + static const char fs_legacy[] = + "#version 120\n" + "varying vec4 out_color;\n" + "\n" + "void main()\n" + "{\n" + " gl_FragData[0] = out_color;\n" + "}\n"; + const char *source[2]; + GLuint vs_id, fs_id; + unsigned int i; + + if (!geometry) + geometry = default_geometry; + + if (!gl_info->supported[ARB_VERTEX_BUFFER_OBJECT] || !gl_info->supported[ARB_VERTEX_SHADER] + || !gl_info->supported[ARB_FRAGMENT_SHADER]) + { + gl_info->gl_ops.gl.p_glDisable(GL_LIGHTING); + gl_info->gl_ops.gl.p_glMatrixMode(GL_MODELVIEW); + gl_info->gl_ops.gl.p_glLoadIdentity(); + gl_info->gl_ops.gl.p_glMatrixMode(GL_PROJECTION); + gl_info->gl_ops.gl.p_glLoadIdentity(); + + gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP); + gl_info->gl_ops.gl.p_glColor4f(color->r, color->g, color->b, color->a); + for (i = 0; i < 4; ++i) + gl_info->gl_ops.gl.p_glVertex3fv(&geometry[i].x); + gl_info->gl_ops.gl.p_glEnd(); + checkGLcall("draw quad"); + return; + } + + if (!ctx->test_vbo) + GL_EXTCALL(glGenBuffers(1, &ctx->test_vbo)); + GL_EXTCALL(glBindBuffer(GL_ARRAY_BUFFER, ctx->test_vbo)); + GL_EXTCALL(glBufferData(GL_ARRAY_BUFFER, sizeof(struct wined3d_vec3) * 4, geometry, GL_STREAM_DRAW)); + GL_EXTCALL(glVertexAttribPointer(0, 3, GL_FLOAT, FALSE, 0, NULL)); + GL_EXTCALL(glVertexAttrib4f(1, color->r, color->g, color->b, color->a)); + GL_EXTCALL(glEnableVertexAttribArray(0)); + GL_EXTCALL(glDisableVertexAttribArray(1)); + + if (!ctx->test_program_id) + { + BOOL use_glsl_150 = gl_info->glsl_version >= MAKEDWORD_VERSION(1, 50); + + ctx->test_program_id = GL_EXTCALL(glCreateProgram()); + + vs_id = GL_EXTCALL(glCreateShader(GL_VERTEX_SHADER)); + source[0] = use_glsl_150 ? vs_core_header : vs_legacy_header; + source[1] = vs_body; + GL_EXTCALL(glShaderSource(vs_id, 2, source, NULL)); + GL_EXTCALL(glAttachShader(ctx->test_program_id, vs_id)); + GL_EXTCALL(glDeleteShader(vs_id)); + + fs_id = GL_EXTCALL(glCreateShader(GL_FRAGMENT_SHADER)); + source[0] = use_glsl_150 ? fs_core : fs_legacy; + GL_EXTCALL(glShaderSource(fs_id, 1, source, NULL)); + GL_EXTCALL(glAttachShader(ctx->test_program_id, fs_id)); + GL_EXTCALL(glDeleteShader(fs_id)); + + GL_EXTCALL(glBindAttribLocation(ctx->test_program_id, 0, "pos")); + GL_EXTCALL(glBindAttribLocation(ctx->test_program_id, 1, "color")); + + if (use_glsl_150) + GL_EXTCALL(glBindFragDataLocation(ctx->test_program_id, 0, "fragment_color")); + + GL_EXTCALL(glCompileShader(vs_id)); + print_glsl_info_log(gl_info, vs_id, FALSE); + GL_EXTCALL(glCompileShader(fs_id)); + print_glsl_info_log(gl_info, fs_id, FALSE); + GL_EXTCALL(glLinkProgram(ctx->test_program_id)); + shader_glsl_validate_link(gl_info, ctx->test_program_id); + } + GL_EXTCALL(glUseProgram(ctx->test_program_id)); + + gl_info->gl_ops.gl.p_glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + GL_EXTCALL(glUseProgram(0)); + GL_EXTCALL(glDisableVertexAttribArray(0)); + GL_EXTCALL(glBindBuffer(GL_ARRAY_BUFFER, 0)); + checkGLcall("draw quad"); +} + +/* Context activation is done by the caller. */ +static void check_fbo_compat(struct wined3d_caps_gl_ctx *ctx, struct wined3d_format_gl *format) +{ + /* Check if the default internal format is supported as a frame buffer + * target, otherwise fall back to the render target internal. + * + * Try to stick to the standard format if possible, this limits precision differences. */ + static const struct wined3d_color black = {0.0f, 0.0f, 0.0f, 1.0f}; + static const struct wined3d_color half_transparent_red = {1.0f, 0.0f, 0.0f, 0.5f}; + const struct wined3d_gl_info *gl_info = ctx->gl_info; + GLenum status, rt_internal = format->rt_internal; + GLuint object, color_rb; + enum wined3d_gl_resource_type type; + BOOL fallback_fmt_used = FALSE, regular_fmt_used = FALSE; + + gl_info->gl_ops.gl.p_glDisable(GL_BLEND); + + for (type = 0; type < ARRAY_SIZE(format->f.flags); ++type) + { + const char *type_string = "color"; + + if (type == WINED3D_GL_RES_TYPE_BUFFER) + continue; + + create_and_bind_fbo_attachment(gl_info, format, type, &object, format->internal); + + if (format->f.flags[type] & WINED3DFMT_FLAG_DEPTH_STENCIL) + { + gl_info->fbo_ops.glGenRenderbuffers(1, &color_rb); + gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, color_rb); + if (type == WINED3D_GL_RES_TYPE_TEX_1D) + gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, 16, 1); + else + gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, 16, 16); + + gl_info->fbo_ops.glFramebufferRenderbuffer(GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, color_rb); + checkGLcall("Create and attach color rb attachment"); + type_string = "depth / stencil"; + } + + status = gl_info->fbo_ops.glCheckFramebufferStatus(GL_FRAMEBUFFER); + checkGLcall("Framebuffer format check"); + + if (status == GL_FRAMEBUFFER_COMPLETE) + { + TRACE("Format %s is supported as FBO %s attachment, type %u.\n", + debug_d3dformat(format->f.id), type_string, type); + format->f.flags[type] |= WINED3DFMT_FLAG_FBO_ATTACHABLE; + format->rt_internal = format->internal; + regular_fmt_used = TRUE; + } + else + { + if (!rt_internal) + { + if (format->f.flags[type] & (WINED3DFMT_FLAG_RENDERTARGET | WINED3DFMT_FLAG_DEPTH_STENCIL)) + { + WARN("Format %s with rendertarget flag is not supported as FBO color attachment (type %u)," + " and no fallback specified.\n", debug_d3dformat(format->f.id), type); + format->f.flags[type] &= ~(WINED3DFMT_FLAG_RENDERTARGET | WINED3DFMT_FLAG_DEPTH_STENCIL); + } + else + { + TRACE("Format %s is not supported as FBO %s attachment, type %u.\n", + debug_d3dformat(format->f.id), type_string, type); + } + format->rt_internal = format->internal; + } + else + { + TRACE("Format %s is not supported as FBO %s attachment (type %u)," + " trying rtInternal format as fallback.\n", + debug_d3dformat(format->f.id), type_string, type); + + while (gl_info->gl_ops.gl.p_glGetError()); + + delete_fbo_attachment(gl_info, type, object); + create_and_bind_fbo_attachment(gl_info, format, type, &object, format->rt_internal); + + status = gl_info->fbo_ops.glCheckFramebufferStatus(GL_FRAMEBUFFER); + checkGLcall("Framebuffer format check"); + + if (status == GL_FRAMEBUFFER_COMPLETE) + { + TRACE("Format %s rtInternal format is supported as FBO %s attachment, type %u.\n", + debug_d3dformat(format->f.id), type_string, type); + fallback_fmt_used = TRUE; + } + else + { + WARN("Format %s rtInternal format is not supported as FBO %s attachment, type %u.\n", + debug_d3dformat(format->f.id), type_string, type); + format->f.flags[type] &= ~(WINED3DFMT_FLAG_RENDERTARGET | WINED3DFMT_FLAG_DEPTH_STENCIL); + } + } + } + + if (status == GL_FRAMEBUFFER_COMPLETE + && ((format->f.flags[type] & WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING) + || !(gl_info->quirks & WINED3D_QUIRK_LIMITED_TEX_FILTERING)) + && !(format->f.flags[type] & WINED3DFMT_FLAG_INTEGER) + && format->f.id != WINED3DFMT_NULL && format->f.id != WINED3DFMT_P8_UINT + && format->format != GL_LUMINANCE && format->format != GL_LUMINANCE_ALPHA + && (format->f.red_size || format->f.alpha_size)) + { + DWORD readback[16 * 16 * 16], color = 0, r_range, a_range; + BYTE r, a; + BOOL match = TRUE; + GLuint rb; + + if (gl_info->supported[EXT_PACKED_DEPTH_STENCIL]) + { + gl_info->fbo_ops.glGenRenderbuffers(1, &rb); + gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, rb); + if (type == WINED3D_GL_RES_TYPE_TEX_1D) + gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 16, 1); + else + gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 16, 16); + gl_info->fbo_ops.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rb); + gl_info->fbo_ops.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rb); + checkGLcall("RB attachment"); + } + + gl_info->gl_ops.gl.p_glEnable(GL_BLEND); + gl_info->gl_ops.gl.p_glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + gl_info->gl_ops.gl.p_glClear(GL_COLOR_BUFFER_BIT); + if (gl_info->gl_ops.gl.p_glGetError() == GL_INVALID_FRAMEBUFFER_OPERATION) + { + while (gl_info->gl_ops.gl.p_glGetError()); + TRACE("Format %s doesn't support post-pixelshader blending, type %u.\n", + debug_d3dformat(format->f.id), type); + format->f.flags[type] &= ~WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING; + } + else + { + gl_info->gl_ops.gl.p_glDisable(GL_BLEND); + if (type == WINED3D_GL_RES_TYPE_TEX_1D) + gl_info->gl_ops.gl.p_glViewport(0, 0, 16, 1); + else + gl_info->gl_ops.gl.p_glViewport(0, 0, 16, 16); + gl_info->gl_ops.gl.p_glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + draw_test_quad(ctx, NULL, &black); + + gl_info->gl_ops.gl.p_glEnable(GL_BLEND); + + draw_test_quad(ctx, NULL, &half_transparent_red); + + gl_info->gl_ops.gl.p_glDisable(GL_BLEND); + + switch (type) + { + case WINED3D_GL_RES_TYPE_TEX_1D: + /* Rebinding texture to workaround a fglrx bug. */ + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_1D, object); + gl_info->gl_ops.gl.p_glGetTexImage(GL_TEXTURE_1D, 0, GL_BGRA, + GL_UNSIGNED_INT_8_8_8_8_REV, readback); + color = readback[7]; + break; + + case WINED3D_GL_RES_TYPE_TEX_2D: + case WINED3D_GL_RES_TYPE_TEX_3D: + case WINED3D_GL_RES_TYPE_TEX_RECT: + /* Rebinding texture to workaround a fglrx bug. */ + gl_info->gl_ops.gl.p_glBindTexture(wined3d_gl_type_to_enum(type), object); + gl_info->gl_ops.gl.p_glGetTexImage(wined3d_gl_type_to_enum(type), 0, GL_BGRA, + GL_UNSIGNED_INT_8_8_8_8_REV, readback); + color = readback[7 * 16 + 7]; + break; + + case WINED3D_GL_RES_TYPE_TEX_CUBE: + /* Rebinding texture to workaround a fglrx bug. */ + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, object); + gl_info->gl_ops.gl.p_glGetTexImage(GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB, 0, GL_BGRA, + GL_UNSIGNED_INT_8_8_8_8_REV, readback); + color = readback[7 * 16 + 7]; + break; + + case WINED3D_GL_RES_TYPE_RB: + gl_info->gl_ops.gl.p_glReadPixels(0, 0, 16, 16, + GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, readback); + color = readback[7 * 16 + 7]; + break; + + case WINED3D_GL_RES_TYPE_BUFFER: + case WINED3D_GL_RES_TYPE_COUNT: + color = 0; + break; + } + checkGLcall("Post-pixelshader blending check"); + + a = color >> 24; + r = (color & 0x00ff0000u) >> 16; + + r_range = format->f.red_size < 8 ? 1u << (8 - format->f.red_size) : 1; + a_range = format->f.alpha_size < 8 ? 1u << (8 - format->f.alpha_size) : 1; + if (format->f.red_size && (r < 0x7f - r_range || r > 0x7f + r_range)) + match = FALSE; + else if (format->f.alpha_size > 1 && (a < 0xbf - a_range || a > 0xbf + a_range)) + match = FALSE; + if (!match) + { + TRACE("Format %s doesn't support post-pixelshader blending, type %u.\n", + debug_d3dformat(format->f.id), type); + TRACE("Color output: %#x\n", color); + format->f.flags[type] &= ~WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING; + } + else + { + TRACE("Format %s supports post-pixelshader blending, type %u.\n", + debug_d3dformat(format->f.id), type); + TRACE("Color output: %#x\n", color); + format->f.flags[type] |= WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING; + } + } + + if (gl_info->supported[EXT_PACKED_DEPTH_STENCIL]) + { + gl_info->fbo_ops.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0); + gl_info->fbo_ops.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0); + gl_info->fbo_ops.glDeleteRenderbuffers(1, &rb); + checkGLcall("RB cleanup"); + } + } + + if (format->internal != format->srgb_internal) + { + delete_fbo_attachment(gl_info, type, object); + create_and_bind_fbo_attachment(gl_info, format, type, &object, format->srgb_internal); + + status = gl_info->fbo_ops.glCheckFramebufferStatus(GL_FRAMEBUFFER); + checkGLcall("Framebuffer format check"); + + if (status == GL_FRAMEBUFFER_COMPLETE) + { + TRACE("Format %s's sRGB format is FBO attachable, type %u.\n", + debug_d3dformat(format->f.id), type); + format->f.flags[type] |= WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB; + if (gl_info->supported[EXT_TEXTURE_SRGB_DECODE]) + format->internal = format->srgb_internal; + } + else + { + WARN("Format %s's sRGB format is not FBO attachable, type %u.\n", + debug_d3dformat(format->f.id), type); + format_clear_flag(&format->f, WINED3DFMT_FLAG_SRGB_WRITE); + } + } + else if (status == GL_FRAMEBUFFER_COMPLETE) + format->f.flags[type] |= WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB; + + if (format->f.flags[type] & WINED3DFMT_FLAG_DEPTH_STENCIL) + { + gl_info->fbo_ops.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, 0); + gl_info->fbo_ops.glDeleteRenderbuffers(1, &color_rb); + } + + delete_fbo_attachment(gl_info, type, object); + checkGLcall("Framebuffer format check cleanup"); + } + + if (fallback_fmt_used && regular_fmt_used) + { + FIXME("Format %s needs different render target formats for different resource types.\n", + debug_d3dformat(format->f.id)); + format_clear_flag(&format->f, WINED3DFMT_FLAG_RENDERTARGET | WINED3DFMT_FLAG_DEPTH_STENCIL + | WINED3DFMT_FLAG_FBO_ATTACHABLE | WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB + | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING); + } +} + +static void query_format_flag(struct wined3d_gl_info *gl_info, struct wined3d_format_gl *format, + GLint internal, GLenum pname, DWORD flag, const char *string) +{ + GLint value; + enum wined3d_gl_resource_type type; + + for (type = 0; type < ARRAY_SIZE(format->f.flags); ++type) + { + gl_info->gl_ops.ext.p_glGetInternalformativ(wined3d_gl_type_to_enum(type), internal, pname, 1, &value); + if (value == GL_FULL_SUPPORT) + { + TRACE("Format %s supports %s, resource type %u.\n", debug_d3dformat(format->f.id), string, type); + format->f.flags[type] |= flag; + } + else + { + TRACE("Format %s doesn't support %s, resource type %u.\n", debug_d3dformat(format->f.id), string, type); + format->f.flags[type] &= ~flag; + } + } +} + +/* Context activation is done by the caller. */ +static void init_format_fbo_compat_info(const struct wined3d_adapter *adapter, + struct wined3d_caps_gl_ctx *ctx) +{ + const struct wined3d_gl_info *gl_info = ctx->gl_info; + unsigned int i, type; + GLuint fbo; + + if (gl_info->supported[ARB_INTERNALFORMAT_QUERY2]) + { + for (i = 0; i < WINED3D_FORMAT_COUNT; ++i) + { + struct wined3d_format_gl *format = get_format_gl_by_idx(adapter, i); + BOOL fallback_fmt_used = FALSE, regular_fmt_used = FALSE; + GLenum rt_internal = format->rt_internal; + GLint value; + + if (!format->internal) + continue; + + for (type = 0; type < ARRAY_SIZE(format->f.flags); ++type) + { + gl_info->gl_ops.ext.p_glGetInternalformativ(wined3d_gl_type_to_enum(type), + format->internal, GL_FRAMEBUFFER_RENDERABLE, 1, &value); + if (value == GL_FULL_SUPPORT) + { + TRACE("Format %s is supported as FBO color attachment, resource type %u.\n", + debug_d3dformat(format->f.id), type); + format->f.flags[type] |= WINED3DFMT_FLAG_FBO_ATTACHABLE; + format->rt_internal = format->internal; + regular_fmt_used = TRUE; + + gl_info->gl_ops.ext.p_glGetInternalformativ(wined3d_gl_type_to_enum(type), + format->internal, GL_FRAMEBUFFER_BLEND, 1, &value); + if (value == GL_FULL_SUPPORT) + { + TRACE("Format %s supports post-pixelshader blending, resource type %u.\n", + debug_d3dformat(format->f.id), type); + format->f.flags[type] |= WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING; + } + else + { + TRACE("Format %s doesn't support post-pixelshader blending, resource typed %u.\n", + debug_d3dformat(format->f.id), type); + format->f.flags[type] &= ~WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING; + } + } + else + { + if (!rt_internal) + { + if (format->f.flags[type] & WINED3DFMT_FLAG_RENDERTARGET) + { + WARN("Format %s with rendertarget flag is not supported as FBO color attachment" + " and no fallback specified, resource type %u.\n", + debug_d3dformat(format->f.id), type); + format->f.flags[type] &= ~WINED3DFMT_FLAG_RENDERTARGET; + } + else + TRACE("Format %s is not supported as FBO color attachment," + " resource type %u.\n", debug_d3dformat(format->f.id), type); + format->rt_internal = format->internal; + } + else + { + gl_info->gl_ops.ext.p_glGetInternalformativ(wined3d_gl_type_to_enum(type), + rt_internal, GL_FRAMEBUFFER_RENDERABLE, 1, &value); + if (value == GL_FULL_SUPPORT) + { + TRACE("Format %s rtInternal format is supported as FBO color attachment," + " resource type %u.\n", debug_d3dformat(format->f.id), type); + fallback_fmt_used = TRUE; + } + else + { + WARN("Format %s rtInternal format is not supported as FBO color attachment," + " resource type %u.\n", debug_d3dformat(format->f.id), type); + format->f.flags[type] &= ~WINED3DFMT_FLAG_RENDERTARGET; + } + } + } + + if (format->internal != format->srgb_internal) + { + gl_info->gl_ops.ext.p_glGetInternalformativ(wined3d_gl_type_to_enum(type), + format->srgb_internal, GL_FRAMEBUFFER_RENDERABLE, 1, &value); + if (value == GL_FULL_SUPPORT) + { + TRACE("Format %s's sRGB format is FBO attachable, resource type %u.\n", + debug_d3dformat(format->f.id), type); + format->f.flags[type] |= WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB; + if (gl_info->supported[EXT_TEXTURE_SRGB_DECODE]) + format->internal = format->srgb_internal; + } + else + { + WARN("Format %s's sRGB format is not FBO attachable, resource type %u.\n", + debug_d3dformat(format->f.id), type); + format_clear_flag(&format->f, WINED3DFMT_FLAG_SRGB_WRITE); + } + } + else if (format->f.flags[type] & WINED3DFMT_FLAG_FBO_ATTACHABLE) + format->f.flags[type] |= WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB; + } + + if (fallback_fmt_used && regular_fmt_used) + { + FIXME("Format %s needs different render target formats for different resource types.\n", + debug_d3dformat(format->f.id)); + format_clear_flag(&format->f, WINED3DFMT_FLAG_RENDERTARGET | WINED3DFMT_FLAG_FBO_ATTACHABLE + | WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING); + } + } + return; + } + + if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) + { + gl_info->fbo_ops.glGenFramebuffers(1, &fbo); + gl_info->fbo_ops.glBindFramebuffer(GL_FRAMEBUFFER, fbo); + gl_info->gl_ops.gl.p_glDrawBuffer(GL_COLOR_ATTACHMENT0); + gl_info->gl_ops.gl.p_glReadBuffer(GL_COLOR_ATTACHMENT0); + } + + for (i = 0; i < WINED3D_FORMAT_COUNT; ++i) + { + struct wined3d_format_gl *format = get_format_gl_by_idx(adapter, i); + + if (!format->internal) + continue; + + if (format->f.flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED) + { + TRACE("Skipping format %s because it's a compressed format.\n", + debug_d3dformat(format->f.id)); + continue; + } + + if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) + { + TRACE("Checking if format %s is supported as FBO color attachment...\n", debug_d3dformat(format->f.id)); + check_fbo_compat(ctx, format); + } + else + { + format->rt_internal = format->internal; + } + } + + if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) + gl_info->fbo_ops.glDeleteFramebuffers(1, &fbo); +} + +static GLenum lookup_gl_view_class(GLenum internal_format) +{ + static const struct + { + GLenum internal_format; + GLenum view_class; + } + view_classes[] = + { + /* 128-bit */ + {GL_RGBA32F, GL_VIEW_CLASS_128_BITS}, + {GL_RGBA32UI, GL_VIEW_CLASS_128_BITS}, + {GL_RGBA32I, GL_VIEW_CLASS_128_BITS}, + /* 96-bit */ + {GL_RGB32F, GL_VIEW_CLASS_96_BITS}, + {GL_RGB32UI, GL_VIEW_CLASS_96_BITS}, + {GL_RGB32I, GL_VIEW_CLASS_96_BITS}, + /* 64-bit */ + {GL_RGBA16F, GL_VIEW_CLASS_64_BITS}, + {GL_RG32F, GL_VIEW_CLASS_64_BITS}, + {GL_RGBA16UI, GL_VIEW_CLASS_64_BITS}, + {GL_RG32UI, GL_VIEW_CLASS_64_BITS}, + {GL_RGBA16I, GL_VIEW_CLASS_64_BITS}, + {GL_RG32I, GL_VIEW_CLASS_64_BITS}, + {GL_RGBA16, GL_VIEW_CLASS_64_BITS}, + {GL_RGBA16_SNORM, GL_VIEW_CLASS_64_BITS}, + /* 48-bit */ + {GL_RGB16, GL_VIEW_CLASS_48_BITS}, + {GL_RGB16_SNORM, GL_VIEW_CLASS_48_BITS}, + {GL_RGB16F, GL_VIEW_CLASS_48_BITS}, + {GL_RGB16UI, GL_VIEW_CLASS_48_BITS}, + {GL_RGB16I, GL_VIEW_CLASS_48_BITS}, + /* 32-bit */ + {GL_RG16F, GL_VIEW_CLASS_32_BITS}, + {GL_R11F_G11F_B10F, GL_VIEW_CLASS_32_BITS}, + {GL_R32F, GL_VIEW_CLASS_32_BITS}, + {GL_RGB10_A2UI, GL_VIEW_CLASS_32_BITS}, + {GL_RGBA8UI, GL_VIEW_CLASS_32_BITS}, + {GL_RG16UI, GL_VIEW_CLASS_32_BITS}, + {GL_R32UI, GL_VIEW_CLASS_32_BITS}, + {GL_RGBA8I, GL_VIEW_CLASS_32_BITS}, + {GL_RG16I, GL_VIEW_CLASS_32_BITS}, + {GL_R32I, GL_VIEW_CLASS_32_BITS}, + {GL_RGB10_A2, GL_VIEW_CLASS_32_BITS}, + {GL_RGBA8, GL_VIEW_CLASS_32_BITS}, + {GL_RG16, GL_VIEW_CLASS_32_BITS}, + {GL_RGBA8_SNORM, GL_VIEW_CLASS_32_BITS}, + {GL_RG16_SNORM, GL_VIEW_CLASS_32_BITS}, + {GL_SRGB8_ALPHA8, GL_VIEW_CLASS_32_BITS}, + {GL_RGB9_E5, GL_VIEW_CLASS_32_BITS}, + /* 24-bit */ + {GL_RGB8, GL_VIEW_CLASS_24_BITS}, + {GL_RGB8_SNORM, GL_VIEW_CLASS_24_BITS}, + {GL_SRGB8, GL_VIEW_CLASS_24_BITS}, + {GL_RGB8UI, GL_VIEW_CLASS_24_BITS}, + {GL_RGB8I, GL_VIEW_CLASS_24_BITS}, + /* 16-bit */ + {GL_R16F, GL_VIEW_CLASS_16_BITS}, + {GL_RG8UI, GL_VIEW_CLASS_16_BITS}, + {GL_R16UI, GL_VIEW_CLASS_16_BITS}, + {GL_RG8I, GL_VIEW_CLASS_16_BITS}, + {GL_R16I, GL_VIEW_CLASS_16_BITS}, + {GL_RG8, GL_VIEW_CLASS_16_BITS}, + {GL_R16, GL_VIEW_CLASS_16_BITS}, + {GL_RG8_SNORM, GL_VIEW_CLASS_16_BITS}, + {GL_R16_SNORM, GL_VIEW_CLASS_16_BITS}, + /* 8-bit */ + {GL_R8UI, GL_VIEW_CLASS_8_BITS}, + {GL_R8I, GL_VIEW_CLASS_8_BITS}, + {GL_R8, GL_VIEW_CLASS_8_BITS}, + {GL_R8_SNORM, GL_VIEW_CLASS_8_BITS}, + + /* RGTC1 */ + {GL_COMPRESSED_RED_RGTC1, GL_VIEW_CLASS_RGTC1_RED}, + {GL_COMPRESSED_SIGNED_RED_RGTC1, GL_VIEW_CLASS_RGTC1_RED}, + /* RGTC2 */ + {GL_COMPRESSED_RG_RGTC2, GL_VIEW_CLASS_RGTC2_RG}, + {GL_COMPRESSED_SIGNED_RG_RGTC2, GL_VIEW_CLASS_RGTC2_RG}, + + /* BPTC unorm */ + {GL_COMPRESSED_RGBA_BPTC_UNORM, GL_VIEW_CLASS_BPTC_UNORM}, + {GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM, GL_VIEW_CLASS_BPTC_UNORM}, + /* BPTC float */ + {GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT, GL_VIEW_CLASS_BPTC_FLOAT}, + {GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT, GL_VIEW_CLASS_BPTC_FLOAT}, + + /* DXT1 RGB */ + {GL_COMPRESSED_RGB_S3TC_DXT1_EXT, GL_VIEW_CLASS_S3TC_DXT1_RGB}, + {GL_COMPRESSED_SRGB_S3TC_DXT1_EXT, GL_VIEW_CLASS_S3TC_DXT1_RGB}, + /* DXT1 RGBA */ + {GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, GL_VIEW_CLASS_S3TC_DXT1_RGBA}, + {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT, GL_VIEW_CLASS_S3TC_DXT1_RGBA}, + /* DXT3 */ + {GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_VIEW_CLASS_S3TC_DXT3_RGBA}, + {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT, GL_VIEW_CLASS_S3TC_DXT3_RGBA}, + /* DXT5 */ + {GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_VIEW_CLASS_S3TC_DXT5_RGBA}, + {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, GL_VIEW_CLASS_S3TC_DXT5_RGBA}, + }; + + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(view_classes); ++i) + { + if (view_classes[i].internal_format == internal_format) + return view_classes[i].view_class; + } + + return GL_NONE; +} + +static void query_view_class(struct wined3d_format_gl *format) +{ + GLenum internal_view_class, gamma_view_class, rt_view_class; + + internal_view_class = lookup_gl_view_class(format->internal); + gamma_view_class = lookup_gl_view_class(format->srgb_internal); + rt_view_class = lookup_gl_view_class(format->rt_internal); + + if (internal_view_class == gamma_view_class || gamma_view_class == rt_view_class) + { + format->view_class = internal_view_class; + TRACE("Format %s is member of GL view class %#x.\n", + debug_d3dformat(format->f.id), format->view_class); + } + else + { + format->view_class = GL_NONE; + } +} + +static void query_internal_format(struct wined3d_adapter *adapter, + struct wined3d_format_gl *format, const struct wined3d_format_texture_info *texture_info, + struct wined3d_gl_info *gl_info, BOOL srgb_write_supported, BOOL srgb_format) +{ + GLint count, multisample_types[8]; + unsigned int i, max_log2; + GLenum target; + + if (gl_info->supported[ARB_INTERNALFORMAT_QUERY2]) + { + query_format_flag(gl_info, format, format->internal, GL_VERTEX_TEXTURE, + WINED3DFMT_FLAG_VTF, "vertex texture usage"); + query_format_flag(gl_info, format, format->internal, GL_FILTER, + WINED3DFMT_FLAG_FILTERING, "filtering"); + query_format_flag(gl_info, format, format->internal, GL_SHADER_IMAGE_STORE, + WINED3DFMT_FLAG_UNORDERED_ACCESS, "unordered access"); + + if (srgb_format || format->srgb_internal != format->internal) + { + query_format_flag(gl_info, format, format->srgb_internal, GL_SRGB_READ, + WINED3DFMT_FLAG_SRGB_READ, "sRGB read"); + + if (srgb_write_supported) + query_format_flag(gl_info, format, format->srgb_internal, GL_SRGB_WRITE, + WINED3DFMT_FLAG_SRGB_WRITE, "sRGB write"); + else + format_clear_flag(&format->f, WINED3DFMT_FLAG_SRGB_WRITE); + + if (!(format->f.flags[WINED3D_GL_RES_TYPE_TEX_2D] + & (WINED3DFMT_FLAG_SRGB_READ | WINED3DFMT_FLAG_SRGB_WRITE))) + format->srgb_internal = format->internal; + else if (gl_info->supported[EXT_TEXTURE_SRGB_DECODE]) + format->internal = format->srgb_internal; + } + } + else + { + if (!gl_info->limits.samplers[WINED3D_SHADER_TYPE_VERTEX]) + format_clear_flag(&format->f, WINED3DFMT_FLAG_VTF); + + if (!(gl_info->quirks & WINED3D_QUIRK_LIMITED_TEX_FILTERING)) + format_set_flag(&format->f, WINED3DFMT_FLAG_FILTERING); + else if (format->f.id != WINED3DFMT_R32G32B32A32_FLOAT && format->f.id != WINED3DFMT_R32_FLOAT) + format_clear_flag(&format->f, WINED3DFMT_FLAG_VTF); + + if (srgb_format || format->srgb_internal != format->internal) + { + /* Filter sRGB capabilities if EXT_texture_sRGB is not supported. */ + if (!gl_info->supported[EXT_TEXTURE_SRGB]) + { + format->srgb_internal = format->internal; + format_clear_flag(&format->f, WINED3DFMT_FLAG_SRGB_READ | WINED3DFMT_FLAG_SRGB_WRITE); + } + else if (gl_info->supported[EXT_TEXTURE_SRGB_DECODE]) + { + format->internal = format->srgb_internal; + } + } + + if ((format->f.flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_SRGB_WRITE) && !srgb_write_supported) + format_clear_flag(&format->f, WINED3DFMT_FLAG_SRGB_WRITE); + } + + if ((!gl_info->supported[ARB_DEPTH_TEXTURE] || wined3d_settings.offscreen_rendering_mode != ORM_FBO) + && (format->f.depth_size || format->f.stencil_size)) + { + TRACE("Disabling texturing support for depth / stencil format %s.\n", debug_d3dformat(format->f.id)); + format->f.flags[WINED3D_GL_RES_TYPE_TEX_1D] &= ~WINED3DFMT_FLAG_TEXTURE; + format->f.flags[WINED3D_GL_RES_TYPE_TEX_2D] &= ~WINED3DFMT_FLAG_TEXTURE; + format->f.flags[WINED3D_GL_RES_TYPE_TEX_3D] &= ~WINED3DFMT_FLAG_TEXTURE; + format->f.flags[WINED3D_GL_RES_TYPE_TEX_CUBE] &= ~WINED3DFMT_FLAG_TEXTURE; + format->f.flags[WINED3D_GL_RES_TYPE_TEX_RECT] &= ~WINED3DFMT_FLAG_TEXTURE; + } + + query_view_class(format); + + if (format->internal && format->f.flags[WINED3D_GL_RES_TYPE_RB] + & (WINED3DFMT_FLAG_RENDERTARGET | WINED3DFMT_FLAG_DEPTH_STENCIL)) + { + if (gl_info->supported[ARB_INTERNALFORMAT_QUERY]) + { + target = gl_info->supported[ARB_TEXTURE_MULTISAMPLE] ? GL_TEXTURE_2D_MULTISAMPLE : GL_RENDERBUFFER; + count = 0; + GL_EXTCALL(glGetInternalformativ(target, format->internal, + GL_NUM_SAMPLE_COUNTS, 1, &count)); + if (count > ARRAY_SIZE(multisample_types)) + FIXME("Unexpectedly high number of multisample types %d.\n", count); + count = min(count, ARRAY_SIZE(multisample_types)); + GL_EXTCALL(glGetInternalformativ(target, format->internal, + GL_SAMPLES, count, multisample_types)); + checkGLcall("query sample counts"); + for (i = 0; i < count; ++i) + { + if (multisample_types[i] > sizeof(format->f.multisample_types) * CHAR_BIT) + continue; + format->f.multisample_types |= 1u << (multisample_types[i] - 1); + } + } + else + { + max_log2 = wined3d_log2i(min(gl_info->limits.samples, + sizeof(format->f.multisample_types) * CHAR_BIT)); + for (i = 1; i <= max_log2; ++i) + format->f.multisample_types |= 1u << ((1u << i) - 1); + } + } +} + +static BOOL init_format_texture_info(struct wined3d_adapter *adapter, struct wined3d_gl_info *gl_info) +{ + struct wined3d_format_gl *format, *srgb_format; + struct fragment_caps fragment_caps; + struct shader_caps shader_caps; + unsigned int i, j; + BOOL srgb_write; + + adapter->fragment_pipe->get_caps(adapter, &fragment_caps); + adapter->shader_backend->shader_get_caps(adapter, &shader_caps); + srgb_write = (fragment_caps.wined3d_caps & WINED3D_FRAGMENT_CAP_SRGB_WRITE) + && (shader_caps.wined3d_caps & WINED3D_SHADER_CAP_SRGB_WRITE); + + for (i = 0; i < ARRAY_SIZE(format_texture_info); ++i) + { + if (!(format = get_format_gl_internal(adapter, format_texture_info[i].id))) + return FALSE; + + if (!gl_info->supported[format_texture_info[i].extension]) + continue; + + /* ARB_texture_rg defines floating point formats, but only if + * ARB_texture_float is also supported. */ + if (!gl_info->supported[ARB_TEXTURE_FLOAT] + && (format->f.flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FLOAT)) + continue; + + /* ARB_texture_rg defines integer formats if EXT_texture_integer is also supported. */ + if (!gl_info->supported[EXT_TEXTURE_INTEGER] + && (format->f.flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_INTEGER)) + continue; + + format->internal = format_texture_info[i].gl_internal; + format->srgb_internal = format_texture_info[i].gl_srgb_internal; + format->rt_internal = format_texture_info[i].gl_rt_internal; + format->format = format_texture_info[i].gl_format; + format->type = format_texture_info[i].gl_type; + format->f.color_fixup = COLOR_FIXUP_IDENTITY; + format->f.height_scale.numerator = 1; + format->f.height_scale.denominator = 1; + + format->f.flags[WINED3D_GL_RES_TYPE_TEX_1D] |= format_texture_info[i].flags | WINED3DFMT_FLAG_BLIT; + format->f.flags[WINED3D_GL_RES_TYPE_TEX_2D] |= format_texture_info[i].flags | WINED3DFMT_FLAG_BLIT; + format->f.flags[WINED3D_GL_RES_TYPE_BUFFER] |= format_texture_info[i].flags | WINED3DFMT_FLAG_BLIT; + + /* GL_ARB_depth_texture does not support 3D textures. It also says "cube textures are + * problematic", but doesn't explicitly mandate that an error is generated. */ + if (gl_info->supported[EXT_TEXTURE3D] && !(format_texture_info[i].flags & WINED3DFMT_FLAG_DEPTH_STENCIL)) + format->f.flags[WINED3D_GL_RES_TYPE_TEX_3D] |= format_texture_info[i].flags | WINED3DFMT_FLAG_BLIT; + + if (gl_info->supported[ARB_TEXTURE_CUBE_MAP]) + format->f.flags[WINED3D_GL_RES_TYPE_TEX_CUBE] |= format_texture_info[i].flags | WINED3DFMT_FLAG_BLIT; + + if (gl_info->supported[ARB_TEXTURE_RECTANGLE]) + format->f.flags[WINED3D_GL_RES_TYPE_TEX_RECT] |= format_texture_info[i].flags | WINED3DFMT_FLAG_BLIT; + + format->f.flags[WINED3D_GL_RES_TYPE_RB] |= format_texture_info[i].flags | WINED3DFMT_FLAG_BLIT; + format->f.flags[WINED3D_GL_RES_TYPE_RB] &= ~WINED3DFMT_FLAG_TEXTURE; + + if (format->srgb_internal != format->internal + && !(adapter->d3d_info.wined3d_creation_flags & WINED3D_SRGB_READ_WRITE_CONTROL)) + { + format->srgb_internal = format->internal; + format_clear_flag(&format->f, WINED3DFMT_FLAG_SRGB_READ | WINED3DFMT_FLAG_SRGB_WRITE); + } + + if (!gl_info->supported[ARB_SHADOW] && (format->f.flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_SHADOW)) + format_clear_flag(&format->f, WINED3DFMT_FLAG_TEXTURE); + + query_internal_format(adapter, format, &format_texture_info[i], gl_info, srgb_write, FALSE); + + /* Texture conversion stuff */ + format->f.conv_byte_count = format_texture_info[i].conv_byte_count; + format->f.upload = format_texture_info[i].upload; + format->f.download = format_texture_info[i].download; + + srgb_format = NULL; + for (j = 0; j < ARRAY_SIZE(format_srgb_info); ++j) + { + if (format_srgb_info[j].base_format_id == format->f.id) + { + if (!(srgb_format = get_format_gl_internal(adapter, format_srgb_info[j].srgb_format_id))) + return FALSE; + break; + } + } + if (!srgb_format) + continue; + + copy_format(adapter, &srgb_format->f, &format->f); + + if (gl_info->supported[EXT_TEXTURE_SRGB] + && !(adapter->d3d_info.wined3d_creation_flags & WINED3D_SRGB_READ_WRITE_CONTROL)) + { + srgb_format->internal = format_texture_info[i].gl_srgb_internal; + srgb_format->srgb_internal = format_texture_info[i].gl_srgb_internal; + format_set_flag(&srgb_format->f, WINED3DFMT_FLAG_SRGB_READ | WINED3DFMT_FLAG_SRGB_WRITE); + query_internal_format(adapter, srgb_format, &format_texture_info[i], gl_info, srgb_write, TRUE); + } + } + + return TRUE; +} + +static BOOL compare_uint(unsigned int x, unsigned int y, unsigned int max_diff) +{ + unsigned int diff = x > y ? x - y : y - x; + + return diff <= max_diff; +} + +static BOOL compare_colour(DWORD c1, DWORD c2, BYTE max_diff) +{ + return compare_uint(c1 & 0xff, c2 & 0xff, max_diff) + && compare_uint((c1 >> 8) & 0xff, (c2 >> 8) & 0xff, max_diff) + && compare_uint((c1 >> 16) & 0xff, (c2 >> 16) & 0xff, max_diff) + && compare_uint((c1 >> 24) & 0xff, (c2 >> 24) & 0xff, max_diff); +} + +/* A context is provided by the caller */ +static BOOL check_filter(const struct wined3d_gl_info *gl_info, GLenum internal) +{ + static const DWORD data[] = {0x00000000, 0xffffffff}; + GLuint tex, fbo, buffer; + DWORD readback[16 * 1]; + BOOL ret = FALSE; + + /* Render a filtered texture and see what happens. This is intended to detect the lack of + * float16 filtering on ATI X1000 class cards. The drivers disable filtering instead of + * falling back to software. If this changes in the future this code will get fooled and + * apps might hit the software path due to incorrectly advertised caps. + * + * Its unlikely that this changes however. GL Games like Mass Effect depend on the filter + * disable fallback, if Apple or ATI ever change the driver behavior they will break more + * than Wine. The Linux binary <= r500 driver is not maintained any more anyway + */ + + while (gl_info->gl_ops.gl.p_glGetError()); + + gl_info->gl_ops.gl.p_glGenTextures(1, &buffer); + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_2D, buffer); + memset(readback, 0x7e, sizeof(readback)); + gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 16, 1, 0, + GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, readback); + gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + + gl_info->gl_ops.gl.p_glGenTextures(1, &tex); + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_2D, tex); + gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, internal, 2, 1, 0, + GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, data); + gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D); + + gl_info->fbo_ops.glGenFramebuffers(1, &fbo); + gl_info->fbo_ops.glBindFramebuffer(GL_FRAMEBUFFER, fbo); + gl_info->fbo_ops.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, buffer, 0); + gl_info->gl_ops.gl.p_glDrawBuffer(GL_COLOR_ATTACHMENT0); + + gl_info->gl_ops.gl.p_glViewport(0, 0, 16, 1); + gl_info->gl_ops.gl.p_glDisable(GL_LIGHTING); + gl_info->gl_ops.gl.p_glMatrixMode(GL_MODELVIEW); + gl_info->gl_ops.gl.p_glLoadIdentity(); + gl_info->gl_ops.gl.p_glMatrixMode(GL_PROJECTION); + gl_info->gl_ops.gl.p_glLoadIdentity(); + + gl_info->gl_ops.gl.p_glClearColor(0, 1, 0, 0); + gl_info->gl_ops.gl.p_glClear(GL_COLOR_BUFFER_BIT); + + gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP); + gl_info->gl_ops.gl.p_glTexCoord2f(0.0, 0.0); + gl_info->gl_ops.gl.p_glVertex2f(-1.0f, -1.0f); + gl_info->gl_ops.gl.p_glTexCoord2f(1.0, 0.0); + gl_info->gl_ops.gl.p_glVertex2f(1.0f, -1.0f); + gl_info->gl_ops.gl.p_glTexCoord2f(0.0, 1.0); + gl_info->gl_ops.gl.p_glVertex2f(-1.0f, 1.0f); + gl_info->gl_ops.gl.p_glTexCoord2f(1.0, 1.0); + gl_info->gl_ops.gl.p_glVertex2f(1.0f, 1.0f); + gl_info->gl_ops.gl.p_glEnd(); + + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_2D, buffer); + memset(readback, 0x7f, sizeof(readback)); + gl_info->gl_ops.gl.p_glGetTexImage(GL_TEXTURE_2D, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, readback); + if (compare_colour(readback[6], 0xffffffff, 5) || compare_colour(readback[6], 0x00000000, 5) + || compare_colour(readback[9], 0xffffffff, 5) || compare_colour(readback[9], 0x00000000, 5)) + { + TRACE("Read back colors 0x%08x and 0x%08x close to unfiltered color, assuming no filtering\n", + readback[6], readback[9]); + ret = FALSE; + } + else + { + TRACE("Read back colors are 0x%08x and 0x%08x, assuming texture is filtered\n", + readback[6], readback[9]); + ret = TRUE; + } + + gl_info->fbo_ops.glBindFramebuffer(GL_FRAMEBUFFER, 0); + gl_info->fbo_ops.glDeleteFramebuffers(1, &fbo); + gl_info->gl_ops.gl.p_glDeleteTextures(1, &tex); + gl_info->gl_ops.gl.p_glDeleteTextures(1, &buffer); + gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_2D); + + if (gl_info->gl_ops.gl.p_glGetError()) + { + FIXME("Error during filtering test for format %x, returning no filtering\n", internal); + ret = FALSE; + } + + return ret; +} + +static void init_format_filter_info(struct wined3d_adapter *adapter, + struct wined3d_gl_info *gl_info) +{ + enum wined3d_pci_vendor vendor = adapter->driver_info.vendor; + struct wined3d_format_gl *format; + unsigned int i; + static const enum wined3d_format_id fmts16[] = + { + WINED3DFMT_R16_FLOAT, + WINED3DFMT_R16G16_FLOAT, + WINED3DFMT_R16G16B16A16_FLOAT, + }; + BOOL filtered; + + /* This was already handled by init_format_texture_info(). */ + if (gl_info->supported[ARB_INTERNALFORMAT_QUERY2]) + return; + + if (wined3d_settings.offscreen_rendering_mode != ORM_FBO + || !gl_info->supported[WINED3D_GL_LEGACY_CONTEXT]) + { + WARN("No FBO support, or no FBO ORM, guessing filter info from GL caps\n"); + if (vendor == HW_VENDOR_NVIDIA && gl_info->supported[ARB_TEXTURE_FLOAT]) + { + TRACE("Nvidia card with texture_float support: Assuming float16 blending\n"); + filtered = TRUE; + } + else if (gl_info->limits.glsl_varyings > 44) + { + TRACE("More than 44 GLSL varyings - assuming d3d10 card with float16 blending\n"); + filtered = TRUE; + } + else + { + TRACE("Assuming no float16 blending\n"); + filtered = FALSE; + } + + if (filtered) + { + for (i = 0; i < ARRAY_SIZE(fmts16); ++i) + { + format = get_format_gl_internal(adapter, fmts16[i]); + format_set_flag(&format->f, WINED3DFMT_FLAG_FILTERING); + } + } + return; + } + + for (i = 0; i < ARRAY_SIZE(fmts16); ++i) + { + format = get_format_gl_internal(adapter, fmts16[i]); + if (!format->internal) + continue; /* Not supported by GL */ + + filtered = check_filter(gl_info, format->internal); + if (filtered) + { + TRACE("Format %s supports filtering.\n", debug_d3dformat(format->f.id)); + format_set_flag(&format->f, WINED3DFMT_FLAG_FILTERING); + } + else + { + TRACE("Format %s does not support filtering.\n", debug_d3dformat(format->f.id)); + } + } +} + +static enum fixup_channel_source fixup_source_from_char(char c) +{ + switch (c) + { + default: + case '0': + return CHANNEL_SOURCE_ZERO; + case '1': + return CHANNEL_SOURCE_ONE; + case 'x': + case 'X': + return CHANNEL_SOURCE_X; + case 'y': + case 'Y': + return CHANNEL_SOURCE_Y; + case 'z': + case 'Z': + return CHANNEL_SOURCE_Z; + case 'w': + case 'W': + return CHANNEL_SOURCE_W; + } +} + +static unsigned int fixup_sign_from_char(char c) +{ + if (c == 'x' || c == 'y' || c == 'z' || c == 'w') + return 1; + return 0; +} + +static struct color_fixup_desc create_color_fixup_desc_from_string(const char *s) +{ + struct color_fixup_desc fixup; + + if (strlen(s) != 4) + { + ERR("Invalid fixup string %s.\n", wine_dbgstr_a(s)); + return COLOR_FIXUP_IDENTITY; + } + + fixup.x_sign_fixup = fixup_sign_from_char(s[0]); + fixup.x_source = fixup_source_from_char(s[0]); + fixup.y_sign_fixup = fixup_sign_from_char(s[1]); + fixup.y_source = fixup_source_from_char(s[1]); + fixup.z_sign_fixup = fixup_sign_from_char(s[2]); + fixup.z_source = fixup_source_from_char(s[2]); + fixup.w_sign_fixup = fixup_sign_from_char(s[3]); + fixup.w_source = fixup_source_from_char(s[3]); + + return fixup; +} + +static void apply_format_fixups(struct wined3d_adapter *adapter, struct wined3d_gl_info *gl_info) +{ + const struct wined3d_d3d_info *d3d_info = &adapter->d3d_info; + struct wined3d_format_gl *format; + BOOL use_legacy_fixups; + unsigned int i; + + static const struct + { + enum wined3d_format_id id; + const char *fixup; + BOOL legacy; + enum wined3d_gl_extension extension; + } + fixups[] = + { + {WINED3DFMT_R16_FLOAT, "X11W", TRUE, WINED3D_GL_EXT_NONE}, + {WINED3DFMT_R32_FLOAT, "X11W", TRUE, WINED3D_GL_EXT_NONE}, + {WINED3DFMT_R16G16_UNORM, "XY1W", TRUE, WINED3D_GL_EXT_NONE}, + {WINED3DFMT_R16G16_FLOAT, "XY1W", TRUE, WINED3D_GL_EXT_NONE}, + {WINED3DFMT_R32G32_FLOAT, "XY1W", TRUE, WINED3D_GL_EXT_NONE}, + + {WINED3DFMT_R8G8_SNORM, "xy11", FALSE, WINED3D_GL_EXT_NONE}, + {WINED3DFMT_R8G8_SNORM, "XY11", TRUE, NV_TEXTURE_SHADER}, + {WINED3DFMT_R8G8_SNORM, "XY11", TRUE, EXT_TEXTURE_SNORM}, + + {WINED3DFMT_R16G16_SNORM, "xy11", FALSE, WINED3D_GL_EXT_NONE}, + {WINED3DFMT_R16G16_SNORM, "XY11", TRUE, NV_TEXTURE_SHADER}, + {WINED3DFMT_R16G16_SNORM, "XY11", TRUE, EXT_TEXTURE_SNORM}, + + {WINED3DFMT_R8G8B8A8_SNORM, "xyzw", FALSE, WINED3D_GL_EXT_NONE}, + {WINED3DFMT_R8G8B8A8_SNORM, "XYZW", FALSE, NV_TEXTURE_SHADER}, + {WINED3DFMT_R8G8B8A8_SNORM, "XYZW", FALSE, EXT_TEXTURE_SNORM}, + + {WINED3DFMT_R5G5_SNORM_L6_UNORM, "xzY1", FALSE, WINED3D_GL_EXT_NONE}, + {WINED3DFMT_R5G5_SNORM_L6_UNORM, "XYZW", FALSE, NV_TEXTURE_SHADER}, + {WINED3DFMT_R5G5_SNORM_L6_UNORM, "XYZW", FALSE, EXT_TEXTURE_SNORM}, + + {WINED3DFMT_R8G8_SNORM_L8X8_UNORM, "xyZW", FALSE, WINED3D_GL_EXT_NONE}, + {WINED3DFMT_R8G8_SNORM_L8X8_UNORM, "XYZW", FALSE, NV_TEXTURE_SHADER}, + + {WINED3DFMT_ATI1N, "XXXX", FALSE, EXT_TEXTURE_COMPRESSION_RGTC}, + {WINED3DFMT_ATI1N, "XXXX", FALSE, ARB_TEXTURE_COMPRESSION_RGTC}, + + {WINED3DFMT_ATI2N, "XW11", FALSE, ATI_TEXTURE_COMPRESSION_3DC}, + {WINED3DFMT_ATI2N, "YX11", FALSE, EXT_TEXTURE_COMPRESSION_RGTC}, + {WINED3DFMT_ATI2N, "YX11", FALSE, ARB_TEXTURE_COMPRESSION_RGTC}, + + {WINED3DFMT_A8_UNORM, "000X", FALSE, WINED3D_GL_EXT_NONE}, + {WINED3DFMT_A8_UNORM, "XYZW", FALSE, WINED3D_GL_LEGACY_CONTEXT}, + + {WINED3DFMT_L8A8_UNORM, "XXXY", FALSE, WINED3D_GL_EXT_NONE}, + {WINED3DFMT_L8A8_UNORM, "XYZW", FALSE, WINED3D_GL_LEGACY_CONTEXT}, + + {WINED3DFMT_L4A4_UNORM, "XXXY", FALSE, WINED3D_GL_EXT_NONE}, + {WINED3DFMT_L4A4_UNORM, "XYZW", FALSE, WINED3D_GL_LEGACY_CONTEXT}, + + {WINED3DFMT_L16_UNORM, "XXX1", FALSE, WINED3D_GL_EXT_NONE}, + {WINED3DFMT_L16_UNORM, "XYZW", FALSE, WINED3D_GL_LEGACY_CONTEXT}, + + {WINED3DFMT_INTZ, "XXXX", FALSE, WINED3D_GL_EXT_NONE}, + {WINED3DFMT_INTZ, "XYZW", FALSE, WINED3D_GL_LEGACY_CONTEXT}, + + {WINED3DFMT_L8_UNORM, "XXX1", FALSE, ARB_TEXTURE_RG}, + }; + + use_legacy_fixups = d3d_info->wined3d_creation_flags & WINED3D_LEGACY_UNBOUND_RESOURCE_COLOR; + for (i = 0; i < ARRAY_SIZE(fixups); ++i) + { + if (fixups[i].legacy && !use_legacy_fixups) + continue; + + if (!gl_info->supported[fixups[i].extension]) + continue; + + format = get_format_gl_internal(adapter, fixups[i].id); + format->f.color_fixup = create_color_fixup_desc_from_string(fixups[i].fixup); + } + + if (!gl_info->supported[APPLE_YCBCR_422] && !gl_info->supported[APPLE_RGB_422] + && (gl_info->supported[ARB_FRAGMENT_PROGRAM] + || (gl_info->supported[ARB_FRAGMENT_SHADER] && gl_info->supported[ARB_VERTEX_SHADER]))) + { + format = get_format_gl_internal(adapter, WINED3DFMT_YUY2); + format->f.color_fixup = create_complex_fixup_desc(COMPLEX_FIXUP_YUY2); + + format = get_format_gl_internal(adapter, WINED3DFMT_UYVY); + format->f.color_fixup = create_complex_fixup_desc(COMPLEX_FIXUP_UYVY); + } + else if (!gl_info->supported[APPLE_YCBCR_422] && gl_info->supported[APPLE_RGB_422]) + { + format = get_format_gl_internal(adapter, WINED3DFMT_YUY2); + format->f.color_fixup = create_complex_fixup_desc(COMPLEX_FIXUP_YUV); + + format = get_format_gl_internal(adapter, WINED3DFMT_UYVY); + format->f.color_fixup = create_complex_fixup_desc(COMPLEX_FIXUP_YUV); + } + else if (!gl_info->supported[APPLE_YCBCR_422] && (!gl_info->supported[ARB_FRAGMENT_PROGRAM] + && (!gl_info->supported[ARB_FRAGMENT_SHADER] || !gl_info->supported[ARB_VERTEX_SHADER]))) + { + format = get_format_gl_internal(adapter, WINED3DFMT_YUY2); + format_clear_flag(&format->f, WINED3DFMT_FLAG_BLIT); + format->internal = 0; + + format = get_format_gl_internal(adapter, WINED3DFMT_UYVY); + format_clear_flag(&format->f, WINED3DFMT_FLAG_BLIT); + format->internal = 0; + } + + if (gl_info->supported[ARB_FRAGMENT_PROGRAM] + || (gl_info->supported[ARB_FRAGMENT_SHADER] && gl_info->supported[ARB_VERTEX_SHADER])) + { + format = get_format_gl_internal(adapter, WINED3DFMT_YV12); + format_set_flag(&format->f, WINED3DFMT_FLAG_HEIGHT_SCALE); + format->f.height_scale.numerator = 3; + format->f.height_scale.denominator = 2; + format->f.color_fixup = create_complex_fixup_desc(COMPLEX_FIXUP_YV12); + + format = get_format_gl_internal(adapter, WINED3DFMT_NV12); + format_set_flag(&format->f, WINED3DFMT_FLAG_HEIGHT_SCALE); + format->f.height_scale.numerator = 3; + format->f.height_scale.denominator = 2; + format->f.color_fixup = create_complex_fixup_desc(COMPLEX_FIXUP_NV12); + } + else + { + format = get_format_gl_internal(adapter, WINED3DFMT_YV12); + format_clear_flag(&format->f, WINED3DFMT_FLAG_BLIT); + format->internal = 0; + + format = get_format_gl_internal(adapter, WINED3DFMT_NV12); + format_clear_flag(&format->f, WINED3DFMT_FLAG_BLIT); + format->internal = 0; + } + + if (gl_info->supported[ARB_FRAGMENT_PROGRAM] || gl_info->supported[ARB_FRAGMENT_SHADER]) + { + format = get_format_gl_internal(adapter, WINED3DFMT_P8_UINT); + format->f.color_fixup = create_complex_fixup_desc(COMPLEX_FIXUP_P8); + } + + if (!gl_info->supported[ARB_HALF_FLOAT_PIXEL]) + { + format = get_format_gl_internal(adapter, WINED3DFMT_R16_FLOAT); + format_clear_flag(&format->f, WINED3DFMT_FLAG_TEXTURE); + + format = get_format_gl_internal(adapter, WINED3DFMT_R16G16_FLOAT); + format_clear_flag(&format->f, WINED3DFMT_FLAG_TEXTURE); + + format = get_format_gl_internal(adapter, WINED3DFMT_R16G16B16A16_FLOAT); + format_clear_flag(&format->f, WINED3DFMT_FLAG_TEXTURE); + } + + if (gl_info->quirks & WINED3D_QUIRK_BROKEN_RGBA16) + { + format = get_format_gl_internal(adapter, WINED3DFMT_R16G16B16A16_UNORM); + format_clear_flag(&format->f, WINED3DFMT_FLAG_TEXTURE); + } + + /* ATI instancing hack: Although ATI cards do not support Shader Model + * 3.0, they support instancing. To query if the card supports instancing + * CheckDeviceFormat() with the special format MAKEFOURCC('I','N','S','T') + * is used. Should an application check for this, provide a proper return + * value. We can do instancing with all shader versions, but we need + * vertex shaders. + * + * Additionally applications have to set the D3DRS_POINTSIZE render state + * to MAKEFOURCC('I','N','S','T') once to enable instancing. Wined3d + * doesn't need that and just ignores it. + * + * With Shader Model 3.0 capable cards Instancing 'just works' in Windows. */ + /* FIXME: This should just check the shader backend caps. */ + if (gl_info->supported[ARB_VERTEX_PROGRAM] || gl_info->supported[ARB_VERTEX_SHADER]) + { + format = get_format_gl_internal(adapter, WINED3DFMT_INST); + format_set_flag(&format->f, WINED3DFMT_FLAG_TEXTURE); + } + + /* Depth bound test. To query if the card supports it CheckDeviceFormat() + * with the special format MAKEFOURCC('N','V','D','B') is used. It is + * enabled by setting D3DRS_ADAPTIVETESS_X render state to + * MAKEFOURCC('N','V','D','B') and then controlled by setting + * D3DRS_ADAPTIVETESS_Z (zMin) and D3DRS_ADAPTIVETESS_W (zMax) to test + * value. */ + if (gl_info->supported[EXT_DEPTH_BOUNDS_TEST]) + { + format = get_format_gl_internal(adapter, WINED3DFMT_NVDB); + format_set_flag(&format->f, WINED3DFMT_FLAG_TEXTURE); + } + + if (gl_info->supported[ARB_MULTISAMPLE]) + { + format = get_format_gl_internal(adapter, WINED3DFMT_ATOC); + format_set_flag(&format->f, WINED3DFMT_FLAG_TEXTURE); + } + + /* RESZ aka AMD DX9-level hack for multisampled depth buffer resolve. You query for RESZ + * support by checking for availability of MAKEFOURCC('R','E','S','Z') surfaces with + * RENDERTARGET usage. */ + if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]) + { + format = get_format_gl_internal(adapter, WINED3DFMT_RESZ); + format_set_flag(&format->f, WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_RENDERTARGET); + } + + for (i = 0; i < WINED3D_FORMAT_COUNT; ++i) + { + format = get_format_gl_by_idx(adapter, i); + + if (!(format->f.flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_TEXTURE)) + continue; + + if (is_identity_fixup(format->f.color_fixup)) + continue; + + TRACE("Checking support for fixup:\n"); + dump_color_fixup_desc(format->f.color_fixup); + if (!adapter->shader_backend->shader_color_fixup_supported(format->f.color_fixup) + || !adapter->fragment_pipe->color_fixup_supported(format->f.color_fixup)) + { + TRACE("[FAILED]\n"); + format_clear_flag(&format->f, WINED3DFMT_FLAG_TEXTURE); + } + else + { + TRACE("[OK]\n"); + } + } + + /* These formats are not supported for 3D textures. See also + * WINED3DFMT_FLAG_DECOMPRESS. */ + format = get_format_gl_internal(adapter, WINED3DFMT_ATI1N); + format->f.flags[WINED3D_GL_RES_TYPE_TEX_3D] &= ~WINED3DFMT_FLAG_TEXTURE; + format = get_format_gl_internal(adapter, WINED3DFMT_ATI2N); + format->f.flags[WINED3D_GL_RES_TYPE_TEX_3D] &= ~WINED3DFMT_FLAG_TEXTURE; + format = get_format_gl_internal(adapter, WINED3DFMT_BC4_UNORM); + format->f.flags[WINED3D_GL_RES_TYPE_TEX_3D] &= ~WINED3DFMT_FLAG_TEXTURE; + format = get_format_gl_internal(adapter, WINED3DFMT_BC4_SNORM); + format->f.flags[WINED3D_GL_RES_TYPE_TEX_3D] &= ~WINED3DFMT_FLAG_TEXTURE; + format = get_format_gl_internal(adapter, WINED3DFMT_BC5_UNORM); + format->f.flags[WINED3D_GL_RES_TYPE_TEX_3D] &= ~WINED3DFMT_FLAG_TEXTURE; + format = get_format_gl_internal(adapter, WINED3DFMT_BC5_SNORM); + format->f.flags[WINED3D_GL_RES_TYPE_TEX_3D] &= ~WINED3DFMT_FLAG_TEXTURE; +} + +static BOOL init_format_vertex_info(const struct wined3d_adapter *adapter, + struct wined3d_gl_info *gl_info) +{ + struct wined3d_format_gl *format; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(format_vertex_info); ++i) + { + if (!(format = get_format_gl_internal(adapter, format_vertex_info[i].id))) + return FALSE; + + if (!gl_info->supported[format_vertex_info[i].extension]) + continue; + + format->f.emit_idx = format_vertex_info[i].emit_idx; + format->vtx_type = format_vertex_info[i].gl_vtx_type; + format->vtx_format = format->f.component_count; + format->f.flags[WINED3D_GL_RES_TYPE_BUFFER] |= WINED3DFMT_FLAG_VERTEX_ATTRIBUTE; + } + + if (gl_info->supported[ARB_VERTEX_ARRAY_BGRA]) + { + format = get_format_gl_internal(adapter, WINED3DFMT_B8G8R8A8_UNORM); + format->vtx_format = GL_BGRA; + } + + return TRUE; +} + +static BOOL init_typeless_formats(const struct wined3d_adapter *adapter) +{ + unsigned int flags[WINED3D_GL_RES_TYPE_COUNT]; + unsigned int i, j; + + for (i = 0; i < ARRAY_SIZE(typed_formats); ++i) + { + struct wined3d_format *format, *typeless_format; + + if (!(format = get_format_internal(adapter, typed_formats[i].id))) + return FALSE; + if (!(typeless_format = get_format_internal(adapter, typed_formats[i].typeless_id))) + return FALSE; + + memcpy(flags, typeless_format->flags, sizeof(flags)); + copy_format(adapter, typeless_format, format); + for (j = 0; j < ARRAY_SIZE(typeless_format->flags); ++j) + typeless_format->flags[j] |= flags[j]; + } + + for (i = 0; i < ARRAY_SIZE(typeless_depth_stencil_formats); ++i) + { + struct wined3d_format *typeless_format, *typeless_ds_format, *ds_format; + struct wined3d_format *depth_view_format, *stencil_view_format; + enum wined3d_format_id format_id; + + if (!(typeless_format = get_format_internal(adapter, typeless_depth_stencil_formats[i].typeless_id))) + return FALSE; + if (!(ds_format = get_format_internal(adapter, typeless_depth_stencil_formats[i].depth_stencil_id))) + return FALSE; + + typeless_ds_format = get_format_by_idx(adapter, WINED3D_FORMAT_COUNT + i); + typeless_ds_format->id = typeless_depth_stencil_formats[i].typeless_id; + copy_format(adapter, typeless_ds_format, ds_format); + for (j = 0; j < ARRAY_SIZE(typeless_ds_format->flags); ++j) + { + typeless_ds_format->flags[j] = typeless_format->flags[j]; + typeless_format->flags[j] &= ~WINED3DFMT_FLAG_DEPTH_STENCIL; + } + + if ((format_id = typeless_depth_stencil_formats[i].depth_view_id) + && typeless_depth_stencil_formats[i].separate_depth_view_format) + { + if (!(depth_view_format = get_format_internal(adapter, format_id))) + return FALSE; + copy_format(adapter, depth_view_format, ds_format); + depth_view_format->red_size = depth_view_format->depth_size; + depth_view_format->depth_size = 0; + depth_view_format->stencil_size = 0; + } + if ((format_id = typeless_depth_stencil_formats[i].stencil_view_id)) + { + if (!(stencil_view_format = get_format_internal(adapter, format_id))) + return FALSE; + copy_format(adapter, stencil_view_format, ds_format); + stencil_view_format->green_size = stencil_view_format->stencil_size; + stencil_view_format->depth_size = 0; + stencil_view_format->stencil_size = 0; + } + } + + return TRUE; +} + +static void init_format_gen_mipmap_info(const struct wined3d_adapter *adapter, + struct wined3d_gl_info *gl_info) +{ + unsigned int i, j; + + if (!gl_info->fbo_ops.glGenerateMipmap) + return; + + for (i = 0; i < WINED3D_FORMAT_COUNT; ++i) + { + struct wined3d_format *format = get_format_by_idx(adapter, i); + + for (j = 0; j < ARRAY_SIZE(format->flags); ++j) + if (!(~format->flags[j] & (WINED3DFMT_FLAG_RENDERTARGET | WINED3DFMT_FLAG_FILTERING))) + format->flags[j] |= WINED3DFMT_FLAG_GEN_MIPMAP; + } +} + +BOOL wined3d_caps_gl_ctx_test_viewport_subpixel_bits(struct wined3d_caps_gl_ctx *ctx) +{ + static const struct wined3d_color red = {1.0f, 0.0f, 0.0f, 1.0f}; + const struct wined3d_gl_info *gl_info = ctx->gl_info; + static const float offset = -63.0f / 128.0f; + GLuint texture, fbo; + DWORD readback[4]; + unsigned int i; + + gl_info->gl_ops.gl.p_glGenTextures(1, &texture); + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_2D, texture); + gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); + gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, ARRAY_SIZE(readback), 1, 0, + GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL); + gl_info->fbo_ops.glGenFramebuffers(1, &fbo); + gl_info->fbo_ops.glBindFramebuffer(GL_FRAMEBUFFER, fbo); + gl_info->fbo_ops.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, texture, 0); + checkGLcall("create resources"); + + gl_info->gl_ops.gl.p_glClearColor(1.0f, 1.0f, 1.0f, 1.0f); + gl_info->gl_ops.gl.p_glClear(GL_COLOR_BUFFER_BIT); + GL_EXTCALL(glViewportIndexedf(0, offset, offset, 4.0f, 1.0f)); + draw_test_quad(ctx, NULL, &red); + checkGLcall("draw"); + + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_2D, texture); + gl_info->gl_ops.gl.p_glGetTexImage(GL_TEXTURE_2D, 0, + GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, readback); + checkGLcall("readback"); + + TRACE("Readback colors are 0x%08x, 0x%08x, 0x%08x, 0x%08x.\n", + readback[0], readback[1], readback[2], readback[3]); + + gl_info->gl_ops.gl.p_glDeleteTextures(1, &texture); + gl_info->fbo_ops.glDeleteFramebuffers(1, &fbo); + gl_info->fbo_ops.glBindFramebuffer(GL_FRAMEBUFFER, 0); + checkGLcall("delete resources"); + + for (i = 0; i < ARRAY_SIZE(readback); ++i) + { + if (readback[i] != 0xffff0000) + return FALSE; + } + return TRUE; +} + +static float wined3d_adapter_find_polyoffset_scale(struct wined3d_caps_gl_ctx *ctx, GLenum format) +{ + const struct wined3d_gl_info *gl_info = ctx->gl_info; + static const struct wined3d_color blue = {0.0f, 0.0f, 1.0f, 1.0f}; + GLuint fbo, color, depth; + unsigned int low = 0, high = 32, cur; + DWORD readback[256]; + static const struct wined3d_vec3 geometry[] = + { + {-1.0f, -1.0f, -1.0f}, + { 1.0f, -1.0f, 0.0f}, + {-1.0f, 1.0f, -1.0f}, + { 1.0f, 1.0f, 0.0f}, + }; + + /* Most drivers want 2^23 for fixed point depth buffers, including r300g, r600g, + * Nvidia. Use this as a fallback if the detection fails. */ + unsigned int fallback = 23; + + if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) + { + FIXME("No FBOs, assuming polyoffset scale of 2^%u.\n", fallback); + return (float)(1u << fallback); + } + + gl_info->gl_ops.gl.p_glGenTextures(1, &color); + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_2D, color); + gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); + gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 256, 1, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, 0); + + gl_info->fbo_ops.glGenRenderbuffers(1, &depth); + gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, depth); + gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, format, 256, 1); + + gl_info->fbo_ops.glGenFramebuffers(1, &fbo); + gl_info->fbo_ops.glBindFramebuffer(GL_FRAMEBUFFER, fbo); + gl_info->fbo_ops.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color, 0); + gl_info->fbo_ops.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depth); + checkGLcall("Setup framebuffer"); + + gl_info->gl_ops.gl.p_glClearColor(0.0f, 0.0f, 0.5f, 0.0f); + gl_info->gl_ops.gl.p_glClearDepth(0.5f); + gl_info->gl_ops.gl.p_glEnable(GL_DEPTH_TEST); + gl_info->gl_ops.gl.p_glEnable(GL_POLYGON_OFFSET_FILL); + gl_info->gl_ops.gl.p_glViewport(0, 0, 256, 1); + checkGLcall("Misc parameters"); + + for (;;) + { + if (high - low <= 1) + { + ERR("PolygonOffset scale factor detection failed, using fallback value 2^%u.\n", fallback); + cur = fallback; + break; + } + cur = (low + high) / 2; + + gl_info->gl_ops.gl.p_glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + /* The post viewport transform Z of the geometry runs from 0.0 to 0.5. We want to push it another + * 0.25 so that the Z buffer content (0.5) cuts the quad off at half the screen. */ + gl_info->gl_ops.gl.p_glPolygonOffset(0.0f, (float)(1u << cur) * 0.25f); + draw_test_quad(ctx, geometry, &blue); + checkGLcall("Test draw"); + + /* Rebinding texture to workaround a fglrx bug. */ + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_2D, color); + gl_info->gl_ops.gl.p_glGetTexImage(GL_TEXTURE_2D, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, readback); + checkGLcall("readback"); + + TRACE("low %02u, high %02u, cur %2u, 0=0x%08x, 125=0x%08x, 131=0x%08x, 255=0x%08x\n", + low, high, cur, readback[0], readback[125], readback[131], readback[255]); + + if ((readback[125] & 0xff) < 0xa0) + high = cur; + else if ((readback[131] & 0xff) > 0xa0) + low = cur; + else + { + TRACE("Found scale factor 2^%u for format %x.\n", cur, format); + break; + } + } + + gl_info->gl_ops.gl.p_glDeleteTextures(1, &color); + gl_info->fbo_ops.glDeleteRenderbuffers(1, &depth); + gl_info->fbo_ops.glDeleteFramebuffers(1, &fbo); + gl_info->fbo_ops.glBindFramebuffer(GL_FRAMEBUFFER, 0); + checkGLcall("Delete framebuffer"); + + gl_info->gl_ops.gl.p_glDisable(GL_DEPTH_TEST); + gl_info->gl_ops.gl.p_glDisable(GL_POLYGON_OFFSET_FILL); + return (float)(1u << cur); +} + +static void init_format_depth_bias_scale(struct wined3d_adapter *adapter, + struct wined3d_caps_gl_ctx *ctx) +{ + const struct wined3d_d3d_info *d3d_info = &adapter->d3d_info; + unsigned int i; + + for (i = 0; i < WINED3D_FORMAT_COUNT; ++i) + { + struct wined3d_format_gl *format = get_format_gl_by_idx(adapter, i); + + if (format->f.depth_size && (format->f.flags[WINED3D_GL_RES_TYPE_RB] & WINED3DFMT_FLAG_DEPTH_STENCIL)) + { + TRACE("Testing depth bias scale for format %s.\n", debug_d3dformat(format->f.id)); + format->f.depth_bias_scale = wined3d_adapter_find_polyoffset_scale(ctx, format->internal); + + if (!(d3d_info->wined3d_creation_flags & WINED3D_NORMALIZED_DEPTH_BIAS)) + { + /* The single-precision binary floating-point format has + * a significand precision of 24 bits. + */ + if (format->f.flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FLOAT) + format->f.depth_bias_scale /= 1u << 24; + else + format->f.depth_bias_scale /= 1u << format->f.depth_size; + } + } + } +} + +static BOOL wined3d_adapter_init_format_info(struct wined3d_adapter *adapter, size_t format_size) +{ + unsigned int count = WINED3D_FORMAT_COUNT + ARRAY_SIZE(typeless_depth_stencil_formats); + + if (!(adapter->formats = heap_calloc(count, format_size))) + { + ERR("Failed to allocate memory.\n"); + return FALSE; + } + adapter->format_size = format_size; + + if (!init_format_base_info(adapter)) + goto fail; + if (!init_format_block_info(adapter)) + goto fail; + if (!init_format_decompress_info(adapter)) + goto fail; + if (!init_srgb_formats(adapter)) + goto fail; + + return TRUE; + +fail: + heap_free(adapter->formats); + adapter->formats = NULL; + return FALSE; +} + +BOOL wined3d_adapter_no3d_init_format_info(struct wined3d_adapter *adapter) +{ + struct wined3d_format *format; + unsigned int i; + + static const enum wined3d_format_id blit_formats[] = + { + WINED3DFMT_B8G8R8A8_UNORM, + WINED3DFMT_B8G8R8X8_UNORM, + WINED3DFMT_B5G6R5_UNORM, + WINED3DFMT_B5G5R5X1_UNORM, + WINED3DFMT_B5G5R5A1_UNORM, + WINED3DFMT_B4G4R4A4_UNORM, + WINED3DFMT_B2G3R3_UNORM, + WINED3DFMT_A8_UNORM, + WINED3DFMT_B2G3R3A8_UNORM, + WINED3DFMT_B4G4R4X4_UNORM, + WINED3DFMT_R10G10B10A2_UNORM, + WINED3DFMT_R8G8B8A8_UNORM, + WINED3DFMT_R8G8B8X8_UNORM, + WINED3DFMT_R16G16_UNORM, + WINED3DFMT_B10G10R10A2_UNORM, + WINED3DFMT_R16G16B16A16_UNORM, + WINED3DFMT_P8_UINT, + }; + + if (!wined3d_adapter_init_format_info(adapter, sizeof(struct wined3d_format))) + return FALSE; + + for (i = 0; i < ARRAY_SIZE(blit_formats); ++i) + { + if (!(format = get_format_internal(adapter, blit_formats[i]))) + return FALSE; + + format->flags[WINED3D_GL_RES_TYPE_TEX_2D] |= WINED3DFMT_FLAG_BLIT; + format->flags[WINED3D_GL_RES_TYPE_RB] |= WINED3DFMT_FLAG_BLIT; + } + + return TRUE; +} + +/* Context activation is done by the caller. */ +BOOL wined3d_adapter_gl_init_format_info(struct wined3d_adapter *adapter, struct wined3d_caps_gl_ctx *ctx) +{ + struct wined3d_gl_info *gl_info = &adapter->gl_info; + + if (!wined3d_adapter_init_format_info(adapter, sizeof(struct wined3d_format_gl))) + return FALSE; + + if (!init_format_texture_info(adapter, gl_info)) goto fail; + if (!init_format_vertex_info(adapter, gl_info)) goto fail; + + apply_format_fixups(adapter, gl_info); + init_format_fbo_compat_info(adapter, ctx); + init_format_filter_info(adapter, gl_info); + init_format_gen_mipmap_info(adapter, gl_info); + init_format_depth_bias_scale(adapter, ctx); + + if (!init_typeless_formats(adapter)) goto fail; + + return TRUE; + +fail: + heap_free(adapter->formats); + adapter->formats = NULL; + return FALSE; +} + +static void init_vulkan_format_info(struct wined3d_format_vk *format, + const struct wined3d_vk_info *vk_info, VkPhysicalDevice vk_physical_device) +{ + static const struct + { + enum wined3d_format_id id; + VkFormat vk_format; + const char *fixup; + } + vulkan_formats[] = + { + {WINED3DFMT_R32G32B32A32_FLOAT, VK_FORMAT_R32G32B32A32_SFLOAT, }, + {WINED3DFMT_R32G32B32A32_UINT, VK_FORMAT_R32G32B32A32_UINT, }, + {WINED3DFMT_R32G32B32A32_SINT, VK_FORMAT_R32G32B32A32_SINT, }, + {WINED3DFMT_R32G32B32_FLOAT, VK_FORMAT_R32G32B32_SFLOAT, }, + {WINED3DFMT_R32G32B32_UINT, VK_FORMAT_R32G32B32_UINT, }, + {WINED3DFMT_R32G32B32_SINT, VK_FORMAT_R32G32B32_SINT, }, + {WINED3DFMT_R16G16B16A16_FLOAT, VK_FORMAT_R16G16B16A16_SFLOAT, }, + {WINED3DFMT_R16G16B16A16_UNORM, VK_FORMAT_R16G16B16A16_UNORM, }, + {WINED3DFMT_R16G16B16A16_UINT, VK_FORMAT_R16G16B16A16_UINT, }, + {WINED3DFMT_R16G16B16A16_SNORM, VK_FORMAT_R16G16B16A16_SNORM, }, + {WINED3DFMT_R16G16B16A16_SINT, VK_FORMAT_R16G16B16A16_SINT, }, + {WINED3DFMT_R32G32_FLOAT, VK_FORMAT_R32G32_SFLOAT, }, + {WINED3DFMT_R32G32_UINT, VK_FORMAT_R32G32_UINT, }, + {WINED3DFMT_R32G32_SINT, VK_FORMAT_R32G32_SINT, }, + {WINED3DFMT_R10G10B10A2_UNORM, VK_FORMAT_A2B10G10R10_UNORM_PACK32,}, + {WINED3DFMT_R11G11B10_FLOAT, VK_FORMAT_B10G11R11_UFLOAT_PACK32, }, + {WINED3DFMT_R8G8_UNORM, VK_FORMAT_R8G8_UNORM, }, + {WINED3DFMT_R8G8_UINT, VK_FORMAT_R8G8_UINT, }, + {WINED3DFMT_R8G8_SNORM, VK_FORMAT_R8G8_SNORM, }, + {WINED3DFMT_R8G8_SINT, VK_FORMAT_R8G8_SINT, }, + {WINED3DFMT_R8G8B8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, }, + {WINED3DFMT_R8G8B8A8_UNORM_SRGB, VK_FORMAT_R8G8B8A8_SRGB, }, + {WINED3DFMT_R8G8B8A8_UINT, VK_FORMAT_R8G8B8A8_UINT, }, + {WINED3DFMT_R8G8B8A8_SNORM, VK_FORMAT_R8G8B8A8_SNORM, }, + {WINED3DFMT_R8G8B8A8_SINT, VK_FORMAT_R8G8B8A8_SINT, }, + {WINED3DFMT_R16G16_FLOAT, VK_FORMAT_R16G16_SFLOAT, }, + {WINED3DFMT_R16G16_UNORM, VK_FORMAT_R16G16_UNORM, }, + {WINED3DFMT_R16G16_UINT, VK_FORMAT_R16G16_UINT, }, + {WINED3DFMT_R16G16_SNORM, VK_FORMAT_R16G16_SNORM, }, + {WINED3DFMT_R16G16_SINT, VK_FORMAT_R16G16_SINT, }, + {WINED3DFMT_D32_FLOAT, VK_FORMAT_D32_SFLOAT, }, + {WINED3DFMT_R32_FLOAT, VK_FORMAT_R32_SFLOAT, }, + {WINED3DFMT_R32_UINT, VK_FORMAT_R32_UINT, }, + {WINED3DFMT_R32_SINT, VK_FORMAT_R32_SINT, }, + {WINED3DFMT_R16_FLOAT, VK_FORMAT_R16_SFLOAT, }, + {WINED3DFMT_D16_UNORM, VK_FORMAT_D16_UNORM, }, + {WINED3DFMT_R16_UNORM, VK_FORMAT_R16_UNORM, }, + {WINED3DFMT_R16_UINT, VK_FORMAT_R16_UINT, }, + {WINED3DFMT_R16_SNORM, VK_FORMAT_R16_SNORM, }, + {WINED3DFMT_R16_SINT, VK_FORMAT_R16_SINT, }, + {WINED3DFMT_R8_UNORM, VK_FORMAT_R8_UNORM, }, + {WINED3DFMT_R8_UINT, VK_FORMAT_R8_UINT, }, + {WINED3DFMT_R8_SNORM, VK_FORMAT_R8_SNORM, }, + {WINED3DFMT_R8_SINT, VK_FORMAT_R8_SINT, }, + {WINED3DFMT_A8_UNORM, VK_FORMAT_R8_UNORM, "000X"}, + {WINED3DFMT_B8G8R8A8_UNORM, VK_FORMAT_B8G8R8A8_UNORM, }, + {WINED3DFMT_B8G8R8A8_UNORM_SRGB, VK_FORMAT_B8G8R8A8_SRGB, }, + {WINED3DFMT_B8G8R8X8_UNORM, VK_FORMAT_B8G8R8A8_UNORM, "XYZ1"}, + {WINED3DFMT_B8G8R8X8_UNORM_SRGB, VK_FORMAT_B8G8R8A8_SRGB, "XYZ1"}, + {WINED3DFMT_BC1_UNORM, VK_FORMAT_BC1_RGBA_UNORM_BLOCK, }, + {WINED3DFMT_BC1_UNORM_SRGB, VK_FORMAT_BC1_RGBA_SRGB_BLOCK, }, + {WINED3DFMT_BC2_UNORM, VK_FORMAT_BC2_UNORM_BLOCK, }, + {WINED3DFMT_BC2_UNORM_SRGB, VK_FORMAT_BC2_SRGB_BLOCK, }, + {WINED3DFMT_BC3_UNORM, VK_FORMAT_BC3_UNORM_BLOCK, }, + {WINED3DFMT_BC3_UNORM_SRGB, VK_FORMAT_BC3_SRGB_BLOCK, }, + {WINED3DFMT_BC4_UNORM, VK_FORMAT_BC4_UNORM_BLOCK, }, + {WINED3DFMT_BC4_SNORM, VK_FORMAT_BC4_SNORM_BLOCK, }, + {WINED3DFMT_BC5_UNORM, VK_FORMAT_BC5_UNORM_BLOCK, }, + {WINED3DFMT_BC5_SNORM, VK_FORMAT_BC5_SNORM_BLOCK, }, + {WINED3DFMT_BC6H_UF16, VK_FORMAT_BC6H_UFLOAT_BLOCK, }, + {WINED3DFMT_BC6H_SF16, VK_FORMAT_BC6H_SFLOAT_BLOCK, }, + {WINED3DFMT_BC7_UNORM, VK_FORMAT_BC7_UNORM_BLOCK, }, + {WINED3DFMT_BC7_UNORM_SRGB, VK_FORMAT_BC7_SRGB_BLOCK, }, + {WINED3DFMT_R9G9B9E5_SHAREDEXP, VK_FORMAT_E5B9G9R9_UFLOAT_PACK32, }, + {WINED3DFMT_D32_FLOAT_S8X24_UINT, VK_FORMAT_D32_SFLOAT_S8_UINT, }, + {WINED3DFMT_R32_FLOAT_X8X24_TYPELESS, VK_FORMAT_D32_SFLOAT_S8_UINT, }, + {WINED3DFMT_X32_TYPELESS_G8X24_UINT, VK_FORMAT_D32_SFLOAT_S8_UINT, }, + {WINED3DFMT_D24_UNORM_S8_UINT, VK_FORMAT_D32_SFLOAT_S8_UINT, }, + }; + VkFormat vk_format = VK_FORMAT_UNDEFINED; + VkImageFormatProperties image_properties; + VkFormatFeatureFlags texture_flags; + VkFormatProperties properties; + VkImageUsageFlags vk_usage; + unsigned int flags; + const char *fixup; + unsigned int i; + uint32_t mask; + VkResult vr; + + for (i = 0; i < ARRAY_SIZE(vulkan_formats); ++i) + { + if (vulkan_formats[i].id == format->f.id) + { + vk_format = vulkan_formats[i].vk_format; + fixup = vulkan_formats[i].fixup; + break; + } + } + if (!vk_format) + { + WARN("Unsupported format %s.\n", debug_d3dformat(format->f.id)); + return; + } + + format->vk_format = vk_format; + if (fixup) + format->f.color_fixup = create_color_fixup_desc_from_string(fixup); + else + format->f.color_fixup = COLOR_FIXUP_IDENTITY; + + VK_CALL(vkGetPhysicalDeviceFormatProperties(vk_physical_device, vk_format, &properties)); + + if (properties.bufferFeatures & VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT) + format->f.flags[WINED3D_GL_RES_TYPE_BUFFER] |= WINED3DFMT_FLAG_VERTEX_ATTRIBUTE; + if (properties.bufferFeatures & VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT) + format->f.flags[WINED3D_GL_RES_TYPE_BUFFER] |= WINED3DFMT_FLAG_TEXTURE; + + flags = 0; + texture_flags = properties.linearTilingFeatures | properties.optimalTilingFeatures; + if (texture_flags & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) + { + flags |= WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_VTF; + } + if (texture_flags & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT) + { + flags |= WINED3DFMT_FLAG_RENDERTARGET; + } + if (texture_flags & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT) + { + flags |= WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING; + } + if (texture_flags & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) + { + flags |= WINED3DFMT_FLAG_DEPTH_STENCIL; + } + if (texture_flags & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT) + { + flags |= WINED3DFMT_FLAG_FILTERING; + } + if (texture_flags & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT) + { + flags |= WINED3DFMT_FLAG_UNORDERED_ACCESS; + } + + format->f.flags[WINED3D_GL_RES_TYPE_TEX_1D] |= flags; + format->f.flags[WINED3D_GL_RES_TYPE_TEX_2D] |= flags; + format->f.flags[WINED3D_GL_RES_TYPE_TEX_3D] |= flags; + format->f.flags[WINED3D_GL_RES_TYPE_TEX_CUBE] |= flags; + + vk_usage = 0; + if (texture_flags & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT) + vk_usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + else if (texture_flags & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) + vk_usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + if (vk_usage) + { + if ((vr = VK_CALL(vkGetPhysicalDeviceImageFormatProperties(vk_physical_device, vk_format, + VK_IMAGE_TYPE_2D, VK_IMAGE_TILING_OPTIMAL, vk_usage, 0, &image_properties))) < 0) + { + ERR("Failed to get image format properties, vr %s.\n", wined3d_debug_vkresult(vr)); + return; + } + + mask = image_properties.sampleCounts & 0x3f; + while (mask) + { + i = (1u << wined3d_bit_scan(&mask)) - 1; + format->f.multisample_types |= 1u << i; + } + } +} + +BOOL wined3d_adapter_vk_init_format_info(struct wined3d_adapter_vk *adapter_vk, + const struct wined3d_vk_info *vk_info) +{ + VkPhysicalDevice vk_physical_device = adapter_vk->physical_device; + struct wined3d_adapter *adapter = &adapter_vk->a; + struct wined3d_format_vk *format; + unsigned int i; + + if (!wined3d_adapter_init_format_info(adapter, sizeof(*format))) + return FALSE; + + for (i = 0; i < WINED3D_FORMAT_COUNT; ++i) + { + format = wined3d_format_vk_mutable(get_format_by_idx(adapter, i)); + + if (format->f.id) + init_vulkan_format_info(format, vk_info, vk_physical_device); + } + + if (!init_typeless_formats(adapter)) goto fail; + + return TRUE; + +fail: + heap_free(adapter->formats); + adapter->formats = NULL; + return FALSE; +} + +const struct wined3d_format *wined3d_get_format(const struct wined3d_adapter *adapter, + enum wined3d_format_id format_id, unsigned int bind_flags) +{ + const struct wined3d_format *format; + int idx = get_format_idx(format_id); + unsigned int i; + + if (idx == -1) + { + FIXME("Can't find format %s (%#x) in the format lookup table.\n", + debug_d3dformat(format_id), format_id); + return get_format_internal(adapter, WINED3DFMT_UNKNOWN); + } + + format = get_format_by_idx(adapter, idx); + + if (bind_flags & WINED3D_BIND_DEPTH_STENCIL && wined3d_format_is_typeless(format)) + { + for (i = 0; i < ARRAY_SIZE(typeless_depth_stencil_formats); ++i) + { + if (typeless_depth_stencil_formats[i].typeless_id == format_id) + return get_format_by_idx(adapter, WINED3D_FORMAT_COUNT + i); + } + + FIXME("Cannot find depth/stencil typeless format %s (%#x).\n", + debug_d3dformat(format_id), format_id); + return get_format_internal(adapter, WINED3DFMT_UNKNOWN); + } + + return format; +} + +BOOL wined3d_format_is_depth_view(enum wined3d_format_id resource_format_id, + enum wined3d_format_id view_format_id) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(typeless_depth_stencil_formats); ++i) + { + if (typeless_depth_stencil_formats[i].typeless_id == resource_format_id) + return typeless_depth_stencil_formats[i].depth_view_id == view_format_id; + } + return FALSE; +} + +void wined3d_format_calculate_pitch(const struct wined3d_format *format, unsigned int alignment, + unsigned int width, unsigned int height, unsigned int *row_pitch, unsigned int *slice_pitch) +{ + /* For block based formats, pitch means the amount of bytes to the next + * row of blocks rather than the next row of pixels. */ + if (format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_BLOCKS) + { + unsigned int row_block_count = (width + format->block_width - 1) / format->block_width; + unsigned int slice_block_count = (height + format->block_height - 1) / format->block_height; + *row_pitch = row_block_count * format->block_byte_count; + *row_pitch = (*row_pitch + alignment - 1) & ~(alignment - 1); + *slice_pitch = *row_pitch * slice_block_count; + } + else + { + *row_pitch = format->byte_count * width; /* Bytes / row */ + *row_pitch = (*row_pitch + alignment - 1) & ~(alignment - 1); + *slice_pitch = *row_pitch * height; + } + + if (format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_HEIGHT_SCALE) + { + /* The D3D format requirements make sure that the resulting format is an integer again */ + *slice_pitch *= format->height_scale.numerator; + *slice_pitch /= format->height_scale.denominator; + } + + TRACE("Returning row pitch %u, slice pitch %u.\n", *row_pitch, *slice_pitch); +} + +UINT wined3d_format_calculate_size(const struct wined3d_format *format, UINT alignment, + UINT width, UINT height, UINT depth) +{ + unsigned int row_pitch, slice_pitch; + + if (format->id == WINED3DFMT_UNKNOWN) + return 0; + + if (format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_BROKEN_PITCH) + return width * height * depth * format->byte_count; + + wined3d_format_calculate_pitch(format, alignment, width, height, &row_pitch, &slice_pitch); + + return slice_pitch * depth; +} + +BOOL wined3d_formats_are_srgb_variants(enum wined3d_format_id format1, enum wined3d_format_id format2) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(format_srgb_info); ++i) + { + if (format1 == format_srgb_info[i].srgb_format_id) + return format2 == format_srgb_info[i].base_format_id; + if (format1 == format_srgb_info[i].base_format_id) + return format2 == format_srgb_info[i].srgb_format_id; + } + return FALSE; +} + +/***************************************************************************** + * Trace formatting of useful values + */ +const char *debug_box(const struct wined3d_box *box) +{ + if (!box) + return "(null)"; + return wine_dbg_sprintf("(%u, %u, %u)-(%u, %u, %u)", + box->left, box->top, box->front, + box->right, box->bottom, box->back); +} + +const char *debug_color(const struct wined3d_color *color) +{ + if (!color) + return "(null)"; + return wine_dbg_sprintf("{%.8e, %.8e, %.8e, %.8e}", + color->r, color->g, color->b, color->a); +} + +const char *debug_ivec4(const struct wined3d_ivec4 *v) +{ + if (!v) + return "(null)"; + return wine_dbg_sprintf("{%d, %d, %d, %d}", + v->x, v->y, v->z, v->w); +} + +const char *debug_uvec4(const struct wined3d_uvec4 *v) +{ + if (!v) + return "(null)"; + return wine_dbg_sprintf("{%u, %u, %u, %u}", + v->x, v->y, v->z, v->w); +} + +const char *debug_vec4(const struct wined3d_vec4 *v) +{ + if (!v) + return "(null)"; + return wine_dbg_sprintf("{%.8e, %.8e, %.8e, %.8e}", + v->x, v->y, v->z, v->w); +} + +const char *debug_const_bo_address(const struct wined3d_const_bo_address *address) +{ + if (!address) + return "(null)"; + return wine_dbg_sprintf("{%#lx:%p}", address->buffer_object, address->addr); +} + +const char *debug_bo_address(const struct wined3d_bo_address *address) +{ + return debug_const_bo_address((const struct wined3d_const_bo_address *)address); +} + +const char *debug_d3dformat(enum wined3d_format_id format_id) +{ + switch (format_id) + { +#define FMT_TO_STR(format_id) case format_id: return #format_id + FMT_TO_STR(WINED3DFMT_UNKNOWN); + FMT_TO_STR(WINED3DFMT_B8G8R8_UNORM); + FMT_TO_STR(WINED3DFMT_B5G5R5X1_UNORM); + FMT_TO_STR(WINED3DFMT_B4G4R4A4_UNORM); + FMT_TO_STR(WINED3DFMT_B2G3R3_UNORM); + FMT_TO_STR(WINED3DFMT_B2G3R3A8_UNORM); + FMT_TO_STR(WINED3DFMT_B4G4R4X4_UNORM); + FMT_TO_STR(WINED3DFMT_R8G8B8X8_UNORM); + FMT_TO_STR(WINED3DFMT_B10G10R10A2_UNORM); + FMT_TO_STR(WINED3DFMT_P8_UINT_A8_UNORM); + FMT_TO_STR(WINED3DFMT_P8_UINT); + FMT_TO_STR(WINED3DFMT_L8_UNORM); + FMT_TO_STR(WINED3DFMT_L8A8_UNORM); + FMT_TO_STR(WINED3DFMT_L4A4_UNORM); + FMT_TO_STR(WINED3DFMT_R5G5_SNORM_L6_UNORM); + FMT_TO_STR(WINED3DFMT_R8G8_SNORM_L8X8_UNORM); + FMT_TO_STR(WINED3DFMT_R10G11B11_SNORM); + FMT_TO_STR(WINED3DFMT_R10G10B10X2_TYPELESS); + FMT_TO_STR(WINED3DFMT_R10G10B10X2_UINT); + FMT_TO_STR(WINED3DFMT_R10G10B10X2_SNORM); + FMT_TO_STR(WINED3DFMT_R10G10B10_SNORM_A2_UNORM); + FMT_TO_STR(WINED3DFMT_D16_LOCKABLE); + FMT_TO_STR(WINED3DFMT_D32_UNORM); + FMT_TO_STR(WINED3DFMT_S1_UINT_D15_UNORM); + FMT_TO_STR(WINED3DFMT_X8D24_UNORM); + FMT_TO_STR(WINED3DFMT_S4X4_UINT_D24_UNORM); + FMT_TO_STR(WINED3DFMT_L16_UNORM); + FMT_TO_STR(WINED3DFMT_S8_UINT_D24_FLOAT); + FMT_TO_STR(WINED3DFMT_R8G8_SNORM_Cx); + FMT_TO_STR(WINED3DFMT_R32G32B32A32_TYPELESS); + FMT_TO_STR(WINED3DFMT_R32G32B32A32_FLOAT); + FMT_TO_STR(WINED3DFMT_R32G32B32A32_UINT); + FMT_TO_STR(WINED3DFMT_R32G32B32A32_SINT); + FMT_TO_STR(WINED3DFMT_R32G32B32_TYPELESS); + FMT_TO_STR(WINED3DFMT_R32G32B32_FLOAT); + FMT_TO_STR(WINED3DFMT_R32G32B32_UINT); + FMT_TO_STR(WINED3DFMT_R32G32B32_SINT); + FMT_TO_STR(WINED3DFMT_R16G16B16A16_TYPELESS); + FMT_TO_STR(WINED3DFMT_R16G16B16A16_FLOAT); + FMT_TO_STR(WINED3DFMT_R16G16B16A16_UNORM); + FMT_TO_STR(WINED3DFMT_R16G16B16A16_UINT); + FMT_TO_STR(WINED3DFMT_R16G16B16A16_SNORM); + FMT_TO_STR(WINED3DFMT_R16G16B16A16_SINT); + FMT_TO_STR(WINED3DFMT_R32G32_TYPELESS); + FMT_TO_STR(WINED3DFMT_R32G32_FLOAT); + FMT_TO_STR(WINED3DFMT_R32G32_UINT); + FMT_TO_STR(WINED3DFMT_R32G32_SINT); + FMT_TO_STR(WINED3DFMT_R32G8X24_TYPELESS); + FMT_TO_STR(WINED3DFMT_D32_FLOAT_S8X24_UINT); + FMT_TO_STR(WINED3DFMT_R32_FLOAT_X8X24_TYPELESS); + FMT_TO_STR(WINED3DFMT_X32_TYPELESS_G8X24_UINT); + FMT_TO_STR(WINED3DFMT_R10G10B10A2_TYPELESS); + FMT_TO_STR(WINED3DFMT_R10G10B10A2_UNORM); + FMT_TO_STR(WINED3DFMT_R10G10B10A2_UINT); + FMT_TO_STR(WINED3DFMT_R10G10B10A2_SNORM); + FMT_TO_STR(WINED3DFMT_R10G10B10_XR_BIAS_A2_UNORM); + FMT_TO_STR(WINED3DFMT_R11G11B10_FLOAT); + FMT_TO_STR(WINED3DFMT_R8G8B8A8_TYPELESS); + FMT_TO_STR(WINED3DFMT_R8G8B8A8_UNORM); + FMT_TO_STR(WINED3DFMT_R8G8B8A8_UNORM_SRGB); + FMT_TO_STR(WINED3DFMT_R8G8B8A8_UINT); + FMT_TO_STR(WINED3DFMT_R8G8B8A8_SNORM); + FMT_TO_STR(WINED3DFMT_R8G8B8A8_SINT); + FMT_TO_STR(WINED3DFMT_R16G16_TYPELESS); + FMT_TO_STR(WINED3DFMT_R16G16_FLOAT); + FMT_TO_STR(WINED3DFMT_R16G16_UNORM); + FMT_TO_STR(WINED3DFMT_R16G16_UINT); + FMT_TO_STR(WINED3DFMT_R16G16_SNORM); + FMT_TO_STR(WINED3DFMT_R16G16_SINT); + FMT_TO_STR(WINED3DFMT_R32_TYPELESS); + FMT_TO_STR(WINED3DFMT_D32_FLOAT); + FMT_TO_STR(WINED3DFMT_R32_FLOAT); + FMT_TO_STR(WINED3DFMT_R32_UINT); + FMT_TO_STR(WINED3DFMT_R32_SINT); + FMT_TO_STR(WINED3DFMT_R24G8_TYPELESS); + FMT_TO_STR(WINED3DFMT_D24_UNORM_S8_UINT); + FMT_TO_STR(WINED3DFMT_R24_UNORM_X8_TYPELESS); + FMT_TO_STR(WINED3DFMT_X24_TYPELESS_G8_UINT); + FMT_TO_STR(WINED3DFMT_R8G8_TYPELESS); + FMT_TO_STR(WINED3DFMT_R8G8_UNORM); + FMT_TO_STR(WINED3DFMT_R8G8_UINT); + FMT_TO_STR(WINED3DFMT_R8G8_SNORM); + FMT_TO_STR(WINED3DFMT_R8G8_SINT); + FMT_TO_STR(WINED3DFMT_R16_TYPELESS); + FMT_TO_STR(WINED3DFMT_R16_FLOAT); + FMT_TO_STR(WINED3DFMT_D16_UNORM); + FMT_TO_STR(WINED3DFMT_R16_UNORM); + FMT_TO_STR(WINED3DFMT_R16_UINT); + FMT_TO_STR(WINED3DFMT_R16_SNORM); + FMT_TO_STR(WINED3DFMT_R16_SINT); + FMT_TO_STR(WINED3DFMT_R8_TYPELESS); + FMT_TO_STR(WINED3DFMT_R8_UNORM); + FMT_TO_STR(WINED3DFMT_R8_UINT); + FMT_TO_STR(WINED3DFMT_R8_SNORM); + FMT_TO_STR(WINED3DFMT_R8_SINT); + FMT_TO_STR(WINED3DFMT_A8_UNORM); + FMT_TO_STR(WINED3DFMT_R1_UNORM); + FMT_TO_STR(WINED3DFMT_R9G9B9E5_SHAREDEXP); + FMT_TO_STR(WINED3DFMT_R8G8_B8G8_UNORM); + FMT_TO_STR(WINED3DFMT_G8R8_G8B8_UNORM); + FMT_TO_STR(WINED3DFMT_BC1_TYPELESS); + FMT_TO_STR(WINED3DFMT_BC1_UNORM); + FMT_TO_STR(WINED3DFMT_BC1_UNORM_SRGB); + FMT_TO_STR(WINED3DFMT_BC2_TYPELESS); + FMT_TO_STR(WINED3DFMT_BC2_UNORM); + FMT_TO_STR(WINED3DFMT_BC2_UNORM_SRGB); + FMT_TO_STR(WINED3DFMT_BC3_TYPELESS); + FMT_TO_STR(WINED3DFMT_BC3_UNORM); + FMT_TO_STR(WINED3DFMT_BC3_UNORM_SRGB); + FMT_TO_STR(WINED3DFMT_BC4_TYPELESS); + FMT_TO_STR(WINED3DFMT_BC4_UNORM); + FMT_TO_STR(WINED3DFMT_BC4_SNORM); + FMT_TO_STR(WINED3DFMT_BC5_TYPELESS); + FMT_TO_STR(WINED3DFMT_BC5_UNORM); + FMT_TO_STR(WINED3DFMT_BC5_SNORM); + FMT_TO_STR(WINED3DFMT_B5G6R5_UNORM); + FMT_TO_STR(WINED3DFMT_B5G5R5A1_UNORM); + FMT_TO_STR(WINED3DFMT_B8G8R8A8_UNORM); + FMT_TO_STR(WINED3DFMT_B8G8R8X8_UNORM); + FMT_TO_STR(WINED3DFMT_B8G8R8A8_TYPELESS); + FMT_TO_STR(WINED3DFMT_B8G8R8A8_UNORM_SRGB); + FMT_TO_STR(WINED3DFMT_B8G8R8X8_TYPELESS); + FMT_TO_STR(WINED3DFMT_B8G8R8X8_UNORM_SRGB); + FMT_TO_STR(WINED3DFMT_BC6H_TYPELESS); + FMT_TO_STR(WINED3DFMT_BC6H_UF16); + FMT_TO_STR(WINED3DFMT_BC6H_SF16); + FMT_TO_STR(WINED3DFMT_BC7_TYPELESS); + FMT_TO_STR(WINED3DFMT_BC7_UNORM); + FMT_TO_STR(WINED3DFMT_BC7_UNORM_SRGB); + FMT_TO_STR(WINED3DFMT_UYVY); + FMT_TO_STR(WINED3DFMT_YUY2); + FMT_TO_STR(WINED3DFMT_YV12); + FMT_TO_STR(WINED3DFMT_DXT1); + FMT_TO_STR(WINED3DFMT_DXT2); + FMT_TO_STR(WINED3DFMT_DXT3); + FMT_TO_STR(WINED3DFMT_DXT4); + FMT_TO_STR(WINED3DFMT_DXT5); + FMT_TO_STR(WINED3DFMT_MULTI2_ARGB8); + FMT_TO_STR(WINED3DFMT_G8R8_G8B8); + FMT_TO_STR(WINED3DFMT_R8G8_B8G8); + FMT_TO_STR(WINED3DFMT_ATI1N); + FMT_TO_STR(WINED3DFMT_ATI2N); + FMT_TO_STR(WINED3DFMT_INST); + FMT_TO_STR(WINED3DFMT_NVDB); + FMT_TO_STR(WINED3DFMT_NVHU); + FMT_TO_STR(WINED3DFMT_NVHS); + FMT_TO_STR(WINED3DFMT_INTZ); + FMT_TO_STR(WINED3DFMT_RESZ); + FMT_TO_STR(WINED3DFMT_NULL); + FMT_TO_STR(WINED3DFMT_R16); + FMT_TO_STR(WINED3DFMT_AL16); + FMT_TO_STR(WINED3DFMT_NV12); + FMT_TO_STR(WINED3DFMT_ATOC); +#undef FMT_TO_STR + default: + { + char fourcc[5]; + fourcc[0] = (char)(format_id); + fourcc[1] = (char)(format_id >> 8); + fourcc[2] = (char)(format_id >> 16); + fourcc[3] = (char)(format_id >> 24); + fourcc[4] = 0; + if (isprint(fourcc[0]) && isprint(fourcc[1]) && isprint(fourcc[2]) && isprint(fourcc[3])) + FIXME("Unrecognized %#x (as fourcc: %s) WINED3DFORMAT!\n", format_id, fourcc); + else + FIXME("Unrecognized %#x WINED3DFORMAT!\n", format_id); + } + return "unrecognized"; + } +} + +const char *debug_d3ddevicetype(enum wined3d_device_type device_type) +{ + switch (device_type) + { +#define DEVTYPE_TO_STR(dev) case dev: return #dev + DEVTYPE_TO_STR(WINED3D_DEVICE_TYPE_HAL); + DEVTYPE_TO_STR(WINED3D_DEVICE_TYPE_REF); + DEVTYPE_TO_STR(WINED3D_DEVICE_TYPE_SW); +#undef DEVTYPE_TO_STR + default: + FIXME("Unrecognized device type %#x.\n", device_type); + return "unrecognized"; + } +} + +struct debug_buffer +{ + char str[200]; /* wine_dbg_sprintf() limits string size to 200 */ + char *ptr; + int size; +}; + +static void init_debug_buffer(struct debug_buffer *buffer, const char *default_string) +{ + snprintf(buffer->str, sizeof(buffer->str), "%s", default_string); + buffer->ptr = buffer->str; + buffer->size = sizeof(buffer->str); +} + +static void debug_append(struct debug_buffer *buffer, const char *str, const char *separator) +{ + int size; + + if (!separator || buffer->ptr == buffer->str) + separator = ""; + size = snprintf(buffer->ptr, buffer->size, "%s%s", separator, str); + if (size == -1 || size >= buffer->size) + { + buffer->size = 0; + strcpy(&buffer->str[sizeof(buffer->str) - 4], "..."); + return; + } + + buffer->ptr += size; + buffer->size -= size; +} + +const char *wined3d_debug_resource_access(DWORD access) +{ + struct debug_buffer buffer; + + init_debug_buffer(&buffer, "0"); +#define ACCESS_TO_STR(x) if (access & x) { debug_append(&buffer, #x, " | "); access &= ~x; } + ACCESS_TO_STR(WINED3D_RESOURCE_ACCESS_GPU); + ACCESS_TO_STR(WINED3D_RESOURCE_ACCESS_CPU); + ACCESS_TO_STR(WINED3D_RESOURCE_ACCESS_MAP_R); + ACCESS_TO_STR(WINED3D_RESOURCE_ACCESS_MAP_W); +#undef ACCESS_TO_STR + if (access) + FIXME("Unrecognised access flag(s) %#x.\n", access); + + return wine_dbg_sprintf("%s", buffer.str); +} + +const char *wined3d_debug_bind_flags(DWORD bind_flags) +{ + struct debug_buffer buffer; + + init_debug_buffer(&buffer, "0"); +#define BIND_FLAG_TO_STR(x) if (bind_flags & x) { debug_append(&buffer, #x, " | "); bind_flags &= ~x; } + BIND_FLAG_TO_STR(WINED3D_BIND_VERTEX_BUFFER); + BIND_FLAG_TO_STR(WINED3D_BIND_INDEX_BUFFER); + BIND_FLAG_TO_STR(WINED3D_BIND_CONSTANT_BUFFER); + BIND_FLAG_TO_STR(WINED3D_BIND_SHADER_RESOURCE); + BIND_FLAG_TO_STR(WINED3D_BIND_STREAM_OUTPUT); + BIND_FLAG_TO_STR(WINED3D_BIND_RENDER_TARGET); + BIND_FLAG_TO_STR(WINED3D_BIND_DEPTH_STENCIL); + BIND_FLAG_TO_STR(WINED3D_BIND_UNORDERED_ACCESS); + BIND_FLAG_TO_STR(WINED3D_BIND_INDIRECT_BUFFER); +#undef BIND_FLAG_TO_STR + if (bind_flags) + FIXME("Unrecognised bind flag(s) %#x.\n", bind_flags); + + return wine_dbg_sprintf("%s", buffer.str); +} + +const char *wined3d_debug_view_desc(const struct wined3d_view_desc *d, const struct wined3d_resource *resource) +{ + struct debug_buffer buffer; + unsigned int flags = d->flags; + + init_debug_buffer(&buffer, "0"); +#define VIEW_FLAG_TO_STR(x) if (flags & x) { debug_append(&buffer, #x, " | "); flags &= ~x; } + VIEW_FLAG_TO_STR(WINED3D_VIEW_BUFFER_RAW); + VIEW_FLAG_TO_STR(WINED3D_VIEW_BUFFER_APPEND); + VIEW_FLAG_TO_STR(WINED3D_VIEW_BUFFER_COUNTER); + VIEW_FLAG_TO_STR(WINED3D_VIEW_TEXTURE_CUBE); + VIEW_FLAG_TO_STR(WINED3D_VIEW_TEXTURE_ARRAY); +#undef VIEW_FLAG_TO_STR + if (flags) + FIXME("Unrecognised view flag(s) %#x.\n", flags); + + if (resource->type == WINED3D_RTYPE_BUFFER) + return wine_dbg_sprintf("format %s, flags %s, start_idx %u, count %u", + debug_d3dformat(d->format_id), buffer.str, d->u.buffer.start_idx, d->u.buffer.count); + else + return wine_dbg_sprintf("format %s, flags %s, level_idx %u, level_count %u, layer_idx %u, layer_count %u", + debug_d3dformat(d->format_id), buffer.str, d->u.texture.level_idx, d->u.texture.level_count, + d->u.texture.layer_idx, d->u.texture.layer_count); +} + +const char *debug_d3dusage(DWORD usage) +{ + struct debug_buffer buffer; + + init_debug_buffer(&buffer, "0"); +#define WINED3DUSAGE_TO_STR(x) if (usage & x) { debug_append(&buffer, #x, " | "); usage &= ~x; } + WINED3DUSAGE_TO_STR(WINED3DUSAGE_SOFTWAREPROCESSING); + WINED3DUSAGE_TO_STR(WINED3DUSAGE_DONOTCLIP); + WINED3DUSAGE_TO_STR(WINED3DUSAGE_POINTS); + WINED3DUSAGE_TO_STR(WINED3DUSAGE_RTPATCHES); + WINED3DUSAGE_TO_STR(WINED3DUSAGE_NPATCHES); + WINED3DUSAGE_TO_STR(WINED3DUSAGE_DYNAMIC); + WINED3DUSAGE_TO_STR(WINED3DUSAGE_RESTRICTED_CONTENT); + WINED3DUSAGE_TO_STR(WINED3DUSAGE_RESTRICT_SHARED_RESOURCE_DRIVER); + WINED3DUSAGE_TO_STR(WINED3DUSAGE_RESTRICT_SHARED_RESOURCE); + WINED3DUSAGE_TO_STR(WINED3DUSAGE_DMAP); + WINED3DUSAGE_TO_STR(WINED3DUSAGE_TEXTAPI); + WINED3DUSAGE_TO_STR(WINED3DUSAGE_LEGACY_CUBEMAP); + WINED3DUSAGE_TO_STR(WINED3DUSAGE_OWNDC); + WINED3DUSAGE_TO_STR(WINED3DUSAGE_STATICDECL); + WINED3DUSAGE_TO_STR(WINED3DUSAGE_OVERLAY); + WINED3DUSAGE_TO_STR(WINED3DUSAGE_QUERY_FILTER); + WINED3DUSAGE_TO_STR(WINED3DUSAGE_QUERY_GENMIPMAP); + WINED3DUSAGE_TO_STR(WINED3DUSAGE_QUERY_LEGACYBUMPMAP); + WINED3DUSAGE_TO_STR(WINED3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING); + WINED3DUSAGE_TO_STR(WINED3DUSAGE_QUERY_SRGBREAD); + WINED3DUSAGE_TO_STR(WINED3DUSAGE_QUERY_SRGBWRITE); + WINED3DUSAGE_TO_STR(WINED3DUSAGE_QUERY_VERTEXTEXTURE); + WINED3DUSAGE_TO_STR(WINED3DUSAGE_QUERY_WRAPANDMIP); +#undef WINED3DUSAGE_TO_STR + if (usage) + FIXME("Unrecognized usage flag(s) %#x.\n", usage); + + return wine_dbg_sprintf("%s", buffer.str); +} + +const char *debug_d3ddeclmethod(enum wined3d_decl_method method) +{ + switch (method) + { +#define WINED3DDECLMETHOD_TO_STR(u) case u: return #u + WINED3DDECLMETHOD_TO_STR(WINED3D_DECL_METHOD_DEFAULT); + WINED3DDECLMETHOD_TO_STR(WINED3D_DECL_METHOD_PARTIAL_U); + WINED3DDECLMETHOD_TO_STR(WINED3D_DECL_METHOD_PARTIAL_V); + WINED3DDECLMETHOD_TO_STR(WINED3D_DECL_METHOD_CROSS_UV); + WINED3DDECLMETHOD_TO_STR(WINED3D_DECL_METHOD_UV); + WINED3DDECLMETHOD_TO_STR(WINED3D_DECL_METHOD_LOOKUP); + WINED3DDECLMETHOD_TO_STR(WINED3D_DECL_METHOD_LOOKUP_PRESAMPLED); +#undef WINED3DDECLMETHOD_TO_STR + default: + FIXME("Unrecognized declaration method %#x.\n", method); + return "unrecognized"; + } +} + +const char *debug_d3ddeclusage(enum wined3d_decl_usage usage) +{ + switch (usage) + { +#define WINED3DDECLUSAGE_TO_STR(u) case u: return #u + WINED3DDECLUSAGE_TO_STR(WINED3D_DECL_USAGE_POSITION); + WINED3DDECLUSAGE_TO_STR(WINED3D_DECL_USAGE_BLEND_WEIGHT); + WINED3DDECLUSAGE_TO_STR(WINED3D_DECL_USAGE_BLEND_INDICES); + WINED3DDECLUSAGE_TO_STR(WINED3D_DECL_USAGE_NORMAL); + WINED3DDECLUSAGE_TO_STR(WINED3D_DECL_USAGE_PSIZE); + WINED3DDECLUSAGE_TO_STR(WINED3D_DECL_USAGE_TEXCOORD); + WINED3DDECLUSAGE_TO_STR(WINED3D_DECL_USAGE_TANGENT); + WINED3DDECLUSAGE_TO_STR(WINED3D_DECL_USAGE_BINORMAL); + WINED3DDECLUSAGE_TO_STR(WINED3D_DECL_USAGE_TESS_FACTOR); + WINED3DDECLUSAGE_TO_STR(WINED3D_DECL_USAGE_POSITIONT); + WINED3DDECLUSAGE_TO_STR(WINED3D_DECL_USAGE_COLOR); + WINED3DDECLUSAGE_TO_STR(WINED3D_DECL_USAGE_FOG); + WINED3DDECLUSAGE_TO_STR(WINED3D_DECL_USAGE_DEPTH); + WINED3DDECLUSAGE_TO_STR(WINED3D_DECL_USAGE_SAMPLE); +#undef WINED3DDECLUSAGE_TO_STR + default: + FIXME("Unrecognized %u declaration usage!\n", usage); + return "unrecognized"; + } +} + +const char *debug_d3dinput_classification(enum wined3d_input_classification classification) +{ + switch (classification) + { +#define WINED3D_TO_STR(x) case x: return #x + WINED3D_TO_STR(WINED3D_INPUT_PER_VERTEX_DATA); + WINED3D_TO_STR(WINED3D_INPUT_PER_INSTANCE_DATA); +#undef WINED3D_TO_STR + default: + FIXME("Unrecognized input classification %#x.\n", classification); + return "unrecognized"; + } +} + +const char *debug_d3dresourcetype(enum wined3d_resource_type resource_type) +{ + switch (resource_type) + { +#define WINED3D_TO_STR(x) case x: return #x + WINED3D_TO_STR(WINED3D_RTYPE_NONE); + WINED3D_TO_STR(WINED3D_RTYPE_BUFFER); + WINED3D_TO_STR(WINED3D_RTYPE_TEXTURE_1D); + WINED3D_TO_STR(WINED3D_RTYPE_TEXTURE_2D); + WINED3D_TO_STR(WINED3D_RTYPE_TEXTURE_3D); +#undef WINED3D_TO_STR + default: + FIXME("Unrecognized resource type %#x.\n", resource_type); + return "unrecognized"; + } +} + +const char *debug_d3dprimitivetype(enum wined3d_primitive_type primitive_type) +{ + switch (primitive_type) + { +#define PRIM_TO_STR(prim) case prim: return #prim + PRIM_TO_STR(WINED3D_PT_UNDEFINED); + PRIM_TO_STR(WINED3D_PT_POINTLIST); + PRIM_TO_STR(WINED3D_PT_LINELIST); + PRIM_TO_STR(WINED3D_PT_LINESTRIP); + PRIM_TO_STR(WINED3D_PT_TRIANGLELIST); + PRIM_TO_STR(WINED3D_PT_TRIANGLESTRIP); + PRIM_TO_STR(WINED3D_PT_TRIANGLEFAN); + PRIM_TO_STR(WINED3D_PT_LINELIST_ADJ); + PRIM_TO_STR(WINED3D_PT_LINESTRIP_ADJ); + PRIM_TO_STR(WINED3D_PT_TRIANGLELIST_ADJ); + PRIM_TO_STR(WINED3D_PT_TRIANGLESTRIP_ADJ); + PRIM_TO_STR(WINED3D_PT_PATCH); +#undef PRIM_TO_STR + default: + FIXME("Unrecognized primitive type %#x.\n", primitive_type); + return "unrecognized"; + } +} + +const char *debug_d3drenderstate(enum wined3d_render_state state) +{ + switch (state) + { +#define D3DSTATE_TO_STR(u) case u: return #u + D3DSTATE_TO_STR(WINED3D_RS_ANTIALIAS); + D3DSTATE_TO_STR(WINED3D_RS_TEXTUREPERSPECTIVE); + D3DSTATE_TO_STR(WINED3D_RS_WRAPU); + D3DSTATE_TO_STR(WINED3D_RS_WRAPV); + D3DSTATE_TO_STR(WINED3D_RS_ZENABLE); + D3DSTATE_TO_STR(WINED3D_RS_FILLMODE); + D3DSTATE_TO_STR(WINED3D_RS_SHADEMODE); + D3DSTATE_TO_STR(WINED3D_RS_LINEPATTERN); + D3DSTATE_TO_STR(WINED3D_RS_MONOENABLE); + D3DSTATE_TO_STR(WINED3D_RS_ROP2); + D3DSTATE_TO_STR(WINED3D_RS_PLANEMASK); + D3DSTATE_TO_STR(WINED3D_RS_ZWRITEENABLE); + D3DSTATE_TO_STR(WINED3D_RS_ALPHATESTENABLE); + D3DSTATE_TO_STR(WINED3D_RS_LASTPIXEL); + D3DSTATE_TO_STR(WINED3D_RS_SRCBLEND); + D3DSTATE_TO_STR(WINED3D_RS_DESTBLEND); + D3DSTATE_TO_STR(WINED3D_RS_CULLMODE); + D3DSTATE_TO_STR(WINED3D_RS_ZFUNC); + D3DSTATE_TO_STR(WINED3D_RS_ALPHAREF); + D3DSTATE_TO_STR(WINED3D_RS_ALPHAFUNC); + D3DSTATE_TO_STR(WINED3D_RS_DITHERENABLE); + D3DSTATE_TO_STR(WINED3D_RS_ALPHABLENDENABLE); + D3DSTATE_TO_STR(WINED3D_RS_FOGENABLE); + D3DSTATE_TO_STR(WINED3D_RS_SPECULARENABLE); + D3DSTATE_TO_STR(WINED3D_RS_ZVISIBLE); + D3DSTATE_TO_STR(WINED3D_RS_SUBPIXEL); + D3DSTATE_TO_STR(WINED3D_RS_SUBPIXELX); + D3DSTATE_TO_STR(WINED3D_RS_STIPPLEDALPHA); + D3DSTATE_TO_STR(WINED3D_RS_FOGCOLOR); + D3DSTATE_TO_STR(WINED3D_RS_FOGTABLEMODE); + D3DSTATE_TO_STR(WINED3D_RS_FOGSTART); + D3DSTATE_TO_STR(WINED3D_RS_FOGEND); + D3DSTATE_TO_STR(WINED3D_RS_FOGDENSITY); + D3DSTATE_TO_STR(WINED3D_RS_STIPPLEENABLE); + D3DSTATE_TO_STR(WINED3D_RS_COLORKEYENABLE); + D3DSTATE_TO_STR(WINED3D_RS_MIPMAPLODBIAS); + D3DSTATE_TO_STR(WINED3D_RS_RANGEFOGENABLE); + D3DSTATE_TO_STR(WINED3D_RS_ANISOTROPY); + D3DSTATE_TO_STR(WINED3D_RS_FLUSHBATCH); + D3DSTATE_TO_STR(WINED3D_RS_TRANSLUCENTSORTINDEPENDENT); + D3DSTATE_TO_STR(WINED3D_RS_STENCILENABLE); + D3DSTATE_TO_STR(WINED3D_RS_STENCILFAIL); + D3DSTATE_TO_STR(WINED3D_RS_STENCILZFAIL); + D3DSTATE_TO_STR(WINED3D_RS_STENCILPASS); + D3DSTATE_TO_STR(WINED3D_RS_STENCILFUNC); + D3DSTATE_TO_STR(WINED3D_RS_STENCILREF); + D3DSTATE_TO_STR(WINED3D_RS_STENCILMASK); + D3DSTATE_TO_STR(WINED3D_RS_STENCILWRITEMASK); + D3DSTATE_TO_STR(WINED3D_RS_TEXTUREFACTOR); + D3DSTATE_TO_STR(WINED3D_RS_WRAP0); + D3DSTATE_TO_STR(WINED3D_RS_WRAP1); + D3DSTATE_TO_STR(WINED3D_RS_WRAP2); + D3DSTATE_TO_STR(WINED3D_RS_WRAP3); + D3DSTATE_TO_STR(WINED3D_RS_WRAP4); + D3DSTATE_TO_STR(WINED3D_RS_WRAP5); + D3DSTATE_TO_STR(WINED3D_RS_WRAP6); + D3DSTATE_TO_STR(WINED3D_RS_WRAP7); + D3DSTATE_TO_STR(WINED3D_RS_CLIPPING); + D3DSTATE_TO_STR(WINED3D_RS_LIGHTING); + D3DSTATE_TO_STR(WINED3D_RS_EXTENTS); + D3DSTATE_TO_STR(WINED3D_RS_AMBIENT); + D3DSTATE_TO_STR(WINED3D_RS_FOGVERTEXMODE); + D3DSTATE_TO_STR(WINED3D_RS_COLORVERTEX); + D3DSTATE_TO_STR(WINED3D_RS_LOCALVIEWER); + D3DSTATE_TO_STR(WINED3D_RS_NORMALIZENORMALS); + D3DSTATE_TO_STR(WINED3D_RS_COLORKEYBLENDENABLE); + D3DSTATE_TO_STR(WINED3D_RS_DIFFUSEMATERIALSOURCE); + D3DSTATE_TO_STR(WINED3D_RS_SPECULARMATERIALSOURCE); + D3DSTATE_TO_STR(WINED3D_RS_AMBIENTMATERIALSOURCE); + D3DSTATE_TO_STR(WINED3D_RS_EMISSIVEMATERIALSOURCE); + D3DSTATE_TO_STR(WINED3D_RS_VERTEXBLEND); + D3DSTATE_TO_STR(WINED3D_RS_CLIPPLANEENABLE); + D3DSTATE_TO_STR(WINED3D_RS_SOFTWAREVERTEXPROCESSING); + D3DSTATE_TO_STR(WINED3D_RS_POINTSIZE); + D3DSTATE_TO_STR(WINED3D_RS_POINTSIZE_MIN); + D3DSTATE_TO_STR(WINED3D_RS_POINTSPRITEENABLE); + D3DSTATE_TO_STR(WINED3D_RS_POINTSCALEENABLE); + D3DSTATE_TO_STR(WINED3D_RS_POINTSCALE_A); + D3DSTATE_TO_STR(WINED3D_RS_POINTSCALE_B); + D3DSTATE_TO_STR(WINED3D_RS_POINTSCALE_C); + D3DSTATE_TO_STR(WINED3D_RS_MULTISAMPLEANTIALIAS); + D3DSTATE_TO_STR(WINED3D_RS_MULTISAMPLEMASK); + D3DSTATE_TO_STR(WINED3D_RS_PATCHEDGESTYLE); + D3DSTATE_TO_STR(WINED3D_RS_PATCHSEGMENTS); + D3DSTATE_TO_STR(WINED3D_RS_DEBUGMONITORTOKEN); + D3DSTATE_TO_STR(WINED3D_RS_POINTSIZE_MAX); + D3DSTATE_TO_STR(WINED3D_RS_INDEXEDVERTEXBLENDENABLE); + D3DSTATE_TO_STR(WINED3D_RS_COLORWRITEENABLE); + D3DSTATE_TO_STR(WINED3D_RS_TWEENFACTOR); + D3DSTATE_TO_STR(WINED3D_RS_BLENDOP); + D3DSTATE_TO_STR(WINED3D_RS_POSITIONDEGREE); + D3DSTATE_TO_STR(WINED3D_RS_NORMALDEGREE); + D3DSTATE_TO_STR(WINED3D_RS_SCISSORTESTENABLE); + D3DSTATE_TO_STR(WINED3D_RS_SLOPESCALEDEPTHBIAS); + D3DSTATE_TO_STR(WINED3D_RS_ANTIALIASEDLINEENABLE); + D3DSTATE_TO_STR(WINED3D_RS_MINTESSELLATIONLEVEL); + D3DSTATE_TO_STR(WINED3D_RS_MAXTESSELLATIONLEVEL); + D3DSTATE_TO_STR(WINED3D_RS_ADAPTIVETESS_X); + D3DSTATE_TO_STR(WINED3D_RS_ADAPTIVETESS_Y); + D3DSTATE_TO_STR(WINED3D_RS_ADAPTIVETESS_Z); + D3DSTATE_TO_STR(WINED3D_RS_ADAPTIVETESS_W); + D3DSTATE_TO_STR(WINED3D_RS_ENABLEADAPTIVETESSELLATION); + D3DSTATE_TO_STR(WINED3D_RS_TWOSIDEDSTENCILMODE); + D3DSTATE_TO_STR(WINED3D_RS_BACK_STENCILFAIL); + D3DSTATE_TO_STR(WINED3D_RS_BACK_STENCILZFAIL); + D3DSTATE_TO_STR(WINED3D_RS_BACK_STENCILPASS); + D3DSTATE_TO_STR(WINED3D_RS_BACK_STENCILFUNC); + D3DSTATE_TO_STR(WINED3D_RS_COLORWRITEENABLE1); + D3DSTATE_TO_STR(WINED3D_RS_COLORWRITEENABLE2); + D3DSTATE_TO_STR(WINED3D_RS_COLORWRITEENABLE3); + D3DSTATE_TO_STR(WINED3D_RS_SRGBWRITEENABLE); + D3DSTATE_TO_STR(WINED3D_RS_DEPTHBIAS); + D3DSTATE_TO_STR(WINED3D_RS_WRAP8); + D3DSTATE_TO_STR(WINED3D_RS_WRAP9); + D3DSTATE_TO_STR(WINED3D_RS_WRAP10); + D3DSTATE_TO_STR(WINED3D_RS_WRAP11); + D3DSTATE_TO_STR(WINED3D_RS_WRAP12); + D3DSTATE_TO_STR(WINED3D_RS_WRAP13); + D3DSTATE_TO_STR(WINED3D_RS_WRAP14); + D3DSTATE_TO_STR(WINED3D_RS_WRAP15); + D3DSTATE_TO_STR(WINED3D_RS_SEPARATEALPHABLENDENABLE); + D3DSTATE_TO_STR(WINED3D_RS_SRCBLENDALPHA); + D3DSTATE_TO_STR(WINED3D_RS_DESTBLENDALPHA); + D3DSTATE_TO_STR(WINED3D_RS_BLENDOPALPHA); +#undef D3DSTATE_TO_STR + default: + FIXME("Unrecognized %u render state!\n", state); + return "unrecognized"; + } +} + +const char *debug_d3dsamplerstate(enum wined3d_sampler_state state) +{ + switch (state) + { +#define D3DSTATE_TO_STR(u) case u: return #u + D3DSTATE_TO_STR(WINED3D_SAMP_BORDER_COLOR); + D3DSTATE_TO_STR(WINED3D_SAMP_ADDRESS_U); + D3DSTATE_TO_STR(WINED3D_SAMP_ADDRESS_V); + D3DSTATE_TO_STR(WINED3D_SAMP_ADDRESS_W); + D3DSTATE_TO_STR(WINED3D_SAMP_MAG_FILTER); + D3DSTATE_TO_STR(WINED3D_SAMP_MIN_FILTER); + D3DSTATE_TO_STR(WINED3D_SAMP_MIP_FILTER); + D3DSTATE_TO_STR(WINED3D_SAMP_MIPMAP_LOD_BIAS); + D3DSTATE_TO_STR(WINED3D_SAMP_MAX_MIP_LEVEL); + D3DSTATE_TO_STR(WINED3D_SAMP_MAX_ANISOTROPY); + D3DSTATE_TO_STR(WINED3D_SAMP_SRGB_TEXTURE); + D3DSTATE_TO_STR(WINED3D_SAMP_ELEMENT_INDEX); + D3DSTATE_TO_STR(WINED3D_SAMP_DMAP_OFFSET); +#undef D3DSTATE_TO_STR + default: + FIXME("Unrecognized %u sampler state!\n", state); + return "unrecognized"; + } +} + +const char *debug_d3dtexturefiltertype(enum wined3d_texture_filter_type filter_type) +{ + switch (filter_type) + { +#define D3DTEXTUREFILTERTYPE_TO_STR(u) case u: return #u + D3DTEXTUREFILTERTYPE_TO_STR(WINED3D_TEXF_NONE); + D3DTEXTUREFILTERTYPE_TO_STR(WINED3D_TEXF_POINT); + D3DTEXTUREFILTERTYPE_TO_STR(WINED3D_TEXF_LINEAR); + D3DTEXTUREFILTERTYPE_TO_STR(WINED3D_TEXF_ANISOTROPIC); + D3DTEXTUREFILTERTYPE_TO_STR(WINED3D_TEXF_FLAT_CUBIC); + D3DTEXTUREFILTERTYPE_TO_STR(WINED3D_TEXF_GAUSSIAN_CUBIC); + D3DTEXTUREFILTERTYPE_TO_STR(WINED3D_TEXF_PYRAMIDAL_QUAD); + D3DTEXTUREFILTERTYPE_TO_STR(WINED3D_TEXF_GAUSSIAN_QUAD); +#undef D3DTEXTUREFILTERTYPE_TO_STR + default: + FIXME("Unrecognized texture filter type 0x%08x.\n", filter_type); + return "unrecognized"; + } +} + +const char *debug_d3dtexturestate(enum wined3d_texture_stage_state state) +{ + switch (state) + { +#define D3DSTATE_TO_STR(u) case u: return #u + D3DSTATE_TO_STR(WINED3D_TSS_COLOR_OP); + D3DSTATE_TO_STR(WINED3D_TSS_COLOR_ARG1); + D3DSTATE_TO_STR(WINED3D_TSS_COLOR_ARG2); + D3DSTATE_TO_STR(WINED3D_TSS_ALPHA_OP); + D3DSTATE_TO_STR(WINED3D_TSS_ALPHA_ARG1); + D3DSTATE_TO_STR(WINED3D_TSS_ALPHA_ARG2); + D3DSTATE_TO_STR(WINED3D_TSS_BUMPENV_MAT00); + D3DSTATE_TO_STR(WINED3D_TSS_BUMPENV_MAT01); + D3DSTATE_TO_STR(WINED3D_TSS_BUMPENV_MAT10); + D3DSTATE_TO_STR(WINED3D_TSS_BUMPENV_MAT11); + D3DSTATE_TO_STR(WINED3D_TSS_TEXCOORD_INDEX); + D3DSTATE_TO_STR(WINED3D_TSS_BUMPENV_LSCALE); + D3DSTATE_TO_STR(WINED3D_TSS_BUMPENV_LOFFSET); + D3DSTATE_TO_STR(WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS); + D3DSTATE_TO_STR(WINED3D_TSS_COLOR_ARG0); + D3DSTATE_TO_STR(WINED3D_TSS_ALPHA_ARG0); + D3DSTATE_TO_STR(WINED3D_TSS_RESULT_ARG); + D3DSTATE_TO_STR(WINED3D_TSS_CONSTANT); +#undef D3DSTATE_TO_STR + default: + FIXME("Unrecognized %u texture state!\n", state); + return "unrecognized"; + } +} + +const char *debug_d3dtop(enum wined3d_texture_op d3dtop) +{ + switch (d3dtop) + { +#define D3DTOP_TO_STR(u) case u: return #u + D3DTOP_TO_STR(WINED3D_TOP_DISABLE); + D3DTOP_TO_STR(WINED3D_TOP_SELECT_ARG1); + D3DTOP_TO_STR(WINED3D_TOP_SELECT_ARG2); + D3DTOP_TO_STR(WINED3D_TOP_MODULATE); + D3DTOP_TO_STR(WINED3D_TOP_MODULATE_2X); + D3DTOP_TO_STR(WINED3D_TOP_MODULATE_4X); + D3DTOP_TO_STR(WINED3D_TOP_ADD); + D3DTOP_TO_STR(WINED3D_TOP_ADD_SIGNED); + D3DTOP_TO_STR(WINED3D_TOP_ADD_SIGNED_2X); + D3DTOP_TO_STR(WINED3D_TOP_SUBTRACT); + D3DTOP_TO_STR(WINED3D_TOP_ADD_SMOOTH); + D3DTOP_TO_STR(WINED3D_TOP_BLEND_DIFFUSE_ALPHA); + D3DTOP_TO_STR(WINED3D_TOP_BLEND_TEXTURE_ALPHA); + D3DTOP_TO_STR(WINED3D_TOP_BLEND_FACTOR_ALPHA); + D3DTOP_TO_STR(WINED3D_TOP_BLEND_TEXTURE_ALPHA_PM); + D3DTOP_TO_STR(WINED3D_TOP_BLEND_CURRENT_ALPHA); + D3DTOP_TO_STR(WINED3D_TOP_PREMODULATE); + D3DTOP_TO_STR(WINED3D_TOP_MODULATE_ALPHA_ADD_COLOR); + D3DTOP_TO_STR(WINED3D_TOP_MODULATE_COLOR_ADD_ALPHA); + D3DTOP_TO_STR(WINED3D_TOP_MODULATE_INVALPHA_ADD_COLOR); + D3DTOP_TO_STR(WINED3D_TOP_MODULATE_INVCOLOR_ADD_ALPHA); + D3DTOP_TO_STR(WINED3D_TOP_BUMPENVMAP); + D3DTOP_TO_STR(WINED3D_TOP_BUMPENVMAP_LUMINANCE); + D3DTOP_TO_STR(WINED3D_TOP_DOTPRODUCT3); + D3DTOP_TO_STR(WINED3D_TOP_MULTIPLY_ADD); + D3DTOP_TO_STR(WINED3D_TOP_LERP); +#undef D3DTOP_TO_STR + default: + FIXME("Unrecognized texture op %#x.\n", d3dtop); + return "unrecognized"; + } +} + +const char *debug_d3dtstype(enum wined3d_transform_state tstype) +{ + switch (tstype) + { +#define TSTYPE_TO_STR(tstype) case tstype: return #tstype + TSTYPE_TO_STR(WINED3D_TS_VIEW); + TSTYPE_TO_STR(WINED3D_TS_PROJECTION); + TSTYPE_TO_STR(WINED3D_TS_TEXTURE0); + TSTYPE_TO_STR(WINED3D_TS_TEXTURE1); + TSTYPE_TO_STR(WINED3D_TS_TEXTURE2); + TSTYPE_TO_STR(WINED3D_TS_TEXTURE3); + TSTYPE_TO_STR(WINED3D_TS_TEXTURE4); + TSTYPE_TO_STR(WINED3D_TS_TEXTURE5); + TSTYPE_TO_STR(WINED3D_TS_TEXTURE6); + TSTYPE_TO_STR(WINED3D_TS_TEXTURE7); + TSTYPE_TO_STR(WINED3D_TS_WORLD_MATRIX(0)); + TSTYPE_TO_STR(WINED3D_TS_WORLD_MATRIX(1)); + TSTYPE_TO_STR(WINED3D_TS_WORLD_MATRIX(2)); + TSTYPE_TO_STR(WINED3D_TS_WORLD_MATRIX(3)); +#undef TSTYPE_TO_STR + default: + if (tstype > 256 && tstype < 512) + { + FIXME("WINED3D_TS_WORLD_MATRIX(%u). 1..255 not currently supported.\n", tstype); + return ("WINED3D_TS_WORLD_MATRIX > 0"); + } + FIXME("Unrecognized transform state %#x.\n", tstype); + return "unrecognized"; + } +} + +const char *debug_shader_type(enum wined3d_shader_type type) +{ + switch(type) + { +#define WINED3D_TO_STR(type) case type: return #type + WINED3D_TO_STR(WINED3D_SHADER_TYPE_PIXEL); + WINED3D_TO_STR(WINED3D_SHADER_TYPE_VERTEX); + WINED3D_TO_STR(WINED3D_SHADER_TYPE_GEOMETRY); + WINED3D_TO_STR(WINED3D_SHADER_TYPE_HULL); + WINED3D_TO_STR(WINED3D_SHADER_TYPE_DOMAIN); + WINED3D_TO_STR(WINED3D_SHADER_TYPE_COMPUTE); +#undef WINED3D_TO_STR + default: + FIXME("Unrecognized shader type %#x.\n", type); + return "unrecognized"; + } +} + +const char *debug_d3dstate(DWORD state) +{ + if (STATE_IS_RENDER(state)) + return wine_dbg_sprintf("STATE_RENDER(%s)", debug_d3drenderstate(state - STATE_RENDER(0))); + if (STATE_IS_TEXTURESTAGE(state)) + { + DWORD texture_stage = (state - STATE_TEXTURESTAGE(0, 0)) / (WINED3D_HIGHEST_TEXTURE_STATE + 1); + DWORD texture_state = state - STATE_TEXTURESTAGE(texture_stage, 0); + return wine_dbg_sprintf("STATE_TEXTURESTAGE(%#x, %s)", + texture_stage, debug_d3dtexturestate(texture_state)); + } + if (STATE_IS_SAMPLER(state)) + return wine_dbg_sprintf("STATE_SAMPLER(%#x)", state - STATE_SAMPLER(0)); + if (STATE_IS_COMPUTE_SHADER(state)) + return wine_dbg_sprintf("STATE_SHADER(%s)", debug_shader_type(WINED3D_SHADER_TYPE_COMPUTE)); + if (STATE_IS_GRAPHICS_SHADER(state)) + return wine_dbg_sprintf("STATE_SHADER(%s)", debug_shader_type(state - STATE_SHADER(0))); + if (STATE_IS_COMPUTE_CONSTANT_BUFFER(state)) + return wine_dbg_sprintf("STATE_CONSTANT_BUFFER(%s)", debug_shader_type(WINED3D_SHADER_TYPE_COMPUTE)); + if (STATE_IS_GRAPHICS_CONSTANT_BUFFER(state)) + return wine_dbg_sprintf("STATE_CONSTANT_BUFFER(%s)", debug_shader_type(state - STATE_CONSTANT_BUFFER(0))); + if (STATE_IS_COMPUTE_SHADER_RESOURCE_BINDING(state)) + return "STATE_COMPUTE_SHADER_RESOURCE_BINDING"; + if (STATE_IS_GRAPHICS_SHADER_RESOURCE_BINDING(state)) + return "STATE_GRAPHICS_SHADER_RESOURCE_BINDING"; + if (STATE_IS_COMPUTE_UNORDERED_ACCESS_VIEW_BINDING(state)) + return "STATE_COMPUTE_UNORDERED_ACCESS_VIEW_BINDING"; + if (STATE_IS_GRAPHICS_UNORDERED_ACCESS_VIEW_BINDING(state)) + return "STATE_GRAPHICS_UNORDERED_ACCESS_VIEW_BINDING"; + if (STATE_IS_TRANSFORM(state)) + return wine_dbg_sprintf("STATE_TRANSFORM(%s)", debug_d3dtstype(state - STATE_TRANSFORM(0))); + if (STATE_IS_STREAMSRC(state)) + return "STATE_STREAMSRC"; + if (STATE_IS_INDEXBUFFER(state)) + return "STATE_INDEXBUFFER"; + if (STATE_IS_VDECL(state)) + return "STATE_VDECL"; + if (STATE_IS_VIEWPORT(state)) + return "STATE_VIEWPORT"; + if (STATE_IS_LIGHT_TYPE(state)) + return "STATE_LIGHT_TYPE"; + if (STATE_IS_ACTIVELIGHT(state)) + return wine_dbg_sprintf("STATE_ACTIVELIGHT(%#x)", state - STATE_ACTIVELIGHT(0)); + if (STATE_IS_SCISSORRECT(state)) + return "STATE_SCISSORRECT"; + if (STATE_IS_CLIPPLANE(state)) + return wine_dbg_sprintf("STATE_CLIPPLANE(%#x)", state - STATE_CLIPPLANE(0)); + if (STATE_IS_MATERIAL(state)) + return "STATE_MATERIAL"; + if (STATE_IS_RASTERIZER(state)) + return "STATE_RASTERIZER"; + if (STATE_IS_POINTSPRITECOORDORIGIN(state)) + return "STATE_POINTSPRITECOORDORIGIN"; + if (STATE_IS_BASEVERTEXINDEX(state)) + return "STATE_BASEVERTEXINDEX"; + if (STATE_IS_FRAMEBUFFER(state)) + return "STATE_FRAMEBUFFER"; + if (STATE_IS_POINT_ENABLE(state)) + return "STATE_POINT_ENABLE"; + if (STATE_IS_COLOR_KEY(state)) + return "STATE_COLOR_KEY"; + if (STATE_IS_STREAM_OUTPUT(state)) + return "STATE_STREAM_OUTPUT"; + if (STATE_IS_BLEND(state)) + return "STATE_BLEND"; + if (STATE_IS_BLEND_FACTOR(state)) + return "STATE_BLEND_FACTOR"; + if (STATE_IS_SAMPLE_MASK(state)) + return "STATE_SAMPLE_MASK"; + if (STATE_IS_DEPTH_STENCIL(state)) + return "STATE_DEPTH_STENCIL"; + if (STATE_IS_STENCIL_REF(state)) + return "STATE_STENCIL_REF"; + + return wine_dbg_sprintf("UNKNOWN_STATE(%#x)", state); +} + +const char *debug_fboattachment(GLenum attachment) +{ + switch(attachment) + { +#define WINED3D_TO_STR(x) case x: return #x + WINED3D_TO_STR(GL_COLOR_ATTACHMENT0); + WINED3D_TO_STR(GL_COLOR_ATTACHMENT1); + WINED3D_TO_STR(GL_COLOR_ATTACHMENT2); + WINED3D_TO_STR(GL_COLOR_ATTACHMENT3); + WINED3D_TO_STR(GL_COLOR_ATTACHMENT4); + WINED3D_TO_STR(GL_COLOR_ATTACHMENT5); + WINED3D_TO_STR(GL_COLOR_ATTACHMENT6); + WINED3D_TO_STR(GL_COLOR_ATTACHMENT7); + WINED3D_TO_STR(GL_COLOR_ATTACHMENT8); + WINED3D_TO_STR(GL_COLOR_ATTACHMENT9); + WINED3D_TO_STR(GL_COLOR_ATTACHMENT10); + WINED3D_TO_STR(GL_COLOR_ATTACHMENT11); + WINED3D_TO_STR(GL_COLOR_ATTACHMENT12); + WINED3D_TO_STR(GL_COLOR_ATTACHMENT13); + WINED3D_TO_STR(GL_COLOR_ATTACHMENT14); + WINED3D_TO_STR(GL_COLOR_ATTACHMENT15); + WINED3D_TO_STR(GL_DEPTH_ATTACHMENT); + WINED3D_TO_STR(GL_STENCIL_ATTACHMENT); +#undef WINED3D_TO_STR + default: + return wine_dbg_sprintf("Unknown FBO attachment %#x", attachment); + } +} + +const char *debug_fbostatus(GLenum status) { + switch(status) { +#define FBOSTATUS_TO_STR(u) case u: return #u + FBOSTATUS_TO_STR(GL_FRAMEBUFFER_COMPLETE); + FBOSTATUS_TO_STR(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT); + FBOSTATUS_TO_STR(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT); + FBOSTATUS_TO_STR(GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT); + FBOSTATUS_TO_STR(GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT); + FBOSTATUS_TO_STR(GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER); + FBOSTATUS_TO_STR(GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER); + FBOSTATUS_TO_STR(GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE); + FBOSTATUS_TO_STR(GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS); + FBOSTATUS_TO_STR(GL_FRAMEBUFFER_INCOMPLETE_LAYER_COUNT_ARB); + FBOSTATUS_TO_STR(GL_FRAMEBUFFER_UNSUPPORTED); + FBOSTATUS_TO_STR(GL_FRAMEBUFFER_UNDEFINED); +#undef FBOSTATUS_TO_STR + default: + FIXME("Unrecognized FBO status 0x%08x.\n", status); + return "unrecognized"; + } +} + +const char *debug_glerror(GLenum error) { + switch(error) { +#define GLERROR_TO_STR(u) case u: return #u + GLERROR_TO_STR(GL_NO_ERROR); + GLERROR_TO_STR(GL_INVALID_ENUM); + GLERROR_TO_STR(GL_INVALID_VALUE); + GLERROR_TO_STR(GL_INVALID_OPERATION); + GLERROR_TO_STR(GL_STACK_OVERFLOW); + GLERROR_TO_STR(GL_STACK_UNDERFLOW); + GLERROR_TO_STR(GL_OUT_OF_MEMORY); + GLERROR_TO_STR(GL_INVALID_FRAMEBUFFER_OPERATION); +#undef GLERROR_TO_STR + default: + FIXME("Unrecognized GL error 0x%08x.\n", error); + return "unrecognized"; + } +} + +const char *wined3d_debug_vkresult(VkResult vr) +{ + switch (vr) + { +#define WINED3D_TO_STR(x) case x: return #x + WINED3D_TO_STR(VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS_KHR); + WINED3D_TO_STR(VK_ERROR_NOT_PERMITTED_EXT); + WINED3D_TO_STR(VK_ERROR_FRAGMENTATION_EXT); + WINED3D_TO_STR(VK_ERROR_INVALID_EXTERNAL_HANDLE); + WINED3D_TO_STR(VK_ERROR_OUT_OF_POOL_MEMORY); + WINED3D_TO_STR(VK_ERROR_INVALID_SHADER_NV); + WINED3D_TO_STR(VK_ERROR_OUT_OF_DATE_KHR); + WINED3D_TO_STR(VK_ERROR_NATIVE_WINDOW_IN_USE_KHR); + WINED3D_TO_STR(VK_ERROR_SURFACE_LOST_KHR); + WINED3D_TO_STR(VK_ERROR_FRAGMENTED_POOL); + WINED3D_TO_STR(VK_ERROR_FORMAT_NOT_SUPPORTED); + WINED3D_TO_STR(VK_ERROR_TOO_MANY_OBJECTS); + WINED3D_TO_STR(VK_ERROR_INCOMPATIBLE_DRIVER); + WINED3D_TO_STR(VK_ERROR_FEATURE_NOT_PRESENT); + WINED3D_TO_STR(VK_ERROR_EXTENSION_NOT_PRESENT); + WINED3D_TO_STR(VK_ERROR_LAYER_NOT_PRESENT); + WINED3D_TO_STR(VK_ERROR_MEMORY_MAP_FAILED); + WINED3D_TO_STR(VK_ERROR_DEVICE_LOST); + WINED3D_TO_STR(VK_ERROR_INITIALIZATION_FAILED); + WINED3D_TO_STR(VK_ERROR_OUT_OF_DEVICE_MEMORY); + WINED3D_TO_STR(VK_ERROR_OUT_OF_HOST_MEMORY); + WINED3D_TO_STR(VK_SUCCESS); + WINED3D_TO_STR(VK_NOT_READY); + WINED3D_TO_STR(VK_TIMEOUT); + WINED3D_TO_STR(VK_EVENT_SET); + WINED3D_TO_STR(VK_EVENT_RESET); + WINED3D_TO_STR(VK_INCOMPLETE); + WINED3D_TO_STR(VK_SUBOPTIMAL_KHR); +#undef WINED3D_TO_STR + default: + return wine_dbg_sprintf("unrecognised(%d)", vr); + } +} + +static const char *debug_fixup_channel_source(enum fixup_channel_source source) +{ + switch(source) + { +#define WINED3D_TO_STR(x) case x: return #x + WINED3D_TO_STR(CHANNEL_SOURCE_ZERO); + WINED3D_TO_STR(CHANNEL_SOURCE_ONE); + WINED3D_TO_STR(CHANNEL_SOURCE_X); + WINED3D_TO_STR(CHANNEL_SOURCE_Y); + WINED3D_TO_STR(CHANNEL_SOURCE_Z); + WINED3D_TO_STR(CHANNEL_SOURCE_W); + WINED3D_TO_STR(CHANNEL_SOURCE_COMPLEX0); + WINED3D_TO_STR(CHANNEL_SOURCE_COMPLEX1); +#undef WINED3D_TO_STR + default: + FIXME("Unrecognized fixup_channel_source %#x\n", source); + return "unrecognized"; + } +} + +static const char *debug_complex_fixup(enum complex_fixup fixup) +{ + switch(fixup) + { +#define WINED3D_TO_STR(x) case x: return #x + WINED3D_TO_STR(COMPLEX_FIXUP_YUY2); + WINED3D_TO_STR(COMPLEX_FIXUP_UYVY); + WINED3D_TO_STR(COMPLEX_FIXUP_YV12); + WINED3D_TO_STR(COMPLEX_FIXUP_NV12); + WINED3D_TO_STR(COMPLEX_FIXUP_P8); + WINED3D_TO_STR(COMPLEX_FIXUP_YUV); +#undef WINED3D_TO_STR + default: + FIXME("Unrecognized complex fixup %#x\n", fixup); + return "unrecognized"; + } +} + +void dump_color_fixup_desc(struct color_fixup_desc fixup) +{ + if (is_complex_fixup(fixup)) + { + TRACE("\tComplex: %s\n", debug_complex_fixup(get_complex_fixup(fixup))); + return; + } + + TRACE("\tX: %s%s\n", debug_fixup_channel_source(fixup.x_source), fixup.x_sign_fixup ? ", SIGN_FIXUP" : ""); + TRACE("\tY: %s%s\n", debug_fixup_channel_source(fixup.y_source), fixup.y_sign_fixup ? ", SIGN_FIXUP" : ""); + TRACE("\tZ: %s%s\n", debug_fixup_channel_source(fixup.z_source), fixup.z_sign_fixup ? ", SIGN_FIXUP" : ""); + TRACE("\tW: %s%s\n", debug_fixup_channel_source(fixup.w_source), fixup.w_sign_fixup ? ", SIGN_FIXUP" : ""); +} + +BOOL is_invalid_op(const struct wined3d_state *state, int stage, + enum wined3d_texture_op op, DWORD arg1, DWORD arg2, DWORD arg3) +{ + if (op == WINED3D_TOP_DISABLE) + return FALSE; + if (state->textures[stage]) + return FALSE; + + if ((arg1 & WINED3DTA_SELECTMASK) == WINED3DTA_TEXTURE + && op != WINED3D_TOP_SELECT_ARG2) + return TRUE; + if ((arg2 & WINED3DTA_SELECTMASK) == WINED3DTA_TEXTURE + && op != WINED3D_TOP_SELECT_ARG1) + return TRUE; + if ((arg3 & WINED3DTA_SELECTMASK) == WINED3DTA_TEXTURE + && (op == WINED3D_TOP_MULTIPLY_ADD || op == WINED3D_TOP_LERP)) + return TRUE; + + return FALSE; +} + +void get_identity_matrix(struct wined3d_matrix *mat) +{ + static const struct wined3d_matrix identity = + { + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f, + }; + + *mat = identity; +} + +void get_modelview_matrix(const struct wined3d_context *context, const struct wined3d_state *state, + unsigned int index, struct wined3d_matrix *mat) +{ + if (context->last_was_rhw) + get_identity_matrix(mat); + else + multiply_matrix(mat, &state->transforms[WINED3D_TS_VIEW], &state->transforms[WINED3D_TS_WORLD_MATRIX(index)]); +} + +void get_projection_matrix(const struct wined3d_context *context, const struct wined3d_state *state, + struct wined3d_matrix *mat) +{ + const struct wined3d_d3d_info *d3d_info = context->d3d_info; + BOOL clip_control, flip; + float center_offset; + + /* There are a couple of additional things we have to take into account + * here besides the projection transformation itself: + * - We need to flip along the y-axis in case of offscreen rendering. + * - OpenGL Z range is {-Wc,...,Wc} while D3D Z range is {0,...,Wc}. + * - <= D3D9 coordinates refer to pixel centers while GL coordinates + * refer to pixel corners. + * - D3D has a top-left filling convention. We need to maintain this + * even after the y-flip mentioned above. + * In order to handle the last two points, we translate by + * (63.0 / 128.0) / VPw and (63.0 / 128.0) / VPh. This is equivalent to + * translating slightly less than half a pixel. We want the difference to + * be large enough that it doesn't get lost due to rounding inside the + * driver, but small enough to prevent it from interfering with any + * anti-aliasing. */ + + clip_control = d3d_info->clip_control; + flip = !clip_control && context->render_offscreen; + if (!clip_control && d3d_info->wined3d_creation_flags & WINED3D_PIXEL_CENTER_INTEGER) + center_offset = 63.0f / 64.0f; + else + center_offset = -1.0f / 64.0f; + + if (context->last_was_rhw) + { + /* Transform D3D RHW coordinates to OpenGL clip coordinates. */ + float x = state->viewports[0].x; + float y = state->viewports[0].y; + float w = state->viewports[0].width; + float h = state->viewports[0].height; + float x_scale = 2.0f / w; + float x_offset = (center_offset - (2.0f * x) - w) / w; + float y_scale = flip ? 2.0f / h : 2.0f / -h; + float y_offset = flip + ? (center_offset - (2.0f * y) - h) / h + : (center_offset - (2.0f * y) - h) / -h; + bool zenable = state->fb.depth_stencil ? + (state->depth_stencil_state ? state->depth_stencil_state->desc.depth : true) : false; + float z_scale = zenable ? clip_control ? 1.0f : 2.0f : 0.0f; + float z_offset = zenable ? clip_control ? 0.0f : -1.0f : 0.0f; + const struct wined3d_matrix projection = + { + x_scale, 0.0f, 0.0f, 0.0f, + 0.0f, y_scale, 0.0f, 0.0f, + 0.0f, 0.0f, z_scale, 0.0f, + x_offset, y_offset, z_offset, 1.0f, + }; + + *mat = projection; + } + else + { + float y_scale = flip ? -1.0f : 1.0f; + float x_offset = center_offset / state->viewports[0].width; + float y_offset = flip + ? center_offset / state->viewports[0].height + : -center_offset / state->viewports[0].height; + float z_scale = clip_control ? 1.0f : 2.0f; + float z_offset = clip_control ? 0.0f : -1.0f; + const struct wined3d_matrix projection = + { + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, y_scale, 0.0f, 0.0f, + 0.0f, 0.0f, z_scale, 0.0f, + x_offset, y_offset, z_offset, 1.0f, + }; + + multiply_matrix(mat, &projection, &state->transforms[WINED3D_TS_PROJECTION]); + } +} + +/* Setup this textures matrix according to the texture flags. */ +static void compute_texture_matrix(const struct wined3d_matrix *matrix, uint32_t flags, BOOL calculated_coords, + BOOL transformed, enum wined3d_format_id format_id, BOOL ffp_proj_control, struct wined3d_matrix *out_matrix) +{ + struct wined3d_matrix mat; + + if (flags == WINED3D_TTFF_DISABLE || flags == WINED3D_TTFF_COUNT1 || transformed) + { + get_identity_matrix(out_matrix); + return; + } + + if (flags == (WINED3D_TTFF_COUNT1 | WINED3D_TTFF_PROJECTED)) + { + ERR("Invalid texture transform flags: WINED3D_TTFF_COUNT1 | WINED3D_TTFF_PROJECTED.\n"); + return; + } + + mat = *matrix; + + if (flags & WINED3D_TTFF_PROJECTED) + { + if (!ffp_proj_control) + { + switch (flags & ~WINED3D_TTFF_PROJECTED) + { + case WINED3D_TTFF_COUNT2: + mat._14 = mat._12; + mat._24 = mat._22; + mat._34 = mat._32; + mat._44 = mat._42; + mat._12 = mat._22 = mat._32 = mat._42 = 0.0f; + break; + case WINED3D_TTFF_COUNT3: + mat._14 = mat._13; + mat._24 = mat._23; + mat._34 = mat._33; + mat._44 = mat._43; + mat._13 = mat._23 = mat._33 = mat._43 = 0.0f; + break; + } + } + } + else + { + /* Under Direct3D the R/Z coord can be used for translation, under + * OpenGL we use the Q coord instead. */ + if (!calculated_coords) + { + switch (format_id) + { + /* Direct3D passes the default 1.0 in the 2nd coord, while GL + * passes it in the 4th. Swap 2nd and 4th coord. No need to + * store the value of mat._41 in mat._21 because the input + * value to the transformation will be 0, so the matrix value + * is irrelevant. */ + case WINED3DFMT_R32_FLOAT: + mat._41 = mat._21; + mat._42 = mat._22; + mat._43 = mat._23; + mat._44 = mat._24; + break; + /* See above, just 3rd and 4th coord. */ + case WINED3DFMT_R32G32_FLOAT: + mat._41 = mat._31; + mat._42 = mat._32; + mat._43 = mat._33; + mat._44 = mat._34; + break; + case WINED3DFMT_R32G32B32_FLOAT: /* Opengl defaults match dx defaults */ + case WINED3DFMT_R32G32B32A32_FLOAT: /* No defaults apply, all app defined */ + + /* This is to prevent swapping the matrix lines and put the default 4th coord = 1.0 + * into a bad place. The division elimination below will apply to make sure the + * 1.0 doesn't do anything bad. The caller will set this value if the stride is 0 + */ + case WINED3DFMT_UNKNOWN: /* No texture coords, 0/0/0/1 defaults are passed */ + break; + default: + FIXME("Unexpected fixed function texture coord input\n"); + } + } + if (!ffp_proj_control) + { + switch (flags & ~WINED3D_TTFF_PROJECTED) + { + /* case WINED3D_TTFF_COUNT1: Won't ever get here. */ + case WINED3D_TTFF_COUNT2: + mat._13 = mat._23 = mat._33 = mat._43 = 0.0f; + /* OpenGL divides the first 3 vertex coordinates by the 4th by + * default, which is essentially the same as D3DTTFF_PROJECTED. + * Make sure that the 4th coordinate evaluates to 1.0 to + * eliminate that. + * + * If the fixed function pipeline is used, the 4th value + * remains unused, so there is no danger in doing this. With + * vertex shaders we have a problem. Should an application hit + * that problem, the code here would have to check for pixel + * shaders, and the shader has to undo the default GL divide. + * + * A more serious problem occurs if the application passes 4 + * coordinates in, and the 4th is != 1.0 (OpenGL default). + * This would have to be fixed with immediate mode draws. */ + default: + mat._14 = mat._24 = mat._34 = 0.0f; mat._44 = 1.0f; + } + } + } + + *out_matrix = mat; +} + +void get_texture_matrix(const struct wined3d_context *context, const struct wined3d_state *state, + unsigned int tex, struct wined3d_matrix *mat) +{ + const struct wined3d_device *device = context->device; + BOOL generated = (state->texture_states[tex][WINED3D_TSS_TEXCOORD_INDEX] & 0xffff0000) + != WINED3DTSS_TCI_PASSTHRU; + unsigned int coord_idx = min(state->texture_states[tex][WINED3D_TSS_TEXCOORD_INDEX & 0x0000ffff], + WINED3D_MAX_TEXTURES - 1); + + compute_texture_matrix(&state->transforms[WINED3D_TS_TEXTURE0 + tex], + state->texture_states[tex][WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS], + generated, context->last_was_rhw, + context->stream_info.use_map & (1u << (WINED3D_FFP_TEXCOORD0 + coord_idx)) + ? context->stream_info.elements[WINED3D_FFP_TEXCOORD0 + coord_idx].format->id + : WINED3DFMT_UNKNOWN, + device->shader_backend->shader_has_ffp_proj_control(device->shader_priv), mat); + + if ((context->lastWasPow2Texture & (1u << tex)) && state->textures[tex]) + { + if (generated) + FIXME("Non-power-of-two texture being used with generated texture coords.\n"); + /* NP2 texcoord fixup is implemented for pixelshaders so only enable the + * fixed-function-pipeline fixup via pow2Matrix when no PS is used. */ + if (!use_ps(state)) + { + TRACE("Non-power-of-two texture matrix multiply fixup.\n"); + multiply_matrix(mat, mat, (struct wined3d_matrix *)state->textures[tex]->pow2_matrix); + } + } +} + +void get_pointsize_minmax(const struct wined3d_context *context, const struct wined3d_state *state, + float *out_min, float *out_max) +{ + union + { + DWORD d; + float f; + } min, max; + + min.d = state->render_states[WINED3D_RS_POINTSIZE_MIN]; + max.d = state->render_states[WINED3D_RS_POINTSIZE_MAX]; + + if (min.f > max.f) + min.f = max.f; + + *out_min = min.f; + *out_max = max.f; +} + +void get_pointsize(const struct wined3d_context *context, const struct wined3d_state *state, + float *out_pointsize, float *out_att) +{ + /* POINTSCALEENABLE controls how point size value is treated. If set to + * true, the point size is scaled with respect to height of viewport. + * When set to false point size is in pixels. */ + union + { + DWORD d; + float f; + } pointsize, a, b, c; + + out_att[0] = 1.0f; + out_att[1] = 0.0f; + out_att[2] = 0.0f; + + pointsize.d = state->render_states[WINED3D_RS_POINTSIZE]; + a.d = state->render_states[WINED3D_RS_POINTSCALE_A]; + b.d = state->render_states[WINED3D_RS_POINTSCALE_B]; + c.d = state->render_states[WINED3D_RS_POINTSCALE_C]; + + /* Always use first viewport, this path does not apply to d3d10/11 multiple viewports case. */ + if (state->render_states[WINED3D_RS_POINTSCALEENABLE]) + { + float scale_factor = state->viewports[0].height * state->viewports[0].height; + + out_att[0] = a.f / scale_factor; + out_att[1] = b.f / scale_factor; + out_att[2] = c.f / scale_factor; + } + *out_pointsize = pointsize.f; +} + +void get_fog_start_end(const struct wined3d_context *context, const struct wined3d_state *state, + float *start, float *end) +{ + union + { + DWORD d; + float f; + } tmpvalue; + + switch (context->fog_source) + { + case FOGSOURCE_VS: + *start = 1.0f; + *end = 0.0f; + break; + + case FOGSOURCE_COORD: + *start = 255.0f; + *end = 0.0f; + break; + + case FOGSOURCE_FFP: + tmpvalue.d = state->render_states[WINED3D_RS_FOGSTART]; + *start = tmpvalue.f; + tmpvalue.d = state->render_states[WINED3D_RS_FOGEND]; + *end = tmpvalue.f; + /* Special handling for fog_start == fog_end. In d3d with vertex + * fog, everything is fogged. With table fog, everything with + * fog_coord < fog_start is unfogged, and fog_coord > fog_start + * is fogged. Windows drivers disagree when fog_coord == fog_start. */ + if (state->render_states[WINED3D_RS_FOGTABLEMODE] == WINED3D_FOG_NONE && *start == *end) + { + *start = -INFINITY; + *end = 0.0f; + } + break; + + default: + /* This should not happen, context->fog_source is set in wined3d, not the app. */ + ERR("Unexpected fog coordinate source.\n"); + *start = 0.0f; + *end = 0.0f; + } +} + +static BOOL wined3d_get_primary_display(WCHAR *display) +{ + DISPLAY_DEVICEW display_device; + DWORD device_idx = 0; + + display_device.cb = sizeof(display_device); + while (EnumDisplayDevicesW(NULL, device_idx++, &display_device, 0)) + { + if (display_device.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) + { + lstrcpyW(display, display_device.DeviceName); + return TRUE; + } + } + + return FALSE; +} + +BOOL wined3d_get_primary_adapter_luid(LUID *luid) +{ + D3DKMT_OPENADAPTERFROMGDIDISPLAYNAME open_adapter_param; + D3DKMT_CLOSEADAPTER close_adapter_param; + + if (!wined3d_get_primary_display(open_adapter_param.DeviceName)) + return FALSE; + + if (D3DKMTOpenAdapterFromGdiDisplayName(&open_adapter_param)) + return FALSE; + + *luid = open_adapter_param.AdapterLuid; + close_adapter_param.hAdapter = open_adapter_param.hAdapter; + D3DKMTCloseAdapter(&close_adapter_param); + return TRUE; +} + +/* Note: It's the caller's responsibility to ensure values can be expressed + * in the requested format. UNORM formats for example can only express values + * in the range 0.0f -> 1.0f. */ +DWORD wined3d_format_convert_from_float(const struct wined3d_format *format, const struct wined3d_color *color) +{ + static const struct + { + enum wined3d_format_id format_id; + struct wined3d_vec4 mul; + struct wined3d_uvec4 shift; + } + float_conv[] = + { + {WINED3DFMT_B8G8R8A8_UNORM, { 255.0f, 255.0f, 255.0f, 255.0f}, {16, 8, 0, 24}}, + {WINED3DFMT_B8G8R8X8_UNORM, { 255.0f, 255.0f, 255.0f, 255.0f}, {16, 8, 0, 24}}, + {WINED3DFMT_B8G8R8_UNORM, { 255.0f, 255.0f, 255.0f, 255.0f}, {16, 8, 0, 24}}, + {WINED3DFMT_B5G6R5_UNORM, { 31.0f, 63.0f, 31.0f, 0.0f}, {11, 5, 0, 0}}, + {WINED3DFMT_B5G5R5A1_UNORM, { 31.0f, 31.0f, 31.0f, 1.0f}, {10, 5, 0, 15}}, + {WINED3DFMT_B5G5R5X1_UNORM, { 31.0f, 31.0f, 31.0f, 1.0f}, {10, 5, 0, 15}}, + {WINED3DFMT_R8_UNORM, { 255.0f, 0.0f, 0.0f, 0.0f}, { 0, 0, 0, 0}}, + {WINED3DFMT_A8_UNORM, { 0.0f, 0.0f, 0.0f, 255.0f}, { 0, 0, 0, 0}}, + {WINED3DFMT_B4G4R4A4_UNORM, { 15.0f, 15.0f, 15.0f, 15.0f}, { 8, 4, 0, 12}}, + {WINED3DFMT_B4G4R4X4_UNORM, { 15.0f, 15.0f, 15.0f, 15.0f}, { 8, 4, 0, 12}}, + {WINED3DFMT_B2G3R3_UNORM, { 7.0f, 7.0f, 3.0f, 0.0f}, { 5, 2, 0, 0}}, + {WINED3DFMT_R8G8B8A8_UNORM, { 255.0f, 255.0f, 255.0f, 255.0f}, { 0, 8, 16, 24}}, + {WINED3DFMT_R8G8B8X8_UNORM, { 255.0f, 255.0f, 255.0f, 255.0f}, { 0, 8, 16, 24}}, + {WINED3DFMT_B10G10R10A2_UNORM, { 1023.0f, 1023.0f, 1023.0f, 3.0f}, {20, 10, 0, 30}}, + {WINED3DFMT_R10G10B10A2_UNORM, { 1023.0f, 1023.0f, 1023.0f, 3.0f}, { 0, 10, 20, 30}}, + {WINED3DFMT_P8_UINT, { 0.0f, 0.0f, 0.0f, 255.0f}, { 0, 0, 0, 0}}, + {WINED3DFMT_S1_UINT_D15_UNORM, { 32767.0f, 0.0f, 0.0f, 0.0f}, { 0, 0, 0, 0}}, + {WINED3DFMT_D16_UNORM, { 65535.0f, 0.0f, 0.0f, 0.0f}, { 0, 0, 0, 0}}, + }; + static const struct + { + enum wined3d_format_id format_id; + struct wined3d_dvec4 mul; + struct wined3d_uvec4 shift; + } + double_conv[] = + { + {WINED3DFMT_D24_UNORM_S8_UINT, { 16777215.0, 1.0, 0.0, 0.0}, {8, 0, 0, 0}}, + {WINED3DFMT_X8D24_UNORM, { 16777215.0, 0.0, 0.0, 0.0}, {0, 0, 0, 0}}, + {WINED3DFMT_D32_UNORM, {4294967295.0, 0.0, 0.0, 0.0}, {0, 0, 0, 0}}, + }; + enum wined3d_format_id format_id = format->id; + struct wined3d_color colour_srgb; + unsigned int i; + DWORD ret; + + TRACE("Converting colour %s to format %s.\n", debug_color(color), debug_d3dformat(format_id)); + + for (i = 0; i < ARRAY_SIZE(format_srgb_info); ++i) + { + if (format_id != format_srgb_info[i].srgb_format_id) + continue; + + wined3d_colour_srgb_from_linear(&colour_srgb, color); + format_id = format_srgb_info[i].base_format_id; + color = &colour_srgb; + break; + } + + for (i = 0; i < ARRAY_SIZE(float_conv); ++i) + { + if (format_id != float_conv[i].format_id) + continue; + + ret = ((DWORD)((color->r * float_conv[i].mul.x) + 0.5f)) << float_conv[i].shift.x; + ret |= ((DWORD)((color->g * float_conv[i].mul.y) + 0.5f)) << float_conv[i].shift.y; + ret |= ((DWORD)((color->b * float_conv[i].mul.z) + 0.5f)) << float_conv[i].shift.z; + ret |= ((DWORD)((color->a * float_conv[i].mul.w) + 0.5f)) << float_conv[i].shift.w; + + TRACE("Returning 0x%08x.\n", ret); + + return ret; + } + + for (i = 0; i < ARRAY_SIZE(double_conv); ++i) + { + if (format_id != double_conv[i].format_id) + continue; + + ret = ((DWORD)((color->r * double_conv[i].mul.x) + 0.5)) << double_conv[i].shift.x; + ret |= ((DWORD)((color->g * double_conv[i].mul.y) + 0.5)) << double_conv[i].shift.y; + ret |= ((DWORD)((color->b * double_conv[i].mul.z) + 0.5)) << double_conv[i].shift.z; + ret |= ((DWORD)((color->a * double_conv[i].mul.w) + 0.5)) << double_conv[i].shift.w; + + TRACE("Returning 0x%08x.\n", ret); + + return ret; + } + + FIXME("Conversion for format %s not implemented.\n", debug_d3dformat(format_id)); + + return 0; +} + +static float color_to_float(DWORD color, DWORD size, DWORD offset) +{ + DWORD mask = size < 32 ? (1u << size) - 1 : ~0u; + + if (!size) + return 1.0f; + + color >>= offset; + color &= mask; + + return (float)color / (float)mask; +} + +void wined3d_format_get_float_color_key(const struct wined3d_format *format, + const struct wined3d_color_key *key, struct wined3d_color *float_colors) +{ + struct wined3d_color slop; + + switch (format->id) + { + case WINED3DFMT_B8G8R8_UNORM: + case WINED3DFMT_B8G8R8A8_UNORM: + case WINED3DFMT_B8G8R8X8_UNORM: + case WINED3DFMT_B5G6R5_UNORM: + case WINED3DFMT_B5G5R5X1_UNORM: + case WINED3DFMT_B5G5R5A1_UNORM: + case WINED3DFMT_B4G4R4A4_UNORM: + case WINED3DFMT_B2G3R3_UNORM: + case WINED3DFMT_R8_UNORM: + case WINED3DFMT_A8_UNORM: + case WINED3DFMT_B2G3R3A8_UNORM: + case WINED3DFMT_B4G4R4X4_UNORM: + case WINED3DFMT_R10G10B10A2_UNORM: + case WINED3DFMT_R10G10B10A2_SNORM: + case WINED3DFMT_R8G8B8A8_UNORM: + case WINED3DFMT_R8G8B8X8_UNORM: + case WINED3DFMT_R16G16_UNORM: + case WINED3DFMT_B10G10R10A2_UNORM: + slop.r = 0.5f / ((1u << format->red_size) - 1); + slop.g = 0.5f / ((1u << format->green_size) - 1); + slop.b = 0.5f / ((1u << format->blue_size) - 1); + slop.a = 0.5f / ((1u << format->alpha_size) - 1); + + float_colors[0].r = color_to_float(key->color_space_low_value, format->red_size, format->red_offset) + - slop.r; + float_colors[0].g = color_to_float(key->color_space_low_value, format->green_size, format->green_offset) + - slop.g; + float_colors[0].b = color_to_float(key->color_space_low_value, format->blue_size, format->blue_offset) + - slop.b; + float_colors[0].a = color_to_float(key->color_space_low_value, format->alpha_size, format->alpha_offset) + - slop.a; + + float_colors[1].r = color_to_float(key->color_space_high_value, format->red_size, format->red_offset) + + slop.r; + float_colors[1].g = color_to_float(key->color_space_high_value, format->green_size, format->green_offset) + + slop.g; + float_colors[1].b = color_to_float(key->color_space_high_value, format->blue_size, format->blue_offset) + + slop.b; + float_colors[1].a = color_to_float(key->color_space_high_value, format->alpha_size, format->alpha_offset) + + slop.a; + break; + + case WINED3DFMT_P8_UINT: + float_colors[0].r = 0.0f; + float_colors[0].g = 0.0f; + float_colors[0].b = 0.0f; + float_colors[0].a = (key->color_space_low_value - 0.5f) / 255.0f; + + float_colors[1].r = 0.0f; + float_colors[1].g = 0.0f; + float_colors[1].b = 0.0f; + float_colors[1].a = (key->color_space_high_value + 0.5f) / 255.0f; + break; + + default: + ERR("Unhandled color key to float conversion for format %s.\n", debug_d3dformat(format->id)); + } +} + +enum wined3d_format_id pixelformat_for_depth(DWORD depth) +{ + switch (depth) + { + case 8: return WINED3DFMT_P8_UINT; + case 15: return WINED3DFMT_B5G5R5X1_UNORM; + case 16: return WINED3DFMT_B5G6R5_UNORM; + case 24: return WINED3DFMT_B8G8R8X8_UNORM; /* Robots needs 24bit to be WINED3DFMT_B8G8R8X8_UNORM */ + case 32: return WINED3DFMT_B8G8R8X8_UNORM; /* EVE online and the Fur demo need 32bit AdapterDisplayMode to return WINED3DFMT_B8G8R8X8_UNORM */ + default: return WINED3DFMT_UNKNOWN; + } +} + +void wined3d_format_copy_data(const struct wined3d_format *format, const uint8_t *src, + unsigned int src_row_pitch, unsigned int src_slice_pitch, uint8_t *dst, unsigned int dst_row_pitch, + unsigned int dst_slice_pitch, unsigned int w, unsigned int h, unsigned int d) +{ + unsigned int row_block_count, row_count, row_size, slice, row; + unsigned int slice_count = d; + const uint8_t *src_row; + uint8_t *dst_row; + + row_block_count = (w + format->block_width - 1) / format->block_width; + row_count = (h + format->block_height - 1) / format->block_height; + row_size = row_block_count * format->block_byte_count; + + if (src_row_pitch == row_size && dst_row_pitch == row_size && src_slice_pitch == dst_slice_pitch) + { + memcpy(dst, src, slice_count * row_count * row_size); + return; + } + + for (slice = 0; slice < slice_count; ++slice) + { + for (row = 0; row < row_count; ++row) + { + src_row = &src[slice * src_slice_pitch + row * src_row_pitch]; + dst_row = &dst[slice * dst_slice_pitch + row * dst_row_pitch]; + memcpy(dst_row, src_row, row_size); + } + } +} + +void multiply_matrix(struct wined3d_matrix *dst, const struct wined3d_matrix *src1, const struct wined3d_matrix *src2) +{ + struct wined3d_matrix tmp; + + /* Now do the multiplication 'by hand'. + I know that all this could be optimised, but this will be done later :-) */ + tmp._11 = (src1->_11 * src2->_11) + (src1->_21 * src2->_12) + (src1->_31 * src2->_13) + (src1->_41 * src2->_14); + tmp._21 = (src1->_11 * src2->_21) + (src1->_21 * src2->_22) + (src1->_31 * src2->_23) + (src1->_41 * src2->_24); + tmp._31 = (src1->_11 * src2->_31) + (src1->_21 * src2->_32) + (src1->_31 * src2->_33) + (src1->_41 * src2->_34); + tmp._41 = (src1->_11 * src2->_41) + (src1->_21 * src2->_42) + (src1->_31 * src2->_43) + (src1->_41 * src2->_44); + + tmp._12 = (src1->_12 * src2->_11) + (src1->_22 * src2->_12) + (src1->_32 * src2->_13) + (src1->_42 * src2->_14); + tmp._22 = (src1->_12 * src2->_21) + (src1->_22 * src2->_22) + (src1->_32 * src2->_23) + (src1->_42 * src2->_24); + tmp._32 = (src1->_12 * src2->_31) + (src1->_22 * src2->_32) + (src1->_32 * src2->_33) + (src1->_42 * src2->_34); + tmp._42 = (src1->_12 * src2->_41) + (src1->_22 * src2->_42) + (src1->_32 * src2->_43) + (src1->_42 * src2->_44); + + tmp._13 = (src1->_13 * src2->_11) + (src1->_23 * src2->_12) + (src1->_33 * src2->_13) + (src1->_43 * src2->_14); + tmp._23 = (src1->_13 * src2->_21) + (src1->_23 * src2->_22) + (src1->_33 * src2->_23) + (src1->_43 * src2->_24); + tmp._33 = (src1->_13 * src2->_31) + (src1->_23 * src2->_32) + (src1->_33 * src2->_33) + (src1->_43 * src2->_34); + tmp._43 = (src1->_13 * src2->_41) + (src1->_23 * src2->_42) + (src1->_33 * src2->_43) + (src1->_43 * src2->_44); + + tmp._14 = (src1->_14 * src2->_11) + (src1->_24 * src2->_12) + (src1->_34 * src2->_13) + (src1->_44 * src2->_14); + tmp._24 = (src1->_14 * src2->_21) + (src1->_24 * src2->_22) + (src1->_34 * src2->_23) + (src1->_44 * src2->_24); + tmp._34 = (src1->_14 * src2->_31) + (src1->_24 * src2->_32) + (src1->_34 * src2->_33) + (src1->_44 * src2->_34); + tmp._44 = (src1->_14 * src2->_41) + (src1->_24 * src2->_42) + (src1->_34 * src2->_43) + (src1->_44 * src2->_44); + + *dst = tmp; +} + +void gen_ffp_frag_op(const struct wined3d_context *context, const struct wined3d_state *state, + struct ffp_frag_settings *settings, BOOL ignore_textype) +{ +#define ARG1 0x01 +#define ARG2 0x02 +#define ARG0 0x04 + static const unsigned char args[WINED3D_TOP_LERP + 1] = + { + /* undefined */ 0, + /* D3DTOP_DISABLE */ 0, + /* D3DTOP_SELECTARG1 */ ARG1, + /* D3DTOP_SELECTARG2 */ ARG2, + /* D3DTOP_MODULATE */ ARG1 | ARG2, + /* D3DTOP_MODULATE2X */ ARG1 | ARG2, + /* D3DTOP_MODULATE4X */ ARG1 | ARG2, + /* D3DTOP_ADD */ ARG1 | ARG2, + /* D3DTOP_ADDSIGNED */ ARG1 | ARG2, + /* D3DTOP_ADDSIGNED2X */ ARG1 | ARG2, + /* D3DTOP_SUBTRACT */ ARG1 | ARG2, + /* D3DTOP_ADDSMOOTH */ ARG1 | ARG2, + /* D3DTOP_BLENDDIFFUSEALPHA */ ARG1 | ARG2, + /* D3DTOP_BLENDTEXTUREALPHA */ ARG1 | ARG2, + /* D3DTOP_BLENDFACTORALPHA */ ARG1 | ARG2, + /* D3DTOP_BLENDTEXTUREALPHAPM */ ARG1 | ARG2, + /* D3DTOP_BLENDCURRENTALPHA */ ARG1 | ARG2, + /* D3DTOP_PREMODULATE */ ARG1 | ARG2, + /* D3DTOP_MODULATEALPHA_ADDCOLOR */ ARG1 | ARG2, + /* D3DTOP_MODULATECOLOR_ADDALPHA */ ARG1 | ARG2, + /* D3DTOP_MODULATEINVALPHA_ADDCOLOR */ ARG1 | ARG2, + /* D3DTOP_MODULATEINVCOLOR_ADDALPHA */ ARG1 | ARG2, + /* D3DTOP_BUMPENVMAP */ ARG1 | ARG2, + /* D3DTOP_BUMPENVMAPLUMINANCE */ ARG1 | ARG2, + /* D3DTOP_DOTPRODUCT3 */ ARG1 | ARG2, + /* D3DTOP_MULTIPLYADD */ ARG1 | ARG2 | ARG0, + /* D3DTOP_LERP */ ARG1 | ARG2 | ARG0 + }; + unsigned int i; + DWORD ttff; + DWORD cop, aop, carg0, carg1, carg2, aarg0, aarg1, aarg2; + const struct wined3d_d3d_info *d3d_info = context->d3d_info; + + settings->padding = 0; + + for (i = 0; i < d3d_info->limits.ffp_blend_stages; ++i) + { + struct wined3d_texture *texture; + + settings->op[i].padding = 0; + if (state->texture_states[i][WINED3D_TSS_COLOR_OP] == WINED3D_TOP_DISABLE) + { + settings->op[i].cop = WINED3D_TOP_DISABLE; + settings->op[i].aop = WINED3D_TOP_DISABLE; + settings->op[i].carg0 = settings->op[i].carg1 = settings->op[i].carg2 = ARG_UNUSED; + settings->op[i].aarg0 = settings->op[i].aarg1 = settings->op[i].aarg2 = ARG_UNUSED; + settings->op[i].color_fixup = COLOR_FIXUP_IDENTITY; + settings->op[i].tmp_dst = 0; + settings->op[i].tex_type = WINED3D_GL_RES_TYPE_TEX_1D; + settings->op[i].projected = WINED3D_PROJECTION_NONE; + i++; + break; + } + + if ((texture = state->textures[i])) + { + if (can_use_texture_swizzle(d3d_info, texture->resource.format)) + settings->op[i].color_fixup = COLOR_FIXUP_IDENTITY; + else + settings->op[i].color_fixup = texture->resource.format->color_fixup; + if (ignore_textype) + { + settings->op[i].tex_type = WINED3D_GL_RES_TYPE_TEX_1D; + } + else + { + switch (wined3d_texture_gl(texture)->target) + { + case GL_TEXTURE_1D: + settings->op[i].tex_type = WINED3D_GL_RES_TYPE_TEX_1D; + break; + case GL_TEXTURE_2D: + settings->op[i].tex_type = WINED3D_GL_RES_TYPE_TEX_2D; + break; + case GL_TEXTURE_3D: + settings->op[i].tex_type = WINED3D_GL_RES_TYPE_TEX_3D; + break; + case GL_TEXTURE_CUBE_MAP_ARB: + settings->op[i].tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE; + break; + case GL_TEXTURE_RECTANGLE_ARB: + settings->op[i].tex_type = WINED3D_GL_RES_TYPE_TEX_RECT; + break; + } + } + } else { + settings->op[i].color_fixup = COLOR_FIXUP_IDENTITY; + settings->op[i].tex_type = WINED3D_GL_RES_TYPE_TEX_1D; + } + + cop = state->texture_states[i][WINED3D_TSS_COLOR_OP]; + aop = state->texture_states[i][WINED3D_TSS_ALPHA_OP]; + + carg1 = (args[cop] & ARG1) ? state->texture_states[i][WINED3D_TSS_COLOR_ARG1] : ARG_UNUSED; + carg2 = (args[cop] & ARG2) ? state->texture_states[i][WINED3D_TSS_COLOR_ARG2] : ARG_UNUSED; + carg0 = (args[cop] & ARG0) ? state->texture_states[i][WINED3D_TSS_COLOR_ARG0] : ARG_UNUSED; + + if (is_invalid_op(state, i, cop, carg1, carg2, carg0)) + { + carg0 = ARG_UNUSED; + carg2 = ARG_UNUSED; + carg1 = WINED3DTA_CURRENT; + cop = WINED3D_TOP_SELECT_ARG1; + } + + if (cop == WINED3D_TOP_DOTPRODUCT3) + { + /* A dotproduct3 on the colorop overwrites the alphaop operation and replicates + * the color result to the alpha component of the destination + */ + aop = cop; + aarg1 = carg1; + aarg2 = carg2; + aarg0 = carg0; + } + else + { + aarg1 = (args[aop] & ARG1) ? state->texture_states[i][WINED3D_TSS_ALPHA_ARG1] : ARG_UNUSED; + aarg2 = (args[aop] & ARG2) ? state->texture_states[i][WINED3D_TSS_ALPHA_ARG2] : ARG_UNUSED; + aarg0 = (args[aop] & ARG0) ? state->texture_states[i][WINED3D_TSS_ALPHA_ARG0] : ARG_UNUSED; + } + + if (!i && state->textures[0] && state->render_states[WINED3D_RS_COLORKEYENABLE]) + { + GLenum texture_dimensions; + + texture = state->textures[0]; + texture_dimensions = wined3d_texture_gl(texture)->target; + + if (texture_dimensions == GL_TEXTURE_2D || texture_dimensions == GL_TEXTURE_RECTANGLE_ARB) + { + if (texture->async.color_key_flags & WINED3D_CKEY_SRC_BLT && !texture->resource.format->alpha_size) + { + if (aop == WINED3D_TOP_DISABLE) + { + aarg1 = WINED3DTA_TEXTURE; + aop = WINED3D_TOP_SELECT_ARG1; + } + else if (aop == WINED3D_TOP_SELECT_ARG1 && aarg1 != WINED3DTA_TEXTURE) + { + if (state->blend_state && state->blend_state->desc.rt[0].enable) + { + aarg2 = WINED3DTA_TEXTURE; + aop = WINED3D_TOP_MODULATE; + } + else aarg1 = WINED3DTA_TEXTURE; + } + else if (aop == WINED3D_TOP_SELECT_ARG2 && aarg2 != WINED3DTA_TEXTURE) + { + if (state->blend_state && state->blend_state->desc.rt[0].enable) + { + aarg1 = WINED3DTA_TEXTURE; + aop = WINED3D_TOP_MODULATE; + } + else aarg2 = WINED3DTA_TEXTURE; + } + } + } + } + + if (is_invalid_op(state, i, aop, aarg1, aarg2, aarg0)) + { + aarg0 = ARG_UNUSED; + aarg2 = ARG_UNUSED; + aarg1 = WINED3DTA_CURRENT; + aop = WINED3D_TOP_SELECT_ARG1; + } + + if (carg1 == WINED3DTA_TEXTURE || carg2 == WINED3DTA_TEXTURE || carg0 == WINED3DTA_TEXTURE + || aarg1 == WINED3DTA_TEXTURE || aarg2 == WINED3DTA_TEXTURE || aarg0 == WINED3DTA_TEXTURE) + { + ttff = state->texture_states[i][WINED3D_TSS_TEXTURE_TRANSFORM_FLAGS]; + if (ttff == (WINED3D_TTFF_PROJECTED | WINED3D_TTFF_COUNT3)) + settings->op[i].projected = WINED3D_PROJECTION_COUNT3; + else if (ttff & WINED3D_TTFF_PROJECTED) + settings->op[i].projected = WINED3D_PROJECTION_COUNT4; + else + settings->op[i].projected = WINED3D_PROJECTION_NONE; + } + else + { + settings->op[i].projected = WINED3D_PROJECTION_NONE; + } + + settings->op[i].cop = cop; + settings->op[i].aop = aop; + settings->op[i].carg0 = carg0; + settings->op[i].carg1 = carg1; + settings->op[i].carg2 = carg2; + settings->op[i].aarg0 = aarg0; + settings->op[i].aarg1 = aarg1; + settings->op[i].aarg2 = aarg2; + settings->op[i].tmp_dst = state->texture_states[i][WINED3D_TSS_RESULT_ARG] == WINED3DTA_TEMP; + } + + /* Clear unsupported stages */ + for(; i < WINED3D_MAX_TEXTURES; i++) { + memset(&settings->op[i], 0xff, sizeof(settings->op[i])); + } + + if (!state->render_states[WINED3D_RS_FOGENABLE]) + { + settings->fog = WINED3D_FFP_PS_FOG_OFF; + } + else if (state->render_states[WINED3D_RS_FOGTABLEMODE] == WINED3D_FOG_NONE) + { + if (use_vs(state) || state->vertex_declaration->position_transformed) + { + settings->fog = WINED3D_FFP_PS_FOG_LINEAR; + } + else + { + switch (state->render_states[WINED3D_RS_FOGVERTEXMODE]) + { + case WINED3D_FOG_NONE: + case WINED3D_FOG_LINEAR: + settings->fog = WINED3D_FFP_PS_FOG_LINEAR; + break; + case WINED3D_FOG_EXP: + settings->fog = WINED3D_FFP_PS_FOG_EXP; + break; + case WINED3D_FOG_EXP2: + settings->fog = WINED3D_FFP_PS_FOG_EXP2; + break; + } + } + } + else + { + switch (state->render_states[WINED3D_RS_FOGTABLEMODE]) + { + case WINED3D_FOG_LINEAR: + settings->fog = WINED3D_FFP_PS_FOG_LINEAR; + break; + case WINED3D_FOG_EXP: + settings->fog = WINED3D_FFP_PS_FOG_EXP; + break; + case WINED3D_FOG_EXP2: + settings->fog = WINED3D_FFP_PS_FOG_EXP2; + break; + } + } + settings->sRGB_write = !d3d_info->srgb_write_control && needs_srgb_write(d3d_info, state, &state->fb); + if (d3d_info->vs_clipping || !use_vs(state) || !state->render_states[WINED3D_RS_CLIPPING] + || !state->render_states[WINED3D_RS_CLIPPLANEENABLE]) + { + /* No need to emulate clipplanes if GL supports native vertex shader clipping or if + * the fixed function vertex pipeline is used(which always supports clipplanes), or + * if no clipplane is enabled + */ + settings->emul_clipplanes = 0; + } else { + settings->emul_clipplanes = 1; + } + + if (state->render_states[WINED3D_RS_COLORKEYENABLE] && state->textures[0] + && state->textures[0]->async.color_key_flags & WINED3D_CKEY_SRC_BLT + && settings->op[0].cop != WINED3D_TOP_DISABLE) + settings->color_key_enabled = 1; + else + settings->color_key_enabled = 0; + + /* texcoords_initialized is set to meaningful values only when GL doesn't + * support enough varyings to always pass around all the possible texture + * coordinates. + * This is used to avoid reading a varying not written by the vertex shader. + * Reading uninitialized varyings on core profile contexts results in an + * error while with builtin varyings on legacy contexts you get undefined + * behavior. */ + if (d3d_info->limits.varying_count && !d3d_info->full_ffp_varyings) + { + settings->texcoords_initialized = 0; + for (i = 0; i < WINED3D_MAX_TEXTURES; ++i) + { + if (use_vs(state)) + { + if (state->shader[WINED3D_SHADER_TYPE_VERTEX]->reg_maps.output_registers & (1u << i)) + settings->texcoords_initialized |= 1u << i; + } + else + { + const struct wined3d_stream_info *si = &context->stream_info; + unsigned int coord_idx = state->texture_states[i][WINED3D_TSS_TEXCOORD_INDEX]; + if ((state->texture_states[i][WINED3D_TSS_TEXCOORD_INDEX] >> WINED3D_FFP_TCI_SHIFT) + & WINED3D_FFP_TCI_MASK + || (coord_idx < WINED3D_MAX_TEXTURES && (si->use_map & (1u << (WINED3D_FFP_TEXCOORD0 + coord_idx))))) + settings->texcoords_initialized |= 1u << i; + } + } + } + else + { + settings->texcoords_initialized = (1u << WINED3D_MAX_TEXTURES) - 1; + } + + settings->pointsprite = state->render_states[WINED3D_RS_POINTSPRITEENABLE] + && state->primitive_type == WINED3D_PT_POINTLIST; + + if (d3d_info->ffp_alpha_test) + settings->alpha_test_func = WINED3D_CMP_ALWAYS - 1; + else + settings->alpha_test_func = (state->render_states[WINED3D_RS_ALPHATESTENABLE] + ? wined3d_sanitize_cmp_func(state->render_states[WINED3D_RS_ALPHAFUNC]) + : WINED3D_CMP_ALWAYS) - 1; + + if (d3d_info->emulated_flatshading) + settings->flatshading = state->render_states[WINED3D_RS_SHADEMODE] == WINED3D_SHADE_FLAT; + else + settings->flatshading = FALSE; +} + +const struct ffp_frag_desc *find_ffp_frag_shader(const struct wine_rb_tree *fragment_shaders, + const struct ffp_frag_settings *settings) +{ + struct wine_rb_entry *entry = wine_rb_get(fragment_shaders, settings); + return entry ? WINE_RB_ENTRY_VALUE(entry, struct ffp_frag_desc, entry) : NULL; +} + +void add_ffp_frag_shader(struct wine_rb_tree *shaders, struct ffp_frag_desc *desc) +{ + /* Note that the key is the implementation independent part of the ffp_frag_desc structure, + * whereas desc points to an extended structure with implementation specific parts. */ + if (wine_rb_put(shaders, &desc->settings, &desc->entry) == -1) + { + ERR("Failed to insert ffp frag shader.\n"); + } +} + +/* Activates the texture dimension according to the bound D3D texture. Does + * not care for the colorop or correct gl texture unit (when using nvrc). + * Requires the caller to activate the correct unit. */ +/* Context activation is done by the caller (state handler). */ +void texture_activate_dimensions(struct wined3d_texture *texture, const struct wined3d_gl_info *gl_info) +{ + if (texture) + { + switch (wined3d_texture_gl(texture)->target) + { + case GL_TEXTURE_2D: + gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_3D); + checkGLcall("glDisable(GL_TEXTURE_3D)"); + if (gl_info->supported[ARB_TEXTURE_CUBE_MAP]) + { + gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_CUBE_MAP_ARB); + checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)"); + } + if (gl_info->supported[ARB_TEXTURE_RECTANGLE]) + { + gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_RECTANGLE_ARB); + checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)"); + } + gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D); + checkGLcall("glEnable(GL_TEXTURE_2D)"); + break; + case GL_TEXTURE_RECTANGLE_ARB: + gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_2D); + checkGLcall("glDisable(GL_TEXTURE_2D)"); + gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_3D); + checkGLcall("glDisable(GL_TEXTURE_3D)"); + if (gl_info->supported[ARB_TEXTURE_CUBE_MAP]) + { + gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_CUBE_MAP_ARB); + checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)"); + } + gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_RECTANGLE_ARB); + checkGLcall("glEnable(GL_TEXTURE_RECTANGLE_ARB)"); + break; + case GL_TEXTURE_3D: + if (gl_info->supported[ARB_TEXTURE_CUBE_MAP]) + { + gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_CUBE_MAP_ARB); + checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)"); + } + if (gl_info->supported[ARB_TEXTURE_RECTANGLE]) + { + gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_RECTANGLE_ARB); + checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)"); + } + gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_2D); + checkGLcall("glDisable(GL_TEXTURE_2D)"); + gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_3D); + checkGLcall("glEnable(GL_TEXTURE_3D)"); + break; + case GL_TEXTURE_CUBE_MAP_ARB: + gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_2D); + checkGLcall("glDisable(GL_TEXTURE_2D)"); + gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_3D); + checkGLcall("glDisable(GL_TEXTURE_3D)"); + if (gl_info->supported[ARB_TEXTURE_RECTANGLE]) + { + gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_RECTANGLE_ARB); + checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)"); + } + gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_CUBE_MAP_ARB); + checkGLcall("glEnable(GL_TEXTURE_CUBE_MAP_ARB)"); + break; + } + } + else + { + gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D); + checkGLcall("glEnable(GL_TEXTURE_2D)"); + gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_3D); + checkGLcall("glDisable(GL_TEXTURE_3D)"); + if (gl_info->supported[ARB_TEXTURE_CUBE_MAP]) + { + gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_CUBE_MAP_ARB); + checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)"); + } + if (gl_info->supported[ARB_TEXTURE_RECTANGLE]) + { + gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_RECTANGLE_ARB); + checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)"); + } + /* Binding textures is done by samplers. A dummy texture will be bound */ + } +} + +/* Context activation is done by the caller (state handler). */ +void sampler_texdim(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id) +{ + struct wined3d_context_gl *context_gl = wined3d_context_gl(context); + unsigned int sampler = state_id - STATE_SAMPLER(0); + unsigned int mapped_stage; + + /* No need to enable / disable anything here for unused samplers. The + * tex_colorop handler takes care. Also no action is needed with pixel + * shaders, or if tex_colorop will take care of this business. */ + mapped_stage = context_gl->tex_unit_map[sampler]; + if (mapped_stage == WINED3D_UNMAPPED_STAGE || mapped_stage >= context_gl->gl_info->limits.textures) + return; + if (sampler >= context->lowest_disabled_stage) + return; + if (isStateDirty(context, STATE_TEXTURESTAGE(sampler, WINED3D_TSS_COLOR_OP))) + return; + + texture_activate_dimensions(state->textures[sampler], context_gl->gl_info); +} + +int wined3d_ffp_frag_program_key_compare(const void *key, const struct wine_rb_entry *entry) +{ + const struct ffp_frag_settings *ka = key; + const struct ffp_frag_settings *kb = &WINE_RB_ENTRY_VALUE(entry, const struct ffp_frag_desc, entry)->settings; + + return memcmp(ka, kb, sizeof(*ka)); +} + +void wined3d_ffp_get_vs_settings(const struct wined3d_context *context, + const struct wined3d_state *state, struct wined3d_ffp_vs_settings *settings) +{ + enum wined3d_material_color_source diffuse_source, emissive_source, ambient_source, specular_source; + const struct wined3d_stream_info *si = &context->stream_info; + const struct wined3d_d3d_info *d3d_info = context->d3d_info; + unsigned int coord_idx, i; + + memset(settings, 0, sizeof(*settings)); + + if (si->position_transformed) + { + settings->transformed = 1; + settings->point_size = state->primitive_type == WINED3D_PT_POINTLIST; + settings->per_vertex_point_size = !!(si->use_map & 1u << WINED3D_FFP_PSIZE); + if (!state->render_states[WINED3D_RS_FOGENABLE]) + settings->fog_mode = WINED3D_FFP_VS_FOG_OFF; + else if (state->render_states[WINED3D_RS_FOGTABLEMODE] != WINED3D_FOG_NONE) + settings->fog_mode = WINED3D_FFP_VS_FOG_DEPTH; + else + settings->fog_mode = WINED3D_FFP_VS_FOG_FOGCOORD; + + for (i = 0; i < WINED3D_MAX_TEXTURES; ++i) + { + coord_idx = state->texture_states[i][WINED3D_TSS_TEXCOORD_INDEX]; + if (coord_idx < WINED3D_MAX_TEXTURES && (si->use_map & (1u << (WINED3D_FFP_TEXCOORD0 + coord_idx)))) + settings->texcoords |= 1u << i; + settings->texgen[i] = state->texture_states[i][WINED3D_TSS_TEXCOORD_INDEX]; + } + if (d3d_info->full_ffp_varyings) + settings->texcoords = (1u << WINED3D_MAX_TEXTURES) - 1; + + if (d3d_info->emulated_flatshading) + settings->flatshading = state->render_states[WINED3D_RS_SHADEMODE] == WINED3D_SHADE_FLAT; + else + settings->flatshading = FALSE; + + settings->swizzle_map = si->swizzle_map; + + return; + } + + switch (state->render_states[WINED3D_RS_VERTEXBLEND]) + { + case WINED3D_VBF_DISABLE: + case WINED3D_VBF_1WEIGHTS: + case WINED3D_VBF_2WEIGHTS: + case WINED3D_VBF_3WEIGHTS: + settings->vertexblends = state->render_states[WINED3D_RS_VERTEXBLEND]; + break; + default: + FIXME("Unsupported vertex blending: %d\n", state->render_states[WINED3D_RS_VERTEXBLEND]); + break; + } + + settings->clipping = state->render_states[WINED3D_RS_CLIPPING] + && state->render_states[WINED3D_RS_CLIPPLANEENABLE]; + settings->normal = !!(si->use_map & (1u << WINED3D_FFP_NORMAL)); + settings->normalize = settings->normal && state->render_states[WINED3D_RS_NORMALIZENORMALS]; + settings->lighting = !!state->render_states[WINED3D_RS_LIGHTING]; + settings->localviewer = !!state->render_states[WINED3D_RS_LOCALVIEWER]; + settings->point_size = state->primitive_type == WINED3D_PT_POINTLIST; + settings->per_vertex_point_size = !!(si->use_map & 1u << WINED3D_FFP_PSIZE); + + wined3d_get_material_colour_source(&diffuse_source, &emissive_source, + &ambient_source, &specular_source, state, si); + settings->diffuse_source = diffuse_source; + settings->emissive_source = emissive_source; + settings->ambient_source = ambient_source; + settings->specular_source = specular_source; + + for (i = 0; i < WINED3D_MAX_TEXTURES; ++i) + { + coord_idx = state->texture_states[i][WINED3D_TSS_TEXCOORD_INDEX]; + if (coord_idx < WINED3D_MAX_TEXTURES && (si->use_map & (1u << (WINED3D_FFP_TEXCOORD0 + coord_idx)))) + settings->texcoords |= 1u << i; + settings->texgen[i] = state->texture_states[i][WINED3D_TSS_TEXCOORD_INDEX]; + } + if (d3d_info->full_ffp_varyings) + settings->texcoords = (1u << WINED3D_MAX_TEXTURES) - 1; + + for (i = 0; i < WINED3D_MAX_ACTIVE_LIGHTS; ++i) + { + if (!state->light_state.lights[i]) + continue; + + switch (state->light_state.lights[i]->OriginalParms.type) + { + case WINED3D_LIGHT_POINT: + ++settings->point_light_count; + break; + case WINED3D_LIGHT_SPOT: + ++settings->spot_light_count; + break; + case WINED3D_LIGHT_DIRECTIONAL: + ++settings->directional_light_count; + break; + case WINED3D_LIGHT_PARALLELPOINT: + ++settings->parallel_point_light_count; + break; + default: + FIXME("Unhandled light type %#x.\n", state->light_state.lights[i]->OriginalParms.type); + break; + } + } + + if (!state->render_states[WINED3D_RS_FOGENABLE]) + settings->fog_mode = WINED3D_FFP_VS_FOG_OFF; + else if (state->render_states[WINED3D_RS_FOGTABLEMODE] != WINED3D_FOG_NONE) + { + settings->fog_mode = WINED3D_FFP_VS_FOG_DEPTH; + + if (state->transforms[WINED3D_TS_PROJECTION]._14 == 0.0f + && state->transforms[WINED3D_TS_PROJECTION]._24 == 0.0f + && state->transforms[WINED3D_TS_PROJECTION]._34 == 0.0f + && state->transforms[WINED3D_TS_PROJECTION]._44 == 1.0f) + settings->ortho_fog = 1; + } + else if (state->render_states[WINED3D_RS_FOGVERTEXMODE] == WINED3D_FOG_NONE) + settings->fog_mode = WINED3D_FFP_VS_FOG_FOGCOORD; + else if (state->render_states[WINED3D_RS_RANGEFOGENABLE]) + settings->fog_mode = WINED3D_FFP_VS_FOG_RANGE; + else + settings->fog_mode = WINED3D_FFP_VS_FOG_DEPTH; + + if (d3d_info->emulated_flatshading) + settings->flatshading = state->render_states[WINED3D_RS_SHADEMODE] == WINED3D_SHADE_FLAT; + else + settings->flatshading = FALSE; + + settings->swizzle_map = si->swizzle_map; +} + +int wined3d_ffp_vertex_program_key_compare(const void *key, const struct wine_rb_entry *entry) +{ + const struct wined3d_ffp_vs_settings *ka = key; + const struct wined3d_ffp_vs_settings *kb = &WINE_RB_ENTRY_VALUE(entry, + const struct wined3d_ffp_vs_desc, entry)->settings; + + return memcmp(ka, kb, sizeof(*ka)); +} + +const char *wined3d_debug_location(DWORD location) +{ + struct debug_buffer buffer; + const char *prefix = ""; + const char *suffix = ""; + + if (wined3d_popcount(location) > 16) + { + prefix = "~("; + location = ~location; + suffix = ")"; + } + + init_debug_buffer(&buffer, "0"); +#define LOCATION_TO_STR(x) if (location & x) { debug_append(&buffer, #x, " | "); location &= ~x; } + LOCATION_TO_STR(WINED3D_LOCATION_DISCARDED); + LOCATION_TO_STR(WINED3D_LOCATION_SYSMEM); + LOCATION_TO_STR(WINED3D_LOCATION_BUFFER); + LOCATION_TO_STR(WINED3D_LOCATION_TEXTURE_RGB); + LOCATION_TO_STR(WINED3D_LOCATION_TEXTURE_SRGB); + LOCATION_TO_STR(WINED3D_LOCATION_DRAWABLE); + LOCATION_TO_STR(WINED3D_LOCATION_RB_MULTISAMPLE); + LOCATION_TO_STR(WINED3D_LOCATION_RB_RESOLVED); +#undef LOCATION_TO_STR + if (location) + FIXME("Unrecognized location flag(s) %#x.\n", location); + + return wine_dbg_sprintf("%s%s%s", prefix, buffer.str, suffix); +} + +const char *wined3d_debug_feature_level(enum wined3d_feature_level level) +{ + switch (level) + { +#define LEVEL_TO_STR(level) case level: return #level + LEVEL_TO_STR(WINED3D_FEATURE_LEVEL_5); + LEVEL_TO_STR(WINED3D_FEATURE_LEVEL_6); + LEVEL_TO_STR(WINED3D_FEATURE_LEVEL_7); + LEVEL_TO_STR(WINED3D_FEATURE_LEVEL_8); + LEVEL_TO_STR(WINED3D_FEATURE_LEVEL_9_1); + LEVEL_TO_STR(WINED3D_FEATURE_LEVEL_9_2); + LEVEL_TO_STR(WINED3D_FEATURE_LEVEL_9_3); + LEVEL_TO_STR(WINED3D_FEATURE_LEVEL_10); + LEVEL_TO_STR(WINED3D_FEATURE_LEVEL_10_1); + LEVEL_TO_STR(WINED3D_FEATURE_LEVEL_11); + LEVEL_TO_STR(WINED3D_FEATURE_LEVEL_11_1); +#undef LEVEL_TO_STR + default: + return wine_dbg_sprintf("%#x", level); + } +} + +/* Print a floating point value with the %.8e format specifier, always using + * '.' as decimal separator. */ +void wined3d_ftoa(float value, char *s) +{ + int idx = 1; + + if (copysignf(1.0f, value) < 0.0f) + ++idx; + + /* Be sure to allocate a buffer of at least 17 characters for the result + as sprintf may return a 3 digit exponent when using the MSVC runtime + instead of a 2 digit exponent. */ + sprintf(s, "%.8e", value); + if (isfinite(value)) + s[idx] = '.'; +} + +void wined3d_release_dc(HWND window, HDC dc) +{ + /* You'd figure ReleaseDC() would fail if the DC doesn't match the window. + * However, that's not what actually happens, and there are user32 tests + * that confirm ReleaseDC() with the wrong window is supposed to succeed. + * So explicitly check that the DC belongs to the window, since we want to + * avoid releasing a DC that belongs to some other window if the original + * window was already destroyed. */ + if (WindowFromDC(dc) != window) + WARN("DC %p does not belong to window %p.\n", dc, window); + else if (!ReleaseDC(window, dc)) + ERR("Failed to release device context %p, last error %#x.\n", dc, GetLastError()); +} + +BOOL wined3d_clip_blit(const RECT *clip_rect, RECT *clipped, RECT *other) +{ + RECT orig = *clipped; + float scale_x = (float)(orig.right - orig.left) / (float)(other->right - other->left); + float scale_y = (float)(orig.bottom - orig.top) / (float)(other->bottom - other->top); + + IntersectRect(clipped, clipped, clip_rect); + + if (IsRectEmpty(clipped)) + { + SetRectEmpty(other); + return FALSE; + } + + other->left += (LONG)((clipped->left - orig.left) / scale_x); + other->top += (LONG)((clipped->top - orig.top) / scale_y); + other->right -= (LONG)((orig.right - clipped->right) / scale_x); + other->bottom -= (LONG)((orig.bottom - clipped->bottom) / scale_y); + + return TRUE; +} + +void wined3d_gl_limits_get_uniform_block_range(const struct wined3d_gl_limits *gl_limits, + enum wined3d_shader_type shader_type, unsigned int *base, unsigned int *count) +{ + unsigned int i; + + *base = 0; + for (i = 0; i < WINED3D_SHADER_TYPE_COUNT; ++i) + { + *count = gl_limits->uniform_blocks[i]; + if (i == shader_type) + return; + *base += *count; + } + + ERR("Unrecognized shader type %#x.\n", shader_type); + *count = 0; +} + +void wined3d_gl_limits_get_texture_unit_range(const struct wined3d_gl_limits *gl_limits, + enum wined3d_shader_type shader_type, unsigned int *base, unsigned int *count) +{ + unsigned int i; + + if (shader_type == WINED3D_SHADER_TYPE_COMPUTE) + { + if (gl_limits->combined_samplers == gl_limits->graphics_samplers) + *base = 0; + else + *base = gl_limits->graphics_samplers; + *count = gl_limits->samplers[WINED3D_SHADER_TYPE_COMPUTE]; + return; + } + + *base = 0; + for (i = 0; i < WINED3D_SHADER_TYPE_GRAPHICS_COUNT; ++i) + { + *count = gl_limits->samplers[i]; + if (i == shader_type) + return; + *base += *count; + } + + ERR("Unrecognized shader type %#x.\n", shader_type); + *count = 0; +} + +BOOL wined3d_array_reserve(void **elements, SIZE_T *capacity, SIZE_T count, SIZE_T size) +{ + SIZE_T max_capacity, new_capacity; + void *new_elements; + + if (count <= *capacity) + return TRUE; + + max_capacity = ~(SIZE_T)0 / size; + if (count > max_capacity) + return FALSE; + + new_capacity = max(1, *capacity); + while (new_capacity < count && new_capacity <= max_capacity / 2) + new_capacity *= 2; + if (new_capacity < count) + new_capacity = count; + + if (!*elements) + new_elements = heap_alloc_zero(new_capacity * size); + else + new_elements = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *elements, new_capacity * size); + if (!new_elements) + return FALSE; + + *elements = new_elements; + *capacity = new_capacity; + return TRUE; +} + +static void swap_rows(float **a, float **b) +{ + float *tmp = *a; + + *a = *b; + *b = tmp; +} + +BOOL invert_matrix(struct wined3d_matrix *out, const struct wined3d_matrix *m) +{ + float wtmp[4][8]; + float m0, m1, m2, m3, s; + float *r0, *r1, *r2, *r3; + + r0 = wtmp[0]; + r1 = wtmp[1]; + r2 = wtmp[2]; + r3 = wtmp[3]; + + r0[0] = m->_11; + r0[1] = m->_12; + r0[2] = m->_13; + r0[3] = m->_14; + r0[4] = 1.0f; + r0[5] = r0[6] = r0[7] = 0.0f; + + r1[0] = m->_21; + r1[1] = m->_22; + r1[2] = m->_23; + r1[3] = m->_24; + r1[5] = 1.0f; + r1[4] = r1[6] = r1[7] = 0.0f; + + r2[0] = m->_31; + r2[1] = m->_32; + r2[2] = m->_33; + r2[3] = m->_34; + r2[6] = 1.0f; + r2[4] = r2[5] = r2[7] = 0.0f; + + r3[0] = m->_41; + r3[1] = m->_42; + r3[2] = m->_43; + r3[3] = m->_44; + r3[7] = 1.0f; + r3[4] = r3[5] = r3[6] = 0.0f; + + /* Choose pivot - or die. */ + if (fabsf(r3[0]) > fabsf(r2[0])) + swap_rows(&r3, &r2); + if (fabsf(r2[0]) > fabsf(r1[0])) + swap_rows(&r2, &r1); + if (fabsf(r1[0]) > fabsf(r0[0])) + swap_rows(&r1, &r0); + if (r0[0] == 0.0f) + return FALSE; + + /* Eliminate first variable. */ + m1 = r1[0] / r0[0]; m2 = r2[0] / r0[0]; m3 = r3[0] / r0[0]; + s = r0[1]; r1[1] -= m1 * s; r2[1] -= m2 * s; r3[1] -= m3 * s; + s = r0[2]; r1[2] -= m1 * s; r2[2] -= m2 * s; r3[2] -= m3 * s; + s = r0[3]; r1[3] -= m1 * s; r2[3] -= m2 * s; r3[3] -= m3 * s; + s = r0[4]; + if (s != 0.0f) + { + r1[4] -= m1 * s; + r2[4] -= m2 * s; + r3[4] -= m3 * s; + } + s = r0[5]; + if (s != 0.0f) + { + r1[5] -= m1 * s; + r2[5] -= m2 * s; + r3[5] -= m3 * s; + } + s = r0[6]; + if (s != 0.0f) + { + r1[6] -= m1 * s; + r2[6] -= m2 * s; + r3[6] -= m3 * s; + } + s = r0[7]; + if (s != 0.0f) + { + r1[7] -= m1 * s; + r2[7] -= m2 * s; + r3[7] -= m3 * s; + } + + /* Choose pivot - or die. */ + if (fabsf(r3[1]) > fabsf(r2[1])) + swap_rows(&r3, &r2); + if (fabsf(r2[1]) > fabsf(r1[1])) + swap_rows(&r2, &r1); + if (r1[1] == 0.0f) + return FALSE; + + /* Eliminate second variable. */ + m2 = r2[1] / r1[1]; m3 = r3[1] / r1[1]; + r2[2] -= m2 * r1[2]; r3[2] -= m3 * r1[2]; + r2[3] -= m2 * r1[3]; r3[3] -= m3 * r1[3]; + s = r1[4]; + if (s != 0.0f) + { + r2[4] -= m2 * s; + r3[4] -= m3 * s; + } + s = r1[5]; + if (s != 0.0f) + { + r2[5] -= m2 * s; + r3[5] -= m3 * s; + } + s = r1[6]; + if (s != 0.0f) + { + r2[6] -= m2 * s; + r3[6] -= m3 * s; + } + s = r1[7]; + if (s != 0.0f) + { + r2[7] -= m2 * s; + r3[7] -= m3 * s; + } + + /* Choose pivot - or die. */ + if (fabsf(r3[2]) > fabsf(r2[2])) + swap_rows(&r3, &r2); + if (r2[2] == 0.0f) + return FALSE; + + /* Eliminate third variable. */ + m3 = r3[2] / r2[2]; + r3[3] -= m3 * r2[3]; + r3[4] -= m3 * r2[4]; + r3[5] -= m3 * r2[5]; + r3[6] -= m3 * r2[6]; + r3[7] -= m3 * r2[7]; + + /* Last check. */ + if (r3[3] == 0.0f) + return FALSE; + + /* Back substitute row 3. */ + s = 1.0f / r3[3]; + r3[4] *= s; + r3[5] *= s; + r3[6] *= s; + r3[7] *= s; + + /* Back substitute row 2. */ + m2 = r2[3]; + s = 1.0f / r2[2]; + r2[4] = s * (r2[4] - r3[4] * m2); + r2[5] = s * (r2[5] - r3[5] * m2); + r2[6] = s * (r2[6] - r3[6] * m2); + r2[7] = s * (r2[7] - r3[7] * m2); + m1 = r1[3]; + r1[4] -= r3[4] * m1; + r1[5] -= r3[5] * m1; + r1[6] -= r3[6] * m1; + r1[7] -= r3[7] * m1; + m0 = r0[3]; + r0[4] -= r3[4] * m0; + r0[5] -= r3[5] * m0; + r0[6] -= r3[6] * m0; + r0[7] -= r3[7] * m0; + + /* Back substitute row 1. */ + m1 = r1[2]; + s = 1.0f / r1[1]; + r1[4] = s * (r1[4] - r2[4] * m1); + r1[5] = s * (r1[5] - r2[5] * m1); + r1[6] = s * (r1[6] - r2[6] * m1); + r1[7] = s * (r1[7] - r2[7] * m1); + m0 = r0[2]; + r0[4] -= r2[4] * m0; + r0[5] -= r2[5] * m0; + r0[6] -= r2[6] * m0; + r0[7] -= r2[7] * m0; + + /* Back substitute row 0. */ + m0 = r0[1]; + s = 1.0f / r0[0]; + r0[4] = s * (r0[4] - r1[4] * m0); + r0[5] = s * (r0[5] - r1[5] * m0); + r0[6] = s * (r0[6] - r1[6] * m0); + r0[7] = s * (r0[7] - r1[7] * m0); + + out->_11 = r0[4]; + out->_12 = r0[5]; + out->_13 = r0[6]; + out->_14 = r0[7]; + out->_21 = r1[4]; + out->_22 = r1[5]; + out->_23 = r1[6]; + out->_24 = r1[7]; + out->_31 = r2[4]; + out->_32 = r2[5]; + out->_33 = r2[6]; + out->_34 = r2[7]; + out->_41 = r3[4]; + out->_42 = r3[5]; + out->_43 = r3[6]; + out->_44 = r3[7]; + + return TRUE; +} + +/* Taken and adapted from Mesa. */ +static BOOL invert_matrix_3d(struct wined3d_matrix *out, const struct wined3d_matrix *in) +{ + float pos, neg, t, det; + struct wined3d_matrix temp; + + /* Calculate the determinant of upper left 3x3 submatrix and + * determine if the matrix is singular. */ + pos = neg = 0.0f; + t = in->_11 * in->_22 * in->_33; + if (t >= 0.0f) + pos += t; + else + neg += t; + + t = in->_21 * in->_32 * in->_13; + if (t >= 0.0f) + pos += t; + else + neg += t; + t = in->_31 * in->_12 * in->_23; + if (t >= 0.0f) + pos += t; + else + neg += t; + + t = -in->_31 * in->_22 * in->_13; + if (t >= 0.0f) + pos += t; + else + neg += t; + t = -in->_21 * in->_12 * in->_33; + if (t >= 0.0f) + pos += t; + else + neg += t; + + t = -in->_11 * in->_32 * in->_23; + if (t >= 0.0f) + pos += t; + else + neg += t; + + det = pos + neg; + + if (fabsf(det) < 1e-25f) + return FALSE; + + det = 1.0f / det; + temp._11 = (in->_22 * in->_33 - in->_32 * in->_23) * det; + temp._12 = -(in->_12 * in->_33 - in->_32 * in->_13) * det; + temp._13 = (in->_12 * in->_23 - in->_22 * in->_13) * det; + temp._21 = -(in->_21 * in->_33 - in->_31 * in->_23) * det; + temp._22 = (in->_11 * in->_33 - in->_31 * in->_13) * det; + temp._23 = -(in->_11 * in->_23 - in->_21 * in->_13) * det; + temp._31 = (in->_21 * in->_32 - in->_31 * in->_22) * det; + temp._32 = -(in->_11 * in->_32 - in->_31 * in->_12) * det; + temp._33 = (in->_11 * in->_22 - in->_21 * in->_12) * det; + + *out = temp; + return TRUE; +} + +void compute_normal_matrix(float *normal_matrix, BOOL legacy_lighting, + const struct wined3d_matrix *modelview) +{ + struct wined3d_matrix mv; + unsigned int i, j; + + mv = *modelview; + if (legacy_lighting) + invert_matrix_3d(&mv, &mv); + else + invert_matrix(&mv, &mv); + /* Tests show that singular modelview matrices are used unchanged as normal + * matrices on D3D3 and older. There seems to be no clearly consistent + * behavior on newer D3D versions so always follow older ddraw behavior. */ + for (i = 0; i < 3; ++i) + for (j = 0; j < 3; ++j) + normal_matrix[i * 3 + j] = (&mv._11)[j * 4 + i]; +} + +static void wined3d_allocator_release_block(struct wined3d_allocator *allocator, + struct wined3d_allocator_block *block) +{ + block->parent = allocator->free; + allocator->free = block; +} + +static struct wined3d_allocator_block *wined3d_allocator_acquire_block(struct wined3d_allocator *allocator) +{ + struct wined3d_allocator_block *block; + + if (!allocator->free) + return heap_alloc(sizeof(*block)); + + block = allocator->free; + allocator->free = block->parent; + + return block; +} + +void wined3d_allocator_block_free(struct wined3d_allocator_block *block) +{ + struct wined3d_allocator *allocator = block->chunk->allocator; + struct wined3d_allocator_block *parent; + + while ((parent = block->parent) && block->sibling->free) + { + list_remove(&block->sibling->entry); + wined3d_allocator_release_block(allocator, block->sibling); + wined3d_allocator_release_block(allocator, block); + block = parent; + } + + block->free = true; + list_add_head(&block->chunk->available[block->order], &block->entry); +} + +static void wined3d_allocator_block_init(struct wined3d_allocator_block *block, + struct wined3d_allocator_chunk *chunk, struct wined3d_allocator_block *parent, + struct wined3d_allocator_block *sibling, unsigned int order, size_t offset, bool free) +{ + list_init(&block->entry); + block->chunk = chunk; + block->parent = parent; + block->sibling = sibling; + block->order = order; + block->offset = offset; + block->free = free; +} + +void wined3d_allocator_chunk_cleanup(struct wined3d_allocator_chunk *chunk) +{ + struct wined3d_allocator_block *block; + size_t i; + + if (list_empty(&chunk->available[0])) + { + ERR("Chunk %p is not empty.\n", chunk); + return; + } + + for (i = 1; i < ARRAY_SIZE(chunk->available); ++i) + { + if (!list_empty(&chunk->available[i])) + { + ERR("Chunk %p is not empty.\n", chunk); + return; + } + } + + block = LIST_ENTRY(list_head(&chunk->available[0]), struct wined3d_allocator_block, entry); + wined3d_allocator_release_block(chunk->allocator, block); +} + +bool wined3d_allocator_chunk_init(struct wined3d_allocator_chunk *chunk, struct wined3d_allocator *allocator) +{ + struct wined3d_allocator_block *block; + unsigned int i; + + if (!(block = wined3d_allocator_acquire_block(allocator))) + return false; + wined3d_allocator_block_init(block, chunk, NULL, NULL, 0, 0, true); + + list_init(&chunk->entry); + for (i = 0; i < ARRAY_SIZE(chunk->available); ++i) + { + list_init(&chunk->available[i]); + } + list_add_head(&chunk->available[0], &block->entry); + chunk->allocator = allocator; + chunk->map_count = 0; + chunk->map_ptr = NULL; + + return true; +} + +void wined3d_allocator_cleanup(struct wined3d_allocator *allocator) +{ + struct wined3d_allocator_chunk *chunk, *chunk2; + struct wined3d_allocator_block *block, *next; + size_t i; + + for (i = 0; i < allocator->pool_count; ++i) + { + LIST_FOR_EACH_ENTRY_SAFE(chunk, chunk2, &allocator->pools[i].chunks, struct wined3d_allocator_chunk, entry) + { + list_remove(&chunk->entry); + allocator->ops->allocator_destroy_chunk(chunk); + } + } + heap_free(allocator->pools); + + next = allocator->free; + while ((block = next)) + { + next = block->parent; + heap_free(block); + } +} + +static struct wined3d_allocator_block *wined3d_allocator_chunk_allocate(struct wined3d_allocator_chunk *chunk, + unsigned int order) +{ + struct wined3d_allocator_block *block, *left, *right; + unsigned int i = order; + + while (i) + { + if (!list_empty(&chunk->available[i])) + break; + --i; + } + + if (list_empty(&chunk->available[i])) + return NULL; + + block = LIST_ENTRY(list_head(&chunk->available[i]), struct wined3d_allocator_block, entry); + list_remove(&block->entry); + block->free = false; + + while (i < order) + { + if (!(left = wined3d_allocator_acquire_block(chunk->allocator))) + { + ERR("Failed to allocate left.\n"); + break; + } + + if (!(right = wined3d_allocator_acquire_block(chunk->allocator))) + { + ERR("Failed to allocate right.\n"); + wined3d_allocator_release_block(chunk->allocator, left); + break; + } + + wined3d_allocator_block_init(left, chunk, block, right, block->order + 1, block->offset, false); + wined3d_allocator_block_init(right, chunk, block, left, block->order + 1, + block->offset + (WINED3D_ALLOCATOR_CHUNK_SIZE >> left->order), true); + list_add_head(&chunk->available[right->order], &right->entry); + + block = left; + ++i; + } + + return block; +} + +struct wined3d_allocator_block *wined3d_allocator_allocate(struct wined3d_allocator *allocator, + struct wined3d_context *context, unsigned int memory_type, size_t size) +{ + struct wined3d_allocator_chunk *chunk; + struct wined3d_allocator_block *block; + unsigned int order; + + if (size > WINED3D_ALLOCATOR_CHUNK_SIZE / 2) + return NULL; + + if (size < WINED3D_ALLOCATOR_MIN_BLOCK_SIZE) + order = WINED3D_ALLOCATOR_CHUNK_ORDER_COUNT - 1; + else + order = wined3d_log2i(WINED3D_ALLOCATOR_CHUNK_SIZE / size); + + LIST_FOR_EACH_ENTRY(chunk, &allocator->pools[memory_type].chunks, struct wined3d_allocator_chunk, entry) + { + if ((block = wined3d_allocator_chunk_allocate(chunk, order))) + return block; + } + + if (!(chunk = allocator->ops->allocator_create_chunk(allocator, + context, memory_type, WINED3D_ALLOCATOR_CHUNK_SIZE))) + return NULL; + + if (!(block = wined3d_allocator_chunk_allocate(chunk, order))) + return NULL; + + return block; +} + +bool wined3d_allocator_init(struct wined3d_allocator *allocator, + size_t pool_count, const struct wined3d_allocator_ops *allocator_ops) +{ + size_t i; + + allocator->ops = allocator_ops; + allocator->pool_count = pool_count; + if (!(allocator->pools = heap_calloc(pool_count, sizeof(*allocator->pools)))) + return false; + for (i = 0; i < pool_count; ++i) + { + list_init(&allocator->pools[i].chunks); + } + + return true; +} diff --git a/wrappers/directx/d3dwine_wrapper/version.rc b/wrappers/directx/d3dwine_wrapper/version.rc new file mode 100644 index 00000000000..04393758af1 --- /dev/null +++ b/wrappers/directx/d3dwine_wrapper/version.rc @@ -0,0 +1,27 @@ +/* + * Copyright 2009 Austin English + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "config.h" /* Needed to get PACKAGE_VERSION */ + +#define WINE_FILEDESCRIPTION_STR "Wine D3D" +#define WINE_FILENAME_STR "wined3d.dll" +#define WINE_FILEVERSION_STR PACKAGE_VERSION +#define WINE_PRODUCTVERSION_STR PACKAGE_VERSION +#define WINE_PRODUCTNAME_STR "Wine D3D" + +#include "wine/wine_common_ver.rc" diff --git a/wrappers/directx/d3dwine_wrapper/vertexdeclaration.c b/wrappers/directx/d3dwine_wrapper/vertexdeclaration.c new file mode 100644 index 00000000000..45a01f4bdd4 --- /dev/null +++ b/wrappers/directx/d3dwine_wrapper/vertexdeclaration.c @@ -0,0 +1,453 @@ +/* + * vertex declaration implementation + * + * Copyright 2002-2005 Raphael Junqueira + * Copyright 2004 Jason Edmeades + * Copyright 2004 Christian Costa + * Copyright 2005 Oliver Stieber + * Copyright 2009 Henri Verbeet for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "config.h" +#include "wine/port.h" +#include "wined3d_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(d3d_decl); + +static void dump_wined3d_vertex_element(const struct wined3d_vertex_element *element) +{ + TRACE(" format: %s (%#x)\n", debug_d3dformat(element->format), element->format); + TRACE(" input_slot: %u\n", element->input_slot); + TRACE(" offset: %u\n", element->offset); + TRACE(" output_slot: %u\n", element->output_slot); + TRACE(" input slot class: %s\n", debug_d3dinput_classification(element->input_slot_class)); + TRACE("instance data step rate: %u\n", element->instance_data_step_rate); + TRACE(" method: %s (%#x)\n", debug_d3ddeclmethod(element->method), element->method); + TRACE(" usage: %s (%#x)\n", debug_d3ddeclusage(element->usage), element->usage); + TRACE(" usage_idx: %u\n", element->usage_idx); +} + +ULONG CDECL wined3d_vertex_declaration_incref(struct wined3d_vertex_declaration *declaration) +{ + ULONG refcount = InterlockedIncrement(&declaration->ref); + + TRACE("%p increasing refcount to %u.\n", declaration, refcount); + + return refcount; +} + +static void wined3d_vertex_declaration_destroy_object(void *object) +{ + struct wined3d_vertex_declaration *declaration = object; + + TRACE("declaration %p.\n", declaration); + + heap_free(declaration->elements); + heap_free(declaration); +} + +ULONG CDECL wined3d_vertex_declaration_decref(struct wined3d_vertex_declaration *declaration) +{ + ULONG refcount = InterlockedDecrement(&declaration->ref); + + TRACE("%p decreasing refcount to %u.\n", declaration, refcount); + + if (!refcount) + { + declaration->parent_ops->wined3d_object_destroyed(declaration->parent); + wined3d_cs_destroy_object(declaration->device->cs, + wined3d_vertex_declaration_destroy_object, declaration); + } + + return refcount; +} + +void * CDECL wined3d_vertex_declaration_get_parent(const struct wined3d_vertex_declaration *declaration) +{ + TRACE("declaration %p.\n", declaration); + + return declaration->parent; +} + +static BOOL declaration_element_valid_ffp(const struct wined3d_vertex_element *element) +{ + switch(element->usage) + { + case WINED3D_DECL_USAGE_POSITION: + case WINED3D_DECL_USAGE_POSITIONT: + switch(element->format) + { + case WINED3DFMT_R32G32_FLOAT: + case WINED3DFMT_R32G32B32_FLOAT: + case WINED3DFMT_R32G32B32A32_FLOAT: + case WINED3DFMT_R16G16_SINT: + case WINED3DFMT_R16G16B16A16_SINT: + case WINED3DFMT_R16G16_FLOAT: + case WINED3DFMT_R16G16B16A16_FLOAT: + return TRUE; + default: + return FALSE; + } + + case WINED3D_DECL_USAGE_BLEND_WEIGHT: + switch(element->format) + { + case WINED3DFMT_R32_FLOAT: + case WINED3DFMT_R32G32_FLOAT: + case WINED3DFMT_R32G32B32_FLOAT: + case WINED3DFMT_R32G32B32A32_FLOAT: + case WINED3DFMT_B8G8R8A8_UNORM: + case WINED3DFMT_R8G8B8A8_UINT: + case WINED3DFMT_R16G16_SINT: + case WINED3DFMT_R16G16B16A16_SINT: + case WINED3DFMT_R16G16_FLOAT: + case WINED3DFMT_R16G16B16A16_FLOAT: + return TRUE; + default: + return FALSE; + } + + case WINED3D_DECL_USAGE_NORMAL: + switch(element->format) + { + case WINED3DFMT_R32G32B32_FLOAT: + case WINED3DFMT_R32G32B32A32_FLOAT: + case WINED3DFMT_R16G16B16A16_SINT: + case WINED3DFMT_R16G16B16A16_FLOAT: + return TRUE; + default: + return FALSE; + } + + case WINED3D_DECL_USAGE_TEXCOORD: + switch(element->format) + { + case WINED3DFMT_R32_FLOAT: + case WINED3DFMT_R32G32_FLOAT: + case WINED3DFMT_R32G32B32_FLOAT: + case WINED3DFMT_R32G32B32A32_FLOAT: + case WINED3DFMT_R16G16_SINT: + case WINED3DFMT_R16G16B16A16_SINT: + case WINED3DFMT_R16G16_FLOAT: + case WINED3DFMT_R16G16B16A16_FLOAT: + return TRUE; + default: + return FALSE; + } + + case WINED3D_DECL_USAGE_COLOR: + switch(element->format) + { + case WINED3DFMT_R32G32B32_FLOAT: + case WINED3DFMT_R32G32B32A32_FLOAT: + case WINED3DFMT_B8G8R8A8_UNORM: + case WINED3DFMT_R8G8B8A8_UINT: + case WINED3DFMT_R16G16B16A16_SINT: + case WINED3DFMT_R8G8B8A8_UNORM: + case WINED3DFMT_R16G16B16A16_SNORM: + case WINED3DFMT_R16G16B16A16_UNORM: + case WINED3DFMT_R16G16B16A16_FLOAT: + return TRUE; + default: + return FALSE; + } + + default: + return FALSE; + } +} + +static HRESULT vertexdeclaration_init(struct wined3d_vertex_declaration *declaration, + struct wined3d_device *device, const struct wined3d_vertex_element *elements, UINT element_count, + void *parent, const struct wined3d_parent_ops *parent_ops) +{ + const struct wined3d_adapter *adapter = device->adapter; + unsigned int i; + + if (TRACE_ON(d3d_decl)) + { + for (i = 0; i < element_count; ++i) + { + dump_wined3d_vertex_element(elements + i); + } + } + + declaration->ref = 1; + declaration->parent = parent; + declaration->parent_ops = parent_ops; + declaration->device = device; + if (!(declaration->elements = heap_calloc(element_count, sizeof(*declaration->elements)))) + { + ERR("Failed to allocate elements memory.\n"); + return E_OUTOFMEMORY; + } + declaration->element_count = element_count; + + /* Do some static analysis on the elements to make reading the + * declaration more comfortable for the drawing code. */ + for (i = 0; i < element_count; ++i) + { + struct wined3d_vertex_declaration_element *e = &declaration->elements[i]; + unsigned int alignment; + + e->format = wined3d_get_format(adapter, elements[i].format, 0); + e->ffp_valid = declaration_element_valid_ffp(&elements[i]); + e->input_slot = elements[i].input_slot; + e->offset = elements[i].offset; + e->output_slot = elements[i].output_slot; + e->input_slot_class = elements[i].input_slot_class; + e->instance_data_step_rate = elements[i].instance_data_step_rate; + e->method = elements[i].method; + e->usage = elements[i].usage; + e->usage_idx = elements[i].usage_idx; + + if ((alignment = e->format->byte_count) > 4) + alignment = 4; + + if (e->usage == WINED3D_DECL_USAGE_POSITIONT) + declaration->position_transformed = TRUE; + + /* Find the streams used in the declaration. The vertex buffers have + * to be loaded when drawing, but filter tessellation pseudo streams. */ + if (e->input_slot >= WINED3D_MAX_STREAMS) + continue; + + if (!(e->format->flags[WINED3D_GL_RES_TYPE_BUFFER] & WINED3DFMT_FLAG_VERTEX_ATTRIBUTE)) + { + FIXME("The application tries to use an unsupported format (%s).\n", + debug_d3dformat(elements[i].format)); + heap_free(declaration->elements); + return E_INVALIDARG; + } + + if (e->offset == WINED3D_APPEND_ALIGNED_ELEMENT) + { + const struct wined3d_vertex_declaration_element *prev; + unsigned int j; + + e->offset = 0; + for (j = 1; j <= i; ++j) + { + prev = &declaration->elements[i - j]; + if (prev->input_slot == e->input_slot) + { + e->offset = (prev->offset + prev->format->byte_count + alignment - 1) & ~(alignment - 1); + break; + } + } + } + + if (e->offset & (alignment - 1)) + { + WARN("Declaration element %u with format %s and offset %u is not %u byte aligned.\n", + i, debug_d3dformat(elements[i].format), e->offset, alignment); + heap_free(declaration->elements); + return E_INVALIDARG; + } + } + + return WINED3D_OK; +} + +HRESULT CDECL wined3d_vertex_declaration_create(struct wined3d_device *device, + const struct wined3d_vertex_element *elements, UINT element_count, void *parent, + const struct wined3d_parent_ops *parent_ops, struct wined3d_vertex_declaration **declaration) +{ + struct wined3d_vertex_declaration *object; + HRESULT hr; + + TRACE("device %p, elements %p, element_count %u, parent %p, parent_ops %p, declaration %p.\n", + device, elements, element_count, parent, parent_ops, declaration); + + if (!(object = heap_alloc_zero(sizeof(*object)))) + return E_OUTOFMEMORY; + + hr = vertexdeclaration_init(object, device, elements, element_count, parent, parent_ops); + if (FAILED(hr)) + { + WARN("Failed to initialize vertex declaration, hr %#x.\n", hr); + heap_free(object); + return hr; + } + + TRACE("Created vertex declaration %p.\n", object); + *declaration = object; + + return WINED3D_OK; +} + +struct wined3d_fvf_convert_state +{ + const struct wined3d_adapter *adapter; + struct wined3d_vertex_element *elements; + unsigned int offset; + unsigned int idx; +}; + +static void append_decl_element(struct wined3d_fvf_convert_state *state, + enum wined3d_format_id format_id, enum wined3d_decl_usage usage, UINT usage_idx) +{ + struct wined3d_vertex_element *elements = state->elements; + const struct wined3d_format *format; + UINT offset = state->offset; + UINT idx = state->idx; + + elements[idx].format = format_id; + elements[idx].input_slot = 0; + elements[idx].offset = offset; + elements[idx].output_slot = WINED3D_OUTPUT_SLOT_SEMANTIC; + elements[idx].input_slot_class = WINED3D_INPUT_PER_VERTEX_DATA; + elements[idx].instance_data_step_rate = 0; + elements[idx].method = WINED3D_DECL_METHOD_DEFAULT; + elements[idx].usage = usage; + elements[idx].usage_idx = usage_idx; + + format = wined3d_get_format(state->adapter, format_id, 0); + state->offset += format->byte_count; + ++state->idx; +} + +static unsigned int convert_fvf_to_declaration(const struct wined3d_adapter *adapter, + DWORD fvf, struct wined3d_vertex_element **elements) +{ + BOOL has_pos = !!(fvf & WINED3DFVF_POSITION_MASK); + BOOL has_blend = (fvf & WINED3DFVF_XYZB5) > WINED3DFVF_XYZRHW; + BOOL has_blend_idx = has_blend && + (((fvf & WINED3DFVF_XYZB5) == WINED3DFVF_XYZB5) || + (fvf & WINED3DFVF_LASTBETA_D3DCOLOR) || + (fvf & WINED3DFVF_LASTBETA_UBYTE4)); + BOOL has_normal = !!(fvf & WINED3DFVF_NORMAL); + BOOL has_psize = !!(fvf & WINED3DFVF_PSIZE); + BOOL has_diffuse = !!(fvf & WINED3DFVF_DIFFUSE); + BOOL has_specular = !!(fvf & WINED3DFVF_SPECULAR); + + DWORD num_textures = (fvf & WINED3DFVF_TEXCOUNT_MASK) >> WINED3DFVF_TEXCOUNT_SHIFT; + DWORD texcoords = (fvf & 0xffff0000) >> 16; + struct wined3d_fvf_convert_state state; + unsigned int size; + unsigned int idx; + DWORD num_blends = 1 + (((fvf & WINED3DFVF_XYZB5) - WINED3DFVF_XYZB1) >> 1); + if (has_blend_idx) num_blends--; + + /* Compute declaration size */ + size = has_pos + (has_blend && num_blends > 0) + has_blend_idx + has_normal + + has_psize + has_diffuse + has_specular + num_textures; + + state.adapter = adapter; + if (!(state.elements = heap_calloc(size, sizeof(*state.elements)))) + return ~0u; + state.offset = 0; + state.idx = 0; + + if (has_pos) + { + if (!has_blend && (fvf & WINED3DFVF_XYZRHW)) + append_decl_element(&state, WINED3DFMT_R32G32B32A32_FLOAT, WINED3D_DECL_USAGE_POSITIONT, 0); + else if ((fvf & WINED3DFVF_XYZW) == WINED3DFVF_XYZW) + append_decl_element(&state, WINED3DFMT_R32G32B32A32_FLOAT, WINED3D_DECL_USAGE_POSITION, 0); + else + append_decl_element(&state, WINED3DFMT_R32G32B32_FLOAT, WINED3D_DECL_USAGE_POSITION, 0); + } + + if (has_blend && (num_blends > 0)) + { + if ((fvf & WINED3DFVF_XYZB5) == WINED3DFVF_XYZB2 && (fvf & WINED3DFVF_LASTBETA_D3DCOLOR)) + append_decl_element(&state, WINED3DFMT_B8G8R8A8_UNORM, WINED3D_DECL_USAGE_BLEND_WEIGHT, 0); + else + { + switch (num_blends) + { + case 1: + append_decl_element(&state, WINED3DFMT_R32_FLOAT, WINED3D_DECL_USAGE_BLEND_WEIGHT, 0); + break; + case 2: + append_decl_element(&state, WINED3DFMT_R32G32_FLOAT, WINED3D_DECL_USAGE_BLEND_WEIGHT, 0); + break; + case 3: + append_decl_element(&state, WINED3DFMT_R32G32B32_FLOAT, WINED3D_DECL_USAGE_BLEND_WEIGHT, 0); + break; + case 4: + append_decl_element(&state, WINED3DFMT_R32G32B32A32_FLOAT, WINED3D_DECL_USAGE_BLEND_WEIGHT, 0); + break; + default: + ERR("Unexpected amount of blend values: %u\n", num_blends); + } + } + } + + if (has_blend_idx) + { + if ((fvf & WINED3DFVF_LASTBETA_UBYTE4) + || ((fvf & WINED3DFVF_XYZB5) == WINED3DFVF_XYZB2 && (fvf & WINED3DFVF_LASTBETA_D3DCOLOR))) + append_decl_element(&state, WINED3DFMT_R8G8B8A8_UINT, WINED3D_DECL_USAGE_BLEND_INDICES, 0); + else if (fvf & WINED3DFVF_LASTBETA_D3DCOLOR) + append_decl_element(&state, WINED3DFMT_B8G8R8A8_UNORM, WINED3D_DECL_USAGE_BLEND_INDICES, 0); + else + append_decl_element(&state, WINED3DFMT_R32_FLOAT, WINED3D_DECL_USAGE_BLEND_INDICES, 0); + } + + if (has_normal) + append_decl_element(&state, WINED3DFMT_R32G32B32_FLOAT, WINED3D_DECL_USAGE_NORMAL, 0); + if (has_psize) + append_decl_element(&state, WINED3DFMT_R32_FLOAT, WINED3D_DECL_USAGE_PSIZE, 0); + if (has_diffuse) + append_decl_element(&state, WINED3DFMT_B8G8R8A8_UNORM, WINED3D_DECL_USAGE_COLOR, 0); + if (has_specular) + append_decl_element(&state, WINED3DFMT_B8G8R8A8_UNORM, WINED3D_DECL_USAGE_COLOR, 1); + + for (idx = 0; idx < num_textures; ++idx) + { + switch ((texcoords >> (idx * 2)) & 0x03) + { + case WINED3DFVF_TEXTUREFORMAT1: + append_decl_element(&state, WINED3DFMT_R32_FLOAT, WINED3D_DECL_USAGE_TEXCOORD, idx); + break; + case WINED3DFVF_TEXTUREFORMAT2: + append_decl_element(&state, WINED3DFMT_R32G32_FLOAT, WINED3D_DECL_USAGE_TEXCOORD, idx); + break; + case WINED3DFVF_TEXTUREFORMAT3: + append_decl_element(&state, WINED3DFMT_R32G32B32_FLOAT, WINED3D_DECL_USAGE_TEXCOORD, idx); + break; + case WINED3DFVF_TEXTUREFORMAT4: + append_decl_element(&state, WINED3DFMT_R32G32B32A32_FLOAT, WINED3D_DECL_USAGE_TEXCOORD, idx); + break; + } + } + + *elements = state.elements; + return size; +} + +HRESULT CDECL wined3d_vertex_declaration_create_from_fvf(struct wined3d_device *device, + DWORD fvf, void *parent, const struct wined3d_parent_ops *parent_ops, + struct wined3d_vertex_declaration **declaration) +{ + struct wined3d_vertex_element *elements; + unsigned int size; + DWORD hr; + + TRACE("device %p, fvf %#x, parent %p, parent_ops %p, declaration %p.\n", + device, fvf, parent, parent_ops, declaration); + + size = convert_fvf_to_declaration(device->adapter, fvf, &elements); + if (size == ~0u) + return E_OUTOFMEMORY; + + hr = wined3d_vertex_declaration_create(device, elements, size, parent, parent_ops, declaration); + heap_free(elements); + return hr; +} diff --git a/wrappers/directx/d3dwine_wrapper/view.c b/wrappers/directx/d3dwine_wrapper/view.c new file mode 100644 index 00000000000..9650f6c08e3 --- /dev/null +++ b/wrappers/directx/d3dwine_wrapper/view.c @@ -0,0 +1,1736 @@ +/* + * Copyright 2009, 2011 Henri Verbeet for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + */ + +#include "config.h" +#include "wine/port.h" + +#include "wined3d_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(d3d); + +#define WINED3D_VIEW_CUBE_ARRAY (WINED3D_VIEW_TEXTURE_CUBE | WINED3D_VIEW_TEXTURE_ARRAY) + +static BOOL is_stencil_view_format(const struct wined3d_format *format) +{ + return format->id == WINED3DFMT_X24_TYPELESS_G8_UINT + || format->id == WINED3DFMT_X32_TYPELESS_G8X24_UINT; +} + +static GLenum get_texture_view_target(const struct wined3d_gl_info *gl_info, + const struct wined3d_view_desc *desc, const struct wined3d_texture_gl *texture_gl) +{ + static const struct + { + GLenum texture_target; + unsigned int view_flags; + GLenum view_target; + enum wined3d_gl_extension extension; + } + view_types[] = + { + {GL_TEXTURE_CUBE_MAP, 0, GL_TEXTURE_CUBE_MAP}, + {GL_TEXTURE_RECTANGLE, 0, GL_TEXTURE_RECTANGLE}, + {GL_TEXTURE_3D, 0, GL_TEXTURE_3D}, + + {GL_TEXTURE_2D, 0, GL_TEXTURE_2D}, + {GL_TEXTURE_2D, WINED3D_VIEW_TEXTURE_ARRAY, GL_TEXTURE_2D_ARRAY}, + {GL_TEXTURE_2D_ARRAY, 0, GL_TEXTURE_2D}, + {GL_TEXTURE_2D_ARRAY, WINED3D_VIEW_TEXTURE_ARRAY, GL_TEXTURE_2D_ARRAY}, + {GL_TEXTURE_2D_ARRAY, WINED3D_VIEW_TEXTURE_CUBE, GL_TEXTURE_CUBE_MAP}, + {GL_TEXTURE_2D_ARRAY, WINED3D_VIEW_CUBE_ARRAY, GL_TEXTURE_CUBE_MAP_ARRAY, ARB_TEXTURE_CUBE_MAP_ARRAY}, + + {GL_TEXTURE_2D_MULTISAMPLE, 0, GL_TEXTURE_2D_MULTISAMPLE}, + {GL_TEXTURE_2D_MULTISAMPLE, WINED3D_VIEW_TEXTURE_ARRAY, GL_TEXTURE_2D_MULTISAMPLE_ARRAY}, + {GL_TEXTURE_2D_MULTISAMPLE_ARRAY, 0, GL_TEXTURE_2D_MULTISAMPLE}, + {GL_TEXTURE_2D_MULTISAMPLE_ARRAY, WINED3D_VIEW_TEXTURE_ARRAY, GL_TEXTURE_2D_MULTISAMPLE_ARRAY}, + + {GL_TEXTURE_1D, 0, GL_TEXTURE_1D}, + {GL_TEXTURE_1D, WINED3D_VIEW_TEXTURE_ARRAY, GL_TEXTURE_1D_ARRAY}, + {GL_TEXTURE_1D_ARRAY, 0, GL_TEXTURE_1D}, + {GL_TEXTURE_1D_ARRAY, WINED3D_VIEW_TEXTURE_ARRAY, GL_TEXTURE_1D_ARRAY}, + }; + unsigned int flags = desc->flags & (WINED3D_VIEW_BUFFER_RAW | WINED3D_VIEW_BUFFER_APPEND + | WINED3D_VIEW_BUFFER_COUNTER | WINED3D_VIEW_TEXTURE_CUBE | WINED3D_VIEW_TEXTURE_ARRAY); + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(view_types); ++i) + { + if (view_types[i].texture_target != texture_gl->target || view_types[i].view_flags != flags) + continue; + if (gl_info->supported[view_types[i].extension]) + return view_types[i].view_target; + + FIXME("Extension %#x not supported.\n", view_types[i].extension); + } + + FIXME("Unhandled view flags %#x for texture target %#x.\n", flags, texture_gl->target); + return texture_gl->target; +} + +static const struct wined3d_format *validate_resource_view(const struct wined3d_view_desc *desc, + struct wined3d_resource *resource, BOOL mip_slice, BOOL allow_srgb_toggle) +{ + const struct wined3d_adapter *adapter = resource->device->adapter; + const struct wined3d_format *format; + + format = wined3d_get_format(adapter, desc->format_id, resource->bind_flags); + if (resource->type == WINED3D_RTYPE_BUFFER && (desc->flags & WINED3D_VIEW_BUFFER_RAW)) + { + if (format->id != WINED3DFMT_R32_TYPELESS) + { + WARN("Invalid format %s for raw buffer view.\n", debug_d3dformat(format->id)); + return NULL; + } + + format = wined3d_get_format(adapter, WINED3DFMT_R32_UINT, resource->bind_flags); + } + + if (wined3d_format_is_typeless(format)) + { + WARN("Trying to create view for typeless format %s.\n", debug_d3dformat(format->id)); + return NULL; + } + + if (resource->type == WINED3D_RTYPE_BUFFER) + { + struct wined3d_buffer *buffer = buffer_from_resource(resource); + unsigned int buffer_size, element_size; + + if (buffer->structure_byte_stride) + { + if (desc->format_id != WINED3DFMT_UNKNOWN) + { + WARN("Invalid format %s for structured buffer view.\n", debug_d3dformat(desc->format_id)); + return NULL; + } + + format = wined3d_get_format(adapter, WINED3DFMT_R32_UINT, resource->bind_flags); + element_size = buffer->structure_byte_stride; + } + else + { + element_size = format->byte_count; + } + + if (!element_size) + return NULL; + + buffer_size = buffer->resource.size / element_size; + if (desc->u.buffer.start_idx >= buffer_size + || desc->u.buffer.count > buffer_size - desc->u.buffer.start_idx) + return NULL; + } + else + { + struct wined3d_texture *texture = texture_from_resource(resource); + unsigned int depth_or_layer_count; + + if (resource->format->id != format->id && !wined3d_format_is_typeless(resource->format) + && (!allow_srgb_toggle || !wined3d_formats_are_srgb_variants(resource->format->id, format->id))) + { + WARN("Trying to create incompatible view for non typeless format %s.\n", + debug_d3dformat(format->id)); + return NULL; + } + + if (mip_slice && resource->type == WINED3D_RTYPE_TEXTURE_3D) + depth_or_layer_count = wined3d_texture_get_level_depth(texture, desc->u.texture.level_idx); + else + depth_or_layer_count = texture->layer_count; + + if (!desc->u.texture.level_count + || (mip_slice && desc->u.texture.level_count != 1) + || desc->u.texture.level_idx >= texture->level_count + || desc->u.texture.level_count > texture->level_count - desc->u.texture.level_idx + || !desc->u.texture.layer_count + || desc->u.texture.layer_idx >= depth_or_layer_count + || desc->u.texture.layer_count > depth_or_layer_count - desc->u.texture.layer_idx) + return NULL; + } + + return format; +} + +static void create_texture_view(struct wined3d_gl_view *view, GLenum view_target, + const struct wined3d_view_desc *desc, struct wined3d_texture_gl *texture_gl, + const struct wined3d_format *view_format) +{ + const struct wined3d_format_gl *view_format_gl; + unsigned int level_idx, layer_idx, layer_count; + const struct wined3d_gl_info *gl_info; + struct wined3d_context_gl *context_gl; + struct wined3d_context *context; + GLuint texture_name; + + view_format_gl = wined3d_format_gl(view_format); + view->target = view_target; + + context = context_acquire(texture_gl->t.resource.device, NULL, 0); + context_gl = wined3d_context_gl(context); + gl_info = context_gl->gl_info; + + if (!gl_info->supported[ARB_TEXTURE_VIEW]) + { + context_release(context); + FIXME("OpenGL implementation does not support texture views.\n"); + return; + } + + wined3d_texture_gl_prepare_texture(texture_gl, context_gl, FALSE); + texture_name = wined3d_texture_gl_get_texture_name(texture_gl, context, FALSE); + + level_idx = desc->u.texture.level_idx; + layer_idx = desc->u.texture.layer_idx; + layer_count = desc->u.texture.layer_count; + if (view_target == GL_TEXTURE_3D) + { + if (layer_idx || layer_count != wined3d_texture_get_level_depth(&texture_gl->t, level_idx)) + FIXME("Depth slice (%u-%u) not supported.\n", layer_idx, layer_count); + layer_idx = 0; + layer_count = 1; + } + + gl_info->gl_ops.gl.p_glGenTextures(1, &view->name); + GL_EXTCALL(glTextureView(view->name, view->target, texture_name, view_format_gl->internal, + level_idx, desc->u.texture.level_count, layer_idx, layer_count)); + checkGLcall("create texture view"); + + if (is_stencil_view_format(view_format)) + { + static const GLint swizzle[] = {GL_ZERO, GL_RED, GL_ZERO, GL_ZERO}; + + if (!gl_info->supported[ARB_STENCIL_TEXTURING]) + { + context_release(context); + FIXME("OpenGL implementation does not support stencil texturing.\n"); + return; + } + + wined3d_context_gl_bind_texture(context_gl, view->target, view->name); + gl_info->gl_ops.gl.p_glTexParameteriv(view->target, GL_TEXTURE_SWIZZLE_RGBA, swizzle); + gl_info->gl_ops.gl.p_glTexParameteri(view->target, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_STENCIL_INDEX); + checkGLcall("initialize stencil view"); + + context_invalidate_compute_state(context, STATE_COMPUTE_SHADER_RESOURCE_BINDING); + context_invalidate_state(context, STATE_GRAPHICS_SHADER_RESOURCE_BINDING); + } + else if (!is_identity_fixup(view_format->color_fixup) && can_use_texture_swizzle(context->d3d_info, view_format)) + { + GLint swizzle[4]; + + wined3d_context_gl_bind_texture(context_gl, view->target, view->name); + wined3d_gl_texture_swizzle_from_color_fixup(swizzle, view_format->color_fixup); + gl_info->gl_ops.gl.p_glTexParameteriv(view->target, GL_TEXTURE_SWIZZLE_RGBA, swizzle); + checkGLcall("set format swizzle"); + + context_invalidate_compute_state(context, STATE_COMPUTE_SHADER_RESOURCE_BINDING); + context_invalidate_state(context, STATE_GRAPHICS_SHADER_RESOURCE_BINDING); + } + + context_release(context); +} + +static void create_buffer_texture(struct wined3d_gl_view *view, struct wined3d_context_gl *context_gl, + struct wined3d_buffer_gl *buffer_gl, const struct wined3d_format_gl *view_format_gl, + unsigned int offset, unsigned int size) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + + if (!gl_info->supported[ARB_TEXTURE_BUFFER_OBJECT]) + { + FIXME("OpenGL implementation does not support buffer textures.\n"); + return; + } + + if ((offset & (gl_info->limits.texture_buffer_offset_alignment - 1))) + { + FIXME("Buffer offset %u is not %u byte aligned.\n", + offset, gl_info->limits.texture_buffer_offset_alignment); + return; + } + + wined3d_buffer_load_location(&buffer_gl->b, &context_gl->c, WINED3D_LOCATION_BUFFER); + + view->target = GL_TEXTURE_BUFFER; + if (!view->name) + gl_info->gl_ops.gl.p_glGenTextures(1, &view->name); + + wined3d_context_gl_bind_texture(context_gl, GL_TEXTURE_BUFFER, view->name); + if (gl_info->supported[ARB_TEXTURE_BUFFER_RANGE]) + { + GL_EXTCALL(glTexBufferRange(GL_TEXTURE_BUFFER, view_format_gl->internal, buffer_gl->bo.id, offset, size)); + } + else + { + if (offset || size != buffer_gl->b.resource.size) + FIXME("OpenGL implementation does not support ARB_texture_buffer_range.\n"); + GL_EXTCALL(glTexBuffer(GL_TEXTURE_BUFFER, view_format_gl->internal, buffer_gl->bo.id)); + } + checkGLcall("Create buffer texture"); + + context_invalidate_compute_state(&context_gl->c, STATE_COMPUTE_SHADER_RESOURCE_BINDING); + context_invalidate_state(&context_gl->c, STATE_GRAPHICS_SHADER_RESOURCE_BINDING); +} + +static void get_buffer_view_range(const struct wined3d_buffer *buffer, + const struct wined3d_view_desc *desc, const struct wined3d_format *view_format, + unsigned int *offset, unsigned int *size) +{ + if (desc->format_id == WINED3DFMT_UNKNOWN) + { + *offset = desc->u.buffer.start_idx * buffer->structure_byte_stride; + *size = desc->u.buffer.count * buffer->structure_byte_stride; + } + else + { + *offset = desc->u.buffer.start_idx * view_format->byte_count; + *size = desc->u.buffer.count * view_format->byte_count; + } +} + +static void create_buffer_view(struct wined3d_gl_view *view, struct wined3d_context *context, + const struct wined3d_view_desc *desc, struct wined3d_buffer *buffer, + const struct wined3d_format *view_format) +{ + unsigned int offset, size; + + get_buffer_view_range(buffer, desc, view_format, &offset, &size); + create_buffer_texture(view, wined3d_context_gl(context), + wined3d_buffer_gl(buffer), wined3d_format_gl(view_format), offset, size); +} + +static void wined3d_view_invalidate_location(struct wined3d_resource *resource, + const struct wined3d_view_desc *desc, DWORD location) +{ + unsigned int i, sub_resource_idx, layer_count; + struct wined3d_texture *texture; + + if (resource->type == WINED3D_RTYPE_BUFFER) + { + wined3d_buffer_invalidate_location(buffer_from_resource(resource), location); + return; + } + + texture = texture_from_resource(resource); + + sub_resource_idx = desc->u.texture.layer_idx * texture->level_count + desc->u.texture.level_idx; + layer_count = resource->type != WINED3D_RTYPE_TEXTURE_3D ? desc->u.texture.layer_count : 1; + for (i = 0; i < layer_count; ++i, sub_resource_idx += texture->level_count) + wined3d_texture_invalidate_location(texture, sub_resource_idx, location); +} + +ULONG CDECL wined3d_rendertarget_view_incref(struct wined3d_rendertarget_view *view) +{ + ULONG refcount = InterlockedIncrement(&view->refcount); + + TRACE("%p increasing refcount to %u.\n", view, refcount); + + return refcount; +} + +void wined3d_rendertarget_view_cleanup(struct wined3d_rendertarget_view *view) +{ + /* Call wined3d_object_destroyed() before releasing the resource, + * since releasing the resource may end up destroying the parent. */ + view->parent_ops->wined3d_object_destroyed(view->parent); + wined3d_resource_decref(view->resource); +} + +ULONG CDECL wined3d_rendertarget_view_decref(struct wined3d_rendertarget_view *view) +{ + ULONG refcount = InterlockedDecrement(&view->refcount); + + TRACE("%p decreasing refcount to %u.\n", view, refcount); + + if (!refcount) + view->resource->device->adapter->adapter_ops->adapter_destroy_rendertarget_view(view); + + return refcount; +} + +void * CDECL wined3d_rendertarget_view_get_parent(const struct wined3d_rendertarget_view *view) +{ + TRACE("view %p.\n", view); + + return view->parent; +} + +void * CDECL wined3d_rendertarget_view_get_sub_resource_parent(const struct wined3d_rendertarget_view *view) +{ + struct wined3d_texture *texture; + + TRACE("view %p.\n", view); + + if (view->resource->type == WINED3D_RTYPE_BUFFER) + return wined3d_buffer_get_parent(buffer_from_resource(view->resource)); + + texture = texture_from_resource(view->resource); + + return texture->sub_resources[view->sub_resource_idx].parent; +} + +void CDECL wined3d_rendertarget_view_set_parent(struct wined3d_rendertarget_view *view, void *parent) +{ + TRACE("view %p, parent %p.\n", view, parent); + + view->parent = parent; +} + +struct wined3d_resource * CDECL wined3d_rendertarget_view_get_resource(const struct wined3d_rendertarget_view *view) +{ + TRACE("view %p.\n", view); + + return view->resource; +} + +void wined3d_rendertarget_view_get_drawable_size(const struct wined3d_rendertarget_view *view, + const struct wined3d_context *context, unsigned int *width, unsigned int *height) +{ + const struct wined3d_texture *texture; + + if (view->resource->type != WINED3D_RTYPE_TEXTURE_2D) + { + *width = view->width; + *height = view->height; + return; + } + + texture = texture_from_resource(view->resource); + if (texture->swapchain) + { + /* The drawable size of an onscreen drawable is the surface size. + * (Actually: The window size, but the surface is created in window + * size.) */ + *width = texture->resource.width; + *height = texture->resource.height; + } + else if (wined3d_settings.offscreen_rendering_mode == ORM_BACKBUFFER) + { + const struct wined3d_swapchain_desc *desc = &context->swapchain->state.desc; + + /* The drawable size of a backbuffer / aux buffer offscreen target is + * the size of the current context's drawable, which is the size of + * the back buffer of the swapchain the active context belongs to. */ + *width = desc->backbuffer_width; + *height = desc->backbuffer_height; + } + else + { + unsigned int level_idx = view->sub_resource_idx % texture->level_count; + + /* The drawable size of an FBO target is the OpenGL texture size, + * which is the power of two size. */ + *width = wined3d_texture_get_level_pow2_width(texture, level_idx); + *height = wined3d_texture_get_level_pow2_height(texture, level_idx); + } +} + +void wined3d_rendertarget_view_prepare_location(struct wined3d_rendertarget_view *view, + struct wined3d_context *context, DWORD location) +{ + struct wined3d_resource *resource = view->resource; + unsigned int i, sub_resource_idx, layer_count; + struct wined3d_texture *texture; + + if (resource->type == WINED3D_RTYPE_BUFFER) + { + FIXME("Not implemented for resources %s.\n", debug_d3dresourcetype(resource->type)); + return; + } + + texture = texture_from_resource(resource); + sub_resource_idx = view->sub_resource_idx; + layer_count = resource->type != WINED3D_RTYPE_TEXTURE_3D ? view->layer_count : 1; + for (i = 0; i < layer_count; ++i, sub_resource_idx += texture->level_count) + wined3d_texture_prepare_location(texture, sub_resource_idx, context, location); +} + +void wined3d_rendertarget_view_load_location(struct wined3d_rendertarget_view *view, + struct wined3d_context *context, DWORD location) +{ + struct wined3d_resource *resource = view->resource; + unsigned int i, sub_resource_idx, layer_count; + struct wined3d_texture *texture; + + if (resource->type == WINED3D_RTYPE_BUFFER) + { + wined3d_buffer_load_location(buffer_from_resource(resource), context, location); + return; + } + + texture = texture_from_resource(resource); + sub_resource_idx = view->sub_resource_idx; + layer_count = resource->type != WINED3D_RTYPE_TEXTURE_3D ? view->layer_count : 1; + for (i = 0; i < layer_count; ++i, sub_resource_idx += texture->level_count) + wined3d_texture_load_location(texture, sub_resource_idx, context, location); +} + +void wined3d_rendertarget_view_validate_location(struct wined3d_rendertarget_view *view, DWORD location) +{ + struct wined3d_resource *resource = view->resource; + unsigned int i, sub_resource_idx, layer_count; + struct wined3d_texture *texture; + + if (resource->type == WINED3D_RTYPE_BUFFER) + { + FIXME("Not implemented for resources %s.\n", debug_d3dresourcetype(resource->type)); + return; + } + + texture = texture_from_resource(resource); + sub_resource_idx = view->sub_resource_idx; + layer_count = resource->type != WINED3D_RTYPE_TEXTURE_3D ? view->layer_count : 1; + for (i = 0; i < layer_count; ++i, sub_resource_idx += texture->level_count) + wined3d_texture_validate_location(texture, sub_resource_idx, location); +} + +void wined3d_rendertarget_view_invalidate_location(struct wined3d_rendertarget_view *view, DWORD location) +{ + wined3d_view_invalidate_location(view->resource, &view->desc, location); +} + +static void wined3d_render_target_view_gl_cs_init(void *object) +{ + struct wined3d_rendertarget_view_gl *view_gl = object; + struct wined3d_resource *resource = view_gl->v.resource; + const struct wined3d_view_desc *desc = &view_gl->v.desc; + + TRACE("view_gl %p.\n", view_gl); + + if (resource->type == WINED3D_RTYPE_BUFFER) + { + FIXME("Not implemented for resources %s.\n", debug_d3dresourcetype(resource->type)); + } + else + { + struct wined3d_texture_gl *texture_gl = wined3d_texture_gl(texture_from_resource(resource)); + unsigned int depth_or_layer_count; + + if (resource->type == WINED3D_RTYPE_TEXTURE_3D) + depth_or_layer_count = wined3d_texture_get_level_depth(&texture_gl->t, desc->u.texture.level_idx); + else + depth_or_layer_count = texture_gl->t.layer_count; + + if (resource->format->id != view_gl->v.format->id + || (view_gl->v.layer_count != 1 && view_gl->v.layer_count != depth_or_layer_count)) + { + GLenum resource_class, view_class; + + resource_class = wined3d_format_gl(resource->format)->view_class; + view_class = wined3d_format_gl(view_gl->v.format)->view_class; + if (resource_class != view_class) + { + FIXME("Render target view not supported, resource format %s, view format %s.\n", + debug_d3dformat(resource->format->id), debug_d3dformat(view_gl->v.format->id)); + return; + } + if (texture_gl->t.swapchain && texture_gl->t.swapchain->state.desc.backbuffer_count > 1) + { + FIXME("Swapchain views not supported.\n"); + return; + } + + create_texture_view(&view_gl->gl_view, texture_gl->target, desc, texture_gl, view_gl->v.format); + } + } +} + +static HRESULT wined3d_rendertarget_view_init(struct wined3d_rendertarget_view *view, + const struct wined3d_view_desc *desc, struct wined3d_resource *resource, + void *parent, const struct wined3d_parent_ops *parent_ops) +{ + BOOL allow_srgb_toggle = FALSE; + + view->refcount = 1; + view->parent = parent; + view->parent_ops = parent_ops; + + if (resource->type != WINED3D_RTYPE_BUFFER) + { + struct wined3d_texture *texture = texture_from_resource(resource); + + if (texture->swapchain) + allow_srgb_toggle = TRUE; + } + if (!(view->format = validate_resource_view(desc, resource, TRUE, allow_srgb_toggle))) + return E_INVALIDARG; + view->format_flags = view->format->flags[resource->gl_type]; + view->desc = *desc; + + if (resource->type == WINED3D_RTYPE_BUFFER) + { + view->sub_resource_idx = 0; + view->layer_count = 1; + view->width = desc->u.buffer.count; + view->height = 1; + } + else + { + struct wined3d_texture *texture = texture_from_resource(resource); + + view->sub_resource_idx = desc->u.texture.level_idx; + if (resource->type != WINED3D_RTYPE_TEXTURE_3D) + view->sub_resource_idx += desc->u.texture.layer_idx * texture->level_count; + view->layer_count = desc->u.texture.layer_count; + view->width = wined3d_texture_get_level_width(texture, desc->u.texture.level_idx); + view->height = wined3d_texture_get_level_height(texture, desc->u.texture.level_idx); + } + + wined3d_resource_incref(view->resource = resource); + + return WINED3D_OK; +} + +HRESULT wined3d_rendertarget_view_no3d_init(struct wined3d_rendertarget_view *view_no3d, + const struct wined3d_view_desc *desc, struct wined3d_resource *resource, + void *parent, const struct wined3d_parent_ops *parent_ops) +{ + TRACE("view_no3d %p, desc %s, resource %p, parent %p, parent_ops %p.\n", + view_no3d, wined3d_debug_view_desc(desc, resource), resource, parent, parent_ops); + + return wined3d_rendertarget_view_init(view_no3d, desc, resource, parent, parent_ops); +} + +HRESULT wined3d_rendertarget_view_gl_init(struct wined3d_rendertarget_view_gl *view_gl, + const struct wined3d_view_desc *desc, struct wined3d_resource *resource, + void *parent, const struct wined3d_parent_ops *parent_ops) +{ + HRESULT hr; + + TRACE("view_gl %p, desc %s, resource %p, parent %p, parent_ops %p.\n", + view_gl, wined3d_debug_view_desc(desc, resource), resource, parent, parent_ops); + + if (FAILED(hr = wined3d_rendertarget_view_init(&view_gl->v, desc, resource, parent, parent_ops))) + return hr; + + wined3d_cs_init_object(resource->device->cs, wined3d_render_target_view_gl_cs_init, view_gl); + + return hr; +} + +VkImageViewType vk_image_view_type_from_wined3d(enum wined3d_resource_type type, uint32_t flags) +{ + switch (type) + { + case WINED3D_RTYPE_TEXTURE_1D: + if (flags & WINED3D_VIEW_TEXTURE_ARRAY) + return VK_IMAGE_VIEW_TYPE_1D_ARRAY; + else + return VK_IMAGE_VIEW_TYPE_1D; + + case WINED3D_RTYPE_TEXTURE_2D: + if (flags & WINED3D_VIEW_TEXTURE_CUBE) + { + if (flags & WINED3D_VIEW_TEXTURE_ARRAY) + return VK_IMAGE_VIEW_TYPE_CUBE_ARRAY; + else + return VK_IMAGE_VIEW_TYPE_CUBE; + } + if (flags & WINED3D_VIEW_TEXTURE_ARRAY) + return VK_IMAGE_VIEW_TYPE_2D_ARRAY; + else + return VK_IMAGE_VIEW_TYPE_2D; + + case WINED3D_RTYPE_TEXTURE_3D: + return VK_IMAGE_VIEW_TYPE_3D; + + default: + ERR("Unhandled resource type %s.\n", debug_d3dresourcetype(type)); + return ~0u; + } +} + +static VkBufferView wined3d_view_vk_create_buffer_view(struct wined3d_context_vk *context_vk, + const struct wined3d_view_desc *desc, struct wined3d_buffer_vk *buffer_vk, + const struct wined3d_format_vk *view_format_vk) +{ + const struct wined3d_vk_info *vk_info = context_vk->vk_info; + VkBufferViewCreateInfo create_info; + struct wined3d_device_vk *device_vk; + VkBufferView vk_buffer_view; + unsigned int offset, size; + VkResult vr; + + get_buffer_view_range(&buffer_vk->b, desc, &view_format_vk->f, &offset, &size); + wined3d_buffer_prepare_location(&buffer_vk->b, &context_vk->c, WINED3D_LOCATION_BUFFER); + + create_info.sType = VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO; + create_info.pNext = NULL; + create_info.flags = 0; + create_info.buffer = buffer_vk->bo.vk_buffer; + create_info.format = view_format_vk->vk_format; + create_info.offset = buffer_vk->bo.buffer_offset + offset; + create_info.range = size; + + device_vk = wined3d_device_vk(buffer_vk->b.resource.device); + if ((vr = VK_CALL(vkCreateBufferView(device_vk->vk_device, &create_info, NULL, &vk_buffer_view))) < 0) + { + ERR("Failed to create buffer view, vr %s.\n", wined3d_debug_vkresult(vr)); + return VK_NULL_HANDLE; + } + + return vk_buffer_view; +} + +static VkImageView wined3d_view_vk_create_texture_view(struct wined3d_context_vk *context_vk, + const struct wined3d_view_desc *desc, struct wined3d_texture_vk *texture_vk, + const struct wined3d_format_vk *view_format_vk, struct color_fixup_desc fixup, bool rtv) +{ + const struct wined3d_resource *resource = &texture_vk->t.resource; + const struct wined3d_vk_info *vk_info = context_vk->vk_info; + const struct wined3d_format_vk *format_vk; + struct wined3d_device_vk *device_vk; + VkImageViewCreateInfo create_info; + VkImageView vk_image_view; + VkResult vr; + + device_vk = wined3d_device_vk(resource->device); + + if (!wined3d_texture_vk_prepare_texture(texture_vk, context_vk)) + { + ERR("Failed to prepare texture.\n"); + return VK_NULL_HANDLE; + } + + /* Depth formats are a little complicated. For example, the typeless + * format corresponding to depth/stencil view format WINED3DFMT_D32_FLOAT + * is WINED3DFMT_R32_TYPELESS, and the corresponding shader resource view + * format would be WINED3DFMT_R32_FLOAT. Vulkan depth/stencil formats are + * only compatible with themselves, so it's not possible to create e.g. a + * VK_FORMAT_R32_SFLOAT view on a VK_FORMAT_D32_SFLOAT image. In order to + * make it work, we create Vulkan images for WINED3DFMT_R32_TYPELESS + * resources with either a depth format (VK_FORMAT_D32_SFLOAT) or a colour + * format, depending on whether the bind flags include + * WINED3D_BIND_DEPTH_STENCIL or not. In order to then create a Vulkan + * view on the image, we then replace the view format here with the + * underlying resource format. However, that means it's still not possible + * to create e.g. a WINED3DFMT_R32_UINT view on a WINED3DFMT_R32_TYPELESS + * depth/stencil resource. */ + if (resource->bind_flags & WINED3D_BIND_DEPTH_STENCIL) + format_vk = wined3d_format_vk(resource->format); + else + format_vk = view_format_vk; + + create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + create_info.pNext = NULL; + create_info.flags = 0; + create_info.image = texture_vk->vk_image; + create_info.viewType = vk_image_view_type_from_wined3d(resource->type, desc->flags); + if (rtv && create_info.viewType == VK_IMAGE_VIEW_TYPE_3D) + { + if (desc->u.texture.layer_count > 1) + create_info.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY; + else + create_info.viewType = VK_IMAGE_VIEW_TYPE_2D; + } + create_info.format = format_vk->vk_format; + if (is_stencil_view_format(&view_format_vk->f)) + { + create_info.components.r = VK_COMPONENT_SWIZZLE_ZERO; + create_info.components.g = VK_COMPONENT_SWIZZLE_R; + create_info.components.b = VK_COMPONENT_SWIZZLE_ZERO; + create_info.components.a = VK_COMPONENT_SWIZZLE_ZERO; + } + else if (is_identity_fixup(fixup) || !can_use_texture_swizzle(context_vk->c.d3d_info, &format_vk->f)) + { + create_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + create_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + create_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + create_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + } + else + { + wined3d_vk_swizzle_from_color_fixup(&create_info.components, fixup); + } + if ((resource->bind_flags & WINED3D_BIND_DEPTH_STENCIL) + && (view_format_vk->f.red_size || view_format_vk->f.green_size)) + { + create_info.subresourceRange.aspectMask = 0; + if (view_format_vk->f.red_size) + create_info.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; + if (view_format_vk->f.green_size) + create_info.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; + } + else + { + create_info.subresourceRange.aspectMask = vk_aspect_mask_from_format(&format_vk->f); + } + create_info.subresourceRange.baseMipLevel = desc->u.texture.level_idx; + create_info.subresourceRange.levelCount = desc->u.texture.level_count; + create_info.subresourceRange.baseArrayLayer = desc->u.texture.layer_idx; + create_info.subresourceRange.layerCount = desc->u.texture.layer_count; + if ((vr = VK_CALL(vkCreateImageView(device_vk->vk_device, &create_info, NULL, &vk_image_view))) < 0) + { + ERR("Failed to create Vulkan image view, vr %s.\n", wined3d_debug_vkresult(vr)); + return VK_NULL_HANDLE; + } + + return vk_image_view; +} + +static void wined3d_render_target_view_vk_cs_init(void *object) +{ + struct wined3d_rendertarget_view_vk *view_vk = object; + struct wined3d_view_desc *desc = &view_vk->v.desc; + const struct wined3d_format_vk *format_vk; + struct wined3d_texture_vk *texture_vk; + struct wined3d_resource *resource; + struct wined3d_context *context; + uint32_t default_flags = 0; + + TRACE("view_vk %p.\n", view_vk); + + resource = view_vk->v.resource; + if (resource->type == WINED3D_RTYPE_BUFFER) + { + FIXME("Buffer views not implemented.\n"); + return; + } + + texture_vk = wined3d_texture_vk(texture_from_resource(resource)); + format_vk = wined3d_format_vk(view_vk->v.format); + + if (texture_vk->t.layer_count > 1) + default_flags |= WINED3D_VIEW_TEXTURE_ARRAY; + + if (resource->format->id == format_vk->f.id && desc->flags == default_flags + && !desc->u.texture.level_idx && desc->u.texture.level_count == texture_vk->t.level_count + && !desc->u.texture.layer_idx && desc->u.texture.layer_count == texture_vk->t.layer_count + && !is_stencil_view_format(&format_vk->f) && resource->type != WINED3D_RTYPE_TEXTURE_3D + && is_identity_fixup(format_vk->f.color_fixup)) + { + TRACE("Creating identity render target view.\n"); + return; + } + + if (texture_vk->t.swapchain && texture_vk->t.swapchain->state.desc.backbuffer_count > 1) + { + FIXME("Swapchain views not supported.\n"); + return; + } + + context = context_acquire(resource->device, NULL, 0); + view_vk->vk_image_view = wined3d_view_vk_create_texture_view(wined3d_context_vk(context), + desc, texture_vk, format_vk, COLOR_FIXUP_IDENTITY, true); + context_release(context); + + if (!view_vk->vk_image_view) + return; + + TRACE("Created image view 0x%s.\n", wine_dbgstr_longlong(view_vk->vk_image_view)); +} + +HRESULT wined3d_rendertarget_view_vk_init(struct wined3d_rendertarget_view_vk *view_vk, + const struct wined3d_view_desc *desc, struct wined3d_resource *resource, + void *parent, const struct wined3d_parent_ops *parent_ops) +{ + HRESULT hr; + + TRACE("view_vk %p, desc %s, resource %p, parent %p, parent_ops %p.\n", + view_vk, wined3d_debug_view_desc(desc, resource), resource, parent, parent_ops); + + if (FAILED(hr = wined3d_rendertarget_view_init(&view_vk->v, desc, resource, parent, parent_ops))) + return hr; + + wined3d_cs_init_object(resource->device->cs, wined3d_render_target_view_vk_cs_init, view_vk); + + return hr; +} + +HRESULT CDECL wined3d_rendertarget_view_create(const struct wined3d_view_desc *desc, + struct wined3d_resource *resource, void *parent, const struct wined3d_parent_ops *parent_ops, + struct wined3d_rendertarget_view **view) +{ + const struct wined3d_adapter_ops *adapter_ops; + + TRACE("desc %s, resource %p, parent %p, parent_ops %p, view %p.\n", + wined3d_debug_view_desc(desc, resource), resource, parent, parent_ops, view); + + adapter_ops = resource->device->adapter->adapter_ops; + return adapter_ops->adapter_create_rendertarget_view(desc, resource, parent, parent_ops, view); +} + +HRESULT CDECL wined3d_rendertarget_view_create_from_sub_resource(struct wined3d_texture *texture, + unsigned int sub_resource_idx, void *parent, const struct wined3d_parent_ops *parent_ops, + struct wined3d_rendertarget_view **view) +{ + struct wined3d_view_desc desc; + + TRACE("texture %p, sub_resource_idx %u, parent %p, parent_ops %p, view %p.\n", + texture, sub_resource_idx, parent, parent_ops, view); + + desc.format_id = texture->resource.format->id; + desc.flags = 0; + desc.u.texture.level_idx = sub_resource_idx % texture->level_count; + desc.u.texture.level_count = 1; + desc.u.texture.layer_idx = sub_resource_idx / texture->level_count; + desc.u.texture.layer_count = 1; + + return wined3d_rendertarget_view_create(&desc, &texture->resource, parent, parent_ops, view); +} + +ULONG CDECL wined3d_shader_resource_view_incref(struct wined3d_shader_resource_view *view) +{ + ULONG refcount = InterlockedIncrement(&view->refcount); + + TRACE("%p increasing refcount to %u.\n", view, refcount); + + return refcount; +} + +void wined3d_shader_resource_view_cleanup(struct wined3d_shader_resource_view *view) +{ + /* Call wined3d_object_destroyed() before releasing the resource, + * since releasing the resource may end up destroying the parent. */ + view->parent_ops->wined3d_object_destroyed(view->parent); + wined3d_resource_decref(view->resource); +} + +ULONG CDECL wined3d_shader_resource_view_decref(struct wined3d_shader_resource_view *view) +{ + ULONG refcount = InterlockedDecrement(&view->refcount); + + TRACE("%p decreasing refcount to %u.\n", view, refcount); + + if (!refcount) + view->resource->device->adapter->adapter_ops->adapter_destroy_shader_resource_view(view); + + return refcount; +} + +void * CDECL wined3d_shader_resource_view_get_parent(const struct wined3d_shader_resource_view *view) +{ + TRACE("view %p.\n", view); + + return view->parent; +} + +void wined3d_shader_resource_view_gl_update(struct wined3d_shader_resource_view_gl *srv_gl, + struct wined3d_context_gl *context_gl) +{ + create_buffer_view(&srv_gl->gl_view, &context_gl->c, &srv_gl->v.desc, + buffer_from_resource(srv_gl->v.resource), srv_gl->v.format); + srv_gl->bo_user.valid = true; +} + +static void wined3d_shader_resource_view_gl_cs_init(void *object) +{ + struct wined3d_shader_resource_view_gl *view_gl = object; + struct wined3d_resource *resource = view_gl->v.resource; + const struct wined3d_format *view_format; + const struct wined3d_gl_info *gl_info; + const struct wined3d_view_desc *desc; + GLenum view_target; + + TRACE("view_gl %p.\n", view_gl); + + view_format = view_gl->v.format; + gl_info = &resource->device->adapter->gl_info; + desc = &view_gl->v.desc; + + if (resource->type == WINED3D_RTYPE_BUFFER) + { + struct wined3d_buffer *buffer = buffer_from_resource(resource); + struct wined3d_context *context; + + context = context_acquire(resource->device, NULL, 0); + create_buffer_view(&view_gl->gl_view, context, desc, buffer, view_format); + view_gl->bo_user.valid = true; + list_add_head(&wined3d_buffer_gl(buffer)->bo.users, &view_gl->bo_user.entry); + context_release(context); + } + else + { + struct wined3d_texture_gl *texture_gl = wined3d_texture_gl(texture_from_resource(resource)); + GLenum resource_class, view_class; + + resource_class = wined3d_format_gl(resource->format)->view_class; + view_class = wined3d_format_gl(view_format)->view_class; + view_target = get_texture_view_target(gl_info, desc, texture_gl); + + if (resource->format->id == view_format->id && texture_gl->target == view_target + && !desc->u.texture.level_idx && desc->u.texture.level_count == texture_gl->t.level_count + && !desc->u.texture.layer_idx && desc->u.texture.layer_count == texture_gl->t.layer_count + && !is_stencil_view_format(view_format)) + { + TRACE("Creating identity shader resource view.\n"); + } + else if (texture_gl->t.swapchain && texture_gl->t.swapchain->state.desc.backbuffer_count > 1) + { + FIXME("Swapchain shader resource views not supported.\n"); + } + else if (resource->format->typeless_id == view_format->typeless_id + && resource_class == view_class) + { + create_texture_view(&view_gl->gl_view, view_target, desc, texture_gl, view_format); + } + else if (wined3d_format_is_depth_view(resource->format->id, view_format->id)) + { + create_texture_view(&view_gl->gl_view, view_target, desc, texture_gl, resource->format); + } + else + { + FIXME("Shader resource view not supported, resource format %s, view format %s.\n", + debug_d3dformat(resource->format->id), debug_d3dformat(view_format->id)); + } + } +} + +static HRESULT wined3d_shader_resource_view_init(struct wined3d_shader_resource_view *view, + const struct wined3d_view_desc *desc, struct wined3d_resource *resource, + void *parent, const struct wined3d_parent_ops *parent_ops) +{ + view->refcount = 1; + view->parent = parent; + view->parent_ops = parent_ops; + + if (!(resource->bind_flags & WINED3D_BIND_SHADER_RESOURCE)) + return E_INVALIDARG; + if (!(view->format = validate_resource_view(desc, resource, FALSE, FALSE))) + return E_INVALIDARG; + view->desc = *desc; + + wined3d_resource_incref(view->resource = resource); + + return WINED3D_OK; +} + +HRESULT wined3d_shader_resource_view_gl_init(struct wined3d_shader_resource_view_gl *view_gl, + const struct wined3d_view_desc *desc, struct wined3d_resource *resource, + void *parent, const struct wined3d_parent_ops *parent_ops) +{ + HRESULT hr; + + TRACE("view_gl %p, desc %s, resource %p, parent %p, parent_ops %p.\n", + view_gl, wined3d_debug_view_desc(desc, resource), resource, parent, parent_ops); + + if (FAILED(hr = wined3d_shader_resource_view_init(&view_gl->v, desc, resource, parent, parent_ops))) + return hr; + + list_init(&view_gl->bo_user.entry); + wined3d_cs_init_object(resource->device->cs, wined3d_shader_resource_view_gl_cs_init, view_gl); + + return hr; +} + +void wined3d_shader_resource_view_vk_update(struct wined3d_shader_resource_view_vk *srv_vk, + struct wined3d_context_vk *context_vk) +{ + const struct wined3d_format_vk *view_format_vk = wined3d_format_vk(srv_vk->v.format); + const struct wined3d_view_desc *desc = &srv_vk->v.desc; + struct wined3d_resource *resource = srv_vk->v.resource; + struct wined3d_view_vk *view_vk = &srv_vk->view_vk; + struct wined3d_buffer_vk *buffer_vk; + VkBufferView vk_buffer_view; + + buffer_vk = wined3d_buffer_vk(buffer_from_resource(resource)); + wined3d_context_vk_destroy_buffer_view(context_vk, view_vk->u.vk_buffer_view, view_vk->command_buffer_id); + if ((vk_buffer_view = wined3d_view_vk_create_buffer_view(context_vk, desc, buffer_vk, view_format_vk))) + { + view_vk->u.vk_buffer_view = vk_buffer_view; + view_vk->bo_user.valid = true; + } +} + +static void wined3d_shader_resource_view_vk_cs_init(void *object) +{ + struct wined3d_shader_resource_view_vk *srv_vk = object; + struct wined3d_view_desc *desc = &srv_vk->v.desc; + struct wined3d_texture_vk *texture_vk; + const struct wined3d_format *format; + struct wined3d_buffer_vk *buffer_vk; + struct wined3d_resource *resource; + struct wined3d_context *context; + VkBufferView vk_buffer_view; + uint32_t default_flags = 0; + VkImageView vk_image_view; + + TRACE("srv_vk %p.\n", srv_vk); + + resource = srv_vk->v.resource; + format = srv_vk->v.format; + + if (resource->type == WINED3D_RTYPE_BUFFER) + { + buffer_vk = wined3d_buffer_vk(buffer_from_resource(resource)); + + context = context_acquire(resource->device, NULL, 0); + vk_buffer_view = wined3d_view_vk_create_buffer_view(wined3d_context_vk(context), + desc, buffer_vk, wined3d_format_vk(format)); + context_release(context); + + if (!vk_buffer_view) + return; + + TRACE("Created buffer view 0x%s.\n", wine_dbgstr_longlong(vk_buffer_view)); + + srv_vk->view_vk.u.vk_buffer_view = vk_buffer_view; + srv_vk->view_vk.bo_user.valid = true; + list_add_head(&buffer_vk->bo.users, &srv_vk->view_vk.bo_user.entry); + + return; + } + + texture_vk = wined3d_texture_vk(texture_from_resource(resource)); + + if (texture_vk->t.layer_count > 1) + default_flags |= WINED3D_VIEW_TEXTURE_ARRAY; + + if (resource->format->id == format->id && desc->flags == default_flags + && !desc->u.texture.level_idx && desc->u.texture.level_count == texture_vk->t.level_count + && !desc->u.texture.layer_idx && desc->u.texture.layer_count == texture_vk->t.layer_count + && !(resource->bind_flags & WINED3D_BIND_DEPTH_STENCIL)) + { + TRACE("Creating identity shader resource view.\n"); + return; + } + + if (texture_vk->t.swapchain && texture_vk->t.swapchain->state.desc.backbuffer_count > 1) + FIXME("Swapchain shader resource views not supported.\n"); + + context = context_acquire(resource->device, NULL, 0); + vk_image_view = wined3d_view_vk_create_texture_view(wined3d_context_vk(context), + desc, texture_vk, wined3d_format_vk(format), format->color_fixup, false); + context_release(context); + + if (!vk_image_view) + return; + + TRACE("Created image view 0x%s.\n", wine_dbgstr_longlong(vk_image_view)); + + srv_vk->view_vk.u.vk_image_info.imageView = vk_image_view; + srv_vk->view_vk.u.vk_image_info.sampler = VK_NULL_HANDLE; + srv_vk->view_vk.u.vk_image_info.imageLayout = texture_vk->layout; +} + +HRESULT wined3d_shader_resource_view_vk_init(struct wined3d_shader_resource_view_vk *view_vk, + const struct wined3d_view_desc *desc, struct wined3d_resource *resource, + void *parent, const struct wined3d_parent_ops *parent_ops) +{ + HRESULT hr; + + TRACE("view_vk %p, desc %s, resource %p, parent %p, parent_ops %p.\n", + view_vk, wined3d_debug_view_desc(desc, resource), resource, parent, parent_ops); + + if (FAILED(hr = wined3d_shader_resource_view_init(&view_vk->v, desc, resource, parent, parent_ops))) + return hr; + + list_init(&view_vk->view_vk.bo_user.entry); + wined3d_cs_init_object(resource->device->cs, wined3d_shader_resource_view_vk_cs_init, view_vk); + + return hr; +} + +HRESULT CDECL wined3d_shader_resource_view_create(const struct wined3d_view_desc *desc, + struct wined3d_resource *resource, void *parent, const struct wined3d_parent_ops *parent_ops, + struct wined3d_shader_resource_view **view) +{ + const struct wined3d_adapter_ops *adapter_ops; + + TRACE("desc %s, resource %p, parent %p, parent_ops %p, view %p.\n", + wined3d_debug_view_desc(desc, resource), resource, parent, parent_ops, view); + + adapter_ops = resource->device->adapter->adapter_ops; + return adapter_ops->adapter_create_shader_resource_view(desc, resource, parent, parent_ops, view); +} + +void wined3d_shader_resource_view_gl_bind(struct wined3d_shader_resource_view_gl *view_gl, + unsigned int unit, struct wined3d_sampler_gl *sampler_gl, struct wined3d_context_gl *context_gl) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + struct wined3d_texture_gl *texture_gl; + + wined3d_context_gl_active_texture(context_gl, gl_info, unit); + + if (view_gl->gl_view.name) + { + wined3d_context_gl_bind_texture(context_gl, view_gl->gl_view.target, view_gl->gl_view.name); + wined3d_sampler_gl_bind(sampler_gl, unit, NULL, context_gl); + return; + } + + if (view_gl->v.resource->type == WINED3D_RTYPE_BUFFER) + { + FIXME("Buffer shader resources not supported.\n"); + return; + } + + texture_gl = wined3d_texture_gl(wined3d_texture_from_resource(view_gl->v.resource)); + wined3d_texture_gl_bind(texture_gl, context_gl, FALSE); + wined3d_sampler_gl_bind(sampler_gl, unit, texture_gl, context_gl); +} + +/* Context activation is done by the caller. */ +static void shader_resource_view_gl_bind_and_dirtify(struct wined3d_shader_resource_view_gl *view_gl, + struct wined3d_context_gl *context_gl) +{ + if (context_gl->active_texture < ARRAY_SIZE(context_gl->rev_tex_unit_map)) + { + unsigned int active_sampler = context_gl->rev_tex_unit_map[context_gl->active_texture]; + if (active_sampler != WINED3D_UNMAPPED_STAGE) + context_invalidate_state(&context_gl->c, STATE_SAMPLER(active_sampler)); + } + /* FIXME: Ideally we'd only do this when touching a binding that's used by + * a shader. */ + context_invalidate_compute_state(&context_gl->c, STATE_COMPUTE_SHADER_RESOURCE_BINDING); + context_invalidate_state(&context_gl->c, STATE_GRAPHICS_SHADER_RESOURCE_BINDING); + + wined3d_context_gl_bind_texture(context_gl, view_gl->gl_view.target, view_gl->gl_view.name); +} + +void shader_resource_view_generate_mipmaps(struct wined3d_shader_resource_view *view) +{ + struct wined3d_shader_resource_view_gl *view_gl = wined3d_shader_resource_view_gl(view); + unsigned int i, j, layer_count, level_count, base_level, max_level; + const struct wined3d_gl_info *gl_info; + struct wined3d_texture_gl *texture_gl; + struct wined3d_context_gl *context_gl; + struct wined3d_context *context; + struct gl_texture *gl_tex; + DWORD location; + BOOL srgb; + + TRACE("view %p.\n", view); + + context = context_acquire(view_gl->v.resource->device, NULL, 0); + context_gl = wined3d_context_gl(context); + gl_info = context_gl->gl_info; + layer_count = view_gl->v.desc.u.texture.layer_count; + level_count = view_gl->v.desc.u.texture.level_count; + base_level = view_gl->v.desc.u.texture.level_idx; + max_level = base_level + level_count - 1; + + texture_gl = wined3d_texture_gl(texture_from_resource(view_gl->v.resource)); + srgb = !!(texture_gl->t.flags & WINED3D_TEXTURE_IS_SRGB); + location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB; + for (i = 0; i < layer_count; ++i) + wined3d_texture_load_location(&texture_gl->t, i * level_count + base_level, context, location); + + if (view_gl->gl_view.name) + { + shader_resource_view_gl_bind_and_dirtify(view_gl, context_gl); + } + else + { + wined3d_texture_gl_bind_and_dirtify(texture_gl, context_gl, srgb); + gl_info->gl_ops.gl.p_glTexParameteri(texture_gl->target, GL_TEXTURE_BASE_LEVEL, base_level); + gl_info->gl_ops.gl.p_glTexParameteri(texture_gl->target, GL_TEXTURE_MAX_LEVEL, max_level); + } + + if (gl_info->supported[ARB_SAMPLER_OBJECTS]) + GL_EXTCALL(glBindSampler(context_gl->active_texture, 0)); + gl_tex = wined3d_texture_gl_get_gl_texture(texture_gl, srgb); + if (context->d3d_info->wined3d_creation_flags & WINED3D_SRGB_READ_WRITE_CONTROL) + { + gl_info->gl_ops.gl.p_glTexParameteri(texture_gl->target, + GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT); + gl_tex->sampler_desc.srgb_decode = FALSE; + } + + gl_info->fbo_ops.glGenerateMipmap(texture_gl->target); + checkGLcall("glGenerateMipMap()"); + + for (i = 0; i < layer_count; ++i) + { + for (j = base_level + 1; j <= max_level; ++j) + { + wined3d_texture_validate_location(&texture_gl->t, i * level_count + j, location); + wined3d_texture_invalidate_location(&texture_gl->t, i * level_count + j, ~location); + } + } + + if (!view_gl->gl_view.name) + { + gl_tex->base_level = base_level; + gl_info->gl_ops.gl.p_glTexParameteri(texture_gl->target, + GL_TEXTURE_MAX_LEVEL, texture_gl->t.level_count - 1); + } + + context_release(context); +} + +void CDECL wined3d_shader_resource_view_generate_mipmaps(struct wined3d_shader_resource_view *view) +{ + struct wined3d_texture *texture; + + TRACE("view %p.\n", view); + + if (view->resource->type == WINED3D_RTYPE_BUFFER) + { + WARN("Called on buffer resource %p.\n", view->resource); + return; + } + + texture = texture_from_resource(view->resource); + if (!(texture->flags & WINED3D_TEXTURE_GENERATE_MIPMAPS)) + { + WARN("Texture without the WINED3D_TEXTURE_GENERATE_MIPMAPS flag, ignoring.\n"); + return; + } + + wined3d_cs_emit_generate_mipmaps(view->resource->device->cs, view); +} + +ULONG CDECL wined3d_unordered_access_view_incref(struct wined3d_unordered_access_view *view) +{ + ULONG refcount = InterlockedIncrement(&view->refcount); + + TRACE("%p increasing refcount to %u.\n", view, refcount); + + return refcount; +} + +void wined3d_unordered_access_view_cleanup(struct wined3d_unordered_access_view *view) +{ + /* Call wined3d_object_destroyed() before releasing the resource, + * since releasing the resource may end up destroying the parent. */ + view->parent_ops->wined3d_object_destroyed(view->parent); + wined3d_resource_decref(view->resource); +} + +ULONG CDECL wined3d_unordered_access_view_decref(struct wined3d_unordered_access_view *view) +{ + ULONG refcount = InterlockedDecrement(&view->refcount); + + TRACE("%p decreasing refcount to %u.\n", view, refcount); + + if (!refcount) + view->resource->device->adapter->adapter_ops->adapter_destroy_unordered_access_view(view); + + return refcount; +} + +void * CDECL wined3d_unordered_access_view_get_parent(const struct wined3d_unordered_access_view *view) +{ + TRACE("view %p.\n", view); + + return view->parent; +} + +void wined3d_unordered_access_view_invalidate_location(struct wined3d_unordered_access_view *view, + DWORD location) +{ + wined3d_view_invalidate_location(view->resource, &view->desc, location); +} + +void wined3d_unordered_access_view_gl_clear_uint(struct wined3d_unordered_access_view_gl *view_gl, + const struct wined3d_uvec4 *clear_value, struct wined3d_context_gl *context_gl) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + const struct wined3d_format_gl *format; + struct wined3d_buffer_gl *buffer_gl; + struct wined3d_resource *resource; + unsigned int offset, size; + + resource = view_gl->v.resource; + if (resource->type != WINED3D_RTYPE_BUFFER) + { + FIXME("Not implemented for %s resources.\n", debug_d3dresourcetype(resource->type)); + return; + } + + if (!gl_info->supported[ARB_CLEAR_BUFFER_OBJECT]) + { + FIXME("OpenGL implementation does not support ARB_clear_buffer_object.\n"); + return; + } + + format = wined3d_format_gl(view_gl->v.format); + if (format->f.id != WINED3DFMT_R32_UINT && format->f.id != WINED3DFMT_R32_SINT + && format->f.id != WINED3DFMT_R32G32B32A32_UINT + && format->f.id != WINED3DFMT_R32G32B32A32_SINT) + { + FIXME("Not implemented for format %s.\n", debug_d3dformat(format->f.id)); + return; + } + + buffer_gl = wined3d_buffer_gl(buffer_from_resource(resource)); + wined3d_buffer_load_location(&buffer_gl->b, &context_gl->c, WINED3D_LOCATION_BUFFER); + wined3d_unordered_access_view_invalidate_location(&view_gl->v, ~WINED3D_LOCATION_BUFFER); + + get_buffer_view_range(&buffer_gl->b, &view_gl->v.desc, &format->f, &offset, &size); + wined3d_context_gl_bind_bo(context_gl, buffer_gl->bo.binding, buffer_gl->bo.id); + GL_EXTCALL(glClearBufferSubData(buffer_gl->bo.binding, format->internal, + offset, size, format->format, format->type, clear_value)); + wined3d_context_gl_reference_bo(context_gl, &buffer_gl->bo); + checkGLcall("clear unordered access view"); +} + +void wined3d_unordered_access_view_set_counter(struct wined3d_unordered_access_view *view, + unsigned int value) +{ + struct wined3d_bo_address dst, src; + struct wined3d_context *context; + + if (!view->counter_bo) + return; + + context = context_acquire(view->resource->device, NULL, 0); + + src.buffer_object = 0; + src.addr = (void *)&value; + + dst.buffer_object = view->counter_bo; + dst.addr = NULL; + + wined3d_context_copy_bo_address(context, &dst, &src, sizeof(uint32_t)); + + context_release(context); +} + +void wined3d_unordered_access_view_copy_counter(struct wined3d_unordered_access_view *view, + struct wined3d_buffer *buffer, unsigned int offset, struct wined3d_context *context) +{ + struct wined3d_bo_address dst, src; + DWORD dst_location; + + if (!view->counter_bo) + return; + + dst_location = wined3d_buffer_get_memory(buffer, &dst, buffer->locations); + dst.addr += offset; + + src.buffer_object = view->counter_bo; + src.addr = NULL; + + wined3d_context_copy_bo_address(context, &dst, &src, sizeof(uint32_t)); + + wined3d_buffer_invalidate_location(buffer, ~dst_location); +} + +void wined3d_unordered_access_view_gl_update(struct wined3d_unordered_access_view_gl *uav_gl, + struct wined3d_context_gl *context_gl) +{ + create_buffer_view(&uav_gl->gl_view, &context_gl->c, &uav_gl->v.desc, + buffer_from_resource(uav_gl->v.resource), uav_gl->v.format); + uav_gl->bo_user.valid = true; +} + +static void wined3d_unordered_access_view_gl_cs_init(void *object) +{ + struct wined3d_unordered_access_view_gl *view_gl = object; + struct wined3d_resource *resource = view_gl->v.resource; + struct wined3d_view_desc *desc = &view_gl->v.desc; + const struct wined3d_gl_info *gl_info; + + TRACE("view_gl %p.\n", view_gl); + + gl_info = &resource->device->adapter->gl_info; + + if (resource->type == WINED3D_RTYPE_BUFFER) + { + struct wined3d_buffer *buffer = buffer_from_resource(resource); + struct wined3d_context_gl *context_gl; + + context_gl = wined3d_context_gl(context_acquire(resource->device, NULL, 0)); + create_buffer_view(&view_gl->gl_view, &context_gl->c, desc, buffer, view_gl->v.format); + view_gl->bo_user.valid = true; + list_add_head(&wined3d_buffer_gl(buffer)->bo.users, &view_gl->bo_user.entry); + if (desc->flags & (WINED3D_VIEW_BUFFER_COUNTER | WINED3D_VIEW_BUFFER_APPEND)) + { + struct wined3d_bo_gl *bo = &view_gl->counter_bo; + + view_gl->v.counter_bo = (uintptr_t)bo; + wined3d_context_gl_create_bo(context_gl, sizeof(uint32_t), GL_ATOMIC_COUNTER_BUFFER, + GL_STATIC_DRAW, true, GL_MAP_READ_BIT | GL_MAP_WRITE_BIT | GL_CLIENT_STORAGE_BIT, bo); + wined3d_unordered_access_view_set_counter(&view_gl->v, 0); + } + context_release(&context_gl->c); + } + else + { + struct wined3d_texture_gl *texture_gl = wined3d_texture_gl(texture_from_resource(resource)); + unsigned int depth_or_layer_count; + + if (resource->type == WINED3D_RTYPE_TEXTURE_3D) + depth_or_layer_count = wined3d_texture_get_level_depth(&texture_gl->t, desc->u.texture.level_idx); + else + depth_or_layer_count = texture_gl->t.layer_count; + + if (desc->u.texture.layer_idx || desc->u.texture.layer_count != depth_or_layer_count) + { + create_texture_view(&view_gl->gl_view, get_texture_view_target(gl_info, desc, texture_gl), + desc, texture_gl, view_gl->v.format); + } + } +} + +static HRESULT wined3d_unordered_access_view_init(struct wined3d_unordered_access_view *view, + const struct wined3d_view_desc *desc, struct wined3d_resource *resource, + void *parent, const struct wined3d_parent_ops *parent_ops) +{ + view->refcount = 1; + view->parent = parent; + view->parent_ops = parent_ops; + + if (!(resource->bind_flags & WINED3D_BIND_UNORDERED_ACCESS)) + return E_INVALIDARG; + if (!(view->format = validate_resource_view(desc, resource, TRUE, FALSE))) + return E_INVALIDARG; + view->desc = *desc; + + wined3d_resource_incref(view->resource = resource); + + return WINED3D_OK; +} + +HRESULT wined3d_unordered_access_view_gl_init(struct wined3d_unordered_access_view_gl *view_gl, + const struct wined3d_view_desc *desc, struct wined3d_resource *resource, + void *parent, const struct wined3d_parent_ops *parent_ops) +{ + HRESULT hr; + + TRACE("view_gl %p, desc %s, resource %p, parent %p, parent_ops %p.\n", + view_gl, wined3d_debug_view_desc(desc, resource), resource, parent, parent_ops); + + if (FAILED(hr = wined3d_unordered_access_view_init(&view_gl->v, desc, resource, parent, parent_ops))) + return hr; + + list_init(&view_gl->bo_user.entry); + wined3d_cs_init_object(resource->device->cs, wined3d_unordered_access_view_gl_cs_init, view_gl); + + return hr; +} + +void wined3d_unordered_access_view_vk_clear_uint(struct wined3d_unordered_access_view_vk *view_vk, + const struct wined3d_uvec4 *clear_value, struct wined3d_context_vk *context_vk) +{ + const struct wined3d_vk_info *vk_info; + const struct wined3d_format *format; + struct wined3d_buffer_vk *buffer_vk; + struct wined3d_resource *resource; + VkCommandBuffer vk_command_buffer; + VkBufferMemoryBarrier vk_barrier; + VkAccessFlags access_mask; + unsigned int offset, size; + + TRACE("view_vk %p, clear_value %s, context_vk %p.\n", view_vk, debug_uvec4(clear_value), context_vk); + + resource = view_vk->v.resource; + if (resource->type != WINED3D_RTYPE_BUFFER) + { + FIXME("Not implemented for %s resources.\n", debug_d3dresourcetype(resource->type)); + return; + } + + format = view_vk->v.format; + if (format->id != WINED3DFMT_R32_UINT && format->id != WINED3DFMT_R32_SINT) + { + FIXME("Not implemented for format %s.\n", debug_d3dformat(format->id)); + return; + } + + vk_info = context_vk->vk_info; + buffer_vk = wined3d_buffer_vk(buffer_from_resource(resource)); + wined3d_buffer_load_location(&buffer_vk->b, &context_vk->c, WINED3D_LOCATION_BUFFER); + wined3d_buffer_invalidate_location(&buffer_vk->b, ~WINED3D_LOCATION_BUFFER); + + get_buffer_view_range(&buffer_vk->b, &view_vk->v.desc, format, &offset, &size); + + if (!(vk_command_buffer = wined3d_context_vk_get_command_buffer(context_vk))) + return; + wined3d_context_vk_end_current_render_pass(context_vk); + + access_mask = vk_access_mask_from_bind_flags(buffer_vk->b.resource.bind_flags); + vk_barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; + vk_barrier.pNext = NULL; + vk_barrier.srcAccessMask = access_mask; + vk_barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + vk_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + vk_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + vk_barrier.buffer = buffer_vk->bo.vk_buffer; + vk_barrier.offset = buffer_vk->bo.buffer_offset + offset; + vk_barrier.size = size; + VK_CALL(vkCmdPipelineBarrier(vk_command_buffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, NULL, 1, &vk_barrier, 0, NULL)); + + VK_CALL(vkCmdFillBuffer(vk_command_buffer, buffer_vk->bo.vk_buffer, + buffer_vk->bo.buffer_offset + offset, size, clear_value->x)); + + vk_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + vk_barrier.dstAccessMask = access_mask; + VK_CALL(vkCmdPipelineBarrier(vk_command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, NULL, 1, &vk_barrier, 0, NULL)); + + wined3d_context_vk_reference_bo(context_vk, &buffer_vk->bo); +} + +void wined3d_unordered_access_view_vk_update(struct wined3d_unordered_access_view_vk *uav_vk, + struct wined3d_context_vk *context_vk) +{ + const struct wined3d_format_vk *view_format_vk = wined3d_format_vk(uav_vk->v.format); + const struct wined3d_view_desc *desc = &uav_vk->v.desc; + struct wined3d_resource *resource = uav_vk->v.resource; + struct wined3d_view_vk *view_vk = &uav_vk->view_vk; + struct wined3d_buffer_vk *buffer_vk; + VkBufferView vk_buffer_view; + + buffer_vk = wined3d_buffer_vk(buffer_from_resource(resource)); + wined3d_context_vk_destroy_buffer_view(context_vk, view_vk->u.vk_buffer_view, view_vk->command_buffer_id); + if ((vk_buffer_view = wined3d_view_vk_create_buffer_view(context_vk, desc, buffer_vk, view_format_vk))) + { + view_vk->u.vk_buffer_view = vk_buffer_view; + view_vk->bo_user.valid = true; + } +} + +static void wined3d_unordered_access_view_vk_cs_init(void *object) +{ + struct wined3d_unordered_access_view_vk *uav_vk = object; + struct wined3d_view_vk *view_vk = &uav_vk->view_vk; + struct wined3d_view_desc *desc = &uav_vk->v.desc; + const struct wined3d_format_vk *format_vk; + const struct wined3d_vk_info *vk_info; + struct wined3d_texture_vk *texture_vk; + struct wined3d_context_vk *context_vk; + struct wined3d_device_vk *device_vk; + struct wined3d_buffer_vk *buffer_vk; + VkBufferViewCreateInfo create_info; + struct wined3d_resource *resource; + VkBufferView vk_buffer_view; + uint32_t default_flags = 0; + VkImageView vk_image_view; + VkResult vr; + + TRACE("uav_vk %p.\n", uav_vk); + + resource = uav_vk->v.resource; + device_vk = wined3d_device_vk(resource->device); + format_vk = wined3d_format_vk(uav_vk->v.format); + + if (resource->type == WINED3D_RTYPE_BUFFER) + { + buffer_vk = wined3d_buffer_vk(buffer_from_resource(resource)); + + context_vk = wined3d_context_vk(context_acquire(&device_vk->d, NULL, 0)); + vk_info = context_vk->vk_info; + + if ((vk_buffer_view = wined3d_view_vk_create_buffer_view(context_vk, desc, buffer_vk, format_vk))) + { + TRACE("Created buffer view 0x%s.\n", wine_dbgstr_longlong(vk_buffer_view)); + + uav_vk->view_vk.u.vk_buffer_view = vk_buffer_view; + uav_vk->view_vk.bo_user.valid = true; + list_add_head(&buffer_vk->bo.users, &view_vk->bo_user.entry); + } + + if (desc->flags & (WINED3D_VIEW_BUFFER_COUNTER | WINED3D_VIEW_BUFFER_APPEND)) + { + if (!wined3d_context_vk_create_bo(context_vk, sizeof(uint32_t), VK_BUFFER_USAGE_TRANSFER_SRC_BIT + | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &uav_vk->counter_bo)) + { + ERR("Failed to create counter bo.\n"); + context_release(&context_vk->c); + + return; + } + + wined3d_context_vk_end_current_render_pass(context_vk); + VK_CALL(vkCmdFillBuffer(wined3d_context_vk_get_command_buffer(context_vk), + uav_vk->counter_bo.vk_buffer, uav_vk->counter_bo.buffer_offset, sizeof(uint32_t), 0)); + wined3d_context_vk_reference_bo(context_vk, &uav_vk->counter_bo); + + create_info.sType = VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO; + create_info.pNext = NULL; + create_info.flags = 0; + create_info.buffer = uav_vk->counter_bo.vk_buffer; + create_info.format = VK_FORMAT_R32_UINT; + create_info.offset = uav_vk->counter_bo.buffer_offset; + create_info.range = sizeof(uint32_t); + if ((vr = VK_CALL(vkCreateBufferView(device_vk->vk_device, + &create_info, NULL, &uav_vk->vk_counter_view))) < 0) + { + ERR("Failed to create counter buffer view, vr %s.\n", wined3d_debug_vkresult(vr)); + } + else + { + TRACE("Created counter buffer view 0x%s.\n", wine_dbgstr_longlong(uav_vk->vk_counter_view)); + + uav_vk->v.counter_bo = (uintptr_t)&uav_vk->counter_bo; + } + } + + context_release(&context_vk->c); + + return; + } + + texture_vk = wined3d_texture_vk(texture_from_resource(resource)); + + if (texture_vk->t.layer_count > 1) + default_flags |= WINED3D_VIEW_TEXTURE_ARRAY; + + if (resource->format->id == format_vk->f.id && desc->flags == default_flags + && !desc->u.texture.level_idx && desc->u.texture.level_count == texture_vk->t.level_count + && !desc->u.texture.layer_idx && desc->u.texture.layer_count == texture_vk->t.layer_count + && !(resource->bind_flags & WINED3D_BIND_DEPTH_STENCIL) && resource->type != WINED3D_RTYPE_TEXTURE_3D) + { + TRACE("Creating identity unordered access view.\n"); + return; + } + + if (texture_vk->t.swapchain && texture_vk->t.swapchain->state.desc.backbuffer_count > 1) + FIXME("Swapchain unordered access views not supported.\n"); + + context_vk = wined3d_context_vk(context_acquire(&device_vk->d, NULL, 0)); + vk_image_view = wined3d_view_vk_create_texture_view(context_vk, desc, + texture_vk, format_vk, format_vk->f.color_fixup, false); + context_release(&context_vk->c); + + if (!vk_image_view) + return; + + TRACE("Created image view 0x%s.\n", wine_dbgstr_longlong(vk_image_view)); + + view_vk->u.vk_image_info.imageView = vk_image_view; + view_vk->u.vk_image_info.sampler = VK_NULL_HANDLE; + view_vk->u.vk_image_info.imageLayout = texture_vk->layout; +} + +HRESULT wined3d_unordered_access_view_vk_init(struct wined3d_unordered_access_view_vk *view_vk, + const struct wined3d_view_desc *desc, struct wined3d_resource *resource, + void *parent, const struct wined3d_parent_ops *parent_ops) +{ + HRESULT hr; + + TRACE("view_vk %p, desc %s, resource %p, parent %p, parent_ops %p.\n", + view_vk, wined3d_debug_view_desc(desc, resource), resource, parent, parent_ops); + + if (FAILED(hr = wined3d_unordered_access_view_init(&view_vk->v, desc, resource, parent, parent_ops))) + return hr; + + list_init(&view_vk->view_vk.bo_user.entry); + wined3d_cs_init_object(resource->device->cs, wined3d_unordered_access_view_vk_cs_init, view_vk); + + return hr; +} + +HRESULT CDECL wined3d_unordered_access_view_create(const struct wined3d_view_desc *desc, + struct wined3d_resource *resource, void *parent, const struct wined3d_parent_ops *parent_ops, + struct wined3d_unordered_access_view **view) +{ + const struct wined3d_adapter_ops *adapter_ops; + + TRACE("desc %s, resource %p, parent %p, parent_ops %p, view %p.\n", + wined3d_debug_view_desc(desc, resource), resource, parent, parent_ops, view); + + adapter_ops = resource->device->adapter->adapter_ops; + return adapter_ops->adapter_create_unordered_access_view(desc, resource, parent, parent_ops, view); +} diff --git a/wrappers/directx/d3dwine_wrapper/wined3d.spec b/wrappers/directx/d3dwine_wrapper/wined3d.spec new file mode 100644 index 00000000000..44008119a8d --- /dev/null +++ b/wrappers/directx/d3dwine_wrapper/wined3d.spec @@ -0,0 +1,333 @@ +@ stdcall wined3d_mutex_lock() +@ stdcall wined3d_mutex_unlock() + +@ cdecl wined3d_calculate_format_pitch(ptr long long) +@ cdecl wined3d_check_depth_stencil_match(ptr long long long long) +@ cdecl wined3d_check_device_format(ptr ptr long long long long long long) +@ cdecl wined3d_check_device_format_conversion(ptr long long long) +@ cdecl wined3d_check_device_multisample_type(ptr long long long long ptr) +@ cdecl wined3d_check_device_type(ptr ptr long long long long) +@ cdecl wined3d_create(long) +@ cdecl wined3d_decref(ptr) +@ cdecl wined3d_get_adapter(ptr long) +@ cdecl wined3d_get_adapter_count(ptr) +@ cdecl wined3d_get_device_caps(ptr long ptr) +@ cdecl wined3d_incref(ptr) +@ cdecl wined3d_register_software_device(ptr ptr) +@ cdecl wined3d_register_window(ptr ptr ptr long) +@ cdecl wined3d_restore_display_modes(ptr) +@ cdecl wined3d_unregister_windows(ptr) + +@ cdecl wined3d_adapter_get_identifier(ptr long ptr) +@ cdecl wined3d_adapter_get_output(ptr long) +@ cdecl wined3d_adapter_get_output_count(ptr) + +@ cdecl wined3d_blend_state_create(ptr ptr ptr ptr ptr) +@ cdecl wined3d_blend_state_decref(ptr) +@ cdecl wined3d_blend_state_get_parent(ptr) +@ cdecl wined3d_blend_state_incref(ptr) + +@ cdecl wined3d_buffer_create(ptr ptr ptr ptr ptr ptr) +@ cdecl wined3d_buffer_decref(ptr) +@ cdecl wined3d_buffer_get_parent(ptr) +@ cdecl wined3d_buffer_get_resource(ptr) +@ cdecl wined3d_buffer_incref(ptr) + +@ cdecl wined3d_depth_stencil_state_create(ptr ptr ptr ptr ptr) +@ cdecl wined3d_depth_stencil_state_decref(ptr) +@ cdecl wined3d_depth_stencil_state_get_parent(ptr) +@ cdecl wined3d_depth_stencil_state_incref(ptr) + +@ cdecl wined3d_device_acquire_focus_window(ptr ptr) +@ cdecl wined3d_device_apply_stateblock(ptr ptr) +@ cdecl wined3d_device_begin_scene(ptr) +@ cdecl wined3d_device_clear(ptr long ptr long ptr float long) +@ cdecl wined3d_device_clear_rendertarget_view(ptr ptr ptr long ptr float long) +@ cdecl wined3d_device_clear_unordered_access_view_uint(ptr ptr ptr) +@ cdecl wined3d_device_copy_resource(ptr ptr ptr) +@ cdecl wined3d_device_copy_sub_resource_region(ptr ptr long long long long ptr long ptr long) +@ cdecl wined3d_device_copy_uav_counter(ptr ptr long ptr) +@ cdecl wined3d_device_create(ptr ptr long ptr long long ptr long ptr ptr) +@ cdecl wined3d_device_decref(ptr) +@ cdecl wined3d_device_dispatch_compute(ptr long long long) +@ cdecl wined3d_device_dispatch_compute_indirect(ptr ptr long) +@ cdecl wined3d_device_draw_indexed_primitive(ptr long long) +@ cdecl wined3d_device_draw_indexed_primitive_instanced(ptr long long long long) +@ cdecl wined3d_device_draw_indexed_primitive_instanced_indirect(ptr ptr long) +@ cdecl wined3d_device_draw_primitive(ptr long long) +@ cdecl wined3d_device_draw_primitive_instanced(ptr long long long long) +@ cdecl wined3d_device_draw_primitive_instanced_indirect(ptr ptr long) +@ cdecl wined3d_device_end_scene(ptr) +@ cdecl wined3d_device_evict_managed_resources(ptr) +@ cdecl wined3d_device_flush(ptr) +@ cdecl wined3d_device_get_available_texture_mem(ptr) +@ cdecl wined3d_device_get_blend_state(ptr ptr) +@ cdecl wined3d_device_get_clip_status(ptr ptr) +@ cdecl wined3d_device_get_compute_shader(ptr) +@ cdecl wined3d_device_get_constant_buffer(ptr long long) +@ cdecl wined3d_device_get_creation_parameters(ptr ptr) +@ cdecl wined3d_device_get_cs_resource_view(ptr long) +@ cdecl wined3d_device_get_cs_sampler(ptr long) +@ cdecl wined3d_device_get_cs_uav(ptr long) +@ cdecl wined3d_device_get_depth_stencil_state(ptr) +@ cdecl wined3d_device_get_depth_stencil_view(ptr) +@ cdecl wined3d_device_get_device_caps(ptr ptr) +@ cdecl wined3d_device_get_display_mode(ptr long ptr ptr) +@ cdecl wined3d_device_get_domain_shader(ptr) +@ cdecl wined3d_device_get_ds_resource_view(ptr long) +@ cdecl wined3d_device_get_ds_sampler(ptr long) +@ cdecl wined3d_device_get_feature_level(ptr) +@ cdecl wined3d_device_get_gamma_ramp(ptr long ptr) +@ cdecl wined3d_device_get_geometry_shader(ptr) +@ cdecl wined3d_device_get_gs_resource_view(ptr long) +@ cdecl wined3d_device_get_gs_sampler(ptr long) +@ cdecl wined3d_device_get_hs_resource_view(ptr long) +@ cdecl wined3d_device_get_hs_sampler(ptr long) +@ cdecl wined3d_device_get_hull_shader(ptr) +@ cdecl wined3d_device_get_index_buffer(ptr ptr ptr) +@ cdecl wined3d_device_get_max_frame_latency(ptr) +@ cdecl wined3d_device_get_npatch_mode(ptr) +@ cdecl wined3d_device_get_pixel_shader(ptr) +@ cdecl wined3d_device_get_predication(ptr ptr) +@ cdecl wined3d_device_get_primitive_type(ptr ptr ptr) +@ cdecl wined3d_device_get_ps_resource_view(ptr long) +@ cdecl wined3d_device_get_ps_sampler(ptr long) +@ cdecl wined3d_device_get_raster_status(ptr long ptr) +@ cdecl wined3d_device_get_rasterizer_state(ptr) +@ cdecl wined3d_device_get_render_state(ptr long) +@ cdecl wined3d_device_get_rendertarget_view(ptr long) +@ cdecl wined3d_device_get_scissor_rects(ptr ptr ptr) +@ cdecl wined3d_device_get_software_vertex_processing(ptr) +@ cdecl wined3d_device_get_stream_output(ptr long ptr) +@ cdecl wined3d_device_get_stream_source(ptr long ptr ptr ptr) +@ cdecl wined3d_device_get_swapchain(ptr long) +@ cdecl wined3d_device_get_swapchain_count(ptr) +@ cdecl wined3d_device_get_unordered_access_view(ptr long) +@ cdecl wined3d_device_get_vertex_declaration(ptr) +@ cdecl wined3d_device_get_vertex_shader(ptr) +@ cdecl wined3d_device_get_viewports(ptr ptr ptr) +@ cdecl wined3d_device_get_vs_resource_view(ptr long) +@ cdecl wined3d_device_get_vs_sampler(ptr long) +@ cdecl wined3d_device_get_wined3d(ptr) +@ cdecl wined3d_device_incref(ptr) +@ cdecl wined3d_device_process_vertices(ptr long long long ptr ptr long long) +@ cdecl wined3d_device_release_focus_window(ptr) +@ cdecl wined3d_device_reset(ptr ptr ptr ptr long) +@ cdecl wined3d_device_resolve_sub_resource(ptr ptr long ptr long long) +@ cdecl wined3d_device_set_base_vertex_index(ptr long) +@ cdecl wined3d_device_set_blend_state(ptr ptr ptr) +@ cdecl wined3d_device_set_clip_status(ptr ptr) +@ cdecl wined3d_device_set_compute_shader(ptr ptr) +@ cdecl wined3d_device_set_constant_buffer(ptr long long ptr) +@ cdecl wined3d_device_set_cs_resource_view(ptr long ptr) +@ cdecl wined3d_device_set_cs_sampler(ptr long ptr) +@ cdecl wined3d_device_set_cs_uav(ptr long ptr long) +@ cdecl wined3d_device_set_cursor_position(ptr long long long) +@ cdecl wined3d_device_set_cursor_properties(ptr long long ptr long) +@ cdecl wined3d_device_set_depth_stencil_state(ptr ptr) +@ cdecl wined3d_device_set_depth_stencil_view(ptr ptr) +@ cdecl wined3d_device_set_dialog_box_mode(ptr long) +@ cdecl wined3d_device_set_domain_shader(ptr ptr) +@ cdecl wined3d_device_set_ds_resource_view(ptr long ptr) +@ cdecl wined3d_device_set_ds_sampler(ptr long ptr) +@ cdecl wined3d_device_set_gamma_ramp(ptr long long ptr) +@ cdecl wined3d_device_set_geometry_shader(ptr ptr) +@ cdecl wined3d_device_set_gs_resource_view(ptr long ptr) +@ cdecl wined3d_device_set_gs_sampler(ptr long ptr) +@ cdecl wined3d_device_set_hs_resource_view(ptr long ptr) +@ cdecl wined3d_device_set_hs_sampler(ptr long ptr) +@ cdecl wined3d_device_set_hull_shader(ptr ptr) +@ cdecl wined3d_device_set_index_buffer(ptr ptr long long) +@ cdecl wined3d_device_set_max_frame_latency(ptr long) +@ cdecl wined3d_device_set_multithreaded(ptr) +@ cdecl wined3d_device_set_npatch_mode(ptr float) +@ cdecl wined3d_device_set_pixel_shader(ptr ptr) +@ cdecl wined3d_device_set_predication(ptr ptr long) +@ cdecl wined3d_device_set_primitive_type(ptr long long) +@ cdecl wined3d_device_set_ps_resource_view(ptr long ptr) +@ cdecl wined3d_device_set_ps_sampler(ptr long ptr) +@ cdecl wined3d_device_set_rasterizer_state(ptr ptr) +@ cdecl wined3d_device_set_render_state(ptr long long) +@ cdecl wined3d_device_set_rendertarget_view(ptr long ptr long) +@ cdecl wined3d_device_set_scissor_rects(ptr long ptr) +@ cdecl wined3d_device_set_software_vertex_processing(ptr long) +@ cdecl wined3d_device_set_stream_output(ptr long ptr long) +@ cdecl wined3d_device_set_stream_source(ptr long ptr long long) +@ cdecl wined3d_device_set_unordered_access_view(ptr long ptr long) +@ cdecl wined3d_device_set_vertex_declaration(ptr ptr) +@ cdecl wined3d_device_set_vertex_shader(ptr ptr) +@ cdecl wined3d_device_set_viewports(ptr long ptr) +@ cdecl wined3d_device_set_vs_resource_view(ptr long ptr) +@ cdecl wined3d_device_set_vs_sampler(ptr long ptr) +@ cdecl wined3d_device_show_cursor(ptr long) +@ cdecl wined3d_device_update_sub_resource(ptr ptr long ptr ptr long long long) +@ cdecl wined3d_device_update_texture(ptr ptr ptr) +@ cdecl wined3d_device_validate_device(ptr ptr) + +@ cdecl wined3d_output_find_closest_matching_mode(ptr ptr) +@ cdecl wined3d_output_get_adapter(ptr) +@ cdecl wined3d_output_get_desc(ptr ptr) +@ cdecl wined3d_output_get_display_mode(ptr ptr ptr) +@ cdecl wined3d_output_get_mode(ptr long long long ptr) +@ cdecl wined3d_output_get_mode_count(ptr long long) +@ cdecl wined3d_output_get_raster_status(ptr ptr) +@ cdecl wined3d_output_release_ownership(ptr) +@ cdecl wined3d_output_set_display_mode(ptr ptr) +@ cdecl wined3d_output_take_ownership(ptr long) + +@ cdecl wined3d_palette_create(ptr long long ptr ptr) +@ cdecl wined3d_palette_decref(ptr) +@ cdecl wined3d_palette_get_entries(ptr long long long ptr) +@ cdecl wined3d_palette_apply_to_dc(ptr ptr) +@ cdecl wined3d_palette_incref(ptr) +@ cdecl wined3d_palette_set_entries(ptr long long long ptr) + +@ cdecl wined3d_query_create(ptr long ptr ptr ptr) +@ cdecl wined3d_query_decref(ptr) +@ cdecl wined3d_query_get_data(ptr ptr long long) +@ cdecl wined3d_query_get_data_size(ptr) +@ cdecl wined3d_query_get_parent(ptr) +@ cdecl wined3d_query_get_type(ptr) +@ cdecl wined3d_query_incref(ptr) +@ cdecl wined3d_query_issue(ptr long) + +@ cdecl wined3d_rasterizer_state_create(ptr ptr ptr ptr ptr) +@ cdecl wined3d_rasterizer_state_decref(ptr) +@ cdecl wined3d_rasterizer_state_get_parent(ptr) +@ cdecl wined3d_rasterizer_state_incref(ptr) + +@ cdecl wined3d_resource_get_desc(ptr ptr) +@ cdecl wined3d_resource_get_parent(ptr) +@ cdecl wined3d_resource_get_priority(ptr) +@ cdecl wined3d_resource_map(ptr long ptr ptr long) +@ cdecl wined3d_resource_preload(ptr) +@ cdecl wined3d_resource_set_parent(ptr ptr) +@ cdecl wined3d_resource_set_priority(ptr long) +@ cdecl wined3d_resource_unmap(ptr long) + +@ cdecl wined3d_rendertarget_view_create(ptr ptr ptr ptr ptr) +@ cdecl wined3d_rendertarget_view_create_from_sub_resource(ptr long ptr ptr ptr) +@ cdecl wined3d_rendertarget_view_decref(ptr) +@ cdecl wined3d_rendertarget_view_get_parent(ptr) +@ cdecl wined3d_rendertarget_view_get_resource(ptr) +@ cdecl wined3d_rendertarget_view_get_sub_resource_parent(ptr) +@ cdecl wined3d_rendertarget_view_incref(ptr) +@ cdecl wined3d_rendertarget_view_set_parent(ptr ptr) + +@ cdecl wined3d_sampler_create(ptr ptr ptr ptr ptr) +@ cdecl wined3d_sampler_decref(ptr) +@ cdecl wined3d_sampler_get_parent(ptr) +@ cdecl wined3d_sampler_incref(ptr) + +@ cdecl wined3d_shader_create_cs(ptr ptr ptr ptr ptr) +@ cdecl wined3d_shader_create_ds(ptr ptr ptr ptr ptr) +@ cdecl wined3d_shader_create_gs(ptr ptr ptr ptr ptr ptr) +@ cdecl wined3d_shader_create_hs(ptr ptr ptr ptr ptr) +@ cdecl wined3d_shader_create_ps(ptr ptr ptr ptr ptr) +@ cdecl wined3d_shader_create_vs(ptr ptr ptr ptr ptr) +@ cdecl wined3d_shader_decref(ptr) +@ cdecl wined3d_shader_get_byte_code(ptr ptr ptr) +@ cdecl wined3d_shader_get_parent(ptr) +@ cdecl wined3d_shader_incref(ptr) +@ cdecl wined3d_shader_set_local_constants_float(ptr long ptr long) + +@ cdecl wined3d_shader_resource_view_create(ptr ptr ptr ptr ptr) +@ cdecl wined3d_shader_resource_view_decref(ptr) +@ cdecl wined3d_shader_resource_view_generate_mipmaps(ptr) +@ cdecl wined3d_shader_resource_view_get_parent(ptr) +@ cdecl wined3d_shader_resource_view_incref(ptr) + +@ cdecl wined3d_stateblock_apply(ptr ptr) +@ cdecl wined3d_stateblock_capture(ptr ptr) +@ cdecl wined3d_stateblock_create(ptr ptr long ptr) +@ cdecl wined3d_stateblock_decref(ptr) +@ cdecl wined3d_stateblock_get_light(ptr long ptr ptr) +@ cdecl wined3d_stateblock_get_state(ptr) +@ cdecl wined3d_stateblock_incref(ptr) +@ cdecl wined3d_stateblock_init_contained_states(ptr) +@ cdecl wined3d_stateblock_multiply_transform(ptr long ptr) +@ cdecl wined3d_stateblock_reset(ptr) +@ cdecl wined3d_stateblock_set_base_vertex_index(ptr long) +@ cdecl wined3d_stateblock_set_clip_plane(ptr long ptr) +@ cdecl wined3d_stateblock_set_index_buffer(ptr ptr long) +@ cdecl wined3d_stateblock_set_light(ptr long ptr) +@ cdecl wined3d_stateblock_set_light_enable(ptr long long) +@ cdecl wined3d_stateblock_set_material(ptr ptr) +@ cdecl wined3d_stateblock_set_pixel_shader(ptr ptr) +@ cdecl wined3d_stateblock_set_ps_consts_b(ptr long long ptr) +@ cdecl wined3d_stateblock_set_ps_consts_f(ptr long long ptr) +@ cdecl wined3d_stateblock_set_ps_consts_i(ptr long long ptr) +@ cdecl wined3d_stateblock_set_render_state(ptr long long) +@ cdecl wined3d_stateblock_set_sampler_state(ptr long long long) +@ cdecl wined3d_stateblock_set_scissor_rect(ptr ptr) +@ cdecl wined3d_stateblock_set_stream_source(ptr long ptr long long) +@ cdecl wined3d_stateblock_set_stream_source_freq(ptr long long) +@ cdecl wined3d_stateblock_set_texture(ptr long ptr) +@ cdecl wined3d_stateblock_set_texture_stage_state(ptr long long long) +@ cdecl wined3d_stateblock_set_transform(ptr long ptr) +@ cdecl wined3d_stateblock_set_vertex_declaration(ptr ptr) +@ cdecl wined3d_stateblock_set_vertex_shader(ptr ptr) +@ cdecl wined3d_stateblock_set_viewport(ptr ptr) +@ cdecl wined3d_stateblock_set_vs_consts_b(ptr long long ptr) +@ cdecl wined3d_stateblock_set_vs_consts_f(ptr long long ptr) +@ cdecl wined3d_stateblock_set_vs_consts_i(ptr long long ptr) + +@ cdecl wined3d_swapchain_create(ptr ptr ptr ptr ptr ptr) +@ cdecl wined3d_swapchain_decref(ptr) +@ cdecl wined3d_swapchain_get_back_buffer(ptr long) +@ cdecl wined3d_swapchain_get_device(ptr) +@ cdecl wined3d_swapchain_get_display_mode(ptr ptr ptr) +@ cdecl wined3d_swapchain_get_front_buffer_data(ptr ptr long) +@ cdecl wined3d_swapchain_get_gamma_ramp(ptr ptr) +@ cdecl wined3d_swapchain_get_parent(ptr) +@ cdecl wined3d_swapchain_get_desc(ptr ptr) +@ cdecl wined3d_swapchain_get_raster_status(ptr ptr) +@ cdecl wined3d_swapchain_get_state(ptr) +@ cdecl wined3d_swapchain_incref(ptr) +@ cdecl wined3d_swapchain_present(ptr ptr ptr ptr long long) +@ cdecl wined3d_swapchain_resize_buffers(ptr long long long long long long) +@ cdecl wined3d_swapchain_set_gamma_ramp(ptr long ptr) +@ cdecl wined3d_swapchain_set_palette(ptr ptr) +@ cdecl wined3d_swapchain_set_window(ptr ptr) + +@ cdecl wined3d_swapchain_state_create(ptr ptr ptr ptr ptr) +@ cdecl wined3d_swapchain_state_destroy(ptr) +@ cdecl wined3d_swapchain_state_is_windowed(ptr) +@ cdecl wined3d_swapchain_state_resize_target(ptr ptr) +@ cdecl wined3d_swapchain_state_set_fullscreen(ptr ptr ptr) + +@ cdecl wined3d_texture_add_dirty_region(ptr long ptr) +@ cdecl wined3d_texture_blt(ptr long ptr ptr long ptr long ptr long) +@ cdecl wined3d_texture_create(ptr ptr long long long ptr ptr ptr ptr) +@ cdecl wined3d_texture_decref(ptr) +@ cdecl wined3d_texture_from_resource(ptr) +@ cdecl wined3d_texture_get_dc(ptr long ptr) +@ cdecl wined3d_texture_get_level_count(ptr) +@ cdecl wined3d_texture_get_lod(ptr) +@ cdecl wined3d_texture_get_overlay_position(ptr long ptr ptr) +@ cdecl wined3d_texture_get_parent(ptr) +@ cdecl wined3d_texture_get_pitch(ptr long ptr ptr) +@ cdecl wined3d_texture_get_resource(ptr) +@ cdecl wined3d_texture_get_sub_resource_desc(ptr long ptr) +@ cdecl wined3d_texture_get_sub_resource_parent(ptr long) +@ cdecl wined3d_texture_incref(ptr) +@ cdecl wined3d_texture_release_dc(ptr long ptr) +@ cdecl wined3d_texture_set_color_key(ptr long ptr) +@ cdecl wined3d_texture_set_lod(ptr long) +@ cdecl wined3d_texture_set_overlay_position(ptr long long long) +@ cdecl wined3d_texture_set_sub_resource_parent(ptr long ptr) +@ cdecl wined3d_texture_update_desc(ptr long long long long long long ptr long) +@ cdecl wined3d_texture_update_overlay(ptr long ptr ptr long ptr long) + +@ cdecl wined3d_unordered_access_view_create(ptr ptr ptr ptr ptr) +@ cdecl wined3d_unordered_access_view_decref(ptr) +@ cdecl wined3d_unordered_access_view_get_parent(ptr) +@ cdecl wined3d_unordered_access_view_incref(ptr) + +@ cdecl wined3d_vertex_declaration_create(ptr ptr long ptr ptr ptr) +@ cdecl wined3d_vertex_declaration_create_from_fvf(ptr long ptr ptr ptr) +@ cdecl wined3d_vertex_declaration_decref(ptr) +@ cdecl wined3d_vertex_declaration_get_parent(ptr) +@ cdecl wined3d_vertex_declaration_incref(ptr) + +@ cdecl wined3d_extract_shader_input_signature_from_dxbc(ptr ptr long) diff --git a/wrappers/directx/d3dwine_wrapper/wined3d_gl.h b/wrappers/directx/d3dwine_wrapper/wined3d_gl.h new file mode 100644 index 00000000000..2dbc56ab79d --- /dev/null +++ b/wrappers/directx/d3dwine_wrapper/wined3d_gl.h @@ -0,0 +1,229 @@ +/* + * Direct3D wine OpenGL include file + * + * Copyright 2002-2003 The wine-d3d team + * Copyright 2002-2004 Jason Edmeades + * Raphael Junqueira + * Copyright 2007 Roderick Colenbrander + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __WINE_WINED3D_GL_H +#define __WINE_WINED3D_GL_H + +#include "wine/wgl.h" + +#define GL_COMPRESSED_LUMINANCE_ALPHA_3DC_ATI 0x8837 /* not in the gl spec */ + +/* OpenGL extensions. */ +enum wined3d_gl_extension +{ + WINED3D_GL_EXT_NONE, + + /* APPLE */ + APPLE_FENCE, + APPLE_FLOAT_PIXELS, + APPLE_FLUSH_BUFFER_RANGE, + APPLE_FLUSH_RENDER, + APPLE_RGB_422, + APPLE_YCBCR_422, + /* ARB */ + ARB_BASE_INSTANCE, + ARB_BLEND_FUNC_EXTENDED, + ARB_BUFFER_STORAGE, + ARB_CLEAR_BUFFER_OBJECT, + ARB_CLEAR_TEXTURE, + ARB_CLIP_CONTROL, + ARB_COLOR_BUFFER_FLOAT, + ARB_COMPUTE_SHADER, + ARB_CONSERVATIVE_DEPTH, + ARB_COPY_BUFFER, + ARB_COPY_IMAGE, + ARB_CULL_DISTANCE, + ARB_DEBUG_OUTPUT, + ARB_DEPTH_BUFFER_FLOAT, + ARB_DEPTH_CLAMP, + ARB_DEPTH_TEXTURE, + ARB_DERIVATIVE_CONTROL, + ARB_DRAW_BUFFERS, + ARB_DRAW_BUFFERS_BLEND, + ARB_DRAW_ELEMENTS_BASE_VERTEX, + ARB_DRAW_INDIRECT, + ARB_DRAW_INSTANCED, + ARB_ES2_COMPATIBILITY, + ARB_ES3_COMPATIBILITY, + ARB_EXPLICIT_ATTRIB_LOCATION, + ARB_FRAGMENT_COORD_CONVENTIONS, + ARB_FRAGMENT_LAYER_VIEWPORT, + ARB_FRAGMENT_PROGRAM, + ARB_FRAGMENT_SHADER, + ARB_FRAMEBUFFER_NO_ATTACHMENTS, + ARB_FRAMEBUFFER_OBJECT, + ARB_FRAMEBUFFER_SRGB, + ARB_GEOMETRY_SHADER4, + ARB_GPU_SHADER5, + ARB_HALF_FLOAT_PIXEL, + ARB_HALF_FLOAT_VERTEX, + ARB_INSTANCED_ARRAYS, + ARB_INTERNALFORMAT_QUERY, + ARB_INTERNALFORMAT_QUERY2, + ARB_MAP_BUFFER_ALIGNMENT, + ARB_MAP_BUFFER_RANGE, + ARB_MULTISAMPLE, + ARB_MULTITEXTURE, + ARB_OCCLUSION_QUERY, + ARB_PIPELINE_STATISTICS_QUERY, + ARB_PIXEL_BUFFER_OBJECT, + ARB_POINT_PARAMETERS, + ARB_POINT_SPRITE, + ARB_POLYGON_OFFSET_CLAMP, + ARB_PROVOKING_VERTEX, + ARB_QUERY_BUFFER_OBJECT, + ARB_SAMPLE_SHADING, + ARB_SAMPLER_OBJECTS, + ARB_SEAMLESS_CUBE_MAP, + ARB_SHADER_ATOMIC_COUNTERS, + ARB_SHADER_VIEWPORT_LAYER_ARRAY, + ARB_SHADER_BIT_ENCODING, + ARB_SHADER_IMAGE_LOAD_STORE, + ARB_SHADER_IMAGE_SIZE, + ARB_SHADER_STORAGE_BUFFER_OBJECT, + ARB_SHADER_TEXTURE_IMAGE_SAMPLES, + ARB_SHADER_TEXTURE_LOD, + ARB_SHADING_LANGUAGE_100, + ARB_SHADING_LANGUAGE_420PACK, + ARB_SHADING_LANGUAGE_PACKING, + ARB_SHADOW, + ARB_STENCIL_TEXTURING, + ARB_SYNC, + ARB_TESSELLATION_SHADER, + ARB_TEXTURE_BORDER_CLAMP, + ARB_TEXTURE_BUFFER_OBJECT, + ARB_TEXTURE_BUFFER_RANGE, + ARB_TEXTURE_COMPRESSION, + ARB_TEXTURE_COMPRESSION_BPTC, + ARB_TEXTURE_COMPRESSION_RGTC, + ARB_TEXTURE_CUBE_MAP, + ARB_TEXTURE_CUBE_MAP_ARRAY, + ARB_TEXTURE_ENV_COMBINE, + ARB_TEXTURE_ENV_DOT3, + ARB_TEXTURE_FILTER_ANISOTROPIC, + ARB_TEXTURE_FLOAT, + ARB_TEXTURE_GATHER, + ARB_TEXTURE_MIRRORED_REPEAT, + ARB_TEXTURE_MIRROR_CLAMP_TO_EDGE, + ARB_TEXTURE_MULTISAMPLE, + ARB_TEXTURE_NON_POWER_OF_TWO, + ARB_TEXTURE_QUERY_LEVELS, + ARB_TEXTURE_RECTANGLE, + ARB_TEXTURE_RG, + ARB_TEXTURE_RGB10_A2UI, + ARB_TEXTURE_STORAGE, + ARB_TEXTURE_STORAGE_MULTISAMPLE, + ARB_TEXTURE_SWIZZLE, + ARB_TEXTURE_VIEW, + ARB_TIMER_QUERY, + ARB_TRANSFORM_FEEDBACK2, + ARB_TRANSFORM_FEEDBACK3, + ARB_UNIFORM_BUFFER_OBJECT, + ARB_VERTEX_ARRAY_BGRA, + ARB_VERTEX_BUFFER_OBJECT, + ARB_VERTEX_PROGRAM, + ARB_VERTEX_SHADER, + ARB_VERTEX_TYPE_2_10_10_10_REV, + ARB_VIEWPORT_ARRAY, + ARB_TEXTURE_BARRIER, + /* ATI */ + ATI_FRAGMENT_SHADER, + ATI_SEPARATE_STENCIL, + ATI_TEXTURE_COMPRESSION_3DC, + ATI_TEXTURE_ENV_COMBINE3, + ATI_TEXTURE_MIRROR_ONCE, + /* EXT */ + EXT_BLEND_COLOR, + EXT_BLEND_EQUATION_SEPARATE, + EXT_BLEND_FUNC_SEPARATE, + EXT_BLEND_MINMAX, + EXT_BLEND_SUBTRACT, + EXT_DEPTH_BOUNDS_TEST, + EXT_DRAW_BUFFERS2, + EXT_FOG_COORD, + EXT_FRAMEBUFFER_BLIT, + EXT_FRAMEBUFFER_MULTISAMPLE, + EXT_FRAMEBUFFER_MULTISAMPLE_BLIT_SCALED, + EXT_FRAMEBUFFER_OBJECT, + EXT_GPU_PROGRAM_PARAMETERS, + EXT_GPU_SHADER4, + EXT_MEMORY_OBJECT, + EXT_PACKED_DEPTH_STENCIL, + EXT_PACKED_FLOAT, + EXT_POINT_PARAMETERS, + EXT_PROVOKING_VERTEX, + EXT_SECONDARY_COLOR, + EXT_STENCIL_TWO_SIDE, + EXT_STENCIL_WRAP, + EXT_TEXTURE3D, + EXT_TEXTURE_ARRAY, + EXT_TEXTURE_COMPRESSION_RGTC, + EXT_TEXTURE_COMPRESSION_S3TC, + EXT_TEXTURE_ENV_COMBINE, + EXT_TEXTURE_ENV_DOT3, + EXT_TEXTURE_INTEGER, + EXT_TEXTURE_LOD_BIAS, + EXT_TEXTURE_MIRROR_CLAMP, + EXT_TEXTURE_SHADOW_LOD, + EXT_TEXTURE_SHARED_EXPONENT, + EXT_TEXTURE_SNORM, + EXT_TEXTURE_SRGB, + EXT_TEXTURE_SRGB_DECODE, + /* NVIDIA */ + NV_FENCE, + NV_FOG_DISTANCE, + NV_FRAGMENT_PROGRAM, + NV_FRAGMENT_PROGRAM2, + NV_FRAGMENT_PROGRAM_OPTION, + NV_HALF_FLOAT, + NV_LIGHT_MAX_EXPONENT, + NV_POINT_SPRITE, + NV_REGISTER_COMBINERS, + NV_REGISTER_COMBINERS2, + NV_TEXGEN_REFLECTION, + NV_TEXTURE_ENV_COMBINE4, + NV_TEXTURE_SHADER, + NV_TEXTURE_SHADER2, + NV_VERTEX_PROGRAM, + NV_VERTEX_PROGRAM1_1, + NV_VERTEX_PROGRAM2, + NV_VERTEX_PROGRAM2_OPTION, + NV_VERTEX_PROGRAM3, + NV_TEXTURE_BARRIER, + /* WGL extensions */ + WGL_ARB_PIXEL_FORMAT, + WGL_EXT_SWAP_CONTROL, + WGL_WINE_PIXEL_FORMAT_PASSTHROUGH, + WGL_WINE_QUERY_RENDERER, + /* Internally used */ + WINED3D_GL_BLEND_EQUATION, + WINED3D_GL_LEGACY_CONTEXT, + WINED3D_GL_NORMALIZED_TEXRECT, + WINED3D_GL_PRIMITIVE_QUERY, + WINED3D_GL_VERSION_2_0, + WINED3D_GL_VERSION_3_2, + WINED3D_GLSL_130, + + WINED3D_GL_EXT_COUNT, +}; +#endif /* __WINE_WINED3D_GL */ diff --git a/wrappers/directx/d3dwine_wrapper/wined3d_main.c b/wrappers/directx/d3dwine_wrapper/wined3d_main.c new file mode 100644 index 00000000000..293359714c3 --- /dev/null +++ b/wrappers/directx/d3dwine_wrapper/wined3d_main.c @@ -0,0 +1,881 @@ +/* + * Direct3D wine internal interface main + * + * Copyright 2002-2003 The wine-d3d team + * Copyright 2002-2003 Raphael Junqueira + * Copyright 2004 Jason Edmeades + * Copyright 2007-2008 Stefan Dösinger for CodeWeavers + * Copyright 2009 Henri Verbeet for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "config.h" +#include "wine/port.h" + +#include "initguid.h" +#include "wined3d_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(d3d); +WINE_DECLARE_DEBUG_CHANNEL(winediag); + +struct wined3d_wndproc +{ + struct wined3d *wined3d; + HWND window; + BOOL unicode; + BOOL filter; + WNDPROC proc; + struct wined3d_device *device; + uint32_t flags; +}; + +struct wined3d_wndproc_table +{ + struct wined3d_wndproc *entries; + SIZE_T count; + SIZE_T size; +}; + +struct wined3d_window_hook +{ + HHOOK hook; + DWORD thread_id; + unsigned int count; +}; + +struct wined3d_registered_swapchain_state +{ + struct wined3d_swapchain_state *state; + DWORD thread_id; +}; + +struct wined3d_swapchain_state_table +{ + struct wined3d_window_hook *hooks; + SIZE_T hooks_size; + SIZE_T hook_count; + + struct wined3d_registered_swapchain_state *states; + SIZE_T states_size; + SIZE_T state_count; +}; + +static struct wined3d_wndproc_table wndproc_table; +static struct wined3d_swapchain_state_table swapchain_state_table; + +static CRITICAL_SECTION wined3d_cs; +static CRITICAL_SECTION_DEBUG wined3d_cs_debug = +{ + 0, 0, &wined3d_cs, + {&wined3d_cs_debug.ProcessLocksList, + &wined3d_cs_debug.ProcessLocksList}, + 0, 0, {(DWORD_PTR)(__FILE__ ": wined3d_cs")} +}; +static CRITICAL_SECTION wined3d_cs = {&wined3d_cs_debug, -1, 0, 0, 0, 0}; + +static CRITICAL_SECTION wined3d_wndproc_cs; +static CRITICAL_SECTION_DEBUG wined3d_wndproc_cs_debug = +{ + 0, 0, &wined3d_wndproc_cs, + {&wined3d_wndproc_cs_debug.ProcessLocksList, + &wined3d_wndproc_cs_debug.ProcessLocksList}, + 0, 0, {(DWORD_PTR)(__FILE__ ": wined3d_wndproc_cs")} +}; +static CRITICAL_SECTION wined3d_wndproc_cs = {&wined3d_wndproc_cs_debug, -1, 0, 0, 0, 0}; + +CRITICAL_SECTION wined3d_command_cs; +static CRITICAL_SECTION_DEBUG wined3d_command_cs_debug = +{ + 0, 0, &wined3d_command_cs, + {&wined3d_command_cs_debug.ProcessLocksList, + &wined3d_command_cs_debug.ProcessLocksList}, + 0, 0, {(DWORD_PTR)(__FILE__ ": wined3d_command_cs")} +}; +CRITICAL_SECTION wined3d_command_cs = {&wined3d_command_cs_debug, -1, 0, 0, 0, 0}; + +/* When updating default value here, make sure to update winecfg as well, + * where appropriate. */ +struct wined3d_settings wined3d_settings = +{ + WINED3D_CSMT_ENABLE, /* Multithreaded CS by default. */ + MAKEDWORD_VERSION(4, 4), /* Default to OpenGL 4.4 */ + ORM_FBO, /* Use FBOs to do offscreen rendering */ + PCI_VENDOR_NONE,/* PCI Vendor ID */ + PCI_DEVICE_NONE,/* PCI Device ID */ + 0, /* The default of memory is set in init_driver_info */ + NULL, /* No wine logo by default */ + TRUE, /* Prefer multisample textures to multisample renderbuffers. */ + ~0u, /* Don't force a specific sample count by default. */ + FALSE, /* Don't range check relative addressing indices in float constants. */ + FALSE, /* No strict shader math by default. */ + ~0U, /* No VS shader model limit by default. */ + ~0U, /* No HS shader model limit by default. */ + ~0U, /* No DS shader model limit by default. */ + ~0U, /* No GS shader model limit by default. */ + ~0U, /* No PS shader model limit by default. */ + ~0u, /* No CS shader model limit by default. */ + WINED3D_RENDERER_AUTO, + WINED3D_SHADER_BACKEND_AUTO, +}; + +struct wined3d * CDECL wined3d_create(DWORD flags) +{ + struct wined3d *object; + HRESULT hr; + + if (!(object = heap_alloc_zero(FIELD_OFFSET(struct wined3d, adapters[1])))) + { + ERR("Failed to allocate wined3d object memory.\n"); + return NULL; + } + + if (wined3d_settings.renderer == WINED3D_RENDERER_NO3D) + flags |= WINED3D_NO3D; + + if (FAILED(hr = wined3d_init(object, flags))) + { + WARN("Failed to initialize wined3d object, hr %#x.\n", hr); + heap_free(object); + return NULL; + } + + TRACE("Created wined3d object %p.\n", object); + + return object; +} + +static DWORD get_config_key(HKEY defkey, HKEY appkey, const char *name, char *buffer, DWORD size) +{ + if (appkey && !RegQueryValueExA(appkey, name, 0, NULL, (BYTE *)buffer, &size)) return 0; + if (defkey && !RegQueryValueExA(defkey, name, 0, NULL, (BYTE *)buffer, &size)) return 0; + return ERROR_FILE_NOT_FOUND; +} + +static DWORD get_config_key_dword(HKEY defkey, HKEY appkey, const char *name, DWORD *value) +{ + DWORD type, data, size; + + size = sizeof(data); + if (appkey && !RegQueryValueExA(appkey, name, 0, &type, (BYTE *)&data, &size) && type == REG_DWORD) goto success; + size = sizeof(data); + if (defkey && !RegQueryValueExA(defkey, name, 0, &type, (BYTE *)&data, &size) && type == REG_DWORD) goto success; + + return ERROR_FILE_NOT_FOUND; + +success: + *value = data; + return 0; +} + +BOOL wined3d_get_app_name(char *app_name, unsigned int app_name_size) +{ + char buffer[MAX_PATH]; + unsigned int len; + char *p, *name; + + len = GetModuleFileNameA(0, buffer, ARRAY_SIZE(buffer)); + if (!(len && len < MAX_PATH)) + return FALSE; + + name = buffer; + if ((p = strrchr(name, '/' ))) + name = p + 1; + if ((p = strrchr(name, '\\'))) + name = p + 1; + + len = strlen(name) + 1; + if (app_name_size < len) + return FALSE; + + memcpy(app_name, name, len); + return TRUE; +} + +static BOOL wined3d_dll_init(HINSTANCE hInstDLL) +{ + DWORD wined3d_context_tls_idx; + char buffer[MAX_PATH+10]; + DWORD size = sizeof(buffer); + HKEY hkey = 0; + HKEY appkey = 0; + DWORD tmpvalue; + WNDCLASSA wc; + + wined3d_context_tls_idx = TlsAlloc(); + if (wined3d_context_tls_idx == TLS_OUT_OF_INDEXES) + { + DWORD err = GetLastError(); + ERR("Failed to allocate context TLS index, err %#x.\n", err); + return FALSE; + } + context_set_tls_idx(wined3d_context_tls_idx); + + /* We need our own window class for a fake window which we use to retrieve GL capabilities */ + /* We might need CS_OWNDC in the future if we notice strange things on Windows. + * Various articles/posts about OpenGL problems on Windows recommend this. */ + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = DefWindowProcA; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = hInstDLL; + wc.hIcon = LoadIconA(NULL, (const char *)IDI_WINLOGO); + wc.hCursor = LoadCursorA(NULL, (const char *)IDC_ARROW); + wc.hbrBackground = NULL; + wc.lpszMenuName = NULL; + wc.lpszClassName = WINED3D_OPENGL_WINDOW_CLASS_NAME; + + if (!RegisterClassA(&wc)) + { + ERR("Failed to register window class 'WineD3D_OpenGL'!\n"); + if (!TlsFree(wined3d_context_tls_idx)) + { + DWORD err = GetLastError(); + ERR("Failed to free context TLS index, err %#x.\n", err); + } + return FALSE; + } + + DisableThreadLibraryCalls(hInstDLL); + + /* @@ Wine registry key: HKCU\Software\Wine\Direct3D */ + if ( RegOpenKeyA( HKEY_CURRENT_USER, "Software\\Wine\\Direct3D", &hkey ) ) hkey = 0; + + if (wined3d_get_app_name(buffer, ARRAY_SIZE(buffer))) + { + HKEY tmpkey; + /* @@ Wine registry key: HKCU\Software\Wine\AppDefaults\app.exe\Direct3D */ + if (!RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\AppDefaults", &tmpkey)) + { + strcat(buffer, "\\Direct3D"); + TRACE("Application name %s.\n", buffer); + if (RegOpenKeyA(tmpkey, buffer, &appkey)) appkey = 0; + RegCloseKey(tmpkey); + } + } + + if (hkey || appkey) + { + if (!get_config_key_dword(hkey, appkey, "csmt", &wined3d_settings.cs_multithreaded)) + ERR_(winediag)("Setting multithreaded command stream to %#x.\n", wined3d_settings.cs_multithreaded); + if (!get_config_key_dword(hkey, appkey, "MaxVersionGL", &tmpvalue)) + { + ERR_(winediag)("Setting maximum allowed wined3d GL version to %u.%u.\n", + tmpvalue >> 16, tmpvalue & 0xffff); + wined3d_settings.max_gl_version = tmpvalue; + } + if (!get_config_key(hkey, appkey, "shader_backend", buffer, size)) + { + if (!_strnicmp(buffer, "glsl", -1)) + { + ERR_(winediag)("Using the GLSL shader backend.\n"); + wined3d_settings.shader_backend = WINED3D_SHADER_BACKEND_GLSL; + } + else if (!_strnicmp(buffer, "arb", -1)) + { + ERR_(winediag)("Using the ARB shader backend.\n"); + wined3d_settings.shader_backend = WINED3D_SHADER_BACKEND_ARB; + } + else if (!_strnicmp(buffer, "none", -1)) + { + ERR_(winediag)("Disabling shader backends.\n"); + wined3d_settings.shader_backend = WINED3D_SHADER_BACKEND_NONE; + } + } + if (wined3d_settings.shader_backend == WINED3D_SHADER_BACKEND_ARB + || wined3d_settings.shader_backend == WINED3D_SHADER_BACKEND_NONE) + { + ERR_(winediag)("The GLSL shader backend has been disabled. You get to keep all the pieces if it breaks.\n"); + TRACE("Use of GL Shading Language disabled.\n"); + } + if (!get_config_key(hkey, appkey, "OffscreenRenderingMode", buffer, size) + && !strcmp(buffer,"backbuffer")) + wined3d_settings.offscreen_rendering_mode = ORM_BACKBUFFER; + if ( !get_config_key_dword( hkey, appkey, "VideoPciDeviceID", &tmpvalue) ) + { + int pci_device_id = tmpvalue; + + /* A pci device id is 16-bit */ + if(pci_device_id > 0xffff) + { + ERR("Invalid value for VideoPciDeviceID. The value should be smaller or equal to 65535 or 0xffff\n"); + } + else + { + TRACE("Using PCI Device ID %04x\n", pci_device_id); + wined3d_settings.pci_device_id = pci_device_id; + } + } + if ( !get_config_key_dword( hkey, appkey, "VideoPciVendorID", &tmpvalue) ) + { + int pci_vendor_id = tmpvalue; + + /* A pci device id is 16-bit */ + if(pci_vendor_id > 0xffff) + { + ERR("Invalid value for VideoPciVendorID. The value should be smaller or equal to 65535 or 0xffff\n"); + } + else + { + TRACE("Using PCI Vendor ID %04x\n", pci_vendor_id); + wined3d_settings.pci_vendor_id = pci_vendor_id; + } + } + if ( !get_config_key( hkey, appkey, "VideoMemorySize", buffer, size) ) + { + int TmpVideoMemorySize = atoi(buffer); + if(TmpVideoMemorySize > 0) + { + wined3d_settings.emulated_textureram = (UINT64)TmpVideoMemorySize *1024*1024; + TRACE("Use %iMiB = 0x%s bytes for emulated_textureram\n", + TmpVideoMemorySize, + wine_dbgstr_longlong(wined3d_settings.emulated_textureram)); + } + else + ERR("VideoMemorySize is %i but must be >0\n", TmpVideoMemorySize); + } + if ( !get_config_key( hkey, appkey, "WineLogo", buffer, size) ) + { + size_t len = strlen(buffer) + 1; + + if (!(wined3d_settings.logo = heap_alloc(len))) + ERR("Failed to allocate logo path memory.\n"); + else + memcpy(wined3d_settings.logo, buffer, len); + } + if (!get_config_key_dword(hkey, appkey, "MultisampleTextures", &wined3d_settings.multisample_textures)) + ERR_(winediag)("Setting multisample textures to %#x.\n", wined3d_settings.multisample_textures); + if (!get_config_key_dword(hkey, appkey, "SampleCount", &wined3d_settings.sample_count)) + ERR_(winediag)("Forcing sample count to %u. This may not be compatible with all applications.\n", + wined3d_settings.sample_count); + if (!get_config_key(hkey, appkey, "CheckFloatConstants", buffer, size) + && !strcmp(buffer, "enabled")) + { + TRACE("Checking relative addressing indices in float constants.\n"); + wined3d_settings.check_float_constants = TRUE; + } + if (!get_config_key_dword(hkey, appkey, "strict_shader_math", &wined3d_settings.strict_shader_math)) + ERR_(winediag)("Setting strict shader math to %#x.\n", wined3d_settings.strict_shader_math); + if (!get_config_key_dword(hkey, appkey, "MaxShaderModelVS", &wined3d_settings.max_sm_vs)) + TRACE("Limiting VS shader model to %u.\n", wined3d_settings.max_sm_vs); + if (!get_config_key_dword(hkey, appkey, "MaxShaderModelHS", &wined3d_settings.max_sm_hs)) + TRACE("Limiting HS shader model to %u.\n", wined3d_settings.max_sm_hs); + if (!get_config_key_dword(hkey, appkey, "MaxShaderModelDS", &wined3d_settings.max_sm_ds)) + TRACE("Limiting DS shader model to %u.\n", wined3d_settings.max_sm_ds); + if (!get_config_key_dword(hkey, appkey, "MaxShaderModelGS", &wined3d_settings.max_sm_gs)) + TRACE("Limiting GS shader model to %u.\n", wined3d_settings.max_sm_gs); + if (!get_config_key_dword(hkey, appkey, "MaxShaderModelPS", &wined3d_settings.max_sm_ps)) + TRACE("Limiting PS shader model to %u.\n", wined3d_settings.max_sm_ps); + if (!get_config_key_dword(hkey, appkey, "MaxShaderModelCS", &wined3d_settings.max_sm_cs)) + TRACE("Limiting CS shader model to %u.\n", wined3d_settings.max_sm_cs); + if (!get_config_key(hkey, appkey, "renderer", buffer, size)) + { + if (!strcmp(buffer, "vulkan")) + { + ERR_(winediag)("Using the Vulkan renderer.\n"); + wined3d_settings.renderer = WINED3D_RENDERER_VULKAN; + } + else if (!strcmp(buffer, "gl")) + { + ERR_(winediag)("Using the OpenGL renderer.\n"); + wined3d_settings.renderer = WINED3D_RENDERER_OPENGL; + } + else if (!strcmp(buffer, "gdi") || !strcmp(buffer, "no3d")) + { + ERR_(winediag)("Disabling 3D support.\n"); + wined3d_settings.renderer = WINED3D_RENDERER_NO3D; + } + } + } + + if (appkey) RegCloseKey( appkey ); + if (hkey) RegCloseKey( hkey ); + + return TRUE; +} + +static BOOL wined3d_dll_destroy(HINSTANCE hInstDLL) +{ + DWORD wined3d_context_tls_idx = context_get_tls_idx(); + unsigned int i; + + wined3d_spirv_shader_backend_cleanup(); + + if (!TlsFree(wined3d_context_tls_idx)) + { + DWORD err = GetLastError(); + ERR("Failed to free context TLS index, err %#x.\n", err); + } + + for (i = 0; i < wndproc_table.count; ++i) + { + /* Trying to unregister these would be futile. These entries can only + * exist if either we skipped them in wined3d_unregister_window() due + * to the application replacing the wndproc after the entry was + * registered, or if the application still has an active wined3d + * device. In the latter case the application has bigger problems than + * these entries. */ + WARN("Leftover wndproc table entry %p.\n", &wndproc_table.entries[i]); + } + heap_free(wndproc_table.entries); + + heap_free(swapchain_state_table.states); + for (i = 0; i < swapchain_state_table.hook_count; ++i) + { + WARN("Leftover swapchain state hook %p.\n", &swapchain_state_table.hooks[i]); + UnhookWindowsHookEx(swapchain_state_table.hooks[i].hook); + } + heap_free(swapchain_state_table.hooks); + + heap_free(wined3d_settings.logo); + UnregisterClassA(WINED3D_OPENGL_WINDOW_CLASS_NAME, hInstDLL); + + DeleteCriticalSection(&wined3d_command_cs); + + DeleteCriticalSection(&wined3d_wndproc_cs); + DeleteCriticalSection(&wined3d_cs); + return TRUE; +} + +void WINAPI wined3d_mutex_lock(void) +{ + EnterCriticalSection(&wined3d_cs); +} + +void WINAPI wined3d_mutex_unlock(void) +{ + LeaveCriticalSection(&wined3d_cs); +} + +static void wined3d_wndproc_mutex_lock(void) +{ + EnterCriticalSection(&wined3d_wndproc_cs); +} + +static void wined3d_wndproc_mutex_unlock(void) +{ + LeaveCriticalSection(&wined3d_wndproc_cs); +} + +static struct wined3d_output * wined3d_get_output_from_window(const struct wined3d *wined3d, + HWND hwnd) +{ + unsigned int adapter_idx, output_idx; + struct wined3d_adapter *adapter; + MONITORINFOEXW monitor_info; + HMONITOR monitor; + + TRACE("wined3d %p, hwnd %p.\n", wined3d, hwnd); + + monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); + monitor_info.cbSize = sizeof(monitor_info); + if (!GetMonitorInfoW(monitor, (MONITORINFO *)&monitor_info)) + { + ERR("GetMonitorInfoW failed, error %#x.\n", GetLastError()); + return NULL; + } + + for (adapter_idx = 0; adapter_idx < wined3d->adapter_count; ++adapter_idx) + { + adapter = wined3d->adapters[adapter_idx]; + for (output_idx = 0; output_idx < adapter->output_count; ++output_idx) + { + if (!lstrcmpiW(adapter->outputs[output_idx].device_name, monitor_info.szDevice)) + return &adapter->outputs[output_idx]; + } + } + + return NULL; +} + +static struct wined3d_wndproc *wined3d_find_wndproc(HWND window, struct wined3d *wined3d) +{ + unsigned int i; + + for (i = 0; i < wndproc_table.count; ++i) + { + struct wined3d_wndproc *entry = &wndproc_table.entries[i]; + + if (entry->window == window && entry->wined3d == wined3d) + return entry; + } + + return NULL; +} + +BOOL wined3d_filter_messages(HWND window, BOOL filter) +{ + struct wined3d_wndproc *entry; + BOOL ret; + + wined3d_wndproc_mutex_lock(); + + if (!(entry = wined3d_find_wndproc(window, NULL))) + { + wined3d_wndproc_mutex_unlock(); + return FALSE; + } + + ret = entry->filter; + entry->filter = filter; + + wined3d_wndproc_mutex_unlock(); + + return ret; +} + +static LRESULT CALLBACK wined3d_wndproc(HWND window, UINT message, WPARAM wparam, LPARAM lparam) +{ + struct wined3d_wndproc *entry; + struct wined3d_device *device; + BOOL unicode, filter; + WNDPROC proc; + + wined3d_wndproc_mutex_lock(); + + if (!(entry = wined3d_find_wndproc(window, NULL))) + { + wined3d_wndproc_mutex_unlock(); + ERR("Window %p is not registered with wined3d.\n", window); + return DefWindowProcW(window, message, wparam, lparam); + } + + device = entry->device; + unicode = entry->unicode; + filter = entry->filter; + proc = entry->proc; + wined3d_wndproc_mutex_unlock(); + + if (device) + { + if (filter && message != WM_DISPLAYCHANGE) + { + TRACE("Filtering message: window %p, message %#x, wparam %#lx, lparam %#lx.\n", + window, message, wparam, lparam); + + if (unicode) + return DefWindowProcW(window, message, wparam, lparam); + return DefWindowProcA(window, message, wparam, lparam); + } + + return device_process_message(device, window, unicode, message, wparam, lparam, proc); + } + if (unicode) + return CallWindowProcW(proc, window, message, wparam, lparam); + return CallWindowProcA(proc, window, message, wparam, lparam); +} + +static LRESULT CALLBACK wined3d_hook_proc(int code, WPARAM wparam, LPARAM lparam) +{ + struct wined3d_swapchain_desc swapchain_desc; + struct wined3d_swapchain_state *state; + struct wined3d_wndproc *entry; + struct wined3d_output *output; + MSG *msg = (MSG *)lparam; + unsigned int i; + + /* Handle Alt+Enter. */ + if (code == HC_ACTION && msg->message == WM_SYSKEYDOWN + && msg->wParam == VK_RETURN && (msg->lParam & (KF_ALTDOWN << 16))) + { + wined3d_wndproc_mutex_lock(); + + for (i = 0; i < swapchain_state_table.state_count; ++i) + { + state = swapchain_state_table.states[i].state; + + if (state->device_window != msg->hwnd) + continue; + + if ((entry = wined3d_find_wndproc(msg->hwnd, state->wined3d)) + && (entry->flags & (WINED3D_REGISTER_WINDOW_NO_WINDOW_CHANGES + | WINED3D_REGISTER_WINDOW_NO_ALT_ENTER))) + continue; + + swapchain_desc = state->desc; + swapchain_desc.windowed = !swapchain_desc.windowed; + if (!(output = wined3d_get_output_from_window(state->wined3d, state->device_window))) + { + ERR("Failed to get output from window %p.\n", state->device_window); + break; + } + swapchain_desc.output = output; + wined3d_swapchain_state_set_fullscreen(state, &swapchain_desc, NULL); + + wined3d_wndproc_mutex_unlock(); + + return 1; + } + + wined3d_wndproc_mutex_unlock(); + } + + return CallNextHookEx(0, code, wparam, lparam); +} + +BOOL CDECL wined3d_register_window(struct wined3d *wined3d, HWND window, + struct wined3d_device *device, unsigned int flags) +{ + struct wined3d_wndproc *entry; + + TRACE("wined3d %p, window %p, device %p, flags %#x.\n", wined3d, window, device, flags); + + wined3d_wndproc_mutex_lock(); + + if ((entry = wined3d_find_wndproc(window, wined3d))) + { + if (!wined3d) + WARN("Window %p is already registered with wined3d.\n", window); + entry->flags = flags; + wined3d_wndproc_mutex_unlock(); + return TRUE; + } + + if (!wined3d_array_reserve((void **)&wndproc_table.entries, &wndproc_table.size, + wndproc_table.count + 1, sizeof(*entry))) + { + wined3d_wndproc_mutex_unlock(); + ERR("Failed to grow table.\n"); + return FALSE; + } + + entry = &wndproc_table.entries[wndproc_table.count++]; + entry->window = window; + entry->unicode = IsWindowUnicode(window); + if (!wined3d) + { + /* Set a window proc that matches the window. Some applications (e.g. + * NoX) replace the window proc after we've set ours, and expect to be + * able to call the previous one (ours) directly, without using + * CallWindowProc(). */ + if (entry->unicode) + entry->proc = (WNDPROC)SetWindowLongPtrW(window, GWLP_WNDPROC, (LONG_PTR)wined3d_wndproc); + else + entry->proc = (WNDPROC)SetWindowLongPtrA(window, GWLP_WNDPROC, (LONG_PTR)wined3d_wndproc); + } + else + { + entry->proc = NULL; + } + entry->device = device; + entry->wined3d = wined3d; + entry->flags = flags; + + wined3d_wndproc_mutex_unlock(); + + return TRUE; +} + +static BOOL restore_wndproc(struct wined3d_wndproc *entry) +{ + LONG_PTR proc; + + if (entry->unicode) + { + proc = GetWindowLongPtrW(entry->window, GWLP_WNDPROC); + if (proc != (LONG_PTR)wined3d_wndproc) + return FALSE; + SetWindowLongPtrW(entry->window, GWLP_WNDPROC, (LONG_PTR)entry->proc); + } + else + { + proc = GetWindowLongPtrA(entry->window, GWLP_WNDPROC); + if (proc != (LONG_PTR)wined3d_wndproc) + return FALSE; + SetWindowLongPtrA(entry->window, GWLP_WNDPROC, (LONG_PTR)entry->proc); + } + + return TRUE; +} + +void wined3d_unregister_window(HWND window) +{ + struct wined3d_wndproc *entry, *last; + + wined3d_wndproc_mutex_lock(); + + if (!(entry = wined3d_find_wndproc(window, NULL))) + { + wined3d_wndproc_mutex_unlock(); + ERR("Window %p is not registered with wined3d.\n", window); + return; + } + + if (entry->proc && !restore_wndproc(entry)) + { + entry->device = NULL; + WARN("Not unregistering window %p, current window proc doesn't match wined3d window proc.\n", window); + wined3d_wndproc_mutex_unlock(); + return; + } + + last = &wndproc_table.entries[--wndproc_table.count]; + if (entry != last) *entry = *last; + + wined3d_wndproc_mutex_unlock(); +} + +void CDECL wined3d_unregister_windows(struct wined3d *wined3d) +{ + struct wined3d_wndproc *entry, *last; + unsigned int i = 0; + + TRACE("wined3d %p.\n", wined3d); + + wined3d_wndproc_mutex_lock(); + + while (i < wndproc_table.count) + { + entry = &wndproc_table.entries[i]; + + if (entry->wined3d != wined3d) + { + ++i; + continue; + } + + if (entry->proc && !restore_wndproc(entry)) + { + entry->device = NULL; + WARN("Not unregistering window %p, current window proc doesn't match wined3d window proc.\n", + entry->window); + ++i; + continue; + } + + last = &wndproc_table.entries[--wndproc_table.count]; + if (entry != last) + *entry = *last; + else + ++i; + } + + wined3d_wndproc_mutex_unlock(); +} + +static struct wined3d_window_hook *wined3d_find_hook(DWORD thread_id) +{ + unsigned int i; + + for (i = 0; i < swapchain_state_table.hook_count; ++i) + { + if (swapchain_state_table.hooks[i].thread_id == thread_id) + return &swapchain_state_table.hooks[i]; + } + + return NULL; +} + +void wined3d_swapchain_state_register(struct wined3d_swapchain_state *state) +{ + struct wined3d_registered_swapchain_state *state_entry; + struct wined3d_window_hook *hook; + + wined3d_wndproc_mutex_lock(); + + if (!wined3d_array_reserve((void **)&swapchain_state_table.states, &swapchain_state_table.states_size, + swapchain_state_table.state_count + 1, sizeof(*state_entry))) + { + wined3d_wndproc_mutex_unlock(); + return; + } + + state_entry = &swapchain_state_table.states[swapchain_state_table.state_count++]; + state_entry->state = state; + state_entry->thread_id = GetWindowThreadProcessId(state->device_window, NULL); + + if ((hook = wined3d_find_hook(state_entry->thread_id))) + { + ++hook->count; + wined3d_wndproc_mutex_unlock(); + return; + } + + if (!wined3d_array_reserve((void **)&swapchain_state_table.hooks, &swapchain_state_table.hooks_size, + swapchain_state_table.hook_count + 1, sizeof(*hook))) + { + --swapchain_state_table.state_count; + wined3d_wndproc_mutex_unlock(); + return; + } + + hook = &swapchain_state_table.hooks[swapchain_state_table.hook_count++]; + hook->thread_id = state_entry->thread_id; + hook->hook = SetWindowsHookExW(WH_GETMESSAGE, wined3d_hook_proc, 0, hook->thread_id); + hook->count = 1; + + wined3d_wndproc_mutex_unlock(); +} + +static void wined3d_swapchain_state_unregister(struct wined3d_swapchain_state *state) +{ + struct wined3d_registered_swapchain_state *state_entry, *last_state_entry; + struct wined3d_window_hook *hook, *last_hook; + unsigned int i; + + wined3d_wndproc_mutex_lock(); + + for (i = 0; i < swapchain_state_table.state_count; ++i) + { + state_entry = &swapchain_state_table.states[i]; + + if (state_entry->state != state) + continue; + + if ((hook = wined3d_find_hook(state_entry->thread_id)) && !--hook->count) + { + UnhookWindowsHookEx(hook->hook); + last_hook = &swapchain_state_table.hooks[--swapchain_state_table.hook_count]; + if (hook != last_hook) + *hook = *last_hook; + } + + last_state_entry = &swapchain_state_table.states[--swapchain_state_table.state_count]; + if (state_entry != last_state_entry) + *state_entry = *last_state_entry; + + break; + } + + wined3d_wndproc_mutex_unlock(); +} + +void wined3d_swapchain_state_cleanup(struct wined3d_swapchain_state *state) +{ + wined3d_swapchain_state_unregister(state); +} + +/* At process attach */ +BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, void *reserved) +{ + switch (reason) + { + case DLL_PROCESS_ATTACH: + return wined3d_dll_init(inst); + + case DLL_PROCESS_DETACH: + if (!reserved) + return wined3d_dll_destroy(inst); + break; + + case DLL_THREAD_DETACH: + if (!wined3d_context_gl_set_current(NULL)) + { + ERR("Failed to clear current context.\n"); + } + return TRUE; + } + return TRUE; +} diff --git a/wrappers/directx/d3dwine_wrapper/wined3d_private.h b/wrappers/directx/d3dwine_wrapper/wined3d_private.h new file mode 100644 index 00000000000..2a75ab5d43d --- /dev/null +++ b/wrappers/directx/d3dwine_wrapper/wined3d_private.h @@ -0,0 +1,6413 @@ +/* + * Direct3D wine internal private include file + * + * Copyright 2002-2003 The wine-d3d team + * Copyright 2002-2003 Raphael Junqueira + * Copyright 2002-2003, 2004 Jason Edmeades + * Copyright 2005 Oliver Stieber + * Copyright 2006-2011, 2013 Stefan Dösinger for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __WINE_WINED3D_PRIVATE_H +#define __WINE_WINED3D_PRIVATE_H + +#ifdef USE_WIN32_OPENGL +#define WINE_GLAPI __stdcall +#else +#define WINE_GLAPI +#endif + +#include +#include +#include +#include +#include +#include "ntstatus.h" +#define WIN32_NO_STATUS +#define NONAMELESSUNION +#define NONAMELESSSTRUCT +#define COBJMACROS +#include "windef.h" +#include "winbase.h" +#include "winreg.h" +#include "wingdi.h" +#include "winuser.h" +#include "winternl.h" +#include "ddk/d3dkmthk.h" +#include "wine/debug.h" +#include "wine/heap.h" +#include "wine/unicode.h" +#ifdef HAVE_FLOAT_H +# include +#endif + +#include "objbase.h" +#include "wine/wined3d.h" +#include "wined3d_gl.h" +#include "wined3d_vk.h" +#include "wine/list.h" +#include "wine/rbtree.h" +#include "wine/wgl_driver.h" + +#define MAKEDWORD_VERSION(maj, min) (((maj & 0xffffu) << 16) | (min & 0xffffu)) + +/* Driver quirks */ +#define WINED3D_QUIRK_ARB_VS_OFFSET_LIMIT 0x00000001 +#define WINED3D_QUIRK_GLSL_CLIP_VARYING 0x00000004 +#define WINED3D_QUIRK_ALLOWS_SPECULAR_ALPHA 0x00000008 +#define WINED3D_QUIRK_NV_CLIP_BROKEN 0x00000010 +#define WINED3D_QUIRK_FBO_TEX_UPDATE 0x00000020 +#define WINED3D_QUIRK_BROKEN_RGBA16 0x00000040 +#define WINED3D_QUIRK_INFO_LOG_SPAM 0x00000080 +#define WINED3D_QUIRK_LIMITED_TEX_FILTERING 0x00000100 +#define WINED3D_QUIRK_BROKEN_ARB_FOG 0x00000200 +#define WINED3D_QUIRK_NO_INDEPENDENT_BIT_DEPTHS 0x00000400 + +#define WINED3D_MAX_DIRTY_REGION_COUNT 7 + +#define WINED3D_ALPHA_TO_COVERAGE_ENABLE MAKEFOURCC('A','2','M','1') +#define WINED3D_ALPHA_TO_COVERAGE_DISABLE MAKEFOURCC('A','2','M','0') + +#define WINED3D_BITMAP_SIZE(x) (((x) + 31) >> 5) + +struct wined3d_fragment_pipe_ops; +struct wined3d_adapter; +struct wined3d_buffer_vk; +struct wined3d_context; +struct wined3d_context_vk; +struct wined3d_gl_info; +struct wined3d_state; +struct wined3d_swapchain_gl; +struct wined3d_texture_gl; +struct wined3d_vertex_pipe_ops; + +enum wined3d_ffp_idx +{ + WINED3D_FFP_POSITION = 0, + WINED3D_FFP_BLENDWEIGHT = 1, + WINED3D_FFP_BLENDINDICES = 2, + WINED3D_FFP_NORMAL = 3, + WINED3D_FFP_PSIZE = 4, + WINED3D_FFP_DIFFUSE = 5, + WINED3D_FFP_SPECULAR = 6, + WINED3D_FFP_TEXCOORD0 = 7, + WINED3D_FFP_TEXCOORD1 = 8, + WINED3D_FFP_TEXCOORD2 = 9, + WINED3D_FFP_TEXCOORD3 = 10, + WINED3D_FFP_TEXCOORD4 = 11, + WINED3D_FFP_TEXCOORD5 = 12, + WINED3D_FFP_TEXCOORD6 = 13, + WINED3D_FFP_TEXCOORD7 = 14, + WINED3D_FFP_ATTRIBS_COUNT = 15, +}; + +enum wined3d_ffp_emit_idx +{ + WINED3D_FFP_EMIT_FLOAT1, + WINED3D_FFP_EMIT_FLOAT2, + WINED3D_FFP_EMIT_FLOAT3, + WINED3D_FFP_EMIT_FLOAT4, + WINED3D_FFP_EMIT_D3DCOLOR, + WINED3D_FFP_EMIT_UBYTE4, + WINED3D_FFP_EMIT_SHORT2, + WINED3D_FFP_EMIT_SHORT4, + WINED3D_FFP_EMIT_UBYTE4N, + WINED3D_FFP_EMIT_SHORT2N, + WINED3D_FFP_EMIT_SHORT4N, + WINED3D_FFP_EMIT_USHORT2N, + WINED3D_FFP_EMIT_USHORT4N, + WINED3D_FFP_EMIT_UDEC3, + WINED3D_FFP_EMIT_DEC3N, + WINED3D_FFP_EMIT_FLOAT16_2, + WINED3D_FFP_EMIT_FLOAT16_4, + WINED3D_FFP_EMIT_INVALID, + WINED3D_FFP_EMIT_COUNT, +}; + +/* Texture format fixups */ + +enum fixup_channel_source +{ + CHANNEL_SOURCE_ZERO = 0, + CHANNEL_SOURCE_ONE = 1, + CHANNEL_SOURCE_X = 2, + CHANNEL_SOURCE_Y = 3, + CHANNEL_SOURCE_Z = 4, + CHANNEL_SOURCE_W = 5, + CHANNEL_SOURCE_COMPLEX0 = 6, + CHANNEL_SOURCE_COMPLEX1 = 7, +}; + +enum complex_fixup +{ + COMPLEX_FIXUP_NONE = 0, + COMPLEX_FIXUP_YUY2 = 1, + COMPLEX_FIXUP_UYVY = 2, + COMPLEX_FIXUP_YV12 = 3, + COMPLEX_FIXUP_P8 = 4, + COMPLEX_FIXUP_NV12 = 5, + COMPLEX_FIXUP_YUV = 6, +}; + +#include +struct color_fixup_desc +{ + unsigned short x_sign_fixup : 1; + unsigned short x_source : 3; + unsigned short y_sign_fixup : 1; + unsigned short y_source : 3; + unsigned short z_sign_fixup : 1; + unsigned short z_source : 3; + unsigned short w_sign_fixup : 1; + unsigned short w_source : 3; +}; +#include + +struct wined3d_d3d_limits +{ + unsigned int vs_version, hs_version, ds_version, gs_version, ps_version, cs_version; + DWORD vs_uniform_count; + DWORD ps_uniform_count; + unsigned int varying_count; + unsigned int ffp_textures; + unsigned int ffp_blend_stages; + unsigned int ffp_vertex_blend_matrices; + unsigned int active_light_count; + + unsigned int max_rt_count; + unsigned int max_clip_distances; + unsigned int texture_size; + float pointsize_max; +}; + +typedef void (WINE_GLAPI *wined3d_ffp_attrib_func)(const void *data); +typedef void (WINE_GLAPI *wined3d_ffp_texcoord_func)(GLenum unit, const void *data); +typedef void (WINE_GLAPI *wined3d_generic_attrib_func)(GLuint idx, const void *data); +extern wined3d_ffp_attrib_func specular_func_3ubv DECLSPEC_HIDDEN; + +struct wined3d_ffp_attrib_ops +{ + wined3d_ffp_attrib_func position[WINED3D_FFP_EMIT_COUNT]; + wined3d_ffp_attrib_func diffuse[WINED3D_FFP_EMIT_COUNT]; + wined3d_ffp_attrib_func specular[WINED3D_FFP_EMIT_COUNT]; + wined3d_ffp_attrib_func normal[WINED3D_FFP_EMIT_COUNT]; + wined3d_ffp_texcoord_func texcoord[WINED3D_FFP_EMIT_COUNT]; + wined3d_generic_attrib_func generic[WINED3D_FFP_EMIT_COUNT]; +}; + +struct wined3d_d3d_info +{ + struct wined3d_d3d_limits limits; + struct wined3d_ffp_attrib_ops ffp_attrib_ops; + uint32_t wined3d_creation_flags; + uint32_t xyzrhw : 1; + uint32_t emulated_flatshading : 1; + uint32_t ffp_generic_attributes : 1; + uint32_t ffp_alpha_test : 1; + uint32_t vs_clipping : 1; + uint32_t shader_color_key : 1; + uint32_t shader_double_precision : 1; + uint32_t shader_output_interpolation : 1; + uint32_t viewport_array_index_any_shader : 1; + uint32_t texture_npot : 1; + uint32_t texture_npot_conditional : 1; + uint32_t draw_base_vertex_offset : 1; + uint32_t vertex_bgra : 1; + uint32_t texture_swizzle : 1; + uint32_t srgb_read_control : 1; + uint32_t srgb_write_control : 1; + uint32_t clip_control : 1; + uint32_t full_ffp_varyings : 1; + uint32_t scaled_resolve : 1; + enum wined3d_feature_level feature_level; + + DWORD multisample_draw_location; +}; + +static const struct color_fixup_desc COLOR_FIXUP_IDENTITY = + {0, CHANNEL_SOURCE_X, 0, CHANNEL_SOURCE_Y, 0, CHANNEL_SOURCE_Z, 0, CHANNEL_SOURCE_W}; + +static inline struct color_fixup_desc create_complex_fixup_desc(enum complex_fixup complex_fixup) +{ + struct color_fixup_desc fixup = + { + 0u, complex_fixup & (1u << 0) ? CHANNEL_SOURCE_COMPLEX1 : CHANNEL_SOURCE_COMPLEX0, + 0u, complex_fixup & (1u << 1) ? CHANNEL_SOURCE_COMPLEX1 : CHANNEL_SOURCE_COMPLEX0, + 0u, complex_fixup & (1u << 2) ? CHANNEL_SOURCE_COMPLEX1 : CHANNEL_SOURCE_COMPLEX0, + 0u, complex_fixup & (1u << 3) ? CHANNEL_SOURCE_COMPLEX1 : CHANNEL_SOURCE_COMPLEX0, + }; + return fixup; +} + +static inline BOOL is_identity_fixup(struct color_fixup_desc fixup) +{ + return !memcmp(&fixup, &COLOR_FIXUP_IDENTITY, sizeof(fixup)); +} + +static inline BOOL is_complex_fixup(struct color_fixup_desc fixup) +{ + return fixup.x_source == CHANNEL_SOURCE_COMPLEX0 || fixup.x_source == CHANNEL_SOURCE_COMPLEX1; +} + +static inline BOOL is_scaling_fixup(struct color_fixup_desc fixup) +{ + return fixup.x_sign_fixup || fixup.y_sign_fixup || fixup.z_sign_fixup || fixup.w_sign_fixup; +} + +static inline BOOL is_same_fixup(struct color_fixup_desc f1, struct color_fixup_desc f2) +{ + return f1.x_sign_fixup == f2.x_sign_fixup && f1.x_source == f2.x_source + && f1.y_sign_fixup == f2.y_sign_fixup && f1.y_source == f2.y_source + && f1.z_sign_fixup == f2.z_sign_fixup && f1.z_source == f2.z_source + && f1.w_sign_fixup == f2.w_sign_fixup && f1.w_source == f2.w_source; +} + +static inline enum complex_fixup get_complex_fixup(struct color_fixup_desc fixup) +{ + enum complex_fixup complex_fixup = 0; + if (fixup.x_source == CHANNEL_SOURCE_COMPLEX1) complex_fixup |= (1u << 0); + if (fixup.y_source == CHANNEL_SOURCE_COMPLEX1) complex_fixup |= (1u << 1); + if (fixup.z_source == CHANNEL_SOURCE_COMPLEX1) complex_fixup |= (1u << 2); + if (fixup.w_source == CHANNEL_SOURCE_COMPLEX1) complex_fixup |= (1u << 3); + return complex_fixup; +} + +/* Device caps */ +#define WINED3D_MAX_ACTIVE_LIGHTS 8 +#define WINED3D_MAX_SOFTWARE_ACTIVE_LIGHTS 32 +#define MAX_CONSTANT_BUFFERS 15 +#define MAX_SAMPLER_OBJECTS 16 +#define MAX_SHADER_RESOURCE_VIEWS 128 +#define MAX_UNORDERED_ACCESS_VIEWS 8 +#define MAX_TGSM_REGISTERS 8192 +#define MAX_VERTEX_BLENDS 4 + +struct min_lookup +{ + GLenum mip[WINED3D_TEXF_LINEAR + 1]; +}; + +extern const struct min_lookup minMipLookup[WINED3D_TEXF_LINEAR + 1] DECLSPEC_HIDDEN; +extern const GLenum magLookup[WINED3D_TEXF_LINEAR + 1] DECLSPEC_HIDDEN; + +GLenum wined3d_gl_compare_func(enum wined3d_cmp_func f) DECLSPEC_HIDDEN; +VkAccessFlags vk_access_mask_from_bind_flags(uint32_t bind_flags) DECLSPEC_HIDDEN; +VkCompareOp vk_compare_op_from_wined3d(enum wined3d_cmp_func op) DECLSPEC_HIDDEN; +VkImageViewType vk_image_view_type_from_wined3d(enum wined3d_resource_type type, uint32_t flags) DECLSPEC_HIDDEN; +VkPipelineStageFlags vk_pipeline_stage_mask_from_bind_flags(uint32_t bind_flags) DECLSPEC_HIDDEN; +VkShaderStageFlagBits vk_shader_stage_from_wined3d(enum wined3d_shader_type shader_type) DECLSPEC_HIDDEN; + +static inline enum wined3d_cmp_func wined3d_sanitize_cmp_func(enum wined3d_cmp_func func) +{ + if (func < WINED3D_CMP_NEVER || func > WINED3D_CMP_ALWAYS) + return WINED3D_CMP_ALWAYS; + return func; +} + +static inline GLenum wined3d_gl_mag_filter(enum wined3d_texture_filter_type mag_filter) +{ + return magLookup[mag_filter]; +} + +static inline GLenum wined3d_gl_min_mip_filter(enum wined3d_texture_filter_type min_filter, + enum wined3d_texture_filter_type mip_filter) +{ + return minMipLookup[min_filter].mip[mip_filter]; +} + +#ifdef HAVE_IEEEFP_H +#include +#endif + +#if !defined(HAVE_ISFINITE) && !defined(isfinite) +static inline int isfinite(double x) { return finite(x); } +#endif + +#if !defined(HAVE_ISINF) && !defined(isinf) +static inline int isinf(double x) { return (!(finite(x) || isnand(x))); } +#endif + +#if !defined(HAVE_ISNAN) && !defined(isnan) +static inline int isnan(double x) { return isnand(x); } +#endif + +#ifndef INFINITY +static inline float __port_infinity(void) +{ + static const unsigned __inf_bytes = 0x7f800000; + return *(const float *)&__inf_bytes; +} +#define INFINITY __port_infinity() +#endif + +#ifndef NAN +static inline float __port_nan(void) +{ + static const unsigned __nan_bytes = 0x7fc00000; + return *(const float *)&__nan_bytes; +} +#define NAN __port_nan() +#endif + +/* float_16_to_32() and float_32_to_16() (see implementation in + * surface_base.c) convert 16 bit floats in the FLOAT16 data type + * to standard C floats and vice versa. They do not depend on the encoding + * of the C float, so they are platform independent, but slow. On x86 and + * other IEEE 754 compliant platforms the conversion can be accelerated by + * bit shifting the exponent and mantissa. There are also some SSE-based + * assembly routines out there. + * + * See GL_NV_half_float for a reference of the FLOAT16 / GL_HALF format + */ +static inline float float_16_to_32(const unsigned short *in) +{ + const unsigned short s = ((*in) & 0x8000u); + const unsigned short e = ((*in) & 0x7c00u) >> 10; + const unsigned short m = (*in) & 0x3ffu; + const float sgn = (s ? -1.0f : 1.0f); + + if(e == 0) { + if(m == 0) return sgn * 0.0f; /* +0.0 or -0.0 */ + else return sgn * powf(2, -14.0f) * ((float)m / 1024.0f); + } else if(e < 31) { + return sgn * powf(2, (float)e - 15.0f) * (1.0f + ((float)m / 1024.0f)); + } else { + if(m == 0) return sgn * INFINITY; + else return NAN; + } +} + +static inline float float_24_to_32(DWORD in) +{ + const float sgn = in & 0x800000u ? -1.0f : 1.0f; + const unsigned short e = (in & 0x780000u) >> 19; + const unsigned int m = in & 0x7ffffu; + + if (e == 0) + { + if (m == 0) return sgn * 0.0f; /* +0.0 or -0.0 */ + else return sgn * powf(2, -6.0f) * ((float)m / 524288.0f); + } + else if (e < 15) + { + return sgn * powf(2, (float)e - 7.0f) * (1.0f + ((float)m / 524288.0f)); + } + else + { + if (m == 0) return sgn * INFINITY; + else return NAN; + } +} + +static inline unsigned int wined3d_popcount(unsigned int x) +{ +#ifdef HAVE___BUILTIN_POPCOUNT + return __builtin_popcount(x); +#else + x -= x >> 1 & 0x55555555; + x = (x & 0x33333333) + (x >> 2 & 0x33333333); + return ((x + (x >> 4)) & 0x0f0f0f0f) * 0x01010101 >> 24; +#endif +} + +#define ORM_BACKBUFFER 0 +#define ORM_FBO 1 + +#define PCI_VENDOR_NONE 0xffff /* e.g. 0x8086 for Intel and 0x10de for Nvidia */ +#define PCI_DEVICE_NONE 0xffff /* e.g. 0x14f for a Geforce6200 */ + +enum wined3d_renderer +{ + WINED3D_RENDERER_AUTO, + WINED3D_RENDERER_VULKAN, + WINED3D_RENDERER_OPENGL, + WINED3D_RENDERER_NO3D, +}; + +enum wined3d_shader_backend +{ + WINED3D_SHADER_BACKEND_AUTO, + WINED3D_SHADER_BACKEND_GLSL, + WINED3D_SHADER_BACKEND_ARB, + WINED3D_SHADER_BACKEND_NONE, +}; + +#define WINED3D_CSMT_ENABLE 0x00000001 +#define WINED3D_CSMT_SERIALIZE 0x00000002 + +/* NOTE: When adding fields to this structure, make sure to update the default + * values in wined3d_main.c as well. */ +struct wined3d_settings +{ + unsigned int cs_multithreaded; + DWORD max_gl_version; + int offscreen_rendering_mode; + unsigned short pci_vendor_id; + unsigned short pci_device_id; + /* Memory tracking and object counting. */ + UINT64 emulated_textureram; + char *logo; + unsigned int multisample_textures; + unsigned int sample_count; + BOOL check_float_constants; + unsigned int strict_shader_math; + unsigned int max_sm_vs; + unsigned int max_sm_hs; + unsigned int max_sm_ds; + unsigned int max_sm_gs; + unsigned int max_sm_ps; + unsigned int max_sm_cs; + enum wined3d_renderer renderer; + enum wined3d_shader_backend shader_backend; +}; + +extern struct wined3d_settings wined3d_settings DECLSPEC_HIDDEN; + +enum wined3d_shader_byte_code_format +{ + WINED3D_SHADER_BYTE_CODE_FORMAT_SM1, + WINED3D_SHADER_BYTE_CODE_FORMAT_SM4, +}; + +enum wined3d_shader_resource_type +{ + WINED3D_SHADER_RESOURCE_NONE, + WINED3D_SHADER_RESOURCE_BUFFER, + WINED3D_SHADER_RESOURCE_TEXTURE_1D, + WINED3D_SHADER_RESOURCE_TEXTURE_2D, + WINED3D_SHADER_RESOURCE_TEXTURE_2DMS, + WINED3D_SHADER_RESOURCE_TEXTURE_3D, + WINED3D_SHADER_RESOURCE_TEXTURE_CUBE, + WINED3D_SHADER_RESOURCE_TEXTURE_1DARRAY, + WINED3D_SHADER_RESOURCE_TEXTURE_2DARRAY, + WINED3D_SHADER_RESOURCE_TEXTURE_2DMSARRAY, + WINED3D_SHADER_RESOURCE_TEXTURE_CUBEARRAY, +}; + +#define WINED3D_SHADER_CONST_VS_F 0x00000001 +#define WINED3D_SHADER_CONST_VS_I 0x00000002 +#define WINED3D_SHADER_CONST_VS_B 0x00000004 +#define WINED3D_SHADER_CONST_VS_CLIP_PLANES 0x00000008 +#define WINED3D_SHADER_CONST_VS_POINTSIZE 0x00000010 +#define WINED3D_SHADER_CONST_POS_FIXUP 0x00000020 +#define WINED3D_SHADER_CONST_PS_F 0x00000040 +#define WINED3D_SHADER_CONST_PS_I 0x00000080 +#define WINED3D_SHADER_CONST_PS_B 0x00000100 +#define WINED3D_SHADER_CONST_PS_BUMP_ENV 0x00000200 +#define WINED3D_SHADER_CONST_PS_FOG 0x00000400 +#define WINED3D_SHADER_CONST_PS_ALPHA_TEST 0x00000800 +#define WINED3D_SHADER_CONST_PS_Y_CORR 0x00001000 +#define WINED3D_SHADER_CONST_PS_NP2_FIXUP 0x00002000 +#define WINED3D_SHADER_CONST_FFP_MODELVIEW 0x00004000 +#define WINED3D_SHADER_CONST_FFP_VERTEXBLEND 0x00008000 +#define WINED3D_SHADER_CONST_FFP_PROJ 0x00010000 +#define WINED3D_SHADER_CONST_FFP_TEXMATRIX 0x00020000 +#define WINED3D_SHADER_CONST_FFP_MATERIAL 0x00040000 +#define WINED3D_SHADER_CONST_FFP_LIGHTS 0x00080000 +#define WINED3D_SHADER_CONST_FFP_PS 0x00100000 +#define WINED3D_SHADER_CONST_FFP_COLOR_KEY 0x00200000 +#define WINED3D_SHADER_CONST_BASE_VERTEX_ID 0x00400000 + +enum wined3d_shader_register_type +{ + WINED3DSPR_TEMP = 0, + WINED3DSPR_INPUT = 1, + WINED3DSPR_CONST = 2, + WINED3DSPR_ADDR = 3, + WINED3DSPR_TEXTURE = 3, + WINED3DSPR_RASTOUT = 4, + WINED3DSPR_ATTROUT = 5, + WINED3DSPR_TEXCRDOUT = 6, + WINED3DSPR_OUTPUT = 6, + WINED3DSPR_CONSTINT = 7, + WINED3DSPR_COLOROUT = 8, + WINED3DSPR_DEPTHOUT = 9, + WINED3DSPR_SAMPLER = 10, + WINED3DSPR_CONST2 = 11, + WINED3DSPR_CONST3 = 12, + WINED3DSPR_CONST4 = 13, + WINED3DSPR_CONSTBOOL = 14, + WINED3DSPR_LOOP = 15, + WINED3DSPR_TEMPFLOAT16 = 16, + WINED3DSPR_MISCTYPE = 17, + WINED3DSPR_LABEL = 18, + WINED3DSPR_PREDICATE = 19, + WINED3DSPR_IMMCONST, + WINED3DSPR_CONSTBUFFER, + WINED3DSPR_IMMCONSTBUFFER, + WINED3DSPR_PRIMID, + WINED3DSPR_NULL, + WINED3DSPR_RESOURCE, + WINED3DSPR_UAV, + WINED3DSPR_OUTPOINTID, + WINED3DSPR_FORKINSTID, + WINED3DSPR_JOININSTID, + WINED3DSPR_INCONTROLPOINT, + WINED3DSPR_OUTCONTROLPOINT, + WINED3DSPR_PATCHCONST, + WINED3DSPR_TESSCOORD, + WINED3DSPR_GROUPSHAREDMEM, + WINED3DSPR_THREADID, + WINED3DSPR_THREADGROUPID, + WINED3DSPR_LOCALTHREADID, + WINED3DSPR_LOCALTHREADINDEX, + WINED3DSPR_IDXTEMP, + WINED3DSPR_STREAM, + WINED3DSPR_FUNCTIONBODY, + WINED3DSPR_FUNCTIONPOINTER, + WINED3DSPR_COVERAGE, + WINED3DSPR_SAMPLEMASK, + WINED3DSPR_GSINSTID, + WINED3DSPR_DEPTHOUTGE, + WINED3DSPR_DEPTHOUTLE, + WINED3DSPR_RASTERIZER, +}; + +enum wined3d_data_type +{ + WINED3D_DATA_FLOAT, + WINED3D_DATA_INT, + WINED3D_DATA_RESOURCE, + WINED3D_DATA_SAMPLER, + WINED3D_DATA_UAV, + WINED3D_DATA_UINT, + WINED3D_DATA_UNORM, + WINED3D_DATA_SNORM, + WINED3D_DATA_OPAQUE, +}; + +enum wined3d_immconst_type +{ + WINED3D_IMMCONST_SCALAR, + WINED3D_IMMCONST_VEC4, +}; + +#define WINED3DSP_NOSWIZZLE (0u | (1u << 2) | (2u << 4) | (3u << 6)) + +enum wined3d_shader_src_modifier +{ + WINED3DSPSM_NONE = 0, + WINED3DSPSM_NEG = 1, + WINED3DSPSM_BIAS = 2, + WINED3DSPSM_BIASNEG = 3, + WINED3DSPSM_SIGN = 4, + WINED3DSPSM_SIGNNEG = 5, + WINED3DSPSM_COMP = 6, + WINED3DSPSM_X2 = 7, + WINED3DSPSM_X2NEG = 8, + WINED3DSPSM_DZ = 9, + WINED3DSPSM_DW = 10, + WINED3DSPSM_ABS = 11, + WINED3DSPSM_ABSNEG = 12, + WINED3DSPSM_NOT = 13, +}; + +#define WINED3DSP_WRITEMASK_0 0x1u /* .x r */ +#define WINED3DSP_WRITEMASK_1 0x2u /* .y g */ +#define WINED3DSP_WRITEMASK_2 0x4u /* .z b */ +#define WINED3DSP_WRITEMASK_3 0x8u /* .w a */ +#define WINED3DSP_WRITEMASK_ALL 0xfu /* all */ + +enum wined3d_shader_dst_modifier +{ + WINED3DSPDM_NONE = 0, + WINED3DSPDM_SATURATE = 1, + WINED3DSPDM_PARTIALPRECISION = 2, + WINED3DSPDM_MSAMPCENTROID = 4, +}; + +enum wined3d_shader_interpolation_mode +{ + WINED3DSIM_NONE = 0, + WINED3DSIM_CONSTANT = 1, + WINED3DSIM_LINEAR = 2, + WINED3DSIM_LINEAR_CENTROID = 3, + WINED3DSIM_LINEAR_NOPERSPECTIVE = 4, + WINED3DSIM_LINEAR_NOPERSPECTIVE_CENTROID = 5, + WINED3DSIM_LINEAR_SAMPLE = 6, + WINED3DSIM_LINEAR_NOPERSPECTIVE_SAMPLE = 7, +}; + +#define WINED3D_PACKED_INTERPOLATION_SIZE 3 +#define WINED3D_PACKED_INTERPOLATION_BIT_COUNT 3 + +enum wined3d_shader_global_flags +{ + WINED3DSGF_REFACTORING_ALLOWED = 0x1, + WINED3DSGF_FORCE_EARLY_DEPTH_STENCIL = 0x4, + WINED3DSGF_ENABLE_RAW_AND_STRUCTURED_BUFFERS = 0x8, +}; + +enum wined3d_shader_sync_flags +{ + WINED3DSSF_THREAD_GROUP = 0x1, + WINED3DSSF_GROUP_SHARED_MEMORY = 0x2, + WINED3DSSF_GLOBAL_UAV = 0x8, +}; + +enum wined3d_shader_uav_flags +{ + WINED3DSUF_GLOBALLY_COHERENT = 0x2, + WINED3DSUF_ORDER_PRESERVING_COUNTER = 0x100, +}; + +enum wined3d_tessellator_domain +{ + WINED3D_TESSELLATOR_DOMAIN_LINE = 1, + WINED3D_TESSELLATOR_DOMAIN_TRIANGLE = 2, + WINED3D_TESSELLATOR_DOMAIN_QUAD = 3, +}; + +enum wined3d_tessellator_output_primitive +{ + WINED3D_TESSELLATOR_OUTPUT_POINT = 1, + WINED3D_TESSELLATOR_OUTPUT_LINE = 2, + WINED3D_TESSELLATOR_OUTPUT_TRIANGLE_CW = 3, + WINED3D_TESSELLATOR_OUTPUT_TRIANGLE_CCW = 4, +}; + +enum wined3d_tessellator_partitioning +{ + WINED3D_TESSELLATOR_PARTITIONING_INTEGER = 1, + WINED3D_TESSELLATOR_PARTITIONING_POW2 = 2, + WINED3D_TESSELLATOR_PARTITIONING_FRACTIONAL_ODD = 3, + WINED3D_TESSELLATOR_PARTITIONING_FRACTIONAL_EVEN = 4, +}; + +/* Undocumented opcode control to identify projective texture lookups in ps 2.0 and later */ +#define WINED3DSI_TEXLD_PROJECT 0x1 +#define WINED3DSI_TEXLD_BIAS 0x2 +#define WINED3DSI_INDEXED_DYNAMIC 0x4 +#define WINED3DSI_RESINFO_RCP_FLOAT 0x1 +#define WINED3DSI_RESINFO_UINT 0x2 +#define WINED3DSI_SAMPLE_INFO_UINT 0x1 +#define WINED3DSI_SAMPLER_COMPARISON_MODE 0x1 + +#define WINED3DSI_PRECISE_X 0x100 +#define WINED3DSI_PRECISE_Y 0x200 +#define WINED3DSI_PRECISE_Z 0x400 +#define WINED3DSI_PRECISE_W 0x800 +#define WINED3DSI_PRECISE_XYZW (WINED3DSI_PRECISE_X | WINED3DSI_PRECISE_Y \ + | WINED3DSI_PRECISE_Z | WINED3DSI_PRECISE_W) +#define WINED3DSI_PRECISE_SHIFT 8 + +enum wined3d_shader_rel_op +{ + WINED3D_SHADER_REL_OP_GT = 1, + WINED3D_SHADER_REL_OP_EQ = 2, + WINED3D_SHADER_REL_OP_GE = 3, + WINED3D_SHADER_REL_OP_LT = 4, + WINED3D_SHADER_REL_OP_NE = 5, + WINED3D_SHADER_REL_OP_LE = 6, +}; + +enum wined3d_shader_conditional_op +{ + WINED3D_SHADER_CONDITIONAL_OP_NZ = 0, + WINED3D_SHADER_CONDITIONAL_OP_Z = 1 +}; + +#define WINED3D_SM1_VS 0xfffeu +#define WINED3D_SM1_PS 0xffffu +#define WINED3D_SM4_PS 0x0000u +#define WINED3D_SM4_VS 0x0001u +#define WINED3D_SM4_GS 0x0002u +#define WINED3D_SM5_HS 0x0003u +#define WINED3D_SM5_DS 0x0004u +#define WINED3D_SM5_CS 0x0005u + +/* Shader version tokens, and shader end tokens */ +#define WINED3DPS_VERSION(major, minor) ((WINED3D_SM1_PS << 16) | ((major) << 8) | (minor)) +#define WINED3DVS_VERSION(major, minor) ((WINED3D_SM1_VS << 16) | ((major) << 8) | (minor)) + +/* Shader backends */ + +/* TODO: Make this dynamic, based on shader limits ? */ +#define MAX_ATTRIBS 16 +#define MAX_REG_ADDR 1 +#define MAX_REG_TEXCRD 8 +#define MAX_REG_INPUT 32 +#define MAX_REG_OUTPUT 32 +#define WINED3D_MAX_CBS 15 + +/* FIXME: This needs to go up to 2048 for + * Shader model 3 according to msdn (and for software shaders) */ +#define MAX_LABELS 16 + +#define MAX_IMMEDIATE_CONSTANT_BUFFER_SIZE 4096 + +struct wined3d_string_buffer +{ + struct list entry; + char *buffer; + unsigned int buffer_size; + unsigned int content_size; +}; + +enum WINED3D_SHADER_INSTRUCTION_HANDLER +{ + WINED3DSIH_ABS, + WINED3DSIH_ADD, + WINED3DSIH_AND, + WINED3DSIH_ATOMIC_AND, + WINED3DSIH_ATOMIC_CMP_STORE, + WINED3DSIH_ATOMIC_IADD, + WINED3DSIH_ATOMIC_IMAX, + WINED3DSIH_ATOMIC_IMIN, + WINED3DSIH_ATOMIC_OR, + WINED3DSIH_ATOMIC_UMAX, + WINED3DSIH_ATOMIC_UMIN, + WINED3DSIH_ATOMIC_XOR, + WINED3DSIH_BEM, + WINED3DSIH_BFI, + WINED3DSIH_BFREV, + WINED3DSIH_BREAK, + WINED3DSIH_BREAKC, + WINED3DSIH_BREAKP, + WINED3DSIH_BUFINFO, + WINED3DSIH_CALL, + WINED3DSIH_CALLNZ, + WINED3DSIH_CASE, + WINED3DSIH_CMP, + WINED3DSIH_CND, + WINED3DSIH_CONTINUE, + WINED3DSIH_CONTINUEP, + WINED3DSIH_COUNTBITS, + WINED3DSIH_CRS, + WINED3DSIH_CUT, + WINED3DSIH_CUT_STREAM, + WINED3DSIH_DCL, + WINED3DSIH_DCL_CONSTANT_BUFFER, + WINED3DSIH_DCL_FUNCTION_BODY, + WINED3DSIH_DCL_FUNCTION_TABLE, + WINED3DSIH_DCL_GLOBAL_FLAGS, + WINED3DSIH_DCL_GS_INSTANCES, + WINED3DSIH_DCL_HS_FORK_PHASE_INSTANCE_COUNT, + WINED3DSIH_DCL_HS_JOIN_PHASE_INSTANCE_COUNT, + WINED3DSIH_DCL_HS_MAX_TESSFACTOR, + WINED3DSIH_DCL_IMMEDIATE_CONSTANT_BUFFER, + WINED3DSIH_DCL_INDEX_RANGE, + WINED3DSIH_DCL_INDEXABLE_TEMP, + WINED3DSIH_DCL_INPUT, + WINED3DSIH_DCL_INPUT_CONTROL_POINT_COUNT, + WINED3DSIH_DCL_INPUT_PRIMITIVE, + WINED3DSIH_DCL_INPUT_PS, + WINED3DSIH_DCL_INPUT_PS_SGV, + WINED3DSIH_DCL_INPUT_PS_SIV, + WINED3DSIH_DCL_INPUT_SGV, + WINED3DSIH_DCL_INPUT_SIV, + WINED3DSIH_DCL_INTERFACE, + WINED3DSIH_DCL_OUTPUT, + WINED3DSIH_DCL_OUTPUT_CONTROL_POINT_COUNT, + WINED3DSIH_DCL_OUTPUT_SIV, + WINED3DSIH_DCL_OUTPUT_TOPOLOGY, + WINED3DSIH_DCL_RESOURCE_RAW, + WINED3DSIH_DCL_RESOURCE_STRUCTURED, + WINED3DSIH_DCL_SAMPLER, + WINED3DSIH_DCL_STREAM, + WINED3DSIH_DCL_TEMPS, + WINED3DSIH_DCL_TESSELLATOR_DOMAIN, + WINED3DSIH_DCL_TESSELLATOR_OUTPUT_PRIMITIVE, + WINED3DSIH_DCL_TESSELLATOR_PARTITIONING, + WINED3DSIH_DCL_TGSM_RAW, + WINED3DSIH_DCL_TGSM_STRUCTURED, + WINED3DSIH_DCL_THREAD_GROUP, + WINED3DSIH_DCL_UAV_RAW, + WINED3DSIH_DCL_UAV_STRUCTURED, + WINED3DSIH_DCL_UAV_TYPED, + WINED3DSIH_DCL_VERTICES_OUT, + WINED3DSIH_DEF, + WINED3DSIH_DEFAULT, + WINED3DSIH_DEFB, + WINED3DSIH_DEFI, + WINED3DSIH_DIV, + WINED3DSIH_DP2, + WINED3DSIH_DP2ADD, + WINED3DSIH_DP3, + WINED3DSIH_DP4, + WINED3DSIH_DST, + WINED3DSIH_DSX, + WINED3DSIH_DSX_COARSE, + WINED3DSIH_DSX_FINE, + WINED3DSIH_DSY, + WINED3DSIH_DSY_COARSE, + WINED3DSIH_DSY_FINE, + WINED3DSIH_ELSE, + WINED3DSIH_EMIT, + WINED3DSIH_EMIT_STREAM, + WINED3DSIH_ENDIF, + WINED3DSIH_ENDLOOP, + WINED3DSIH_ENDREP, + WINED3DSIH_ENDSWITCH, + WINED3DSIH_EQ, + WINED3DSIH_EVAL_SAMPLE_INDEX, + WINED3DSIH_EXP, + WINED3DSIH_EXPP, + WINED3DSIH_F16TOF32, + WINED3DSIH_F32TOF16, + WINED3DSIH_FCALL, + WINED3DSIH_FIRSTBIT_HI, + WINED3DSIH_FIRSTBIT_LO, + WINED3DSIH_FIRSTBIT_SHI, + WINED3DSIH_FRC, + WINED3DSIH_FTOI, + WINED3DSIH_FTOU, + WINED3DSIH_GATHER4, + WINED3DSIH_GATHER4_C, + WINED3DSIH_GATHER4_PO, + WINED3DSIH_GATHER4_PO_C, + WINED3DSIH_GE, + WINED3DSIH_HS_CONTROL_POINT_PHASE, + WINED3DSIH_HS_DECLS, + WINED3DSIH_HS_FORK_PHASE, + WINED3DSIH_HS_JOIN_PHASE, + WINED3DSIH_IADD, + WINED3DSIH_IBFE, + WINED3DSIH_IEQ, + WINED3DSIH_IF, + WINED3DSIH_IFC, + WINED3DSIH_IGE, + WINED3DSIH_ILT, + WINED3DSIH_IMAD, + WINED3DSIH_IMAX, + WINED3DSIH_IMIN, + WINED3DSIH_IMM_ATOMIC_ALLOC, + WINED3DSIH_IMM_ATOMIC_AND, + WINED3DSIH_IMM_ATOMIC_CMP_EXCH, + WINED3DSIH_IMM_ATOMIC_CONSUME, + WINED3DSIH_IMM_ATOMIC_EXCH, + WINED3DSIH_IMM_ATOMIC_IADD, + WINED3DSIH_IMM_ATOMIC_IMAX, + WINED3DSIH_IMM_ATOMIC_IMIN, + WINED3DSIH_IMM_ATOMIC_OR, + WINED3DSIH_IMM_ATOMIC_UMAX, + WINED3DSIH_IMM_ATOMIC_UMIN, + WINED3DSIH_IMM_ATOMIC_XOR, + WINED3DSIH_IMUL, + WINED3DSIH_INE, + WINED3DSIH_INEG, + WINED3DSIH_ISHL, + WINED3DSIH_ISHR, + WINED3DSIH_ITOF, + WINED3DSIH_LABEL, + WINED3DSIH_LD, + WINED3DSIH_LD2DMS, + WINED3DSIH_LD_RAW, + WINED3DSIH_LD_STRUCTURED, + WINED3DSIH_LD_UAV_TYPED, + WINED3DSIH_LIT, + WINED3DSIH_LOD, + WINED3DSIH_LOG, + WINED3DSIH_LOGP, + WINED3DSIH_LOOP, + WINED3DSIH_LRP, + WINED3DSIH_LT, + WINED3DSIH_M3x2, + WINED3DSIH_M3x3, + WINED3DSIH_M3x4, + WINED3DSIH_M4x3, + WINED3DSIH_M4x4, + WINED3DSIH_MAD, + WINED3DSIH_MAX, + WINED3DSIH_MIN, + WINED3DSIH_MOV, + WINED3DSIH_MOVA, + WINED3DSIH_MOVC, + WINED3DSIH_MUL, + WINED3DSIH_NE, + WINED3DSIH_NOP, + WINED3DSIH_NOT, + WINED3DSIH_NRM, + WINED3DSIH_OR, + WINED3DSIH_PHASE, + WINED3DSIH_POW, + WINED3DSIH_RCP, + WINED3DSIH_REP, + WINED3DSIH_RESINFO, + WINED3DSIH_RET, + WINED3DSIH_RETP, + WINED3DSIH_ROUND_NE, + WINED3DSIH_ROUND_NI, + WINED3DSIH_ROUND_PI, + WINED3DSIH_ROUND_Z, + WINED3DSIH_RSQ, + WINED3DSIH_SAMPLE, + WINED3DSIH_SAMPLE_B, + WINED3DSIH_SAMPLE_C, + WINED3DSIH_SAMPLE_C_LZ, + WINED3DSIH_SAMPLE_GRAD, + WINED3DSIH_SAMPLE_INFO, + WINED3DSIH_SAMPLE_LOD, + WINED3DSIH_SAMPLE_POS, + WINED3DSIH_SETP, + WINED3DSIH_SGE, + WINED3DSIH_SGN, + WINED3DSIH_SINCOS, + WINED3DSIH_SLT, + WINED3DSIH_SQRT, + WINED3DSIH_STORE_RAW, + WINED3DSIH_STORE_STRUCTURED, + WINED3DSIH_STORE_UAV_TYPED, + WINED3DSIH_SUB, + WINED3DSIH_SWAPC, + WINED3DSIH_SWITCH, + WINED3DSIH_SYNC, + WINED3DSIH_TEX, + WINED3DSIH_TEXBEM, + WINED3DSIH_TEXBEML, + WINED3DSIH_TEXCOORD, + WINED3DSIH_TEXDEPTH, + WINED3DSIH_TEXDP3, + WINED3DSIH_TEXDP3TEX, + WINED3DSIH_TEXKILL, + WINED3DSIH_TEXLDD, + WINED3DSIH_TEXLDL, + WINED3DSIH_TEXM3x2DEPTH, + WINED3DSIH_TEXM3x2PAD, + WINED3DSIH_TEXM3x2TEX, + WINED3DSIH_TEXM3x3, + WINED3DSIH_TEXM3x3DIFF, + WINED3DSIH_TEXM3x3PAD, + WINED3DSIH_TEXM3x3SPEC, + WINED3DSIH_TEXM3x3TEX, + WINED3DSIH_TEXM3x3VSPEC, + WINED3DSIH_TEXREG2AR, + WINED3DSIH_TEXREG2GB, + WINED3DSIH_TEXREG2RGB, + WINED3DSIH_UBFE, + WINED3DSIH_UDIV, + WINED3DSIH_UGE, + WINED3DSIH_ULT, + WINED3DSIH_UMAX, + WINED3DSIH_UMIN, + WINED3DSIH_UMUL, + WINED3DSIH_USHR, + WINED3DSIH_UTOF, + WINED3DSIH_XOR, + WINED3DSIH_TABLE_SIZE +}; + +struct wined3d_shader_version +{ + enum wined3d_shader_type type; + BYTE major; + BYTE minor; +}; + +struct wined3d_shader_resource_info +{ + enum wined3d_shader_resource_type type; + enum wined3d_data_type data_type; + unsigned int flags; + unsigned int stride; +}; + +#define WINED3D_SAMPLER_DEFAULT ~0x0u + +struct wined3d_shader_sampler_map_entry +{ + unsigned int resource_idx; + unsigned int sampler_idx; + unsigned int bind_idx; +}; + +struct wined3d_shader_sampler_map +{ + struct wined3d_shader_sampler_map_entry *entries; + size_t size; + size_t count; +}; + +struct wined3d_shader_immediate_constant_buffer +{ + unsigned int vec4_count; + DWORD data[MAX_IMMEDIATE_CONSTANT_BUFFER_SIZE]; +}; + +struct wined3d_shader_indexable_temp +{ + struct list entry; + unsigned int register_idx; + unsigned int register_size; + unsigned int component_count; +}; + +#define WINED3D_SHADER_VERSION(major, minor) (((major) << 8) | (minor)) + +struct wined3d_shader_reg_maps +{ + struct wined3d_shader_version shader_version; + BYTE texcoord; /* MAX_REG_TEXCRD, 8 */ + BYTE address; /* MAX_REG_ADDR, 1 */ + WORD labels; /* MAX_LABELS, 16 */ + DWORD temporary; /* 32 */ + unsigned int temporary_count; + DWORD *constf; /* pixel, vertex */ + struct list indexable_temps; + const struct wined3d_shader_immediate_constant_buffer *icb; + union + { + DWORD texcoord_mask[MAX_REG_TEXCRD]; /* vertex < 3.0 */ + BYTE output_registers_mask[MAX_REG_OUTPUT]; /* vertex >= 3.0 */ + } u; + DWORD input_registers; /* max(MAX_REG_INPUT, MAX_ATTRIBS), 32 */ + DWORD output_registers; /* MAX_REG_OUTPUT, 32 */ + WORD integer_constants; /* WINED3D_MAX_CONSTS_I, 16 */ + WORD boolean_constants; /* WINED3D_MAX_CONSTS_B, 16 */ + WORD local_int_consts; /* WINED3D_MAX_CONSTS_I, 16 */ + WORD local_bool_consts; /* WINED3D_MAX_CONSTS_B, 16 */ + UINT cb_sizes[WINED3D_MAX_CBS]; + uint32_t cb_map; /* WINED3D_MAX_CBS, 15 */ + + struct wined3d_shader_resource_info resource_info[MAX_SHADER_RESOURCE_VIEWS]; + uint32_t resource_map[WINED3D_BITMAP_SIZE(MAX_SHADER_RESOURCE_VIEWS)]; + struct wined3d_shader_sampler_map sampler_map; + DWORD sampler_comparison_mode; + BYTE bumpmat; /* WINED3D_MAX_TEXTURES, 8 */ + BYTE luminanceparams; /* WINED3D_MAX_TEXTURES, 8 */ + struct wined3d_shader_resource_info uav_resource_info[MAX_UNORDERED_ACCESS_VIEWS]; + DWORD uav_read_mask : 8; /* MAX_UNORDERED_ACCESS_VIEWS, 8 */ + DWORD uav_counter_mask : 8; /* MAX_UNORDERED_ACCESS_VIEWS, 8 */ + + DWORD clip_distance_mask : 8; /* WINED3D_MAX_CLIP_DISTANCES, 8 */ + DWORD cull_distance_mask : 8; /* WINED3D_MAX_CLIP_DISTANCES, 8 */ + DWORD usesnrm : 1; + DWORD vpos : 1; + DWORD usesdsx : 1; + DWORD usesdsy : 1; + DWORD usestexldd : 1; + DWORD usesmova : 1; + DWORD usesfacing : 1; + DWORD usesrelconstF : 1; + DWORD fog : 1; + DWORD usestexldl : 1; + DWORD usesifc : 1; + DWORD usescall : 1; + DWORD usespow : 1; + DWORD point_size : 1; + DWORD vocp : 1; + DWORD input_rel_addressing : 1; + DWORD viewport_array : 1; + DWORD sample_mask : 1; + DWORD padding : 14; + + DWORD rt_mask; /* Used render targets, 32 max. */ + + /* Whether or not loops are used in this shader, and nesting depth */ + unsigned int loop_depth; + unsigned int min_rel_offset, max_rel_offset; + + struct wined3d_shader_tgsm *tgsm; + SIZE_T tgsm_capacity; + unsigned int tgsm_count; +}; + +/* Keeps track of details for TEX_M#x# instructions which need to maintain + * state information between multiple instructions. */ +struct wined3d_shader_tex_mx +{ + unsigned int current_row; + DWORD texcoord_w[2]; +}; + +struct wined3d_shader_parser_state +{ + unsigned int current_loop_depth; + unsigned int current_loop_reg; + BOOL in_subroutine; +}; + +struct wined3d_shader_context +{ + const struct wined3d_shader *shader; + const struct wined3d_shader_reg_maps *reg_maps; + struct wined3d_string_buffer *buffer; + struct wined3d_shader_tex_mx *tex_mx; + struct wined3d_shader_parser_state *state; + void *backend_data; +}; + +struct wined3d_shader_register_index +{ + const struct wined3d_shader_src_param *rel_addr; + unsigned int offset; +}; + +struct wined3d_shader_register +{ + enum wined3d_shader_register_type type; + enum wined3d_data_type data_type; + struct wined3d_shader_register_index idx[2]; + enum wined3d_immconst_type immconst_type; + union + { + DWORD immconst_data[4]; + unsigned fp_body_idx; + } u; +}; + +struct wined3d_shader_dst_param +{ + struct wined3d_shader_register reg; + DWORD write_mask; + DWORD modifiers; + DWORD shift; +}; + +struct wined3d_shader_src_param +{ + struct wined3d_shader_register reg; + DWORD swizzle; + enum wined3d_shader_src_modifier modifiers; +}; + +struct wined3d_shader_index_range +{ + struct wined3d_shader_dst_param first_register; + unsigned int last_register; +}; + +struct wined3d_shader_semantic +{ + enum wined3d_decl_usage usage; + UINT usage_idx; + enum wined3d_shader_resource_type resource_type; + enum wined3d_data_type resource_data_type; + struct wined3d_shader_dst_param reg; +}; + +enum wined3d_shader_input_sysval_semantic +{ + WINED3D_SIV_POSITION = 1, + WINED3D_SIV_CLIP_DISTANCE = 2, + WINED3D_SIV_CULL_DISTANCE = 3, + WINED3D_SIV_RENDER_TARGET_ARRAY_INDEX = 4, + WINED3D_SIV_VIEWPORT_ARRAY_INDEX = 5, + WINED3D_SIV_VERTEX_ID = 6, + WINED3D_SIV_PRIMITIVE_ID = 7, + WINED3D_SIV_INSTANCE_ID = 8, + WINED3D_SIV_IS_FRONT_FACE = 9, + WINED3D_SIV_SAMPLE_INDEX = 10, + WINED3D_SIV_QUAD_U0_TESS_FACTOR = 11, + WINED3D_SIV_QUAD_V0_TESS_FACTOR = 12, + WINED3D_SIV_QUAD_U1_TESS_FACTOR = 13, + WINED3D_SIV_QUAD_V1_TESS_FACTOR = 14, + WINED3D_SIV_QUAD_U_INNER_TESS_FACTOR = 15, + WINED3D_SIV_QUAD_V_INNER_TESS_FACTOR = 16, + WINED3D_SIV_TRIANGLE_U_TESS_FACTOR = 17, + WINED3D_SIV_TRIANGLE_V_TESS_FACTOR = 18, + WINED3D_SIV_TRIANGLE_W_TESS_FACTOR = 19, + WINED3D_SIV_TRIANGLE_INNER_TESS_FACTOR = 20, + WINED3D_SIV_LINE_DETAIL_TESS_FACTOR = 21, + WINED3D_SIV_LINE_DENSITY_TESS_FACTOR = 22, +}; + +struct wined3d_shader_register_semantic +{ + struct wined3d_shader_dst_param reg; + enum wined3d_shader_input_sysval_semantic sysval_semantic; +}; + +struct wined3d_shader_structured_resource +{ + struct wined3d_shader_dst_param reg; + unsigned int byte_stride; +}; + +struct wined3d_shader_tgsm +{ + unsigned int size; + unsigned int stride; +}; + +struct wined3d_shader_tgsm_raw +{ + struct wined3d_shader_dst_param reg; + unsigned int byte_count; +}; + +struct wined3d_shader_tgsm_structured +{ + struct wined3d_shader_dst_param reg; + unsigned int byte_stride; + unsigned int structure_count; +}; + +struct wined3d_shader_thread_group_size +{ + unsigned int x, y, z; +}; + +struct wined3d_shader_function_table_pointer +{ + unsigned int index; + unsigned int array_size; + unsigned int body_count; + unsigned int table_count; +}; + +struct wined3d_shader_texel_offset +{ + signed char u, v, w; +}; + +struct wined3d_shader_primitive_type +{ + enum wined3d_primitive_type type; + unsigned int patch_vertex_count; +}; + +struct wined3d_shader_instruction +{ + const struct wined3d_shader_context *ctx; + enum WINED3D_SHADER_INSTRUCTION_HANDLER handler_idx; + DWORD flags; + unsigned int dst_count; + unsigned int src_count; + const struct wined3d_shader_dst_param *dst; + const struct wined3d_shader_src_param *src; + struct wined3d_shader_texel_offset texel_offset; + enum wined3d_shader_resource_type resource_type; + enum wined3d_data_type resource_data_type; + BOOL coissue; + const struct wined3d_shader_src_param *predicate; + union + { + struct wined3d_shader_semantic semantic; + struct wined3d_shader_register_semantic register_semantic; + struct wined3d_shader_primitive_type primitive_type; + struct wined3d_shader_dst_param dst; + struct wined3d_shader_src_param src; + unsigned int count; + unsigned int index; + const struct wined3d_shader_immediate_constant_buffer *icb; + struct wined3d_shader_structured_resource structured_resource; + struct wined3d_shader_tgsm_raw tgsm_raw; + struct wined3d_shader_tgsm_structured tgsm_structured; + struct wined3d_shader_thread_group_size thread_group_size; + enum wined3d_tessellator_domain tessellator_domain; + enum wined3d_tessellator_output_primitive tessellator_output_primitive; + enum wined3d_tessellator_partitioning tessellator_partitioning; + float max_tessellation_factor; + struct wined3d_shader_index_range index_range; + struct wined3d_shader_indexable_temp indexable_temp; + struct wined3d_shader_function_table_pointer fp; + } declaration; +}; + +static inline BOOL wined3d_shader_instruction_has_texel_offset(const struct wined3d_shader_instruction *ins) +{ + return ins->texel_offset.u || ins->texel_offset.v || ins->texel_offset.w; +} + +struct wined3d_shader_attribute +{ + enum wined3d_decl_usage usage; + UINT usage_idx; +}; + +struct wined3d_shader_loop_control +{ + unsigned int count; + unsigned int start; + int step; +}; + +struct wined3d_shader_frontend +{ + void *(*shader_init)(const DWORD *byte_code, size_t byte_code_size, + const struct wined3d_shader_signature *output_signature); + void (*shader_free)(void *data); + void (*shader_read_header)(void *data, const DWORD **ptr, struct wined3d_shader_version *shader_version); + void (*shader_read_instruction)(void *data, const DWORD **ptr, struct wined3d_shader_instruction *ins); + BOOL (*shader_is_end)(void *data, const DWORD **ptr); +}; + +extern const struct wined3d_shader_frontend sm1_shader_frontend DECLSPEC_HIDDEN; +extern const struct wined3d_shader_frontend sm4_shader_frontend DECLSPEC_HIDDEN; + +HRESULT shader_extract_from_dxbc(struct wined3d_shader *shader, + unsigned int max_shader_version, enum wined3d_shader_byte_code_format *format) DECLSPEC_HIDDEN; +BOOL shader_get_stream_output_register_info(const struct wined3d_shader *shader, + const struct wined3d_stream_output_element *so_element, unsigned int *register_idx, + unsigned int *component_idx) DECLSPEC_HIDDEN; + +typedef void (*SHADER_HANDLER)(const struct wined3d_shader_instruction *); + +#define WINED3D_SHADER_CAP_VS_CLIPPING 0x00000001u +#define WINED3D_SHADER_CAP_SRGB_WRITE 0x00000002u +#define WINED3D_SHADER_CAP_DOUBLE_PRECISION 0x00000004u +#define WINED3D_SHADER_CAP_OUTPUT_INTERPOLATION 0x00000008u +#define WINED3D_SHADER_CAP_FULL_FFP_VARYINGS 0x00000010u + +struct shader_caps +{ + unsigned int vs_version; + unsigned int hs_version; + unsigned int ds_version; + unsigned int gs_version; + unsigned int ps_version; + unsigned int cs_version; + + unsigned int vs_uniform_count; + unsigned int ps_uniform_count; + float ps_1x_max_value; + unsigned int varying_count; + + DWORD wined3d_caps; +}; + +enum wined3d_gl_resource_type +{ + WINED3D_GL_RES_TYPE_TEX_1D = 0, + WINED3D_GL_RES_TYPE_TEX_2D = 1, + WINED3D_GL_RES_TYPE_TEX_3D = 2, + WINED3D_GL_RES_TYPE_TEX_CUBE = 3, + WINED3D_GL_RES_TYPE_TEX_RECT = 4, + WINED3D_GL_RES_TYPE_BUFFER = 5, + WINED3D_GL_RES_TYPE_RB = 6, + WINED3D_GL_RES_TYPE_COUNT = 7, +}; + +enum wined3d_vertex_processing_mode +{ + WINED3D_VP_MODE_FF, + WINED3D_VP_MODE_SHADER, + WINED3D_VP_MODE_NONE, +}; + +#define WINED3D_CONST_NUM_UNUSED ~0U + +enum wined3d_ffp_ps_fog_mode +{ + WINED3D_FFP_PS_FOG_OFF, + WINED3D_FFP_PS_FOG_LINEAR, + WINED3D_FFP_PS_FOG_EXP, + WINED3D_FFP_PS_FOG_EXP2, +}; + +/* Stateblock dependent parameters which have to be hardcoded + * into the shader code + */ + +#define WINED3D_PSARGS_PROJECTED (1u << 3) +#define WINED3D_PSARGS_TEXTRANSFORM_SHIFT 4 +#define WINED3D_PSARGS_TEXTRANSFORM_MASK 0xfu +#define WINED3D_PSARGS_TEXTYPE_SHIFT 2 +#define WINED3D_PSARGS_TEXTYPE_MASK 0x3u + +/* Used for Shader Model 1 pixel shaders to track the bound texture + * type. 2D and RECT textures are separated through NP2 fixup. */ +enum wined3d_shader_tex_types +{ + WINED3D_SHADER_TEX_2D = 0, + WINED3D_SHADER_TEX_3D = 1, + WINED3D_SHADER_TEX_CUBE = 2, + WINED3D_SHADER_TEX_ERR = 3, +}; + +struct ps_compile_args +{ + struct color_fixup_desc color_fixup[WINED3D_MAX_FRAGMENT_SAMPLERS]; + enum wined3d_vertex_processing_mode vp_mode; + enum wined3d_ffp_ps_fog_mode fog; + DWORD tex_types; /* ps 1 - 3, 16 textures */ + WORD tex_transform; /* ps 1.0-1.3, 4 textures */ + WORD srgb_correction; + /* Bitmap for NP2 texcoord fixups (16 samplers max currently). + D3D9 has a limit of 16 samplers and the fixup is superfluous + in D3D10 (unconditional NP2 support mandatory). */ + WORD np2_fixup; + WORD shadow; /* WINED3D_MAX_FRAGMENT_SAMPLERS, 16 */ + WORD texcoords_initialized; /* WINED3D_MAX_TEXTURES, 8 */ + WORD padding_to_dword; + DWORD pointsprite : 1; + DWORD flatshading : 1; + DWORD alpha_test_func : 3; + DWORD render_offscreen : 1; + DWORD rt_alpha_swizzle : 8; /* WINED3D_MAX_RENDER_TARGETS, 8 */ + DWORD dual_source_blend : 1; + DWORD padding : 17; +}; + +enum fog_src_type +{ + VS_FOG_Z = 0, + VS_FOG_COORD = 1 +}; + +struct vs_compile_args +{ + BYTE fog_src; + BYTE clip_enabled : 1; + BYTE point_size : 1; + BYTE per_vertex_point_size : 1; + BYTE flatshading : 1; + BYTE next_shader_type : 3; + BYTE padding : 1; + WORD swizzle_map; /* MAX_ATTRIBS, 16 */ + unsigned int next_shader_input_count; + DWORD interpolation_mode[WINED3D_PACKED_INTERPOLATION_SIZE]; +}; + +struct ds_compile_args +{ + enum wined3d_tessellator_output_primitive tessellator_output_primitive; + enum wined3d_tessellator_partitioning tessellator_partitioning; + unsigned int output_count : 16; + unsigned int next_shader_type : 3; + unsigned int render_offscreen : 1; + unsigned int padding : 12; + DWORD interpolation_mode[WINED3D_PACKED_INTERPOLATION_SIZE]; +}; + +struct gs_compile_args +{ + unsigned int output_count; + enum wined3d_primitive_type primitive_type; + DWORD interpolation_mode[WINED3D_PACKED_INTERPOLATION_SIZE]; +}; + +struct wined3d_shader_backend_ops +{ + void (*shader_handle_instruction)(const struct wined3d_shader_instruction *); + void (*shader_precompile)(void *shader_priv, struct wined3d_shader *shader); + void (*shader_select)(void *shader_priv, struct wined3d_context *context, + const struct wined3d_state *state); + void (*shader_select_compute)(void *shader_priv, struct wined3d_context *context, + const struct wined3d_state *state); + void (*shader_disable)(void *shader_priv, struct wined3d_context *context); + void (*shader_update_float_vertex_constants)(struct wined3d_device *device, UINT start, UINT count); + void (*shader_update_float_pixel_constants)(struct wined3d_device *device, UINT start, UINT count); + void (*shader_load_constants)(void *shader_priv, struct wined3d_context *context, + const struct wined3d_state *state); + void (*shader_destroy)(struct wined3d_shader *shader); + HRESULT (*shader_alloc_private)(struct wined3d_device *device, const struct wined3d_vertex_pipe_ops *vertex_pipe, + const struct wined3d_fragment_pipe_ops *fragment_pipe); + void (*shader_free_private)(struct wined3d_device *device, struct wined3d_context *context); + BOOL (*shader_allocate_context_data)(struct wined3d_context *context); + void (*shader_free_context_data)(struct wined3d_context *context); + void (*shader_init_context_state)(struct wined3d_context *context); + void (*shader_get_caps)(const struct wined3d_adapter *adapter, struct shader_caps *caps); + BOOL (*shader_color_fixup_supported)(struct color_fixup_desc fixup); + BOOL (*shader_has_ffp_proj_control)(void *shader_priv); +}; + +extern const struct wined3d_shader_backend_ops glsl_shader_backend DECLSPEC_HIDDEN; +extern const struct wined3d_shader_backend_ops arb_program_shader_backend DECLSPEC_HIDDEN; +extern const struct wined3d_shader_backend_ops none_shader_backend DECLSPEC_HIDDEN; + +const struct wined3d_shader_backend_ops *wined3d_spirv_shader_backend_init_vk(void) DECLSPEC_HIDDEN; +void wined3d_spirv_shader_backend_cleanup(void) DECLSPEC_HIDDEN; + +#define GL_EXTCALL(f) (gl_info->gl_ops.ext.p_##f) + +#define D3DCOLOR_B_R(dw) (((dw) >> 16) & 0xff) +#define D3DCOLOR_B_G(dw) (((dw) >> 8) & 0xff) +#define D3DCOLOR_B_B(dw) (((dw) >> 0) & 0xff) +#define D3DCOLOR_B_A(dw) (((dw) >> 24) & 0xff) + +static inline void wined3d_color_from_d3dcolor(struct wined3d_color *wined3d_color, DWORD d3d_color) +{ + wined3d_color->r = D3DCOLOR_B_R(d3d_color) / 255.0f; + wined3d_color->g = D3DCOLOR_B_G(d3d_color) / 255.0f; + wined3d_color->b = D3DCOLOR_B_B(d3d_color) / 255.0f; + wined3d_color->a = D3DCOLOR_B_A(d3d_color) / 255.0f; +} + +extern const struct wined3d_vec4 wined3d_srgb_const[] DECLSPEC_HIDDEN; + +static inline float wined3d_srgb_from_linear(float colour) +{ + if (colour < 0.0f) + return 0.0f; + if (colour < wined3d_srgb_const[1].x) + return colour * wined3d_srgb_const[0].w; + if (colour < 1.0f) + return wined3d_srgb_const[0].y * powf(colour, wined3d_srgb_const[0].x) - wined3d_srgb_const[0].z; + return 1.0f; +} + +static inline void wined3d_colour_srgb_from_linear(struct wined3d_color *colour_srgb, + const struct wined3d_color *colour) +{ + colour_srgb->r = wined3d_srgb_from_linear(colour->r); + colour_srgb->g = wined3d_srgb_from_linear(colour->g); + colour_srgb->b = wined3d_srgb_from_linear(colour->b); + colour_srgb->a = colour->a; +} + +void wined3d_check_gl_call(const struct wined3d_gl_info *gl_info, + const char *file, unsigned int line, const char *name) DECLSPEC_HIDDEN; + +/* Checking of API calls */ +/* --------------------- */ +#ifndef WINE_NO_DEBUG_MSGS +#define checkGLcall(A) \ +do { \ + if (__WINE_IS_DEBUG_ON(_ERR, &__wine_dbch_d3d) \ + && !gl_info->supported[ARB_DEBUG_OUTPUT]) \ + wined3d_check_gl_call(gl_info, __FILE__, __LINE__, A); \ +} while(0) +#else +#define checkGLcall(A) do {} while(0) +#endif + +struct wined3d_bo_gl +{ + GLuint id; + GLsizeiptr size; + GLenum binding; + GLenum usage; + + GLbitfield flags; + bool coherent; + struct list users; + uint64_t command_fence_id; +}; + +static inline GLuint wined3d_bo_gl_id(uintptr_t bo) +{ + return bo ? ((struct wined3d_bo_gl *)bo)->id : 0; +} + +struct wined3d_bo_user +{ + struct list entry; + bool valid; +}; + +struct wined3d_bo_vk +{ + VkBuffer vk_buffer; + struct wined3d_allocator_block *memory; + struct wined3d_bo_slab_vk *slab; + + VkDeviceMemory vk_memory; + void *map_ptr; + + VkDeviceSize buffer_offset; + VkDeviceSize memory_offset; + VkDeviceSize size; + VkBufferUsageFlags usage; + VkMemoryPropertyFlags memory_type; + + struct list users; + uint64_t command_buffer_id; +}; + +struct wined3d_bo_slab_vk_key +{ + VkMemoryPropertyFlags memory_type; + VkBufferUsageFlags usage; + VkDeviceSize size; +}; + +struct wined3d_bo_slab_vk +{ + struct wine_rb_entry entry; + struct wined3d_bo_slab_vk *next; + VkMemoryPropertyFlags requested_memory_type; + struct wined3d_bo_vk bo; + unsigned int map_count; + void *map_ptr; + uint32_t map; +}; + +void *wined3d_bo_slab_vk_map(struct wined3d_bo_slab_vk *slab_vk, + struct wined3d_context_vk *context_vk) DECLSPEC_HIDDEN; +void wined3d_bo_slab_vk_unmap(struct wined3d_bo_slab_vk *slab_vk, + struct wined3d_context_vk *context_vk) DECLSPEC_HIDDEN; + +struct wined3d_bo_address +{ + UINT_PTR buffer_object; + BYTE *addr; +}; + +struct wined3d_const_bo_address +{ + UINT_PTR buffer_object; + const BYTE *addr; +}; + +static inline struct wined3d_const_bo_address *wined3d_const_bo_address(struct wined3d_bo_address *data) +{ + return (struct wined3d_const_bo_address *)data; +} + +struct wined3d_stream_info_element +{ + const struct wined3d_format *format; + struct wined3d_bo_address data; + GLsizei stride; + unsigned int stream_idx; + unsigned int divisor; +}; + +struct wined3d_stream_info +{ + struct wined3d_stream_info_element elements[MAX_ATTRIBS]; + DWORD position_transformed : 1; + DWORD all_vbo : 1; + WORD swizzle_map; /* MAX_ATTRIBS, 16 */ + WORD use_map; /* MAX_ATTRIBS, 16 */ +}; + +void wined3d_stream_info_from_declaration(struct wined3d_stream_info *stream_info, + const struct wined3d_state *state, const struct wined3d_d3d_info *d3d_info) DECLSPEC_HIDDEN; + +struct wined3d_direct_dispatch_parameters +{ + unsigned int group_count_x; + unsigned int group_count_y; + unsigned int group_count_z; +}; + +struct wined3d_indirect_dispatch_parameters +{ + struct wined3d_buffer *buffer; + unsigned int offset; +}; + +struct wined3d_dispatch_parameters +{ + BOOL indirect; + union + { + struct wined3d_direct_dispatch_parameters direct; + struct wined3d_indirect_dispatch_parameters indirect; + } u; +}; + +struct wined3d_direct_draw_parameters +{ + int base_vertex_idx; + unsigned int start_idx; + unsigned int index_count; + unsigned int start_instance; + unsigned int instance_count; +}; + +struct wined3d_indirect_draw_parameters +{ + struct wined3d_buffer *buffer; + unsigned int offset; +}; + +struct wined3d_draw_parameters +{ + BOOL indirect; + union + { + struct wined3d_direct_draw_parameters direct; + struct wined3d_indirect_draw_parameters indirect; + } u; + BOOL indexed; +}; + +void draw_primitive(struct wined3d_device *device, const struct wined3d_state *state, + const struct wined3d_draw_parameters *draw_parameters) DECLSPEC_HIDDEN; +void dispatch_compute(struct wined3d_device *device, const struct wined3d_state *state, + const struct wined3d_dispatch_parameters *dispatch_parameters) DECLSPEC_HIDDEN; + +#define eps 1e-8f + +#define GET_TEXCOORD_SIZE_FROM_FVF(d3dvtVertexType, tex_num) \ + (((((d3dvtVertexType) >> (16 + (2 * (tex_num)))) + 1) & 0x03) + 1) + +enum wined3d_pipeline +{ + WINED3D_PIPELINE_GRAPHICS, + WINED3D_PIPELINE_COMPUTE, + WINED3D_PIPELINE_COUNT, +}; + +/* Routines and structures related to state management */ + +#define STATE_RENDER(a) (a) +#define STATE_IS_RENDER(a) ((a) >= STATE_RENDER(1) && (a) <= STATE_RENDER(WINEHIGHEST_RENDER_STATE)) + +#define STATE_TEXTURESTAGE(stage, num) \ + (STATE_RENDER(WINEHIGHEST_RENDER_STATE) + 1 + (stage) * (WINED3D_HIGHEST_TEXTURE_STATE + 1) + (num)) +#define STATE_IS_TEXTURESTAGE(a) \ + ((a) >= STATE_TEXTURESTAGE(0, 1) && (a) <= STATE_TEXTURESTAGE(WINED3D_MAX_TEXTURES - 1, WINED3D_HIGHEST_TEXTURE_STATE)) + +/* + 1 because samplers start with 0 */ +#define STATE_SAMPLER(num) (STATE_TEXTURESTAGE(WINED3D_MAX_TEXTURES - 1, WINED3D_HIGHEST_TEXTURE_STATE) + 1 + (num)) +#define STATE_IS_SAMPLER(num) ((num) >= STATE_SAMPLER(0) && (num) <= STATE_SAMPLER(WINED3D_MAX_COMBINED_SAMPLERS - 1)) + +#define STATE_GRAPHICS_SHADER(a) (STATE_SAMPLER(WINED3D_MAX_COMBINED_SAMPLERS) + (a)) +#define STATE_IS_GRAPHICS_SHADER(a) \ + ((a) >= STATE_GRAPHICS_SHADER(0) && (a) < STATE_GRAPHICS_SHADER(WINED3D_SHADER_TYPE_GRAPHICS_COUNT)) + +#define STATE_GRAPHICS_CONSTANT_BUFFER(a) (STATE_GRAPHICS_SHADER(WINED3D_SHADER_TYPE_GRAPHICS_COUNT) + (a)) +#define STATE_IS_GRAPHICS_CONSTANT_BUFFER(a) \ + ((a) >= STATE_GRAPHICS_CONSTANT_BUFFER(0) \ + && (a) < STATE_GRAPHICS_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_GRAPHICS_COUNT)) + +#define STATE_GRAPHICS_SHADER_RESOURCE_BINDING (STATE_GRAPHICS_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_GRAPHICS_COUNT)) +#define STATE_IS_GRAPHICS_SHADER_RESOURCE_BINDING(a) ((a) == STATE_GRAPHICS_SHADER_RESOURCE_BINDING) + +#define STATE_GRAPHICS_UNORDERED_ACCESS_VIEW_BINDING (STATE_GRAPHICS_SHADER_RESOURCE_BINDING + 1) +#define STATE_IS_GRAPHICS_UNORDERED_ACCESS_VIEW_BINDING(a) ((a) == STATE_GRAPHICS_UNORDERED_ACCESS_VIEW_BINDING) + +#define STATE_TRANSFORM(a) (STATE_GRAPHICS_UNORDERED_ACCESS_VIEW_BINDING + (a)) +#define STATE_IS_TRANSFORM(a) ((a) >= STATE_TRANSFORM(1) && (a) <= STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(255))) + +#define STATE_STREAMSRC (STATE_TRANSFORM(WINED3D_TS_WORLD_MATRIX(255)) + 1) +#define STATE_IS_STREAMSRC(a) ((a) == STATE_STREAMSRC) +#define STATE_INDEXBUFFER (STATE_STREAMSRC + 1) +#define STATE_IS_INDEXBUFFER(a) ((a) == STATE_INDEXBUFFER) + +#define STATE_VDECL (STATE_INDEXBUFFER + 1) +#define STATE_IS_VDECL(a) ((a) == STATE_VDECL) + +#define STATE_VIEWPORT (STATE_VDECL + 1) +#define STATE_IS_VIEWPORT(a) ((a) == STATE_VIEWPORT) + +#define STATE_LIGHT_TYPE (STATE_VIEWPORT + 1) +#define STATE_IS_LIGHT_TYPE(a) ((a) == STATE_LIGHT_TYPE) +#define STATE_ACTIVELIGHT(a) (STATE_LIGHT_TYPE + 1 + (a)) +#define STATE_IS_ACTIVELIGHT(a) ((a) >= STATE_ACTIVELIGHT(0) && (a) < STATE_ACTIVELIGHT(WINED3D_MAX_ACTIVE_LIGHTS)) + +#define STATE_SCISSORRECT (STATE_ACTIVELIGHT(WINED3D_MAX_ACTIVE_LIGHTS - 1) + 1) +#define STATE_IS_SCISSORRECT(a) ((a) == STATE_SCISSORRECT) + +#define STATE_CLIPPLANE(a) (STATE_SCISSORRECT + 1 + (a)) +#define STATE_IS_CLIPPLANE(a) ((a) >= STATE_CLIPPLANE(0) && (a) <= STATE_CLIPPLANE(WINED3D_MAX_CLIP_DISTANCES - 1)) + +#define STATE_MATERIAL (STATE_CLIPPLANE(WINED3D_MAX_CLIP_DISTANCES)) +#define STATE_IS_MATERIAL(a) ((a) == STATE_MATERIAL) + +#define STATE_RASTERIZER (STATE_MATERIAL + 1) +#define STATE_IS_RASTERIZER(a) ((a) == STATE_RASTERIZER) + +#define STATE_POINTSPRITECOORDORIGIN (STATE_RASTERIZER + 1) +#define STATE_IS_POINTSPRITECOORDORIGIN(a) ((a) == STATE_POINTSPRITECOORDORIGIN) + +#define STATE_BASEVERTEXINDEX (STATE_POINTSPRITECOORDORIGIN + 1) +#define STATE_IS_BASEVERTEXINDEX(a) ((a) == STATE_BASEVERTEXINDEX) + +#define STATE_FRAMEBUFFER (STATE_BASEVERTEXINDEX + 1) +#define STATE_IS_FRAMEBUFFER(a) ((a) == STATE_FRAMEBUFFER) + +#define STATE_POINT_ENABLE (STATE_FRAMEBUFFER + 1) +#define STATE_IS_POINT_ENABLE(a) ((a) == STATE_POINT_ENABLE) + +#define STATE_COLOR_KEY (STATE_POINT_ENABLE + 1) +#define STATE_IS_COLOR_KEY(a) ((a) == STATE_COLOR_KEY) + +#define STATE_STREAM_OUTPUT (STATE_COLOR_KEY + 1) +#define STATE_IS_STREAM_OUTPUT(a) ((a) == STATE_STREAM_OUTPUT) + +#define STATE_BLEND (STATE_STREAM_OUTPUT + 1) +#define STATE_IS_BLEND(a) ((a) == STATE_BLEND) + +#define STATE_BLEND_FACTOR (STATE_BLEND + 1) +#define STATE_IS_BLEND_FACTOR(a) ((a) == STATE_BLEND_FACTOR) + +#define STATE_SAMPLE_MASK (STATE_BLEND_FACTOR + 1) +#define STATE_IS_SAMPLE_MASK(a) ((a) == STATE_SAMPLE_MASK) + +#define STATE_DEPTH_STENCIL (STATE_SAMPLE_MASK + 1) +#define STATE_IS_DEPTH_STENCIL(a) ((a) == STATE_DEPTH_STENCIL) + +#define STATE_STENCIL_REF (STATE_DEPTH_STENCIL + 1) +#define STATE_IS_STENCIL_REF(a) ((a) == STATE_STENCIL_REF) + +#define STATE_COMPUTE_OFFSET (STATE_STENCIL_REF + 1) + +#define STATE_COMPUTE_SHADER (STATE_COMPUTE_OFFSET) +#define STATE_IS_COMPUTE_SHADER(a) ((a) == STATE_COMPUTE_SHADER) + +#define STATE_COMPUTE_CONSTANT_BUFFER (STATE_COMPUTE_SHADER + 1) +#define STATE_IS_COMPUTE_CONSTANT_BUFFER(a) ((a) == STATE_COMPUTE_CONSTANT_BUFFER) + +#define STATE_COMPUTE_SHADER_RESOURCE_BINDING (STATE_COMPUTE_CONSTANT_BUFFER + 1) +#define STATE_IS_COMPUTE_SHADER_RESOURCE_BINDING(a) ((a) == STATE_COMPUTE_SHADER_RESOURCE_BINDING) + +#define STATE_COMPUTE_UNORDERED_ACCESS_VIEW_BINDING (STATE_COMPUTE_SHADER_RESOURCE_BINDING + 1) +#define STATE_IS_COMPUTE_UNORDERED_ACCESS_VIEW_BINDING(a) ((a) == STATE_COMPUTE_UNORDERED_ACCESS_VIEW_BINDING) + +#define STATE_COMPUTE_HIGHEST (STATE_COMPUTE_UNORDERED_ACCESS_VIEW_BINDING) +#define STATE_HIGHEST (STATE_COMPUTE_UNORDERED_ACCESS_VIEW_BINDING) + +#define STATE_IS_COMPUTE(a) ((a) >= STATE_COMPUTE_OFFSET && (a) <= STATE_COMPUTE_HIGHEST) +#define STATE_COMPUTE_COUNT (STATE_COMPUTE_HIGHEST - STATE_COMPUTE_OFFSET + 1) + +#define STATE_SHADER(a) ((a) != WINED3D_SHADER_TYPE_COMPUTE ? STATE_GRAPHICS_SHADER(a) : STATE_COMPUTE_SHADER) +#define STATE_CONSTANT_BUFFER(a) \ + ((a) != WINED3D_SHADER_TYPE_COMPUTE ? STATE_GRAPHICS_CONSTANT_BUFFER(a) : STATE_COMPUTE_CONSTANT_BUFFER) +#define STATE_UNORDERED_ACCESS_VIEW_BINDING(a) ((a) == WINED3D_PIPELINE_GRAPHICS ? \ + STATE_GRAPHICS_UNORDERED_ACCESS_VIEW_BINDING : STATE_COMPUTE_UNORDERED_ACCESS_VIEW_BINDING) + +enum fogsource { + FOGSOURCE_FFP, + FOGSOURCE_VS, + FOGSOURCE_COORD, +}; + +union wined3d_gl_fence_object +{ + GLuint id; + GLsync sync; +}; + +enum wined3d_fence_result +{ + WINED3D_FENCE_OK, + WINED3D_FENCE_WAITING, + WINED3D_FENCE_NOT_STARTED, + WINED3D_FENCE_WRONG_THREAD, + WINED3D_FENCE_ERROR, +}; + +struct wined3d_fence +{ + struct list entry; + union wined3d_gl_fence_object object; + struct wined3d_context_gl *context_gl; +}; + +HRESULT wined3d_fence_create(struct wined3d_device *device, struct wined3d_fence **fence) DECLSPEC_HIDDEN; +void wined3d_fence_destroy(struct wined3d_fence *fence) DECLSPEC_HIDDEN; +void wined3d_fence_issue(struct wined3d_fence *fence, struct wined3d_device *device) DECLSPEC_HIDDEN; +enum wined3d_fence_result wined3d_fence_wait(const struct wined3d_fence *fence, + struct wined3d_device *device) DECLSPEC_HIDDEN; +enum wined3d_fence_result wined3d_fence_test(const struct wined3d_fence *fence, + struct wined3d_device *device, DWORD flags) DECLSPEC_HIDDEN; + +/* Direct3D terminology with little modifications. We do not have an issued + * state because only the driver knows about it, but we have a created state + * because D3D allows GetData() on a created query, but OpenGL doesn't. */ +enum wined3d_query_state +{ + QUERY_CREATED, + QUERY_SIGNALLED, + QUERY_BUILDING +}; + +struct wined3d_query_ops +{ + BOOL (*query_poll)(struct wined3d_query *query, DWORD flags); + BOOL (*query_issue)(struct wined3d_query *query, DWORD flags); + void (*query_destroy)(struct wined3d_query *query); +}; + +struct wined3d_query +{ + LONG ref; + + void *parent; + const struct wined3d_parent_ops *parent_ops; + struct wined3d_device *device; + enum wined3d_query_state state; + enum wined3d_query_type type; + const void *data; + DWORD data_size; + const struct wined3d_query_ops *query_ops; + + LONG counter_main, counter_retrieved; + struct list poll_list_entry; + + GLuint buffer_object; + UINT64 *map_ptr; +}; + +HRESULT wined3d_query_gl_create(struct wined3d_device *device, enum wined3d_query_type type, void *parent, + const struct wined3d_parent_ops *parent_ops, struct wined3d_query **query) DECLSPEC_HIDDEN; +void wined3d_query_gl_destroy_buffer_object(struct wined3d_context_gl *context_gl, + struct wined3d_query *query) DECLSPEC_HIDDEN; + +struct wined3d_event_query +{ + struct wined3d_query query; + + struct wined3d_fence fence; + BOOL signalled; +}; + +struct wined3d_occlusion_query +{ + struct wined3d_query query; + + struct list entry; + GLuint id; + struct wined3d_context_gl *context_gl; + UINT64 samples; + BOOL started; +}; + +struct wined3d_timestamp_query +{ + struct wined3d_query query; + + struct list entry; + GLuint id; + struct wined3d_context_gl *context_gl; + UINT64 timestamp; +}; + +union wined3d_gl_so_statistics_query +{ + GLuint id[2]; + struct + { + GLuint written; + GLuint generated; + } query; +}; + +struct wined3d_so_statistics_query +{ + struct wined3d_query query; + + struct list entry; + union wined3d_gl_so_statistics_query u; + struct wined3d_context_gl *context_gl; + unsigned int stream_idx; + struct wined3d_query_data_so_statistics statistics; + BOOL started; +}; + +union wined3d_gl_pipeline_statistics_query +{ + GLuint id[11]; + struct + { + GLuint vertices; + GLuint primitives; + GLuint vertex_shader; + GLuint tess_control_shader; + GLuint tess_eval_shader; + GLuint geometry_shader; + GLuint geometry_primitives; + GLuint fragment_shader; + GLuint compute_shader; + GLuint clipping_input; + GLuint clipping_output; + } query; +}; + +struct wined3d_pipeline_statistics_query +{ + struct wined3d_query query; + + struct list entry; + union wined3d_gl_pipeline_statistics_query u; + struct wined3d_context_gl *context_gl; + struct wined3d_query_data_pipeline_statistics statistics; + BOOL started; +}; + +#define WINED3D_QUERY_POOL_SIZE 256 + +struct wined3d_query_pool_vk +{ + struct list entry; + + struct list *free_list; + VkQueryPool vk_query_pool; + uint32_t allocated[WINED3D_BITMAP_SIZE(WINED3D_QUERY_POOL_SIZE)]; +}; + +bool wined3d_query_pool_vk_allocate_query(struct wined3d_query_pool_vk *pool_vk, size_t *idx) DECLSPEC_HIDDEN; +void wined3d_query_pool_vk_cleanup(struct wined3d_query_pool_vk *pool_vk, + struct wined3d_context_vk *context_vk) DECLSPEC_HIDDEN; +void wined3d_query_pool_vk_free_query(struct wined3d_query_pool_vk *pool_vk, size_t idx) DECLSPEC_HIDDEN; +bool wined3d_query_pool_vk_init(struct wined3d_query_pool_vk *pool_vk, struct wined3d_context_vk *context_vk, + enum wined3d_query_type type, struct list *free_pools) DECLSPEC_HIDDEN; + +struct wined3d_query_pool_idx_vk +{ + struct wined3d_query_pool_vk *pool_vk; + size_t idx; +}; + +struct wined3d_query_vk +{ + struct wined3d_query q; + + struct list entry; + struct wined3d_query_pool_idx_vk pool_idx; + bool started; + uint64_t command_buffer_id; + uint32_t control_flags; + size_t pending_count; +}; + +static inline struct wined3d_query_vk *wined3d_query_vk(struct wined3d_query *query) +{ + return CONTAINING_RECORD(query, struct wined3d_query_vk, q); +} + +bool wined3d_query_vk_accumulate_data(struct wined3d_query_vk *query_vk, struct wined3d_context_vk *context_vk, + const struct wined3d_query_pool_idx_vk *pool_idx) DECLSPEC_HIDDEN; +HRESULT wined3d_query_vk_create(struct wined3d_device *device, enum wined3d_query_type type, void *parent, + const struct wined3d_parent_ops *parent_ops, struct wined3d_query **query) DECLSPEC_HIDDEN; +void wined3d_query_vk_resume(struct wined3d_query_vk *query_vk, struct wined3d_context_vk *context_vk) DECLSPEC_HIDDEN; +void wined3d_query_vk_suspend(struct wined3d_query_vk *query_vk, struct wined3d_context_vk *context_vk) DECLSPEC_HIDDEN; + +struct wined3d_gl_view +{ + GLenum target; + GLuint name; +}; + +struct wined3d_range +{ + unsigned int offset; + unsigned int size; +}; + +struct wined3d_rendertarget_info +{ + struct wined3d_gl_view gl_view; + struct wined3d_resource *resource; + unsigned int sub_resource_idx; + unsigned int layer_count; +}; + +struct wined3d_fb_state +{ + struct wined3d_rendertarget_view *render_targets[WINED3D_MAX_RENDER_TARGETS]; + struct wined3d_rendertarget_view *depth_stencil; +}; + +#define MAX_GL_FRAGMENT_SAMPLERS 32 + +struct wined3d_context +{ + const struct wined3d_d3d_info *d3d_info; + const struct wined3d_state_entry *state_table; + uint32_t dirty_graphics_states[WINED3D_BITMAP_SIZE(STATE_HIGHEST)]; + uint32_t dirty_compute_states[WINED3D_BITMAP_SIZE(STATE_COMPUTE_COUNT)]; + + struct wined3d_device *device; + struct wined3d_swapchain *swapchain; + struct + { + struct wined3d_texture *texture; + unsigned int sub_resource_idx; + } current_rt; + + /* Stores some information about the context state for optimization */ + DWORD shader_update_mask : 6; /* WINED3D_SHADER_TYPE_COUNT, 6 */ + DWORD update_shader_resource_bindings : 1; + DWORD update_compute_shader_resource_bindings : 1; + DWORD update_unordered_access_view_bindings : 1; + DWORD update_compute_unordered_access_view_bindings : 1; + DWORD last_swizzle_map : 16; /* MAX_ATTRIBS, 16 */ + DWORD last_was_rhw : 1; /* True iff last draw_primitive was in xyzrhw mode. */ + DWORD last_was_pshader : 1; + DWORD last_was_vshader : 1; + DWORD last_was_diffuse : 1; + DWORD last_was_specular : 1; + DWORD last_was_normal : 1; + + DWORD last_was_ffp_blit : 1; + DWORD last_was_blit : 1; + DWORD last_was_ckey : 1; + DWORD last_was_dual_source_blend : 1; + DWORD texShaderBumpMap : 8; /* WINED3D_MAX_TEXTURES, 8 */ + DWORD lastWasPow2Texture : 8; /* WINED3D_MAX_TEXTURES, 8 */ + DWORD fixed_function_usage_map : 8; /* WINED3D_MAX_TEXTURES, 8 */ + DWORD lowest_disabled_stage : 4; /* Max WINED3D_MAX_TEXTURES, 8 */ + + DWORD use_immediate_mode_draw : 1; + DWORD uses_uavs : 1; + DWORD uses_fbo_attached_resources : 1; + DWORD transform_feedback_active : 1; + DWORD transform_feedback_paused : 1; + DWORD fog_coord : 1; + DWORD render_offscreen : 1; + DWORD current : 1; + DWORD destroyed : 1; + DWORD destroy_delayed : 1; + DWORD clip_distance_mask : 8; /* WINED3D_MAX_CLIP_DISTANCES, 8 */ + DWORD namedArraysLoaded : 1; + DWORD padding : 13; + + DWORD constant_update_mask; + DWORD numbered_array_mask; + enum fogsource fog_source; + + UINT instance_count; + + void *shader_backend_data; + void *fragment_pipe_data; + + struct wined3d_stream_info stream_info; + + unsigned int viewport_count; + unsigned int scissor_rect_count; +}; + +void wined3d_context_cleanup(struct wined3d_context *context) DECLSPEC_HIDDEN; +void wined3d_context_init(struct wined3d_context *context, struct wined3d_swapchain *swapchain) DECLSPEC_HIDDEN; +void context_preload_textures(struct wined3d_context *context, const struct wined3d_state *state) DECLSPEC_HIDDEN; +void context_update_stream_info(struct wined3d_context *context, const struct wined3d_state *state) DECLSPEC_HIDDEN; + +HRESULT wined3d_context_no3d_init(struct wined3d_context *context_no3d, + struct wined3d_swapchain *swapchain) DECLSPEC_HIDDEN; + +struct wined3d_command_fence_gl +{ + uint64_t id; + struct wined3d_fence *fence; +}; + +struct wined3d_context_gl +{ + struct wined3d_context c; + + const struct wined3d_gl_info *gl_info; + + DWORD tid; /* Thread ID which owns this context at the moment. */ + + uint32_t dc_is_private : 1; + uint32_t dc_has_format : 1; /* Only meaningful for private DCs. */ + uint32_t fog_enabled : 1; + uint32_t diffuse_attrib_to_1 : 1; + uint32_t rebind_fbo : 1; + uint32_t untracked_material_count : 2; /* Max value 2 */ + uint32_t needs_set : 1; + uint32_t valid : 1; + uint32_t padding : 23; + + uint32_t default_attrib_value_set; + + GLenum tracking_parm; /* Which source is tracking current colour. */ + GLenum untracked_materials[2]; + SIZE blit_size; + unsigned int active_texture; + + GLenum *texture_type; + + /* The WGL context. */ + unsigned int level; + HGLRC restore_ctx; + HDC restore_dc; + int restore_pf; + HWND restore_pf_win; + HGLRC gl_ctx; + HDC dc; + int pixel_format; + HWND window; + GLint aux_buffers; + + /* FBOs. */ + unsigned int fbo_entry_count; + struct list fbo_list; + struct list fbo_destroy_list; + struct fbo_entry *current_fbo; + GLuint fbo_read_binding; + GLuint fbo_draw_binding; + struct wined3d_rendertarget_info blit_targets[WINED3D_MAX_RENDER_TARGETS]; + uint32_t draw_buffers_mask; /* Enabled draw buffers, 31 max. */ + + /* Queries. */ + struct list occlusion_queries; + struct list fences; + struct list timestamp_queries; + struct list so_statistics_queries; + struct list pipeline_statistics_queries; + + GLuint *free_occlusion_queries; + SIZE_T free_occlusion_query_size; + unsigned int free_occlusion_query_count; + + union wined3d_gl_fence_object *free_fences; + SIZE_T free_fence_size; + unsigned int free_fence_count; + + GLuint *free_timestamp_queries; + SIZE_T free_timestamp_query_size; + unsigned int free_timestamp_query_count; + + union wined3d_gl_so_statistics_query *free_so_statistics_queries; + SIZE_T free_so_statistics_query_size; + unsigned int free_so_statistics_query_count; + + union wined3d_gl_pipeline_statistics_query *free_pipeline_statistics_queries; + SIZE_T free_pipeline_statistics_query_size; + unsigned int free_pipeline_statistics_query_count; + + GLuint blit_vbo; + + unsigned int tex_unit_map[WINED3D_MAX_COMBINED_SAMPLERS]; + unsigned int rev_tex_unit_map[MAX_GL_FRAGMENT_SAMPLERS + WINED3D_MAX_VERTEX_SAMPLERS]; + + /* Extension emulation. */ + GLint gl_fog_source; + GLfloat fog_coord_value; + GLfloat colour[4], fog_start, fog_end, fog_colour[4]; + + GLuint dummy_arbfp_prog; + + struct + { + struct wined3d_command_fence_gl *fences; + SIZE_T fences_size; + SIZE_T fence_count; + } submitted; +}; + +static inline struct wined3d_context_gl *wined3d_context_gl(struct wined3d_context *context) +{ + return CONTAINING_RECORD(context, struct wined3d_context_gl, c); +} + +static inline const struct wined3d_context_gl *wined3d_context_gl_const(const struct wined3d_context *context) +{ + return CONTAINING_RECORD(context, struct wined3d_context_gl, c); +} + +struct wined3d_context *wined3d_context_gl_acquire(const struct wined3d_device *device, + struct wined3d_texture *texture, unsigned int sub_resource_idx) DECLSPEC_HIDDEN; +void wined3d_context_gl_active_texture(struct wined3d_context_gl *context_gl, + const struct wined3d_gl_info *gl_info, unsigned int unit) DECLSPEC_HIDDEN; +void wined3d_context_gl_alloc_fence(struct wined3d_context_gl *context_gl, + struct wined3d_fence *fence) DECLSPEC_HIDDEN; +void wined3d_context_gl_alloc_occlusion_query(struct wined3d_context_gl *context_gl, + struct wined3d_occlusion_query *query) DECLSPEC_HIDDEN; +void wined3d_context_gl_alloc_pipeline_statistics_query(struct wined3d_context_gl *context_gl, + struct wined3d_pipeline_statistics_query *query) DECLSPEC_HIDDEN; +void wined3d_context_gl_alloc_so_statistics_query(struct wined3d_context_gl *context_gl, + struct wined3d_so_statistics_query *query) DECLSPEC_HIDDEN; +void wined3d_context_gl_alloc_timestamp_query(struct wined3d_context_gl *context_gl, + struct wined3d_timestamp_query *query) DECLSPEC_HIDDEN; +void wined3d_context_gl_apply_blit_state(struct wined3d_context_gl *context_gl, + const struct wined3d_device *device) DECLSPEC_HIDDEN; +BOOL wined3d_context_gl_apply_clear_state(struct wined3d_context_gl *context_gl, const struct wined3d_state *state, + unsigned int rt_count, const struct wined3d_fb_state *fb) DECLSPEC_HIDDEN; +void wined3d_context_gl_apply_fbo_state_blit(struct wined3d_context_gl *context_gl, GLenum target, + struct wined3d_resource *rt, unsigned int rt_sub_resource_idx, + struct wined3d_resource *ds, unsigned int ds_sub_resource_idx, DWORD location) DECLSPEC_HIDDEN; +void wined3d_context_gl_apply_ffp_blit_state(struct wined3d_context_gl *context_gl, + const struct wined3d_device *device) DECLSPEC_HIDDEN; +void wined3d_context_gl_bind_bo(struct wined3d_context_gl *context_gl, GLenum binding, GLuint name) DECLSPEC_HIDDEN; +void wined3d_context_gl_bind_dummy_textures(const struct wined3d_context_gl *context_gl) DECLSPEC_HIDDEN; +void wined3d_context_gl_bind_texture(struct wined3d_context_gl *context_gl, + GLenum target, GLuint name) DECLSPEC_HIDDEN; +void wined3d_context_gl_check_fbo_status(const struct wined3d_context_gl *context_gl, GLenum target) DECLSPEC_HIDDEN; +void wined3d_context_gl_copy_bo_address(struct wined3d_context_gl *context_gl, + const struct wined3d_bo_address *dst, const struct wined3d_bo_address *src, size_t size) DECLSPEC_HIDDEN; +bool wined3d_context_gl_create_bo(struct wined3d_context_gl *context_gl, GLsizeiptr size, GLenum binding, + GLenum usage, bool coherent, GLbitfield flags, struct wined3d_bo_gl *bo) DECLSPEC_HIDDEN; +void wined3d_context_gl_destroy(struct wined3d_context_gl *context_gl) DECLSPEC_HIDDEN; +void wined3d_context_gl_destroy_bo(struct wined3d_context_gl *context_gl, struct wined3d_bo_gl *bo) DECLSPEC_HIDDEN; +void wined3d_context_gl_draw_shaded_quad(struct wined3d_context_gl *context_gl, struct wined3d_texture_gl *texture_gl, + unsigned int sub_resource_idx, const RECT *src_rect, const RECT *dst_rect, + enum wined3d_texture_filter_type filter) DECLSPEC_HIDDEN; +void wined3d_context_gl_draw_textured_quad(struct wined3d_context_gl *context_gl, + struct wined3d_texture_gl *texture_gl, unsigned int sub_resource_idx, + const RECT *src_rect, const RECT *dst_rect, enum wined3d_texture_filter_type filter) DECLSPEC_HIDDEN; +void wined3d_context_gl_enable_clip_distances(struct wined3d_context_gl *context_gl, uint32_t mask) DECLSPEC_HIDDEN; +void wined3d_context_gl_end_transform_feedback(struct wined3d_context_gl *context_gl) DECLSPEC_HIDDEN; +void wined3d_context_gl_free_fence(struct wined3d_fence *fence) DECLSPEC_HIDDEN; +void wined3d_context_gl_free_occlusion_query(struct wined3d_occlusion_query *query) DECLSPEC_HIDDEN; +void wined3d_context_gl_free_pipeline_statistics_query(struct wined3d_pipeline_statistics_query *query) DECLSPEC_HIDDEN; +void wined3d_context_gl_free_so_statistics_query(struct wined3d_so_statistics_query *query) DECLSPEC_HIDDEN; +void wined3d_context_gl_free_timestamp_query(struct wined3d_timestamp_query *query) DECLSPEC_HIDDEN; +struct wined3d_context_gl *wined3d_context_gl_get_current(void) DECLSPEC_HIDDEN; +GLenum wined3d_context_gl_get_offscreen_gl_buffer(const struct wined3d_context_gl *context_gl) DECLSPEC_HIDDEN; +const unsigned int *wined3d_context_gl_get_tex_unit_mapping(const struct wined3d_context_gl *context_gl, + const struct wined3d_shader_version *shader_version, unsigned int *base, unsigned int *count) DECLSPEC_HIDDEN; +HRESULT wined3d_context_gl_init(struct wined3d_context_gl *context_gl, + struct wined3d_swapchain_gl *swapchain_gl) DECLSPEC_HIDDEN; +void wined3d_context_gl_load_tex_coords(const struct wined3d_context_gl *context_gl, + const struct wined3d_stream_info *si, GLuint *current_bo, const struct wined3d_state *state) DECLSPEC_HIDDEN; +void *wined3d_context_gl_map_bo_address(struct wined3d_context_gl *context_gl, + const struct wined3d_bo_address *data, size_t size, uint32_t flags) DECLSPEC_HIDDEN; +struct wined3d_context_gl *wined3d_context_gl_reacquire(struct wined3d_context_gl *context_gl) DECLSPEC_HIDDEN; +void wined3d_context_gl_release(struct wined3d_context_gl *context_gl) DECLSPEC_HIDDEN; +BOOL wined3d_context_gl_set_current(struct wined3d_context_gl *context_gl) DECLSPEC_HIDDEN; +void wined3d_context_gl_set_draw_buffer(struct wined3d_context_gl *context_gl, GLenum buffer) DECLSPEC_HIDDEN; +void wined3d_context_gl_submit_command_fence(struct wined3d_context_gl *context_gl) DECLSPEC_HIDDEN; +void wined3d_context_gl_texture_update(struct wined3d_context_gl *context_gl, + const struct wined3d_texture_gl *texture_gl) DECLSPEC_HIDDEN; +void wined3d_context_gl_unload_tex_coords(const struct wined3d_context_gl *context_gl) DECLSPEC_HIDDEN; +void wined3d_context_gl_unmap_bo_address(struct wined3d_context_gl *context_gl, const struct wined3d_bo_address *data, + unsigned int range_count, const struct wined3d_range *ranges) DECLSPEC_HIDDEN; +void wined3d_context_gl_update_stream_sources(struct wined3d_context_gl *context_gl, + const struct wined3d_state *state) DECLSPEC_HIDDEN; +void wined3d_context_gl_wait_command_fence(struct wined3d_context_gl *context_gl, uint64_t id) DECLSPEC_HIDDEN; + +struct wined3d_command_buffer_vk +{ + uint64_t id; + VkCommandBuffer vk_command_buffer; + VkFence vk_fence; +}; + +enum wined3d_retired_object_type_vk +{ + WINED3D_RETIRED_FREE_VK, + WINED3D_RETIRED_FRAMEBUFFER_VK, + WINED3D_RETIRED_DESCRIPTOR_POOL_VK, + WINED3D_RETIRED_MEMORY_VK, + WINED3D_RETIRED_ALLOCATOR_BLOCK_VK, + WINED3D_RETIRED_BO_SLAB_SLICE_VK, + WINED3D_RETIRED_BUFFER_VK, + WINED3D_RETIRED_IMAGE_VK, + WINED3D_RETIRED_BUFFER_VIEW_VK, + WINED3D_RETIRED_IMAGE_VIEW_VK, + WINED3D_RETIRED_SAMPLER_VK, +}; + +struct wined3d_retired_object_vk +{ + enum wined3d_retired_object_type_vk type; + union + { + struct wined3d_retired_object_vk *next; + VkFramebuffer vk_framebuffer; + VkDescriptorPool vk_descriptor_pool; + VkDeviceMemory vk_memory; + struct wined3d_allocator_block *block; + struct + { + struct wined3d_bo_slab_vk *slab; + size_t idx; + } slice; + VkBuffer vk_buffer; + VkImage vk_image; + VkBufferView vk_buffer_view; + VkImageView vk_image_view; + VkSampler vk_sampler; + } u; + uint64_t command_buffer_id; +}; + +struct wined3d_retired_objects_vk +{ + struct wined3d_retired_object_vk *objects; + struct wined3d_retired_object_vk *free; + SIZE_T size; + SIZE_T count; +}; + +struct wined3d_render_pass_attachment_vk +{ + VkFormat vk_format; + VkSampleCountFlagBits vk_samples; + VkImageLayout vk_layout; +}; + +struct wined3d_render_pass_key_vk +{ + struct wined3d_render_pass_attachment_vk rt[WINED3D_MAX_RENDER_TARGETS]; + struct wined3d_render_pass_attachment_vk ds; + uint32_t rt_mask; + uint32_t clear_flags; +}; + +struct wined3d_render_pass_vk +{ + struct wine_rb_entry entry; + struct wined3d_render_pass_key_vk key; + VkRenderPass vk_render_pass; +}; + +struct wined3d_pipeline_layout_key_vk +{ + VkDescriptorSetLayoutBinding *bindings; + SIZE_T binding_count; +}; + +struct wined3d_pipeline_layout_vk +{ + struct wine_rb_entry entry; + struct wined3d_pipeline_layout_key_vk key; + VkPipelineLayout vk_pipeline_layout; + VkDescriptorSetLayout vk_set_layout; +}; + +struct wined3d_graphics_pipeline_key_vk +{ + VkPipelineShaderStageCreateInfo stages[WINED3D_SHADER_TYPE_GRAPHICS_COUNT]; + VkVertexInputBindingDivisorDescriptionEXT divisors[MAX_ATTRIBS]; + VkVertexInputAttributeDescription attributes[MAX_ATTRIBS]; + VkVertexInputBindingDescription bindings[MAX_ATTRIBS]; + VkViewport viewport; + VkRect2D scissor; + VkSampleMask sample_mask; + VkPipelineColorBlendAttachmentState blend_attachments[WINED3D_MAX_RENDER_TARGETS]; + + VkPipelineVertexInputDivisorStateCreateInfoEXT divisor_desc; + VkPipelineVertexInputStateCreateInfo input_desc; + VkPipelineInputAssemblyStateCreateInfo ia_desc; + VkPipelineTessellationStateCreateInfo ts_desc; + VkPipelineViewportStateCreateInfo vp_desc; + VkPipelineRasterizationStateCreateInfo rs_desc; + VkPipelineMultisampleStateCreateInfo ms_desc; + VkPipelineDepthStencilStateCreateInfo ds_desc; + VkPipelineColorBlendStateCreateInfo blend_desc; + VkPipelineDynamicStateCreateInfo dynamic_desc; + + VkGraphicsPipelineCreateInfo pipeline_desc; +}; + +struct wined3d_graphics_pipeline_vk +{ + struct wine_rb_entry entry; + struct wined3d_graphics_pipeline_key_vk key; + VkPipeline vk_pipeline; +}; + +enum wined3d_shader_descriptor_type +{ + WINED3D_SHADER_DESCRIPTOR_TYPE_CBV, + WINED3D_SHADER_DESCRIPTOR_TYPE_SRV, + WINED3D_SHADER_DESCRIPTOR_TYPE_UAV, + WINED3D_SHADER_DESCRIPTOR_TYPE_UAV_COUNTER, + WINED3D_SHADER_DESCRIPTOR_TYPE_SAMPLER, +}; + +struct wined3d_shader_resource_binding +{ + enum wined3d_shader_type shader_type; + enum wined3d_shader_descriptor_type shader_descriptor_type; + size_t resource_idx; + enum wined3d_shader_resource_type resource_type; + enum wined3d_data_type resource_data_type; + size_t binding_idx; +}; + +struct wined3d_shader_resource_bindings +{ + struct wined3d_shader_resource_binding *bindings; + SIZE_T size, count; +}; + +struct wined3d_shader_descriptor_writes_vk +{ + VkWriteDescriptorSet *writes; + SIZE_T size, count; +}; + +struct wined3d_pending_query_vk +{ + struct wined3d_query_vk *query_vk; + struct wined3d_query_pool_idx_vk pool_idx; +}; + +struct wined3d_pending_queries_vk +{ + struct wined3d_pending_query_vk *queries; + SIZE_T free_idx; + SIZE_T size; + SIZE_T count; +}; + +struct wined3d_context_vk +{ + struct wined3d_context c; + + const struct wined3d_vk_info *vk_info; + + uint32_t update_compute_pipeline : 1; + uint32_t update_stream_output : 1; + uint32_t padding : 30; + + struct + { + VkShaderModule vk_modules[WINED3D_SHADER_TYPE_GRAPHICS_COUNT]; + struct wined3d_graphics_pipeline_key_vk pipeline_key_vk; + VkPipeline vk_pipeline; + VkPipelineLayout vk_pipeline_layout; + VkDescriptorSetLayout vk_set_layout; + struct wined3d_shader_resource_bindings bindings; + } graphics; + + struct + { + VkPipeline vk_pipeline; + VkPipelineLayout vk_pipeline_layout; + VkDescriptorSetLayout vk_set_layout; + struct wined3d_shader_resource_bindings bindings; + } compute; + + VkCommandPool vk_command_pool; + struct wined3d_command_buffer_vk current_command_buffer; + uint64_t completed_command_buffer_id; + + struct + { + struct wined3d_command_buffer_vk *buffers; + SIZE_T buffers_size; + SIZE_T buffer_count; + } submitted; + + struct wined3d_shader_descriptor_writes_vk descriptor_writes; + + VkFramebuffer vk_framebuffer; + VkRenderPass vk_render_pass; + VkDescriptorPool vk_descriptor_pool; + + VkSampleCountFlagBits sample_count; + unsigned int rt_count; + + VkBuffer vk_so_counters[WINED3D_MAX_STREAM_OUTPUT_BUFFERS]; + VkDeviceSize vk_so_offsets[WINED3D_MAX_STREAM_OUTPUT_BUFFERS]; + struct wined3d_bo_vk vk_so_counter_bo; + + struct list active_queries; + struct wined3d_pending_queries_vk pending_queries; + struct list free_occlusion_query_pools; + struct list free_timestamp_query_pools; + struct list free_pipeline_statistics_query_pools; + struct list free_stream_output_statistics_query_pools; + + struct wined3d_retired_objects_vk retired; + struct wine_rb_tree render_passes; + struct wine_rb_tree pipeline_layouts; + struct wine_rb_tree graphics_pipelines; + struct wine_rb_tree bo_slab_available; +}; + +static inline struct wined3d_context_vk *wined3d_context_vk(struct wined3d_context *context) +{ + return CONTAINING_RECORD(context, struct wined3d_context_vk, c); +} + +void wined3d_context_vk_accumulate_pending_queries(struct wined3d_context_vk *context_vk) DECLSPEC_HIDDEN; +void wined3d_context_vk_add_pending_query(struct wined3d_context_vk *context_vk, + struct wined3d_query_vk *query_vk) DECLSPEC_HIDDEN; +struct wined3d_allocator_block *wined3d_context_vk_allocate_memory(struct wined3d_context_vk *context_vk, + unsigned int memory_type, VkDeviceSize size, VkDeviceMemory *vk_memory) DECLSPEC_HIDDEN; +bool wined3d_context_vk_allocate_query(struct wined3d_context_vk *context_vk, + enum wined3d_query_type type, struct wined3d_query_pool_idx_vk *pool_idx) DECLSPEC_HIDDEN; +VkDeviceMemory wined3d_context_vk_allocate_vram_chunk_memory(struct wined3d_context_vk *context_vk, + unsigned int pool, size_t size) DECLSPEC_HIDDEN; +VkCommandBuffer wined3d_context_vk_apply_compute_state(struct wined3d_context_vk *context_vk, + const struct wined3d_state *state, struct wined3d_buffer_vk *indirect_vk) DECLSPEC_HIDDEN; +VkCommandBuffer wined3d_context_vk_apply_draw_state(struct wined3d_context_vk *context_vk, + const struct wined3d_state *state, struct wined3d_buffer_vk *indirect_vk, bool indexed) DECLSPEC_HIDDEN; +void wined3d_context_vk_cleanup(struct wined3d_context_vk *context_vk) DECLSPEC_HIDDEN; +BOOL wined3d_context_vk_create_bo(struct wined3d_context_vk *context_vk, VkDeviceSize size, + VkBufferUsageFlags usage, VkMemoryPropertyFlags memory_type, struct wined3d_bo_vk *bo) DECLSPEC_HIDDEN; +void wined3d_context_vk_destroy_allocator_block(struct wined3d_context_vk *context_vk, + struct wined3d_allocator_block *block, uint64_t command_buffer_id) DECLSPEC_HIDDEN; +void wined3d_context_vk_destroy_bo(struct wined3d_context_vk *context_vk, + const struct wined3d_bo_vk *bo) DECLSPEC_HIDDEN; +void wined3d_context_vk_destroy_buffer_view(struct wined3d_context_vk *context_vk, + VkBufferView vk_view, uint64_t command_buffer_id) DECLSPEC_HIDDEN; +void wined3d_context_vk_destroy_framebuffer(struct wined3d_context_vk *context_vk, + VkFramebuffer vk_framebuffer, uint64_t command_buffer_id) DECLSPEC_HIDDEN; +void wined3d_context_vk_destroy_image(struct wined3d_context_vk *context_vk, + VkImage vk_image, uint64_t command_buffer_id) DECLSPEC_HIDDEN; +void wined3d_context_vk_destroy_image_view(struct wined3d_context_vk *context_vk, + VkImageView vk_view, uint64_t command_buffer_id) DECLSPEC_HIDDEN; +void wined3d_context_vk_destroy_memory(struct wined3d_context_vk *context_vk, + VkDeviceMemory vk_memory, uint64_t command_buffer_id) DECLSPEC_HIDDEN; +void wined3d_context_vk_destroy_sampler(struct wined3d_context_vk *context_vk, + VkSampler vk_sampler, uint64_t command_buffer_id) DECLSPEC_HIDDEN; +void wined3d_context_vk_end_current_render_pass(struct wined3d_context_vk *context_vk) DECLSPEC_HIDDEN; +VkCommandBuffer wined3d_context_vk_get_command_buffer(struct wined3d_context_vk *context_vk) DECLSPEC_HIDDEN; +struct wined3d_pipeline_layout_vk *wined3d_context_vk_get_pipeline_layout(struct wined3d_context_vk *context_vk, + VkDescriptorSetLayoutBinding *bindings, SIZE_T binding_count) DECLSPEC_HIDDEN; +VkRenderPass wined3d_context_vk_get_render_pass(struct wined3d_context_vk *context_vk, + const struct wined3d_fb_state *fb, unsigned int rt_count, + bool depth_stencil, uint32_t clear_flags) DECLSPEC_HIDDEN; +void wined3d_context_vk_image_barrier(struct wined3d_context_vk *context_vk, + VkCommandBuffer vk_command_buffer, VkPipelineStageFlags src_stage_mask, VkPipelineStageFlags dst_stage_mask, + VkAccessFlags src_access_mask, VkAccessFlags dst_access_mask, VkImageLayout old_layout, + VkImageLayout new_layout, VkImage image, VkImageAspectFlags aspect_mask) DECLSPEC_HIDDEN; +HRESULT wined3d_context_vk_init(struct wined3d_context_vk *context_vk, + struct wined3d_swapchain *swapchain) DECLSPEC_HIDDEN; +void wined3d_context_vk_poll_command_buffers(struct wined3d_context_vk *context_vk) DECLSPEC_HIDDEN; +void wined3d_context_vk_remove_pending_queries(struct wined3d_context_vk *context_vk, + struct wined3d_query_vk *query_vk) DECLSPEC_HIDDEN; +void wined3d_context_vk_submit_command_buffer(struct wined3d_context_vk *context_vk, + unsigned int wait_semaphore_count, const VkSemaphore *wait_semaphores, const VkPipelineStageFlags *wait_stages, + unsigned int signal_semaphore_count, const VkSemaphore *signal_semaphores) DECLSPEC_HIDDEN; +void wined3d_context_vk_wait_command_buffer(struct wined3d_context_vk *context_vk, uint64_t id) DECLSPEC_HIDDEN; + +typedef void (*APPLYSTATEFUNC)(struct wined3d_context *ctx, const struct wined3d_state *state, DWORD state_id); + +struct wined3d_state_entry +{ + unsigned int representative; + APPLYSTATEFUNC apply; +}; + +struct wined3d_state_entry_template +{ + DWORD state; + struct wined3d_state_entry content; + unsigned int extension; +}; + +#define WINED3D_FRAGMENT_CAP_PROJ_CONTROL 0x00000001 +#define WINED3D_FRAGMENT_CAP_SRGB_WRITE 0x00000002 +#define WINED3D_FRAGMENT_CAP_COLOR_KEY 0x00000004 + +struct fragment_caps +{ + DWORD wined3d_caps; + DWORD PrimitiveMiscCaps; + DWORD TextureOpCaps; + DWORD MaxTextureBlendStages; + DWORD MaxSimultaneousTextures; +}; + +#define GL_EXT_EMUL_ARB_MULTITEXTURE 0x00000001 +#define GL_EXT_EMUL_EXT_FOG_COORD 0x00000002 + +struct wined3d_fragment_pipe_ops +{ + void (*fp_enable)(const struct wined3d_context *context, BOOL enable); + void (*get_caps)(const struct wined3d_adapter *adapter, struct fragment_caps *caps); + DWORD (*get_emul_mask)(const struct wined3d_gl_info *gl_info); + void *(*alloc_private)(const struct wined3d_shader_backend_ops *shader_backend, void *shader_priv); + void (*free_private)(struct wined3d_device *device, struct wined3d_context *context); + BOOL (*allocate_context_data)(struct wined3d_context *context); + void (*free_context_data)(struct wined3d_context *context); + BOOL (*color_fixup_supported)(struct color_fixup_desc fixup); + const struct wined3d_state_entry_template *states; +}; + +struct wined3d_vertex_caps +{ + BOOL xyzrhw; + BOOL emulated_flatshading; + BOOL ffp_generic_attributes; + DWORD max_active_lights; + DWORD max_vertex_blend_matrices; + DWORD max_vertex_blend_matrix_index; + DWORD vertex_processing_caps; + DWORD fvf_caps; + DWORD max_user_clip_planes; + DWORD raster_caps; +}; + +struct wined3d_vertex_pipe_ops +{ + void (*vp_enable)(const struct wined3d_context *context, BOOL enable); + void (*vp_get_caps)(const struct wined3d_adapter *adapter, struct wined3d_vertex_caps *caps); + DWORD (*vp_get_emul_mask)(const struct wined3d_gl_info *gl_info); + void *(*vp_alloc)(const struct wined3d_shader_backend_ops *shader_backend, void *shader_priv); + void (*vp_free)(struct wined3d_device *device, struct wined3d_context *context); + const struct wined3d_state_entry_template *vp_states; +}; + +extern const struct wined3d_state_entry_template misc_state_template_gl[] DECLSPEC_HIDDEN; +extern const struct wined3d_fragment_pipe_ops none_fragment_pipe DECLSPEC_HIDDEN; +extern const struct wined3d_fragment_pipe_ops ffp_fragment_pipeline DECLSPEC_HIDDEN; +extern const struct wined3d_fragment_pipe_ops atifs_fragment_pipeline DECLSPEC_HIDDEN; +extern const struct wined3d_fragment_pipe_ops arbfp_fragment_pipeline DECLSPEC_HIDDEN; +extern const struct wined3d_fragment_pipe_ops nvts_fragment_pipeline DECLSPEC_HIDDEN; +extern const struct wined3d_fragment_pipe_ops nvrc_fragment_pipeline DECLSPEC_HIDDEN; +extern const struct wined3d_fragment_pipe_ops glsl_fragment_pipe DECLSPEC_HIDDEN; + +const struct wined3d_fragment_pipe_ops *wined3d_spirv_fragment_pipe_init_vk(void) DECLSPEC_HIDDEN; + +extern const struct wined3d_vertex_pipe_ops none_vertex_pipe DECLSPEC_HIDDEN; +extern const struct wined3d_vertex_pipe_ops ffp_vertex_pipe DECLSPEC_HIDDEN; +extern const struct wined3d_vertex_pipe_ops glsl_vertex_pipe DECLSPEC_HIDDEN; + +const struct wined3d_vertex_pipe_ops *wined3d_spirv_vertex_pipe_init_vk(void) DECLSPEC_HIDDEN; + +/* "Base" state table */ +HRESULT compile_state_table(struct wined3d_state_entry *state_table, APPLYSTATEFUNC **dev_multistate_funcs, + const struct wined3d_d3d_info *d3d_info, const BOOL *supported_extensions, + const struct wined3d_vertex_pipe_ops *vertex, const struct wined3d_fragment_pipe_ops *fragment, + const struct wined3d_state_entry_template *misc) DECLSPEC_HIDDEN; + +enum wined3d_blit_op +{ + WINED3D_BLIT_OP_COLOR_BLIT, + WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST, + WINED3D_BLIT_OP_COLOR_BLIT_CKEY, + WINED3D_BLIT_OP_DEPTH_BLIT, + WINED3D_BLIT_OP_RAW_BLIT, +}; + +struct wined3d_blitter +{ + const struct wined3d_blitter_ops *ops; + struct wined3d_blitter *next; +}; + +struct wined3d_blitter_ops +{ + void (*blitter_destroy)(struct wined3d_blitter *blitter, struct wined3d_context *context); + void (*blitter_clear)(struct wined3d_blitter *blitter, struct wined3d_device *device, + unsigned int rt_count, const struct wined3d_fb_state *fb, unsigned int rect_count, const RECT *clear_rects, + const RECT *draw_rect, DWORD flags, const struct wined3d_color *colour, float depth, DWORD stencil); + DWORD (*blitter_blit)(struct wined3d_blitter *blitter, enum wined3d_blit_op op, struct wined3d_context *context, + struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx, DWORD src_location, + const RECT *src_rect, struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx, + DWORD dst_location, const RECT *dst_rect, const struct wined3d_color_key *colour_key, + enum wined3d_texture_filter_type filter); +}; + +void wined3d_arbfp_blitter_create(struct wined3d_blitter **next, + const struct wined3d_device *device) DECLSPEC_HIDDEN; +struct wined3d_blitter *wined3d_cpu_blitter_create(void) DECLSPEC_HIDDEN; +void wined3d_fbo_blitter_create(struct wined3d_blitter **next, + const struct wined3d_gl_info *gl_info) DECLSPEC_HIDDEN; +void wined3d_ffp_blitter_create(struct wined3d_blitter **next, + const struct wined3d_gl_info *gl_info) DECLSPEC_HIDDEN; +struct wined3d_blitter *wined3d_glsl_blitter_create(struct wined3d_blitter **next, + const struct wined3d_device *device) DECLSPEC_HIDDEN; +void wined3d_raw_blitter_create(struct wined3d_blitter **next, + const struct wined3d_gl_info *gl_info) DECLSPEC_HIDDEN; +void wined3d_vk_blitter_create(struct wined3d_blitter **next) DECLSPEC_HIDDEN; + +BOOL wined3d_clip_blit(const RECT *clip_rect, RECT *clipped, RECT *other) DECLSPEC_HIDDEN; + +HGLRC context_create_wgl_attribs(const struct wined3d_gl_info *gl_info, HDC hdc, HGLRC share_ctx) DECLSPEC_HIDDEN; +DWORD context_get_tls_idx(void) DECLSPEC_HIDDEN; +void context_gl_resource_released(struct wined3d_device *device, + GLuint name, BOOL rb_namespace) DECLSPEC_HIDDEN; +void context_invalidate_compute_state(struct wined3d_context *context, DWORD state_id) DECLSPEC_HIDDEN; +void context_invalidate_state(struct wined3d_context *context, DWORD state_id) DECLSPEC_HIDDEN; +void context_resource_released(const struct wined3d_device *device, struct wined3d_resource *resource) DECLSPEC_HIDDEN; +void context_restore(struct wined3d_context *context, struct wined3d_texture *texture, + unsigned int sub_resource_idx) DECLSPEC_HIDDEN; +void context_set_tls_idx(DWORD idx) DECLSPEC_HIDDEN; +void context_state_drawbuf(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) DECLSPEC_HIDDEN; +void context_state_fb(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) DECLSPEC_HIDDEN; + +/***************************************************************************** + * Internal representation of a light + */ +struct wined3d_light_info +{ + struct wined3d_light OriginalParms; /* Note D3D8LIGHT == D3D9LIGHT */ + DWORD OriginalIndex; + LONG glIndex; + BOOL enabled; + + /* Converted parms to speed up swapping lights */ + struct wined3d_vec4 position; + struct wined3d_vec4 direction; + float exponent; + float cutoff; + + struct list entry; +}; + +/* The default light parameters */ +extern const struct wined3d_light WINED3D_default_light DECLSPEC_HIDDEN; + +struct wined3d_pixel_format +{ + int iPixelFormat; /* WGL pixel format */ + int iPixelType; /* WGL pixel type e.g. WGL_TYPE_RGBA_ARB, WGL_TYPE_RGBA_FLOAT_ARB or WGL_TYPE_COLORINDEX_ARB */ + int redSize, greenSize, blueSize, alphaSize, colorSize; + int depthSize, stencilSize; + BOOL windowDrawable; + BOOL doubleBuffer; + int auxBuffers; + int numSamples; +}; + +enum wined3d_pci_vendor +{ + HW_VENDOR_SOFTWARE = 0x0000, + HW_VENDOR_AMD = 0x1002, + HW_VENDOR_NVIDIA = 0x10de, + HW_VENDOR_VMWARE = 0x15ad, + HW_VENDOR_REDHAT = 0x1af4, + HW_VENDOR_INTEL = 0x8086, +}; + +enum wined3d_pci_device +{ + CARD_WINE = 0x0000, + + CARD_AMD_RAGE_128PRO = 0x5246, + CARD_AMD_RADEON_7200 = 0x5144, + CARD_AMD_RADEON_8500 = 0x514c, + CARD_AMD_RADEON_9500 = 0x4144, + CARD_AMD_RADEON_XPRESS_200M = 0x5955, + CARD_AMD_RADEON_X700 = 0x5e4c, + CARD_AMD_RADEON_X1600 = 0x71c2, + CARD_AMD_RADEON_HD2350 = 0x94c7, + CARD_AMD_RADEON_HD2600 = 0x9581, + CARD_AMD_RADEON_HD2900 = 0x9400, + CARD_AMD_RADEON_HD3200 = 0x9620, + CARD_AMD_RADEON_HD3850 = 0x9515, + CARD_AMD_RADEON_HD4200M = 0x9712, + CARD_AMD_RADEON_HD4350 = 0x954f, + CARD_AMD_RADEON_HD4600 = 0x9495, + CARD_AMD_RADEON_HD4700 = 0x944e, + CARD_AMD_RADEON_HD4800 = 0x944c, + CARD_AMD_RADEON_HD5400 = 0x68f9, + CARD_AMD_RADEON_HD5600 = 0x68d8, + CARD_AMD_RADEON_HD5700 = 0x68be, + CARD_AMD_RADEON_HD5800 = 0x6898, + CARD_AMD_RADEON_HD5900 = 0x689c, + CARD_AMD_RADEON_HD6300 = 0x9803, + CARD_AMD_RADEON_HD6400 = 0x6770, + CARD_AMD_RADEON_HD6490M = 0x6760, + CARD_AMD_RADEON_HD6410D = 0x9644, + CARD_AMD_RADEON_HD6480G = 0x9648, + CARD_AMD_RADEON_HD6550D = 0x9640, + CARD_AMD_RADEON_HD6600 = 0x6758, + CARD_AMD_RADEON_HD6600M = 0x6741, + CARD_AMD_RADEON_HD6700 = 0x68ba, + CARD_AMD_RADEON_HD6800 = 0x6739, + CARD_AMD_RADEON_HD6900 = 0x6719, + CARD_AMD_RADEON_HD7660D = 0x9901, + CARD_AMD_RADEON_HD7700 = 0x683d, + CARD_AMD_RADEON_HD7800 = 0x6819, + CARD_AMD_RADEON_HD7870 = 0x6818, + CARD_AMD_RADEON_HD7900 = 0x679a, + CARD_AMD_RADEON_HD8600M = 0x6660, + CARD_AMD_RADEON_HD8670 = 0x6610, + CARD_AMD_RADEON_HD8770 = 0x665c, + CARD_AMD_RADEON_R3 = 0x9830, + CARD_AMD_RADEON_R7 = 0x130f, + CARD_AMD_RADEON_R9_285 = 0x6939, + CARD_AMD_RADEON_R9_290 = 0x67b1, + CARD_AMD_RADEON_R9_290X = 0x67b0, + CARD_AMD_RADEON_R9_FURY = 0x7300, + CARD_AMD_RADEON_R9_M370X = 0x6821, + CARD_AMD_RADEON_R9_M380 = 0x6647, + CARD_AMD_RADEON_R9_M395X = 0x6920, + CARD_AMD_RADEON_RX_460 = 0x67ef, + CARD_AMD_RADEON_RX_480 = 0x67df, + CARD_AMD_RADEON_RX_VEGA_10 = 0x687f, + CARD_AMD_RADEON_RX_VEGA_12 = 0x69af, + CARD_AMD_RADEON_RAVEN = 0x15dd, + CARD_AMD_RADEON_RX_VEGA_20 = 0x66af, + CARD_AMD_RADEON_RX_NAVI_10 = 0x731f, + CARD_AMD_RADEON_RX_NAVI_14 = 0x7340, + + CARD_NVIDIA_RIVA_128 = 0x0018, + CARD_NVIDIA_RIVA_TNT = 0x0020, + CARD_NVIDIA_RIVA_TNT2 = 0x0028, + CARD_NVIDIA_GEFORCE = 0x0100, + CARD_NVIDIA_GEFORCE2_MX = 0x0110, + CARD_NVIDIA_GEFORCE2 = 0x0150, + CARD_NVIDIA_GEFORCE3 = 0x0200, + CARD_NVIDIA_GEFORCE4_MX = 0x0170, + CARD_NVIDIA_GEFORCE4_TI4200 = 0x0253, + CARD_NVIDIA_GEFORCEFX_5200 = 0x0320, + CARD_NVIDIA_GEFORCEFX_5600 = 0x0312, + CARD_NVIDIA_GEFORCEFX_5800 = 0x0302, + CARD_NVIDIA_GEFORCE_6200 = 0x014f, + CARD_NVIDIA_GEFORCE_6600GT = 0x0140, + CARD_NVIDIA_GEFORCE_6800 = 0x0041, + CARD_NVIDIA_GEFORCE_7300 = 0x01d7, /* GeForce Go 7300 */ + CARD_NVIDIA_GEFORCE_7400 = 0x01d8, + CARD_NVIDIA_GEFORCE_7600 = 0x0391, + CARD_NVIDIA_GEFORCE_7800GT = 0x0092, + CARD_NVIDIA_GEFORCE_8200 = 0x0849, /* Other PCI ID 0x084b */ + CARD_NVIDIA_GEFORCE_8300GS = 0x0423, + CARD_NVIDIA_GEFORCE_8400GS = 0x0404, + CARD_NVIDIA_GEFORCE_8500GT = 0x0421, + CARD_NVIDIA_GEFORCE_8600GT = 0x0402, + CARD_NVIDIA_GEFORCE_8600MGT = 0x0407, + CARD_NVIDIA_GEFORCE_8800GTS = 0x0193, + CARD_NVIDIA_GEFORCE_8800GTX = 0x0191, + CARD_NVIDIA_GEFORCE_9200 = 0x086d, + CARD_NVIDIA_GEFORCE_9300 = 0x086c, + CARD_NVIDIA_GEFORCE_9400M = 0x0863, + CARD_NVIDIA_GEFORCE_9400GT = 0x042c, + CARD_NVIDIA_GEFORCE_9500GT = 0x0640, + CARD_NVIDIA_GEFORCE_9600GT = 0x0622, + CARD_NVIDIA_GEFORCE_9700MGT = 0x064a, + CARD_NVIDIA_GEFORCE_9800GT = 0x0614, + CARD_NVIDIA_GEFORCE_210 = 0x0a23, + CARD_NVIDIA_GEFORCE_GT220 = 0x0a20, + CARD_NVIDIA_GEFORCE_GT240 = 0x0ca3, + CARD_NVIDIA_GEFORCE_GTS250 = 0x0615, + CARD_NVIDIA_GEFORCE_GTX260 = 0x05e2, + CARD_NVIDIA_GEFORCE_GTX275 = 0x05e6, + CARD_NVIDIA_GEFORCE_GTX280 = 0x05e1, + CARD_NVIDIA_GEFORCE_315M = 0x0a7a, + CARD_NVIDIA_GEFORCE_320M = 0x08a3, + CARD_NVIDIA_GEFORCE_GT320M = 0x0a2d, + CARD_NVIDIA_GEFORCE_GT325M = 0x0a35, + CARD_NVIDIA_GEFORCE_GT330 = 0x0ca0, + CARD_NVIDIA_GEFORCE_GTS350M = 0x0cb0, + CARD_NVIDIA_GEFORCE_410M = 0x1055, + CARD_NVIDIA_GEFORCE_GT420 = 0x0de2, + CARD_NVIDIA_GEFORCE_GT425M = 0x0df0, + CARD_NVIDIA_GEFORCE_GT430 = 0x0de1, + CARD_NVIDIA_GEFORCE_GT440 = 0x0de0, + CARD_NVIDIA_GEFORCE_GTS450 = 0x0dc4, + CARD_NVIDIA_GEFORCE_GTX460 = 0x0e22, + CARD_NVIDIA_GEFORCE_GTX460M = 0x0dd1, + CARD_NVIDIA_GEFORCE_GTX465 = 0x06c4, + CARD_NVIDIA_GEFORCE_GTX470 = 0x06cd, + CARD_NVIDIA_GEFORCE_GTX480 = 0x06c0, + CARD_NVIDIA_GEFORCE_GT520 = 0x1040, + CARD_NVIDIA_GEFORCE_GT525M = 0x0dec, + CARD_NVIDIA_GEFORCE_GT540M = 0x0df4, + CARD_NVIDIA_GEFORCE_GTX550 = 0x1244, + CARD_NVIDIA_GEFORCE_GT555M = 0x04b8, + CARD_NVIDIA_GEFORCE_GTX560TI = 0x1200, + CARD_NVIDIA_GEFORCE_GTX560M = 0x1251, + CARD_NVIDIA_GEFORCE_GTX560 = 0x1201, + CARD_NVIDIA_GEFORCE_GTX570 = 0x1081, + CARD_NVIDIA_GEFORCE_GTX580 = 0x1080, + CARD_NVIDIA_GEFORCE_GT610 = 0x104a, + CARD_NVIDIA_GEFORCE_GT630 = 0x0f00, + CARD_NVIDIA_GEFORCE_GT630M = 0x0de9, + CARD_NVIDIA_GEFORCE_GT640 = 0x0fc1, + CARD_NVIDIA_GEFORCE_GT640M = 0x0fd2, + CARD_NVIDIA_GEFORCE_GT650M = 0x0fd1, + CARD_NVIDIA_GEFORCE_GTX650 = 0x0fc6, + CARD_NVIDIA_GEFORCE_GTX650TI = 0x11c6, + CARD_NVIDIA_GEFORCE_GTX660 = 0x11c0, + CARD_NVIDIA_GEFORCE_GTX660M = 0x0fd4, + CARD_NVIDIA_GEFORCE_GTX660TI = 0x1183, + CARD_NVIDIA_GEFORCE_GTX670 = 0x1189, + CARD_NVIDIA_GEFORCE_GTX670MX = 0x11a1, + CARD_NVIDIA_GEFORCE_GTX675MX_1 = 0x11a7, + CARD_NVIDIA_GEFORCE_GTX675MX_2 = 0x11a2, + CARD_NVIDIA_GEFORCE_GTX680 = 0x1180, + CARD_NVIDIA_GEFORCE_GTX690 = 0x1188, + CARD_NVIDIA_GEFORCE_GT720 = 0x128b, + CARD_NVIDIA_GEFORCE_GT730 = 0x1287, + CARD_NVIDIA_GEFORCE_GT730M = 0x0fe1, + CARD_NVIDIA_GEFORCE_GT740M = 0x1292, + CARD_NVIDIA_GEFORCE_GT750M = 0x0fe9, + CARD_NVIDIA_GEFORCE_GT755M = 0x0fcd, + CARD_NVIDIA_GEFORCE_GTX750 = 0x1381, + CARD_NVIDIA_GEFORCE_GTX750TI = 0x1380, + CARD_NVIDIA_GEFORCE_GTX760 = 0x1187, + CARD_NVIDIA_GEFORCE_GTX760TI = 0x1193, + CARD_NVIDIA_GEFORCE_GTX765M = 0x11e2, + CARD_NVIDIA_GEFORCE_GTX770M = 0x11e0, + CARD_NVIDIA_GEFORCE_GTX770 = 0x1184, + CARD_NVIDIA_GEFORCE_GTX775M = 0x119d, + CARD_NVIDIA_GEFORCE_GTX780 = 0x1004, + CARD_NVIDIA_GEFORCE_GTX780M = 0x119e, + CARD_NVIDIA_GEFORCE_GTX780TI = 0x100a, + CARD_NVIDIA_GEFORCE_GTXTITAN = 0x1005, + CARD_NVIDIA_GEFORCE_GTXTITANB = 0x100c, + CARD_NVIDIA_GEFORCE_GTXTITANX = 0x17c2, + CARD_NVIDIA_GEFORCE_GTXTITANZ = 0x1001, + CARD_NVIDIA_GEFORCE_820M = 0x0fed, + CARD_NVIDIA_GEFORCE_830M = 0x1340, + CARD_NVIDIA_GEFORCE_840M = 0x1341, + CARD_NVIDIA_GEFORCE_845M = 0x1344, + CARD_NVIDIA_GEFORCE_GTX850M = 0x1391, + CARD_NVIDIA_GEFORCE_GTX860M = 0x1392, /* Other PCI ID 0x119a */ + CARD_NVIDIA_GEFORCE_GTX870M = 0x1199, + CARD_NVIDIA_GEFORCE_GTX880M = 0x1198, + CARD_NVIDIA_GEFORCE_940M = 0x1347, + CARD_NVIDIA_GEFORCE_GTX950 = 0x1402, + CARD_NVIDIA_GEFORCE_GTX950M = 0x139a, + CARD_NVIDIA_GEFORCE_GTX960 = 0x1401, + CARD_NVIDIA_GEFORCE_GTX960M = 0x139b, + CARD_NVIDIA_GEFORCE_GTX970 = 0x13c2, + CARD_NVIDIA_GEFORCE_GTX970M = 0x13d8, + CARD_NVIDIA_GEFORCE_GTX980 = 0x13c0, + CARD_NVIDIA_GEFORCE_GTX980TI = 0x17c8, + CARD_NVIDIA_GEFORCE_GTX1050 = 0x1c81, + CARD_NVIDIA_GEFORCE_GTX1050TI = 0x1c82, + CARD_NVIDIA_GEFORCE_GTX1060_3GB = 0x1c02, + CARD_NVIDIA_GEFORCE_GTX1060 = 0x1c03, + CARD_NVIDIA_GEFORCE_GTX1060M = 0x1c20, + CARD_NVIDIA_GEFORCE_GTX1070 = 0x1b81, + CARD_NVIDIA_GEFORCE_GTX1080 = 0x1b80, + CARD_NVIDIA_GEFORCE_GTX1080M = 0x1be0, + CARD_NVIDIA_GEFORCE_GTX1080TI = 0x1b06, + CARD_NVIDIA_TITANX_PASCAL = 0x1b00, + CARD_NVIDIA_TITANV = 0x1d81, + CARD_NVIDIA_GEFORCE_GTX1650SUPER= 0x2187, + CARD_NVIDIA_GEFORCE_GTX1660SUPER= 0x21c4, + CARD_NVIDIA_GEFORCE_GTX1660TI = 0x2182, + CARD_NVIDIA_GEFORCE_RTX2060 = 0x1f08, + CARD_NVIDIA_GEFORCE_RTX2070 = 0x1f07, + CARD_NVIDIA_GEFORCE_RTX2080 = 0x1e87, + CARD_NVIDIA_GEFORCE_RTX2080TI = 0x1e07, + + CARD_REDHAT_VIRGL = 0x1010, + + CARD_VMWARE_SVGA3D = 0x0405, + + CARD_INTEL_830M = 0x3577, + CARD_INTEL_855GM = 0x3582, + CARD_INTEL_845G = 0x2562, + CARD_INTEL_865G = 0x2572, + CARD_INTEL_915G = 0x2582, + CARD_INTEL_E7221G = 0x258a, + CARD_INTEL_915GM = 0x2592, + CARD_INTEL_945G = 0x2772, + CARD_INTEL_945GM = 0x27a2, + CARD_INTEL_945GME = 0x27ae, + CARD_INTEL_Q35 = 0x29b2, + CARD_INTEL_G33 = 0x29c2, + CARD_INTEL_Q33 = 0x29d2, + CARD_INTEL_PNVG = 0xa001, + CARD_INTEL_PNVM = 0xa011, + CARD_INTEL_965Q = 0x2992, + CARD_INTEL_965G = 0x2982, + CARD_INTEL_946GZ = 0x2972, + CARD_INTEL_965GM = 0x2a02, + CARD_INTEL_965GME = 0x2a12, + CARD_INTEL_GM45 = 0x2a42, + CARD_INTEL_IGD = 0x2e02, + CARD_INTEL_Q45 = 0x2e12, + CARD_INTEL_G45 = 0x2e22, + CARD_INTEL_G41 = 0x2e32, + CARD_INTEL_B43 = 0x2e92, + CARD_INTEL_ILKD = 0x0042, + CARD_INTEL_ILKM = 0x0046, + CARD_INTEL_SNBD = 0x0122, + CARD_INTEL_SNBM = 0x0126, + CARD_INTEL_SNBS = 0x010a, + CARD_INTEL_IVBD = 0x0162, + CARD_INTEL_IVBM = 0x0166, + CARD_INTEL_IVBS = 0x015a, + CARD_INTEL_HWD = 0x0412, + CARD_INTEL_HWM = 0x0416, + CARD_INTEL_HD5000_1 = 0x0a26, + CARD_INTEL_HD5000_2 = 0x0422, + CARD_INTEL_I5100_1 = 0x0a22, + CARD_INTEL_I5100_2 = 0x0a2a, + CARD_INTEL_I5100_3 = 0x0a2b, + CARD_INTEL_I5100_4 = 0x0a2e, + CARD_INTEL_IP5200_1 = 0x0d22, + CARD_INTEL_IP5200_2 = 0x0d26, + CARD_INTEL_IP5200_3 = 0x0d2a, + CARD_INTEL_IP5200_4 = 0x0d2b, + CARD_INTEL_IP5200_5 = 0x0d2e, + CARD_INTEL_IP5200_6 = 0x0c22, + CARD_INTEL_HD5300 = 0x161e, + CARD_INTEL_HD5500 = 0x1616, + CARD_INTEL_HD5600 = 0x1612, + CARD_INTEL_HD6000 = 0x1626, + CARD_INTEL_I6100 = 0x162b, + CARD_INTEL_IP6200 = 0x1622, + CARD_INTEL_IPP6300 = 0x162a, + CARD_INTEL_HD510_1 = 0x1902, + CARD_INTEL_HD510_2 = 0x1906, + CARD_INTEL_HD510_3 = 0x190b, + CARD_INTEL_HD515 = 0x191e, + CARD_INTEL_HD520_1 = 0x1916, + CARD_INTEL_HD520_2 = 0x1921, + CARD_INTEL_HD530_1 = 0x1912, + CARD_INTEL_HD530_2 = 0x191b, + CARD_INTEL_HDP530 = 0x191d, + CARD_INTEL_I540 = 0x1926, + CARD_INTEL_I550 = 0x1927, + CARD_INTEL_I555 = 0x192b, + CARD_INTEL_IP555 = 0x192d, + CARD_INTEL_IP580_1 = 0x1932, + CARD_INTEL_IP580_2 = 0x193b, + CARD_INTEL_IPP580_1 = 0x193a, + CARD_INTEL_IPP580_2 = 0x193d, + CARD_INTEL_UHD617 = 0x87c0, + CARD_INTEL_UHD620 = 0x3ea0, + CARD_INTEL_HD615 = 0x591e, + CARD_INTEL_HD620 = 0x5916, + CARD_INTEL_HD630_1 = 0x5912, + CARD_INTEL_HD630_2 = 0x591b, +}; + +struct wined3d_fbo_ops +{ + GLboolean (WINE_GLAPI *glIsRenderbuffer)(GLuint renderbuffer); + void (WINE_GLAPI *glBindRenderbuffer)(GLenum target, GLuint renderbuffer); + void (WINE_GLAPI *glDeleteRenderbuffers)(GLsizei n, const GLuint *renderbuffers); + void (WINE_GLAPI *glGenRenderbuffers)(GLsizei n, GLuint *renderbuffers); + void (WINE_GLAPI *glRenderbufferStorage)(GLenum target, GLenum internalformat, + GLsizei width, GLsizei height); + void (WINE_GLAPI *glRenderbufferStorageMultisample)(GLenum target, GLsizei samples, + GLenum internalformat, GLsizei width, GLsizei height); + void (WINE_GLAPI *glGetRenderbufferParameteriv)(GLenum target, GLenum pname, GLint *params); + GLboolean (WINE_GLAPI *glIsFramebuffer)(GLuint framebuffer); + void (WINE_GLAPI *glBindFramebuffer)(GLenum target, GLuint framebuffer); + void (WINE_GLAPI *glDeleteFramebuffers)(GLsizei n, const GLuint *framebuffers); + void (WINE_GLAPI *glGenFramebuffers)(GLsizei n, GLuint *framebuffers); + GLenum (WINE_GLAPI *glCheckFramebufferStatus)(GLenum target); + void (WINE_GLAPI *glFramebufferTexture)(GLenum target, GLenum attachment, + GLuint texture, GLint level); + void (WINE_GLAPI *glFramebufferTexture1D)(GLenum target, GLenum attachment, + GLenum textarget, GLuint texture, GLint level); + void (WINE_GLAPI *glFramebufferTexture2D)(GLenum target, GLenum attachment, + GLenum textarget, GLuint texture, GLint level); + void (WINE_GLAPI *glFramebufferTexture3D)(GLenum target, GLenum attachment, + GLenum textarget, GLuint texture, GLint level, GLint layer); + void (WINE_GLAPI *glFramebufferTextureLayer)(GLenum target, GLenum attachment, + GLuint texture, GLint level, GLint layer); + void (WINE_GLAPI *glFramebufferRenderbuffer)(GLenum target, GLenum attachment, + GLenum renderbuffertarget, GLuint renderbuffer); + void (WINE_GLAPI *glGetFramebufferAttachmentParameteriv)(GLenum target, GLenum attachment, + GLenum pname, GLint *params); + void (WINE_GLAPI *glBlitFramebuffer)(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, + GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); + void (WINE_GLAPI *glGenerateMipmap)(GLenum target); +}; + +struct wined3d_gl_limits +{ + UINT buffers; + UINT lights; + UINT textures; + UINT texture_coords; + unsigned int uniform_blocks[WINED3D_SHADER_TYPE_COUNT]; + unsigned int samplers[WINED3D_SHADER_TYPE_COUNT]; + unsigned int graphics_samplers; + unsigned int combined_samplers; + UINT general_combiners; + UINT user_clip_distances; + unsigned int texture_size; + UINT texture3d_size; + UINT anisotropy; + float shininess; + UINT samples; + UINT vertex_attribs; + + unsigned int texture_buffer_offset_alignment; + + unsigned int framebuffer_width; + unsigned int framebuffer_height; + + UINT glsl_varyings; + UINT glsl_vs_float_constants; + UINT glsl_ps_float_constants; + + UINT arb_vs_float_constants; + UINT arb_vs_native_constants; + UINT arb_vs_instructions; + UINT arb_vs_temps; + UINT arb_ps_float_constants; + UINT arb_ps_local_constants; + UINT arb_ps_native_constants; + UINT arb_ps_instructions; + UINT arb_ps_temps; +}; + +void wined3d_gl_limits_get_texture_unit_range(const struct wined3d_gl_limits *gl_limits, + enum wined3d_shader_type shader_type, unsigned int *base, unsigned int *count) DECLSPEC_HIDDEN; +void wined3d_gl_limits_get_uniform_block_range(const struct wined3d_gl_limits *gl_limits, + enum wined3d_shader_type shader_type, unsigned int *base, unsigned int *count) DECLSPEC_HIDDEN; + +struct wined3d_gl_info +{ + DWORD selected_gl_version; + DWORD glsl_version; + struct wined3d_gl_limits limits; + DWORD reserved_glsl_constants, reserved_arb_constants; + DWORD quirks; + BOOL supported[WINED3D_GL_EXT_COUNT]; + GLint wrap_lookup[WINED3D_TADDRESS_MIRROR_ONCE - WINED3D_TADDRESS_WRAP + 1]; + + HGLRC (WINAPI *p_wglCreateContextAttribsARB)(HDC dc, HGLRC share, const GLint *attribs); + struct opengl_funcs gl_ops; + struct wined3d_fbo_ops fbo_ops; + + void (WINE_GLAPI *p_glDisableWINE)(GLenum cap); + void (WINE_GLAPI *p_glEnableWINE)(GLenum cap); +}; + +/* The driver names reflect the lowest GPU supported + * by a certain driver, so DRIVER_AMD_R300 supports + * R3xx, R4xx and R5xx GPUs. */ +enum wined3d_display_driver +{ + DRIVER_AMD_RAGE_128PRO, + DRIVER_AMD_R100, + DRIVER_AMD_R300, + DRIVER_AMD_R600, + DRIVER_AMD_RX, + DRIVER_INTEL_GMA800, + DRIVER_INTEL_GMA900, + DRIVER_INTEL_GMA950, + DRIVER_INTEL_GMA3000, + DRIVER_INTEL_HD4000, + DRIVER_NVIDIA_TNT, + DRIVER_NVIDIA_GEFORCE2MX, + DRIVER_NVIDIA_GEFORCEFX, + DRIVER_NVIDIA_GEFORCE6, + DRIVER_NVIDIA_GEFORCE8, + DRIVER_NVIDIA_FERMI, + DRIVER_NVIDIA_KEPLER, + DRIVER_REDHAT_VIRGL, + DRIVER_VMWARE, + DRIVER_WINE, + DRIVER_UNKNOWN, +}; + +struct wined3d_gpu_description +{ + enum wined3d_pci_vendor vendor; + enum wined3d_pci_device device; + const char *description; + enum wined3d_display_driver driver; + unsigned int vidmem; +}; + +const struct wined3d_gpu_description *wined3d_get_gpu_description(enum wined3d_pci_vendor vendor, + enum wined3d_pci_device device) DECLSPEC_HIDDEN; +const struct wined3d_gpu_description *wined3d_get_user_override_gpu_description(enum wined3d_pci_vendor vendor, + enum wined3d_pci_device device) DECLSPEC_HIDDEN; +enum wined3d_pci_device wined3d_gpu_from_feature_level(enum wined3d_pci_vendor *vendor, + enum wined3d_feature_level feature_level) DECLSPEC_HIDDEN; + +/* 512 in Direct3D 8/9, 128 in DXGI. */ +#define WINED3D_MAX_DEVICE_IDENTIFIER_LENGTH 512 + +struct wined3d_driver_info +{ + enum wined3d_pci_vendor vendor; + enum wined3d_pci_device device; + const char *name; + char description[WINED3D_MAX_DEVICE_IDENTIFIER_LENGTH]; + UINT64 vram_bytes; + UINT64 sysmem_bytes; + DWORD version_high; + DWORD version_low; +}; + +bool wined3d_driver_info_init(struct wined3d_driver_info *driver_info, + const struct wined3d_gpu_description *gpu_description, enum wined3d_feature_level feature_level, + UINT64 vram_bytes, UINT64 sysmem_bytes) DECLSPEC_HIDDEN; + +struct wined3d_adapter_ops +{ + void (*adapter_destroy)(struct wined3d_adapter *adapter); + HRESULT (*adapter_create_device)(struct wined3d *wined3d, const struct wined3d_adapter *adapter, + enum wined3d_device_type device_type, HWND focus_window, unsigned int flags, + BYTE surface_alignment, const enum wined3d_feature_level *levels, unsigned int level_count, + struct wined3d_device_parent *device_parent, struct wined3d_device **device); + void (*adapter_destroy_device)(struct wined3d_device *device); + struct wined3d_context *(*adapter_acquire_context)(struct wined3d_device *device, + struct wined3d_texture *texture, unsigned int sub_resource_idx); + void (*adapter_release_context)(struct wined3d_context *context); + void (*adapter_get_wined3d_caps)(const struct wined3d_adapter *adapter, struct wined3d_caps *caps); + BOOL (*adapter_check_format)(const struct wined3d_adapter *adapter, + const struct wined3d_format *adapter_format, const struct wined3d_format *rt_format, + const struct wined3d_format *ds_format); + HRESULT (*adapter_init_3d)(struct wined3d_device *device); + void (*adapter_uninit_3d)(struct wined3d_device *device); + void *(*adapter_map_bo_address)(struct wined3d_context *context, + const struct wined3d_bo_address *data, size_t size, uint32_t map_flags); + void (*adapter_unmap_bo_address)(struct wined3d_context *context, const struct wined3d_bo_address *data, + unsigned int range_count, const struct wined3d_range *ranges); + void (*adapter_copy_bo_address)(struct wined3d_context *context, + const struct wined3d_bo_address *dst, const struct wined3d_bo_address *src, size_t size); + HRESULT (*adapter_create_swapchain)(struct wined3d_device *device, + struct wined3d_swapchain_desc *desc, + struct wined3d_swapchain_state_parent *state_parent, void *parent, + const struct wined3d_parent_ops *parent_ops, struct wined3d_swapchain **swapchain); + void (*adapter_destroy_swapchain)(struct wined3d_swapchain *swapchain); + HRESULT (*adapter_create_buffer)(struct wined3d_device *device, const struct wined3d_buffer_desc *desc, + const struct wined3d_sub_resource_data *data, void *parent, const struct wined3d_parent_ops *parent_ops, + struct wined3d_buffer **buffer); + void (*adapter_destroy_buffer)(struct wined3d_buffer *buffer); + HRESULT (*adapter_create_texture)(struct wined3d_device *device, const struct wined3d_resource_desc *desc, + unsigned int layer_count, unsigned int level_count, uint32_t flags, void *parent, + const struct wined3d_parent_ops *parent_ops, struct wined3d_texture **texture); + void (*adapter_destroy_texture)(struct wined3d_texture *texture); + HRESULT (*adapter_create_rendertarget_view)(const struct wined3d_view_desc *desc, + struct wined3d_resource *resource, void *parent, const struct wined3d_parent_ops *parent_ops, + struct wined3d_rendertarget_view **view); + void (*adapter_destroy_rendertarget_view)(struct wined3d_rendertarget_view *view); + HRESULT (*adapter_create_shader_resource_view)(const struct wined3d_view_desc *desc, + struct wined3d_resource *resource, void *parent, const struct wined3d_parent_ops *parent_ops, + struct wined3d_shader_resource_view **view); + void (*adapter_destroy_shader_resource_view)(struct wined3d_shader_resource_view *view); + HRESULT (*adapter_create_unordered_access_view)(const struct wined3d_view_desc *desc, + struct wined3d_resource *resource, void *parent, const struct wined3d_parent_ops *parent_ops, + struct wined3d_unordered_access_view **view); + void (*adapter_destroy_unordered_access_view)(struct wined3d_unordered_access_view *view); + HRESULT (*adapter_create_sampler)(struct wined3d_device *device, const struct wined3d_sampler_desc *desc, + void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_sampler **sampler); + void (*adapter_destroy_sampler)(struct wined3d_sampler *sampler); + HRESULT (*adapter_create_query)(struct wined3d_device *device, enum wined3d_query_type type, + void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_query **query); + void (*adapter_destroy_query)(struct wined3d_query *query); + void (*adapter_flush_context)(struct wined3d_context *context); + void (*adapter_draw_primitive)(struct wined3d_device *device, const struct wined3d_state *state, + const struct wined3d_draw_parameters *parameters); + void (*adapter_dispatch_compute)(struct wined3d_device *device, const struct wined3d_state *state, + const struct wined3d_dispatch_parameters *parameters); + void (*adapter_clear_uav)(struct wined3d_context *context, + struct wined3d_unordered_access_view *view, const struct wined3d_uvec4 *clear_value); +}; + +struct wined3d_output +{ + unsigned int ordinal; + WCHAR device_name[CCHDEVICENAME]; + struct wined3d_adapter *adapter; + enum wined3d_format_id screen_format; + + D3DKMT_HANDLE kmt_adapter; + D3DKMT_HANDLE kmt_device; + D3DDDI_VIDEO_PRESENT_SOURCE_ID vidpn_source_id; +}; + +/* The adapter structure */ +struct wined3d_adapter +{ + unsigned int ordinal; + + struct wined3d_gl_info gl_info; + struct wined3d_d3d_info d3d_info; + struct wined3d_driver_info driver_info; + struct wined3d_output *outputs; + unsigned int output_count; + UINT64 vram_bytes_used; + GUID driver_uuid; + GUID device_uuid; + LUID luid; + + void *formats; + size_t format_size; + + const struct wined3d_vertex_pipe_ops *vertex_pipe; + const struct wined3d_fragment_pipe_ops *fragment_pipe; + const struct wined3d_state_entry_template *misc_state_template; + const struct wined3d_shader_backend_ops *shader_backend; + const struct wined3d_adapter_ops *adapter_ops; +}; + +BOOL wined3d_adapter_init(struct wined3d_adapter *adapter, unsigned int ordinal, const LUID *luid, + const struct wined3d_adapter_ops *adapter_ops) DECLSPEC_HIDDEN; +void wined3d_adapter_cleanup(struct wined3d_adapter *adapter) DECLSPEC_HIDDEN; +BOOL wined3d_get_primary_adapter_luid(LUID *luid) DECLSPEC_HIDDEN; + +struct wined3d_adapter_gl +{ + struct wined3d_adapter a; + + struct wined3d_pixel_format *pixel_formats; + unsigned int pixel_format_count; +}; + +static inline struct wined3d_adapter_gl *wined3d_adapter_gl(struct wined3d_adapter *adapter) +{ + return CONTAINING_RECORD(adapter, struct wined3d_adapter_gl, a); +} + +static inline const struct wined3d_adapter_gl *wined3d_adapter_gl_const(const struct wined3d_adapter *adapter) +{ + return CONTAINING_RECORD(adapter, struct wined3d_adapter_gl, a); +} + +struct wined3d_adapter *wined3d_adapter_gl_create(unsigned int ordinal, + unsigned int wined3d_creation_flags) DECLSPEC_HIDDEN; + +struct wined3d_adapter_vk +{ + struct wined3d_adapter a; + + struct wined3d_vk_info vk_info; + unsigned int device_extension_count; + const char **device_extensions; + VkPhysicalDevice physical_device; + + VkPhysicalDeviceLimits device_limits; + VkPhysicalDeviceMemoryProperties memory_properties; +}; + +static inline struct wined3d_adapter_vk *wined3d_adapter_vk(struct wined3d_adapter *adapter) +{ + return CONTAINING_RECORD(adapter, struct wined3d_adapter_vk, a); +} + +struct wined3d_adapter *wined3d_adapter_vk_create(unsigned int ordinal, + unsigned int wined3d_creation_flags) DECLSPEC_HIDDEN; +unsigned int wined3d_adapter_vk_get_memory_type_index(const struct wined3d_adapter_vk *adapter_vk, + uint32_t memory_type_mask, VkMemoryPropertyFlags flags) DECLSPEC_HIDDEN; + +struct wined3d_caps_gl_ctx +{ + HDC dc; + HWND wnd; + HGLRC gl_ctx; + HDC restore_dc; + HGLRC restore_gl_ctx; + + const struct wined3d_gl_info *gl_info; + GLuint test_vbo; + GLuint test_program_id; + + const struct wined3d_gpu_description *gpu_description; + UINT64 vram_bytes; +}; + +BOOL wined3d_adapter_gl_init_format_info(struct wined3d_adapter *adapter, + struct wined3d_caps_gl_ctx *ctx) DECLSPEC_HIDDEN; +BOOL wined3d_adapter_no3d_init_format_info(struct wined3d_adapter *adapter) DECLSPEC_HIDDEN; +BOOL wined3d_adapter_vk_init_format_info(struct wined3d_adapter_vk *adapter_vk, + const struct wined3d_vk_info *vk_info) DECLSPEC_HIDDEN; +UINT64 adapter_adjust_memory(struct wined3d_adapter *adapter, INT64 amount) DECLSPEC_HIDDEN; + +BOOL wined3d_caps_gl_ctx_test_viewport_subpixel_bits(struct wined3d_caps_gl_ctx *ctx) DECLSPEC_HIDDEN; + +void install_gl_compat_wrapper(struct wined3d_gl_info *gl_info, enum wined3d_gl_extension ext) DECLSPEC_HIDDEN; + +enum wined3d_projection_type +{ + WINED3D_PROJECTION_NONE = 0, + WINED3D_PROJECTION_COUNT3 = 1, + WINED3D_PROJECTION_COUNT4 = 2 +}; + +/***************************************************************************** + * Fixed function pipeline replacements + */ +#define ARG_UNUSED 0xff +struct texture_stage_op +{ + unsigned cop : 8; + unsigned carg1 : 8; + unsigned carg2 : 8; + unsigned carg0 : 8; + + unsigned aop : 8; + unsigned aarg1 : 8; + unsigned aarg2 : 8; + unsigned aarg0 : 8; + + struct color_fixup_desc color_fixup; + unsigned tex_type : 3; + unsigned tmp_dst : 1; + unsigned projected : 2; + unsigned padding : 10; +}; + +struct ffp_frag_settings +{ + struct texture_stage_op op[WINED3D_MAX_TEXTURES]; + enum wined3d_ffp_ps_fog_mode fog; + unsigned char sRGB_write; + unsigned char emul_clipplanes; + unsigned char texcoords_initialized; + unsigned char color_key_enabled : 1; + unsigned char pointsprite : 1; + unsigned char flatshading : 1; + unsigned char alpha_test_func : 3; + unsigned char padding : 2; +}; + +struct ffp_frag_desc +{ + struct wine_rb_entry entry; + struct ffp_frag_settings settings; +}; + +int wined3d_ffp_frag_program_key_compare(const void *key, const struct wine_rb_entry *entry) DECLSPEC_HIDDEN; +int wined3d_ffp_vertex_program_key_compare(const void *key, const struct wine_rb_entry *entry) DECLSPEC_HIDDEN; + +extern const struct wined3d_parent_ops wined3d_null_parent_ops DECLSPEC_HIDDEN; + +void gen_ffp_frag_op(const struct wined3d_context *context, const struct wined3d_state *state, + struct ffp_frag_settings *settings, BOOL ignore_textype) DECLSPEC_HIDDEN; +const struct ffp_frag_desc *find_ffp_frag_shader(const struct wine_rb_tree *fragment_shaders, + const struct ffp_frag_settings *settings) DECLSPEC_HIDDEN; +void add_ffp_frag_shader(struct wine_rb_tree *shaders, struct ffp_frag_desc *desc) DECLSPEC_HIDDEN; +void wined3d_ftoa(float value, char *s) DECLSPEC_HIDDEN; + +enum wined3d_ffp_vs_fog_mode +{ + WINED3D_FFP_VS_FOG_OFF = 0, + WINED3D_FFP_VS_FOG_FOGCOORD = 1, + WINED3D_FFP_VS_FOG_DEPTH = 2, + WINED3D_FFP_VS_FOG_RANGE = 3, +}; + +#define WINED3D_FFP_TCI_SHIFT 16 +#define WINED3D_FFP_TCI_MASK 0xffu + +#define WINED3D_FFP_LIGHT_TYPE_SHIFT(idx) (3 * (idx)) +#define WINED3D_FFP_LIGHT_TYPE_MASK 0x7u + +struct wined3d_ffp_vs_settings +{ + DWORD point_light_count : 4; + DWORD spot_light_count : 4; + DWORD directional_light_count : 4; + DWORD parallel_point_light_count : 4; + DWORD diffuse_source : 2; + DWORD emissive_source : 2; + DWORD ambient_source : 2; + DWORD specular_source : 2; + DWORD transformed : 1; + DWORD vertexblends : 2; + DWORD clipping : 1; + DWORD normal : 1; + DWORD normalize : 1; + DWORD lighting : 1; + DWORD localviewer : 1; + + DWORD point_size : 1; + DWORD per_vertex_point_size : 1; + DWORD fog_mode : 2; + DWORD texcoords : 8; /* WINED3D_MAX_TEXTURES */ + DWORD ortho_fog : 1; + DWORD flatshading : 1; + DWORD swizzle_map : 16; /* MAX_ATTRIBS, 16 */ + DWORD padding : 2; + + DWORD texgen[WINED3D_MAX_TEXTURES]; +}; + +struct wined3d_ffp_vs_desc +{ + struct wine_rb_entry entry; + struct wined3d_ffp_vs_settings settings; +}; + +void wined3d_ffp_get_vs_settings(const struct wined3d_context *context, + const struct wined3d_state *state, struct wined3d_ffp_vs_settings *settings) DECLSPEC_HIDDEN; + +struct wined3d +{ + LONG ref; + unsigned int flags; + unsigned int adapter_count; + struct wined3d_adapter *adapters[1]; +}; + +BOOL wined3d_filter_messages(HWND window, BOOL filter) DECLSPEC_HIDDEN; +HRESULT wined3d_init(struct wined3d *wined3d, DWORD flags) DECLSPEC_HIDDEN; +void wined3d_unregister_window(HWND window) DECLSPEC_HIDDEN; + +BOOL wined3d_get_app_name(char *app_name, unsigned int app_name_size) DECLSPEC_HIDDEN; + +struct wined3d_blend_state +{ + LONG refcount; + struct wined3d_blend_state_desc desc; + BOOL dual_source; + + void *parent; + const struct wined3d_parent_ops *parent_ops; + + struct wined3d_device *device; + struct wine_rb_entry entry; +}; + +static inline unsigned int wined3d_blend_state_get_writemask(const struct wined3d_blend_state *state, + unsigned int index) +{ + if (!state) + return 0xf; + if (!state->desc.independent) + index = 0; + return state->desc.rt[index].writemask; +} + +struct wined3d_depth_stencil_state +{ + LONG refcount; + struct wined3d_depth_stencil_state_desc desc; + + void *parent; + const struct wined3d_parent_ops *parent_ops; + + struct wined3d_device *device; + struct wine_rb_entry entry; +}; + +struct wined3d_rasterizer_state +{ + LONG refcount; + struct wined3d_rasterizer_state_desc desc; + + void *parent; + const struct wined3d_parent_ops *parent_ops; + + struct wined3d_device *device; + struct wine_rb_entry entry; +}; + +struct wined3d_stream_output +{ + struct wined3d_buffer *buffer; + UINT offset; +}; + +#define LIGHTMAP_SIZE 43 +#define LIGHTMAP_HASHFUNC(x) ((x) % LIGHTMAP_SIZE) + +struct wined3d_light_state +{ + /* Light hashmap. Collisions are handled using linked lists. */ + struct list light_map[LIGHTMAP_SIZE]; + const struct wined3d_light_info *lights[WINED3D_MAX_ACTIVE_LIGHTS]; +}; + +#define WINED3D_STATE_NO_REF 0x00000001 +#define WINED3D_STATE_INIT_DEFAULT 0x00000002 + +struct wined3d_state +{ + DWORD flags; + struct wined3d_fb_state fb; + + struct wined3d_vertex_declaration *vertex_declaration; + struct wined3d_stream_output stream_output[WINED3D_MAX_STREAM_OUTPUT_BUFFERS]; + struct wined3d_stream_state streams[WINED3D_MAX_STREAMS]; + struct wined3d_buffer *index_buffer; + enum wined3d_format_id index_format; + unsigned int index_offset; + int base_vertex_index; + int load_base_vertex_index; /* Non-indexed drawing needs 0 here, indexed needs base_vertex_index. */ + enum wined3d_primitive_type primitive_type; + unsigned int patch_vertex_count; + struct wined3d_query *predicate; + BOOL predicate_value; + + struct wined3d_shader *shader[WINED3D_SHADER_TYPE_COUNT]; + struct wined3d_buffer *cb[WINED3D_SHADER_TYPE_COUNT][MAX_CONSTANT_BUFFERS]; + struct wined3d_sampler *sampler[WINED3D_SHADER_TYPE_COUNT][MAX_SAMPLER_OBJECTS]; + struct wined3d_shader_resource_view *shader_resource_view[WINED3D_SHADER_TYPE_COUNT][MAX_SHADER_RESOURCE_VIEWS]; + struct wined3d_unordered_access_view *unordered_access_view[WINED3D_PIPELINE_COUNT][MAX_UNORDERED_ACCESS_VIEWS]; + + struct wined3d_vec4 vs_consts_f[WINED3D_MAX_VS_CONSTS_F]; + struct wined3d_ivec4 vs_consts_i[WINED3D_MAX_CONSTS_I]; + BOOL vs_consts_b[WINED3D_MAX_CONSTS_B]; + + struct wined3d_vec4 ps_consts_f[WINED3D_MAX_PS_CONSTS_F]; + struct wined3d_ivec4 ps_consts_i[WINED3D_MAX_CONSTS_I]; + BOOL ps_consts_b[WINED3D_MAX_CONSTS_B]; + + struct wined3d_texture *textures[WINED3D_MAX_COMBINED_SAMPLERS]; + DWORD sampler_states[WINED3D_MAX_COMBINED_SAMPLERS][WINED3D_HIGHEST_SAMPLER_STATE + 1]; + DWORD texture_states[WINED3D_MAX_TEXTURES][WINED3D_HIGHEST_TEXTURE_STATE + 1]; + + struct wined3d_matrix transforms[WINED3D_HIGHEST_TRANSFORM_STATE + 1]; + struct wined3d_vec4 clip_planes[WINED3D_MAX_CLIP_DISTANCES]; + struct wined3d_material material; + struct wined3d_viewport viewports[WINED3D_MAX_VIEWPORTS]; + unsigned int viewport_count; + RECT scissor_rects[WINED3D_MAX_VIEWPORTS]; + unsigned int scissor_rect_count; + + struct wined3d_light_state light_state; + + DWORD render_states[WINEHIGHEST_RENDER_STATE + 1]; + struct wined3d_blend_state *blend_state; + struct wined3d_color blend_factor; + unsigned int sample_mask; + struct wined3d_depth_stencil_state *depth_stencil_state; + unsigned int stencil_ref; + struct wined3d_rasterizer_state *rasterizer_state; +}; + +static inline bool wined3d_state_uses_depth_buffer(const struct wined3d_state *state) +{ + if (!state->depth_stencil_state) + return true; + return state->depth_stencil_state->desc.depth || state->depth_stencil_state->desc.depth_write + || state->depth_stencil_state->desc.stencil; +} + +struct wined3d_dummy_textures +{ + GLuint tex_1d; + GLuint tex_2d; + GLuint tex_rect; + GLuint tex_3d; + GLuint tex_cube; + GLuint tex_cube_array; + GLuint tex_1d_array; + GLuint tex_2d_array; + GLuint tex_buffer; + GLuint tex_2d_ms; + GLuint tex_2d_ms_array; +}; + +#define WINED3D_UNMAPPED_STAGE ~0u + +/* Multithreaded flag. Removed from the public header to signal that + * wined3d_device_create() ignores it. */ +#define WINED3DCREATE_MULTITHREADED 0x00000004 + +struct wined3d_so_desc_entry +{ + struct wine_rb_entry entry; + struct wined3d_stream_output_desc desc; + struct wined3d_stream_output_element elements[1]; +}; + +struct wined3d_device +{ + LONG ref; + + /* WineD3D Information */ + struct wined3d_device_parent *device_parent; + struct wined3d *wined3d; + struct wined3d_adapter *adapter; + + const struct wined3d_shader_backend_ops *shader_backend; + void *shader_priv; + void *fragment_priv; + void *vertex_priv; + struct wined3d_state_entry state_table[STATE_HIGHEST + 1]; + /* Array of functions for states which are handled by more than one pipeline part */ + APPLYSTATEFUNC *multistate_funcs[STATE_HIGHEST + 1]; + struct wined3d_blitter *blitter; + + BYTE bCursorVisible : 1; + BYTE d3d_initialized : 1; + BYTE inScene : 1; /* A flag to check for proper BeginScene / EndScene call pairs */ + BYTE softwareVertexProcessing : 1; /* process vertex shaders using software or hardware */ + BYTE restore_screensaver : 1; + BYTE padding : 3; + + unsigned char surface_alignment; /* Line Alignment of surfaces */ + + WORD padding2 : 16; + + enum wined3d_feature_level feature_level; + + struct wined3d_state state; + + /* Internal use fields */ + struct wined3d_device_creation_parameters create_parms; + HWND focus_window; + + struct wined3d_rendertarget_view *back_buffer_view; + struct wined3d_swapchain **swapchains; + UINT swapchain_count; + unsigned int max_frame_latency; + + struct list resources; /* a linked list to track resources created by the device */ + struct list shaders; /* a linked list to track shaders (pixel and vertex) */ + struct wine_rb_tree so_descs; + struct wine_rb_tree samplers, rasterizer_states, blend_states, depth_stencil_states; + + /* Render Target Support */ + struct wined3d_rendertarget_view *auto_depth_stencil_view; + + /* Cursor management */ + UINT xHotSpot; + UINT yHotSpot; + UINT xScreenSpace; + UINT yScreenSpace; + UINT cursorWidth, cursorHeight; + struct wined3d_texture *cursor_texture; + HCURSOR hardwareCursor; + + /* The Wine logo texture */ + struct wined3d_texture *logo_texture; + + /* Default sampler used to emulate the direct resource access without using wined3d_sampler */ + struct wined3d_sampler *default_sampler; + struct wined3d_sampler *null_sampler; + + /* Command stream */ + struct wined3d_cs *cs; + + /* Context management */ + struct wined3d_context **contexts; + UINT context_count; +}; + +void wined3d_device_cleanup(struct wined3d_device *device) DECLSPEC_HIDDEN; +BOOL device_context_add(struct wined3d_device *device, struct wined3d_context *context) DECLSPEC_HIDDEN; +void device_context_remove(struct wined3d_device *device, struct wined3d_context *context) DECLSPEC_HIDDEN; +void wined3d_device_create_default_samplers(struct wined3d_device *device, + struct wined3d_context *context) DECLSPEC_HIDDEN; +void wined3d_device_create_primary_opengl_context_cs(void *object) DECLSPEC_HIDDEN; +void wined3d_device_delete_opengl_contexts_cs(void *object) DECLSPEC_HIDDEN; +void wined3d_device_destroy_default_samplers(struct wined3d_device *device, + struct wined3d_context *context) DECLSPEC_HIDDEN; +HRESULT wined3d_device_init(struct wined3d_device *device, struct wined3d *wined3d, + unsigned int adapter_idx, enum wined3d_device_type device_type, HWND focus_window, unsigned int flags, + BYTE surface_alignment, const enum wined3d_feature_level *levels, unsigned int level_count, + const BOOL *supported_extensions, struct wined3d_device_parent *device_parent) DECLSPEC_HIDDEN; +LRESULT device_process_message(struct wined3d_device *device, HWND window, BOOL unicode, + UINT message, WPARAM wparam, LPARAM lparam, WNDPROC proc) DECLSPEC_HIDDEN; +void device_resource_add(struct wined3d_device *device, struct wined3d_resource *resource) DECLSPEC_HIDDEN; +void device_resource_released(struct wined3d_device *device, struct wined3d_resource *resource) DECLSPEC_HIDDEN; +void device_invalidate_state(const struct wined3d_device *device, DWORD state) DECLSPEC_HIDDEN; +HRESULT wined3d_device_set_implicit_swapchain(struct wined3d_device *device, + struct wined3d_swapchain *swapchain) DECLSPEC_HIDDEN; +void wined3d_device_uninit_3d(struct wined3d_device *device) DECLSPEC_HIDDEN; + +struct wined3d_device_no3d +{ + struct wined3d_device d; + + struct wined3d_context context_no3d; +}; + +static inline struct wined3d_device_no3d *wined3d_device_no3d(struct wined3d_device *device) +{ + return CONTAINING_RECORD(device, struct wined3d_device_no3d, d); +} + +struct wined3d_device_gl +{ + struct wined3d_device d; + + /* Textures for when no other textures are bound. */ + struct wined3d_dummy_textures dummy_textures; + + uint64_t completed_fence_id; + uint64_t current_fence_id; +}; + +static inline struct wined3d_device_gl *wined3d_device_gl(struct wined3d_device *device) +{ + return CONTAINING_RECORD(device, struct wined3d_device_gl, d); +} + +struct wined3d_null_image_vk +{ + VkImage vk_image; + struct wined3d_allocator_block *memory; + VkDeviceMemory vk_memory; +}; + +struct wined3d_null_resources_vk +{ + struct wined3d_bo_vk bo; + VkDescriptorBufferInfo buffer_info; + + struct wined3d_null_image_vk image_1d; + struct wined3d_null_image_vk image_2d; + struct wined3d_null_image_vk image_2dms; + struct wined3d_null_image_vk image_3d; +}; + +struct wined3d_null_views_vk +{ + VkBufferView vk_view_buffer_uint; + VkBufferView vk_view_buffer_float; + + VkDescriptorImageInfo vk_info_1d; + VkDescriptorImageInfo vk_info_2d; + VkDescriptorImageInfo vk_info_2dms; + VkDescriptorImageInfo vk_info_3d; + VkDescriptorImageInfo vk_info_cube; + VkDescriptorImageInfo vk_info_2d_array; + VkDescriptorImageInfo vk_info_2dms_array; +}; + +#define WINED3D_ALLOCATOR_CHUNK_SIZE (64 * 1024 * 1024) +#define WINED3D_ALLOCATOR_CHUNK_ORDER_COUNT 15 +#define WINED3D_ALLOCATOR_MIN_BLOCK_SIZE (WINED3D_ALLOCATOR_CHUNK_SIZE >> (WINED3D_ALLOCATOR_CHUNK_ORDER_COUNT - 1)) +#define WINED3D_SLAB_BO_MIN_OBJECT_ALIGN 16 + +struct wined3d_allocator_chunk +{ + struct list entry; + struct list available[WINED3D_ALLOCATOR_CHUNK_ORDER_COUNT]; + struct wined3d_allocator *allocator; + unsigned int map_count; + void *map_ptr; +}; + +void wined3d_allocator_chunk_cleanup(struct wined3d_allocator_chunk *chunk) DECLSPEC_HIDDEN; +bool wined3d_allocator_chunk_init(struct wined3d_allocator_chunk *chunk, + struct wined3d_allocator *allocator) DECLSPEC_HIDDEN; + +struct wined3d_allocator_chunk_vk +{ + struct wined3d_allocator_chunk c; + VkDeviceMemory vk_memory; +}; + +static inline struct wined3d_allocator_chunk_vk *wined3d_allocator_chunk_vk(struct wined3d_allocator_chunk *chunk) +{ + return CONTAINING_RECORD(chunk, struct wined3d_allocator_chunk_vk, c); +} + +void *wined3d_allocator_chunk_vk_map(struct wined3d_allocator_chunk_vk *chunk_vk, + struct wined3d_context_vk *context_vk) DECLSPEC_HIDDEN; +void wined3d_allocator_chunk_vk_unmap(struct wined3d_allocator_chunk_vk *chunk_vk, + struct wined3d_context_vk *context_vk) DECLSPEC_HIDDEN; + +struct wined3d_allocator_block +{ + struct list entry; + struct wined3d_allocator_chunk *chunk; + struct wined3d_allocator_block *parent, *sibling; + unsigned int order; + size_t offset; + bool free; +}; + +void wined3d_allocator_block_free(struct wined3d_allocator_block *block) DECLSPEC_HIDDEN; + +struct wined3d_allocator_pool +{ + struct list chunks; +}; + +struct wined3d_allocator_ops +{ + struct wined3d_allocator_chunk *(*allocator_create_chunk)(struct wined3d_allocator *allocator, + struct wined3d_context *context, unsigned int memory_type, size_t chunk_size); + void (*allocator_destroy_chunk)(struct wined3d_allocator_chunk *chunk); +}; + +struct wined3d_allocator +{ + const struct wined3d_allocator_ops *ops; + struct wined3d_allocator_pool *pools; + size_t pool_count; + struct wined3d_allocator_block *free; +}; + +struct wined3d_allocator_block *wined3d_allocator_allocate(struct wined3d_allocator *allocator, + struct wined3d_context *context, unsigned int memory_type, size_t size) DECLSPEC_HIDDEN; +void wined3d_allocator_cleanup(struct wined3d_allocator *allocator) DECLSPEC_HIDDEN; +bool wined3d_allocator_init(struct wined3d_allocator *allocator, + size_t pool_count, const struct wined3d_allocator_ops *allocator_ops) DECLSPEC_HIDDEN; + +struct wined3d_device_vk +{ + struct wined3d_device d; + + struct wined3d_context_vk context_vk; + + VkDevice vk_device; + VkQueue vk_queue; + uint32_t vk_queue_family_index; + uint32_t timestamp_bits; + + struct wined3d_vk_info vk_info; + + struct wined3d_null_resources_vk null_resources_vk; + struct wined3d_null_views_vk null_views_vk; + + struct wined3d_allocator allocator; +}; + +static inline struct wined3d_device_vk *wined3d_device_vk(struct wined3d_device *device) +{ + return CONTAINING_RECORD(device, struct wined3d_device_vk, d); +} + +bool wined3d_device_vk_create_null_resources(struct wined3d_device_vk *device_vk, + struct wined3d_context_vk *context_vk) DECLSPEC_HIDDEN; +bool wined3d_device_vk_create_null_views(struct wined3d_device_vk *device_vk, + struct wined3d_context_vk *context_vk) DECLSPEC_HIDDEN; +void wined3d_device_vk_destroy_null_resources(struct wined3d_device_vk *device_vk, + struct wined3d_context_vk *context_vk) DECLSPEC_HIDDEN; +void wined3d_device_vk_destroy_null_views(struct wined3d_device_vk *device_vk, + struct wined3d_context_vk *context_vk) DECLSPEC_HIDDEN; + +static inline float wined3d_alpha_ref(const struct wined3d_state *state) +{ + return (state->render_states[WINED3D_RS_ALPHAREF] & 0xff) / 255.0f; +} + +const char *wined3d_debug_resource_access(DWORD access) DECLSPEC_HIDDEN; +const char *wined3d_debug_bind_flags(DWORD bind_flags) DECLSPEC_HIDDEN; +const char *wined3d_debug_view_desc(const struct wined3d_view_desc *d, + const struct wined3d_resource *resource) DECLSPEC_HIDDEN; +const char *wined3d_debug_vkresult(VkResult vr) DECLSPEC_HIDDEN; + +static inline BOOL wined3d_resource_access_is_managed(unsigned int access) +{ + return !(~access & (WINED3D_RESOURCE_ACCESS_GPU | WINED3D_RESOURCE_ACCESS_CPU)); +} + +struct wined3d_resource_ops +{ + ULONG (*resource_incref)(struct wined3d_resource *resource); + ULONG (*resource_decref)(struct wined3d_resource *resource); + void (*resource_preload)(struct wined3d_resource *resource); + void (*resource_unload)(struct wined3d_resource *resource); + HRESULT (*resource_sub_resource_map)(struct wined3d_resource *resource, unsigned int sub_resource_idx, + struct wined3d_map_desc *map_desc, const struct wined3d_box *box, DWORD flags); + HRESULT (*resource_sub_resource_unmap)(struct wined3d_resource *resource, unsigned int sub_resource_idx); +}; + +struct wined3d_resource +{ + LONG ref; + LONG bind_count; + LONG map_count; + LONG access_count; + struct wined3d_device *device; + enum wined3d_resource_type type; + enum wined3d_gl_resource_type gl_type; + const struct wined3d_format *format; + unsigned int format_flags; + enum wined3d_multisample_type multisample_type; + UINT multisample_quality; + DWORD usage; + unsigned int bind_flags; + unsigned int access; + WORD draw_binding; + WORD map_binding; + UINT width; + UINT height; + UINT depth; + UINT size; + DWORD priority; + void *heap_memory; + + void *parent; + const struct wined3d_parent_ops *parent_ops; + const struct wined3d_resource_ops *resource_ops; + + struct list resource_list_entry; + + struct + { + uint32_t srv; + uint32_t rtv; + } + *sub_resource_bind_counts_device; + uint32_t srv_full_bind_count_device; + uint32_t rtv_full_bind_count_device; + uint32_t srv_partial_bind_count_device; + uint32_t rtv_partial_bind_count_device; +}; + +static inline ULONG wined3d_resource_incref(struct wined3d_resource *resource) +{ + return resource->resource_ops->resource_incref(resource); +} + +static inline ULONG wined3d_resource_decref(struct wined3d_resource *resource) +{ + return resource->resource_ops->resource_decref(resource); +} + +static inline void wined3d_resource_acquire(struct wined3d_resource *resource) +{ + InterlockedIncrement(&resource->access_count); +} + +static inline void wined3d_resource_release(struct wined3d_resource *resource) +{ + InterlockedDecrement(&resource->access_count); +} + +void resource_cleanup(struct wined3d_resource *resource) DECLSPEC_HIDDEN; +HRESULT resource_init(struct wined3d_resource *resource, struct wined3d_device *device, + enum wined3d_resource_type type, const struct wined3d_format *format, + enum wined3d_multisample_type multisample_type, unsigned int multisample_quality, unsigned int usage, + unsigned int bind_flags, unsigned int access, unsigned int width, unsigned int height, unsigned int depth, + unsigned int size, void *parent, const struct wined3d_parent_ops *parent_ops, + const struct wined3d_resource_ops *resource_ops) DECLSPEC_HIDDEN; +void resource_unload(struct wined3d_resource *resource) DECLSPEC_HIDDEN; +void wined3d_resource_free_sysmem(struct wined3d_resource *resource) DECLSPEC_HIDDEN; +const struct wined3d_format *wined3d_resource_get_decompress_format( + const struct wined3d_resource *resource) DECLSPEC_HIDDEN; +unsigned int wined3d_resource_get_sample_count(const struct wined3d_resource *resource) DECLSPEC_HIDDEN; +GLbitfield wined3d_resource_gl_map_flags(DWORD d3d_flags) DECLSPEC_HIDDEN; +GLenum wined3d_resource_gl_legacy_map_flags(DWORD d3d_flags) DECLSPEC_HIDDEN; +GLbitfield wined3d_resource_gl_storage_flags(const struct wined3d_resource *resource) DECLSPEC_HIDDEN; +BOOL wined3d_resource_is_offscreen(struct wined3d_resource *resource) DECLSPEC_HIDDEN; +BOOL wined3d_resource_prepare_sysmem(struct wined3d_resource *resource) DECLSPEC_HIDDEN; +void wined3d_resource_update_draw_binding(struct wined3d_resource *resource) DECLSPEC_HIDDEN; + +/* Tests show that the start address of resources is 32 byte aligned */ +#define RESOURCE_ALIGNMENT 16 +#define WINED3D_CONSTANT_BUFFER_ALIGNMENT 16 + +#define WINED3D_LOCATION_DISCARDED 0x00000001 +#define WINED3D_LOCATION_SYSMEM 0x00000002 +#define WINED3D_LOCATION_BUFFER 0x00000008 +#define WINED3D_LOCATION_TEXTURE_RGB 0x00000010 +#define WINED3D_LOCATION_TEXTURE_SRGB 0x00000020 +#define WINED3D_LOCATION_DRAWABLE 0x00000040 +#define WINED3D_LOCATION_RB_MULTISAMPLE 0x00000080 +#define WINED3D_LOCATION_RB_RESOLVED 0x00000100 + +const char *wined3d_debug_location(DWORD location) DECLSPEC_HIDDEN; + +struct wined3d_blt_info +{ + GLenum bind_target; + struct wined3d_vec3 texcoords[4]; +}; + +struct wined3d_texture_ops +{ + BOOL (*texture_prepare_location)(struct wined3d_texture *texture, unsigned int sub_resource_idx, + struct wined3d_context *context, unsigned int location); + BOOL (*texture_load_location)(struct wined3d_texture *texture, unsigned int sub_resource_idx, + struct wined3d_context *context, unsigned int location); + void (*texture_unload_location)(struct wined3d_texture *texture, + struct wined3d_context *context, unsigned int location); + void (*texture_upload_data)(struct wined3d_context *context, const struct wined3d_const_bo_address *src_bo_addr, + const struct wined3d_format *src_format, const struct wined3d_box *src_box, unsigned int src_row_pitch, + unsigned int src_slice_pitch, struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx, + unsigned int dst_location, unsigned int dst_x, unsigned int dst_y, unsigned int dst_z); + void (*texture_download_data)(struct wined3d_context *context, struct wined3d_texture *src_texture, + unsigned int src_sub_resource_idx, unsigned int src_location, const struct wined3d_box *src_box, + const struct wined3d_bo_address *dst_bo_addr, const struct wined3d_format *dst_format, + unsigned int dst_x, unsigned int dst_y, unsigned int dst_z, + unsigned int dst_row_pitch, unsigned int dst_slice_pitch); +}; + +#define WINED3D_TEXTURE_COND_NP2 0x00000001 +#define WINED3D_TEXTURE_COND_NP2_EMULATED 0x00000002 +#define WINED3D_TEXTURE_POW2_MAT_IDENT 0x00000004 +#define WINED3D_TEXTURE_IS_SRGB 0x00000008 +#define WINED3D_TEXTURE_RGB_ALLOCATED 0x00000010 +#define WINED3D_TEXTURE_RGB_VALID 0x00000020 +#define WINED3D_TEXTURE_SRGB_ALLOCATED 0x00000040 +#define WINED3D_TEXTURE_SRGB_VALID 0x00000080 +#define WINED3D_TEXTURE_CONVERTED 0x00000100 +#define WINED3D_TEXTURE_PIN_SYSMEM 0x00000200 +#define WINED3D_TEXTURE_NORMALIZED_COORDS 0x00000400 +#define WINED3D_TEXTURE_GET_DC_LENIENT 0x00000800 +#define WINED3D_TEXTURE_DC_IN_USE 0x00001000 +#define WINED3D_TEXTURE_DISCARD 0x00002000 +#define WINED3D_TEXTURE_GET_DC 0x00004000 +#define WINED3D_TEXTURE_GENERATE_MIPMAPS 0x00008000 +#define WINED3D_TEXTURE_DOWNLOADABLE 0x00010000 + +#define WINED3D_TEXTURE_ASYNC_COLOR_KEY 0x00000001 + +struct wined3d_texture +{ + struct wined3d_resource resource; + const struct wined3d_texture_ops *texture_ops; + struct wined3d_swapchain *swapchain; + unsigned int pow2_width; + unsigned int pow2_height; + UINT layer_count; + UINT level_count; + unsigned int download_count; + unsigned int sysmem_count; + float pow2_matrix[16]; + UINT lod; + DWORD sampler; + DWORD flags; + DWORD update_map_binding; + + unsigned int row_pitch; + unsigned int slice_pitch; + + /* May only be accessed from the command stream worker thread. */ + struct wined3d_texture_async + { + DWORD flags; + + /* Color keys for DDraw */ + struct wined3d_color_key dst_blt_color_key; + struct wined3d_color_key src_blt_color_key; + struct wined3d_color_key dst_overlay_color_key; + struct wined3d_color_key src_overlay_color_key; + struct wined3d_color_key gl_color_key; + DWORD color_key_flags; + } async; + + struct wined3d_dirty_regions + { + struct wined3d_box *boxes; + SIZE_T boxes_size; + unsigned int box_count; + } *dirty_regions; + + struct wined3d_overlay_info + { + struct list entry; + struct list overlays; + struct wined3d_texture *dst_texture; + unsigned int dst_sub_resource_idx; + RECT src_rect; + RECT dst_rect; + } *overlay_info; + + struct wined3d_dc_info + { + HBITMAP bitmap; + HDC dc; + } *dc_info; + + struct wined3d_texture_sub_resource + { + void *parent; + const struct wined3d_parent_ops *parent_ops; + + unsigned int offset; + unsigned int size; + + unsigned int map_count; + uint32_t map_flags; + DWORD locations; + struct wined3d_bo_gl bo; + + void *user_memory; + } *sub_resources; +}; + +static inline void *wined3d_texture_allocate_object_memory(SIZE_T s, SIZE_T level_count, SIZE_T layer_count) +{ + struct wined3d_texture *t; + + if (level_count > ((~(SIZE_T)0 - s) / sizeof(*t->sub_resources)) / layer_count) + return NULL; + + return heap_alloc_zero(s + level_count * layer_count * sizeof(*t->sub_resources)); +} + +static inline struct wined3d_texture *texture_from_resource(struct wined3d_resource *resource) +{ + return CONTAINING_RECORD(resource, struct wined3d_texture, resource); +} + +static inline unsigned int wined3d_texture_get_level_width(const struct wined3d_texture *texture, + unsigned int level) +{ + return max(1, texture->resource.width >> level); +} + +static inline unsigned int wined3d_texture_get_level_height(const struct wined3d_texture *texture, + unsigned int level) +{ + return max(1, texture->resource.height >> level); +} + +static inline unsigned int wined3d_texture_get_level_depth(const struct wined3d_texture *texture, + unsigned int level) +{ + return max(1, texture->resource.depth >> level); +} + +static inline unsigned int wined3d_texture_get_level_pow2_width(const struct wined3d_texture *texture, + unsigned int level) +{ + return max(1, texture->pow2_width >> level); +} + +static inline unsigned int wined3d_texture_get_level_pow2_height(const struct wined3d_texture *texture, + unsigned int level) +{ + return max(1, texture->pow2_height >> level); +} + +static inline void wined3d_texture_get_level_box(const struct wined3d_texture *texture, + unsigned int level, struct wined3d_box *box) +{ + wined3d_box_set(box, 0, 0, + wined3d_texture_get_level_width(texture, level), + wined3d_texture_get_level_height(texture, level), + 0, wined3d_texture_get_level_depth(texture, level)); +} + +static inline bool wined3d_texture_is_full_rect(const struct wined3d_texture *texture, + unsigned int level, const RECT *r) +{ + unsigned int t; + + t = wined3d_texture_get_level_width(texture, level); + if ((r->left && r->right) || abs(r->right - r->left) != t) + return false; + t = wined3d_texture_get_level_height(texture, level); + if ((r->top && r->bottom) || abs(r->bottom - r->top) != t) + return false; + return true; +} + +HRESULT texture2d_blt(struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx, + const struct wined3d_box *dst_box, struct wined3d_texture *src_texture, + unsigned int src_sub_resource_idx, const struct wined3d_box *src_box, DWORD flags, + const struct wined3d_blt_fx *blt_fx, enum wined3d_texture_filter_type filter) DECLSPEC_HIDDEN; +void texture2d_get_blt_info(const struct wined3d_texture_gl *texture_gl, unsigned int sub_resource_idx, + const RECT *rect, struct wined3d_blt_info *info) DECLSPEC_HIDDEN; +void texture2d_load_fb_texture(struct wined3d_texture_gl *texture_gl, unsigned int sub_resource_idx, + BOOL srgb, struct wined3d_context *context) DECLSPEC_HIDDEN; +void texture2d_read_from_framebuffer(struct wined3d_texture *texture, unsigned int sub_resource_idx, + struct wined3d_context *context, DWORD src_location, DWORD dst_location) DECLSPEC_HIDDEN; + +HRESULT wined3d_texture_check_box_dimensions(const struct wined3d_texture *texture, + unsigned int level, const struct wined3d_box *box) DECLSPEC_HIDDEN; +void wined3d_texture_cleanup(struct wined3d_texture *texture) DECLSPEC_HIDDEN; +void wined3d_texture_download_from_texture(struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx, + struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx) DECLSPEC_HIDDEN; +GLenum wined3d_texture_get_gl_buffer(const struct wined3d_texture *texture) DECLSPEC_HIDDEN; +void wined3d_texture_get_memory(struct wined3d_texture *texture, unsigned int sub_resource_idx, + struct wined3d_bo_address *data, DWORD locations) DECLSPEC_HIDDEN; +void wined3d_texture_invalidate_location(struct wined3d_texture *texture, + unsigned int sub_resource_idx, DWORD location) DECLSPEC_HIDDEN; +void wined3d_texture_load(struct wined3d_texture *texture, + struct wined3d_context *context, BOOL srgb) DECLSPEC_HIDDEN; +BOOL wined3d_texture_load_location(struct wined3d_texture *texture, + unsigned int sub_resource_idx, struct wined3d_context *context, DWORD location) DECLSPEC_HIDDEN; +BOOL wined3d_texture_prepare_location(struct wined3d_texture *texture, unsigned int sub_resource_idx, + struct wined3d_context *context, DWORD location) DECLSPEC_HIDDEN; +void wined3d_texture_set_map_binding(struct wined3d_texture *texture, DWORD map_binding) DECLSPEC_HIDDEN; +void wined3d_texture_set_swapchain(struct wined3d_texture *texture, + struct wined3d_swapchain *swapchain) DECLSPEC_HIDDEN; +void wined3d_texture_sub_resources_destroyed(struct wined3d_texture *texture) DECLSPEC_HIDDEN; +void wined3d_texture_translate_drawable_coords(const struct wined3d_texture *texture, + HWND window, RECT *rect) DECLSPEC_HIDDEN; +void wined3d_texture_upload_from_texture(struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx, + unsigned int dst_x, unsigned int dst_y, unsigned int dst_z, struct wined3d_texture *src_texture, + unsigned int src_sub_resource_idx, const struct wined3d_box *src_box) DECLSPEC_HIDDEN; +void wined3d_texture_validate_location(struct wined3d_texture *texture, + unsigned int sub_resource_idx, DWORD location) DECLSPEC_HIDDEN; +void wined3d_texture_clear_dirty_regions(struct wined3d_texture *texture) DECLSPEC_HIDDEN; + +HRESULT wined3d_texture_no3d_init(struct wined3d_texture *texture_no3d, struct wined3d_device *device, + const struct wined3d_resource_desc *desc, unsigned int layer_count, unsigned int level_count, + uint32_t flags, void *parent, const struct wined3d_parent_ops *parent_ops) DECLSPEC_HIDDEN; + +void wined3d_gl_texture_swizzle_from_color_fixup(GLint swizzle[4], struct color_fixup_desc fixup) DECLSPEC_HIDDEN; +void wined3d_vk_swizzle_from_color_fixup(VkComponentMapping *mapping, struct color_fixup_desc fixup) DECLSPEC_HIDDEN; + +struct gl_texture +{ + struct wined3d_sampler_desc sampler_desc; + unsigned int base_level; + GLuint name; +}; + +struct wined3d_texture_gl +{ + struct wined3d_texture t; + + struct gl_texture texture_rgb, texture_srgb; + + GLenum target; + + GLuint rb_multisample; + GLuint rb_resolved; + + struct list renderbuffers; + const struct wined3d_renderbuffer_entry *current_renderbuffer; +}; + +static inline struct wined3d_texture_gl *wined3d_texture_gl(struct wined3d_texture *texture) +{ + return CONTAINING_RECORD(texture, struct wined3d_texture_gl, t); +} + +static inline struct gl_texture *wined3d_texture_gl_get_gl_texture(struct wined3d_texture_gl *texture_gl, + BOOL srgb) +{ + return srgb ? &texture_gl->texture_srgb : &texture_gl->texture_rgb; +} + +static inline GLenum wined3d_texture_gl_get_sub_resource_target(const struct wined3d_texture_gl *texture_gl, + unsigned int sub_resource_idx) +{ + static const GLenum cube_targets[] = + { + GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB, + GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB, + GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB, + GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB, + GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB, + GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB, + }; + + return texture_gl->t.resource.usage & WINED3DUSAGE_LEGACY_CUBEMAP + ? cube_targets[sub_resource_idx / texture_gl->t.level_count] : texture_gl->target; +} + +static inline BOOL wined3d_texture_gl_is_multisample_location(const struct wined3d_texture_gl *texture_gl, + DWORD location) +{ + if (location == WINED3D_LOCATION_RB_MULTISAMPLE) + return TRUE; + if (location != WINED3D_LOCATION_TEXTURE_RGB && location != WINED3D_LOCATION_TEXTURE_SRGB) + return FALSE; + return texture_gl->target == GL_TEXTURE_2D_MULTISAMPLE || texture_gl->target == GL_TEXTURE_2D_MULTISAMPLE_ARRAY; +} + +void wined3d_texture_gl_apply_sampler_desc(struct wined3d_texture_gl *texture_gl, + const struct wined3d_sampler_desc *sampler_desc, const struct wined3d_context_gl *context_gl) DECLSPEC_HIDDEN; +void wined3d_texture_gl_bind(struct wined3d_texture_gl *texture_gl, + struct wined3d_context_gl *context_gl, BOOL srgb) DECLSPEC_HIDDEN; +void wined3d_texture_gl_bind_and_dirtify(struct wined3d_texture_gl *texture_gl, + struct wined3d_context_gl *context_gl, BOOL srgb) DECLSPEC_HIDDEN; +HRESULT wined3d_texture_gl_init(struct wined3d_texture_gl *texture_gl, struct wined3d_device *device, + const struct wined3d_resource_desc *desc, unsigned int layer_count, unsigned int level_count, + uint32_t flags, void *parent, const struct wined3d_parent_ops *parent_ops) DECLSPEC_HIDDEN; +void wined3d_texture_gl_prepare_texture(struct wined3d_texture_gl *texture_gl, + struct wined3d_context_gl *context_gl, BOOL srgb) DECLSPEC_HIDDEN; +void wined3d_texture_gl_set_compatible_renderbuffer(struct wined3d_texture_gl *texture_gl, + struct wined3d_context_gl *context_gl, unsigned int level, + const struct wined3d_rendertarget_info *rt) DECLSPEC_HIDDEN; + +struct wined3d_texture_vk +{ + struct wined3d_texture t; + + VkImage vk_image; + struct wined3d_allocator_block *memory; + VkDeviceMemory vk_memory; + enum VkImageLayout layout; + uint32_t bind_mask; + uint64_t command_buffer_id; + + VkDescriptorImageInfo default_image_info; +}; + +static inline struct wined3d_texture_vk *wined3d_texture_vk(struct wined3d_texture *texture) +{ + return CONTAINING_RECORD(texture, struct wined3d_texture_vk, t); +} + +void wined3d_texture_vk_barrier(struct wined3d_texture_vk *texture_vk, + struct wined3d_context_vk *context_vk, uint32_t bind_mask) DECLSPEC_HIDDEN; +const VkDescriptorImageInfo *wined3d_texture_vk_get_default_image_info(struct wined3d_texture_vk *texture_vk, + struct wined3d_context_vk *context_vk) DECLSPEC_HIDDEN; +HRESULT wined3d_texture_vk_init(struct wined3d_texture_vk *texture_vk, struct wined3d_device *device, + const struct wined3d_resource_desc *desc, unsigned int layer_count, unsigned int level_count, + uint32_t flags, void *parent, const struct wined3d_parent_ops *parent_ops) DECLSPEC_HIDDEN; +BOOL wined3d_texture_vk_prepare_texture(struct wined3d_texture_vk *texture_vk, + struct wined3d_context_vk *context_vk) DECLSPEC_HIDDEN; + +struct wined3d_renderbuffer_entry +{ + struct list entry; + GLuint id; + UINT width; + UINT height; +}; + +struct wined3d_fbo_resource +{ + GLuint object; + GLenum target; + GLuint level, layer; +}; + +#define WINED3D_FBO_ENTRY_FLAG_ATTACHED 0x1 +#define WINED3D_FBO_ENTRY_FLAG_DEPTH 0x2 +#define WINED3D_FBO_ENTRY_FLAG_STENCIL 0x4 + +struct fbo_entry +{ + struct list entry; + DWORD flags; + DWORD rt_mask; + GLuint id; + struct wined3d_fbo_entry_key + { + DWORD rb_namespace; + struct wined3d_fbo_resource objects[WINED3D_MAX_RENDER_TARGETS + 1]; + } key; +}; + +struct wined3d_sampler +{ + struct wine_rb_entry entry; + LONG refcount; + struct wined3d_device *device; + void *parent; + const struct wined3d_parent_ops *parent_ops; + struct wined3d_sampler_desc desc; +}; + +struct wined3d_sampler_gl +{ + struct wined3d_sampler s; + + GLuint name; +}; + +static inline struct wined3d_sampler_gl *wined3d_sampler_gl(struct wined3d_sampler *sampler) +{ + return CONTAINING_RECORD(sampler, struct wined3d_sampler_gl, s); +} + +void wined3d_sampler_gl_bind(struct wined3d_sampler_gl *sampler_gl, unsigned int unit, + struct wined3d_texture_gl *texture_gl, const struct wined3d_context_gl *context_gl) DECLSPEC_HIDDEN; +void wined3d_sampler_gl_init(struct wined3d_sampler_gl *sampler_gl, + struct wined3d_device *device, const struct wined3d_sampler_desc *desc, + void *parent, const struct wined3d_parent_ops *parent_ops) DECLSPEC_HIDDEN; + +struct wined3d_sampler_vk +{ + struct wined3d_sampler s; + + VkDescriptorImageInfo vk_image_info; + uint64_t command_buffer_id; +}; + +static inline struct wined3d_sampler_vk *wined3d_sampler_vk(struct wined3d_sampler *sampler) +{ + return CONTAINING_RECORD(sampler, struct wined3d_sampler_vk, s); +} + +void wined3d_sampler_vk_init(struct wined3d_sampler_vk *sampler_vk, + struct wined3d_device *device, const struct wined3d_sampler_desc *desc, + void *parent, const struct wined3d_parent_ops *parent_ops) DECLSPEC_HIDDEN; + +struct wined3d_vertex_declaration_element +{ + const struct wined3d_format *format; + BOOL ffp_valid; + unsigned int input_slot; + unsigned int offset; + unsigned int output_slot; + enum wined3d_input_classification input_slot_class; + unsigned int instance_data_step_rate; + BYTE method; + BYTE usage; + BYTE usage_idx; +}; + +struct wined3d_vertex_declaration +{ + LONG ref; + void *parent; + const struct wined3d_parent_ops *parent_ops; + struct wined3d_device *device; + + struct wined3d_vertex_declaration_element *elements; + unsigned int element_count; + + BOOL position_transformed; +}; + +struct wined3d_saved_states +{ + uint32_t vs_consts_f[WINED3D_BITMAP_SIZE(WINED3D_MAX_VS_CONSTS_F)]; + WORD vertexShaderConstantsI; /* WINED3D_MAX_CONSTS_I, 16 */ + WORD vertexShaderConstantsB; /* WINED3D_MAX_CONSTS_B, 16 */ + uint32_t ps_consts_f[WINED3D_BITMAP_SIZE(WINED3D_MAX_PS_CONSTS_F)]; + WORD pixelShaderConstantsI; /* WINED3D_MAX_CONSTS_I, 16 */ + WORD pixelShaderConstantsB; /* WINED3D_MAX_CONSTS_B, 16 */ + uint32_t transform[WINED3D_BITMAP_SIZE(WINED3D_HIGHEST_TRANSFORM_STATE + 1)]; + WORD streamSource; /* WINED3D_MAX_STREAMS, 16 */ + WORD streamFreq; /* WINED3D_MAX_STREAMS, 16 */ + uint32_t renderState[WINED3D_BITMAP_SIZE(WINEHIGHEST_RENDER_STATE + 1)]; + DWORD textureState[WINED3D_MAX_TEXTURES]; /* WINED3D_HIGHEST_TEXTURE_STATE + 1, 18 */ + WORD samplerState[WINED3D_MAX_COMBINED_SAMPLERS]; /* WINED3D_HIGHEST_SAMPLER_STATE + 1, 14 */ + DWORD clipplane; /* WINED3D_MAX_CLIP_DISTANCES, 8 */ + DWORD textures : 20; /* WINED3D_MAX_COMBINED_SAMPLERS, 20 */ + DWORD indices : 1; + DWORD material : 1; + DWORD viewport : 1; + DWORD vertexDecl : 1; + DWORD pixelShader : 1; + DWORD vertexShader : 1; + DWORD scissorRect : 1; + DWORD store_stream_offset : 1; + DWORD alpha_to_coverage : 1; + DWORD lights : 1; + DWORD transforms : 1; + DWORD padding : 1; +}; + +struct StageState { + DWORD stage; + DWORD state; +}; + +struct wined3d_stateblock +{ + LONG ref; /* Note: Ref counting not required */ + struct wined3d_device *device; + + /* Array indicating whether things have been set or changed */ + struct wined3d_saved_states changed; + + struct wined3d_stateblock_state stateblock_state; + struct wined3d_light_state light_state; + + /* Contained state management */ + DWORD contained_render_states[WINEHIGHEST_RENDER_STATE + 1]; + unsigned int num_contained_render_states; + DWORD contained_transform_states[WINED3D_HIGHEST_TRANSFORM_STATE + 1]; + unsigned int num_contained_transform_states; + struct StageState contained_tss_states[WINED3D_MAX_TEXTURES * (WINED3D_HIGHEST_TEXTURE_STATE + 1)]; + unsigned int num_contained_tss_states; + struct StageState contained_sampler_states[WINED3D_MAX_COMBINED_SAMPLERS * WINED3D_HIGHEST_SAMPLER_STATE]; + unsigned int num_contained_sampler_states; +}; + +void wined3d_stateblock_state_init(struct wined3d_stateblock_state *state, + const struct wined3d_device *device, DWORD flags) DECLSPEC_HIDDEN; +void wined3d_stateblock_state_cleanup(struct wined3d_stateblock_state *state) DECLSPEC_HIDDEN; + +void wined3d_light_state_enable_light(struct wined3d_light_state *state, const struct wined3d_d3d_info *d3d_info, + struct wined3d_light_info *light_info, BOOL enable) DECLSPEC_HIDDEN; +struct wined3d_light_info *wined3d_light_state_get_light(const struct wined3d_light_state *state, + unsigned int idx) DECLSPEC_HIDDEN; +HRESULT wined3d_light_state_set_light(struct wined3d_light_state *state, DWORD light_idx, + const struct wined3d_light *params, struct wined3d_light_info **light_info) DECLSPEC_HIDDEN; + +void state_cleanup(struct wined3d_state *state) DECLSPEC_HIDDEN; +void state_init(struct wined3d_state *state, const struct wined3d_d3d_info *d3d_info, DWORD flags) DECLSPEC_HIDDEN; +void state_unbind_resources(struct wined3d_state *state) DECLSPEC_HIDDEN; + +enum wined3d_cs_queue_id +{ + WINED3D_CS_QUEUE_DEFAULT = 0, + WINED3D_CS_QUEUE_MAP, + WINED3D_CS_QUEUE_COUNT, +}; + +enum wined3d_push_constants +{ + WINED3D_PUSH_CONSTANTS_VS_F, + WINED3D_PUSH_CONSTANTS_PS_F, + WINED3D_PUSH_CONSTANTS_VS_I, + WINED3D_PUSH_CONSTANTS_PS_I, + WINED3D_PUSH_CONSTANTS_VS_B, + WINED3D_PUSH_CONSTANTS_PS_B, +}; + +#define WINED3D_CS_QUERY_POLL_INTERVAL 10u +#define WINED3D_CS_QUEUE_SIZE 0x100000u +#define WINED3D_CS_SPIN_COUNT 10000000u + +struct wined3d_cs_queue +{ + LONG head, tail; + BYTE data[WINED3D_CS_QUEUE_SIZE]; +}; + +struct wined3d_cs_ops +{ + void *(*require_space)(struct wined3d_cs *cs, size_t size, enum wined3d_cs_queue_id queue_id); + void (*submit)(struct wined3d_cs *cs, enum wined3d_cs_queue_id queue_id); + void (*finish)(struct wined3d_cs *cs, enum wined3d_cs_queue_id queue_id); + void (*push_constants)(struct wined3d_cs *cs, enum wined3d_push_constants p, + unsigned int start_idx, unsigned int count, const void *constants); +}; + +struct wined3d_cs +{ + const struct wined3d_cs_ops *ops; + struct wined3d_device *device; + struct wined3d_state state; + HMODULE wined3d_module; + HANDLE thread; + DWORD thread_id; + BOOL serialize_commands; + + struct wined3d_cs_queue queue[WINED3D_CS_QUEUE_COUNT]; + size_t data_size, start, end; + void *data; + struct list query_poll_list; + BOOL queries_flushed; + + HANDLE event; + BOOL waiting_for_event; + LONG pending_presents; +}; + +struct wined3d_cs *wined3d_cs_create(struct wined3d_device *device) DECLSPEC_HIDDEN; +void wined3d_cs_destroy(struct wined3d_cs *cs) DECLSPEC_HIDDEN; +void wined3d_cs_destroy_object(struct wined3d_cs *cs, + void (*callback)(void *object), void *object) DECLSPEC_HIDDEN; +void wined3d_cs_emit_add_dirty_texture_region(struct wined3d_cs *cs, + struct wined3d_texture *texture, unsigned int layer) DECLSPEC_HIDDEN; +void wined3d_cs_emit_blt_sub_resource(struct wined3d_cs *cs, struct wined3d_resource *dst_resource, + unsigned int dst_sub_resource_idx, const struct wined3d_box *dst_box, struct wined3d_resource *src_resource, + unsigned int src_sub_resource_idx, const struct wined3d_box *src_box, DWORD flags, + const struct wined3d_blt_fx *fx, enum wined3d_texture_filter_type filter) DECLSPEC_HIDDEN; +void wined3d_cs_emit_clear(struct wined3d_cs *cs, DWORD rect_count, const RECT *rects, + DWORD flags, const struct wined3d_color *color, float depth, DWORD stencil) DECLSPEC_HIDDEN; +void wined3d_cs_emit_clear_rendertarget_view(struct wined3d_cs *cs, struct wined3d_rendertarget_view *view, + const RECT *rect, DWORD flags, const struct wined3d_color *color, float depth, DWORD stencil) DECLSPEC_HIDDEN; +void wined3d_cs_emit_clear_unordered_access_view_uint(struct wined3d_cs *cs, + struct wined3d_unordered_access_view *view, const struct wined3d_uvec4 *clear_value) DECLSPEC_HIDDEN; +void wined3d_cs_emit_copy_uav_counter(struct wined3d_cs *cs, struct wined3d_buffer *dst_buffer, + unsigned int offset, struct wined3d_unordered_access_view *uav) DECLSPEC_HIDDEN; +void wined3d_cs_emit_dispatch(struct wined3d_cs *cs, + unsigned int group_count_x, unsigned int group_count_y, unsigned int group_count_z) DECLSPEC_HIDDEN; +void wined3d_cs_emit_dispatch_indirect(struct wined3d_cs *cs, + struct wined3d_buffer *buffer, unsigned int offset) DECLSPEC_HIDDEN; +void wined3d_cs_emit_draw(struct wined3d_cs *cs, enum wined3d_primitive_type primitive_type, + unsigned int patch_vertex_count, int base_vertex_idx, unsigned int start_idx, unsigned int index_count, + unsigned int start_instance, unsigned int instance_count, bool indexed) DECLSPEC_HIDDEN; +void wined3d_cs_emit_draw_indirect(struct wined3d_cs *cs, enum wined3d_primitive_type primitive_type, + unsigned int patch_vertex_count, struct wined3d_buffer *buffer, + unsigned int offset, bool indexed) DECLSPEC_HIDDEN; +void wined3d_cs_emit_flush(struct wined3d_cs *cs) DECLSPEC_HIDDEN; +void wined3d_cs_emit_generate_mipmaps(struct wined3d_cs *cs, struct wined3d_shader_resource_view *view) DECLSPEC_HIDDEN; +void wined3d_cs_emit_preload_resource(struct wined3d_cs *cs, struct wined3d_resource *resource) DECLSPEC_HIDDEN; +void wined3d_cs_emit_present(struct wined3d_cs *cs, struct wined3d_swapchain *swapchain, const RECT *src_rect, + const RECT *dst_rect, HWND dst_window_override, unsigned int swap_interval, DWORD flags) DECLSPEC_HIDDEN; +void wined3d_cs_emit_query_issue(struct wined3d_cs *cs, struct wined3d_query *query, DWORD flags) DECLSPEC_HIDDEN; +void wined3d_cs_emit_reset_state(struct wined3d_cs *cs) DECLSPEC_HIDDEN; +void wined3d_cs_emit_set_blend_state(struct wined3d_cs *cs, struct wined3d_blend_state *state, + const struct wined3d_color *blend_factor, unsigned int sample_mask) DECLSPEC_HIDDEN; +void wined3d_cs_emit_set_clip_plane(struct wined3d_cs *cs, UINT plane_idx, + const struct wined3d_vec4 *plane) DECLSPEC_HIDDEN; +void wined3d_cs_emit_set_color_key(struct wined3d_cs *cs, struct wined3d_texture *texture, + WORD flags, const struct wined3d_color_key *color_key) DECLSPEC_HIDDEN; +void wined3d_cs_emit_set_constant_buffer(struct wined3d_cs *cs, enum wined3d_shader_type type, + UINT cb_idx, struct wined3d_buffer *buffer) DECLSPEC_HIDDEN; +void wined3d_cs_emit_set_depth_stencil_state(struct wined3d_cs *cs, + struct wined3d_depth_stencil_state *state, unsigned int stencil_ref) DECLSPEC_HIDDEN; +void wined3d_cs_emit_set_depth_stencil_view(struct wined3d_cs *cs, + struct wined3d_rendertarget_view *view) DECLSPEC_HIDDEN; +void wined3d_cs_emit_set_index_buffer(struct wined3d_cs *cs, struct wined3d_buffer *buffer, + enum wined3d_format_id format_id, unsigned int offset) DECLSPEC_HIDDEN; +void wined3d_cs_emit_set_light(struct wined3d_cs *cs, const struct wined3d_light_info *light) DECLSPEC_HIDDEN; +void wined3d_cs_emit_set_light_enable(struct wined3d_cs *cs, unsigned int idx, BOOL enable) DECLSPEC_HIDDEN; +void wined3d_cs_emit_set_material(struct wined3d_cs *cs, const struct wined3d_material *material) DECLSPEC_HIDDEN; +void wined3d_cs_emit_set_predication(struct wined3d_cs *cs, + struct wined3d_query *predicate, BOOL value) DECLSPEC_HIDDEN; +void wined3d_cs_emit_set_rasterizer_state(struct wined3d_cs *cs, + struct wined3d_rasterizer_state *rasterizer_state) DECLSPEC_HIDDEN; +void wined3d_cs_emit_set_render_state(struct wined3d_cs *cs, + enum wined3d_render_state state, DWORD value) DECLSPEC_HIDDEN; +void wined3d_cs_emit_set_rendertarget_view(struct wined3d_cs *cs, unsigned int view_idx, + struct wined3d_rendertarget_view *view) DECLSPEC_HIDDEN; +void wined3d_cs_emit_set_shader_resource_view(struct wined3d_cs *cs, enum wined3d_shader_type type, + UINT view_idx, struct wined3d_shader_resource_view *view) DECLSPEC_HIDDEN; +void wined3d_cs_emit_set_sampler(struct wined3d_cs *cs, enum wined3d_shader_type type, + UINT sampler_idx, struct wined3d_sampler *sampler) DECLSPEC_HIDDEN; +void wined3d_cs_emit_set_sampler_state(struct wined3d_cs *cs, UINT sampler_idx, + enum wined3d_sampler_state state, DWORD value) DECLSPEC_HIDDEN; +void wined3d_cs_emit_set_scissor_rects(struct wined3d_cs *cs, unsigned int rect_count, const RECT *rects) DECLSPEC_HIDDEN; +void wined3d_cs_emit_set_shader(struct wined3d_cs *cs, enum wined3d_shader_type type, + struct wined3d_shader *shader) DECLSPEC_HIDDEN; +void wined3d_cs_emit_set_stream_output(struct wined3d_cs *cs, UINT stream_idx, + struct wined3d_buffer *buffer, UINT offset) DECLSPEC_HIDDEN; +void wined3d_cs_emit_set_stream_source(struct wined3d_cs *cs, UINT stream_idx, + struct wined3d_buffer *buffer, UINT offset, UINT stride) DECLSPEC_HIDDEN; +void wined3d_cs_emit_set_stream_source_freq(struct wined3d_cs *cs, UINT stream_idx, + UINT frequency, UINT flags) DECLSPEC_HIDDEN; +void wined3d_cs_emit_set_texture(struct wined3d_cs *cs, UINT stage, struct wined3d_texture *texture) DECLSPEC_HIDDEN; +void wined3d_cs_emit_set_texture_state(struct wined3d_cs *cs, UINT stage, + enum wined3d_texture_stage_state state, DWORD value) DECLSPEC_HIDDEN; +void wined3d_cs_emit_set_transform(struct wined3d_cs *cs, enum wined3d_transform_state state, + const struct wined3d_matrix *matrix) DECLSPEC_HIDDEN; +void wined3d_cs_emit_set_unordered_access_view(struct wined3d_cs *cs, enum wined3d_pipeline pipeline, + unsigned int view_idx, struct wined3d_unordered_access_view *view, + unsigned int initial_count) DECLSPEC_HIDDEN; +void wined3d_cs_emit_set_vertex_declaration(struct wined3d_cs *cs, + struct wined3d_vertex_declaration *declaration) DECLSPEC_HIDDEN; +void wined3d_cs_emit_set_viewports(struct wined3d_cs *cs, unsigned int viewport_count, const struct wined3d_viewport *viewports) DECLSPEC_HIDDEN; +void wined3d_cs_emit_unload_resource(struct wined3d_cs *cs, struct wined3d_resource *resource) DECLSPEC_HIDDEN; +void wined3d_cs_emit_update_sub_resource(struct wined3d_cs *cs, struct wined3d_resource *resource, + unsigned int sub_resource_idx, const struct wined3d_box *box, const void *data, unsigned int row_pitch, + unsigned int slice_pitch) DECLSPEC_HIDDEN; +void wined3d_cs_init_object(struct wined3d_cs *cs, + void (*callback)(void *object), void *object) DECLSPEC_HIDDEN; +HRESULT wined3d_cs_map(struct wined3d_cs *cs, struct wined3d_resource *resource, unsigned int sub_resource_idx, + struct wined3d_map_desc *map_desc, const struct wined3d_box *box, unsigned int flags) DECLSPEC_HIDDEN; +HRESULT wined3d_cs_unmap(struct wined3d_cs *cs, struct wined3d_resource *resource, + unsigned int sub_resource_idx) DECLSPEC_HIDDEN; + +static inline void wined3d_cs_finish(struct wined3d_cs *cs, enum wined3d_cs_queue_id queue_id) +{ + cs->ops->finish(cs, queue_id); +} + +static inline void wined3d_cs_push_constants(struct wined3d_cs *cs, enum wined3d_push_constants p, + unsigned int start_idx, unsigned int count, const void *constants) +{ + cs->ops->push_constants(cs, p, start_idx, count, constants); +} + +static inline void wined3d_resource_wait_idle(struct wined3d_resource *resource) +{ + const struct wined3d_cs *cs = resource->device->cs; + + if (!cs->thread || cs->thread_id == GetCurrentThreadId()) + return; + + while (InterlockedCompareExchange(&resource->access_count, 0, 0)) + YieldProcessor(); +} + +/* TODO: Add tests and support for FLOAT16_4 POSITIONT, D3DCOLOR position, other + * fixed function semantics as D3DCOLOR or FLOAT16 */ +enum wined3d_buffer_conversion_type +{ + CONV_NONE, + CONV_D3DCOLOR, + CONV_POSITIONT, +}; + +struct wined3d_buffer_ops +{ + BOOL (*buffer_prepare_location)(struct wined3d_buffer *buffer, + struct wined3d_context *context, unsigned int location); + void (*buffer_unload_location)(struct wined3d_buffer *buffer, + struct wined3d_context *context, unsigned int location); + void (*buffer_upload_ranges)(struct wined3d_buffer *buffer, struct wined3d_context *context, const void *data, + unsigned int data_offset, unsigned int range_count, const struct wined3d_range *ranges); + void (*buffer_download_ranges)(struct wined3d_buffer *buffer, struct wined3d_context *context, void *data, + unsigned int data_offset, unsigned int range_count, const struct wined3d_range *ranges); +}; + +struct wined3d_buffer +{ + struct wined3d_resource resource; + const struct wined3d_buffer_ops *buffer_ops; + + unsigned int structure_byte_stride; + DWORD flags; + DWORD locations; + void *map_ptr; + uintptr_t buffer_object; + + struct wined3d_range *maps; + SIZE_T maps_size, modified_areas; + + /* conversion stuff */ + UINT decl_change_count, full_conversion_count; + UINT draw_count; + UINT stride; /* 0 if no conversion */ + enum wined3d_buffer_conversion_type *conversion_map; /* NULL if no conversion */ + UINT conversion_stride; /* 0 if no shifted conversion */ +}; + +static inline struct wined3d_buffer *buffer_from_resource(struct wined3d_resource *resource) +{ + return CONTAINING_RECORD(resource, struct wined3d_buffer, resource); +} + +void wined3d_buffer_cleanup(struct wined3d_buffer *buffer) DECLSPEC_HIDDEN; +void wined3d_buffer_copy(struct wined3d_buffer *dst_buffer, unsigned int dst_offset, + struct wined3d_buffer *src_buffer, unsigned int src_offset, unsigned int size) DECLSPEC_HIDDEN; +DWORD wined3d_buffer_get_memory(struct wined3d_buffer *buffer, + struct wined3d_bo_address *data, DWORD locations) DECLSPEC_HIDDEN; +void wined3d_buffer_invalidate_location(struct wined3d_buffer *buffer, DWORD location) DECLSPEC_HIDDEN; +void wined3d_buffer_load(struct wined3d_buffer *buffer, struct wined3d_context *context, + const struct wined3d_state *state) DECLSPEC_HIDDEN; +BOOL wined3d_buffer_load_location(struct wined3d_buffer *buffer, + struct wined3d_context *context, DWORD location) DECLSPEC_HIDDEN; +BYTE *wined3d_buffer_load_sysmem(struct wined3d_buffer *buffer, struct wined3d_context *context) DECLSPEC_HIDDEN; +BOOL wined3d_buffer_prepare_location(struct wined3d_buffer *buffer, + struct wined3d_context *context, unsigned int location) DECLSPEC_HIDDEN; +void wined3d_buffer_upload_data(struct wined3d_buffer *buffer, struct wined3d_context *context, + const struct wined3d_box *box, const void *data) DECLSPEC_HIDDEN; + +HRESULT wined3d_buffer_no3d_init(struct wined3d_buffer *buffer_no3d, struct wined3d_device *device, + const struct wined3d_buffer_desc *desc, const struct wined3d_sub_resource_data *data, + void *parent, const struct wined3d_parent_ops *parent_ops) DECLSPEC_HIDDEN; + +struct wined3d_buffer_gl +{ + struct wined3d_buffer b; + + struct wined3d_bo_gl bo; + struct wined3d_bo_user bo_user; +}; + +static inline struct wined3d_buffer_gl *wined3d_buffer_gl(struct wined3d_buffer *buffer) +{ + return CONTAINING_RECORD(buffer, struct wined3d_buffer_gl, b); +} + +static inline const struct wined3d_buffer_gl *wined3d_buffer_gl_const(const struct wined3d_buffer *buffer) +{ + return CONTAINING_RECORD(buffer, struct wined3d_buffer_gl, b); +} + +GLenum wined3d_buffer_gl_binding_from_bind_flags(const struct wined3d_gl_info *gl_info, + uint32_t bind_flags) DECLSPEC_HIDDEN; +HRESULT wined3d_buffer_gl_init(struct wined3d_buffer_gl *buffer_gl, struct wined3d_device *device, + const struct wined3d_buffer_desc *desc, const struct wined3d_sub_resource_data *data, + void *parent, const struct wined3d_parent_ops *parent_ops) DECLSPEC_HIDDEN; + +struct wined3d_buffer_vk +{ + struct wined3d_buffer b; + + struct wined3d_bo_vk bo; + struct wined3d_bo_user bo_user; + VkDescriptorBufferInfo buffer_info; + uint32_t bind_mask; +}; + +static inline struct wined3d_buffer_vk *wined3d_buffer_vk(struct wined3d_buffer *buffer) +{ + return CONTAINING_RECORD(buffer, struct wined3d_buffer_vk, b); +} + +void wined3d_buffer_vk_barrier(struct wined3d_buffer_vk *buffer_vk, + struct wined3d_context_vk *context_vk, uint32_t bind_mask) DECLSPEC_HIDDEN; +const VkDescriptorBufferInfo *wined3d_buffer_vk_get_buffer_info(struct wined3d_buffer_vk *buffer_vk) DECLSPEC_HIDDEN; +HRESULT wined3d_buffer_vk_init(struct wined3d_buffer_vk *buffer_vk, struct wined3d_device *device, + const struct wined3d_buffer_desc *desc, const struct wined3d_sub_resource_data *data, + void *parent, const struct wined3d_parent_ops *parent_ops) DECLSPEC_HIDDEN; + +static inline void wined3d_resource_vk_barrier(struct wined3d_resource *resource, + struct wined3d_context_vk *context_vk, uint32_t bind_mask) +{ + if (resource->type == WINED3D_RTYPE_BUFFER) + wined3d_buffer_vk_barrier(wined3d_buffer_vk(buffer_from_resource(resource)), context_vk, bind_mask); + else + wined3d_texture_vk_barrier(wined3d_texture_vk(texture_from_resource(resource)), context_vk, bind_mask); +} + +struct wined3d_rendertarget_view +{ + LONG refcount; + + struct wined3d_resource *resource; + void *parent; + const struct wined3d_parent_ops *parent_ops; + + const struct wined3d_format *format; + unsigned int format_flags; + unsigned int sub_resource_idx; + unsigned int layer_count; + + unsigned int width; + unsigned int height; + + struct wined3d_view_desc desc; +}; + +void wined3d_rendertarget_view_cleanup(struct wined3d_rendertarget_view *view) DECLSPEC_HIDDEN; +void wined3d_rendertarget_view_get_drawable_size(const struct wined3d_rendertarget_view *view, + const struct wined3d_context *context, unsigned int *width, unsigned int *height) DECLSPEC_HIDDEN; +void wined3d_rendertarget_view_invalidate_location(struct wined3d_rendertarget_view *view, + DWORD location) DECLSPEC_HIDDEN; +void wined3d_rendertarget_view_load_location(struct wined3d_rendertarget_view *view, + struct wined3d_context *context, DWORD location) DECLSPEC_HIDDEN; +void wined3d_rendertarget_view_prepare_location(struct wined3d_rendertarget_view *view, + struct wined3d_context *context, DWORD location) DECLSPEC_HIDDEN; +void wined3d_rendertarget_view_validate_location(struct wined3d_rendertarget_view *view, + DWORD location) DECLSPEC_HIDDEN; + +HRESULT wined3d_rendertarget_view_no3d_init(struct wined3d_rendertarget_view *view_no3d, + const struct wined3d_view_desc *desc, struct wined3d_resource *resource, + void *parent, const struct wined3d_parent_ops *parent_ops) DECLSPEC_HIDDEN; + +struct wined3d_rendertarget_view_gl +{ + struct wined3d_rendertarget_view v; + struct wined3d_gl_view gl_view; +}; + +static inline struct wined3d_rendertarget_view_gl *wined3d_rendertarget_view_gl( + struct wined3d_rendertarget_view *view) +{ + return CONTAINING_RECORD(view, struct wined3d_rendertarget_view_gl, v); +} + +HRESULT wined3d_rendertarget_view_gl_init(struct wined3d_rendertarget_view_gl *view_gl, + const struct wined3d_view_desc *desc, struct wined3d_resource *resource, + void *parent, const struct wined3d_parent_ops *parent_ops) DECLSPEC_HIDDEN; + +struct wined3d_rendertarget_view_vk +{ + struct wined3d_rendertarget_view v; + + VkImageView vk_image_view; + uint64_t command_buffer_id; +}; + +static inline struct wined3d_rendertarget_view_vk *wined3d_rendertarget_view_vk( + struct wined3d_rendertarget_view *view) +{ + return CONTAINING_RECORD(view, struct wined3d_rendertarget_view_vk, v); +} + +static inline void wined3d_rendertarget_view_vk_barrier(struct wined3d_rendertarget_view_vk *rtv_vk, + struct wined3d_context_vk *context_vk, uint32_t bind_mask) +{ + wined3d_resource_vk_barrier(rtv_vk->v.resource, context_vk, bind_mask); +} + +static inline VkImageView wined3d_rendertarget_view_vk_get_image_view(struct wined3d_rendertarget_view_vk *rtv_vk, + struct wined3d_context_vk *context_vk) +{ + struct wined3d_texture_vk *texture_vk; + + if (rtv_vk->vk_image_view) + return rtv_vk->vk_image_view; + + texture_vk = wined3d_texture_vk(wined3d_texture_from_resource(rtv_vk->v.resource)); + return wined3d_texture_vk_get_default_image_info(texture_vk, context_vk)->imageView; +} + +HRESULT wined3d_rendertarget_view_vk_init(struct wined3d_rendertarget_view_vk *view_vk, + const struct wined3d_view_desc *desc, struct wined3d_resource *resource, + void *parent, const struct wined3d_parent_ops *parent_ops) DECLSPEC_HIDDEN; + +struct wined3d_shader_resource_view +{ + LONG refcount; + + struct wined3d_resource *resource; + void *parent; + const struct wined3d_parent_ops *parent_ops; + + const struct wined3d_format *format; + + struct wined3d_view_desc desc; +}; + +void wined3d_shader_resource_view_cleanup(struct wined3d_shader_resource_view *view) DECLSPEC_HIDDEN; +void shader_resource_view_generate_mipmaps(struct wined3d_shader_resource_view *view) DECLSPEC_HIDDEN; + +struct wined3d_shader_resource_view_gl +{ + struct wined3d_shader_resource_view v; + struct wined3d_bo_user bo_user; + struct wined3d_gl_view gl_view; +}; + +static inline struct wined3d_shader_resource_view_gl *wined3d_shader_resource_view_gl( + struct wined3d_shader_resource_view *view) +{ + return CONTAINING_RECORD(view, struct wined3d_shader_resource_view_gl, v); +} + +void wined3d_shader_resource_view_gl_bind(struct wined3d_shader_resource_view_gl *view_gl, unsigned int unit, + struct wined3d_sampler_gl *sampler_gl, struct wined3d_context_gl *context_gl) DECLSPEC_HIDDEN; +HRESULT wined3d_shader_resource_view_gl_init(struct wined3d_shader_resource_view_gl *view_gl, + const struct wined3d_view_desc *desc, struct wined3d_resource *resource, + void *parent, const struct wined3d_parent_ops *parent_ops) DECLSPEC_HIDDEN; +void wined3d_shader_resource_view_gl_update(struct wined3d_shader_resource_view_gl *srv_gl, + struct wined3d_context_gl *context_gl) DECLSPEC_HIDDEN; + +struct wined3d_view_vk +{ + struct wined3d_bo_user bo_user; + union + { + VkBufferView vk_buffer_view; + VkDescriptorImageInfo vk_image_info; + } u; + uint64_t command_buffer_id; +}; + +struct wined3d_shader_resource_view_vk +{ + struct wined3d_shader_resource_view v; + struct wined3d_view_vk view_vk; +}; + +static inline struct wined3d_shader_resource_view_vk *wined3d_shader_resource_view_vk( + struct wined3d_shader_resource_view *view) +{ + return CONTAINING_RECORD(view, struct wined3d_shader_resource_view_vk, v); +} + +static inline void wined3d_shader_resource_view_vk_barrier(struct wined3d_shader_resource_view_vk *srv_vk, + struct wined3d_context_vk *context_vk, uint32_t bind_mask) +{ + wined3d_resource_vk_barrier(srv_vk->v.resource, context_vk, bind_mask); +} + +HRESULT wined3d_shader_resource_view_vk_init(struct wined3d_shader_resource_view_vk *view_vk, + const struct wined3d_view_desc *desc, struct wined3d_resource *resource, + void *parent, const struct wined3d_parent_ops *parent_ops) DECLSPEC_HIDDEN; +void wined3d_shader_resource_view_vk_update(struct wined3d_shader_resource_view_vk *view_vk, + struct wined3d_context_vk *context_vk) DECLSPEC_HIDDEN; + +struct wined3d_unordered_access_view +{ + LONG refcount; + + struct wined3d_resource *resource; + void *parent; + const struct wined3d_parent_ops *parent_ops; + + const struct wined3d_format *format; + + struct wined3d_view_desc desc; + uintptr_t counter_bo; +}; + +void wined3d_unordered_access_view_cleanup(struct wined3d_unordered_access_view *view) DECLSPEC_HIDDEN; +void wined3d_unordered_access_view_copy_counter(struct wined3d_unordered_access_view *view, + struct wined3d_buffer *buffer, unsigned int offset, struct wined3d_context *context) DECLSPEC_HIDDEN; +void wined3d_unordered_access_view_invalidate_location(struct wined3d_unordered_access_view *view, + DWORD location) DECLSPEC_HIDDEN; +void wined3d_unordered_access_view_set_counter(struct wined3d_unordered_access_view *view, + unsigned int value) DECLSPEC_HIDDEN; + +struct wined3d_unordered_access_view_gl +{ + struct wined3d_unordered_access_view v; + struct wined3d_bo_user bo_user; + struct wined3d_gl_view gl_view; + struct wined3d_bo_gl counter_bo; +}; + +static inline struct wined3d_unordered_access_view_gl *wined3d_unordered_access_view_gl( + struct wined3d_unordered_access_view *view) +{ + return CONTAINING_RECORD(view, struct wined3d_unordered_access_view_gl, v); +} + +void wined3d_unordered_access_view_gl_clear_uint(struct wined3d_unordered_access_view_gl *view_gl, + const struct wined3d_uvec4 *clear_value, struct wined3d_context_gl *context_gl) DECLSPEC_HIDDEN; +HRESULT wined3d_unordered_access_view_gl_init(struct wined3d_unordered_access_view_gl *view_gl, + const struct wined3d_view_desc *desc, struct wined3d_resource *resource, + void *parent, const struct wined3d_parent_ops *parent_ops) DECLSPEC_HIDDEN; +void wined3d_unordered_access_view_gl_update(struct wined3d_unordered_access_view_gl *uav_gl, + struct wined3d_context_gl *context_gl) DECLSPEC_HIDDEN; + +struct wined3d_unordered_access_view_vk +{ + struct wined3d_unordered_access_view v; + struct wined3d_view_vk view_vk; + + VkBufferView vk_counter_view; + struct wined3d_bo_vk counter_bo; +}; + +static inline struct wined3d_unordered_access_view_vk *wined3d_unordered_access_view_vk( + struct wined3d_unordered_access_view *view) +{ + return CONTAINING_RECORD(view, struct wined3d_unordered_access_view_vk, v); +} + +static inline void wined3d_unordered_access_view_vk_barrier(struct wined3d_unordered_access_view_vk *uav_vk, + struct wined3d_context_vk *context_vk, uint32_t bind_mask) +{ + wined3d_resource_vk_barrier(uav_vk->v.resource, context_vk, bind_mask); +} + +void wined3d_unordered_access_view_vk_clear_uint(struct wined3d_unordered_access_view_vk *view_vk, + const struct wined3d_uvec4 *clear_value, struct wined3d_context_vk *context_vk) DECLSPEC_HIDDEN; +HRESULT wined3d_unordered_access_view_vk_init(struct wined3d_unordered_access_view_vk *view_vk, + const struct wined3d_view_desc *desc, struct wined3d_resource *resource, + void *parent, const struct wined3d_parent_ops *parent_ops) DECLSPEC_HIDDEN; +void wined3d_unordered_access_view_vk_update(struct wined3d_unordered_access_view_vk *view_vk, + struct wined3d_context_vk *context_vk) DECLSPEC_HIDDEN; + +struct wined3d_swapchain_state +{ + struct wined3d *wined3d; + struct wined3d_swapchain_desc desc; + struct wined3d_swapchain_state_parent *parent; + + struct wined3d_display_mode original_mode, d3d_mode; + RECT original_window_rect; + + /* Window styles to restore when switching fullscreen mode. */ + LONG style; + LONG exstyle; + HWND device_window; +}; + +void wined3d_swapchain_state_cleanup(struct wined3d_swapchain_state *state) DECLSPEC_HIDDEN; +void wined3d_swapchain_state_register(struct wined3d_swapchain_state *state) DECLSPEC_HIDDEN; +void wined3d_swapchain_state_restore_from_fullscreen(struct wined3d_swapchain_state *state, + HWND window, const RECT *window_rect) DECLSPEC_HIDDEN; +HRESULT wined3d_swapchain_state_setup_fullscreen(struct wined3d_swapchain_state *state, + HWND window, int x, int y, int width, int height) DECLSPEC_HIDDEN; + +struct wined3d_swapchain_ops +{ + void (*swapchain_present)(struct wined3d_swapchain *swapchain, + const RECT *src_rect, const RECT *dst_rect, unsigned int swap_interval, DWORD flags); + void (*swapchain_frontbuffer_updated)(struct wined3d_swapchain *swapchain); +}; + +struct wined3d_swapchain +{ + LONG ref; + void *parent; + const struct wined3d_parent_ops *parent_ops; + const struct wined3d_swapchain_ops *swapchain_ops; + struct wined3d_device *device; + + struct wined3d_texture **back_buffers; + struct wined3d_texture *front_buffer; + struct wined3d_gamma_ramp orig_gamma; + BOOL render_to_fbo, reapply_mode; + const struct wined3d_format *ds_format; + struct wined3d_palette *palette; + RECT front_buffer_update; + unsigned int swap_interval; + unsigned int max_frame_latency; + + LONG prev_time, frames; /* Performance tracking */ + + struct wined3d_swapchain_state state; + HWND win_handle; +}; + +void wined3d_swapchain_activate(struct wined3d_swapchain *swapchain, BOOL activate) DECLSPEC_HIDDEN; +void wined3d_swapchain_cleanup(struct wined3d_swapchain *swapchain) DECLSPEC_HIDDEN; +struct wined3d_output * wined3d_swapchain_get_output(const struct wined3d_swapchain *swapchain) DECLSPEC_HIDDEN; +void swapchain_update_draw_bindings(struct wined3d_swapchain *swapchain) DECLSPEC_HIDDEN; +void swapchain_set_max_frame_latency(struct wined3d_swapchain *swapchain, + const struct wined3d_device *device) DECLSPEC_HIDDEN; + +HRESULT wined3d_swapchain_no3d_init(struct wined3d_swapchain *swapchain_no3d, + struct wined3d_device *device, struct wined3d_swapchain_desc *desc, + struct wined3d_swapchain_state_parent *state_parent, void *parent, + const struct wined3d_parent_ops *parent_ops) DECLSPEC_HIDDEN; + +struct wined3d_swapchain_gl +{ + struct wined3d_swapchain s; + + struct wined3d_context_gl **contexts; + SIZE_T contexts_size; + SIZE_T context_count; + + HDC backup_dc; + HWND backup_wnd; +}; + +static inline struct wined3d_swapchain_gl *wined3d_swapchain_gl(struct wined3d_swapchain *swapchain) +{ + return CONTAINING_RECORD(swapchain, struct wined3d_swapchain_gl, s); +} + +void wined3d_swapchain_gl_cleanup(struct wined3d_swapchain_gl *swapchain_gl) DECLSPEC_HIDDEN; +void wined3d_swapchain_gl_destroy_contexts(struct wined3d_swapchain_gl *swapchain_gl) DECLSPEC_HIDDEN; +HDC wined3d_swapchain_gl_get_backup_dc(struct wined3d_swapchain_gl *swapchain_gl) DECLSPEC_HIDDEN; +struct wined3d_context_gl *wined3d_swapchain_gl_get_context(struct wined3d_swapchain_gl *swapchain_gl) DECLSPEC_HIDDEN; +HRESULT wined3d_swapchain_gl_init(struct wined3d_swapchain_gl *swapchain_gl, + struct wined3d_device *device, struct wined3d_swapchain_desc *desc, + struct wined3d_swapchain_state_parent *state_parent, void *parent, + const struct wined3d_parent_ops *parent_ops) DECLSPEC_HIDDEN; + +struct wined3d_swapchain_vk +{ + struct wined3d_swapchain s; + + VkSwapchainKHR vk_swapchain; + VkSurfaceKHR vk_surface; + VkImage *vk_images; + struct + { + VkSemaphore available; + VkSemaphore presentable; + uint64_t command_buffer_id; + } *vk_semaphores; + unsigned int current, image_count; +}; + +static inline struct wined3d_swapchain_vk *wined3d_swapchain_vk(struct wined3d_swapchain *swapchain) +{ + return CONTAINING_RECORD(swapchain, struct wined3d_swapchain_vk, s); +} + +void wined3d_swapchain_vk_cleanup(struct wined3d_swapchain_vk *swapchain_vk) DECLSPEC_HIDDEN; +HRESULT wined3d_swapchain_vk_init(struct wined3d_swapchain_vk *swapchain_vk, + struct wined3d_device *device, struct wined3d_swapchain_desc *desc, + struct wined3d_swapchain_state_parent *state_parent, void *parent, + const struct wined3d_parent_ops *parent_ops) DECLSPEC_HIDDEN; + +/***************************************************************************** + * Utility function prototypes + */ + +/* Trace routines */ +const char *debug_bo_address(const struct wined3d_bo_address *address) DECLSPEC_HIDDEN; +const char *debug_box(const struct wined3d_box *box) DECLSPEC_HIDDEN; +const char *debug_color(const struct wined3d_color *color) DECLSPEC_HIDDEN; +const char *debug_const_bo_address(const struct wined3d_const_bo_address *address) DECLSPEC_HIDDEN; +const char *debug_d3dshaderinstructionhandler(enum WINED3D_SHADER_INSTRUCTION_HANDLER handler_idx) DECLSPEC_HIDDEN; +const char *debug_d3dformat(enum wined3d_format_id format_id) DECLSPEC_HIDDEN; +const char *debug_d3ddevicetype(enum wined3d_device_type device_type) DECLSPEC_HIDDEN; +const char *debug_d3dresourcetype(enum wined3d_resource_type resource_type) DECLSPEC_HIDDEN; +const char *debug_d3dusage(DWORD usage) DECLSPEC_HIDDEN; +const char *debug_d3ddeclmethod(enum wined3d_decl_method method) DECLSPEC_HIDDEN; +const char *debug_d3ddeclusage(enum wined3d_decl_usage usage) DECLSPEC_HIDDEN; +const char *debug_d3dinput_classification(enum wined3d_input_classification classification) DECLSPEC_HIDDEN; +const char *debug_d3dprimitivetype(enum wined3d_primitive_type primitive_type) DECLSPEC_HIDDEN; +const char *debug_d3drenderstate(enum wined3d_render_state state) DECLSPEC_HIDDEN; +const char *debug_d3dsamplerstate(enum wined3d_sampler_state state) DECLSPEC_HIDDEN; +const char *debug_d3dstate(DWORD state) DECLSPEC_HIDDEN; +const char *debug_d3dtexturefiltertype(enum wined3d_texture_filter_type filter_type) DECLSPEC_HIDDEN; +const char *debug_d3dtexturestate(enum wined3d_texture_stage_state state) DECLSPEC_HIDDEN; +const char *debug_d3dtop(enum wined3d_texture_op d3dtop) DECLSPEC_HIDDEN; +const char *debug_d3dtstype(enum wined3d_transform_state tstype) DECLSPEC_HIDDEN; +const char *debug_fboattachment(GLenum attachment) DECLSPEC_HIDDEN; +const char *debug_fbostatus(GLenum status) DECLSPEC_HIDDEN; +const char *debug_glerror(GLenum error) DECLSPEC_HIDDEN; +const char *debug_ivec4(const struct wined3d_ivec4 *v) DECLSPEC_HIDDEN; +const char *debug_uvec4(const struct wined3d_uvec4 *v) DECLSPEC_HIDDEN; +const char *debug_shader_type(enum wined3d_shader_type shader_type) DECLSPEC_HIDDEN; +const char *debug_vec4(const struct wined3d_vec4 *v) DECLSPEC_HIDDEN; +const char *wined3d_debug_feature_level(enum wined3d_feature_level level) DECLSPEC_HIDDEN; +void dump_color_fixup_desc(struct color_fixup_desc fixup) DECLSPEC_HIDDEN; + +BOOL is_invalid_op(const struct wined3d_state *state, int stage, + enum wined3d_texture_op op, DWORD arg1, DWORD arg2, DWORD arg3) DECLSPEC_HIDDEN; +void set_tex_op_nvrc(const struct wined3d_gl_info *gl_info, const struct wined3d_state *state, + BOOL is_alpha, int stage, enum wined3d_texture_op op, DWORD arg1, DWORD arg2, DWORD arg3, + INT texture_idx, DWORD dst) DECLSPEC_HIDDEN; +void texture_activate_dimensions(struct wined3d_texture *texture, + const struct wined3d_gl_info *gl_info) DECLSPEC_HIDDEN; +void sampler_texdim(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) DECLSPEC_HIDDEN; +void tex_alphaop(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) DECLSPEC_HIDDEN; +void apply_pixelshader(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) DECLSPEC_HIDDEN; +void state_alpha_test(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) DECLSPEC_HIDDEN; +void state_fogcolor(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) DECLSPEC_HIDDEN; +void state_fogdensity(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) DECLSPEC_HIDDEN; +void state_fogstartend(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) DECLSPEC_HIDDEN; +void state_fog_fragpart(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) DECLSPEC_HIDDEN; +void state_nop(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) DECLSPEC_HIDDEN; +void state_srgbwrite(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) DECLSPEC_HIDDEN; + +void state_clipping(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) DECLSPEC_HIDDEN; +void clipplane(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) DECLSPEC_HIDDEN; +void state_pointsprite_w(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) DECLSPEC_HIDDEN; +void state_pointsprite(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) DECLSPEC_HIDDEN; +void state_shademode(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) DECLSPEC_HIDDEN; + +/* Math utils */ +void multiply_matrix(struct wined3d_matrix *dest, const struct wined3d_matrix *src1, + const struct wined3d_matrix *src2) DECLSPEC_HIDDEN; + +void wined3d_release_dc(HWND window, HDC dc) DECLSPEC_HIDDEN; + +struct wined3d_shader_lconst +{ + struct list entry; + unsigned int idx; + DWORD value[4]; +}; + +struct wined3d_shader_limits +{ + unsigned int sampler; + unsigned int constant_int; + unsigned int constant_float; + unsigned int constant_bool; + unsigned int packed_output; + unsigned int packed_input; +}; + +#ifdef __GNUC__ +#define PRINTF_ATTR(fmt,args) __attribute__((format (printf,fmt,args))) +#else +#define PRINTF_ATTR(fmt,args) +#endif + +struct wined3d_string_buffer_list +{ + struct list list; +}; + +struct wined3d_string_buffer *string_buffer_get(struct wined3d_string_buffer_list *list) DECLSPEC_HIDDEN; +void string_buffer_sprintf(struct wined3d_string_buffer *buffer, const char *format, ...) PRINTF_ATTR(2, 3) DECLSPEC_HIDDEN; +void string_buffer_release(struct wined3d_string_buffer_list *list, struct wined3d_string_buffer *buffer) DECLSPEC_HIDDEN; +void string_buffer_list_init(struct wined3d_string_buffer_list *list) DECLSPEC_HIDDEN; +void string_buffer_list_cleanup(struct wined3d_string_buffer_list *list) DECLSPEC_HIDDEN; + +int shader_addline(struct wined3d_string_buffer *buffer, const char *fmt, ...) PRINTF_ATTR(2,3) DECLSPEC_HIDDEN; +BOOL string_buffer_resize(struct wined3d_string_buffer *buffer, int rc) DECLSPEC_HIDDEN; +int shader_vaddline(struct wined3d_string_buffer *buffer, const char *fmt, va_list args) DECLSPEC_HIDDEN; + +struct wined3d_shader_phase +{ + const DWORD *start; + const DWORD *end; + unsigned int instance_count; + unsigned int temporary_count; +}; + +struct wined3d_vertex_shader +{ + struct wined3d_shader_attribute attributes[MAX_ATTRIBS]; +}; + +struct wined3d_hull_shader +{ + struct + { + struct wined3d_shader_phase *control_point; + unsigned int fork_count; + unsigned int join_count; + struct wined3d_shader_phase *fork; + SIZE_T fork_size; + struct wined3d_shader_phase *join; + SIZE_T join_size; + } phases; + unsigned int output_vertex_count; + enum wined3d_tessellator_output_primitive tessellator_output_primitive; + enum wined3d_tessellator_partitioning tessellator_partitioning; +}; + +struct wined3d_domain_shader +{ + enum wined3d_tessellator_domain tessellator_domain; +}; + +struct wined3d_geometry_shader +{ + enum wined3d_primitive_type input_type; + enum wined3d_primitive_type output_type; + unsigned int vertices_out; + unsigned int instance_count; + + const struct wined3d_stream_output_desc *so_desc; +}; + +struct wined3d_pixel_shader +{ + /* Pixel shader input semantics */ + DWORD input_reg_map[MAX_REG_INPUT]; + DWORD input_reg_used; /* MAX_REG_INPUT, 32 */ + unsigned int declared_in_count; + + /* Some information about the shader behavior */ + BOOL color0_mov; + DWORD color0_reg; + + BOOL force_early_depth_stencil; + enum wined3d_shader_register_type depth_output; + DWORD interpolation_mode[WINED3D_PACKED_INTERPOLATION_SIZE]; +}; + +struct wined3d_compute_shader +{ + struct wined3d_shader_thread_group_size thread_group_size; +}; + +struct wined3d_shader +{ + LONG ref; + const struct wined3d_shader_limits *limits; + const DWORD *function; + unsigned int functionLength; + void *byte_code; + unsigned int byte_code_size; + BOOL load_local_constsF; + const struct wined3d_shader_frontend *frontend; + void *frontend_data; + void *backend_data; + + void *parent; + const struct wined3d_parent_ops *parent_ops; + + /* Programs this shader is linked with */ + struct list linked_programs; + + /* Immediate constants (override global ones) */ + struct list constantsB; + struct list constantsF; + struct list constantsI; + struct wined3d_shader_reg_maps reg_maps; + BOOL lconst_inf_or_nan; + + struct wined3d_shader_signature input_signature; + struct wined3d_shader_signature output_signature; + struct wined3d_shader_signature patch_constant_signature; + + /* Pointer to the parent device */ + struct wined3d_device *device; + struct list shader_list_entry; + + union + { + struct wined3d_vertex_shader vs; + struct wined3d_hull_shader hs; + struct wined3d_domain_shader ds; + struct wined3d_geometry_shader gs; + struct wined3d_pixel_shader ps; + struct wined3d_compute_shader cs; + } u; +}; + +enum wined3d_shader_resource_type pixelshader_get_resource_type(const struct wined3d_shader_reg_maps *reg_maps, + unsigned int resource_idx, DWORD tex_types) DECLSPEC_HIDDEN; +void find_ps_compile_args(const struct wined3d_state *state, const struct wined3d_shader *shader, + BOOL position_transformed, struct ps_compile_args *args, + const struct wined3d_context *context) DECLSPEC_HIDDEN; + +BOOL vshader_get_input(const struct wined3d_shader *shader, + BYTE usage_req, BYTE usage_idx_req, unsigned int *regnum) DECLSPEC_HIDDEN; +void find_vs_compile_args(const struct wined3d_state *state, const struct wined3d_shader *shader, + WORD swizzle_map, struct vs_compile_args *args, + const struct wined3d_context *context) DECLSPEC_HIDDEN; + +void find_ds_compile_args(const struct wined3d_state *state, const struct wined3d_shader *shader, + struct ds_compile_args *args, const struct wined3d_context *context) DECLSPEC_HIDDEN; + +void find_gs_compile_args(const struct wined3d_state *state, const struct wined3d_shader *shader, + struct gs_compile_args *args, const struct wined3d_context *context) DECLSPEC_HIDDEN; + +void string_buffer_clear(struct wined3d_string_buffer *buffer) DECLSPEC_HIDDEN; +BOOL string_buffer_init(struct wined3d_string_buffer *buffer) DECLSPEC_HIDDEN; +void string_buffer_free(struct wined3d_string_buffer *buffer) DECLSPEC_HIDDEN; +unsigned int shader_find_free_input_register(const struct wined3d_shader_reg_maps *reg_maps, + unsigned int max) DECLSPEC_HIDDEN; +HRESULT shader_generate_code(const struct wined3d_shader *shader, struct wined3d_string_buffer *buffer, + const struct wined3d_shader_reg_maps *reg_maps, void *backend_ctx, + const DWORD *start, const DWORD *end) DECLSPEC_HIDDEN; +BOOL shader_match_semantic(const char *semantic_name, enum wined3d_decl_usage usage) DECLSPEC_HIDDEN; + +static inline BOOL shader_is_scalar(const struct wined3d_shader_register *reg) +{ + switch (reg->type) + { + case WINED3DSPR_RASTOUT: + /* oFog & oPts */ + if (reg->idx[0].offset) + return TRUE; + /* oPos */ + return FALSE; + + case WINED3DSPR_CONSTBOOL: /* b# */ + case WINED3DSPR_DEPTHOUT: /* oDepth */ + case WINED3DSPR_DEPTHOUTGE: + case WINED3DSPR_DEPTHOUTLE: + case WINED3DSPR_LOOP: /* aL */ + case WINED3DSPR_OUTPOINTID: + case WINED3DSPR_PREDICATE: /* p0 */ + case WINED3DSPR_PRIMID: /* primID */ + case WINED3DSPR_COVERAGE: /* vCoverage */ + case WINED3DSPR_SAMPLEMASK: /* oMask */ + return TRUE; + + case WINED3DSPR_MISCTYPE: + switch (reg->idx[0].offset) + { + case 0: /* vPos */ + return FALSE; + case 1: /* vFace */ + return TRUE; + default: + return FALSE; + } + + case WINED3DSPR_IMMCONST: + return reg->immconst_type == WINED3D_IMMCONST_SCALAR; + + default: + return FALSE; + } +} + +static inline void shader_get_position_fixup(const struct wined3d_context *context, + const struct wined3d_state *state, unsigned int fixup_count, float *position_fixup) +{ + float center_offset; + unsigned int i; + + if (context->d3d_info->wined3d_creation_flags & WINED3D_PIXEL_CENTER_INTEGER) + center_offset = 63.0f / 64.0f; + else + center_offset = -1.0f / 64.0f; + + for (i = 0; i < fixup_count; ++i) + { + position_fixup[4 * i ] = 1.0f; + position_fixup[4 * i + 1] = 1.0f; + position_fixup[4 * i + 2] = center_offset / state->viewports[i].width; + position_fixup[4 * i + 3] = -center_offset / state->viewports[i].height; + + if (context->render_offscreen) + { + position_fixup[4 * i + 1] *= -1.0f; + position_fixup[4 * i + 3] *= -1.0f; + } + } +} + +static inline BOOL shader_constant_is_local(const struct wined3d_shader *shader, DWORD reg) +{ + struct wined3d_shader_lconst *lconst; + + if (shader->load_local_constsF) + return FALSE; + + LIST_FOR_EACH_ENTRY(lconst, &shader->constantsF, struct wined3d_shader_lconst, entry) + { + if (lconst->idx == reg) + return TRUE; + } + + return FALSE; +} + +void get_identity_matrix(struct wined3d_matrix *mat) DECLSPEC_HIDDEN; +void get_modelview_matrix(const struct wined3d_context *context, const struct wined3d_state *state, + unsigned int index, struct wined3d_matrix *mat) DECLSPEC_HIDDEN; +void get_projection_matrix(const struct wined3d_context *context, const struct wined3d_state *state, + struct wined3d_matrix *mat) DECLSPEC_HIDDEN; +void get_texture_matrix(const struct wined3d_context *context, const struct wined3d_state *state, + unsigned int tex, struct wined3d_matrix *mat) DECLSPEC_HIDDEN; +void get_pointsize_minmax(const struct wined3d_context *context, const struct wined3d_state *state, + float *out_min, float *out_max) DECLSPEC_HIDDEN; +void get_pointsize(const struct wined3d_context *context, const struct wined3d_state *state, + float *out_pointsize, float *out_att) DECLSPEC_HIDDEN; +void get_fog_start_end(const struct wined3d_context *context, const struct wined3d_state *state, + float *start, float *end) DECLSPEC_HIDDEN; + +/* Using additional shader constants (uniforms in GLSL / program environment + * or local parameters in ARB) is costly: + * ARB only knows float4 parameters and GLSL compiler are not really smart + * when it comes to efficiently pack float2 uniforms, so no space is wasted + * (in fact most compilers map a float2 to a full float4 uniform). + * + * For NP2 texcoord fixup we only need 2 floats (width and height) for each + * 2D texture used in the shader. We therefore pack fixup info for 2 textures + * into a single shader constant (uniform / program parameter). + * + * This structure is shared between the GLSL and the ARB backend.*/ +struct ps_np2fixup_info { + unsigned char idx[WINED3D_MAX_FRAGMENT_SAMPLERS]; /* indices to the real constant */ + WORD active; /* bitfield indicating if we can apply the fixup */ + WORD num_consts; +}; + +void print_glsl_info_log(const struct wined3d_gl_info *gl_info, GLuint id, BOOL program) DECLSPEC_HIDDEN; +void shader_glsl_validate_link(const struct wined3d_gl_info *gl_info, GLuint program) DECLSPEC_HIDDEN; + +struct wined3d_palette +{ + LONG ref; + struct wined3d_device *device; + + unsigned int size; + RGBQUAD colors[256]; + DWORD flags; +}; + +/* DirectDraw utility functions */ +extern enum wined3d_format_id pixelformat_for_depth(DWORD depth) DECLSPEC_HIDDEN; + +/***************************************************************************** + * Pixel format management + */ + +/* WineD3D pixel format flags */ +#define WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING 0x00000001 +#define WINED3DFMT_FLAG_FILTERING 0x00000002 +#define WINED3DFMT_FLAG_UNORDERED_ACCESS 0x00000004 +#define WINED3DFMT_FLAG_DEPTH_STENCIL 0x00000008 +#define WINED3DFMT_FLAG_RENDERTARGET 0x00000010 +#define WINED3DFMT_FLAG_EXTENSION 0x00000020 +#define WINED3DFMT_FLAG_FBO_ATTACHABLE 0x00000040 +#define WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB 0x00000080 +#define WINED3DFMT_FLAG_DECOMPRESS 0x00000100 +#define WINED3DFMT_FLAG_FLOAT 0x00000200 +#define WINED3DFMT_FLAG_BUMPMAP 0x00000400 +#define WINED3DFMT_FLAG_SRGB_READ 0x00000800 +#define WINED3DFMT_FLAG_SRGB_WRITE 0x00001000 +#define WINED3DFMT_FLAG_VTF 0x00002000 +#define WINED3DFMT_FLAG_SHADOW 0x00004000 +#define WINED3DFMT_FLAG_COMPRESSED 0x00008000 +#define WINED3DFMT_FLAG_BROKEN_PITCH 0x00010000 +#define WINED3DFMT_FLAG_BLOCKS 0x00020000 +#define WINED3DFMT_FLAG_HEIGHT_SCALE 0x00040000 +#define WINED3DFMT_FLAG_TEXTURE 0x00080000 +#define WINED3DFMT_FLAG_BLOCKS_NO_VERIFY 0x00100000 +#define WINED3DFMT_FLAG_INTEGER 0x00200000 +#define WINED3DFMT_FLAG_GEN_MIPMAP 0x00400000 +#define WINED3DFMT_FLAG_NORMALISED 0x00800000 +#define WINED3DFMT_FLAG_VERTEX_ATTRIBUTE 0x01000000 +#define WINED3DFMT_FLAG_BLIT 0x02000000 +#define WINED3DFMT_FLAG_MAPPABLE 0x04000000 +#define WINED3DFMT_FLAG_CAST_TO_BLOCK 0x08000000 + +struct wined3d_rational +{ + UINT numerator; + UINT denominator; +}; + +struct wined3d_color_key_conversion +{ + enum wined3d_format_id dst_format; + void (*convert)(const BYTE *src, unsigned int src_pitch, BYTE *dst, unsigned int dst_pitch, + unsigned int width, unsigned int height, const struct wined3d_color_key *colour_key); +}; + +struct wined3d_format +{ + enum wined3d_format_id id; + + D3DDDIFORMAT ddi_format; + unsigned int component_count; + DWORD red_size; + DWORD green_size; + DWORD blue_size; + DWORD alpha_size; + DWORD red_offset; + DWORD green_offset; + DWORD blue_offset; + DWORD alpha_offset; + UINT byte_count; + BYTE depth_size; + BYTE stencil_size; + + UINT block_width; + UINT block_height; + UINT block_byte_count; + + enum wined3d_ffp_emit_idx emit_idx; + + UINT conv_byte_count; + DWORD multisample_types; + unsigned int flags[WINED3D_GL_RES_TYPE_COUNT]; + float depth_bias_scale; + struct wined3d_rational height_scale; + struct color_fixup_desc color_fixup; + void (*upload)(const BYTE *src, BYTE *dst, unsigned int src_row_pitch, unsigned int src_slice_pitch, + unsigned int dst_row_pitch, unsigned dst_slice_pitch, + unsigned int width, unsigned int height, unsigned int depth); + void (*download)(const BYTE *src, BYTE *dst, unsigned int src_row_pitch, unsigned int src_slice_pitch, + unsigned int dst_row_pitch, unsigned dst_slice_pitch, + unsigned int width, unsigned int height, unsigned int depth); + void (*decompress)(const BYTE *src, BYTE *dst, unsigned int src_row_pitch, unsigned int src_slice_pitch, + unsigned int dst_row_pitch, unsigned dst_slice_pitch, + unsigned int width, unsigned int height, unsigned int depth); + + enum wined3d_format_id typeless_id; +}; + +const struct wined3d_format *wined3d_get_format(const struct wined3d_adapter *adapter, + enum wined3d_format_id format_id, unsigned int bind_flags) DECLSPEC_HIDDEN; +void wined3d_format_calculate_pitch(const struct wined3d_format *format, unsigned int alignment, + unsigned int width, unsigned int height, unsigned int *row_pitch, unsigned int *slice_pitch) DECLSPEC_HIDDEN; +UINT wined3d_format_calculate_size(const struct wined3d_format *format, + UINT alignment, UINT width, UINT height, UINT depth) DECLSPEC_HIDDEN; +DWORD wined3d_format_convert_from_float(const struct wined3d_format *format, + const struct wined3d_color *color) DECLSPEC_HIDDEN; +void wined3d_format_copy_data(const struct wined3d_format *format, const uint8_t *src, + unsigned int src_row_pitch, unsigned int src_slice_pitch, uint8_t *dst, unsigned int dst_row_pitch, + unsigned int dst_slice_pitch, unsigned int w, unsigned int h, unsigned int d) DECLSPEC_HIDDEN; +void wined3d_format_get_float_color_key(const struct wined3d_format *format, + const struct wined3d_color_key *key, struct wined3d_color *float_colors) DECLSPEC_HIDDEN; +BOOL wined3d_format_is_depth_view(enum wined3d_format_id resource_format_id, + enum wined3d_format_id view_format_id) DECLSPEC_HIDDEN; +const struct wined3d_color_key_conversion * wined3d_format_get_color_key_conversion( + const struct wined3d_texture *texture, BOOL need_alpha_ck) DECLSPEC_HIDDEN; +BOOL wined3d_formats_are_srgb_variants(enum wined3d_format_id format1, + enum wined3d_format_id format2) DECLSPEC_HIDDEN; + +struct wined3d_format_gl +{ + struct wined3d_format f; + + GLenum vtx_type; + GLint vtx_format; + + GLint internal; + GLint srgb_internal; + GLint rt_internal; + GLint format; + GLint type; + + GLenum view_class; +}; + +static inline const struct wined3d_format_gl *wined3d_format_gl(const struct wined3d_format *format) +{ + return CONTAINING_RECORD(format, struct wined3d_format_gl, f); +} + +struct wined3d_format_vk +{ + struct wined3d_format f; + + VkFormat vk_format; +}; + +static inline const struct wined3d_format_vk *wined3d_format_vk(const struct wined3d_format *format) +{ + return CONTAINING_RECORD(format, struct wined3d_format_vk, f); +} + +BOOL wined3d_array_reserve(void **elements, SIZE_T *capacity, SIZE_T count, SIZE_T size) DECLSPEC_HIDDEN; + +static inline BOOL wined3d_format_is_typeless(const struct wined3d_format *format) +{ + return format->id == format->typeless_id && format->id != WINED3DFMT_UNKNOWN; +} + +static inline BOOL use_vs(const struct wined3d_state *state) +{ + /* Check state->vertex_declaration to allow this to be used before the + * stream info is validated, for example in device_update_tex_unit_map(). */ + return state->shader[WINED3D_SHADER_TYPE_VERTEX] + && (!state->vertex_declaration || !state->vertex_declaration->position_transformed); +} + +static inline BOOL use_ps(const struct wined3d_state *state) +{ + return !!state->shader[WINED3D_SHADER_TYPE_PIXEL]; +} + +static inline BOOL use_transform_feedback(const struct wined3d_state *state) +{ + const struct wined3d_shader *shader; + + if (!(shader = state->shader[WINED3D_SHADER_TYPE_GEOMETRY])) + return FALSE; + return !!shader->u.gs.so_desc; +} + +static inline void context_apply_state(struct wined3d_context *context, + const struct wined3d_state *state, DWORD state_id) +{ + const struct wined3d_state_entry *state_table = context->state_table; + unsigned int rep = state_table[state_id].representative; + state_table[rep].apply(context, state, rep); +} + +static inline BOOL is_srgb_enabled(const DWORD *sampler_states) +{ + /* Only use the LSB of the WINED3D_SAMP_SRGB_TEXTURE value. This matches + * the behaviour of the AMD Windows driver. + * + * Might & Magic: Heroes VI - Shades of Darkness sets + * WINED3D_SAMP_SRGB_TEXTURE to a large value that looks like a + * pointer—presumably by accident—and expects sRGB decoding to be + * disabled. */ + return sampler_states[WINED3D_SAMP_SRGB_TEXTURE] & 0x1; +} + +static inline BOOL needs_separate_srgb_gl_texture(const struct wined3d_context *context, + const struct wined3d_texture *texture) +{ + if (!(context->d3d_info->wined3d_creation_flags & WINED3D_SRGB_READ_WRITE_CONTROL)) + return FALSE; + + if (!context->d3d_info->srgb_read_control + && (texture->resource.bind_flags & WINED3D_BIND_SHADER_RESOURCE) + && (texture->resource.format_flags & WINED3DFMT_FLAG_SRGB_READ)) + return TRUE; + + if (!context->d3d_info->srgb_write_control + && (texture->resource.bind_flags & WINED3D_BIND_RENDER_TARGET) + && (texture->resource.format_flags & WINED3DFMT_FLAG_SRGB_WRITE)) + return TRUE; + + return FALSE; +} + +static inline BOOL needs_srgb_write(const struct wined3d_d3d_info *d3d_info, + const struct wined3d_state *state, const struct wined3d_fb_state *fb) +{ + return (!(d3d_info->wined3d_creation_flags & WINED3D_SRGB_READ_WRITE_CONTROL) + || state->render_states[WINED3D_RS_SRGBWRITEENABLE]) + && fb->render_targets[0] && fb->render_targets[0]->format_flags & WINED3DFMT_FLAG_SRGB_WRITE; +} + +static inline GLuint wined3d_texture_gl_get_texture_name(const struct wined3d_texture_gl *texture_gl, + const struct wined3d_context *context, BOOL srgb) +{ + return srgb && needs_separate_srgb_gl_texture(context, &texture_gl->t) + ? texture_gl->texture_srgb.name : texture_gl->texture_rgb.name; +} + +static inline BOOL can_use_texture_swizzle(const struct wined3d_d3d_info *d3d_info, const struct wined3d_format *format) +{ + return d3d_info->texture_swizzle && !is_complex_fixup(format->color_fixup) && !is_scaling_fixup(format->color_fixup); +} + +static inline BOOL is_rasterization_disabled(const struct wined3d_shader *geometry_shader) +{ + return geometry_shader && geometry_shader->u.gs.so_desc + && geometry_shader->u.gs.so_desc->rasterizer_stream_idx == WINED3D_NO_RASTERIZER_STREAM; +} + +static inline DWORD wined3d_extract_bits(const DWORD *bitstream, + unsigned int offset, unsigned int count) +{ + const unsigned int word_bit_count = sizeof(*bitstream) * CHAR_BIT; + const unsigned int idx = offset / word_bit_count; + const unsigned int shift = offset % word_bit_count; + DWORD mask = (1u << count) - 1; + DWORD ret; + + ret = (bitstream[idx] >> shift) & mask; + if (shift + count > word_bit_count) + { + const unsigned int extracted_bit_count = word_bit_count - shift; + const unsigned int remaining_bit_count = count - extracted_bit_count; + mask = (1u << remaining_bit_count) - 1; + ret |= (bitstream[idx + 1] & mask) << extracted_bit_count; + } + return ret; +} + +static inline void wined3d_insert_bits(DWORD *bitstream, + unsigned int offset, unsigned int count, DWORD bits) +{ + const unsigned int word_bit_count = sizeof(*bitstream) * CHAR_BIT; + const unsigned int idx = offset / word_bit_count; + const unsigned int shift = offset % word_bit_count; + DWORD mask = (1u << count) - 1; + + bitstream[idx] |= (bits & mask) << shift; + if (shift + count > word_bit_count) + { + const unsigned int inserted_bit_count = word_bit_count - shift; + const unsigned int remaining_bit_count = count - inserted_bit_count; + mask = (1u << remaining_bit_count) - 1; + bitstream[idx + 1] |= (bits >> inserted_bit_count) & mask; + } +} + +static inline void wined3d_from_cs(const struct wined3d_cs *cs) +{ + if (cs->thread) + assert(cs->thread_id == GetCurrentThreadId()); +} + +static inline void wined3d_not_from_cs(struct wined3d_cs *cs) +{ + assert(cs->thread_id != GetCurrentThreadId()); +} + +static inline enum wined3d_material_color_source validate_material_colour_source(WORD use_map, + enum wined3d_material_color_source source) +{ + if (source == WINED3D_MCS_COLOR1 && use_map & (1u << WINED3D_FFP_DIFFUSE)) + return source; + if (source == WINED3D_MCS_COLOR2 && use_map & (1u << WINED3D_FFP_SPECULAR)) + return source; + return WINED3D_MCS_MATERIAL; +} + +static inline void wined3d_get_material_colour_source(enum wined3d_material_color_source *diffuse, + enum wined3d_material_color_source *emissive, enum wined3d_material_color_source *ambient, + enum wined3d_material_color_source *specular, const struct wined3d_state *state, + const struct wined3d_stream_info *si) +{ + if (!state->render_states[WINED3D_RS_LIGHTING]) + { + *diffuse = WINED3D_MCS_COLOR1; + *specular = WINED3D_MCS_COLOR2; + *emissive = *ambient = WINED3D_MCS_MATERIAL; + + return; + } + + if (!state->render_states[WINED3D_RS_COLORVERTEX]) + { + *diffuse = *emissive = *ambient = *specular = WINED3D_MCS_MATERIAL; + + return; + } + + *diffuse = validate_material_colour_source(si->use_map, state->render_states[WINED3D_RS_DIFFUSEMATERIALSOURCE]); + *emissive = validate_material_colour_source(si->use_map, state->render_states[WINED3D_RS_EMISSIVEMATERIALSOURCE]); + *ambient = validate_material_colour_source(si->use_map, state->render_states[WINED3D_RS_AMBIENTMATERIALSOURCE]); + *specular = validate_material_colour_source(si->use_map, state->render_states[WINED3D_RS_SPECULARMATERIALSOURCE]); +} + +static inline void wined3d_vec4_transform(struct wined3d_vec4 *dst, + const struct wined3d_vec4 *v, const struct wined3d_matrix *m) +{ + struct wined3d_vec4 tmp; + + tmp.x = v->x * m->_11 + v->y * m->_21 + v->z * m->_31 + v->w * m->_41; + tmp.y = v->x * m->_12 + v->y * m->_22 + v->z * m->_32 + v->w * m->_42; + tmp.z = v->x * m->_13 + v->y * m->_23 + v->z * m->_33 + v->w * m->_43; + tmp.w = v->x * m->_14 + v->y * m->_24 + v->z * m->_34 + v->w * m->_44; + + *dst = tmp; +} + +BOOL invert_matrix(struct wined3d_matrix *out, const struct wined3d_matrix *m) DECLSPEC_HIDDEN; + +void compute_normal_matrix(float *normal_matrix, BOOL legacy_lighting, + const struct wined3d_matrix *modelview) DECLSPEC_HIDDEN; + +static inline struct wined3d_context *context_acquire(struct wined3d_device *device, + struct wined3d_texture *texture, unsigned int sub_resource_idx) +{ + wined3d_from_cs(device->cs); + + return device->adapter->adapter_ops->adapter_acquire_context(device, texture, sub_resource_idx); +} + +static inline void context_release(struct wined3d_context *context) +{ + context->device->adapter->adapter_ops->adapter_release_context(context); +} + +static inline float wined3d_get_float_state(const struct wined3d_state *state, enum wined3d_render_state rs) +{ + union + { + DWORD d; + float f; + } + tmpvalue; + + tmpvalue.d = state->render_states[rs]; + return tmpvalue.f; +} + +static inline void *wined3d_context_map_bo_address(struct wined3d_context *context, + const struct wined3d_bo_address *data, size_t size, uint32_t map_flags) +{ + return context->device->adapter->adapter_ops->adapter_map_bo_address(context, data, size, map_flags); +} + +static inline void wined3d_context_unmap_bo_address(struct wined3d_context *context, + const struct wined3d_bo_address *data, unsigned int range_count, const struct wined3d_range *ranges) +{ + context->device->adapter->adapter_ops->adapter_unmap_bo_address(context, data, range_count, ranges); +} + +static inline void wined3d_context_copy_bo_address(struct wined3d_context *context, + const struct wined3d_bo_address *dst, const struct wined3d_bo_address *src, size_t size) +{ + context->device->adapter->adapter_ops->adapter_copy_bo_address(context, dst, src, size); +} + +static inline void wined3d_context_vk_reference_bo(const struct wined3d_context_vk *context_vk, + struct wined3d_bo_vk *bo) +{ + bo->command_buffer_id = context_vk->current_command_buffer.id; +} + +static inline void wined3d_context_vk_reference_texture(const struct wined3d_context_vk *context_vk, + struct wined3d_texture_vk *texture_vk) +{ + texture_vk->command_buffer_id = context_vk->current_command_buffer.id; +} + +static inline void wined3d_context_vk_reference_resource(const struct wined3d_context_vk *context_vk, + struct wined3d_resource *resource) +{ + if (resource->type == WINED3D_RTYPE_BUFFER) + wined3d_context_vk_reference_bo(context_vk, &wined3d_buffer_vk(buffer_from_resource(resource))->bo); + else + wined3d_context_vk_reference_texture(context_vk, wined3d_texture_vk(texture_from_resource(resource))); +} + +static inline void wined3d_context_vk_reference_query(const struct wined3d_context_vk *context_vk, + struct wined3d_query_vk *query_vk) +{ + query_vk->command_buffer_id = context_vk->current_command_buffer.id; +} + +static inline void wined3d_context_vk_reference_sampler(const struct wined3d_context_vk *context_vk, + struct wined3d_sampler_vk *sampler_vk) +{ + sampler_vk->command_buffer_id = context_vk->current_command_buffer.id; +} + +static inline void wined3d_context_vk_reference_rendertarget_view(const struct wined3d_context_vk *context_vk, + struct wined3d_rendertarget_view_vk *rtv_vk) +{ + wined3d_context_vk_reference_resource(context_vk, rtv_vk->v.resource); + rtv_vk->command_buffer_id = context_vk->current_command_buffer.id; +} + +static inline void wined3d_context_vk_reference_shader_resource_view(const struct wined3d_context_vk *context_vk, + struct wined3d_shader_resource_view_vk *srv_vk) +{ + wined3d_context_vk_reference_resource(context_vk, srv_vk->v.resource); + srv_vk->view_vk.command_buffer_id = context_vk->current_command_buffer.id; +} + +static inline void wined3d_context_vk_reference_unordered_access_view(const struct wined3d_context_vk *context_vk, + struct wined3d_unordered_access_view_vk *uav_vk) +{ + wined3d_context_vk_reference_resource(context_vk, uav_vk->v.resource); + uav_vk->view_vk.command_buffer_id = context_vk->current_command_buffer.id; +} + +static inline BOOL wined3d_dsv_srv_conflict(const struct wined3d_rendertarget_view *dsv, + const struct wined3d_format *srv_format) +{ + return !srv_format || (srv_format->red_size && !(dsv->desc.flags & WINED3D_VIEW_READ_ONLY_DEPTH)) + || (srv_format->green_size && !(dsv->desc.flags & WINED3D_VIEW_READ_ONLY_STENCIL)); +} + +static inline unsigned int wined3d_bind_layer_count(const struct wined3d_texture *texture) +{ + return texture->resource.type == WINED3D_RTYPE_TEXTURE_3D ? texture->resource.depth : texture->layer_count; +} + +static inline bool wined3d_srv_all_subresources(const struct wined3d_shader_resource_view *srv) +{ + struct wined3d_texture *texture; + + if (srv->resource->type == WINED3D_RTYPE_BUFFER) + return TRUE; + + if (srv->desc.u.texture.layer_idx || srv->desc.u.texture.level_idx) + return FALSE; + + texture = texture_from_resource(srv->resource); + return srv->desc.u.texture.level_count == texture->level_count + && srv->desc.u.texture.layer_count == wined3d_bind_layer_count(texture); +} + +static inline bool wined3d_rtv_all_subresources(const struct wined3d_rendertarget_view *rtv) +{ + struct wined3d_texture *texture; + + if (rtv->resource->type == WINED3D_RTYPE_BUFFER) + return TRUE; + + if (rtv->sub_resource_idx) + return FALSE; + + texture = texture_from_resource(rtv->resource); + return texture->level_count == 1 && rtv->layer_count == wined3d_bind_layer_count(texture); +} + +static inline void wined3d_srv_bind_count_add(struct wined3d_shader_resource_view *srv, int value) +{ + struct wined3d_resource *resource = srv->resource; + struct wined3d_texture *texture; + unsigned int level, layer; + + if (wined3d_srv_all_subresources(srv)) + { + resource->srv_full_bind_count_device += value; + return; + } + + resource->srv_partial_bind_count_device += value; + + texture = texture_from_resource(resource); + + if (!resource->sub_resource_bind_counts_device + && !(resource->sub_resource_bind_counts_device = heap_alloc_zero(texture->level_count + * wined3d_bind_layer_count(texture) * sizeof(*resource->sub_resource_bind_counts_device)))) + return; + + for (layer = 0; layer < srv->desc.u.texture.layer_count; ++layer) + for (level = 0; level < srv->desc.u.texture.level_count; ++level) + resource->sub_resource_bind_counts_device[(layer + srv->desc.u.texture.layer_idx) + * texture->level_count + srv->desc.u.texture.level_idx + level].srv += value; +} + +static inline void wined3d_srv_bind_count_inc(struct wined3d_shader_resource_view *srv) +{ + wined3d_srv_bind_count_add(srv, 1); +} + +static inline void wined3d_srv_bind_count_dec(struct wined3d_shader_resource_view *srv) +{ + wined3d_srv_bind_count_add(srv, -1); +} + +static inline void wined3d_rtv_bind_count_add(struct wined3d_rendertarget_view *rtv, int value) +{ + struct wined3d_resource *resource = rtv->resource; + struct wined3d_texture *texture; + unsigned int layer; + + if (wined3d_rtv_all_subresources(rtv)) + { + resource->rtv_full_bind_count_device += value; + return; + } + + resource->rtv_partial_bind_count_device += value; + + texture = texture_from_resource(resource); + + if (!resource->sub_resource_bind_counts_device + && !(resource->sub_resource_bind_counts_device = heap_alloc_zero(texture->level_count + * wined3d_bind_layer_count(texture) * sizeof(*resource->sub_resource_bind_counts_device)))) + return; + + for (layer = 0; layer < rtv->layer_count; ++layer) + resource->sub_resource_bind_counts_device[rtv->sub_resource_idx + layer * texture->level_count].rtv += value; +} + +static inline void wined3d_rtv_bind_count_inc(struct wined3d_rendertarget_view *rtv) +{ + wined3d_rtv_bind_count_add(rtv, 1); +} + +static inline void wined3d_rtv_bind_count_dec(struct wined3d_rendertarget_view *rtv) +{ + wined3d_rtv_bind_count_add(rtv, -1); +} + +static inline bool wined3d_is_srv_rtv_bound(const struct wined3d_shader_resource_view *srv) +{ + struct wined3d_resource *resource = srv->resource; + struct wined3d_texture *texture; + unsigned int level, layer; + + if (!(resource->rtv_full_bind_count_device + resource->rtv_partial_bind_count_device)) + return FALSE; + + if (resource->rtv_full_bind_count_device || wined3d_srv_all_subresources(srv)) + return TRUE; + + texture = texture_from_resource(resource); + + for (layer = 0; layer < srv->desc.u.texture.layer_count; ++layer) + for (level = 0; level < srv->desc.u.texture.level_count; ++level) + if (resource->sub_resource_bind_counts_device[(layer + srv->desc.u.texture.layer_idx) + * texture->level_count + srv->desc.u.texture.level_idx + level].rtv) + return TRUE; + + return FALSE; +} + +static inline bool wined3d_is_rtv_srv_bound(const struct wined3d_rendertarget_view *rtv) +{ + struct wined3d_resource *resource = rtv->resource; + struct wined3d_texture *texture; + unsigned int layer; + + if (!(resource->srv_full_bind_count_device + resource->srv_partial_bind_count_device)) + return FALSE; + + if (resource->srv_full_bind_count_device || wined3d_rtv_all_subresources(rtv)) + return TRUE; + + texture = texture_from_resource(resource); + + for (layer = 0; layer < rtv->layer_count; ++layer) + if (resource->sub_resource_bind_counts_device[rtv->sub_resource_idx + layer * texture->level_count].srv) + return TRUE; + + return FALSE; +} + +static inline void wined3d_viewport_get_z_range(const struct wined3d_viewport *vp, float *min_z, float *max_z) +{ + *min_z = vp->min_z; + + /* The magic constant is derived from tests. */ + *max_z = max(vp->max_z, vp->min_z + 0.001f); +} + +static inline BOOL wined3d_bitmap_clear(uint32_t *map, unsigned int idx) +{ + return map[idx >> 5] &= ~(1u << (idx & 0x1f)); +} + +static inline BOOL wined3d_bitmap_set(uint32_t *map, unsigned int idx) +{ + return map[idx >> 5] |= (1u << (idx & 0x1f)); +} + +static inline BOOL wined3d_bitmap_is_set(const uint32_t *map, unsigned int idx) +{ + return map[idx >> 5] & (1u << (idx & 0x1f)); +} + +static inline unsigned int wined3d_bitmap_ffs_xor(const uint32_t *bitmap, unsigned int bit_count, + unsigned int start, uint32_t xor_mask) +{ + const unsigned int word_bit_count = sizeof(*bitmap) * CHAR_BIT; + const uint32_t *ptr, *end_ptr; + uint32_t map, mask; + + assert(bit_count < word_bit_count || !(bit_count % word_bit_count)); + + ptr = bitmap + start / word_bit_count; + end_ptr = bitmap + (bit_count + word_bit_count - 1) / word_bit_count; + + if (ptr >= end_ptr) + return ~0u; + + mask = ~0u << start % word_bit_count; + map = (*ptr ^ xor_mask) & mask; + while (!map) + { + if (++ptr == end_ptr) + return ~0u; + map = *ptr ^ xor_mask; + } + return (ptr - bitmap) * word_bit_count + wined3d_bit_scan(&map); +} + +static inline unsigned int wined3d_bitmap_ffs(const uint32_t *bitmap, unsigned int bit_count, unsigned int start) +{ + return wined3d_bitmap_ffs_xor(bitmap, bit_count, start, 0); +} + +static inline unsigned int wined3d_bitmap_ffz(const uint32_t *bitmap, unsigned int bit_count, unsigned int start) +{ + return wined3d_bitmap_ffs_xor(bitmap, bit_count, start, ~0u); +} + +static inline BOOL wined3d_bitmap_get_range(const DWORD *bitmap, unsigned int bit_count, + unsigned int start, struct wined3d_range *range) +{ + unsigned int range_start, range_end; + + range_start = wined3d_bitmap_ffs(bitmap, bit_count, start); + if (range_start == ~0u) + return FALSE; + + range_end = wined3d_bitmap_ffz(bitmap, bit_count, range_start + 1); + if (range_end == ~0u) + range_end = bit_count; + + range->offset = range_start; + range->size = range_end - range_start; + return TRUE; +} + +static inline bool wined3d_context_is_graphics_state_dirty(const struct wined3d_context *context, unsigned int state_id) +{ + return wined3d_bitmap_is_set(context->dirty_graphics_states, state_id); +} + +static inline bool wined3d_context_is_compute_state_dirty(const struct wined3d_context *context, unsigned int state_id) +{ + return wined3d_bitmap_is_set(context->dirty_compute_states, state_id - STATE_COMPUTE_OFFSET); +} + +static inline bool isStateDirty(const struct wined3d_context *context, unsigned int state_id) +{ + return wined3d_context_is_graphics_state_dirty(context, state_id); +} + +static inline VkImageAspectFlags vk_aspect_mask_from_format(const struct wined3d_format *format) +{ + VkImageAspectFlags mask = 0; + + if (format->depth_size) + mask |= VK_IMAGE_ASPECT_DEPTH_BIT; + if (format->stencil_size) + mask |= VK_IMAGE_ASPECT_STENCIL_BIT; + if (!mask || format->red_size || format->green_size || format->blue_size || format->alpha_size) + mask |= VK_IMAGE_ASPECT_COLOR_BIT; + + return mask; +} + +static inline bool wined3d_primitive_type_is_list(enum wined3d_primitive_type t) +{ + return t == WINED3D_PT_POINTLIST + || t == WINED3D_PT_LINELIST + || t == WINED3D_PT_TRIANGLELIST + || t == WINED3D_PT_LINELIST_ADJ + || t == WINED3D_PT_TRIANGLELIST_ADJ + || t == WINED3D_PT_PATCH; +} + +static inline void wined3d_context_gl_reference_bo(struct wined3d_context_gl *context_gl, struct wined3d_bo_gl *bo_gl) +{ + struct wined3d_device_gl *device_gl = wined3d_device_gl(context_gl->c.device); + + bo_gl->command_fence_id = device_gl->current_fence_id; +} + +static inline bool wined3d_map_persistent(void) +{ + return sizeof(void *) >= sizeof(uint64_t); +} + +/* The WNDCLASS-Name for the fake window which we use to retrieve the GL capabilities */ +#define WINED3D_OPENGL_WINDOW_CLASS_NAME "WineD3D_OpenGL" + +extern CRITICAL_SECTION wined3d_command_cs; + +#endif diff --git a/wrappers/directx/d3dwine_wrapper/wined3d_vk.h b/wrappers/directx/d3dwine_wrapper/wined3d_vk.h new file mode 100644 index 00000000000..a417a795901 --- /dev/null +++ b/wrappers/directx/d3dwine_wrapper/wined3d_vk.h @@ -0,0 +1,232 @@ +/* + * Copyright 2018 Józef Kucia for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __WINE_WINED3D_VK_H +#define __WINE_WINED3D_VK_H + +#define VK_NO_PROTOTYPES +#ifndef USE_WIN32_VULKAN +#define WINE_VK_HOST +#endif +#include "wine/vulkan.h" + +#define VK_INSTANCE_FUNCS() \ + VK_INSTANCE_PFN(vkCreateDevice) \ + VK_INSTANCE_PFN(vkDestroyInstance) \ + VK_INSTANCE_PFN(vkEnumerateDeviceExtensionProperties) \ + VK_INSTANCE_PFN(vkEnumerateDeviceLayerProperties) \ + VK_INSTANCE_PFN(vkEnumeratePhysicalDevices) \ + VK_INSTANCE_PFN(vkGetDeviceProcAddr) \ + VK_INSTANCE_PFN(vkGetPhysicalDeviceFeatures) \ + VK_INSTANCE_PFN(vkGetPhysicalDeviceFormatProperties) \ + VK_INSTANCE_PFN(vkGetPhysicalDeviceImageFormatProperties) \ + VK_INSTANCE_PFN(vkGetPhysicalDeviceMemoryProperties) \ + VK_INSTANCE_PFN(vkGetPhysicalDeviceProperties) \ + VK_INSTANCE_PFN(vkGetPhysicalDeviceQueueFamilyProperties) \ + VK_INSTANCE_PFN(vkGetPhysicalDeviceSparseImageFormatProperties) \ + /* Vulkan 1.1 */ \ + VK_INSTANCE_EXT_PFN(vkGetPhysicalDeviceFeatures2) \ + VK_INSTANCE_EXT_PFN(vkGetPhysicalDeviceProperties2) \ + /* VK_KHR_surface */ \ + VK_INSTANCE_PFN(vkDestroySurfaceKHR) \ + VK_INSTANCE_PFN(vkGetPhysicalDeviceSurfaceCapabilitiesKHR) \ + VK_INSTANCE_PFN(vkGetPhysicalDeviceSurfaceFormatsKHR) \ + VK_INSTANCE_PFN(vkGetPhysicalDeviceSurfacePresentModesKHR) \ + VK_INSTANCE_PFN(vkGetPhysicalDeviceSurfaceSupportKHR) \ + /* VK_KHR_win32_surface */ \ + VK_INSTANCE_PFN(vkCreateWin32SurfaceKHR) + +#define VK_DEVICE_FUNCS() \ + VK_DEVICE_PFN(vkAllocateCommandBuffers) \ + VK_DEVICE_PFN(vkAllocateDescriptorSets) \ + VK_DEVICE_PFN(vkAllocateMemory) \ + VK_DEVICE_PFN(vkBeginCommandBuffer) \ + VK_DEVICE_PFN(vkBindBufferMemory) \ + VK_DEVICE_PFN(vkBindImageMemory) \ + VK_DEVICE_PFN(vkCmdBeginQuery) \ + VK_DEVICE_PFN(vkCmdBeginRenderPass) \ + VK_DEVICE_PFN(vkCmdBindDescriptorSets) \ + VK_DEVICE_PFN(vkCmdBindIndexBuffer) \ + VK_DEVICE_PFN(vkCmdBindPipeline) \ + VK_DEVICE_PFN(vkCmdBindVertexBuffers) \ + VK_DEVICE_PFN(vkCmdBlitImage) \ + VK_DEVICE_PFN(vkCmdClearAttachments) \ + VK_DEVICE_PFN(vkCmdClearColorImage) \ + VK_DEVICE_PFN(vkCmdClearDepthStencilImage) \ + VK_DEVICE_PFN(vkCmdCopyBuffer) \ + VK_DEVICE_PFN(vkCmdCopyBufferToImage) \ + VK_DEVICE_PFN(vkCmdCopyImage) \ + VK_DEVICE_PFN(vkCmdCopyImageToBuffer) \ + VK_DEVICE_PFN(vkCmdCopyQueryPoolResults) \ + VK_DEVICE_PFN(vkCmdDispatch) \ + VK_DEVICE_PFN(vkCmdDispatchIndirect) \ + VK_DEVICE_PFN(vkCmdDraw) \ + VK_DEVICE_PFN(vkCmdDrawIndexed) \ + VK_DEVICE_PFN(vkCmdDrawIndexedIndirect) \ + VK_DEVICE_PFN(vkCmdDrawIndirect) \ + VK_DEVICE_PFN(vkCmdEndQuery) \ + VK_DEVICE_PFN(vkCmdEndRenderPass) \ + VK_DEVICE_PFN(vkCmdExecuteCommands) \ + VK_DEVICE_PFN(vkCmdFillBuffer) \ + VK_DEVICE_PFN(vkCmdNextSubpass) \ + VK_DEVICE_PFN(vkCmdPipelineBarrier) \ + VK_DEVICE_PFN(vkCmdPushConstants) \ + VK_DEVICE_PFN(vkCmdResetEvent) \ + VK_DEVICE_PFN(vkCmdResetQueryPool) \ + VK_DEVICE_PFN(vkCmdResolveImage) \ + VK_DEVICE_PFN(vkCmdSetBlendConstants) \ + VK_DEVICE_PFN(vkCmdSetDepthBias) \ + VK_DEVICE_PFN(vkCmdSetDepthBounds) \ + VK_DEVICE_PFN(vkCmdSetEvent) \ + VK_DEVICE_PFN(vkCmdSetLineWidth) \ + VK_DEVICE_PFN(vkCmdSetScissor) \ + VK_DEVICE_PFN(vkCmdSetStencilCompareMask) \ + VK_DEVICE_PFN(vkCmdSetStencilReference) \ + VK_DEVICE_PFN(vkCmdSetStencilWriteMask) \ + VK_DEVICE_PFN(vkCmdSetViewport) \ + VK_DEVICE_PFN(vkCmdUpdateBuffer) \ + VK_DEVICE_PFN(vkCmdWaitEvents) \ + VK_DEVICE_PFN(vkCmdWriteTimestamp) \ + VK_DEVICE_PFN(vkCreateBuffer) \ + VK_DEVICE_PFN(vkCreateBufferView) \ + VK_DEVICE_PFN(vkCreateCommandPool) \ + VK_DEVICE_PFN(vkCreateComputePipelines) \ + VK_DEVICE_PFN(vkCreateDescriptorPool) \ + VK_DEVICE_PFN(vkCreateDescriptorSetLayout) \ + VK_DEVICE_PFN(vkCreateEvent) \ + VK_DEVICE_PFN(vkCreateFence) \ + VK_DEVICE_PFN(vkCreateFramebuffer) \ + VK_DEVICE_PFN(vkCreateGraphicsPipelines) \ + VK_DEVICE_PFN(vkCreateImage) \ + VK_DEVICE_PFN(vkCreateImageView) \ + VK_DEVICE_PFN(vkCreatePipelineCache) \ + VK_DEVICE_PFN(vkCreatePipelineLayout) \ + VK_DEVICE_PFN(vkCreateQueryPool) \ + VK_DEVICE_PFN(vkCreateRenderPass) \ + VK_DEVICE_PFN(vkCreateSampler) \ + VK_DEVICE_PFN(vkCreateSemaphore) \ + VK_DEVICE_PFN(vkCreateShaderModule) \ + VK_DEVICE_PFN(vkDestroyBuffer) \ + VK_DEVICE_PFN(vkDestroyBufferView) \ + VK_DEVICE_PFN(vkDestroyCommandPool) \ + VK_DEVICE_PFN(vkDestroyDescriptorPool) \ + VK_DEVICE_PFN(vkDestroyDescriptorSetLayout) \ + VK_DEVICE_PFN(vkDestroyDevice) \ + VK_DEVICE_PFN(vkDestroyEvent) \ + VK_DEVICE_PFN(vkDestroyFence) \ + VK_DEVICE_PFN(vkDestroyFramebuffer) \ + VK_DEVICE_PFN(vkDestroyImage) \ + VK_DEVICE_PFN(vkDestroyImageView) \ + VK_DEVICE_PFN(vkDestroyPipeline) \ + VK_DEVICE_PFN(vkDestroyPipelineCache) \ + VK_DEVICE_PFN(vkDestroyPipelineLayout) \ + VK_DEVICE_PFN(vkDestroyQueryPool) \ + VK_DEVICE_PFN(vkDestroyRenderPass) \ + VK_DEVICE_PFN(vkDestroySampler) \ + VK_DEVICE_PFN(vkDestroySemaphore) \ + VK_DEVICE_PFN(vkDestroyShaderModule) \ + VK_DEVICE_PFN(vkDeviceWaitIdle) \ + VK_DEVICE_PFN(vkEndCommandBuffer) \ + VK_DEVICE_PFN(vkFlushMappedMemoryRanges) \ + VK_DEVICE_PFN(vkFreeCommandBuffers) \ + VK_DEVICE_PFN(vkFreeDescriptorSets) \ + VK_DEVICE_PFN(vkFreeMemory) \ + VK_DEVICE_PFN(vkGetBufferMemoryRequirements) \ + VK_DEVICE_PFN(vkGetDeviceMemoryCommitment) \ + VK_DEVICE_PFN(vkGetDeviceQueue) \ + VK_DEVICE_PFN(vkGetEventStatus) \ + VK_DEVICE_PFN(vkGetFenceStatus) \ + VK_DEVICE_PFN(vkGetImageMemoryRequirements) \ + VK_DEVICE_PFN(vkGetImageSparseMemoryRequirements) \ + VK_DEVICE_PFN(vkGetImageSubresourceLayout) \ + VK_DEVICE_PFN(vkGetPipelineCacheData) \ + VK_DEVICE_PFN(vkGetQueryPoolResults) \ + VK_DEVICE_PFN(vkGetRenderAreaGranularity) \ + VK_DEVICE_PFN(vkInvalidateMappedMemoryRanges) \ + VK_DEVICE_PFN(vkMapMemory) \ + VK_DEVICE_PFN(vkMergePipelineCaches) \ + VK_DEVICE_PFN(vkQueueBindSparse) \ + VK_DEVICE_PFN(vkQueueSubmit) \ + VK_DEVICE_PFN(vkQueueWaitIdle) \ + VK_DEVICE_PFN(vkResetCommandBuffer) \ + VK_DEVICE_PFN(vkResetCommandPool) \ + VK_DEVICE_PFN(vkResetDescriptorPool) \ + VK_DEVICE_PFN(vkResetEvent) \ + VK_DEVICE_PFN(vkResetFences) \ + VK_DEVICE_PFN(vkSetEvent) \ + VK_DEVICE_PFN(vkUnmapMemory) \ + VK_DEVICE_PFN(vkUpdateDescriptorSets) \ + VK_DEVICE_PFN(vkWaitForFences) \ + /* VK_EXT_transform_feedback */ \ + VK_DEVICE_EXT_PFN(vkCmdBeginQueryIndexedEXT) \ + VK_DEVICE_EXT_PFN(vkCmdBeginTransformFeedbackEXT) \ + VK_DEVICE_EXT_PFN(vkCmdBindTransformFeedbackBuffersEXT) \ + VK_DEVICE_EXT_PFN(vkCmdEndQueryIndexedEXT) \ + VK_DEVICE_EXT_PFN(vkCmdEndTransformFeedbackEXT) \ + /* VK_KHR_swapchain */ \ + VK_DEVICE_PFN(vkAcquireNextImageKHR) \ + VK_DEVICE_PFN(vkCreateSwapchainKHR) \ + VK_DEVICE_PFN(vkDestroySwapchainKHR) \ + VK_DEVICE_PFN(vkGetSwapchainImagesKHR) \ + VK_DEVICE_PFN(vkQueuePresentKHR) + +#define DECLARE_VK_PFN(name) PFN_##name name; + +struct vulkan_ops +{ +#define VK_INSTANCE_PFN DECLARE_VK_PFN +#define VK_INSTANCE_EXT_PFN DECLARE_VK_PFN +#define VK_DEVICE_PFN DECLARE_VK_PFN +#define VK_DEVICE_EXT_PFN DECLARE_VK_PFN + VK_DEVICE_FUNCS() + VK_INSTANCE_FUNCS() +#undef VK_INSTANCE_PFN +#undef VK_INSTANCE_EXT_PFN +#undef VK_DEVICE_PFN +#undef VK_DEVICE_EXT_PFN + + PFN_vkCreateInstance vkCreateInstance; + PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr; +}; + +enum wined3d_vk_extension +{ + WINED3D_VK_EXT_NONE, + + WINED3D_VK_EXT_TRANSFORM_FEEDBACK, + WINED3D_VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE, + + WINED3D_VK_EXT_COUNT, +}; + +struct wined3d_vk_info +{ + struct vulkan_ops vk_ops; + + VkInstance instance; + unsigned int api_version; + + BOOL supported[WINED3D_VK_EXT_COUNT]; +#ifdef USE_WIN32_VULKAN + HMODULE vulkan_lib; +#endif +}; + +#define VK_CALL(f) (vk_info->vk_ops.f) + +#endif /* __WINE_WINED3D_VK */ diff --git a/wrappers/new-dlls/cryptsp/CRYPTSP.spec b/wrappers/new-dlls/cryptsp/CRYPTSP.spec index f890bf346a4..5795eb7c342 100644 --- a/wrappers/new-dlls/cryptsp/CRYPTSP.spec +++ b/wrappers/new-dlls/cryptsp/CRYPTSP.spec @@ -1,3 +1,4 @@ +@ stdcall CheckSignatureInFile(str) advapi32.SystemFunction035 @ stdcall CryptAcquireContextA(ptr str str long long) @ stdcall CryptAcquireContextW(ptr wstr wstr long long) @ stdcall CryptContextAddRef(long ptr long) @@ -37,4 +38,27 @@ @ stdcall CryptSignHashW(long long ptr long ptr ptr) @ stdcall CryptVerifySignatureA(long ptr long long ptr long) @ stdcall CryptVerifySignatureW(long ptr long long ptr long) +@ stdcall -import SystemFunction006(ptr ptr) +@ stdcall -import SystemFunction007(ptr ptr) +@ stdcall -import SystemFunction008(ptr ptr ptr) +@ stdcall -import SystemFunction009(ptr ptr ptr) +@ stdcall -import SystemFunction010(ptr ptr ptr) +@ stdcall -import SystemFunction011(ptr ptr ptr) +@ stdcall -import SystemFunction012(ptr ptr ptr) +@ stdcall -import SystemFunction013(ptr ptr ptr) +@ stdcall -import SystemFunction014(ptr ptr ptr) +@ stdcall -import SystemFunction015(ptr ptr ptr) +@ stdcall -import SystemFunction016(ptr ptr ptr) +@ stdcall -import SystemFunction018(ptr ptr ptr) +@ stdcall -import SystemFunction020(ptr ptr ptr) +@ stdcall -import SystemFunction021(ptr ptr ptr) +@ stdcall -import SystemFunction022(ptr ptr ptr) +@ stdcall -import SystemFunction023(ptr ptr ptr) +@ stdcall -import SystemFunction024(ptr ptr ptr) +@ stdcall -import SystemFunction025(ptr ptr ptr) +@ stdcall -import SystemFunction026(ptr ptr ptr) +@ stdcall -import SystemFunction027(ptr ptr ptr) +@ stdcall -import SystemFunction030(ptr ptr) +@ stdcall -import SystemFunction031(ptr ptr) +@ stdcall -import SystemFunction032(ptr ptr) @ stdcall SystemFunction035(str) \ No newline at end of file