diff --git a/CMakeLists.txt b/CMakeLists.txt index b3249f2eea..b1921725f1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -347,6 +347,8 @@ add_library(Common STATIC Common/Misc.cpp Common/MsgHandler.cpp Common/MsgHandler.h + Common/OSVersion.cpp + Common/OSVersion.h Common/StringUtils.cpp Common/StringUtils.h Common/ThreadPools.cpp @@ -1156,13 +1158,36 @@ set(GPU_D3D9 GPU/Directx9/VertexShaderGeneratorDX9.h ) +set(GPU_D3D11 + GPU/D3D11/DepalettizeShaderD3D11.cpp + GPU/D3D11/DepalettizeShaderD3D11.h + GPU/D3D11/DrawEngineD3D11.cpp + GPU/D3D11/DrawEngineD3D11.h + GPU/D3D11/FragmentShaderGeneratorD3D11.cpp + GPU/D3D11/FragmentShaderGeneratorD3D11.h + GPU/D3D11/FramebufferManagerD3D11.cpp + GPU/D3D11/FramebufferManagerD3D11.h + GPU/D3D11/GPU_D3D11.cpp + GPU/D3D11/GPU_D3D11.h + GPU/D3D11/StencilBufferD3D11.cpp + GPU/D3D11/ShaderManagerD3D11.cpp + GPU/D3D11/ShaderManagerD3D11.h + GPU/D3D11/StateMappingD3D11.cpp + GPU/D3D11/StateMappingD3D11.h + GPU/D3D11/TextureCacheD3D11.cpp + GPU/D3D11/TextureCacheD3D11.h + GPU/D3D11/TextureScalerD3D11.cpp + GPU/D3D11/TextureScalerD3D11.h + GPU/D3D11/VertexShaderGeneratorD3D11.cpp + GPU/D3D11/VertexShaderGeneratorD3D11.h +) set(GPU_IMPLS ${GPU_GLES}) if(VULKAN) set(GPU_IMPLS ${GPU_IMPLS} ${GPU_VULKAN}) endif() if(WIN32) - list(APPEND GPU_IMPLS ${GPU_D3D9}) + list(APPEND GPU_IMPLS ${GPU_D3D9} ${GPU_D3D11}) endif() if(ARMV7 OR ARM64) diff --git a/Common/Common.vcxproj b/Common/Common.vcxproj index c2ed3cd411..c2266ce701 100644 --- a/Common/Common.vcxproj +++ b/Common/Common.vcxproj @@ -233,6 +233,7 @@ + @@ -300,6 +301,7 @@ + Create Create @@ -326,4 +328,4 @@ - + \ No newline at end of file diff --git a/Common/Common.vcxproj.filters b/Common/Common.vcxproj.filters index c707726aa0..874c86925a 100644 --- a/Common/Common.vcxproj.filters +++ b/Common/Common.vcxproj.filters @@ -73,6 +73,7 @@ Vulkan + @@ -135,6 +136,7 @@ + @@ -150,4 +152,4 @@ {c14d66ef-5f7c-4565-975a-72774e7ccfb9} - + \ No newline at end of file diff --git a/Common/OSVersion.cpp b/Common/OSVersion.cpp new file mode 100644 index 0000000000..4e1a2f5381 --- /dev/null +++ b/Common/OSVersion.cpp @@ -0,0 +1,87 @@ + +#ifdef _MSC_VER + +#include +#include "OSVersion.h" +#include "Common/CommonWindows.h" + +bool DoesVersionMatchWindows(uint32_t major, uint32_t minor, uint32_t spMajor, uint32_t spMinor) { + uint64_t conditionMask = 0; + OSVERSIONINFOEX osvi; + ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); + + osvi.dwOSVersionInfoSize = sizeof(osvi); + osvi.dwMajorVersion = major; + osvi.dwMinorVersion = minor; + osvi.wServicePackMajor = spMajor; + osvi.wServicePackMinor = spMinor; + uint32_t op = VER_EQUAL; + + VER_SET_CONDITION(conditionMask, VER_MAJORVERSION, op); + VER_SET_CONDITION(conditionMask, VER_MINORVERSION, op); + VER_SET_CONDITION(conditionMask, VER_SERVICEPACKMAJOR, op); + VER_SET_CONDITION(conditionMask, VER_SERVICEPACKMINOR, op); + + const uint32_t typeMask = VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR; + + return VerifyVersionInfo(&osvi, typeMask, conditionMask) != FALSE; +} + +std::string GetWindowsVersion() { + const bool IsWindowsXPSP2 = DoesVersionMatchWindows(5, 1, 2, 0); + const bool IsWindowsXPSP3 = DoesVersionMatchWindows(5, 1, 3, 0); + const bool IsWindowsVista = DoesVersionMatchWindows(6, 0); + const bool IsWindowsVistaSP1 = DoesVersionMatchWindows(6, 0, 1, 0); + const bool IsWindowsVistaSP2 = DoesVersionMatchWindows(6, 0, 2, 0); + const bool IsWindows7 = DoesVersionMatchWindows(6, 1); + const bool IsWindows7SP1 = DoesVersionMatchWindows(6, 1, 1, 0); + const bool IsWindows8 = DoesVersionMatchWindows(6, 2); + const bool IsWindows8_1 = DoesVersionMatchWindows(6, 3); + + if (IsWindowsXPSP2) + return "Microsoft Windows XP, Service Pack 2"; + + if (IsWindowsXPSP3) + return "Microsoft Windows XP, Service Pack 3"; + + if (IsWindowsVista) + return "Microsoft Windows Vista"; + + if (IsWindowsVistaSP1) + return "Microsoft Windows Vista, Service Pack 1"; + + if (IsWindowsVistaSP2) + return "Microsoft Windows Vista, Service Pack 2"; + + if (IsWindows7) + return "Microsoft Windows 7"; + + if (IsWindows7SP1) + return "Microsoft Windows 7, Service Pack 1"; + + if (IsWindows8) + return "Microsoft Windows 8 or greater"; // "Applications not manifested for Windows 10 will return the Windows 8 OS version value (6.2)." + + if (IsWindows8_1) + return "Microsoft Windows 8.1"; + + return "Unsupported version of Microsoft Windows."; +} + +std::string GetWindowsSystemArchitecture() { + SYSTEM_INFO sysinfo; + ZeroMemory(&sysinfo, sizeof(SYSTEM_INFO)); + GetNativeSystemInfo(&sysinfo); + + if (sysinfo.wProcessorArchitecture & PROCESSOR_ARCHITECTURE_AMD64) + return "(x64)"; + // Need to check for equality here, since ANDing with 0 is always 0. + else if (sysinfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL) + return "(x86)"; + else if (sysinfo.wProcessorArchitecture & PROCESSOR_ARCHITECTURE_ARM) + return "(ARM)"; + else + return "(Unknown)"; +} + +#endif \ No newline at end of file diff --git a/Common/OSVersion.h b/Common/OSVersion.h new file mode 100644 index 0000000000..ded3801046 --- /dev/null +++ b/Common/OSVersion.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +#ifdef _MSC_VER + +bool DoesVersionMatchWindows(uint32_t major, uint32_t minor, uint32_t spMajor = 0, uint32_t spMinor = 0); +std::string GetWindowsVersion(); +std::string GetWindowsSystemArchitecture(); + +#endif \ No newline at end of file diff --git a/GPU/Common/DepalettizeShaderCommon.cpp b/GPU/Common/DepalettizeShaderCommon.cpp index c9b1481138..7c0ffa5e72 100644 --- a/GPU/Common/DepalettizeShaderCommon.cpp +++ b/GPU/Common/DepalettizeShaderCommon.cpp @@ -29,7 +29,12 @@ // Uses integer instructions available since OpenGL 3.0. Suitable for ES 3.0 as well. void GenerateDepalShader300(char *buffer, GEBufferFormat pixelFormat, ShaderLanguage language) { char *p = buffer; - if (language == GLSL_VULKAN) { + if (language == HLSL_D3D11) { + WRITE(p, "SamplerState texSamp : register(s0);\n"); + WRITE(p, "Texture2D tex : register(t0);\n"); + WRITE(p, "SamplerState palSamp : register(s1);\n"); + WRITE(p, "Texture2D pal : register(t1);\n"); + } else if (language == GLSL_VULKAN) { WRITE(p, "#version 140\n"); WRITE(p, "#extension GL_ARB_separate_shader_objects : enable\n"); WRITE(p, "#extension GL_ARB_shading_language_420pack : enable\n"); @@ -50,9 +55,14 @@ void GenerateDepalShader300(char *buffer, GEBufferFormat pixelFormat, ShaderLang WRITE(p, "uniform sampler2D pal;\n"); } - // TODO: Add support for integer textures. Though it hardly matters. - WRITE(p, "void main() {\n"); - WRITE(p, " vec4 color = texture(tex, v_texcoord0);\n"); + if (language == HLSL_D3D11) { + WRITE(p, "float4 main(in float2 v_texcoord0 : TEXCOORD0) : SV_Target {\n"); + WRITE(p, " float4 color = tex.Sample(texSamp, v_texcoord0);\n"); + } else { + // TODO: Add support for integer textures. Though it hardly matters. + WRITE(p, "void main() {\n"); + WRITE(p, " vec4 color = texture(tex, v_texcoord0);\n"); + } int mask = gstate.getClutIndexMask(); int shift = gstate.getClutIndexShift(); @@ -110,7 +120,11 @@ void GenerateDepalShader300(char *buffer, GEBufferFormat pixelFormat, ShaderLang WRITE(p, ";\n"); } - WRITE(p, " fragColor0 = texture(pal, vec2((float(index) + 0.5) * (1.0 / %f), 0.0));\n", texturePixels); + if (language == HLSL_D3D11) { + WRITE(p, " return pal.Sample(palSamp, float2((float(index) + 0.5) * (1.0 / %f), 0.0));\n", texturePixels); + } else { + WRITE(p, " fragColor0 = texture(pal, vec2((float(index) + 0.5) * (1.0 / %f), 0.0));\n", texturePixels); + } WRITE(p, "}\n"); } @@ -262,6 +276,7 @@ void GenerateDepalShader(char *buffer, GEBufferFormat pixelFormat, ShaderLanguag break; case GLSL_300: case GLSL_VULKAN: + case HLSL_D3D11: GenerateDepalShader300(buffer, pixelFormat, language); break; case HLSL_DX9: diff --git a/GPU/Common/DepalettizeShaderCommon.h b/GPU/Common/DepalettizeShaderCommon.h index c49a65cdbf..7d2b02d113 100644 --- a/GPU/Common/DepalettizeShaderCommon.h +++ b/GPU/Common/DepalettizeShaderCommon.h @@ -18,12 +18,6 @@ #pragma once #include "GPU/ge_constants.h" - -enum ShaderLanguage { - GLSL_140, - GLSL_300, - GLSL_VULKAN, - HLSL_DX9, -}; +#include "GPU/Common/ShaderCommon.h" void GenerateDepalShader(char *buffer, GEBufferFormat pixelFormat, ShaderLanguage language); diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index f6b7e5e17f..e4dc209d32 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -567,6 +567,7 @@ void FramebufferManagerCommon::NotifyRenderFramebufferSwitched(VirtualFramebuffe textureCache_->ForgetLastTexture(); if (useBufferedRendering_) { + if (vfb->fbo) { draw_->BindFramebufferAsRenderTarget(vfb->fbo); } else { diff --git a/GPU/Common/ShaderCommon.h b/GPU/Common/ShaderCommon.h index 967d2f01ea..cef8ea7529 100644 --- a/GPU/Common/ShaderCommon.h +++ b/GPU/Common/ShaderCommon.h @@ -17,6 +17,16 @@ #pragma once +#include + +enum ShaderLanguage { + GLSL_140, + GLSL_300, + GLSL_VULKAN, + HLSL_DX9, + HLSL_D3D11, +}; + enum DebugShaderType { SHADER_TYPE_VERTEX = 0, SHADER_TYPE_FRAGMENT = 1, diff --git a/GPU/Common/ShaderId.h b/GPU/Common/ShaderId.h index 0d2f27713c..07060d80e6 100644 --- a/GPU/Common/ShaderId.h +++ b/GPU/Common/ShaderId.h @@ -1,5 +1,7 @@ #pragma once +#include +#include #include "base/basictypes.h" // TODO: There will be additional bits, indicating that groups of these will be @@ -93,9 +95,9 @@ struct ShaderID { } } - u32 d[2]; + uint32_t d[2]; bool operator < (const ShaderID &other) const { - for (size_t i = 0; i < sizeof(d) / sizeof(u32); i++) { + for (size_t i = 0; i < sizeof(d) / sizeof(uint32_t); i++) { if (d[i] < other.d[i]) return true; if (d[i] > other.d[i]) @@ -104,7 +106,7 @@ struct ShaderID { return false; } bool operator == (const ShaderID &other) const { - for (size_t i = 0; i < sizeof(d) / sizeof(u32); i++) { + for (size_t i = 0; i < sizeof(d) / sizeof(uint32_t); i++) { if (d[i] != other.d[i]) return false; } @@ -142,7 +144,7 @@ struct ShaderID { bool CanUseHardwareTransform(int prim); -void ComputeVertexShaderID(ShaderID *id, u32 vertexType, bool useHWTransform); +void ComputeVertexShaderID(ShaderID *id, uint32_t vertexType, bool useHWTransform); // Generates a compact string that describes the shader. Useful in a list to get an overview // of the current flora of shaders. std::string VertexShaderDesc(const ShaderID &id); diff --git a/GPU/Common/ShaderUniforms.cpp b/GPU/Common/ShaderUniforms.cpp new file mode 100644 index 0000000000..99e56f6721 --- /dev/null +++ b/GPU/Common/ShaderUniforms.cpp @@ -0,0 +1,231 @@ +#include + +#include "ShaderUniforms.h" +#include "math/dataconv.h" +#include "math/lin/matrix4x4.h" +#include "math/math_util.h" +#include "math/lin/vec3.h" +#include "GPU/GPUState.h" +#include "GPU/Math3D.h" +#include "Core/Reporting.h" + +static void ConvertProjMatrixToVulkan(Matrix4x4 &in) { + const Vec3 trans(0, 0, gstate_c.vpZOffset * 0.5f + 0.5f); + const Vec3 scale(gstate_c.vpWidthScale, gstate_c.vpHeightScale, gstate_c.vpDepthScale * 0.5f); + in.translateAndScale(trans, scale); +} + +static void ConvertProjMatrixToD3D11(Matrix4x4 &in) { + const Vec3 trans(0, 0, gstate_c.vpZOffset * 0.5f + 0.5f); + const Vec3 scale(gstate_c.vpWidthScale, -gstate_c.vpHeightScale, gstate_c.vpDepthScale * 0.5f); + in.translateAndScale(trans, scale); +} + +void BaseUpdateUniforms(UB_VS_FS_Base *ub, uint64_t dirtyUniforms, bool flipViewport) { + if (dirtyUniforms & DIRTY_TEXENV) { + Uint8x3ToFloat4(ub->texEnvColor, gstate.texenvcolor); + } + if (dirtyUniforms & DIRTY_ALPHACOLORREF) { + Uint8x3ToInt4_Alpha(ub->alphaColorRef, gstate.getColorTestRef(), gstate.getAlphaTestRef() & gstate.getAlphaTestMask()); + } + if (dirtyUniforms & DIRTY_ALPHACOLORMASK) { + Uint8x3ToInt4_Alpha(ub->colorTestMask, gstate.getColorTestMask(), gstate.getAlphaTestMask()); + } + if (dirtyUniforms & DIRTY_FOGCOLOR) { + Uint8x3ToFloat4(ub->fogColor, gstate.fogcolor); + } + if (dirtyUniforms & DIRTY_SHADERBLEND) { + Uint8x3ToFloat4(ub->blendFixA, gstate.getFixA()); + Uint8x3ToFloat4(ub->blendFixB, gstate.getFixB()); + } + if (dirtyUniforms & DIRTY_TEXCLAMP) { + const float invW = 1.0f / (float)gstate_c.curTextureWidth; + const float invH = 1.0f / (float)gstate_c.curTextureHeight; + const int w = gstate.getTextureWidth(0); + const int h = gstate.getTextureHeight(0); + const float widthFactor = (float)w * invW; + const float heightFactor = (float)h * invH; + + // First wrap xy, then half texel xy (for clamp.) + ub->texClamp[0] = widthFactor; + ub->texClamp[1] = heightFactor; + ub->texClamp[2] = invW * 0.5f; + ub->texClamp[3] = invH * 0.5f; + ub->texClampOffset[0] = gstate_c.curTextureXOffset * invW; + ub->texClampOffset[1] = gstate_c.curTextureYOffset * invH; + } + + if (dirtyUniforms & DIRTY_PROJMATRIX) { + Matrix4x4 flippedMatrix; + memcpy(&flippedMatrix, gstate.projMatrix, 16 * sizeof(float)); + + const bool invertedY = gstate_c.vpHeight < 0; + if (invertedY) { + flippedMatrix[1] = -flippedMatrix[1]; + flippedMatrix[5] = -flippedMatrix[5]; + flippedMatrix[9] = -flippedMatrix[9]; + flippedMatrix[13] = -flippedMatrix[13]; + } + const bool invertedX = gstate_c.vpWidth < 0; + if (invertedX) { + flippedMatrix[0] = -flippedMatrix[0]; + flippedMatrix[4] = -flippedMatrix[4]; + flippedMatrix[8] = -flippedMatrix[8]; + flippedMatrix[12] = -flippedMatrix[12]; + } + if (flipViewport) { + ConvertProjMatrixToD3D11(flippedMatrix); + } else { + ConvertProjMatrixToVulkan(flippedMatrix); + } + CopyMatrix4x4(ub->proj, flippedMatrix.getReadPtr()); + } + + if (dirtyUniforms & DIRTY_PROJTHROUGHMATRIX) { + Matrix4x4 proj_through; + if (flipViewport) { + proj_through.setOrthoD3D(0.0f, gstate_c.curRTWidth, gstate_c.curRTHeight, 0, 0, 1); + } else { + proj_through.setOrthoVulkan(0.0f, gstate_c.curRTWidth, 0, gstate_c.curRTHeight, 0, 1); + } + CopyMatrix4x4(ub->proj_through, proj_through.getReadPtr()); + } + + // Transform + if (dirtyUniforms & DIRTY_WORLDMATRIX) { + ConvertMatrix4x3To4x4(ub->world, gstate.worldMatrix); + } + if (dirtyUniforms & DIRTY_VIEWMATRIX) { + ConvertMatrix4x3To4x4(ub->view, gstate.viewMatrix); + } + if (dirtyUniforms & DIRTY_TEXMATRIX) { + ConvertMatrix4x3To4x4(ub->tex, gstate.tgenMatrix); + } + + // Combined two small uniforms + if (dirtyUniforms & (DIRTY_FOGCOEF | DIRTY_STENCILREPLACEVALUE)) { + float fogcoef_stencil[3] = { + getFloat24(gstate.fog1), + getFloat24(gstate.fog2), + (float)gstate.getStencilTestRef()/255.0f + }; + if (my_isinf(fogcoef_stencil[1])) { + // not really sure what a sensible value might be. + fogcoef_stencil[1] = fogcoef_stencil[1] < 0.0f ? -10000.0f : 10000.0f; + } else if (my_isnan(fogcoef_stencil[1])) { + // Workaround for https://github.com/hrydgard/ppsspp/issues/5384#issuecomment-38365988 + // Just put the fog far away at a large finite distance. + // Infinities and NaNs are rather unpredictable in shaders on many GPUs + // so it's best to just make it a sane calculation. + fogcoef_stencil[0] = 100000.0f; + fogcoef_stencil[1] = 1.0f; + } +#ifndef MOBILE_DEVICE + else if (my_isnanorinf(fogcoef_stencil[1]) || my_isnanorinf(fogcoef_stencil[0])) { + ERROR_LOG_REPORT_ONCE(fognan, G3D, "Unhandled fog NaN/INF combo: %f %f", fogcoef_stencil[0], fogcoef_stencil[1]); + } +#endif + CopyFloat3(ub->fogCoef_stencil, fogcoef_stencil); + } + + // Note - this one is not in lighting but in transformCommon as it has uses beyond lighting + if (dirtyUniforms & DIRTY_MATAMBIENTALPHA) { + Uint8x3ToFloat4_AlphaUint8(ub->matAmbient, gstate.materialambient, gstate.getMaterialAmbientA()); + } + + // Texturing + if (dirtyUniforms & DIRTY_UVSCALEOFFSET) { + const float invW = 1.0f / (float)gstate_c.curTextureWidth; + const float invH = 1.0f / (float)gstate_c.curTextureHeight; + const int w = gstate.getTextureWidth(0); + const int h = gstate.getTextureHeight(0); + const float widthFactor = (float)w * invW; + const float heightFactor = (float)h * invH; + ub->uvScaleOffset[0] = widthFactor; + ub->uvScaleOffset[1] = heightFactor; + ub->uvScaleOffset[2] = 0.0f; + ub->uvScaleOffset[3] = 0.0f; + } + + if (dirtyUniforms & DIRTY_DEPTHRANGE) { + float viewZScale = gstate.getViewportZScale(); + float viewZCenter = gstate.getViewportZCenter(); + + // We had to scale and translate Z to account for our clamped Z range. + // Therefore, we also need to reverse this to round properly. + // + // Example: scale = 65535.0, center = 0.0 + // Resulting range = -65535 to 65535, clamped to [0, 65535] + // gstate_c.vpDepthScale = 2.0f + // gstate_c.vpZOffset = -1.0f + // + // The projection already accounts for those, so we need to reverse them. + // + // Additionally, D3D9 uses a range from [0, 1]. We double and move the center. + viewZScale *= (1.0f / gstate_c.vpDepthScale) * 2.0f; + viewZCenter -= 65535.0f * gstate_c.vpZOffset + 32768.5f; + + float viewZInvScale; + if (viewZScale != 0.0) { + viewZInvScale = 1.0f / viewZScale; + } else { + viewZInvScale = 0.0; + } + + ub->depthRange[0] = viewZScale; + ub->depthRange[1] = viewZCenter; + ub->depthRange[2] = viewZCenter; + ub->depthRange[3] = viewZInvScale; + } +} + +void LightUpdateUniforms(UB_VS_Lights *ub, uint64_t dirtyUniforms) { + // Lighting + if (dirtyUniforms & DIRTY_AMBIENT) { + Uint8x3ToFloat4_AlphaUint8(ub->ambientColor, gstate.ambientcolor, gstate.getAmbientA()); + } + if (dirtyUniforms & DIRTY_MATDIFFUSE) { + Uint8x3ToFloat4(ub->materialDiffuse, gstate.materialdiffuse); + } + if (dirtyUniforms & DIRTY_MATSPECULAR) { + Uint8x3ToFloat4_Alpha(ub->materialSpecular, gstate.materialspecular, std::max(0.0f, getFloat24(gstate.materialspecularcoef))); + } + if (dirtyUniforms & DIRTY_MATEMISSIVE) { + Uint8x3ToFloat4(ub->materialEmissive, gstate.materialemissive); + } + + for (int i = 0; i < 4; i++) { + if (dirtyUniforms & (DIRTY_LIGHT0 << i)) { + if (gstate.isDirectionalLight(i)) { + // Prenormalize + float x = getFloat24(gstate.lpos[i * 3 + 0]); + float y = getFloat24(gstate.lpos[i * 3 + 1]); + float z = getFloat24(gstate.lpos[i * 3 + 2]); + float len = sqrtf(x*x + y*y + z*z); + if (len == 0.0f) + len = 1.0f; + else + len = 1.0f / len; + float vec[3] = { x * len, y * len, z * len }; + CopyFloat3To4(ub->lpos[i], vec); + } else { + ExpandFloat24x3ToFloat4(ub->lpos[i], &gstate.lpos[i * 3]); + } + ExpandFloat24x3ToFloat4(ub->ldir[i], &gstate.ldir[i * 3]); + ExpandFloat24x3ToFloat4(ub->latt[i], &gstate.latt[i * 3]); + CopyFloat1To4(ub->lightAngle[i], getFloat24(gstate.lcutoff[i])); + CopyFloat1To4(ub->lightSpotCoef[i], getFloat24(gstate.lconv[i])); + Uint8x3ToFloat4(ub->lightAmbient[i], gstate.lcolor[i * 3]); + Uint8x3ToFloat4(ub->lightDiffuse[i], gstate.lcolor[i * 3 + 1]); + Uint8x3ToFloat4(ub->lightSpecular[i], gstate.lcolor[i * 3 + 2]); + } + } +} + +void BoneUpdateUniforms(UB_VS_Bones *ub, uint64_t dirtyUniforms) { + for (int i = 0; i < 8; i++) { + if (dirtyUniforms & (DIRTY_BONEMATRIX0 << i)) { + ConvertMatrix4x3To4x4(ub->bones[i], gstate.boneMatrix + 12 * i); + } + } +} diff --git a/GPU/Common/ShaderUniforms.h b/GPU/Common/ShaderUniforms.h new file mode 100644 index 0000000000..f4e3b81987 --- /dev/null +++ b/GPU/Common/ShaderUniforms.h @@ -0,0 +1,174 @@ +#pragma once + +#include + +#include "ShaderCommon.h" + +// Used by the "modern" backends that use uniform buffers. They can share this without issue. + +// Pretty much full. Will need more bits for more fine grained dirty tracking for lights. +enum : uint64_t { + DIRTY_BASE_UNIFORMS = + DIRTY_WORLDMATRIX | DIRTY_PROJTHROUGHMATRIX | DIRTY_VIEWMATRIX | DIRTY_TEXMATRIX | DIRTY_ALPHACOLORREF | + DIRTY_PROJMATRIX | DIRTY_FOGCOLOR | DIRTY_FOGCOEF | DIRTY_TEXENV | DIRTY_STENCILREPLACEVALUE | + DIRTY_ALPHACOLORMASK | DIRTY_SHADERBLEND | DIRTY_UVSCALEOFFSET | DIRTY_TEXCLAMP | DIRTY_DEPTHRANGE | DIRTY_MATAMBIENTALPHA, + DIRTY_LIGHT_UNIFORMS = + DIRTY_LIGHT0 | DIRTY_LIGHT1 | DIRTY_LIGHT2 | DIRTY_LIGHT3 | + DIRTY_MATDIFFUSE | DIRTY_MATSPECULAR | DIRTY_MATEMISSIVE | DIRTY_AMBIENT, +}; + +// TODO: Split into two structs, one for software transform and one for hardware transform, to save space. +// 512 bytes. Probably can't get to 256 (nVidia's UBO alignment). +struct UB_VS_FS_Base { + float proj[16]; + float proj_through[16]; + float view[16]; + float world[16]; + float tex[16]; // not that common, may want to break out + float uvScaleOffset[4]; + float depthRange[4]; + float fogCoef_stencil[4]; + float matAmbient[4]; + // Fragment data + float fogColor[4]; + float texEnvColor[4]; + int alphaColorRef[4]; + int colorTestMask[4]; + float blendFixA[4]; + float blendFixB[4]; + float texClamp[4]; + float texClampOffset[4]; +}; + +static const char *ub_baseStr = +R"( mat4 proj_mtx; + mat4 proj_through_mtx; + mat4 view_mtx; + mat4 world_mtx; + mat4 tex_mtx; + vec4 uvscaleoffset; + vec4 depthRange; + vec3 fogcoef_stencilreplace; + vec4 matambientalpha; + vec3 fogcolor; + vec3 texenv; + ivec4 alphacolorref; + ivec4 alphacolormask; + vec3 blendFixA; + vec3 blendFixB; + vec4 texclamp; + vec2 texclampoff; +)"; + +// HLSL code is shared so these names are changed to match those in DX9. +static const char *cb_baseStr = +R"( float4x4 u_proj; + float4x4 u_proj_through; + float4x4 u_view; + float4x4 u_world; + float4x4 u_tex; + float4 u_uvscaleoffset; + float4 u_depthRange; + float3 u_fogcoef_stencilreplace; + float4 u_matambientalpha; + float3 u_fogcolor; + float3 u_texenv; + uint4 u_alphacolorref; + uint4 u_alphacolormask; + float3 u_blendFixA; + float3 u_blendFixB; + float4 u_texclamp; + float2 u_texclampoff; +)"; + +// 576 bytes. Can we get down to 512? +struct UB_VS_Lights { + float ambientColor[4]; + float materialDiffuse[4]; + float materialSpecular[4]; + float materialEmissive[4]; + float lpos[4][4]; + float ldir[4][4]; + float latt[4][4]; + float lightAngle[4][4]; // TODO: Merge with lightSpotCoef, use .xy + float lightSpotCoef[4][4]; + float lightAmbient[4][4]; + float lightDiffuse[4][4]; + float lightSpecular[4][4]; +}; + +static const char *ub_vs_lightsStr = +R"( vec4 u_ambient; + vec3 matdiffuse; + vec4 matspecular; + vec3 matemissive; + vec3 pos[4]; + vec3 dir[4]; + vec3 att[4]; + float angle[4]; + float spotCoef[4]; + vec3 ambient[4]; + vec3 diffuse[4]; + vec3 specular[4]; +)"; + +// HLSL code is shared so these names are changed to match those in DX9. +static const char *cb_vs_lightsStr = +R"( float4 u_ambient; + float3 u_matdiffuse; + float4 u_matspecular; + float3 u_matemissive; + float3 u_lightpos0; + float3 u_lightpos1; + float3 u_lightpos2; + float3 u_lightpos3; + float3 u_lightdir0; + float3 u_lightdir1; + float3 u_lightdir2; + float3 u_lightdir3; + float3 u_lightatt0; + float3 u_lightatt1; + float3 u_lightatt2; + float3 u_lightatt3; + float4 u_lightangle0; + float4 u_lightangle1; + float4 u_lightangle2; + float4 u_lightangle3; + float4 u_lightspotCoef0; + float4 u_lightspotCoef1; + float4 u_lightspotCoef2; + float4 u_lightspotCoef3; + float3 u_lightambient0; + float3 u_lightambient1; + float3 u_lightambient2; + float3 u_lightambient3; + float3 u_lightdiffuse0; + float3 u_lightdiffuse1; + float3 u_lightdiffuse2; + float3 u_lightdiffuse3; + float3 u_lightspecular0; + float3 u_lightspecular1; + float3 u_lightspecular2; + float3 u_lightspecular3; +)"; + +// With some cleverness, we could get away with uploading just half this when only the four first +// bones are being used. This is 512b, 256b would be great. +// Could also move to 4x3 matrices - would let us fit 5 bones into 256b. +struct UB_VS_Bones { + float bones[8][16]; +}; + +static const char *ub_vs_bonesStr = +R"( mat4 m[8]; +)"; + +// HLSL code is shared so these names are changed to match those in DX9. +static const char *cb_vs_bonesStr = +R"( float4x4 m[8]; +)"; + +void BaseUpdateUniforms(UB_VS_FS_Base *ub, uint64_t dirtyUniforms, bool flipViewport); +void LightUpdateUniforms(UB_VS_Lights *ub, uint64_t dirtyUniforms); +void BoneUpdateUniforms(UB_VS_Bones *ub, uint64_t dirtyUniforms); + diff --git a/GPU/Common/SoftwareTransformCommon.cpp b/GPU/Common/SoftwareTransformCommon.cpp index 5a1bfc4955..bab715e13f 100644 --- a/GPU/Common/SoftwareTransformCommon.cpp +++ b/GPU/Common/SoftwareTransformCommon.cpp @@ -406,7 +406,7 @@ void SoftwareTransform( int scissorY2 = gstate.getScissorY2() + 1; reallyAClear = IsReallyAClear(transformed, maxIndex, scissorX2, scissorY2); } - if (reallyAClear && gl_extensions.gpuVendor != GPU_VENDOR_POWERVR) { // && g_Config.iRenderingMode != FB_NON_BUFFERED_MODE) { + if (reallyAClear && gl_extensions.gpuVendor != GPU_VENDOR_POWERVR) { // If alpha is not allowed to be separate, it must match for both depth/stencil and color. Vulkan requires this. bool alphaMatchesColor = gstate.isClearModeColorMask() == gstate.isClearModeAlphaMask(); bool depthMatchesStencil = gstate.isClearModeAlphaMask() == gstate.isClearModeDepthMask(); @@ -502,7 +502,7 @@ void SoftwareTransform( const u16 *indsIn = (const u16 *)inds; u16 *newInds = inds + vertexCount; u16 *indsOut = newInds; - maxIndex = 4 * vertexCount; + maxIndex = 4 * (vertexCount / 2); for (int i = 0; i < vertexCount; i += 2) { const TransformedVertex &transVtxTL = transformed[indsIn[i + 0]]; const TransformedVertex &transVtxBR = transformed[indsIn[i + 1]]; diff --git a/GPU/Common/TextureCacheCommon.h b/GPU/Common/TextureCacheCommon.h index eef5d7f641..56ea4f3fb1 100644 --- a/GPU/Common/TextureCacheCommon.h +++ b/GPU/Common/TextureCacheCommon.h @@ -52,6 +52,29 @@ namespace Draw { class DrawContext; } +// Used by D3D11 and Vulkan, could be used by modern GL +struct SamplerCacheKey { + SamplerCacheKey() : fullKey(0) {} + + union { + u32 fullKey; + struct { + bool mipEnable : 1; + bool minFilt : 1; + bool mipFilt : 1; + bool magFilt : 1; + bool sClamp : 1; + bool tClamp : 1; + int lodBias : 4; + int maxLevel : 4; + }; + }; + + bool operator < (const SamplerCacheKey &other) const { + return fullKey < other.fullKey; + } +}; + class FramebufferManagerCommon; class TextureCacheCommon { @@ -123,6 +146,9 @@ public: void *texturePtr; CachedTextureVulkan *vkTex; }; +#ifdef _WIN32 + void *textureView; // Used by D3D11 only for the shader resource view. +#endif int invalidHint; u32 fullhash; u32 cluthash; diff --git a/GPU/D3D11/D3D11Util.cpp b/GPU/D3D11/D3D11Util.cpp new file mode 100644 index 0000000000..256f57616a --- /dev/null +++ b/GPU/D3D11/D3D11Util.cpp @@ -0,0 +1,110 @@ +#include +#include +#include +#include + +#include "thin3d/d3d11_loader.h" +#include "base/logging.h" +#include "base/stringutil.h" + +#include "D3D11Util.h" + +static std::vector CompileShaderToBytecode(const char *code, size_t codeSize, const char *target) { + ID3DBlob *compiledCode = nullptr; + ID3DBlob *errorMsgs = nullptr; + HRESULT result = ptr_D3DCompile(code, codeSize, nullptr, nullptr, nullptr, "main", target, 0, 0, &compiledCode, &errorMsgs); + std::string errors; + if (errorMsgs) { + errors = std::string((const char *)errorMsgs->GetBufferPointer(), errorMsgs->GetBufferSize()); + ELOG("%s: %s", SUCCEEDED(result) ? "warnings" : "errors", errors.c_str()); + OutputDebugStringA(LineNumberString(code).c_str()); + errorMsgs->Release(); + } + if (compiledCode) { + const uint8_t *buf = (const uint8_t *)compiledCode->GetBufferPointer(); + std::vector compiled = std::vector(buf, buf + compiledCode->GetBufferSize()); + compiledCode->Release(); + return compiled; + } + Crash(); + return std::vector(); +} + +ID3D11VertexShader *CreateVertexShaderD3D11(ID3D11Device *device, const char *code, size_t codeSize, std::vector *byteCodeOut) { + std::vector byteCode = CompileShaderToBytecode(code, codeSize, "vs_5_0"); + if (byteCode.empty()) + return nullptr; + ID3D11VertexShader *vs; + device->CreateVertexShader(byteCode.data(), byteCode.size(), nullptr, &vs); + if (byteCodeOut) + *byteCodeOut = byteCode; + return vs; +} + +ID3D11PixelShader *CreatePixelShaderD3D11(ID3D11Device *device, const char *code, size_t codeSize) { + std::vector byteCode = CompileShaderToBytecode(code, codeSize, "ps_5_0"); + if (byteCode.empty()) + return nullptr; + + ID3D11PixelShader *ps; + device->CreatePixelShader(byteCode.data(), byteCode.size(), nullptr, &ps); + return ps; +} + +void StockObjectsD3D11::Create(ID3D11Device *device) { + D3D11_BLEND_DESC blend_desc{}; + blend_desc.RenderTarget[0].BlendEnable = false; + blend_desc.IndependentBlendEnable = false; + for (int i = 0; i < 16; i++) { + blend_desc.RenderTarget[0].RenderTargetWriteMask = i; + device->CreateBlendState(&blend_desc, &blendStateDisabledWithColorMask[i]); + } + + D3D11_DEPTH_STENCIL_DESC depth_desc{}; + depth_desc.DepthEnable = FALSE; + device->CreateDepthStencilState(&depth_desc, &depthStencilDisabled); + depth_desc.StencilEnable = TRUE; + depth_desc.StencilReadMask = 0xFF; + depth_desc.StencilWriteMask = 0xFF; + depth_desc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_REPLACE; + depth_desc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_REPLACE; + depth_desc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_REPLACE; + depth_desc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS; + depth_desc.BackFace = depth_desc.FrontFace; + device->CreateDepthStencilState(&depth_desc, &depthDisabledStencilWrite); + + D3D11_RASTERIZER_DESC raster_desc{}; + raster_desc.FillMode = D3D11_FILL_SOLID; + raster_desc.CullMode = D3D11_CULL_NONE; + raster_desc.ScissorEnable = FALSE; + device->CreateRasterizerState(&raster_desc, &rasterStateNoCull); + + D3D11_SAMPLER_DESC sampler_desc{}; + sampler_desc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP; + sampler_desc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP; + sampler_desc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP; + sampler_desc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT; + device->CreateSamplerState(&sampler_desc, &samplerPoint2DWrap); + sampler_desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; + device->CreateSamplerState(&sampler_desc, &samplerLinear2DWrap); + sampler_desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP; + sampler_desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP; + sampler_desc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP; + sampler_desc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT; + device->CreateSamplerState(&sampler_desc, &samplerPoint2DClamp); + sampler_desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; + device->CreateSamplerState(&sampler_desc, &samplerLinear2DClamp); +} + +void StockObjectsD3D11::Destroy() { + for (int i = 0; i < 16; i++) { + blendStateDisabledWithColorMask[i]->Release(); + } + depthStencilDisabled->Release(); + depthDisabledStencilWrite->Release(); + rasterStateNoCull->Release(); + samplerPoint2DWrap->Release(); + samplerLinear2DWrap->Release(); +} + +StockObjectsD3D11 stockD3D11; \ No newline at end of file diff --git a/GPU/D3D11/D3D11Util.h b/GPU/D3D11/D3D11Util.h new file mode 100644 index 0000000000..4718756731 --- /dev/null +++ b/GPU/D3D11/D3D11Util.h @@ -0,0 +1,85 @@ +// Copyright (c) 2017- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program 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 General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +#include +#include + +class PushBufferD3D11 { +public: + PushBufferD3D11(ID3D11Device *device, size_t size, D3D11_BIND_FLAG bindFlags) { + D3D11_BUFFER_DESC desc{}; + desc.BindFlags = bindFlags; + desc.ByteWidth = (UINT)size; + desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + desc.Usage = D3D11_USAGE_DYNAMIC; + device->CreateBuffer(&desc, nullptr, &buffer_); + } + PushBufferD3D11(PushBufferD3D11 &) = delete; + ~PushBufferD3D11() { + buffer_->Release(); + } + ID3D11Buffer *Buf() const { + return buffer_; + } + + // Should be done each frame + void Reset() { + pos_ = 0; + nextMapDiscard_ = true; + } + + uint8_t *BeginPush(ID3D11DeviceContext *context, UINT *offset, size_t size, int align = 16) { + D3D11_MAPPED_SUBRESOURCE map; + pos_ = (pos_ + align - 1) & ~(align - 1); + context->Map(buffer_, 0, nextMapDiscard_ ? D3D11_MAP_WRITE_DISCARD : D3D11_MAP_WRITE_NO_OVERWRITE, 0, &map); + nextMapDiscard_ = false; + *offset = (UINT)pos_; + uint8_t *retval = (uint8_t *)map.pData + pos_; + pos_ += size; + return retval; + } + void EndPush(ID3D11DeviceContext *context) { + context->Unmap(buffer_, 0); + } + +private: + ID3D11Buffer *buffer_ = nullptr; + size_t pos_ = 0; + bool nextMapDiscard_ = false; +}; + +ID3D11VertexShader *CreateVertexShaderD3D11(ID3D11Device *device, const char *code, size_t codeSize, std::vector *byteCodeOut); +ID3D11PixelShader *CreatePixelShaderD3D11(ID3D11Device *device, const char *code, size_t codeSize); + +class StockObjectsD3D11 { +public: + void Create(ID3D11Device *device); + void Destroy(); + + ID3D11DepthStencilState *depthStencilDisabled; + ID3D11DepthStencilState *depthDisabledStencilWrite; + ID3D11BlendState *blendStateDisabledWithColorMask[16]; + ID3D11RasterizerState *rasterStateNoCull; + ID3D11SamplerState *samplerPoint2DWrap; + ID3D11SamplerState *samplerLinear2DWrap; + ID3D11SamplerState *samplerPoint2DClamp; + ID3D11SamplerState *samplerLinear2DClamp; +}; + +extern StockObjectsD3D11 stockD3D11; \ No newline at end of file diff --git a/GPU/D3D11/DepalettizeShaderD3D11.cpp b/GPU/D3D11/DepalettizeShaderD3D11.cpp new file mode 100644 index 0000000000..216dd7e05e --- /dev/null +++ b/GPU/D3D11/DepalettizeShaderD3D11.cpp @@ -0,0 +1,180 @@ +// Copyright (c) 2014- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program 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 General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include +#include + +#include "base/basictypes.h" +#include "base/logging.h" +#include "Common/Log.h" +#include "Core/Reporting.h" +#include "GPU/D3D11/TextureCacheD3D11.h" +#include "GPU/D3D11/DepalettizeShaderD3D11.h" +#include "GPU/D3D11/D3D11Util.h" +#include "GPU/Common/DepalettizeShaderCommon.h" + +static const int DEPAL_TEXTURE_OLD_AGE = 120; + +#ifdef _WIN32 +#define SHADERLOG +#endif + +static const char *depalVShaderHLSL = +"struct VS_IN {\n" +" float3 a_position : POSITION;\n" +" float2 a_texcoord0 : TEXCOORD0;\n" +"};\n" +"struct VS_OUT {\n" +" float2 Texcoord : TEXCOORD0;\n" +" float4 Position : SV_Position;\n" +"};\n" +"VS_OUT main(VS_IN input) {\n" +" VS_OUT output;\n" +" output.Texcoord = input.a_texcoord0;\n" +" output.Position = float4(input.a_position, 1.0);\n" +" return output;\n" +"}\n"; + +static const D3D11_INPUT_ELEMENT_DESC g_DepalVertexElements[] = { + { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, }, + { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, }, +}; + +DepalShaderCacheD3D11::DepalShaderCacheD3D11(ID3D11Device *device, ID3D11DeviceContext *context) + : device_(device), context_(context) { + std::string errorMessage; + std::vector vsByteCode; + vertexShader_ = CreateVertexShaderD3D11(device, depalVShaderHLSL, strlen(depalVShaderHLSL), &vsByteCode); + device_->CreateInputLayout(g_DepalVertexElements, ARRAY_SIZE(g_DepalVertexElements), vsByteCode.data(), vsByteCode.size(), &inputLayout_); + + D3D11_SAMPLER_DESC sampDesc{}; + sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP; + sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP; + sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP; + sampDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT; + device_->CreateSamplerState(&sampDesc, &clutSampler); +} + +DepalShaderCacheD3D11::~DepalShaderCacheD3D11() { + Clear(); + if (vertexShader_) { + vertexShader_->Release(); + } + clutSampler->Release(); +} + +u32 DepalShaderCacheD3D11::GenerateShaderID(GEPaletteFormat clutFormat, GEBufferFormat pixelFormat) { + return (clutFormat & 0xFFFFFF) | (pixelFormat << 24); +} + +ID3D11ShaderResourceView *DepalShaderCacheD3D11::GetClutTexture(GEPaletteFormat clutFormat, const u32 clutID, u32 *rawClut) { + const u32 realClutID = clutID ^ clutFormat; + + auto oldtex = texCache_.find(realClutID); + if (oldtex != texCache_.end()) { + oldtex->second->lastFrame = gpuStats.numFlips; + return oldtex->second->view; + } + + DXGI_FORMAT dstFmt = getClutDestFormatD3D11(clutFormat); + int texturePixels = clutFormat == GE_CMODE_32BIT_ABGR8888 ? 256 : 512; + + DepalTextureD3D11 *tex = new DepalTextureD3D11(); + // TODO: Look into 1D textures + + D3D11_TEXTURE2D_DESC desc{}; + desc.Width = texturePixels; + desc.Height = 1; + desc.ArraySize = 1; + desc.MipLevels = 1; + desc.Format = dstFmt; + desc.SampleDesc.Count = 1; + desc.Usage = D3D11_USAGE_IMMUTABLE; + desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + + D3D11_SUBRESOURCE_DATA data{}; + data.pSysMem = rawClut; + // Regardless of format, the CLUT should always be 1024 bytes. + data.SysMemPitch = 1024; + HRESULT hr = device_->CreateTexture2D(&desc, &data, &tex->texture); + if (FAILED(hr)) { + ERROR_LOG(G3D, "Failed to create D3D texture for depal"); + delete tex; + return nullptr; + } + hr = device_->CreateShaderResourceView(tex->texture, nullptr, &tex->view); + if (FAILED(hr)) { + // ... + } + tex->lastFrame = gpuStats.numFlips; + texCache_[realClutID] = tex; + return tex->view; +} + +void DepalShaderCacheD3D11::Clear() { + for (auto shader = cache_.begin(); shader != cache_.end(); ++shader) { + shader->second->pixelShader->Release(); + delete shader->second; + } + cache_.clear(); + + for (auto tex = texCache_.begin(); tex != texCache_.end(); ++tex) { + delete tex->second; + } + texCache_.clear(); +} + +void DepalShaderCacheD3D11::Decimate() { + for (auto tex = texCache_.begin(); tex != texCache_.end();) { + if (tex->second->lastFrame + DEPAL_TEXTURE_OLD_AGE < gpuStats.numFlips) { + delete tex->second; + texCache_.erase(tex++); + } else { + ++tex; + } + } +} + +ID3D11PixelShader *DepalShaderCacheD3D11::GetDepalettizePixelShader(GEPaletteFormat clutFormat, GEBufferFormat pixelFormat) { + u32 id = GenerateShaderID(clutFormat, pixelFormat); + + auto shader = cache_.find(id); + if (shader != cache_.end()) { + return shader->second->pixelShader; + } + + char *buffer = new char[2048]; + + GenerateDepalShader(buffer, pixelFormat, HLSL_D3D11); + + ID3D11PixelShader *pshader = CreatePixelShaderD3D11(device_, buffer, strlen(buffer)); + + if (!pshader) { + ERROR_LOG(G3D, "Failed to compile depal pixel shader"); + delete[] buffer; + return nullptr; + } + + DepalShaderD3D11 *depal = new DepalShaderD3D11(); + depal->pixelShader = pshader; + + cache_[id] = depal; + + delete[] buffer; + + return depal->pixelShader; +} \ No newline at end of file diff --git a/GPU/D3D11/DepalettizeShaderD3D11.h b/GPU/D3D11/DepalettizeShaderD3D11.h new file mode 100644 index 0000000000..c4889b1669 --- /dev/null +++ b/GPU/D3D11/DepalettizeShaderD3D11.h @@ -0,0 +1,69 @@ +// Copyright (c) 2017- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program 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 General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include +#include +#include +#include +#include "Common/CommonTypes.h" +#include "GPU/ge_constants.h" + +class DepalShaderD3D11 { +public: + ID3D11PixelShader *pixelShader; +}; + +class DepalTextureD3D11 { +public: + ~DepalTextureD3D11() { + if (texture) + texture->Release(); + if (view) + view->Release(); + } + ID3D11Texture2D *texture; + ID3D11ShaderResourceView *view; + int lastFrame; +}; + +// Caches both shaders and palette textures. +class DepalShaderCacheD3D11 { +public: + DepalShaderCacheD3D11(ID3D11Device *device, ID3D11DeviceContext *context); + ~DepalShaderCacheD3D11(); + + // This also uploads the palette and binds the correct texture. + ID3D11PixelShader *GetDepalettizePixelShader(GEPaletteFormat clutFormat, GEBufferFormat pixelFormat); + ID3D11VertexShader *GetDepalettizeVertexShader() { return vertexShader_; } + ID3D11InputLayout *GetInputLayout() { return inputLayout_; } + ID3D11ShaderResourceView *GetClutTexture(GEPaletteFormat clutFormat, const u32 clutHash, u32 *rawClut); + ID3D11SamplerState *GetClutSampler() { return clutSampler; } + void Clear(); + void Decimate(); + +private: + u32 GenerateShaderID(GEPaletteFormat clutFormat, GEBufferFormat pixelFormat); + + ID3D11Device *device_; + ID3D11DeviceContext *context_; + ID3D11VertexShader *vertexShader_ = nullptr; + ID3D11InputLayout *inputLayout_ = nullptr; + ID3D11SamplerState *clutSampler = nullptr; + + std::map cache_; + std::map texCache_; +}; diff --git a/GPU/D3D11/DrawEngineD3D11.cpp b/GPU/D3D11/DrawEngineD3D11.cpp new file mode 100644 index 0000000000..e16f67c3b0 --- /dev/null +++ b/GPU/D3D11/DrawEngineD3D11.cpp @@ -0,0 +1,961 @@ +// Copyright (c) 2012- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program 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 General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "base/logging.h" +#include "base/timeutil.h" + +#include "Common/MemoryUtil.h" +#include "Core/MemMap.h" +#include "Core/Host.h" +#include "Core/System.h" +#include "Core/Reporting.h" +#include "Core/Config.h" +#include "Core/CoreTiming.h" + +#include "GPU/Math3D.h" +#include "GPU/GPUState.h" +#include "GPU/ge_constants.h" + +#include "GPU/Common/TextureDecoder.h" +#include "GPU/Common/SplineCommon.h" +#include "GPU/Common/TransformCommon.h" +#include "GPU/Common/VertexDecoderCommon.h" +#include "GPU/Common/SoftwareTransformCommon.h" +#include "GPU/D3D11/TextureCacheD3D11.h" +#include "GPU/D3D11/DrawEngineD3D11.h" +#include "GPU/D3D11/ShaderManagerD3D11.h" +#include "GPU/D3D11/GPU_D3D11.h" + +const D3D11_PRIMITIVE_TOPOLOGY d3d11prim[8] = { + D3D11_PRIMITIVE_TOPOLOGY_POINTLIST, + D3D11_PRIMITIVE_TOPOLOGY_LINELIST, + D3D11_PRIMITIVE_TOPOLOGY_LINESTRIP, + D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST, + D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, + D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST, // Fans not supported + D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST, // Need expansion - though we could do it with geom shaders in most cases +}; + +enum { + TRANSFORMED_VERTEX_BUFFER_SIZE = VERTEX_BUFFER_MAX * sizeof(TransformedVertex) +}; + +#define VERTEXCACHE_DECIMATION_INTERVAL 17 + +enum { VAI_KILL_AGE = 120, VAI_UNRELIABLE_KILL_AGE = 240, VAI_UNRELIABLE_KILL_MAX = 4 }; +enum { + VERTEX_PUSH_SIZE = 1024 * 1024 * 16, + INDEX_PUSH_SIZE = 1024 * 1024 * 4, +}; + +static const D3D11_INPUT_ELEMENT_DESC TransformedVertexElements[] = { + { "POSITION", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, + { "TEXCOORD", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 16, D3D11_INPUT_PER_VERTEX_DATA, 0 }, + { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 28, D3D11_INPUT_PER_VERTEX_DATA, 0 }, + { "COLOR", 1, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 32, D3D11_INPUT_PER_VERTEX_DATA, 0 }, +}; + +DrawEngineD3D11::DrawEngineD3D11(Draw::DrawContext *draw, ID3D11Device *device, ID3D11DeviceContext *context) + : draw_(draw), + device_(device), + context_(context), + decodedVerts_(0), + prevPrim_(GE_PRIM_INVALID), + lastVType_(-1), + shaderManager_(0), + textureCache_(0), + framebufferManager_(0), + numDrawCalls(0), + vertexCountInDrawCalls(0), + decodeCounter_(0), + dcid_(0), + fboTexNeedBind_(false), + fboTexBound_(false) { + decOptions_.expandAllWeightsToFloat = true; + decOptions_.expand8BitNormalsToFloat = true; + + decimationCounter_ = VERTEXCACHE_DECIMATION_INTERVAL; + // Allocate nicely aligned memory. Maybe graphics drivers will + // appreciate it. + // All this is a LOT of memory, need to see if we can cut down somehow. + decoded = (u8 *)AllocateMemoryPages(DECODED_VERTEX_BUFFER_SIZE, MEM_PROT_READ | MEM_PROT_WRITE); + decIndex = (u16 *)AllocateMemoryPages(DECODED_INDEX_BUFFER_SIZE, MEM_PROT_READ | MEM_PROT_WRITE); + splineBuffer = (u8 *)AllocateMemoryPages(SPLINE_BUFFER_SIZE, MEM_PROT_READ | MEM_PROT_WRITE); + transformed = (TransformedVertex *)AllocateMemoryPages(TRANSFORMED_VERTEX_BUFFER_SIZE, MEM_PROT_READ | MEM_PROT_WRITE); + transformedExpanded = (TransformedVertex *)AllocateMemoryPages(3 * TRANSFORMED_VERTEX_BUFFER_SIZE, MEM_PROT_READ | MEM_PROT_WRITE); + + indexGen.Setup(decIndex); + + InitDeviceObjects(); + + tessDataTransfer = new TessellationDataTransferD3D11(); + + // Vertex pushing buffers. For uniforms we use short DISCARD buffers, but we could use + // this kind of buffer there as well with D3D11.1. We might be able to use the same buffer + // for both vertices and indices, and possibly all three data types. + pushVerts_ = new PushBufferD3D11(device, VERTEX_PUSH_SIZE, D3D11_BIND_VERTEX_BUFFER); + pushInds_ = new PushBufferD3D11(device, INDEX_PUSH_SIZE, D3D11_BIND_INDEX_BUFFER); +} + +DrawEngineD3D11::~DrawEngineD3D11() { + delete pushVerts_; + delete pushInds_; + + for (auto &decl : inputLayoutMap_) { + decl.second->Release(); + } + for (auto &depth : depthStencilCache_) { + depth.second->Release(); + } + for (auto &blend : blendCache_) { + blend.second->Release(); + } + + DestroyDeviceObjects(); + FreeMemoryPages(decoded, DECODED_VERTEX_BUFFER_SIZE); + FreeMemoryPages(decIndex, DECODED_INDEX_BUFFER_SIZE); + FreeMemoryPages(splineBuffer, SPLINE_BUFFER_SIZE); + FreeMemoryPages(transformed, TRANSFORMED_VERTEX_BUFFER_SIZE); + FreeMemoryPages(transformedExpanded, 3 * TRANSFORMED_VERTEX_BUFFER_SIZE); + + delete tessDataTransfer; +} + +void DrawEngineD3D11::InitDeviceObjects() { + +} + +void DrawEngineD3D11::DestroyDeviceObjects() { + ClearTrackedVertexArrays(); +} + +struct DeclTypeInfo { + DXGI_FORMAT type; + const char * name; +}; + +static const DeclTypeInfo VComp[] = { + { DXGI_FORMAT_UNKNOWN, "NULL" }, // DEC_NONE, + { DXGI_FORMAT_R32_FLOAT, "D3DDECLTYPE_FLOAT1 " }, // DEC_FLOAT_1, + { DXGI_FORMAT_R32G32_FLOAT, "D3DDECLTYPE_FLOAT2 " }, // DEC_FLOAT_2, + { DXGI_FORMAT_R32G32B32_FLOAT, "D3DDECLTYPE_FLOAT3 " }, // DEC_FLOAT_3, + { DXGI_FORMAT_R32G32B32A32_FLOAT, "D3DDECLTYPE_FLOAT4 " }, // DEC_FLOAT_4, + + { DXGI_FORMAT_R8G8B8A8_SNORM, "UNUSED" }, // DEC_S8_3, + + { DXGI_FORMAT_R16G16B16A16_SNORM, "D3DDECLTYPE_SHORT4N " }, // DEC_S16_3, + { DXGI_FORMAT_R8G8B8A8_UNORM, "D3DDECLTYPE_UBYTE4N " }, // DEC_U8_1, + { DXGI_FORMAT_R8G8B8A8_UNORM, "D3DDECLTYPE_UBYTE4N " }, // DEC_U8_2, + { DXGI_FORMAT_R8G8B8A8_UNORM, "D3DDECLTYPE_UBYTE4N " }, // DEC_U8_3, + { DXGI_FORMAT_R8G8B8A8_UNORM, "D3DDECLTYPE_UBYTE4N " }, // DEC_U8_4, + + { DXGI_FORMAT_UNKNOWN, "UNUSED_DEC_U16_1" }, // DEC_U16_1, + { DXGI_FORMAT_UNKNOWN, "UNUSED_DEC_U16_2" }, // DEC_U16_2, + { DXGI_FORMAT_R16G16B16A16_UNORM ,"D3DDECLTYPE_USHORT4N "}, // DEC_U16_3, + { DXGI_FORMAT_R16G16B16A16_UNORM ,"D3DDECLTYPE_USHORT4N "}, // DEC_U16_4, + { DXGI_FORMAT_UNKNOWN, "UNUSED_DEC_U8A_2"}, // DEC_U8A_2, + { DXGI_FORMAT_UNKNOWN, "UNUSED_DEC_U16A_2" }, // DEC_U16A_2, +}; + +static void VertexAttribSetup(D3D11_INPUT_ELEMENT_DESC * VertexElement, u8 fmt, u8 offset, const char *semantic, u8 semantic_index = 0) { + memset(VertexElement, 0, sizeof(D3D11_INPUT_ELEMENT_DESC)); + VertexElement->AlignedByteOffset = offset; + VertexElement->Format = VComp[fmt].type; + VertexElement->SemanticName = semantic; + VertexElement->SemanticIndex = semantic_index; +} + +ID3D11InputLayout *DrawEngineD3D11::SetupDecFmtForDraw(D3D11VertexShader *vshader, const DecVtxFormat &decFmt, u32 pspFmt) { + // TODO: Instead of one for each vshader, we can reduce it to one for each type of shader + // that reads TEXCOORD or not, etc. Not sure if worth it. + InputLayoutKey key{ pspFmt, vshader }; + auto vertexDeclCached = inputLayoutMap_.find(key); + if (vertexDeclCached == inputLayoutMap_.end()) { + D3D11_INPUT_ELEMENT_DESC VertexElements[8]; + D3D11_INPUT_ELEMENT_DESC *VertexElement = &VertexElements[0]; + + // Vertices Elements orders + // WEIGHT + if (decFmt.w0fmt != 0) { + VertexAttribSetup(VertexElement, decFmt.w0fmt, decFmt.w0off, "TEXCOORD", 1); + VertexElement++; + } + + if (decFmt.w1fmt != 0) { + VertexAttribSetup(VertexElement, decFmt.w1fmt, decFmt.w1off, "TEXCOORD", 2); + VertexElement++; + } + + // TC + if (decFmt.uvfmt != 0) { + VertexAttribSetup(VertexElement, decFmt.uvfmt, decFmt.uvoff, "TEXCOORD", 0); + VertexElement++; + } + + // COLOR + if (decFmt.c0fmt != 0) { + VertexAttribSetup(VertexElement, decFmt.c0fmt, decFmt.c0off, "COLOR", 0); + VertexElement++; + } + // Never used ? + if (decFmt.c1fmt != 0) { + VertexAttribSetup(VertexElement, decFmt.c1fmt, decFmt.c1off, "COLOR", 1); + VertexElement++; + } + + // NORMAL + if (decFmt.nrmfmt != 0) { + VertexAttribSetup(VertexElement, decFmt.nrmfmt, decFmt.nrmoff, "NORMAL", 0); + VertexElement++; + } + + // POSITION + // Always + VertexAttribSetup(VertexElement, decFmt.posfmt, decFmt.posoff, "POSITION", 0); + VertexElement++; + + // Create declaration + ID3D11InputLayout *inputLayout = nullptr; + HRESULT hr = device_->CreateInputLayout(VertexElements, VertexElement - VertexElements, vshader->bytecode().data(), vshader->bytecode().size(), &inputLayout); + if (FAILED(hr)) { + ERROR_LOG(G3D, "Failed to create input layout!"); + inputLayout = nullptr; + } + + // Add it to map + inputLayoutMap_[key] = inputLayout; + return inputLayout; + } else { + // Set it from map + return vertexDeclCached->second; + } +} + +void DrawEngineD3D11::SetupVertexDecoder(u32 vertType) { + SetupVertexDecoderInternal(vertType); +} + +inline void DrawEngineD3D11::SetupVertexDecoderInternal(u32 vertType) { + // As the decoder depends on the UVGenMode when we use UV prescale, we simply mash it + // into the top of the verttype where there are unused bits. + const u32 vertTypeID = (vertType & 0xFFFFFF) | (gstate.getUVGenMode() << 24); + + // If vtype has changed, setup the vertex decoder. + if (vertTypeID != lastVType_) { + dec_ = GetVertexDecoder(vertTypeID); + lastVType_ = vertTypeID; + } +} + +void DrawEngineD3D11::SubmitPrim(void *verts, void *inds, GEPrimitiveType prim, int vertexCount, u32 vertType, int *bytesRead) { + if (!indexGen.PrimCompatible(prevPrim_, prim) || numDrawCalls >= MAX_DEFERRED_DRAW_CALLS || vertexCountInDrawCalls + vertexCount > VERTEX_BUFFER_MAX) + Flush(); + + // TODO: Is this the right thing to do? + if (prim == GE_PRIM_KEEP_PREVIOUS) { + prim = prevPrim_ != GE_PRIM_INVALID ? prevPrim_ : GE_PRIM_POINTS; + } else { + prevPrim_ = prim; + } + + SetupVertexDecoderInternal(vertType); + + *bytesRead = vertexCount * dec_->VertexSize(); + + if ((vertexCount < 2 && prim > 0) || (vertexCount < 3 && prim > 2 && prim != GE_PRIM_RECTANGLES)) + return; + + DeferredDrawCall &dc = drawCalls[numDrawCalls]; + dc.verts = verts; + dc.inds = inds; + dc.vertType = vertType; + dc.indexType = (vertType & GE_VTYPE_IDX_MASK) >> GE_VTYPE_IDX_SHIFT; + dc.prim = prim; + dc.vertexCount = vertexCount; + + u32 dhash = dcid_; + dhash ^= (u32)(uintptr_t)verts; + dhash = __rotl(dhash, 13); + dhash ^= (u32)(uintptr_t)inds; + dhash = __rotl(dhash, 13); + dhash ^= (u32)vertType; + dhash = __rotl(dhash, 13); + dhash ^= (u32)vertexCount; + dhash = __rotl(dhash, 13); + dhash ^= (u32)prim; + dcid_ = dhash; + + if (inds) { + GetIndexBounds(inds, vertexCount, vertType, &dc.indexLowerBound, &dc.indexUpperBound); + } else { + dc.indexLowerBound = 0; + dc.indexUpperBound = vertexCount - 1; + } + + uvScale[numDrawCalls] = gstate_c.uv; + + numDrawCalls++; + vertexCountInDrawCalls += vertexCount; + + if (g_Config.bSoftwareSkinning && (vertType & GE_VTYPE_WEIGHT_MASK)) { + DecodeVertsStep(); + decodeCounter_++; + } + + if (prim == GE_PRIM_RECTANGLES && (gstate.getTextureAddress(0) & 0x3FFFFFFF) == (gstate.getFrameBufAddress() & 0x3FFFFFFF)) { + // Rendertarget == texture? + if (!g_Config.bDisableSlowFramebufEffects) { + gstate_c.Dirty(DIRTY_TEXTURE_PARAMS); + Flush(); + } + } +} + +void DrawEngineD3D11::DecodeVerts() { + const UVScale origUV = gstate_c.uv; + for (; decodeCounter_ < numDrawCalls; decodeCounter_++) { + gstate_c.uv = uvScale[decodeCounter_]; + DecodeVertsStep(); + } + gstate_c.uv = origUV; + + // Sanity check + if (indexGen.Prim() < 0) { + ERROR_LOG_REPORT(G3D, "DecodeVerts: Failed to deduce prim: %i", indexGen.Prim()); + // Force to points (0) + indexGen.AddPrim(GE_PRIM_POINTS, 0); + } +} + +void DrawEngineD3D11::DecodeVertsStep() { + const int i = decodeCounter_; + + const DeferredDrawCall &dc = drawCalls[i]; + + indexGen.SetIndex(decodedVerts_); + int indexLowerBound = dc.indexLowerBound, indexUpperBound = dc.indexUpperBound; + + u32 indexType = dc.indexType; + void *inds = dc.inds; + if (indexType == GE_VTYPE_IDX_NONE >> GE_VTYPE_IDX_SHIFT) { + // Decode the verts and apply morphing. Simple. + dec_->DecodeVerts(decoded + decodedVerts_ * (int)dec_->GetDecVtxFmt().stride, + dc.verts, indexLowerBound, indexUpperBound); + decodedVerts_ += indexUpperBound - indexLowerBound + 1; + indexGen.AddPrim(dc.prim, dc.vertexCount); + } else { + // It's fairly common that games issue long sequences of PRIM calls, with differing + // inds pointer but the same base vertex pointer. We'd like to reuse vertices between + // these as much as possible, so we make sure here to combine as many as possible + // into one nice big drawcall, sharing data. + + // 1. Look ahead to find the max index, only looking as "matching" drawcalls. + // Expand the lower and upper bounds as we go. + int lastMatch = i; + const int total = numDrawCalls; + for (int j = i + 1; j < total; ++j) { + if (drawCalls[j].verts != dc.verts) + break; + if (memcmp(&uvScale[j], &uvScale[i], sizeof(uvScale[0])) != 0) + break; + + indexLowerBound = std::min(indexLowerBound, (int)drawCalls[j].indexLowerBound); + indexUpperBound = std::max(indexUpperBound, (int)drawCalls[j].indexUpperBound); + lastMatch = j; + } + + // 2. Loop through the drawcalls, translating indices as we go. + switch (indexType) { + case GE_VTYPE_IDX_8BIT >> GE_VTYPE_IDX_SHIFT: + for (int j = i; j <= lastMatch; j++) { + indexGen.TranslatePrim(drawCalls[j].prim, drawCalls[j].vertexCount, (const u8 *)drawCalls[j].inds, indexLowerBound); + } + break; + case GE_VTYPE_IDX_16BIT >> GE_VTYPE_IDX_SHIFT: + for (int j = i; j <= lastMatch; j++) { + indexGen.TranslatePrim(drawCalls[j].prim, drawCalls[j].vertexCount, (const u16_le *)drawCalls[j].inds, indexLowerBound); + } + break; + case GE_VTYPE_IDX_32BIT >> GE_VTYPE_IDX_SHIFT: + for (int j = i; j <= lastMatch; j++) { + indexGen.TranslatePrim(drawCalls[j].prim, drawCalls[j].vertexCount, (const u32_le *)drawCalls[j].inds, indexLowerBound); + } + break; + } + + const int vertexCount = indexUpperBound - indexLowerBound + 1; + + // This check is a workaround for Pangya Fantasy Golf, which sends bogus index data when switching items in "My Room" sometimes. + if (decodedVerts_ + vertexCount > VERTEX_BUFFER_MAX) { + return; + } + + // 3. Decode that range of vertex data. + dec_->DecodeVerts(decoded + decodedVerts_ * (int)dec_->GetDecVtxFmt().stride, + dc.verts, indexLowerBound, indexUpperBound); + decodedVerts_ += vertexCount; + + // 4. Advance indexgen vertex counter. + indexGen.Advance(vertexCount); + decodeCounter_ = lastMatch; + } +} + +inline u32 ComputeMiniHashRange(const void *ptr, size_t sz) { + // Switch to u32 units. + const u32 *p = (const u32 *)ptr; + sz >>= 2; + + if (sz > 100) { + size_t step = sz / 4; + u32 hash = 0; + for (size_t i = 0; i < sz; i += step) { + hash += DoReliableHash32(p + i, 100, 0x3A44B9C4); + } + return hash; + } else { + return p[0] + p[sz - 1]; + } +} + +u32 DrawEngineD3D11::ComputeMiniHash() { + u32 fullhash = 0; + const int vertexSize = dec_->GetDecVtxFmt().stride; + const int indexSize = IndexSize(dec_->VertexType()); + + int step; + if (numDrawCalls < 3) { + step = 1; + } else if (numDrawCalls < 8) { + step = 4; + } else { + step = numDrawCalls / 8; + } + for (int i = 0; i < numDrawCalls; i += step) { + const DeferredDrawCall &dc = drawCalls[i]; + if (!dc.inds) { + fullhash += ComputeMiniHashRange(dc.verts, vertexSize * dc.vertexCount); + } else { + int indexLowerBound = dc.indexLowerBound, indexUpperBound = dc.indexUpperBound; + fullhash += ComputeMiniHashRange((const u8 *)dc.verts + vertexSize * indexLowerBound, vertexSize * (indexUpperBound - indexLowerBound)); + fullhash += ComputeMiniHashRange(dc.inds, indexSize * dc.vertexCount); + } + } + + return fullhash; +} + +void DrawEngineD3D11::MarkUnreliable(VertexArrayInfoD3D11 *vai) { + vai->status = VertexArrayInfoD3D11::VAI_UNRELIABLE; + if (vai->vbo) { + vai->vbo->Release(); + vai->vbo = nullptr; + } + if (vai->ebo) { + vai->ebo->Release(); + vai->ebo = nullptr; + } +} + +ReliableHashType DrawEngineD3D11::ComputeHash() { + ReliableHashType fullhash = 0; + const int vertexSize = dec_->GetDecVtxFmt().stride; + const int indexSize = IndexSize(dec_->VertexType()); + + // TODO: Add some caps both for numDrawCalls and num verts to check? + // It is really very expensive to check all the vertex data so often. + for (int i = 0; i < numDrawCalls; i++) { + const DeferredDrawCall &dc = drawCalls[i]; + if (!dc.inds) { + fullhash += DoReliableHash((const char *)dc.verts, vertexSize * dc.vertexCount, 0x1DE8CAC4); + } else { + int indexLowerBound = dc.indexLowerBound, indexUpperBound = dc.indexUpperBound; + int j = i + 1; + int lastMatch = i; + while (j < numDrawCalls) { + if (drawCalls[j].verts != dc.verts) + break; + indexLowerBound = std::min(indexLowerBound, (int)dc.indexLowerBound); + indexUpperBound = std::max(indexUpperBound, (int)dc.indexUpperBound); + lastMatch = j; + j++; + } + // This could get seriously expensive with sparse indices. Need to combine hashing ranges the same way + // we do when drawing. + fullhash += DoReliableHash((const char *)dc.verts + vertexSize * indexLowerBound, + vertexSize * (indexUpperBound - indexLowerBound), 0x029F3EE1); + // Hm, we will miss some indices when combining above, but meh, it should be fine. + fullhash += DoReliableHash((const char *)dc.inds, indexSize * dc.vertexCount, 0x955FD1CA); + i = lastMatch; + } + } + if (uvScale) { + fullhash += DoReliableHash(&uvScale[0], sizeof(uvScale[0]) * numDrawCalls, 0x0123e658); + } + + return fullhash; +} + +void DrawEngineD3D11::ClearTrackedVertexArrays() { + for (auto vai = vai_.begin(); vai != vai_.end(); vai++) { + delete vai->second; + } + vai_.clear(); +} + +void DrawEngineD3D11::BeginFrame() { + pushVerts_->Reset(); + pushInds_->Reset(); + + if (--decimationCounter_ <= 0) { + decimationCounter_ = VERTEXCACHE_DECIMATION_INTERVAL; + } else { + return; + } + + const int threshold = gpuStats.numFlips - VAI_KILL_AGE; + const int unreliableThreshold = gpuStats.numFlips - VAI_UNRELIABLE_KILL_AGE; + int unreliableLeft = VAI_UNRELIABLE_KILL_MAX; + for (auto iter = vai_.begin(); iter != vai_.end(); ) { + bool kill; + if (iter->second->status == VertexArrayInfoD3D11::VAI_UNRELIABLE) { + // We limit killing unreliable so we don't rehash too often. + kill = iter->second->lastFrame < unreliableThreshold && --unreliableLeft >= 0; + } else { + kill = iter->second->lastFrame < threshold; + } + if (kill) { + delete iter->second; + vai_.erase(iter++); + } else { + ++iter; + } + } + + // Enable if you want to see vertex decoders in the log output. Need a better way. +#if 0 + char buffer[16384]; + for (std::map::iterator dec = decoderMap_.begin(); dec != decoderMap_.end(); ++dec) { + char *ptr = buffer; + ptr += dec->second->ToString(ptr); + // *ptr++ = '\n'; + NOTICE_LOG(G3D, buffer); + } +#endif +} + +VertexArrayInfoD3D11::~VertexArrayInfoD3D11() { + if (vbo) + vbo->Release(); + if (ebo) + ebo->Release(); +} + +static uint32_t SwapRB(uint32_t c) { + return (c & 0xFF00FF00) | ((c >> 16) & 0xFF) | ((c << 16) & 0xFF0000); +} + +// The inline wrapper in the header checks for numDrawCalls == 0 +void DrawEngineD3D11::DoFlush() { + gpuStats.numFlushes++; + gpuStats.numTrackedVertexArrays = (int)vai_.size(); + + // This is not done on every drawcall, we should collect vertex data + // until critical state changes. That's when we draw (flush). + + GEPrimitiveType prim = prevPrim_; + ApplyDrawState(prim); + + bool useHWTransform = CanUseHardwareTransform(prim); + + if (useHWTransform) { + ID3D11Buffer *vb_ = nullptr; + ID3D11Buffer *ib_ = nullptr; + + int vertexCount = 0; + int maxIndex = 0; + bool useElements = true; + + // Cannot cache vertex data with morph enabled. + bool useCache = g_Config.bVertexCache && !(lastVType_ & GE_VTYPE_MORPHCOUNT_MASK); + // Also avoid caching when software skinning. + if (g_Config.bSoftwareSkinning && (lastVType_ & GE_VTYPE_WEIGHT_MASK)) + useCache = false; + + if (useCache) { + u32 id = dcid_ ^ gstate.getUVGenMode(); // This can have an effect on which UV decoder we need to use! And hence what the decoded data will look like. See #9263 + auto iter = vai_.find(id); + VertexArrayInfoD3D11 *vai; + if (iter != vai_.end()) { + // We've seen this before. Could have been a cached draw. + vai = iter->second; + } else { + vai = new VertexArrayInfoD3D11(); + vai_[id] = vai; + } + + switch (vai->status) { + case VertexArrayInfoD3D11::VAI_NEW: + { + // Haven't seen this one before. + ReliableHashType dataHash = ComputeHash(); + vai->hash = dataHash; + vai->minihash = ComputeMiniHash(); + vai->status = VertexArrayInfoD3D11::VAI_HASHING; + vai->drawsUntilNextFullHash = 0; + DecodeVerts(); // writes to indexGen + vai->numVerts = indexGen.VertexCount(); + vai->prim = indexGen.Prim(); + vai->maxIndex = indexGen.MaxIndex(); + vai->flags = gstate_c.vertexFullAlpha ? VAI11_FLAG_VERTEXFULLALPHA : 0; + goto rotateVBO; + } + + // Hashing - still gaining confidence about the buffer. + // But if we get this far it's likely to be worth creating a vertex buffer. + case VertexArrayInfoD3D11::VAI_HASHING: + { + vai->numDraws++; + if (vai->lastFrame != gpuStats.numFlips) { + vai->numFrames++; + } + if (vai->drawsUntilNextFullHash == 0) { + // Let's try to skip a full hash if mini would fail. + const u32 newMiniHash = ComputeMiniHash(); + ReliableHashType newHash = vai->hash; + if (newMiniHash == vai->minihash) { + newHash = ComputeHash(); + } + if (newMiniHash != vai->minihash || newHash != vai->hash) { + MarkUnreliable(vai); + DecodeVerts(); + goto rotateVBO; + } + if (vai->numVerts > 64) { + // exponential backoff up to 16 draws, then every 24 + vai->drawsUntilNextFullHash = std::min(24, vai->numFrames); + } else { + // Lower numbers seem much more likely to change. + vai->drawsUntilNextFullHash = 0; + } + // TODO: tweak + //if (vai->numFrames > 1000) { + // vai->status = VertexArrayInfo::VAI_RELIABLE; + //} + } else { + vai->drawsUntilNextFullHash--; + u32 newMiniHash = ComputeMiniHash(); + if (newMiniHash != vai->minihash) { + MarkUnreliable(vai); + DecodeVerts(); + goto rotateVBO; + } + } + + if (vai->vbo == 0) { + DecodeVerts(); + vai->numVerts = indexGen.VertexCount(); + vai->prim = indexGen.Prim(); + vai->maxIndex = indexGen.MaxIndex(); + vai->flags = gstate_c.vertexFullAlpha ? VAI11_FLAG_VERTEXFULLALPHA : 0; + useElements = !indexGen.SeenOnlyPurePrims() || prim == GE_PRIM_TRIANGLE_FAN; + if (!useElements && indexGen.PureCount()) { + vai->numVerts = indexGen.PureCount(); + } + + _dbg_assert_msg_(G3D, gstate_c.vertBounds.minV >= gstate_c.vertBounds.maxV, "Should not have checked UVs when caching."); + + // TODO: Combine these two into one buffer? + u32 size = dec_->GetDecVtxFmt().stride * indexGen.MaxIndex(); + D3D11_BUFFER_DESC desc{ size, D3D11_USAGE_IMMUTABLE, D3D11_BIND_VERTEX_BUFFER, 0 }; + D3D11_SUBRESOURCE_DATA data{ decoded }; + device_->CreateBuffer(&desc, &data, &vai->vbo); + if (useElements) { + u32 size = sizeof(short) * indexGen.VertexCount(); + D3D11_BUFFER_DESC desc{ size, D3D11_USAGE_IMMUTABLE, D3D11_BIND_INDEX_BUFFER, 0 }; + D3D11_SUBRESOURCE_DATA data{ decIndex }; + device_->CreateBuffer(&desc, &data, &vai->ebo); + } else { + vai->ebo = 0; + } + } else { + gpuStats.numCachedDrawCalls++; + useElements = vai->ebo ? true : false; + gpuStats.numCachedVertsDrawn += vai->numVerts; + gstate_c.vertexFullAlpha = vai->flags & VAI11_FLAG_VERTEXFULLALPHA; + } + vb_ = vai->vbo; + ib_ = vai->ebo; + vertexCount = vai->numVerts; + maxIndex = vai->maxIndex; + prim = static_cast(vai->prim); + break; + } + + // Reliable - we don't even bother hashing anymore. Right now we don't go here until after a very long time. + case VertexArrayInfoD3D11::VAI_RELIABLE: + { + vai->numDraws++; + if (vai->lastFrame != gpuStats.numFlips) { + vai->numFrames++; + } + gpuStats.numCachedDrawCalls++; + gpuStats.numCachedVertsDrawn += vai->numVerts; + vb_ = vai->vbo; + ib_ = vai->ebo; + + vertexCount = vai->numVerts; + + maxIndex = vai->maxIndex; + prim = static_cast(vai->prim); + + gstate_c.vertexFullAlpha = vai->flags & VAI11_FLAG_VERTEXFULLALPHA; + break; + } + + case VertexArrayInfoD3D11::VAI_UNRELIABLE: + { + vai->numDraws++; + if (vai->lastFrame != gpuStats.numFlips) { + vai->numFrames++; + } + DecodeVerts(); + goto rotateVBO; + } + } + + vai->lastFrame = gpuStats.numFlips; + } else { + DecodeVerts(); +rotateVBO: + gpuStats.numUncachedVertsDrawn += indexGen.VertexCount(); + useElements = !indexGen.SeenOnlyPurePrims() || prim == GE_PRIM_TRIANGLE_FAN; + vertexCount = indexGen.VertexCount(); + maxIndex = indexGen.MaxIndex(); + if (!useElements && indexGen.PureCount()) { + vertexCount = indexGen.PureCount(); + } + prim = indexGen.Prim(); + } + + VERBOSE_LOG(G3D, "Flush prim %i! %i verts in one go", prim, vertexCount); + bool hasColor = (lastVType_ & GE_VTYPE_COL_MASK) != GE_VTYPE_COL_NONE; + if (gstate.isModeThrough()) { + gstate_c.vertexFullAlpha = gstate_c.vertexFullAlpha && (hasColor || gstate.getMaterialAmbientA() == 255); + } else { + gstate_c.vertexFullAlpha = gstate_c.vertexFullAlpha && ((hasColor && (gstate.materialupdate & 1)) || gstate.getMaterialAmbientA() == 255) && (!gstate.isLightingEnabled() || gstate.getAmbientA() == 255); + } + + ApplyDrawStateLate(true, dynState_.stencilRef); + + D3D11VertexShader *vshader; + D3D11FragmentShader *fshader; + shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, useHWTransform); + ID3D11InputLayout *inputLayout = SetupDecFmtForDraw(vshader, dec_->GetDecVtxFmt(), dec_->VertexType()); + context_->PSSetShader(fshader->GetShader(), nullptr, 0); + context_->VSSetShader(vshader->GetShader(), nullptr, 0); + shaderManager_->UpdateUniforms(); + shaderManager_->BindUniforms(); + + context_->IASetInputLayout(inputLayout); + UINT stride = dec_->GetDecVtxFmt().stride; + context_->IASetPrimitiveTopology(d3d11prim[prim]); + if (!vb_) { + // Push! + UINT vOffset; + int vSize = (maxIndex + 1) * dec_->GetDecVtxFmt().stride; + uint8_t *vptr = pushVerts_->BeginPush(context_, &vOffset, vSize); + memcpy(vptr, decoded, vSize); + pushVerts_->EndPush(context_); + ID3D11Buffer *buf = pushVerts_->Buf(); + context_->IASetVertexBuffers(0, 1, &buf, &stride, &vOffset); + if (useElements) { + UINT iOffset; + int iSize = 2 * indexGen.VertexCount(); + uint8_t *iptr = pushInds_->BeginPush(context_, &iOffset, iSize); + memcpy(iptr, decIndex, iSize); + pushInds_->EndPush(context_); + context_->IASetIndexBuffer(pushInds_->Buf(), DXGI_FORMAT_R16_UINT, iOffset); + context_->DrawIndexed(vertexCount, 0, 0); + } else { + context_->Draw(vertexCount, 0); + } + } else { + UINT offset = 0; + context_->IASetVertexBuffers(0, 1, &vb_, &stride, &offset); + if (useElements) { + context_->IASetIndexBuffer(ib_, DXGI_FORMAT_R16_UINT, 0); + context_->DrawIndexed(vertexCount, 0, 0); + } else { + context_->Draw(vertexCount, 0); + } + } + } else { + DecodeVerts(); + bool hasColor = (lastVType_ & GE_VTYPE_COL_MASK) != GE_VTYPE_COL_NONE; + if (gstate.isModeThrough()) { + gstate_c.vertexFullAlpha = gstate_c.vertexFullAlpha && (hasColor || gstate.getMaterialAmbientA() == 255); + } else { + gstate_c.vertexFullAlpha = gstate_c.vertexFullAlpha && ((hasColor && (gstate.materialupdate & 1)) || gstate.getMaterialAmbientA() == 255) && (!gstate.isLightingEnabled() || gstate.getAmbientA() == 255); + } + + gpuStats.numUncachedVertsDrawn += indexGen.VertexCount(); + prim = indexGen.Prim(); + // Undo the strip optimization, not supported by the SW code yet. + if (prim == GE_PRIM_TRIANGLE_STRIP) + prim = GE_PRIM_TRIANGLES; + VERBOSE_LOG(G3D, "Flush prim %i SW! %i verts in one go", prim, indexGen.VertexCount()); + + int numTrans = 0; + bool drawIndexed = false; + u16 *inds = decIndex; + TransformedVertex *drawBuffer = NULL; + SoftwareTransformResult result; + memset(&result, 0, sizeof(result)); + + SoftwareTransformParams params; + memset(¶ms, 0, sizeof(params)); + params.decoded = decoded; + params.transformed = transformed; + params.transformedExpanded = transformedExpanded; + params.fbman = framebufferManager_; + params.texCache = textureCache_; + params.allowSeparateAlphaClear = false; // D3D11 doesn't support separate alpha clears + + int maxIndex = indexGen.MaxIndex(); + SoftwareTransform( + prim, indexGen.VertexCount(), + dec_->VertexType(), inds, GE_VTYPE_IDX_16BIT, dec_->GetDecVtxFmt(), + maxIndex, drawBuffer, numTrans, drawIndexed, ¶ms, &result); + + if (result.action == SW_DRAW_PRIMITIVES) { + const int vertexSize = sizeof(transformed[0]); + + ApplyDrawStateLate(result.setStencil, result.stencilValue); + + D3D11VertexShader *vshader; + D3D11FragmentShader *fshader; + shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, false); + context_->PSSetShader(fshader->GetShader(), nullptr, 0); + context_->VSSetShader(vshader->GetShader(), nullptr, 0); + shaderManager_->UpdateUniforms(); + shaderManager_->BindUniforms(); + + // We really do need a vertex layout for each vertex shader (or at least check its ID bits for what inputs it uses)! + // Some vertex shaders ignore one of the inputs, and then the layout created from it will lack it, which will be a problem for others. + InputLayoutKey key{ 0xFFFFFFFF, vshader }; // Let's use 0xFFFFFFFF to signify TransformedVertex + auto iter = inputLayoutMap_.find(key); + ID3D11InputLayout *layout; + if (iter == inputLayoutMap_.end()) { + device_->CreateInputLayout(TransformedVertexElements, ARRAY_SIZE(TransformedVertexElements), vshader->bytecode().data(), vshader->bytecode().size(), &layout); + inputLayoutMap_[key] = layout; + } else { + layout = iter->second; + } + context_->IASetInputLayout(layout); + context_->IASetPrimitiveTopology(d3d11prim[prim]); + + UINT stride = sizeof(TransformedVertex); + UINT vOffset = 0; + int vSize = maxIndex * stride; + uint8_t *vptr = pushVerts_->BeginPush(context_, &vOffset, vSize); + memcpy(vptr, drawBuffer, vSize); + pushVerts_->EndPush(context_); + ID3D11Buffer *buf = pushVerts_->Buf(); + context_->IASetVertexBuffers(0, 1, &buf, &stride, &vOffset); + if (drawIndexed) { + UINT iOffset; + int iSize = sizeof(uint16_t) * numTrans; + uint8_t *iptr = pushInds_->BeginPush(context_, &iOffset, iSize); + memcpy(iptr, inds, iSize); + pushInds_->EndPush(context_); + context_->IASetIndexBuffer(pushInds_->Buf(), DXGI_FORMAT_R16_UINT, iOffset); + context_->DrawIndexed(numTrans, 0, 0); + } else { + context_->Draw(numTrans, 0); + } + } else if (result.action == SW_CLEAR) { + u32 clearColor = result.color; + float clearDepth = result.depth; + + uint32_t clearFlag = 0; + + if (gstate.isClearModeColorMask()) clearFlag |= Draw::COLOR; + if (gstate.isClearModeAlphaMask()) clearFlag |= Draw::STENCIL; + if (gstate.isClearModeDepthMask()) clearFlag |= Draw::DEPTH; + + if (clearFlag & Draw::DEPTH) { + framebufferManager_->SetDepthUpdated(); + } + if (clearFlag & Draw::COLOR) { + framebufferManager_->SetColorUpdated(gstate_c.skipDrawReason); + } + + uint8_t clearStencil = clearColor >> 24; + draw_->Clear(clearFlag, clearColor, clearDepth, clearStencil); + + int scissorX2 = gstate.getScissorX2() + 1; + int scissorY2 = gstate.getScissorY2() + 1; + framebufferManager_->SetSafeSize(scissorX2, scissorY2); + if (g_Config.bBlockTransferGPU && (gstate_c.featureFlags & GPU_USE_CLEAR_RAM_HACK) && gstate.isClearModeColorMask() && (gstate.isClearModeAlphaMask() || gstate.FrameBufFormat() == GE_FORMAT_565)) { + int scissorX1 = gstate.getScissorX1(); + int scissorY1 = gstate.getScissorY1(); + ApplyClearToMemory(scissorX1, scissorY1, scissorX2, scissorY2, clearColor); + } + } + } + + gpuStats.numDrawCalls += numDrawCalls; + gpuStats.numVertsSubmitted += vertexCountInDrawCalls; + + indexGen.Reset(); + decodedVerts_ = 0; + numDrawCalls = 0; + vertexCountInDrawCalls = 0; + decodeCounter_ = 0; + dcid_ = 0; + prevPrim_ = GE_PRIM_INVALID; + gstate_c.vertexFullAlpha = true; + framebufferManager_->SetColorUpdated(gstate_c.skipDrawReason); + + // Now seems as good a time as any to reset the min/max coords, which we may examine later. + gstate_c.vertBounds.minU = 512; + gstate_c.vertBounds.minV = 512; + gstate_c.vertBounds.maxU = 0; + gstate_c.vertBounds.maxV = 0; + + host->GPUNotifyDraw(); +} + +void DrawEngineD3D11::Resized() { + decJitCache_->Clear(); + lastVType_ = -1; + dec_ = NULL; + for (auto iter = decoderMap_.begin(); iter != decoderMap_.end(); iter++) { + delete iter->second; + } + decoderMap_.clear(); +} + +bool DrawEngineD3D11::IsCodePtrVertexDecoder(const u8 *ptr) const { + return decJitCache_->IsInSpace(ptr); +} + +void DrawEngineD3D11::TessellationDataTransferD3D11::SendDataToShader(const float * pos, const float * tex, const float * col, int size, bool hasColor, bool hasTexCoords) +{ + // HW tessellation not yet supported +} diff --git a/GPU/D3D11/DrawEngineD3D11.h b/GPU/D3D11/DrawEngineD3D11.h new file mode 100644 index 0000000000..3ce10475d9 --- /dev/null +++ b/GPU/D3D11/DrawEngineD3D11.h @@ -0,0 +1,263 @@ +// Copyright (c) 2012- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program 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 General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +#include + +#include + +#include "GPU/GPUState.h" +#include "GPU/Common/GPUDebugInterface.h" +#include "GPU/Common/IndexGenerator.h" +#include "GPU/Common/VertexDecoderCommon.h" +#include "GPU/Common/DrawEngineCommon.h" +#include "GPU/Common/GPUStateUtils.h" +#include "GPU/D3D11/FragmentShaderGeneratorD3D11.h" +#include "GPU/D3D11/StateMappingD3D11.h" +#include "GPU/D3D11/D3D11Util.h" + +struct DecVtxFormat; +struct UVScale; + +class D3D11VertexShader; +class ShaderManagerD3D11; +class TextureCacheD3D11; +class FramebufferManagerD3D11; + +// States transitions: +// On creation: DRAWN_NEW +// DRAWN_NEW -> DRAWN_HASHING +// DRAWN_HASHING -> DRAWN_RELIABLE +// DRAWN_HASHING -> DRAWN_UNRELIABLE +// DRAWN_ONCE -> UNRELIABLE +// DRAWN_RELIABLE -> DRAWN_SAFE +// UNRELIABLE -> death +// DRAWN_ONCE -> death +// DRAWN_RELIABLE -> death + +enum { + VAI11_FLAG_VERTEXFULLALPHA = 1, +}; + +// Avoiding the full include of TextureDecoder.h. +#if (defined(_M_SSE) && defined(_M_X64)) || defined(ARM64) +typedef u64 ReliableHashType; +#else +typedef u32 ReliableHashType; +#endif + +// Try to keep this POD. +class VertexArrayInfoD3D11 { +public: + VertexArrayInfoD3D11() { + status = VAI_NEW; + vbo = 0; + ebo = 0; + prim = GE_PRIM_INVALID; + numDraws = 0; + numFrames = 0; + lastFrame = gpuStats.numFlips; + numVerts = 0; + drawsUntilNextFullHash = 0; + flags = 0; + } + ~VertexArrayInfoD3D11(); + + enum Status { + VAI_NEW, + VAI_HASHING, + VAI_RELIABLE, // cache, don't hash + VAI_UNRELIABLE, // never cache + }; + + ReliableHashType hash; + u32 minihash; + + Status status; + + ID3D11Buffer *vbo; + ID3D11Buffer *ebo; + + // Precalculated parameter for drawRangeElements + u16 numVerts; + u16 maxIndex; + s8 prim; + + // ID information + int numDraws; + int numFrames; + int lastFrame; // So that we can forget. + u16 drawsUntilNextFullHash; + u8 flags; +}; + +// Handles transform, lighting and drawing. +class DrawEngineD3D11 : public DrawEngineCommon { +public: + DrawEngineD3D11(Draw::DrawContext *draw, ID3D11Device *device, ID3D11DeviceContext *context); + virtual ~DrawEngineD3D11(); + + void SubmitPrim(void *verts, void *inds, GEPrimitiveType prim, int vertexCount, u32 vertType, int *bytesRead); + + void SetShaderManager(ShaderManagerD3D11 *shaderManager) { + shaderManager_ = shaderManager; + } + void SetTextureCache(TextureCacheD3D11 *textureCache) { + textureCache_ = textureCache; + } + void SetFramebufferManager(FramebufferManagerD3D11 *fbManager) { + framebufferManager_ = fbManager; + } + void InitDeviceObjects(); + void DestroyDeviceObjects(); + void GLLost() {}; + + void Resized(); // TODO: Call + + void BeginFrame(); + void ClearTrackedVertexArrays(); + + void SetupVertexDecoder(u32 vertType); + void SetupVertexDecoderInternal(u32 vertType); + + // So that this can be inlined + void Flush() { + if (!numDrawCalls) + return; + DoFlush(); + } + + void FinishDeferred() { + if (!numDrawCalls) + return; + DecodeVerts(); + } + + bool IsCodePtrVertexDecoder(const u8 *ptr) const; + + void DispatchFlush() override { Flush(); } + void DispatchSubmitPrim(void *verts, void *inds, GEPrimitiveType prim, int vertexCount, u32 vertType, int *bytesRead) override { + SubmitPrim(verts, inds, prim, vertexCount, vertType, bytesRead); + } + +private: + void DecodeVerts(); + void DecodeVertsStep(); + void DoFlush(); + + void ApplyDrawState(int prim); + void ApplyDrawStateLate(bool applyStencilRef, uint8_t stencilRef); + + ID3D11InputLayout *SetupDecFmtForDraw(D3D11VertexShader *vshader, const DecVtxFormat &decFmt, u32 pspFmt); + + u32 ComputeMiniHash(); + ReliableHashType ComputeHash(); // Reads deferred vertex data. + void MarkUnreliable(VertexArrayInfoD3D11 *vai); + + Draw::DrawContext *draw_; // Used for framebuffer related things exclusively. + ID3D11Device *device_; + ID3D11DeviceContext *context_; + + // Defer all vertex decoding to a Flush, so that we can hash and cache the + // generated buffers without having to redecode them every time. + struct DeferredDrawCall { + void *verts; + void *inds; + u32 vertType; + u8 indexType; + s8 prim; + u32 vertexCount; + u16 indexLowerBound; + u16 indexUpperBound; + }; + + // Vertex collector state + IndexGenerator indexGen; + int decodedVerts_; + GEPrimitiveType prevPrim_; + + u32 lastVType_; + + TransformedVertex *transformed; + TransformedVertex *transformedExpanded; + + std::unordered_map vai_; + + struct InputLayoutKey { + u32 vertType; + D3D11VertexShader *vshader; + bool operator <(const InputLayoutKey &other) const { + if (vertType < other.vertType) + return true; + if (vertType > other.vertType) + return false; + return vshader < other.vshader; + } + }; + + std::map inputLayoutMap_; + + // Other + ShaderManagerD3D11 *shaderManager_; + TextureCacheD3D11 *textureCache_; + FramebufferManagerD3D11 *framebufferManager_; + + // Pushbuffers + PushBufferD3D11 *pushVerts_; + PushBufferD3D11 *pushInds_; + + enum { MAX_DEFERRED_DRAW_CALLS = 128 }; + + DeferredDrawCall drawCalls[MAX_DEFERRED_DRAW_CALLS]; + int numDrawCalls; + int vertexCountInDrawCalls; + + int decimationCounter_; + int decodeCounter_; + u32 dcid_; + + UVScale uvScale[MAX_DEFERRED_DRAW_CALLS]; + + // D3D11 state object caches + std::map blendCache_; + std::map depthStencilCache_; + std::map rasterCache_; + + // Keep the depth state between ApplyDrawState and ApplyDrawStateLate + ID3D11DepthStencilState *depthStencilState_; + + // State keys + D3D11StateKeys keys_{}; + D3D11DynamicState dynState_{}; + + // Initial work on shader blending + bool fboTexNeedBind_; + bool fboTexBound_; + + // Hardware tessellation + class TessellationDataTransferD3D11 : public TessellationDataTransfer { + private: + int data_tex[3]; + public: + TessellationDataTransferD3D11() : TessellationDataTransfer(), data_tex() { + } + ~TessellationDataTransferD3D11() { + } + void SendDataToShader(const float *pos, const float *tex, const float *col, int size, bool hasColor, bool hasTexCoords) override; + }; +}; diff --git a/GPU/D3D11/FragmentShaderGeneratorD3D11.cpp b/GPU/D3D11/FragmentShaderGeneratorD3D11.cpp new file mode 100644 index 0000000000..49340df4b1 --- /dev/null +++ b/GPU/D3D11/FragmentShaderGeneratorD3D11.cpp @@ -0,0 +1,24 @@ +// Copyright (c) 2017- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program 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 General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "GPU/Common/ShaderCommon.h" +#include "GPU/D3D11/FragmentShaderGeneratorD3D11.h" +#include "GPU/Directx9/PixelShaderGeneratorDX9.h" + +void GenerateFragmentShaderD3D11(const ShaderID &id, char *buffer) { + DX9::GenerateFragmentShaderHLSL(id, buffer, HLSL_D3D11); +} diff --git a/GPU/D3D11/FragmentShaderGeneratorD3D11.h b/GPU/D3D11/FragmentShaderGeneratorD3D11.h new file mode 100644 index 0000000000..c7d49c0c9d --- /dev/null +++ b/GPU/D3D11/FragmentShaderGeneratorD3D11.h @@ -0,0 +1,22 @@ +// Copyright (c) 2017- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program 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 General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +#include "GPU/Common/ShaderId.h" + +void GenerateFragmentShaderD3D11(const ShaderID &id, char *buffer); diff --git a/GPU/D3D11/FramebufferManagerD3D11.cpp b/GPU/D3D11/FramebufferManagerD3D11.cpp new file mode 100644 index 0000000000..a03983f209 --- /dev/null +++ b/GPU/D3D11/FramebufferManagerD3D11.cpp @@ -0,0 +1,1012 @@ +// Copyright (c) 2017- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program 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 General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "math/lin/matrix4x4.h" +#include "ext/native/thin3d/thin3d.h" +#include "base/basictypes.h" + +#include "Common/ColorConv.h" +#include "Core/Host.h" +#include "Core/MemMap.h" +#include "Core/Config.h" +#include "Core/System.h" +#include "Core/Reporting.h" +#include "GPU/ge_constants.h" +#include "GPU/GPUState.h" +#include "GPU/Debugger/Stepping.h" + +#include "GPU/Common/FramebufferCommon.h" +#include "GPU/Common/TextureDecoder.h" +#include "GPU/D3D11/FramebufferManagerD3D11.h" +#include "GPU/D3D11/ShaderManagerD3D11.h" +#include "GPU/D3D11/TextureCacheD3D11.h" +#include "GPU/D3D11/DrawEngineD3D11.h" + +#include "ext/native/thin3d/thin3d.h" + +#include + +#ifdef _M_SSE +#include +#endif + +static const char *vscode = + "struct VS_IN {\n" + " float4 ObjPos : POSITION;\n" + " float2 Uv : TEXCOORD0;\n" + "};" + "struct VS_OUT {\n" + " float2 Uv : TEXCOORD0;\n" + " float4 ProjPos : SV_Position;\n" + "};\n" + "VS_OUT main(VS_IN In) {\n" + " VS_OUT Out;\n" + " Out.ProjPos = In.ObjPos;\n" + " Out.Uv = In.Uv;\n" + " return Out;\n" + "}\n"; + +static const char *pscode = + "SamplerState samp : register(s0);\n" + "Texture2D tex : register(t0);\n" + "struct PS_IN {\n" + " float2 Uv : TEXCOORD0;\n" + "};\n" + "float4 main( PS_IN In ) : SV_Target {\n" + " float4 c = tex.Sample(samp, In.Uv);\n" + " return c;\n" + "}\n"; + +const D3D11_INPUT_ELEMENT_DESC FramebufferManagerD3D11::g_QuadVertexElements[2] = { + { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, }, + { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, }, +}; + +FramebufferManagerD3D11::FramebufferManagerD3D11(Draw::DrawContext *draw) + : FramebufferManagerCommon(draw) { + device_ = (ID3D11Device *)draw->GetNativeObject(Draw::NativeObject::DEVICE); + context_ = (ID3D11DeviceContext *)draw->GetNativeObject(Draw::NativeObject::CONTEXT); + + std::vector bytecode; + + std::string errorMsg; + quadVertexShader_ = CreateVertexShaderD3D11(device_, vscode, strlen(vscode), &bytecode); + quadPixelShader_ = CreatePixelShaderD3D11(device_, pscode, strlen(pscode)); + device_->CreateInputLayout(g_QuadVertexElements, ARRAY_SIZE(g_QuadVertexElements), bytecode.data(), bytecode.size(), &quadInputLayout_); + + // STRIP geometry + static const float fsCoord[20] = { + -1.0f,-1.0f, 0.0f, 0.0f, 0.0f, + 1.0f,-1.0f, 0.0f, 1.0f, 0.0f, + -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, + 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, + }; + D3D11_BUFFER_DESC vb{}; + vb.ByteWidth = 20 * 4; + vb.Usage = D3D11_USAGE_IMMUTABLE; + vb.CPUAccessFlags = 0; + vb.BindFlags = D3D11_BIND_VERTEX_BUFFER; + D3D11_SUBRESOURCE_DATA data{ fsCoord }; + device_->CreateBuffer(&vb, &data, &fsQuadBuffer_); + vb.Usage = D3D11_USAGE_DYNAMIC; + vb.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + device_->CreateBuffer(&vb, nullptr, &quadBuffer_); +} + +FramebufferManagerD3D11::~FramebufferManagerD3D11() { + // Drawing cleanup + if (quadVertexShader_) + quadVertexShader_->Release(); + if (quadPixelShader_) + quadPixelShader_->Release(); + quadInputLayout_->Release(); + quadBuffer_->Release(); + fsQuadBuffer_->Release(); + + if (drawPixelsTex_) + drawPixelsTex_->Release(); + if (drawPixelsTexView_) + drawPixelsTexView_->Release(); + + // FBO cleanup + for (auto it = tempFBOs_.begin(), end = tempFBOs_.end(); it != end; ++it) { + delete it->second.fbo; + } + delete[] convBuf; + + // Stencil cleanup + for (int i = 0; i < 256; i++) { + if (stencilMaskStates_[i]) + stencilMaskStates_[i]->Release(); + } + if (stencilUploadPS_) + stencilUploadPS_->Release(); + if (stencilUploadVS_) + stencilUploadVS_->Release(); + if (stencilUploadInputLayout_) + stencilUploadInputLayout_->Release(); + if (stencilValueBuffer_) + stencilValueBuffer_->Release(); +} + +void FramebufferManagerD3D11::SetTextureCache(TextureCacheD3D11 *tc) { + textureCacheD3D11_ = tc; + textureCache_ = tc; +} + +void FramebufferManagerD3D11::ClearBuffer(bool keepState) { + draw_->Clear(Draw::ClearFlag::COLOR | Draw::ClearFlag::DEPTH | Draw::ClearFlag::STENCIL, 0, ToScaledDepth(0), 0); +} + +void FramebufferManagerD3D11::DisableState() { + context_->OMSetBlendState(stockD3D11.blendStateDisabledWithColorMask[0xF], nullptr, 0xFFFFFFFF); + context_->RSSetState(stockD3D11.rasterStateNoCull); + context_->OMSetDepthStencilState(stockD3D11.depthStencilDisabled, 0xFF); +} + +void FramebufferManagerD3D11::MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height) { + u8 *convBuf = NULL; + + // TODO: Check / use D3DCAPS2_DYNAMICTEXTURES? + if (drawPixelsTex_ && (drawPixelsTexW_ != width || drawPixelsTexH_ != height)) { + drawPixelsTex_->Release(); + drawPixelsTex_ = nullptr; + } + + if (!drawPixelsTex_) { + int usage = 0; + D3D11_TEXTURE2D_DESC desc{}; + desc.Usage = D3D11_USAGE_DYNAMIC; + desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + desc.Width = width; + desc.Height = height; + desc.MipLevels = 1; + desc.ArraySize = 1; + desc.SampleDesc.Count = 1; + desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; + desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + device_->CreateTexture2D(&desc, nullptr, &drawPixelsTex_); + device_->CreateShaderResourceView(drawPixelsTex_, nullptr, &drawPixelsTexView_); + drawPixelsTexW_ = width; + drawPixelsTexH_ = height; + } + + if (!drawPixelsTex_) { + return; + } + + D3D11_MAPPED_SUBRESOURCE map; + context_->Map(drawPixelsTex_, 0, D3D11_MAP_WRITE_DISCARD, 0, &map); + + convBuf = (u8*)map.pData; + + if (srcPixelFormat != GE_FORMAT_8888 || srcStride != 512) { + for (int y = 0; y < height; y++) { + switch (srcPixelFormat) { + case GE_FORMAT_565: + { + const u16_le *src = (const u16_le *)srcPixels + srcStride * y; + u32 *dst = (u32 *)(convBuf + map.RowPitch * y); + ConvertRGB565ToBGRA8888(dst, src, width); + } + break; + // faster + case GE_FORMAT_5551: + { + const u16_le *src = (const u16_le *)srcPixels + srcStride * y; + u32 *dst = (u32 *)(convBuf + map.RowPitch * y); + ConvertRGBA5551ToBGRA8888(dst, src, width); + } + break; + case GE_FORMAT_4444: + { + const u16_le *src = (const u16_le *)srcPixels + srcStride * y; + u8 *dst = (u8 *)(convBuf + map.RowPitch * y); + ConvertRGBA4444ToBGRA8888((u32 *)dst, src, width); + } + break; + + case GE_FORMAT_8888: + { + const u32_le *src = (const u32_le *)srcPixels + srcStride * y; + u32 *dst = (u32 *)(convBuf + map.RowPitch * y); + ConvertRGBA8888ToBGRA8888(dst, src, width); + } + break; + } + } + } else { + for (int y = 0; y < height; y++) { + const u32_le *src = (const u32_le *)srcPixels + srcStride * y; + u32 *dst = (u32 *)(convBuf + map.RowPitch * y); + ConvertRGBA8888ToBGRA8888(dst, src, width); + } + } + + context_->Unmap(drawPixelsTex_, 0); + // D3DXSaveTextureToFile("game:\\cc.png", D3DXIFF_PNG, drawPixelsTex_, NULL); +} + +void FramebufferManagerD3D11::DrawPixels(VirtualFramebuffer *vfb, int dstX, int dstY, const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height) { + if (useBufferedRendering_ && vfb && vfb->fbo) { + draw_->BindFramebufferAsRenderTarget(vfb->fbo); + D3D11_VIEWPORT vp{ 0.0f, 0.0f, (float)vfb->renderWidth, (float)vfb->renderHeight, 0.0f, 1.0f }; + context_->RSSetViewports(1, &vp); + } else { + float x, y, w, h; + CenterDisplayOutputRect(&x, &y, &w, &h, 480.0f, 272.0f, (float)pixelWidth_, (float)pixelHeight_, ROTATION_LOCKED_HORIZONTAL); + D3D11_VIEWPORT vp{ x, y, w, h, 0.0f, 1.0f }; + context_->RSSetViewports(1, &vp); + } + MakePixelTexture(srcPixels, srcPixelFormat, srcStride, width, height); + DisableState(); + context_->PSSetShaderResources(0, 1, &drawPixelsTexView_); + DrawActiveTexture(dstX, dstY, width, height, vfb->bufferWidth, vfb->bufferHeight, 0.0f, 0.0f, 1.0f, 1.0f, ROTATION_LOCKED_HORIZONTAL, true); + textureCacheD3D11_->ForgetLastTexture(); + // context_->RSSetViewports(1, &vp); +} + +void FramebufferManagerD3D11::DrawFramebufferToOutput(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, bool applyPostShader) { + MakePixelTexture(srcPixels, srcPixelFormat, srcStride, 512, 272); + DisableState(); + + // This might draw directly at the backbuffer (if so, applyPostShader is set) so if there's a post shader, we need to apply it here. + // Should try to unify this path with the regular path somehow, but this simple solution works for most of the post shaders + // (it always runs at output resolution so FXAA may look odd). + float x, y, w, h; + int uvRotation = (g_Config.iRenderingMode != FB_NON_BUFFERED_MODE) ? g_Config.iInternalScreenRotation : ROTATION_LOCKED_HORIZONTAL; + CenterDisplayOutputRect(&x, &y, &w, &h, 480.0f, 272.0f, (float)pixelWidth_, (float)pixelHeight_, uvRotation); + context_->PSSetShaderResources(0, 1, &drawPixelsTexView_); + DrawActiveTexture(x, y, w, h, (float)pixelWidth_, (float)pixelHeight_, 0.0f, 0.0f, 480.0f / 512.0f, 1.0f, uvRotation, g_Config.iBufFilter == SCALE_LINEAR); +} + +void FramebufferManagerD3D11::DrawActiveTexture(float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, int uvRotation, bool linearFilter) { + float coord[20] = { + x,y,0, u0,v0, + x + w,y,0, u1,v0, + x + w,y + h,0, u1,v1, + x,y + h,0, u0,v1, + }; + + static const short indices[4] = { 0, 1, 3, 2 }; + + if (uvRotation != ROTATION_LOCKED_HORIZONTAL) { + float temp[8]; + int rotation = 0; + switch (uvRotation) { + case ROTATION_LOCKED_HORIZONTAL180: rotation = 2; break; + case ROTATION_LOCKED_VERTICAL: rotation = 1; break; + case ROTATION_LOCKED_VERTICAL180: rotation = 3; break; + } + + for (int i = 0; i < 4; i++) { + temp[i * 2] = coord[((i + rotation) & 3) * 5 + 3]; + temp[i * 2 + 1] = coord[((i + rotation) & 3) * 5 + 4]; + } + + for (int i = 0; i < 4; i++) { + coord[i * 5 + 3] = temp[i * 2]; + coord[i * 5 + 4] = temp[i * 2 + 1]; + } + } + + float invDestW = 1.0f / (destW * 0.5f); + float invDestH = 1.0f / (destH * 0.5f); + float halfPixelX = invDestW * 0.5f; + float halfPixelY = invDestH * 0.5f; + for (int i = 0; i < 4; i++) { + coord[i * 5] = coord[i * 5] * invDestW - 1.0f - halfPixelX; + coord[i * 5 + 1] = -(coord[i * 5 + 1] * invDestH - 1.0f - halfPixelY); + } + + // The above code is for FAN geometry but we can only do STRIP. So rearrange it a little. + D3D11_MAPPED_SUBRESOURCE map; + context_->Map(quadBuffer_, 0, D3D11_MAP_WRITE_DISCARD, 0, &map); + float *dest = (float *)map.pData; + memcpy(dest, coord, sizeof(float) * 5); + memcpy(dest + 5, coord + 5, sizeof(float) * 5); + memcpy(dest + 10, coord + 15, sizeof(float) * 5); + memcpy(dest + 15, coord + 10, sizeof(float) * 5); + context_->Unmap(quadBuffer_, 0); + + context_->RSSetState(stockD3D11.rasterStateNoCull); + context_->OMSetBlendState(stockD3D11.blendStateDisabledWithColorMask[0xF], nullptr, 0xFFFFFFFF); + context_->OMSetDepthStencilState(stockD3D11.depthStencilDisabled, 0); + context_->IASetInputLayout(quadInputLayout_); + context_->PSSetShader(quadPixelShader_, 0, 0); + context_->VSSetShader(quadVertexShader_, 0, 0); + context_->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + context_->PSSetSamplers(0, 1, linearFilter ? &stockD3D11.samplerLinear2DClamp : &stockD3D11.samplerPoint2DClamp); + UINT stride = 20; + UINT offset = 0; + context_->IASetVertexBuffers(0, 1, &quadBuffer_, &stride, &offset); + context_->Draw(4, 0); + + shaderManager_->DirtyLastShader(); +} + +void FramebufferManagerD3D11::RebindFramebuffer() { + if (currentRenderVfb_ && currentRenderVfb_->fbo) { + draw_->BindFramebufferAsRenderTarget(currentRenderVfb_->fbo); + } else { + draw_->BindBackbufferAsRenderTarget(); + } +} + +void FramebufferManagerD3D11::ReformatFramebufferFrom(VirtualFramebuffer *vfb, GEBufferFormat old) { + if (!useBufferedRendering_ || !vfb->fbo) { + return; + } + + draw_->BindFramebufferAsRenderTarget(vfb->fbo); + + // Technically, we should at this point re-interpret the bytes of the old format to the new. + // That might get tricky, and could cause unnecessary slowness in some games. + // For now, we just clear alpha/stencil from 565, which fixes shadow issues in Kingdom Hearts. + // (it uses 565 to write zeros to the buffer, than 4444 to actually render the shadow.) + // + // The best way to do this may ultimately be to create a new FBO (combine with any resize?) + // and blit with a shader to that, then replace the FBO on vfb. Stencil would still be complex + // to exactly reproduce in 4444 and 8888 formats. + + if (old == GE_FORMAT_565) { + context_->OMSetDepthStencilState(stockD3D11.depthDisabledStencilWrite, 0xFF); + context_->OMSetBlendState(stockD3D11.blendStateDisabledWithColorMask[0], nullptr, 0xFFFFFFFF); + context_->RSSetState(stockD3D11.rasterStateNoCull); + context_->IASetInputLayout(quadInputLayout_); + context_->PSSetShader(quadPixelShader_, nullptr, 0); + context_->VSSetShader(quadVertexShader_, nullptr, 0); + context_->IASetVertexBuffers(0, 1, &fsQuadBuffer_, &quadStride_, &quadOffset_); + shaderManager_->DirtyLastShader(); + D3D11_VIEWPORT vp{ 0.0f, 0.0f, (float)vfb->renderWidth, (float)vfb->renderHeight, 0.0f, 1.0f }; + context_->RSSetViewports(1, &vp); + context_->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + context_->Draw(2, 0); + } + + RebindFramebuffer(); +} + +static void CopyPixelDepthOnly(u32 *dstp, const u32 *srcp, size_t c) { + size_t x = 0; + +#ifdef _M_SSE + size_t sseSize = (c / 4) * 4; + const __m128i srcMask = _mm_set1_epi32(0x00FFFFFF); + const __m128i dstMask = _mm_set1_epi32(0xFF000000); + __m128i *dst = (__m128i *)dstp; + const __m128i *src = (const __m128i *)srcp; + + for (; x < sseSize; x += 4) { + const __m128i bits24 = _mm_and_si128(_mm_load_si128(src), srcMask); + const __m128i bits8 = _mm_and_si128(_mm_load_si128(dst), dstMask); + _mm_store_si128(dst, _mm_or_si128(bits24, bits8)); + dst++; + src++; + } +#endif + + // Copy the remaining pixels that didn't fit in SSE. + for (; x < c; ++x) { + memcpy(dstp + x, srcp + x, 3); + } +} + +void FramebufferManagerD3D11::BlitFramebufferDepth(VirtualFramebuffer *src, VirtualFramebuffer *dst) { + if (g_Config.bDisableSlowFramebufEffects) { + return; + } + bool matchingDepthBuffer = src->z_address == dst->z_address && src->z_stride != 0 && dst->z_stride != 0; + bool matchingSize = src->width == dst->width && src->height == dst->height; + if (matchingDepthBuffer && matchingSize) { + // Doesn't work. Use a shader maybe? + draw_->BindBackbufferAsRenderTarget(); + draw_->CopyFramebufferImage(src->fbo, 0, 0, 0, 0, dst->fbo, 0, 0, 0, 0, src->renderWidth, src->renderHeight, 1, Draw::FB_DEPTH_BIT); + RebindFramebuffer(); + } +} + +void FramebufferManagerD3D11::BindFramebufferColor(int stage, VirtualFramebuffer *framebuffer, int flags) { + if (framebuffer == NULL) { + framebuffer = currentRenderVfb_; + } + + if (!framebuffer->fbo || !useBufferedRendering_) { + ID3D11ShaderResourceView *view = nullptr; + context_->PSSetShaderResources(stage, 1, &view); + gstate_c.skipDrawReason |= SKIPDRAW_BAD_FB_TEXTURE; + return; + } + + // currentRenderVfb_ will always be set when this is called, except from the GE debugger. + // Let's just not bother with the copy in that case. + bool skipCopy = (flags & BINDFBCOLOR_MAY_COPY) == 0; + if (GPUStepping::IsStepping() || g_Config.bDisableSlowFramebufEffects) { + skipCopy = true; + } + // Currently rendering to this framebuffer. Need to make a copy. + if (!skipCopy && currentRenderVfb_ && framebuffer->fb_address == gstate.getFrameBufRawAddress()) { + // TODO: Maybe merge with bvfbs_? Not sure if those could be packing, and they're created at a different size. + Draw::Framebuffer *renderCopy = GetTempFBO(framebuffer->renderWidth, framebuffer->renderHeight, (Draw::FBColorDepth)framebuffer->colorDepth); + if (renderCopy) { + VirtualFramebuffer copyInfo = *framebuffer; + copyInfo.fbo = renderCopy; + + int x = 0; + int y = 0; + int w = framebuffer->drawnWidth; + int h = framebuffer->drawnHeight; + + // If max is not > min, we probably could not detect it. Skip. + // See the vertex decoder, where this is updated. + if ((flags & BINDFBCOLOR_MAY_COPY_WITH_UV) == BINDFBCOLOR_MAY_COPY_WITH_UV && gstate_c.vertBounds.maxU > gstate_c.vertBounds.minU) { + x = gstate_c.vertBounds.minU; + y = gstate_c.vertBounds.minV; + w = gstate_c.vertBounds.maxU - x; + h = gstate_c.vertBounds.maxV - y; + + // If we bound a framebuffer, apply the byte offset as pixels to the copy too. + if (flags & BINDFBCOLOR_APPLY_TEX_OFFSET) { + x += gstate_c.curTextureXOffset; + y += gstate_c.curTextureYOffset; + } + } + + BlitFramebuffer(©Info, x, y, framebuffer, x, y, w, h, 0); + + RebindFramebuffer(); + draw_->BindFramebufferAsTexture(renderCopy, stage, Draw::FB_COLOR_BIT, 0); + } else { + draw_->BindFramebufferAsTexture(framebuffer->fbo, stage, Draw::FB_COLOR_BIT, 0); + } + } else { + draw_->BindFramebufferAsTexture(framebuffer->fbo, stage, Draw::FB_COLOR_BIT, 0); + } +} + +void FramebufferManagerD3D11::CopyDisplayToOutput() { + DownloadFramebufferOnSwitch(currentRenderVfb_); + + draw_->BindBackbufferAsRenderTarget(); + currentRenderVfb_ = 0; + + if (displayFramebufPtr_ == 0) { + DEBUG_LOG(SCEGE, "Display disabled, displaying only black"); + // No framebuffer to display! Clear to black. + ClearBuffer(); + return; + } + + if (useBufferedRendering_) { + // In buffered, we no longer clear the backbuffer before we start rendering. + ClearBuffer(); + } + + D3D11_VIEWPORT vp{ 0.0f, 0.0f, (float)PSP_CoreParameter().pixelWidth, (float)PSP_CoreParameter().pixelHeight, 0.0f, 1.0f }; + context_->RSSetViewports(1, &vp); + + u32 offsetX = 0; + u32 offsetY = 0; + + VirtualFramebuffer *vfb = GetVFBAt(displayFramebufPtr_); + if (!vfb) { + // Let's search for a framebuf within this range. + const u32 addr = (displayFramebufPtr_ & 0x03FFFFFF) | 0x04000000; + for (size_t i = 0; i < vfbs_.size(); ++i) { + VirtualFramebuffer *v = vfbs_[i]; + const u32 v_addr = (v->fb_address & 0x03FFFFFF) | 0x04000000; + const u32 v_size = FramebufferByteSize(v); + if (addr >= v_addr && addr < v_addr + v_size) { + const u32 dstBpp = v->format == GE_FORMAT_8888 ? 4 : 2; + const u32 v_offsetX = ((addr - v_addr) / dstBpp) % v->fb_stride; + const u32 v_offsetY = ((addr - v_addr) / dstBpp) / v->fb_stride; + // We have enough space there for the display, right? + if (v_offsetX + 480 >(u32)v->fb_stride || v->bufferHeight < v_offsetY + 272) { + continue; + } + // Check for the closest one. + if (offsetY == 0 || offsetY > v_offsetY) { + offsetX = v_offsetX; + offsetY = v_offsetY; + vfb = v; + } + } + } + + if (vfb) { + // Okay, we found one above. + INFO_LOG_REPORT_ONCE(displayoffset, HLE, "Rendering from framebuf with offset %08x -> %08x+%dx%d", addr, vfb->fb_address, offsetX, offsetY); + } + } + + if (vfb && vfb->format != displayFormat_) { + if (vfb->last_frame_render + FBO_OLD_AGE < gpuStats.numFlips) { + // The game probably switched formats on us. + vfb->format = displayFormat_; + } else { + vfb = 0; + } + } + + if (!vfb) { + if (Memory::IsValidAddress(displayFramebufPtr_)) { + // The game is displaying something directly from RAM. In GTA, it's decoded video. + + // First check that it's not a known RAM copy of a VRAM framebuffer though, as in MotoGP + for (auto iter = knownFramebufferRAMCopies_.begin(); iter != knownFramebufferRAMCopies_.end(); ++iter) { + if (iter->second == displayFramebufPtr_) { + vfb = GetVFBAt(iter->first); + } + } + + if (!vfb) { + // Just a pointer to plain memory to draw. Draw it. + DrawFramebufferToOutput(Memory::GetPointer(displayFramebufPtr_), displayFormat_, displayStride_, true); + return; + } + } else { + DEBUG_LOG(SCEGE, "Found no FBO to display! displayFBPtr = %08x", displayFramebufPtr_); + // No framebuffer to display! Clear to black. If buffered, we already did that. + if (!useBufferedRendering_) + ClearBuffer(); + return; + } + } + + vfb->usageFlags |= FB_USAGE_DISPLAYED_FRAMEBUFFER; + vfb->last_frame_displayed = gpuStats.numFlips; + vfb->dirtyAfterDisplay = false; + vfb->reallyDirtyAfterDisplay = false; + + if (prevDisplayFramebuf_ != displayFramebuf_) { + prevPrevDisplayFramebuf_ = prevDisplayFramebuf_; + } + if (displayFramebuf_ != vfb) { + prevDisplayFramebuf_ = displayFramebuf_; + } + displayFramebuf_ = vfb; + + if (vfb->fbo) { + DEBUG_LOG(SCEGE, "Displaying FBO %08x", vfb->fb_address); + DisableState(); + draw_->BindFramebufferAsTexture(vfb->fbo, 0, Draw::FB_COLOR_BIT, 0); + + // Output coordinates + float x, y, w, h; + int uvRotation = (g_Config.iRenderingMode != FB_NON_BUFFERED_MODE) ? g_Config.iInternalScreenRotation : ROTATION_LOCKED_HORIZONTAL; + CenterDisplayOutputRect(&x, &y, &w, &h, 480.0f, 272.0f, (float)PSP_CoreParameter().pixelWidth, (float)PSP_CoreParameter().pixelHeight, uvRotation); + + const float u0 = offsetX / (float)vfb->bufferWidth; + const float v0 = offsetY / (float)vfb->bufferHeight; + const float u1 = (480.0f + offsetX) / (float)vfb->bufferWidth; + const float v1 = (272.0f + offsetY) / (float)vfb->bufferHeight; + + if (1) { + const u32 rw = PSP_CoreParameter().pixelWidth; + const u32 rh = PSP_CoreParameter().pixelHeight; + DrawActiveTexture(x, y, w, h, (float)PSP_CoreParameter().pixelWidth, (float)PSP_CoreParameter().pixelHeight, u0, v0, u1, v1, uvRotation, g_Config.iBufFilter == SCALE_LINEAR); + } + + /* + else if (usePostShader_ && extraFBOs_.size() == 1 && !postShaderAtOutputResolution_) { + // An additional pass, post-processing shader to the extra FBO. + BindFramebufferAsRenderTarget(extraFBOs_[0]); + int fbo_w, fbo_h; + fbo_get_dimensions(extraFBOs_[0], &fbo_w, &fbo_h); + DXSetViewport(0, 0, fbo_w, fbo_h); + DrawActiveTexture(colorTexture, 0, 0, fbo_w, fbo_h, fbo_w, fbo_h, true, 1.0f, 1.0f, postShaderProgram_); + + fbo_unbind(); + + // Use the extra FBO, with applied post-processing shader, as a texture. + // fbo_bind_color_as_texture(extraFBOs_[0], 0); + if (extraFBOs_.size() == 0) { + ERROR_LOG(G3D, "WTF?"); + return; + } + colorTexture = fbo_get_color_texture(extraFBOs_[0]); + DXSetViewport(0, 0, PSP_CoreParameter().pixelWidth, PSP_CoreParameter().pixelHeight); + // These are in the output display coordinates + DrawActiveTexture(colorTexture, x, y, w, h, (float)PSP_CoreParameter().pixelWidth, (float)PSP_CoreParameter().pixelHeight, true, 480.0f / (float)vfb->width, 272.0f / (float)vfb->height); + } else { + // Use post-shader, but run shader at output resolution. + DXSetViewport(0, 0, PSP_CoreParameter().pixelWidth, PSP_CoreParameter().pixelHeight); + // These are in the output display coordinates + DrawActiveTexture(colorTexture, x, y, w, h, (float)PSP_CoreParameter().pixelWidth, (float)PSP_CoreParameter().pixelHeight, true, 480.0f / (float)vfb->width, 272.0f / (float)vfb->height, postShaderProgram_); + } + */ + } + // gstate_c.Dirty(DIRTY_VIEWPORT_STATE); +} + +void FramebufferManagerD3D11::ReadFramebufferToMemory(VirtualFramebuffer *vfb, bool sync, int x, int y, int w, int h) { + if (vfb) { + // We'll pseudo-blit framebuffers here to get a resized version of vfb. + VirtualFramebuffer *nvfb = FindDownloadTempBuffer(vfb); + OptimizeDownloadRange(vfb, x, y, w, h); + BlitFramebuffer(nvfb, x, y, vfb, x, y, w, h, 0); + + PackFramebufferD3D11_(nvfb, x, y, w, h); + + textureCacheD3D11_->ForgetLastTexture(); + RebindFramebuffer(); + } +} + +void FramebufferManagerD3D11::DownloadFramebufferForClut(u32 fb_address, u32 loadBytes) { + VirtualFramebuffer *vfb = GetVFBAt(fb_address); + if (vfb && vfb->fb_stride != 0) { + const u32 bpp = vfb->drawnFormat == GE_FORMAT_8888 ? 4 : 2; + int x = 0; + int y = 0; + int pixels = loadBytes / bpp; + // The height will be 1 for each stride or part thereof. + int w = std::min(pixels % vfb->fb_stride, (int)vfb->width); + int h = std::min((pixels + vfb->fb_stride - 1) / vfb->fb_stride, (int)vfb->height); + + // We might still have a pending draw to the fb in question, flush if so. + FlushBeforeCopy(); + + // No need to download if we already have it. + if (!vfb->memoryUpdated && vfb->clutUpdatedBytes < loadBytes) { + // We intentionally don't call OptimizeDownloadRange() here - we don't want to over download. + // CLUT framebuffers are often incorrectly estimated in size. + if (x == 0 && y == 0 && w == vfb->width && h == vfb->height) { + vfb->memoryUpdated = true; + } + vfb->clutUpdatedBytes = loadBytes; + + // We'll pseudo-blit framebuffers here to get a resized version of vfb. + VirtualFramebuffer *nvfb = FindDownloadTempBuffer(vfb); + BlitFramebuffer(nvfb, x, y, vfb, x, y, w, h, 0); + + PackFramebufferD3D11_(nvfb, x, y, w, h); + + textureCacheD3D11_->ForgetLastTexture(); + RebindFramebuffer(); + } + } +} + +bool FramebufferManagerD3D11::CreateDownloadTempBuffer(VirtualFramebuffer *nvfb) { + nvfb->colorDepth = Draw::FBO_8888; + + nvfb->fbo = draw_->CreateFramebuffer({ nvfb->width, nvfb->height, 1, 1, true, (Draw::FBColorDepth)nvfb->colorDepth }); + if (!(nvfb->fbo)) { + ERROR_LOG(SCEGE, "Error creating FBO! %i x %i", nvfb->renderWidth, nvfb->renderHeight); + return false; + } + + draw_->BindFramebufferAsRenderTarget(nvfb->fbo); + ClearBuffer(); + return true; +} + +void FramebufferManagerD3D11::UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb) { + // Nothing to do here. +} + +void FramebufferManagerD3D11::BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp) { + if (!dst->fbo || !src->fbo || !useBufferedRendering_) { + // This can happen if they recently switched from non-buffered. + draw_->BindBackbufferAsRenderTarget(); + return; + } + + float srcXFactor = (float)src->renderWidth / (float)src->bufferWidth; + float srcYFactor = (float)src->renderHeight / (float)src->bufferHeight; + const int srcBpp = src->format == GE_FORMAT_8888 ? 4 : 2; + if (srcBpp != bpp && bpp != 0) { + srcXFactor = (srcXFactor * bpp) / srcBpp; + } + int srcX1 = srcX * srcXFactor; + int srcX2 = (srcX + w) * srcXFactor; + int srcY1 = srcY * srcYFactor; + int srcY2 = (srcY + h) * srcYFactor; + + float dstXFactor = (float)dst->renderWidth / (float)dst->bufferWidth; + float dstYFactor = (float)dst->renderHeight / (float)dst->bufferHeight; + const int dstBpp = dst->format == GE_FORMAT_8888 ? 4 : 2; + if (dstBpp != bpp && bpp != 0) { + dstXFactor = (dstXFactor * bpp) / dstBpp; + } + int dstX1 = dstX * dstXFactor; + int dstX2 = (dstX + w) * dstXFactor; + int dstY1 = dstY * dstYFactor; + int dstY2 = (dstY + h) * dstYFactor; + + // Direct3D 9 doesn't support rect -> self. + Draw::Framebuffer *srcFBO = src->fbo; + if (src == dst) { + Draw::Framebuffer *tempFBO = GetTempFBO(src->renderWidth, src->renderHeight, (Draw::FBColorDepth)src->colorDepth); + bool result = draw_->BlitFramebuffer( + src->fbo, srcX1, srcY1, srcX2, srcY2, + tempFBO, dstX1, dstY1, dstX2, dstY2, + Draw::FB_COLOR_BIT, Draw::FB_BLIT_NEAREST); + if (result) { + srcFBO = tempFBO; + } + } + bool result = draw_->BlitFramebuffer( + srcFBO, srcX1, srcY1, srcX2, srcY2, + dst->fbo, dstX1, dstY1, dstX2, dstY2, + Draw::FB_COLOR_BIT, Draw::FB_BLIT_NEAREST); + if (!result) { + ERROR_LOG_REPORT(G3D, "fbo_blit_color failed in blit: %08x (%08x -> %08x)", src->fb_address, dst->fb_address); + } +} + +// TODO: SSE/NEON +// Could also make C fake-simd for 64-bit, two 8888 pixels fit in a register :) +void ConvertFromRGBA8888(u8 *dst, u8 *src, u32 dstStride, u32 srcStride, u32 width, u32 height, GEBufferFormat format) { + // Must skip stride in the cases below. Some games pack data into the cracks, like MotoGP. + const u32 *src32 = (const u32 *)src; + + if (format == GE_FORMAT_8888) { + u32 *dst32 = (u32 *)dst; + if (src == dst) { + return; + } else { + for (u32 y = 0; y < height; ++y) { + ConvertBGRA8888ToRGBA8888(dst32, src32, width); + src32 += srcStride; + dst32 += dstStride; + } + } + } else { + // But here it shouldn't matter if they do intersect + u16 *dst16 = (u16 *)dst; + switch (format) { + case GE_FORMAT_565: // BGR 565 + for (u32 y = 0; y < height; ++y) { + ConvertBGRA8888ToRGB565(dst16, src32, width); + src32 += srcStride; + dst16 += dstStride; + } + break; + case GE_FORMAT_5551: // ABGR 1555 + for (u32 y = 0; y < height; ++y) { + ConvertBGRA8888ToRGBA5551(dst16, src32, width); + src32 += srcStride; + dst16 += dstStride; + } + break; + case GE_FORMAT_4444: // ABGR 4444 + for (u32 y = 0; y < height; ++y) { + ConvertBGRA8888ToRGBA4444(dst16, src32, width); + src32 += srcStride; + dst16 += dstStride; + } + break; + case GE_FORMAT_8888: + case GE_FORMAT_INVALID: + // Not possible. + break; + } + } +} + +void FramebufferManagerD3D11::PackFramebufferD3D11_(VirtualFramebuffer *vfb, int x, int y, int w, int h) { + if (!vfb->fbo) { + ERROR_LOG_REPORT_ONCE(vfbfbozero, SCEGE, "PackFramebufferD3D11_: vfb->fbo == 0"); + draw_->BindBackbufferAsRenderTarget(); + return; + } + + const u32 fb_address = (0x04000000) | vfb->fb_address; + const int dstBpp = vfb->format == GE_FORMAT_8888 ? 4 : 2; + + // We always need to convert from the framebuffer native format. + // Right now that's always 8888. + DEBUG_LOG(HLE, "Reading framebuffer to mem, fb_address = %08x", fb_address); + + /* + LPDIRECT3DSURFACE9 renderTarget = (LPDIRECT3DSURFACE9)draw_->GetFramebufferAPITexture(vfb->fbo, Draw::FB_COLOR_BIT | Draw::FB_SURFACE_BIT, 0); + D3DSURFACE_DESC desc; + renderTarget->GetDesc(&desc); + + LPDIRECT3DSURFACE9 offscreen = GetOffscreenSurface(renderTarget, vfb); + if (offscreen) { + HRESULT hr = pD3Ddevice->GetRenderTargetData(renderTarget, offscreen); + if (SUCCEEDED(hr)) { + D3DLOCKED_RECT locked; + u32 widthFactor = vfb->renderWidth / vfb->bufferWidth; + u32 heightFactor = vfb->renderHeight / vfb->bufferHeight; + RECT rect = { (LONG)(x * widthFactor), (LONG)(y * heightFactor), (LONG)((x + w) * widthFactor), (LONG)((y + h) * heightFactor) }; + hr = offscreen->LockRect(&locked, &rect, D3DLOCK_READONLY); + if (SUCCEEDED(hr)) { + // TODO: Handle the other formats? We don't currently create them, I think. + const int dstByteOffset = (y * vfb->fb_stride + x) * dstBpp; + // Pixel size always 4 here because we always request BGRA8888. + ConvertFromRGBA8888(Memory::GetPointer(fb_address + dstByteOffset), (u8 *)locked.pBits, vfb->fb_stride, locked.Pitch / 4, w, h, vfb->format); + offscreen->UnlockRect(); + } else { + ERROR_LOG_REPORT(G3D, "Unable to lock rect from %08x: %d,%d %dx%d of %dx%d", fb_address, rect.left, rect.top, rect.right, rect.bottom, vfb->renderWidth, vfb->renderHeight); + } + } else { + ERROR_LOG_REPORT(G3D, "Unable to download render target data from %08x", fb_address); + } + } + */ +} + +void FramebufferManagerD3D11::PackDepthbuffer(VirtualFramebuffer *vfb, int x, int y, int w, int h) { + if (!vfb->fbo) { + ERROR_LOG_REPORT_ONCE(vfbfbozero, SCEGE, "PackDepthbuffer: vfb->fbo == 0"); + return; + } + + // We always read the depth buffer in 24_8 format. + const u32 z_address = (0x04000000) | vfb->z_address; + + /* + DEBUG_LOG(SCEGE, "Reading depthbuffer to mem at %08x for vfb=%08x", z_address, vfb->fb_address); + + LPDIRECT3DTEXTURE9 tex = (LPDIRECT3DTEXTURE9)draw_->GetFramebufferAPITexture(vfb->fbo, Draw::FB_DEPTH_BIT, 0); + if (tex) { + D3DSURFACE_DESC desc; + D3DLOCKED_RECT locked; + tex->GetLevelDesc(0, &desc); + RECT rect = { 0, 0, (LONG)desc.Width, (LONG)desc.Height }; + HRESULT hr = tex->LockRect(0, &locked, &rect, D3DLOCK_READONLY); + + if (SUCCEEDED(hr)) { + const int dstByteOffset = y * vfb->fb_stride * sizeof(s16); + const u32 *packed = (const u32 *)locked.pBits; + u16 *depth = (u16 *)Memory::GetPointer(z_address); + + // TODO: Optimize. + for (int yp = 0; yp < h; ++yp) { + for (int xp = 0; xp < w; ++xp) { + const int offset = (yp + y) & vfb->z_stride + x + xp; + + float scaled = FromScaledDepth((packed[offset] & 0x00FFFFFF) * (1.0f / 16777215.0f)); + if (scaled <= 0.0f) { + depth[offset] = 0; + } else if (scaled >= 65535.0f) { + depth[offset] = 65535; + } else { + depth[offset] = (int)scaled; + } + } + } + + tex->UnlockRect(0); + } else { + ERROR_LOG_REPORT(G3D, "Unable to lock rect from depth %08x: %d,%d %dx%d of %dx%d", vfb->fb_address, rect.left, rect.top, rect.right, rect.bottom, vfb->renderWidth, vfb->renderHeight); + } + } else { + ERROR_LOG_REPORT(G3D, "Unable to download render target depth from %08x", vfb->fb_address); + }*/ +} + +void FramebufferManagerD3D11::EndFrame() { + if (resized_) { + DestroyAllFBOs(false); + // Actually, auto mode should be more granular... + // Round up to a zoom factor for the render size. + int zoom = g_Config.iInternalResolution; + if (zoom == 0) { // auto mode + // Use the longest dimension + if (!g_Config.IsPortrait()) { + zoom = (PSP_CoreParameter().pixelWidth + 479) / 480; + } else { + zoom = (PSP_CoreParameter().pixelHeight + 479) / 480; + } + } + if (zoom <= 1) + zoom = 1; + + if (g_Config.IsPortrait()) { + PSP_CoreParameter().renderWidth = 272 * zoom; + PSP_CoreParameter().renderHeight = 480 * zoom; + } else { + PSP_CoreParameter().renderWidth = 480 * zoom; + PSP_CoreParameter().renderHeight = 272 * zoom; + } + + UpdateSize(); + // Seems related - if you're ok with numbers all the time, show some more :) + if (g_Config.iShowFPSCounter != 0) { + ShowScreenResolution(); + } + resized_ = false; + } +} + +void FramebufferManagerD3D11::DeviceLost() { + DestroyAllFBOs(false); + resized_ = false; +} + +std::vector FramebufferManagerD3D11::GetFramebufferList() { + std::vector list; + + for (size_t i = 0; i < vfbs_.size(); ++i) { + VirtualFramebuffer *vfb = vfbs_[i]; + + FramebufferInfo info; + info.fb_address = vfb->fb_address; + info.z_address = vfb->z_address; + info.format = vfb->format; + info.width = vfb->width; + info.height = vfb->height; + info.fbo = vfb->fbo; + list.push_back(info); + } + + return list; +} + +void FramebufferManagerD3D11::DestroyAllFBOs(bool forceDelete) { + draw_->BindBackbufferAsRenderTarget(); + currentRenderVfb_ = 0; + displayFramebuf_ = 0; + prevDisplayFramebuf_ = 0; + prevPrevDisplayFramebuf_ = 0; + + for (size_t i = 0; i < vfbs_.size(); ++i) { + VirtualFramebuffer *vfb = vfbs_[i]; + INFO_LOG(SCEGE, "Destroying FBO for %08x : %i x %i x %i", vfb->fb_address, vfb->width, vfb->height, vfb->format); + DestroyFramebuf(vfb); + } + vfbs_.clear(); + + for (size_t i = 0; i < bvfbs_.size(); ++i) { + VirtualFramebuffer *vfb = bvfbs_[i]; + DestroyFramebuf(vfb); + } + bvfbs_.clear(); + + for (auto it = tempFBOs_.begin(), end = tempFBOs_.end(); it != end; ++it) { + delete it->second.fbo; + } + tempFBOs_.clear(); + + DisableState(); +} + +void FramebufferManagerD3D11::FlushBeforeCopy() { + // Flush anything not yet drawn before blitting, downloading, or uploading. + // This might be a stalled list, or unflushed before a block transfer, etc. + + // TODO: It's really bad that we are calling SetRenderFramebuffer here with + // all the irrelevant state checking it'll use to decide what to do. Should + // do something more focused here. + SetRenderFrameBuffer(gstate_c.IsDirty(DIRTY_FRAMEBUF), gstate_c.skipDrawReason); + drawEngine_->Flush(); +} + +void FramebufferManagerD3D11::Resized() { + resized_ = true; +} + +bool FramebufferManagerD3D11::GetCurrentFramebuffer(GPUDebugBuffer &buffer, GPUDebugFramebufferType type, int maxRes) { + return false; +} + +bool FramebufferManagerD3D11::GetOutputFramebuffer(GPUDebugBuffer &buffer) { + return false; +} + +bool FramebufferManagerD3D11::GetCurrentDepthbuffer(GPUDebugBuffer &buffer) { + return false; +} + +bool FramebufferManagerD3D11::GetCurrentStencilbuffer(GPUDebugBuffer &buffer) { + return false; +} \ No newline at end of file diff --git a/GPU/D3D11/FramebufferManagerD3D11.h b/GPU/D3D11/FramebufferManagerD3D11.h new file mode 100644 index 0000000000..2eaba71829 --- /dev/null +++ b/GPU/D3D11/FramebufferManagerD3D11.h @@ -0,0 +1,157 @@ +// Copyright (c) 2012- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program 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 General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +#include +#include +#include + +#include + +// Keeps track of allocated FBOs. +// Also provides facilities for drawing and later converting raw +// pixel data. + + +#include "Globals.h" +#include "GPU/GPUCommon.h" +#include "GPU/Common/FramebufferCommon.h" +#include "Core/Config.h" +#include "ext/native/thin3d/thin3d.h" + +class TextureCacheD3D11; +class DrawEngineD3D11; +class ShaderManagerD3D11; + +class FramebufferManagerD3D11 : public FramebufferManagerCommon { +public: + FramebufferManagerD3D11(Draw::DrawContext *draw); + ~FramebufferManagerD3D11(); + + void SetTextureCache(TextureCacheD3D11 *tc); + void SetShaderManager(ShaderManagerD3D11 *sm) { + shaderManager_ = sm; + } + void SetDrawEngine(DrawEngineD3D11 *td) { + drawEngine_ = td; + } + + virtual void DrawPixels(VirtualFramebuffer *vfb, int dstX, int dstY, const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height) override; + virtual void DrawFramebufferToOutput(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, bool applyPostShader) override; + + void DrawActiveTexture(float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, int uvRotation, bool linearFilter); + + void DestroyAllFBOs(bool forceDelete); + + void EndFrame(); + void Resized() override; + void DeviceLost(); + void CopyDisplayToOutput(); + void ReformatFramebufferFrom(VirtualFramebuffer *vfb, GEBufferFormat old) override; + + void BlitFramebufferDepth(VirtualFramebuffer *src, VirtualFramebuffer *dst) override; + + void BindFramebufferColor(int stage, VirtualFramebuffer *framebuffer, int flags); + + void ReadFramebufferToMemory(VirtualFramebuffer *vfb, bool sync, int x, int y, int w, int h) override; + void DownloadFramebufferForClut(u32 fb_address, u32 loadBytes) override; + + std::vector GetFramebufferList(); + + virtual bool NotifyStencilUpload(u32 addr, int size, bool skipZero = false) override; + + bool GetCurrentFramebuffer(GPUDebugBuffer &buffer, GPUDebugFramebufferType type, int maxRes); + bool GetCurrentDepthbuffer(GPUDebugBuffer &buffer); + bool GetCurrentStencilbuffer(GPUDebugBuffer &buffer); + bool GetOutputFramebuffer(GPUDebugBuffer &buffer); + + virtual void RebindFramebuffer() override; + + // TODO: Remove + ID3D11Buffer *GetDynamicQuadBuffer() { + return quadBuffer_; + } + +protected: + void DisableState() override; + void ClearBuffer(bool keepState = false) override; + void FlushBeforeCopy() override; + + // Used by ReadFramebufferToMemory and later framebuffer block copies + void BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp) override; + + bool CreateDownloadTempBuffer(VirtualFramebuffer *nvfb) override; + void UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb) override; + +private: + void MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height); + void CompileDraw2DProgram(); + void DestroyDraw2DProgram(); + + void PackFramebufferD3D11_(VirtualFramebuffer *vfb, int x, int y, int w, int h); + void PackDepthbuffer(VirtualFramebuffer *vfb, int x, int y, int w, int h); + + ID3D11Device *device_; + ID3D11DeviceContext *context_; + + // Used by DrawPixels + ID3D11Texture2D *drawPixelsTex_ = nullptr; + ID3D11ShaderResourceView *drawPixelsTexView_ = nullptr; + int drawPixelsTexW_ = 0; + int drawPixelsTexH_ = 0; + + ID3D11VertexShader *quadVertexShader_; + ID3D11PixelShader *quadPixelShader_; + ID3D11InputLayout *quadInputLayout_; + // Dynamic + ID3D11Buffer *quadBuffer_; + ID3D11Buffer *fsQuadBuffer_; + const UINT quadStride_ = 20; + const UINT quadOffset_ = 0; + static const D3D11_INPUT_ELEMENT_DESC g_QuadVertexElements[2]; + + u8 *convBuf = nullptr; + + int plainColorLoc_; + ID3D11PixelShader *stencilUploadPS_ = nullptr; + ID3D11VertexShader *stencilUploadVS_ = nullptr; + ID3D11InputLayout *stencilUploadInputLayout_ = nullptr; + ID3D11Buffer *stencilValueBuffer_ = nullptr; + ID3D11DepthStencilState *stencilMaskStates_[256]{}; + + TextureCacheD3D11 *textureCacheD3D11_; + ShaderManagerD3D11 *shaderManager_; + DrawEngineD3D11 *drawEngine_; + + // Used by post-processing shader + std::vector extraFBOs_; + + bool resized_; + + struct TempFBO { + Draw::Framebuffer *fbo; + int last_frame_used; + }; + + std::map tempFBOs_; + +#if 0 + AsyncPBO *pixelBufObj_; //this isn't that large + u8 currentPBO_; +#endif +}; \ No newline at end of file diff --git a/GPU/D3D11/GPU_D3D11.cpp b/GPU/D3D11/GPU_D3D11.cpp new file mode 100644 index 0000000000..2410a057e3 --- /dev/null +++ b/GPU/D3D11/GPU_D3D11.cpp @@ -0,0 +1,1032 @@ +// Copyright (c) 2017- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program 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 General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "GPU/D3D11/GPU_D3D11.h" + +// Copyright (c) 2012- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program 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 General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include + +#include "Common/ChunkFile.h" +#include "Common/GraphicsContext.h" +#include "base/NativeApp.h" +#include "base/logging.h" +#include "profiler/profiler.h" +#include "Core/Debugger/Breakpoints.h" +#include "Core/MemMapHelpers.h" +#include "Core/MIPS/MIPS.h" +#include "Core/Host.h" +#include "Core/Config.h" +#include "Core/Reporting.h" +#include "Core/System.h" + +#include "GPU/GPUState.h" +#include "GPU/ge_constants.h" +#include "GPU/GeDisasm.h" + +#include "GPU/Common/FramebufferCommon.h" +#include "GPU/D3D11/ShaderManagerD3D11.h" +#include "GPU/D3D11/GPU_D3D11.h" +#include "GPU/D3D11/FramebufferManagerD3D11.h" +#include "GPU/D3D11/DrawEngineD3D11.h" +#include "GPU/D3D11/TextureCacheD3D11.h" +#include "GPU/D3D11/D3D11Util.h" + +#include "Core/HLE/sceKernelThread.h" +#include "Core/HLE/sceKernelInterrupt.h" +#include "Core/HLE/sceGe.h" + +enum { + FLAG_FLUSHBEFORE = 1, + FLAG_FLUSHBEFOREONCHANGE = 2, + FLAG_EXECUTE = 4, // needs to actually be executed. unused for now. + FLAG_EXECUTEONCHANGE = 8, + FLAG_READS_PC = 16, + FLAG_WRITES_PC = 32, + FLAG_DIRTYONCHANGE = 64, // NOTE: Either this or FLAG_EXECUTE*, not both! +}; + +struct CommandTableEntry { + uint8_t cmd; + uint8_t flags; + uint64_t dirty; + GPU_D3D11::CmdFunc func; +}; + +static const CommandTableEntry commandTable[] = { + // Changes that dirty the framebuffer + { GE_CMD_FRAMEBUFPTR, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS }, + { GE_CMD_FRAMEBUFWIDTH, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS }, + { GE_CMD_FRAMEBUFPIXFORMAT, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS }, + { GE_CMD_ZBUFPTR, FLAG_FLUSHBEFOREONCHANGE }, + { GE_CMD_ZBUFWIDTH, FLAG_FLUSHBEFOREONCHANGE }, + + // Changes that dirty uniforms + { GE_CMD_FOGCOLOR, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FOGCOLOR }, + { GE_CMD_FOG1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FOGCOEF }, + { GE_CMD_FOG2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FOGCOEF }, + + // Should these maybe flush? + { GE_CMD_MINZ, FLAG_FLUSHBEFOREONCHANGE, DIRTY_DEPTHRANGE }, + { GE_CMD_MAXZ, FLAG_FLUSHBEFOREONCHANGE, DIRTY_DEPTHRANGE }, + + // Changes that dirty texture scaling. + { GE_CMD_TEXMAPMODE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_UVSCALEOFFSET }, + { GE_CMD_TEXSCALEU, FLAG_EXECUTEONCHANGE, 0, &GPUCommon::Execute_TexScaleU }, + { GE_CMD_TEXSCALEV, FLAG_EXECUTEONCHANGE, 0, &GPUCommon::Execute_TexScaleV }, + { GE_CMD_TEXOFFSETU, FLAG_EXECUTEONCHANGE, 0, &GPUCommon::Execute_TexOffsetU }, + { GE_CMD_TEXOFFSETV, FLAG_EXECUTEONCHANGE, 0, &GPUCommon::Execute_TexOffsetV }, + + // Changes that dirty the current texture. + { GE_CMD_TEXSIZE0, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTE, 0, &GPU_D3D11::Execute_TexSize0 }, + { GE_CMD_TEXSIZE1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS }, + { GE_CMD_TEXSIZE2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS }, + { GE_CMD_TEXSIZE3, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS }, + { GE_CMD_TEXSIZE4, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS }, + { GE_CMD_TEXSIZE5, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS }, + { GE_CMD_TEXSIZE6, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS }, + { GE_CMD_TEXSIZE7, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS }, + { GE_CMD_TEXFORMAT, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_IMAGE }, + { GE_CMD_TEXLEVEL, 0, DIRTY_TEXTURE_PARAMS }, // Flushing on this is EXPENSIVE in Gran Turismo and of little use + { GE_CMD_TEXADDR0, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_IMAGE | DIRTY_UVSCALEOFFSET }, + { GE_CMD_TEXADDR1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS }, + { GE_CMD_TEXADDR2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS }, + { GE_CMD_TEXADDR3, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS }, + { GE_CMD_TEXADDR4, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS }, + { GE_CMD_TEXADDR5, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS }, + { GE_CMD_TEXADDR6, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS }, + { GE_CMD_TEXADDR7, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS }, + { GE_CMD_TEXBUFWIDTH0, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_IMAGE }, + { GE_CMD_TEXBUFWIDTH1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS }, + { GE_CMD_TEXBUFWIDTH2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS }, + { GE_CMD_TEXBUFWIDTH3, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS }, + { GE_CMD_TEXBUFWIDTH4, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS }, + { GE_CMD_TEXBUFWIDTH5, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS }, + { GE_CMD_TEXBUFWIDTH6, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS }, + { GE_CMD_TEXBUFWIDTH7, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS }, + // These must flush on change, so that LoadClut doesn't have to always flush. + { GE_CMD_CLUTADDR, FLAG_FLUSHBEFOREONCHANGE }, + { GE_CMD_CLUTADDRUPPER, FLAG_FLUSHBEFOREONCHANGE }, + { GE_CMD_CLUTFORMAT, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS }, + + // These affect the fragment shader so need flushing. + { GE_CMD_CLEARMODE, FLAG_FLUSHBEFOREONCHANGE }, + { GE_CMD_TEXTUREMAPENABLE, FLAG_FLUSHBEFOREONCHANGE }, + { GE_CMD_FOGENABLE, FLAG_FLUSHBEFOREONCHANGE }, + { GE_CMD_TEXMODE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS }, + { GE_CMD_TEXSHADELS, FLAG_FLUSHBEFOREONCHANGE }, + { GE_CMD_SHADEMODE, FLAG_FLUSHBEFOREONCHANGE }, + { GE_CMD_TEXFUNC, FLAG_FLUSHBEFOREONCHANGE }, + { GE_CMD_COLORTEST, FLAG_FLUSHBEFOREONCHANGE }, + { GE_CMD_ALPHATESTENABLE, FLAG_FLUSHBEFOREONCHANGE }, + { GE_CMD_COLORTESTENABLE, FLAG_FLUSHBEFOREONCHANGE }, + { GE_CMD_COLORTESTMASK, FLAG_FLUSHBEFOREONCHANGE, DIRTY_ALPHACOLORMASK }, + + // These change the vertex shader so need flushing. + { GE_CMD_REVERSENORMAL, FLAG_FLUSHBEFOREONCHANGE }, + { GE_CMD_LIGHTINGENABLE, FLAG_FLUSHBEFOREONCHANGE }, + { GE_CMD_LIGHTENABLE0, FLAG_FLUSHBEFOREONCHANGE }, + { GE_CMD_LIGHTENABLE1, FLAG_FLUSHBEFOREONCHANGE }, + { GE_CMD_LIGHTENABLE2, FLAG_FLUSHBEFOREONCHANGE }, + { GE_CMD_LIGHTENABLE3, FLAG_FLUSHBEFOREONCHANGE }, + { GE_CMD_LIGHTTYPE0, FLAG_FLUSHBEFOREONCHANGE }, + { GE_CMD_LIGHTTYPE1, FLAG_FLUSHBEFOREONCHANGE }, + { GE_CMD_LIGHTTYPE2, FLAG_FLUSHBEFOREONCHANGE }, + { GE_CMD_LIGHTTYPE3, FLAG_FLUSHBEFOREONCHANGE }, + { GE_CMD_MATERIALUPDATE, FLAG_FLUSHBEFOREONCHANGE }, + + // This changes both shaders so need flushing. + { GE_CMD_LIGHTMODE, FLAG_FLUSHBEFOREONCHANGE }, + { GE_CMD_TEXFILTER, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS }, + { GE_CMD_TEXWRAP, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS }, + + // Uniform changes + { GE_CMD_ALPHATEST, FLAG_FLUSHBEFOREONCHANGE, DIRTY_ALPHACOLORREF | DIRTY_ALPHACOLORMASK }, + { GE_CMD_COLORREF, FLAG_FLUSHBEFOREONCHANGE, DIRTY_ALPHACOLORREF }, + { GE_CMD_TEXENVCOLOR, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXENV }, + + // Simple render state changes. Handled in StateMapping.cpp. + { GE_CMD_OFFSETX, FLAG_FLUSHBEFOREONCHANGE }, + { GE_CMD_OFFSETY, FLAG_FLUSHBEFOREONCHANGE }, + { GE_CMD_CULL, FLAG_FLUSHBEFOREONCHANGE }, + { GE_CMD_CULLFACEENABLE, FLAG_FLUSHBEFOREONCHANGE }, + { GE_CMD_DITHERENABLE, FLAG_FLUSHBEFOREONCHANGE }, + { GE_CMD_STENCILOP, FLAG_FLUSHBEFOREONCHANGE }, + { GE_CMD_STENCILTEST, FLAG_FLUSHBEFOREONCHANGE, DIRTY_STENCILREPLACEVALUE }, + { GE_CMD_STENCILTESTENABLE, FLAG_FLUSHBEFOREONCHANGE }, + { GE_CMD_ALPHABLENDENABLE, FLAG_FLUSHBEFOREONCHANGE }, + { GE_CMD_BLENDMODE, FLAG_FLUSHBEFOREONCHANGE }, + { GE_CMD_BLENDFIXEDA, FLAG_FLUSHBEFOREONCHANGE }, + { GE_CMD_BLENDFIXEDB, FLAG_FLUSHBEFOREONCHANGE }, + { GE_CMD_MASKRGB, FLAG_FLUSHBEFOREONCHANGE }, + { GE_CMD_MASKALPHA, FLAG_FLUSHBEFOREONCHANGE }, + { GE_CMD_ZTEST, FLAG_FLUSHBEFOREONCHANGE }, + { GE_CMD_ZTESTENABLE, FLAG_FLUSHBEFOREONCHANGE }, + { GE_CMD_ZWRITEDISABLE, FLAG_FLUSHBEFOREONCHANGE }, + + // These can't be emulated in D3D (except a few special cases) + { GE_CMD_LOGICOP, FLAG_FLUSHBEFOREONCHANGE }, + { GE_CMD_LOGICOPENABLE, FLAG_FLUSHBEFOREONCHANGE }, + + // Can probably ignore this one as we don't support AA lines. + { GE_CMD_ANTIALIASENABLE, FLAG_FLUSHBEFOREONCHANGE }, + + // Morph weights. TODO: Remove precomputation? + { GE_CMD_MORPHWEIGHT0, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, 0, &GPUCommon::Execute_MorphWeight }, + { GE_CMD_MORPHWEIGHT1, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, 0, &GPUCommon::Execute_MorphWeight }, + { GE_CMD_MORPHWEIGHT2, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, 0, &GPUCommon::Execute_MorphWeight }, + { GE_CMD_MORPHWEIGHT3, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, 0, &GPUCommon::Execute_MorphWeight }, + { GE_CMD_MORPHWEIGHT4, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, 0, &GPUCommon::Execute_MorphWeight }, + { GE_CMD_MORPHWEIGHT5, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, 0, &GPUCommon::Execute_MorphWeight }, + { GE_CMD_MORPHWEIGHT6, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, 0, &GPUCommon::Execute_MorphWeight }, + { GE_CMD_MORPHWEIGHT7, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, 0, &GPUCommon::Execute_MorphWeight }, + + // Control spline/bezier patches. Don't really require flushing as such, but meh. + { GE_CMD_PATCHDIVISION, FLAG_FLUSHBEFOREONCHANGE }, + { GE_CMD_PATCHPRIMITIVE, FLAG_FLUSHBEFOREONCHANGE }, + { GE_CMD_PATCHFACING, FLAG_FLUSHBEFOREONCHANGE }, + { GE_CMD_PATCHCULLENABLE, FLAG_FLUSHBEFOREONCHANGE }, + + // Viewport. + { GE_CMD_VIEWPORTXSCALE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS }, + { GE_CMD_VIEWPORTYSCALE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS }, + { GE_CMD_VIEWPORTXCENTER, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS }, + { GE_CMD_VIEWPORTYCENTER, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS }, + { GE_CMD_VIEWPORTZSCALE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_DEPTHRANGE | DIRTY_PROJMATRIX }, + { GE_CMD_VIEWPORTZCENTER, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_DEPTHRANGE | DIRTY_PROJMATRIX }, + + // Region + { GE_CMD_REGION1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS }, + { GE_CMD_REGION2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS }, + + // Scissor + { GE_CMD_SCISSOR1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS }, + { GE_CMD_SCISSOR2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS }, + + // Lighting base colors + { GE_CMD_AMBIENTCOLOR, FLAG_FLUSHBEFOREONCHANGE, DIRTY_AMBIENT }, + { GE_CMD_AMBIENTALPHA, FLAG_FLUSHBEFOREONCHANGE, DIRTY_AMBIENT }, + { GE_CMD_MATERIALDIFFUSE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_MATDIFFUSE }, + { GE_CMD_MATERIALEMISSIVE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_MATEMISSIVE }, + { GE_CMD_MATERIALAMBIENT, FLAG_FLUSHBEFOREONCHANGE, DIRTY_MATAMBIENTALPHA }, + { GE_CMD_MATERIALALPHA, FLAG_FLUSHBEFOREONCHANGE, DIRTY_MATAMBIENTALPHA }, + { GE_CMD_MATERIALSPECULAR, FLAG_FLUSHBEFOREONCHANGE, DIRTY_MATSPECULAR }, + { GE_CMD_MATERIALSPECULARCOEF, FLAG_FLUSHBEFOREONCHANGE, DIRTY_MATSPECULAR }, + + // Light parameters + { GE_CMD_LX0, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT0 }, + { GE_CMD_LY0, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT0 }, + { GE_CMD_LZ0, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT0 }, + { GE_CMD_LX1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT1 }, + { GE_CMD_LY1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT1 }, + { GE_CMD_LZ1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT1 }, + { GE_CMD_LX2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT2 }, + { GE_CMD_LY2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT2 }, + { GE_CMD_LZ2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT2 }, + { GE_CMD_LX3, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT3 }, + { GE_CMD_LY3, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT3 }, + { GE_CMD_LZ3, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT3 }, + + { GE_CMD_LDX0, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT0 }, + { GE_CMD_LDY0, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT0 }, + { GE_CMD_LDZ0, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT0 }, + { GE_CMD_LDX1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT1 }, + { GE_CMD_LDY1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT1 }, + { GE_CMD_LDZ1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT1 }, + { GE_CMD_LDX2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT2 }, + { GE_CMD_LDY2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT2 }, + { GE_CMD_LDZ2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT2 }, + { GE_CMD_LDX3, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT3 }, + { GE_CMD_LDY3, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT3 }, + { GE_CMD_LDZ3, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT3 }, + + { GE_CMD_LKA0, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT0 }, + { GE_CMD_LKB0, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT0 }, + { GE_CMD_LKC0, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT0 }, + { GE_CMD_LKA1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT1 }, + { GE_CMD_LKB1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT1 }, + { GE_CMD_LKC1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT1 }, + { GE_CMD_LKA2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT2 }, + { GE_CMD_LKB2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT2 }, + { GE_CMD_LKC2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT2 }, + { GE_CMD_LKA3, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT3 }, + { GE_CMD_LKB3, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT3 }, + { GE_CMD_LKC3, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT3 }, + + { GE_CMD_LKS0, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT0 }, + { GE_CMD_LKS1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT1 }, + { GE_CMD_LKS2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT2 }, + { GE_CMD_LKS3, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT3 }, + + { GE_CMD_LKO0, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT0 }, + { GE_CMD_LKO1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT1 }, + { GE_CMD_LKO2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT2 }, + { GE_CMD_LKO3, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT3 }, + + { GE_CMD_LAC0, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT0 }, + { GE_CMD_LDC0, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT0 }, + { GE_CMD_LSC0, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT0 }, + { GE_CMD_LAC1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT1 }, + { GE_CMD_LDC1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT1 }, + { GE_CMD_LSC1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT1 }, + { GE_CMD_LAC2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT2 }, + { GE_CMD_LDC2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT2 }, + { GE_CMD_LSC2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT2 }, + { GE_CMD_LAC3, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT3 }, + { GE_CMD_LDC3, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT3 }, + { GE_CMD_LSC3, FLAG_FLUSHBEFOREONCHANGE, DIRTY_LIGHT3 }, + + // Ignored commands + { GE_CMD_CLIPENABLE, 0 }, + { GE_CMD_TEXFLUSH, 0 }, + { GE_CMD_TEXLODSLOPE, 0 }, + { GE_CMD_TEXSYNC, 0 }, + + // These are just nop or part of other later commands. + { GE_CMD_NOP, 0 }, + { GE_CMD_BASE, 0 }, + { GE_CMD_TRANSFERSRC, 0 }, + { GE_CMD_TRANSFERSRCW, 0 }, + { GE_CMD_TRANSFERDST, 0 }, + { GE_CMD_TRANSFERDSTW, 0 }, + { GE_CMD_TRANSFERSRCPOS, 0 }, + { GE_CMD_TRANSFERDSTPOS, 0 }, + { GE_CMD_TRANSFERSIZE, 0 }, + + // From Common. No flushing but definitely need execute. + { GE_CMD_OFFSETADDR, FLAG_EXECUTE, 0, &GPUCommon::Execute_OffsetAddr }, + { GE_CMD_ORIGIN, FLAG_EXECUTE | FLAG_READS_PC, 0, &GPUCommon::Execute_Origin }, + { GE_CMD_PRIM, FLAG_EXECUTE, 0, &GPU_D3D11::Execute_Prim }, + { GE_CMD_JUMP, FLAG_EXECUTE | FLAG_READS_PC | FLAG_WRITES_PC, 0, &GPUCommon::Execute_Jump }, + { GE_CMD_CALL, FLAG_EXECUTE | FLAG_READS_PC | FLAG_WRITES_PC, 0, &GPUCommon::Execute_Call }, + { GE_CMD_RET, FLAG_EXECUTE | FLAG_READS_PC | FLAG_WRITES_PC, 0, &GPUCommon::Execute_Ret }, + { GE_CMD_END, FLAG_FLUSHBEFORE | FLAG_EXECUTE | FLAG_READS_PC | FLAG_WRITES_PC, 0, &GPUCommon::Execute_End }, + { GE_CMD_VADDR, FLAG_EXECUTE, 0, &GPU_D3D11::Execute_Vaddr }, + { GE_CMD_IADDR, FLAG_EXECUTE, 0, &GPU_D3D11::Execute_Iaddr }, + { GE_CMD_BJUMP, FLAG_EXECUTE | FLAG_READS_PC | FLAG_WRITES_PC, 0, &GPU_D3D11::Execute_BJump }, // EXECUTE + { GE_CMD_BOUNDINGBOX, FLAG_EXECUTE, 0, &GPU_D3D11::Execute_BoundingBox }, // + FLUSHBEFORE when we implement... or not, do we need to? + + // Changing the vertex type requires us to flush. + { GE_CMD_VERTEXTYPE, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, 0, &GPU_D3D11::Execute_VertexType }, + + { GE_CMD_BEZIER, FLAG_FLUSHBEFORE | FLAG_EXECUTE, 0, &GPU_D3D11::Execute_Bezier }, + { GE_CMD_SPLINE, FLAG_FLUSHBEFORE | FLAG_EXECUTE, 0, &GPU_D3D11::Execute_Spline }, + + // These two are actually processed in CMD_END. + { GE_CMD_SIGNAL, FLAG_FLUSHBEFORE }, + { GE_CMD_FINISH, FLAG_FLUSHBEFORE }, + + // Changes that trigger data copies. Only flushing on change for LOADCLUT must be a bit of a hack... + { GE_CMD_LOADCLUT, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTE, 0, &GPU_D3D11::Execute_LoadClut }, + { GE_CMD_TRANSFERSTART, FLAG_FLUSHBEFORE | FLAG_EXECUTE | FLAG_READS_PC, 0, &GPUCommon::Execute_BlockTransferStart }, + + // We don't use the dither table. + { GE_CMD_DITH0 }, + { GE_CMD_DITH1 }, + { GE_CMD_DITH2 }, + { GE_CMD_DITH3 }, + + // These handle their own flushing. + { GE_CMD_WORLDMATRIXNUMBER, FLAG_EXECUTE | FLAG_READS_PC | FLAG_WRITES_PC, 0, &GPUCommon::Execute_WorldMtxNum }, + { GE_CMD_WORLDMATRIXDATA, FLAG_EXECUTE, 0, &GPUCommon::Execute_WorldMtxData }, + { GE_CMD_VIEWMATRIXNUMBER, FLAG_EXECUTE | FLAG_READS_PC | FLAG_WRITES_PC, 0, &GPUCommon::Execute_ViewMtxNum }, + { GE_CMD_VIEWMATRIXDATA, FLAG_EXECUTE, 0, &GPUCommon::Execute_ViewMtxData }, + { GE_CMD_PROJMATRIXNUMBER, FLAG_EXECUTE | FLAG_READS_PC | FLAG_WRITES_PC, 0, &GPUCommon::Execute_ProjMtxNum }, + { GE_CMD_PROJMATRIXDATA, FLAG_EXECUTE, 0, &GPUCommon::Execute_ProjMtxData }, + { GE_CMD_TGENMATRIXNUMBER, FLAG_EXECUTE | FLAG_READS_PC | FLAG_WRITES_PC, 0, &GPUCommon::Execute_TgenMtxNum }, + { GE_CMD_TGENMATRIXDATA, FLAG_EXECUTE, 0, &GPUCommon::Execute_TgenMtxData }, + { GE_CMD_BONEMATRIXNUMBER, FLAG_EXECUTE | FLAG_READS_PC | FLAG_WRITES_PC, 0, &GPUCommon::Execute_BoneMtxNum }, + { GE_CMD_BONEMATRIXDATA, FLAG_EXECUTE, 0, &GPUCommon::Execute_BoneMtxData }, + + // Vertex Screen/Texture/Color + { GE_CMD_VSCX, FLAG_EXECUTE, 0, &GPUCommon::Execute_Unknown }, + { GE_CMD_VSCY, FLAG_EXECUTE, 0, &GPUCommon::Execute_Unknown }, + { GE_CMD_VSCZ, FLAG_EXECUTE, 0, &GPUCommon::Execute_Unknown }, + { GE_CMD_VTCS, FLAG_EXECUTE, 0, &GPUCommon::Execute_Unknown }, + { GE_CMD_VTCT, FLAG_EXECUTE, 0, &GPUCommon::Execute_Unknown }, + { GE_CMD_VTCQ, FLAG_EXECUTE, 0, &GPUCommon::Execute_Unknown }, + { GE_CMD_VCV, FLAG_EXECUTE, 0, &GPUCommon::Execute_Unknown }, + { GE_CMD_VAP, FLAG_EXECUTE, 0, &GPUCommon::Execute_Unknown }, + { GE_CMD_VFC, FLAG_EXECUTE, 0, &GPUCommon::Execute_Unknown }, + { GE_CMD_VSCV, FLAG_EXECUTE, 0, &GPUCommon::Execute_Unknown }, + + // "Missing" commands (gaps in the sequence) + { GE_CMD_UNKNOWN_03, FLAG_EXECUTE, 0, &GPUCommon::Execute_Unknown }, + { GE_CMD_UNKNOWN_0D, FLAG_EXECUTE, 0, &GPUCommon::Execute_Unknown }, + { GE_CMD_UNKNOWN_11, FLAG_EXECUTE, 0, &GPUCommon::Execute_Unknown }, + { GE_CMD_UNKNOWN_29, FLAG_EXECUTE, 0, &GPUCommon::Execute_Unknown }, + { GE_CMD_UNKNOWN_34, FLAG_EXECUTE, 0, &GPUCommon::Execute_Unknown }, + { GE_CMD_UNKNOWN_35, FLAG_EXECUTE, 0, &GPUCommon::Execute_Unknown }, + { GE_CMD_UNKNOWN_39, FLAG_EXECUTE, 0, &GPUCommon::Execute_Unknown }, + { GE_CMD_UNKNOWN_4E, FLAG_EXECUTE, 0, &GPUCommon::Execute_Unknown }, + { GE_CMD_UNKNOWN_4F, FLAG_EXECUTE, 0, &GPUCommon::Execute_Unknown }, + { GE_CMD_UNKNOWN_52, FLAG_EXECUTE, 0, &GPUCommon::Execute_Unknown }, + { GE_CMD_UNKNOWN_59, FLAG_EXECUTE, 0, &GPUCommon::Execute_Unknown }, + { GE_CMD_UNKNOWN_5A, FLAG_EXECUTE, 0, &GPUCommon::Execute_Unknown }, + { GE_CMD_UNKNOWN_B6, FLAG_EXECUTE, 0, &GPUCommon::Execute_Unknown }, + { GE_CMD_UNKNOWN_B7, FLAG_EXECUTE, 0, &GPUCommon::Execute_Unknown }, + { GE_CMD_UNKNOWN_D1, FLAG_EXECUTE, 0, &GPUCommon::Execute_Unknown }, + { GE_CMD_UNKNOWN_ED, FLAG_EXECUTE, 0, &GPUCommon::Execute_Unknown }, + { GE_CMD_UNKNOWN_EF, FLAG_EXECUTE, 0, &GPUCommon::Execute_Unknown }, + { GE_CMD_UNKNOWN_FA, FLAG_EXECUTE, 0, &GPUCommon::Execute_Unknown }, + { GE_CMD_UNKNOWN_FB, FLAG_EXECUTE, 0, &GPUCommon::Execute_Unknown }, + { GE_CMD_UNKNOWN_FC, FLAG_EXECUTE, 0, &GPUCommon::Execute_Unknown }, + { GE_CMD_UNKNOWN_FD, FLAG_EXECUTE, 0, &GPUCommon::Execute_Unknown }, + { GE_CMD_UNKNOWN_FE, FLAG_EXECUTE, 0, &GPUCommon::Execute_Unknown }, + // Appears to be debugging related or something? Hit a lot in GoW. + { GE_CMD_UNKNOWN_FF, 0 }, +}; + +GPU_D3D11::CommandInfo GPU_D3D11::cmdInfo_[256]; + +GPU_D3D11::GPU_D3D11(GraphicsContext *gfxCtx, Draw::DrawContext *draw) + : GPUCommon(gfxCtx, draw), drawEngine_(draw, + (ID3D11Device *)draw->GetNativeObject(Draw::NativeObject::DEVICE), + (ID3D11DeviceContext *)draw->GetNativeObject(Draw::NativeObject::CONTEXT)) { + device_ = (ID3D11Device *)draw->GetNativeObject(Draw::NativeObject::DEVICE); + context_ = (ID3D11DeviceContext *)draw->GetNativeObject(Draw::NativeObject::CONTEXT); + lastVsync_ = g_Config.bVSync ? 1 : 0; + + stockD3D11.Create(device_); + + shaderManagerD3D11_ = new ShaderManagerD3D11(device_, context_); + framebufferManagerD3D11_ = new FramebufferManagerD3D11(draw); + framebufferManager_ = framebufferManagerD3D11_; + textureCacheD3D11_ = new TextureCacheD3D11(draw); + textureCache_ = textureCacheD3D11_; + drawEngineCommon_ = &drawEngine_; + shaderManager_ = shaderManagerD3D11_; + depalShaderCache_ = new DepalShaderCacheD3D11(device_, context_); + drawEngine_.SetShaderManager(shaderManagerD3D11_); + drawEngine_.SetTextureCache(textureCacheD3D11_); + drawEngine_.SetFramebufferManager(framebufferManagerD3D11_); + framebufferManagerD3D11_->Init(); + framebufferManagerD3D11_->SetTextureCache(textureCacheD3D11_); + framebufferManagerD3D11_->SetShaderManager(shaderManagerD3D11_); + framebufferManagerD3D11_->SetDrawEngine(&drawEngine_); + textureCacheD3D11_->SetFramebufferManager(framebufferManagerD3D11_); + textureCacheD3D11_->SetDepalShaderCache(depalShaderCache_); + textureCacheD3D11_->SetShaderManager(shaderManagerD3D11_); + + // Sanity check gstate + if ((int *)&gstate.transferstart - (int *)&gstate != 0xEA) { + ERROR_LOG(G3D, "gstate has drifted out of sync!"); + } + + // Sanity check cmdInfo_ table - no dupes please + std::set dupeCheck; + memset(cmdInfo_, 0, sizeof(cmdInfo_)); + for (size_t i = 0; i < ARRAY_SIZE(commandTable); i++) { + const u8 cmd = commandTable[i].cmd; + if (dupeCheck.find(cmd) != dupeCheck.end()) { + ERROR_LOG(G3D, "Command table Dupe: %02x (%i)", (int)cmd, (int)cmd); + } else { + dupeCheck.insert(cmd); + } + cmdInfo_[cmd].flags |= (uint64_t)commandTable[i].flags | (commandTable[i].dirty << 8); + cmdInfo_[cmd].func = commandTable[i].func; + if ((cmdInfo_[cmd].flags & (FLAG_EXECUTE | FLAG_EXECUTEONCHANGE)) && !cmdInfo_[cmd].func) { + Crash(); + } + } + // Find commands missing from the table. + for (int i = 0; i < 0xEF; i++) { + if (dupeCheck.find((u8)i) == dupeCheck.end()) { + ERROR_LOG(G3D, "Command missing from table: %02x (%i)", i, i); + } + } + + // No need to flush before the tex scale/offset commands if we are baking + // the tex scale/offset into the vertices anyway. + UpdateCmdInfo(); + + BuildReportingInfo(); + + // Some of our defaults are different from hw defaults, let's assert them. + // We restore each frame anyway, but here is convenient for tests. + textureCache_->NotifyConfigChanged(); +} + +GPU_D3D11::~GPU_D3D11() { + delete depalShaderCache_; + framebufferManagerD3D11_->DestroyAllFBOs(true); + delete framebufferManagerD3D11_; + shaderManagerD3D11_->ClearShaders(); + delete shaderManagerD3D11_; + delete textureCacheD3D11_; + draw_->BindPipeline(nullptr); + stockD3D11.Destroy(); +} + +void GPU_D3D11::UpdateCmdInfo() { + if (g_Config.bSoftwareSkinning) { + cmdInfo_[GE_CMD_VERTEXTYPE].flags &= ~FLAG_FLUSHBEFOREONCHANGE; + cmdInfo_[GE_CMD_VERTEXTYPE].func = &GPU_D3D11::Execute_VertexTypeSkinning; + } else { + cmdInfo_[GE_CMD_VERTEXTYPE].flags |= FLAG_FLUSHBEFOREONCHANGE; + cmdInfo_[GE_CMD_VERTEXTYPE].func = &GPU_D3D11::Execute_VertexType; + } + + CheckGPUFeatures(); +} + +void GPU_D3D11::CheckGPUFeatures() { + u32 features = 0; + + features |= GPU_SUPPORTS_BLEND_MINMAX; + features |= GPU_SUPPORTS_TEXTURE_LOD_CONTROL; + features |= GPU_PREFER_CPU_DOWNLOAD; + features |= GPU_SUPPORTS_ACCURATE_DEPTH; + ///features |= GPU_SUPPORTS_ANISOTROPY; + features |= GPU_SUPPORTS_OES_TEXTURE_NPOT; + features |= GPU_SUPPORTS_DUALSOURCE_BLEND; + + // features |= GPU_SUPPORTS_ANY_COPY_IMAGE; + + if (!g_Config.bHighQualityDepth) { + features |= GPU_SCALE_DEPTH_FROM_24BIT_TO_16BIT; + } else if (PSP_CoreParameter().compat.flags().PixelDepthRounding) { + // Assume we always have a 24-bit depth buffer. + features |= GPU_SCALE_DEPTH_FROM_24BIT_TO_16BIT; + } else if (PSP_CoreParameter().compat.flags().VertexDepthRounding) { + features |= GPU_ROUND_DEPTH_TO_16BIT; + } + + if (PSP_CoreParameter().compat.flags().ClearToRAM) { + features |= GPU_USE_CLEAR_RAM_HACK; + } + + gstate_c.featureFlags = features; +} + +// Needs to be called on GPU thread, not reporting thread. +void GPU_D3D11::BuildReportingInfo() { + using namespace Draw; + DrawContext *thin3d = gfxCtx_->GetDrawContext(); + + reportingPrimaryInfo_ = thin3d->GetInfoString(InfoField::VENDORSTRING); + reportingFullInfo_ = reportingPrimaryInfo_ + " - " + System_GetProperty(SYSPROP_GPUDRIVER_VERSION) + " - " + thin3d->GetInfoString(InfoField::SHADELANGVERSION); +} + +void GPU_D3D11::DeviceLost() { + // Simply drop all caches and textures. + // FBOs appear to survive? Or no? + shaderManagerD3D11_->ClearShaders(); + textureCacheD3D11_->Clear(false); + framebufferManagerD3D11_->DeviceLost(); +} + +void GPU_D3D11::DeviceRestore() { + // Nothing needed. +} + +void GPU_D3D11::InitClearInternal() { + bool useNonBufferedRendering = g_Config.iRenderingMode == FB_NON_BUFFERED_MODE; + if (useNonBufferedRendering) { + // device_->Clear(0, NULL, D3DCLEAR_STENCIL | D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 0), 1.f, 0); + } +} + +void GPU_D3D11::DumpNextFrame() { + dumpNextFrame_ = true; +} + +void GPU_D3D11::BeginFrame() { + ScheduleEvent(GPU_EVENT_BEGIN_FRAME); + gstate_c.Dirty(DIRTY_PROJTHROUGHMATRIX); +} + +void GPU_D3D11::ReapplyGfxStateInternal() { + GPUCommon::ReapplyGfxStateInternal(); + + // TODO: Dirty our caches for depth states etc +} + +void GPU_D3D11::EndHostFrame() { + // Tell the DrawContext that it's time to reset everything. + draw_->BindPipeline(nullptr); +} + +void GPU_D3D11::BeginFrameInternal() { + if (resized_) { + UpdateCmdInfo(); + drawEngine_.Resized(); + textureCacheD3D11_->NotifyConfigChanged(); + resized_ = false; + } + + textureCacheD3D11_->StartFrame(); + drawEngine_.BeginFrame(); + depalShaderCache_->Decimate(); + // fragmentTestCache_.Decimate(); + + if (dumpNextFrame_) { + NOTICE_LOG(G3D, "DUMPING THIS FRAME"); + dumpThisFrame_ = true; + dumpNextFrame_ = false; + } else if (dumpThisFrame_) { + dumpThisFrame_ = false; + } + shaderManagerD3D11_->DirtyShader(); + + framebufferManagerD3D11_->BeginFrame(); +} + +void GPU_D3D11::SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format) { + host->GPUNotifyDisplay(framebuf, stride, format); + framebufferManagerD3D11_->SetDisplayFramebuffer(framebuf, stride, format); +} + +bool GPU_D3D11::FramebufferDirty() { + // FIXME: Workaround for displaylists sometimes hanging unprocessed. Not yet sure of the cause. + if (ThreadEnabled()) { + // FIXME: Workaround for displaylists sometimes hanging unprocessed. Not yet sure of the cause. + ScheduleEvent(GPU_EVENT_PROCESS_QUEUE); + // Allow it to process fully before deciding if it's dirty. + SyncThread(); + } + VirtualFramebuffer *vfb = framebufferManager_->GetDisplayVFB(); + if (vfb) { + bool dirty = vfb->dirtyAfterDisplay; + vfb->dirtyAfterDisplay = false; + return dirty; + } + return true; +} +bool GPU_D3D11::FramebufferReallyDirty() { + // FIXME: Workaround for displaylists sometimes hanging unprocessed. Not yet sure of the cause. + if (ThreadEnabled()) { + // FIXME: Workaround for displaylists sometimes hanging unprocessed. Not yet sure of the cause. + ScheduleEvent(GPU_EVENT_PROCESS_QUEUE); + // Allow it to process fully before deciding if it's dirty. + SyncThread(); + } + + VirtualFramebuffer *vfb = framebufferManager_->GetDisplayVFB(); + if (vfb) { + bool dirty = vfb->reallyDirtyAfterDisplay; + vfb->reallyDirtyAfterDisplay = false; + return dirty; + } + return true; +} + +void GPU_D3D11::CopyDisplayToOutputInternal() { + float blendColor[4]{}; + context_->OMSetBlendState(stockD3D11.blendStateDisabledWithColorMask[0xF], blendColor, 0xFFFFFFFF); + + drawEngine_.Flush(); + + framebufferManagerD3D11_->CopyDisplayToOutput(); + framebufferManagerD3D11_->EndFrame(); + + // shaderManager_->EndFrame(); + shaderManagerD3D11_->DirtyLastShader(); + + gstate_c.Dirty(DIRTY_TEXTURE_IMAGE); +} + +// Maybe should write this in ASM... +void GPU_D3D11::FastRunLoop(DisplayList &list) { + PROFILE_THIS_SCOPE("gpuloop"); + const CommandInfo *cmdInfo = cmdInfo_; + for (; downcount > 0; --downcount) { + // We know that display list PCs have the upper nibble == 0 - no need to mask the pointer + const u32 op = *(const u32 *)(Memory::base + list.pc); + const u32 cmd = op >> 24; + const CommandInfo info = cmdInfo[cmd]; + const u8 cmdFlags = info.flags; // If we stashed the cmdFlags in the top bits of the cmdmem, we could get away with one table lookup instead of two + const u32 diff = op ^ gstate.cmdmem[cmd]; + // Inlined CheckFlushOp here to get rid of the dumpThisFrame_ check. + if ((cmdFlags & FLAG_FLUSHBEFORE) || (diff && (cmdFlags & FLAG_FLUSHBEFOREONCHANGE))) { + drawEngine_.Flush(); + } + gstate.cmdmem[cmd] = op; // TODO: no need to write if diff==0... + if ((cmdFlags & FLAG_EXECUTE) || (diff && (cmdFlags & FLAG_EXECUTEONCHANGE))) { + (this->*info.func)(op, diff); + } else if (diff) { + uint64_t dirty = info.flags >> 8; + if (dirty) + gstate_c.Dirty(dirty); + } + list.pc += 4; + } +} + +void GPU_D3D11::FinishDeferred() { + // This finishes reading any vertex data that is pending. + drawEngine_.FinishDeferred(); +} + +inline void GPU_D3D11::CheckFlushOp(int cmd, u32 diff) { + const u8 cmdFlags = cmdInfo_[cmd].flags; + if ((cmdFlags & FLAG_FLUSHBEFORE) || (diff && (cmdFlags & FLAG_FLUSHBEFOREONCHANGE))) { + if (dumpThisFrame_) { + NOTICE_LOG(G3D, "================ FLUSH ================"); + } + drawEngine_.Flush(); + } +} + +void GPU_D3D11::PreExecuteOp(u32 op, u32 diff) { + CheckFlushOp(op >> 24, diff); +} + +void GPU_D3D11::ExecuteOp(u32 op, u32 diff) { + const u8 cmd = op >> 24; + const CommandInfo info = cmdInfo_[cmd]; + const u8 cmdFlags = info.flags; + if ((cmdFlags & FLAG_EXECUTE) || (diff && (cmdFlags & FLAG_EXECUTEONCHANGE))) { + (this->*info.func)(op, diff); + } else if (diff) { + uint64_t dirty = info.flags >> 8; + if (dirty) + gstate_c.Dirty(dirty); + } +} + +void GPU_D3D11::Execute_VertexType(u32 op, u32 diff) { + if (diff & (GE_VTYPE_TC_MASK | GE_VTYPE_THROUGH_MASK)) { + gstate_c.Dirty(DIRTY_UVSCALEOFFSET); + } +} + +void GPU_D3D11::Execute_VertexTypeSkinning(u32 op, u32 diff) { + // Don't flush when weight count changes, unless morph is enabled. + if ((diff & ~GE_VTYPE_WEIGHTCOUNT_MASK) || (op & GE_VTYPE_MORPHCOUNT_MASK) != 0) { + // Restore and flush + gstate.vertType ^= diff; + Flush(); + gstate.vertType ^= diff; + if (diff & (GE_VTYPE_TC_MASK | GE_VTYPE_THROUGH_MASK)) + gstate_c.Dirty(DIRTY_UVSCALEOFFSET); + // In this case, we may be doing weights and morphs. + // Update any bone matrix uniforms so it uses them correctly. + if ((op & GE_VTYPE_MORPHCOUNT_MASK) != 0) { + gstate_c.Dirty(gstate_c.deferredVertTypeDirty); + gstate_c.deferredVertTypeDirty = 0; + } + } +} + +void GPU_D3D11::Execute_Prim(u32 op, u32 diff) { + SetDrawType(DRAW_PRIM); + + // This drives all drawing. All other state we just buffer up, then we apply it only + // when it's time to draw. As most PSP games set state redundantly ALL THE TIME, this is a huge optimization. + + u32 data = op & 0xFFFFFF; + u32 count = data & 0xFFFF; + // Upper bits are ignored. + GEPrimitiveType prim = static_cast((data >> 16) & 7); + + if (count == 0) + return; + + // Discard AA lines as we can't do anything that makes sense with these anyway. The SW plugin might, though. + + if (gstate.isAntiAliasEnabled()) { + // Discard AA lines in DOA + if (prim == GE_PRIM_LINE_STRIP) + return; + // Discard AA lines in Summon Night 5 + if ((prim == GE_PRIM_LINES) && gstate.isSkinningEnabled()) + return; + } + + // This also make skipping drawing very effective. + framebufferManagerD3D11_->SetRenderFrameBuffer(gstate_c.IsDirty(DIRTY_FRAMEBUF), gstate_c.skipDrawReason); + if (gstate_c.skipDrawReason & (SKIPDRAW_SKIPFRAME | SKIPDRAW_NON_DISPLAYED_FB)) { + drawEngine_.SetupVertexDecoder(gstate.vertType); + // Rough estimate, not sure what's correct. + cyclesExecuted += EstimatePerVertexCost() * count; + return; + } + + u32 vertexAddr = gstate_c.vertexAddr; + if (!Memory::IsValidAddress(vertexAddr)) { + ERROR_LOG_REPORT(G3D, "Bad vertex address %08x!", vertexAddr); + return; + } + + void *verts = Memory::GetPointerUnchecked(vertexAddr); + void *inds = 0; + u32 vertexType = gstate.vertType; + if ((vertexType & GE_VTYPE_IDX_MASK) != GE_VTYPE_IDX_NONE) { + u32 indexAddr = gstate_c.indexAddr; + if (!Memory::IsValidAddress(indexAddr)) { + ERROR_LOG_REPORT(G3D, "Bad index address %08x!", indexAddr); + return; + } + inds = Memory::GetPointerUnchecked(indexAddr); + } + +#ifndef MOBILE_DEVICE + if (prim > GE_PRIM_RECTANGLES) { + ERROR_LOG_REPORT_ONCE(reportPrim, G3D, "Unexpected prim type: %d", prim); + } +#endif + + int bytesRead = 0; + drawEngine_.SubmitPrim(verts, inds, prim, count, vertexType, &bytesRead); + + int vertexCost = EstimatePerVertexCost() * count; + gpuStats.vertexGPUCycles += vertexCost; + cyclesExecuted += vertexCost; + + // After drawing, we advance the vertexAddr (when non indexed) or indexAddr (when indexed). + // Some games rely on this, they don't bother reloading VADDR and IADDR. + // The VADDR/IADDR registers are NOT updated. + AdvanceVerts(vertexType, count, bytesRead); +} + +void GPU_D3D11::Execute_Bezier(u32 op, u32 diff) { + SetDrawType(DRAW_BEZIER); + + // This also make skipping drawing very effective. + framebufferManagerD3D11_->SetRenderFrameBuffer(gstate_c.IsDirty(DIRTY_FRAMEBUF), gstate_c.skipDrawReason); + if (gstate_c.skipDrawReason & (SKIPDRAW_SKIPFRAME | SKIPDRAW_NON_DISPLAYED_FB)) { + // TODO: Should this eat some cycles? Probably yes. Not sure if important. + return; + } + + if (!Memory::IsValidAddress(gstate_c.vertexAddr)) { + ERROR_LOG_REPORT(G3D, "Bad vertex address %08x!", gstate_c.vertexAddr); + return; + } + + void *control_points = Memory::GetPointerUnchecked(gstate_c.vertexAddr); + void *indices = NULL; + if ((gstate.vertType & GE_VTYPE_IDX_MASK) != GE_VTYPE_IDX_NONE) { + if (!Memory::IsValidAddress(gstate_c.indexAddr)) { + ERROR_LOG_REPORT(G3D, "Bad index address %08x!", gstate_c.indexAddr); + return; + } + indices = Memory::GetPointerUnchecked(gstate_c.indexAddr); + } + + if (gstate.vertType & GE_VTYPE_MORPHCOUNT_MASK) { + DEBUG_LOG_REPORT(G3D, "Bezier + morph: %i", (gstate.vertType & GE_VTYPE_MORPHCOUNT_MASK) >> GE_VTYPE_MORPHCOUNT_SHIFT); + } + if (vertTypeIsSkinningEnabled(gstate.vertType)) { + DEBUG_LOG_REPORT(G3D, "Bezier + skinning: %i", vertTypeGetNumBoneWeights(gstate.vertType)); + } + + GEPatchPrimType patchPrim = gstate.getPatchPrimitiveType(); + int bz_ucount = op & 0xFF; + int bz_vcount = (op >> 8) & 0xFF; + bool computeNormals = gstate.isLightingEnabled(); + bool patchFacing = gstate.patchfacing & 1; + int bytesRead = 0; + drawEngine_.SubmitBezier(control_points, indices, gstate.getPatchDivisionU(), gstate.getPatchDivisionV(), bz_ucount, bz_vcount, patchPrim, computeNormals, patchFacing, gstate.vertType, &bytesRead); + + // After drawing, we advance pointers - see SubmitPrim which does the same. + int count = bz_ucount * bz_vcount; + AdvanceVerts(gstate.vertType, count, bytesRead); +} + +void GPU_D3D11::Execute_Spline(u32 op, u32 diff) { + SetDrawType(DRAW_SPLINE); + + // This also make skipping drawing very effective. + framebufferManagerD3D11_->SetRenderFrameBuffer(gstate_c.IsDirty(DIRTY_FRAMEBUF), gstate_c.skipDrawReason); + if (gstate_c.skipDrawReason & (SKIPDRAW_SKIPFRAME | SKIPDRAW_NON_DISPLAYED_FB)) { + // TODO: Should this eat some cycles? Probably yes. Not sure if important. + return; + } + + if (!Memory::IsValidAddress(gstate_c.vertexAddr)) { + ERROR_LOG_REPORT(G3D, "Bad vertex address %08x!", gstate_c.vertexAddr); + return; + } + + void *control_points = Memory::GetPointerUnchecked(gstate_c.vertexAddr); + void *indices = NULL; + if ((gstate.vertType & GE_VTYPE_IDX_MASK) != GE_VTYPE_IDX_NONE) { + if (!Memory::IsValidAddress(gstate_c.indexAddr)) { + ERROR_LOG_REPORT(G3D, "Bad index address %08x!", gstate_c.indexAddr); + return; + } + indices = Memory::GetPointerUnchecked(gstate_c.indexAddr); + } + + if (gstate.vertType & GE_VTYPE_MORPHCOUNT_MASK) { + DEBUG_LOG_REPORT(G3D, "Spline + morph: %i", (gstate.vertType & GE_VTYPE_MORPHCOUNT_MASK) >> GE_VTYPE_MORPHCOUNT_SHIFT); + } + if (vertTypeIsSkinningEnabled(gstate.vertType)) { + DEBUG_LOG_REPORT(G3D, "Spline + skinning: %i", vertTypeGetNumBoneWeights(gstate.vertType)); + } + + int sp_ucount = op & 0xFF; + int sp_vcount = (op >> 8) & 0xFF; + int sp_utype = (op >> 16) & 0x3; + int sp_vtype = (op >> 18) & 0x3; + GEPatchPrimType patchPrim = gstate.getPatchPrimitiveType(); + bool computeNormals = gstate.isLightingEnabled(); + bool patchFacing = gstate.patchfacing & 1; + u32 vertType = gstate.vertType; + int bytesRead = 0; + drawEngine_.SubmitSpline(control_points, indices, gstate.getPatchDivisionU(), gstate.getPatchDivisionV(), sp_ucount, sp_vcount, sp_utype, sp_vtype, patchPrim, computeNormals, patchFacing, vertType, &bytesRead); + + // After drawing, we advance pointers - see SubmitPrim which does the same. + int count = sp_ucount * sp_vcount; + AdvanceVerts(gstate.vertType, count, bytesRead); +} + +void GPU_D3D11::Execute_TexSize0(u32 op, u32 diff) { + // Render to texture may have overridden the width/height. + // Don't reset it unless the size is different / the texture has changed. + if (diff || gstate_c.IsDirty(DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS)) { + gstate_c.curTextureWidth = gstate.getTextureWidth(0); + gstate_c.curTextureHeight = gstate.getTextureHeight(0); + gstate_c.Dirty(DIRTY_UVSCALEOFFSET); + // We will need to reset the texture now. + gstate_c.Dirty(DIRTY_TEXTURE_PARAMS); + } +} + +void GPU_D3D11::Execute_LoadClut(u32 op, u32 diff) { + gstate_c.Dirty(DIRTY_TEXTURE_PARAMS); + textureCacheD3D11_->LoadClut(gstate.getClutAddress(), gstate.getClutLoadBytes()); + // This could be used to "dirty" textures with clut. +} + +void GPU_D3D11::GetStats(char *buffer, size_t bufsize) { + float vertexAverageCycles = gpuStats.numVertsSubmitted > 0 ? (float)gpuStats.vertexGPUCycles / (float)gpuStats.numVertsSubmitted : 0.0f; + snprintf(buffer, bufsize - 1, + "DL processing time: %0.2f ms\n" + "Draw calls: %i, flushes %i\n" + "Cached Draw calls: %i\n" + "Num Tracked Vertex Arrays: %i\n" + "GPU cycles executed: %d (%f per vertex)\n" + "Commands per call level: %i %i %i %i\n" + "Vertices submitted: %i\n" + "Cached, Uncached Vertices Drawn: %i, %i\n" + "FBOs active: %i\n" + "Textures active: %i, decoded: %i invalidated: %i\n" + "Vertex, Fragment shaders loaded: %i, %i\n", + gpuStats.msProcessingDisplayLists * 1000.0f, + gpuStats.numDrawCalls, + gpuStats.numFlushes, + gpuStats.numCachedDrawCalls, + gpuStats.numTrackedVertexArrays, + gpuStats.vertexGPUCycles + gpuStats.otherGPUCycles, + vertexAverageCycles, + gpuStats.gpuCommandsAtCallLevel[0], gpuStats.gpuCommandsAtCallLevel[1], gpuStats.gpuCommandsAtCallLevel[2], gpuStats.gpuCommandsAtCallLevel[3], + gpuStats.numVertsSubmitted, + gpuStats.numCachedVertsDrawn, + gpuStats.numUncachedVertsDrawn, + (int)framebufferManagerD3D11_->NumVFBs(), + (int)textureCacheD3D11_->NumLoadedTextures(), + gpuStats.numTexturesDecoded, + gpuStats.numTextureInvalidations, + shaderManagerD3D11_->GetNumVertexShaders(), + shaderManagerD3D11_->GetNumFragmentShaders() + ); +} + +void GPU_D3D11::ClearCacheNextFrame() { + textureCacheD3D11_->ClearNextFrame(); +} + +void GPU_D3D11::ClearShaderCache() { + shaderManagerD3D11_->ClearShaders(); +} + +std::vector GPU_D3D11::GetFramebufferList() { + return framebufferManagerD3D11_->GetFramebufferList(); +} + +void GPU_D3D11::DoState(PointerWrap &p) { + GPUCommon::DoState(p); + + // TODO: Some of these things may not be necessary. + // None of these are necessary when saving. + if (p.mode == p.MODE_READ) { + textureCacheD3D11_->Clear(true); + drawEngine_.ClearTrackedVertexArrays(); + + gstate_c.Dirty(DIRTY_TEXTURE_IMAGE); + framebufferManagerD3D11_->DestroyAllFBOs(true); + shaderManagerD3D11_->ClearShaders(); + } +} + +bool GPU_D3D11::GetCurrentFramebuffer(GPUDebugBuffer &buffer, GPUDebugFramebufferType type, int maxRes) { + return framebufferManagerD3D11_->GetCurrentFramebuffer(buffer, type, maxRes); +} + +bool GPU_D3D11::GetCurrentDepthbuffer(GPUDebugBuffer &buffer) { + return framebufferManagerD3D11_->GetCurrentDepthbuffer(buffer); +} + +bool GPU_D3D11::GetCurrentStencilbuffer(GPUDebugBuffer &buffer) { + return framebufferManagerD3D11_->GetCurrentStencilbuffer(buffer); +} + +bool GPU_D3D11::GetCurrentTexture(GPUDebugBuffer &buffer, int level) { + if (!gstate.isTextureMapEnabled()) { + return false; + } + + // TODO: Implement! + + return false; +} + +bool GPU_D3D11::GetCurrentClut(GPUDebugBuffer &buffer) { + return textureCacheD3D11_->GetCurrentClutBuffer(buffer); +} + +bool GPU_D3D11::GetOutputFramebuffer(GPUDebugBuffer &buffer) { + return framebufferManagerD3D11_->GetOutputFramebuffer(buffer); +} + +bool GPU_D3D11::GetCurrentSimpleVertices(int count, std::vector &vertices, std::vector &indices) { + return drawEngine_.GetCurrentSimpleVertices(count, vertices, indices); +} + +std::vector GPU_D3D11::DebugGetShaderIDs(DebugShaderType type) { + if (type == SHADER_TYPE_VERTEXLOADER) { + return drawEngine_.DebugGetVertexLoaderIDs(); + } else { + return shaderManagerD3D11_->DebugGetShaderIDs(type); + } +} + +std::string GPU_D3D11::DebugGetShaderString(std::string id, DebugShaderType type, DebugShaderStringType stringType) { + if (type == SHADER_TYPE_VERTEXLOADER) { + return drawEngine_.DebugGetVertexLoaderString(id, stringType); + } else { + return shaderManagerD3D11_->DebugGetShaderString(id, type, stringType); + } +} diff --git a/GPU/D3D11/GPU_D3D11.h b/GPU/D3D11/GPU_D3D11.h new file mode 100644 index 0000000000..a618462a57 --- /dev/null +++ b/GPU/D3D11/GPU_D3D11.h @@ -0,0 +1,128 @@ +// Copyright (c) 2017- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program 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 General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +#include +#include +#include + +#include "GPU/GPUCommon.h" +#include "GPU/D3D11/FramebufferManagerD3D11.h" +#include "GPU/D3D11/DrawEngineD3D11.h" +#include "GPU/D3D11/TextureCacheD3D11.h" +#include "GPU/D3D11/DepalettizeShaderD3D11.h" +#include "GPU/Common/VertexDecoderCommon.h" + +class ShaderManagerD3D11; +class LinkedShaderD3D11; + +class GPU_D3D11 : public GPUCommon { +public: + GPU_D3D11(GraphicsContext *gfxCtx, Draw::DrawContext *draw); + ~GPU_D3D11(); + + void CheckGPUFeatures(); + void PreExecuteOp(u32 op, u32 diff) override; + void ExecuteOp(u32 op, u32 diff) override; + + void ReapplyGfxStateInternal() override; + void SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format) override; + void BeginFrame() override; + void GetStats(char *buffer, size_t bufsize) override; + void ClearCacheNextFrame() override; + void DeviceLost() override; // Only happens on Android. Drop all textures and shaders. + void DeviceRestore() override; + + void DumpNextFrame() override; + void DoState(PointerWrap &p) override; + + void ClearShaderCache() override; + bool DecodeTexture(u8 *dest, const GPUgstate &state) override { + return false; + } + bool FramebufferDirty() override; + bool FramebufferReallyDirty() override; + + void GetReportingInfo(std::string &primaryInfo, std::string &fullInfo) override { + primaryInfo = reportingPrimaryInfo_; + fullInfo = reportingFullInfo_; + } + std::vector GetFramebufferList() override; + + bool GetCurrentFramebuffer(GPUDebugBuffer &buffer, GPUDebugFramebufferType type, int maxRes = -1) override; + bool GetCurrentDepthbuffer(GPUDebugBuffer &buffer) override; + bool GetCurrentStencilbuffer(GPUDebugBuffer &buffer) override; + bool GetCurrentTexture(GPUDebugBuffer &buffer, int level) override; + bool GetCurrentClut(GPUDebugBuffer &buffer) override; + bool GetOutputFramebuffer(GPUDebugBuffer &buffer) override; + bool GetCurrentSimpleVertices(int count, std::vector &vertices, std::vector &indices) override; + + typedef void (GPU_D3D11::*CmdFunc)(u32 op, u32 diff); + struct CommandInfo { + uint64_t flags; + GPU_D3D11::CmdFunc func; + }; + + void Execute_Prim(u32 op, u32 diff); + void Execute_Bezier(u32 op, u32 diff); + void Execute_Spline(u32 op, u32 diff); + void Execute_VertexType(u32 op, u32 diff); + void Execute_VertexTypeSkinning(u32 op, u32 diff); + void Execute_TexSize0(u32 op, u32 diff); + void Execute_LoadClut(u32 op, u32 diff); + + // Using string because it's generic - makes no assumptions on the size of the shader IDs of this backend. + std::vector DebugGetShaderIDs(DebugShaderType shader) override; + std::string DebugGetShaderString(std::string id, DebugShaderType shader, DebugShaderStringType stringType) override; + + void EndHostFrame() override; + +protected: + void FastRunLoop(DisplayList &list) override; + void FinishDeferred() override; + +private: + void UpdateCmdInfo(); + + void Flush() { + drawEngine_.Flush(); + } + // void ApplyDrawState(int prim); + void CheckFlushOp(int cmd, u32 diff); + void BuildReportingInfo(); + + void InitClearInternal() override; + void BeginFrameInternal() override; + void CopyDisplayToOutputInternal() override; + + ID3D11Device *device_; + ID3D11DeviceContext *context_; + + FramebufferManagerD3D11 *framebufferManagerD3D11_; + TextureCacheD3D11 *textureCacheD3D11_; + DepalShaderCacheD3D11 *depalShaderCache_; + DrawEngineD3D11 drawEngine_; + ShaderManagerD3D11 *shaderManagerD3D11_; + + static CommandInfo cmdInfo_[256]; + + int lastVsync_; + + std::string reportingPrimaryInfo_; + std::string reportingFullInfo_; +}; \ No newline at end of file diff --git a/GPU/D3D11/ShaderManagerD3D11.cpp b/GPU/D3D11/ShaderManagerD3D11.cpp new file mode 100644 index 0000000000..66067539aa --- /dev/null +++ b/GPU/D3D11/ShaderManagerD3D11.cpp @@ -0,0 +1,277 @@ +// Copyright (c) 2015- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program 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 General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include +#include + +#include + +#include "base/logging.h" +#include "math/lin/matrix4x4.h" +#include "math/math_util.h" +#include "math/dataconv.h" +#include "util/text/utf8.h" +#include "thin3d/d3d11_loader.h" +#include "Common/Common.h" +#include "Core/Config.h" +#include "Core/Reporting.h" +#include "GPU/Math3D.h" +#include "GPU/GPUState.h" +#include "GPU/ge_constants.h" +#include "GPU/D3D11/ShaderManagerD3D11.h" +#include "GPU/D3D11/FragmentShaderGeneratorD3D11.h" +#include "GPU/D3D11/VertexShaderGeneratorD3D11.h" +#include "GPU/D3D11/D3D11Util.h" + +D3D11FragmentShader::D3D11FragmentShader(ID3D11Device *device, ShaderID id, const char *code, bool useHWTransform) + : device_(device), id_(id), failed_(false), useHWTransform_(useHWTransform), module_(0) { + source_ = code; + + module_ = CreatePixelShaderD3D11(device, code, strlen(code)); + if (!module_) + failed_ = true; +} + +D3D11FragmentShader::~D3D11FragmentShader() { + if (module_) + module_->Release(); +} + +std::string D3D11FragmentShader::GetShaderString(DebugShaderStringType type) const { + switch (type) { + case SHADER_STRING_SOURCE_CODE: + return source_; + case SHADER_STRING_SHORT_DESC: + return FragmentShaderDesc(id_); + default: + return "N/A"; + } +} + +D3D11VertexShader::D3D11VertexShader(ID3D11Device *device, ShaderID id, const char *code, int vertType, bool useHWTransform, bool usesLighting) + : device_(device), id_(id), failed_(false), useHWTransform_(useHWTransform), module_(nullptr), usesLighting_(usesLighting) { + source_ = code; + + module_ = CreateVertexShaderD3D11(device, code, strlen(code), &bytecode_); + if (!module_) + failed_ = true; +} + +D3D11VertexShader::~D3D11VertexShader() { + if (module_) + module_->Release(); +} + +std::string D3D11VertexShader::GetShaderString(DebugShaderStringType type) const { + switch (type) { + case SHADER_STRING_SOURCE_CODE: + return source_; + case SHADER_STRING_SHORT_DESC: + return VertexShaderDesc(id_); + default: + return "N/A"; + } +} + +ShaderManagerD3D11::ShaderManagerD3D11(ID3D11Device *device, ID3D11DeviceContext *context) + : device_(device), context_(context), lastVShader_(nullptr), lastFShader_(nullptr) { + codeBuffer_ = new char[16384]; + memset(&ub_base, 0, sizeof(ub_base)); + memset(&ub_lights, 0, sizeof(ub_lights)); + memset(&ub_bones, 0, sizeof(ub_bones)); + + ILOG("sizeof(ub_base): %d", (int)sizeof(ub_base)); + ILOG("sizeof(ub_lights): %d", (int)sizeof(ub_lights)); + ILOG("sizeof(ub_bones): %d", (int)sizeof(ub_bones)); + + D3D11_BUFFER_DESC desc{sizeof(ub_base), D3D11_USAGE_DYNAMIC, D3D11_BIND_CONSTANT_BUFFER, D3D11_CPU_ACCESS_WRITE }; + device_->CreateBuffer(&desc, nullptr, &push_base); + desc.ByteWidth = sizeof(ub_lights); + device_->CreateBuffer(&desc, nullptr, &push_lights); + desc.ByteWidth = sizeof(ub_bones); + device_->CreateBuffer(&desc, nullptr, &push_bones); +} + +ShaderManagerD3D11::~ShaderManagerD3D11() { + ClearShaders(); + delete[] codeBuffer_; +} + +void ShaderManagerD3D11::Clear() { + for (auto iter = fsCache_.begin(); iter != fsCache_.end(); ++iter) { + delete iter->second; + } + for (auto iter = vsCache_.begin(); iter != vsCache_.end(); ++iter) { + delete iter->second; + } + fsCache_.clear(); + vsCache_.clear(); + lastFSID_.clear(); + lastVSID_.clear(); +} + +void ShaderManagerD3D11::ClearShaders() { + Clear(); + DirtyShader(); + gstate_c.Dirty(DIRTY_ALL_UNIFORMS); +} + +void ShaderManagerD3D11::DirtyShader() { + // Forget the last shader ID + lastFSID_.clear(); + lastVSID_.clear(); + lastVShader_ = nullptr; + lastFShader_ = nullptr; +} + +void ShaderManagerD3D11::DirtyLastShader() { // disables vertex arrays + lastVShader_ = nullptr; + lastFShader_ = nullptr; +} + +uint64_t ShaderManagerD3D11::UpdateUniforms() { + uint64_t dirty = gstate_c.GetDirtyUniforms(); + if (dirty != 0) { + D3D11_MAPPED_SUBRESOURCE map; + if (dirty & DIRTY_BASE_UNIFORMS) { + BaseUpdateUniforms(&ub_base, dirty, true); + context_->Map(push_base, 0, D3D11_MAP_WRITE_DISCARD, 0, &map); + memcpy(map.pData, &ub_base, sizeof(ub_base)); + context_->Unmap(push_base, 0); + } + if (dirty & DIRTY_LIGHT_UNIFORMS) { + LightUpdateUniforms(&ub_lights, dirty); + context_->Map(push_lights, 0, D3D11_MAP_WRITE_DISCARD, 0, &map); + memcpy(map.pData, &ub_lights, sizeof(ub_lights)); + context_->Unmap(push_lights, 0); + } + if (dirty & DIRTY_BONE_UNIFORMS) { + BoneUpdateUniforms(&ub_bones, dirty); + context_->Map(push_bones, 0, D3D11_MAP_WRITE_DISCARD, 0, &map); + memcpy(map.pData, &ub_bones, sizeof(ub_bones)); + context_->Unmap(push_bones, 0); + } + } + gstate_c.CleanUniforms(); + return dirty; +} + +void ShaderManagerD3D11::BindUniforms() { + ID3D11Buffer *vs_cbs[3] = { push_base, push_lights, push_bones }; + ID3D11Buffer *ps_cbs[1] = { push_base }; + context_->VSSetConstantBuffers(0, 3, vs_cbs); + context_->PSSetConstantBuffers(0, 1, ps_cbs); +} + +void ShaderManagerD3D11::GetShaders(int prim, u32 vertType, D3D11VertexShader **vshader, D3D11FragmentShader **fshader, bool useHWTransform) { + ShaderID VSID; + ShaderID FSID; + ComputeVertexShaderID(&VSID, vertType, useHWTransform); + ComputeFragmentShaderID(&FSID); + + // Just update uniforms if this is the same shader as last time. + if (lastVShader_ != nullptr && lastFShader_ != nullptr && VSID == lastVSID_ && FSID == lastFSID_) { + *vshader = lastVShader_; + *fshader = lastFShader_; + // Already all set, no need to look up in shader maps. + return; + } + + VSCache::iterator vsIter = vsCache_.find(VSID); + D3D11VertexShader *vs; + if (vsIter == vsCache_.end()) { + // Vertex shader not in cache. Let's compile it. + bool usesLighting; + GenerateVertexShaderD3D11(VSID, codeBuffer_, &usesLighting); + vs = new D3D11VertexShader(device_, VSID, codeBuffer_, vertType, useHWTransform, usesLighting); + vsCache_[VSID] = vs; + } else { + vs = vsIter->second; + } + lastVSID_ = VSID; + + FSCache::iterator fsIter = fsCache_.find(FSID); + D3D11FragmentShader *fs; + if (fsIter == fsCache_.end()) { + // Fragment shader not in cache. Let's compile it. + GenerateFragmentShaderD3D11(FSID, codeBuffer_); + fs = new D3D11FragmentShader(device_, FSID, codeBuffer_, useHWTransform); + fsCache_[FSID] = fs; + } else { + fs = fsIter->second; + } + + lastFSID_ = FSID; + + lastVShader_ = vs; + lastFShader_ = fs; + + *vshader = vs; + *fshader = fs; +} + +std::vector ShaderManagerD3D11::DebugGetShaderIDs(DebugShaderType type) { + std::string id; + std::vector ids; + switch (type) { + case SHADER_TYPE_VERTEX: + { + for (auto iter : vsCache_) { + iter.first.ToString(&id); + ids.push_back(id); + } + break; + } + case SHADER_TYPE_FRAGMENT: + { + for (auto iter : fsCache_) { + iter.first.ToString(&id); + ids.push_back(id); + } + break; + } + default: + break; + } + return ids; +} + +std::string ShaderManagerD3D11::DebugGetShaderString(std::string id, DebugShaderType type, DebugShaderStringType stringType) { + ShaderID shaderId; + shaderId.FromString(id); + switch (type) { + case SHADER_TYPE_VERTEX: + { + auto iter = vsCache_.find(shaderId); + if (iter == vsCache_.end()) { + return ""; + } + return iter->second->GetShaderString(stringType); + } + + case SHADER_TYPE_FRAGMENT: + { + auto iter = fsCache_.find(shaderId); + if (iter == fsCache_.end()) { + return ""; + } + return iter->second->GetShaderString(stringType); + } + default: + return "N/A"; + } +} diff --git a/GPU/D3D11/ShaderManagerD3D11.h b/GPU/D3D11/ShaderManagerD3D11.h new file mode 100644 index 0000000000..aaface9dd9 --- /dev/null +++ b/GPU/D3D11/ShaderManagerD3D11.h @@ -0,0 +1,153 @@ +// Copyright (c) 2017- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program 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 General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +#include + +#include + +#include "base/basictypes.h" +#include "Globals.h" +#include "GPU/Common/ShaderCommon.h" +#include "GPU/Common/ShaderId.h" +// #include "GPU/DX9/VertexShaderGeneratorD3D11.h" +// #include "GPU/DX9/FragmentShaderGeneratorD3D11.h" +#include "math/lin/matrix4x4.h" +#include "GPU/Common/ShaderUniforms.h" + +class D3D11Context; +class D3D11PushBuffer; + +class D3D11FragmentShader { +public: + D3D11FragmentShader(ID3D11Device *device, ShaderID id, const char *code, bool useHWTransform); + ~D3D11FragmentShader(); + + const std::string &source() const { return source_; } + + bool Failed() const { return failed_; } + bool UseHWTransform() const { return useHWTransform_; } + + std::string GetShaderString(DebugShaderStringType type) const; + ID3D11PixelShader *GetShader() const { return module_; } + +protected: + ID3D11PixelShader *module_; + + ID3D11Device *device_; + std::string source_; + bool failed_; + bool useHWTransform_; + ShaderID id_; +}; + +class D3D11VertexShader { +public: + D3D11VertexShader(ID3D11Device *device, ShaderID id, const char *code, int vertType, bool useHWTransform, bool usesLighting); + ~D3D11VertexShader(); + + const std::string &source() const { return source_; } + const std::vector &bytecode() const { return bytecode_; } + bool Failed() const { return failed_; } + bool UseHWTransform() const { return useHWTransform_; } + bool HasBones() const { + return id_.Bit(VS_BIT_ENABLE_BONES); + } + bool HasLights() const { + return usesLighting_; + } + + std::string GetShaderString(DebugShaderStringType type) const; + ID3D11VertexShader *GetShader() const { return module_; } + +protected: + ID3D11VertexShader *module_; + + ID3D11Device *device_; + std::string source_; + std::vector bytecode_; + + bool failed_; + bool useHWTransform_; + bool usesLighting_; + ShaderID id_; +}; + +class D3D11PushBuffer; + +class ShaderManagerD3D11 : public ShaderManagerCommon { +public: + ShaderManagerD3D11(ID3D11Device *device, ID3D11DeviceContext *context); + ~ShaderManagerD3D11(); + + void GetShaders(int prim, u32 vertType, D3D11VertexShader **vshader, D3D11FragmentShader **fshader, bool useHWTransform); + void ClearShaders(); + void DirtyShader(); + void DirtyLastShader(); + + int GetNumVertexShaders() const { return (int)vsCache_.size(); } + int GetNumFragmentShaders() const { return (int)fsCache_.size(); } + + std::vector DebugGetShaderIDs(DebugShaderType type); + std::string DebugGetShaderString(std::string id, DebugShaderType type, DebugShaderStringType stringType); + + uint64_t UpdateUniforms(); + void BindUniforms(); + + // TODO: Avoid copying these buffers if same as last draw, can still point to it assuming we're still in the same pushbuffer. + // Applies dirty changes and copies the buffer. + bool IsBaseDirty() { return true; } + bool IsLightDirty() { return true; } + bool IsBoneDirty() { return true; } + + /* + uint32_t PushBaseBuffer(D3D11PushBuffer *dest, VkBuffer *buf); + uint32_t PushLightBuffer(D3D11PushBuffer *dest, VkBuffer *buf); + uint32_t PushBoneBuffer(D3D11PushBuffer *dest, VkBuffer *buf); + */ + +private: + void Clear(); + + ID3D11Device *device_; + ID3D11DeviceContext *context_; + + typedef std::map FSCache; + FSCache fsCache_; + + typedef std::map VSCache; + VSCache vsCache_; + + char *codeBuffer_; + + // Uniform block scratchpad. These (the relevant ones) are copied to the current pushbuffer at draw time. + UB_VS_FS_Base ub_base; + UB_VS_Lights ub_lights; + UB_VS_Bones ub_bones; + + // Not actual pushbuffers, requires D3D11.1, let's try to live without that first. + ID3D11Buffer *push_base; + ID3D11Buffer *push_lights; + ID3D11Buffer *push_bones; + + D3D11FragmentShader *lastFShader_; + D3D11VertexShader *lastVShader_; + + ShaderID lastFSID_; + ShaderID lastVSID_; +}; \ No newline at end of file diff --git a/GPU/D3D11/StateMappingD3D11.cpp b/GPU/D3D11/StateMappingD3D11.cpp new file mode 100644 index 0000000000..90c448b477 --- /dev/null +++ b/GPU/D3D11/StateMappingD3D11.cpp @@ -0,0 +1,426 @@ +// Copyright (c) 2012- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program 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 General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include + +#include "math/dataconv.h" + +#include "GPU/Math3D.h" +#include "GPU/GPUState.h" +#include "GPU/ge_constants.h" +#include "GPU/Common/GPUStateUtils.h" +#include "Core/System.h" +#include "Core/Config.h" +#include "Core/Reporting.h" + +#include "GPU/Common/FramebufferCommon.h" +#include "GPU/D3D11/DrawEngineD3D11.h" +#include "GPU/D3D11/StateMappingD3D11.h" +#include "GPU/D3D11/FramebufferManagerD3D11.h" +#include "GPU/D3D11/TextureCacheD3D11.h" + +// These tables all fit into u8s. +static const D3D11_BLEND d3d11BlendFactorLookup[(size_t)BlendFactor::COUNT] = { + D3D11_BLEND_ZERO, + D3D11_BLEND_ONE, + D3D11_BLEND_SRC_COLOR, + D3D11_BLEND_INV_SRC_COLOR, + D3D11_BLEND_DEST_COLOR, + D3D11_BLEND_INV_DEST_COLOR, + D3D11_BLEND_SRC_ALPHA, + D3D11_BLEND_INV_SRC_ALPHA, + D3D11_BLEND_DEST_ALPHA, + D3D11_BLEND_INV_DEST_ALPHA, + D3D11_BLEND_BLEND_FACTOR, + D3D11_BLEND_INV_BLEND_FACTOR, + D3D11_BLEND_BLEND_FACTOR, + D3D11_BLEND_INV_BLEND_FACTOR, + D3D11_BLEND_SRC1_COLOR, + D3D11_BLEND_INV_SRC1_COLOR, + D3D11_BLEND_SRC1_ALPHA, + D3D11_BLEND_INV_SRC1_ALPHA, +}; + +static const D3D11_BLEND_OP d3d11BlendEqLookup[(size_t)BlendEq::COUNT] = { + D3D11_BLEND_OP_ADD, + D3D11_BLEND_OP_SUBTRACT, + D3D11_BLEND_OP_REV_SUBTRACT, + D3D11_BLEND_OP_MIN, + D3D11_BLEND_OP_MAX, +}; + +static const D3D11_CULL_MODE cullingMode[] = { + D3D11_CULL_BACK, + D3D11_CULL_FRONT, +}; + +static const D3D11_COMPARISON_FUNC compareOps[] = { + D3D11_COMPARISON_NEVER, + D3D11_COMPARISON_ALWAYS, + D3D11_COMPARISON_EQUAL, + D3D11_COMPARISON_NOT_EQUAL, + D3D11_COMPARISON_LESS, + D3D11_COMPARISON_LESS_EQUAL, + D3D11_COMPARISON_GREATER, + D3D11_COMPARISON_GREATER_EQUAL, +}; + +static const D3D11_STENCIL_OP stencilOps[] = { + D3D11_STENCIL_OP_KEEP, + D3D11_STENCIL_OP_ZERO, + D3D11_STENCIL_OP_REPLACE, + D3D11_STENCIL_OP_INVERT, + D3D11_STENCIL_OP_INCR_SAT, + D3D11_STENCIL_OP_DECR_SAT, + D3D11_STENCIL_OP_KEEP, // reserved + D3D11_STENCIL_OP_KEEP, // reserved +}; + +static const D3D11_PRIMITIVE_TOPOLOGY primToD3D11[8] = { + D3D11_PRIMITIVE_TOPOLOGY_POINTLIST, + D3D11_PRIMITIVE_TOPOLOGY_LINELIST, + D3D11_PRIMITIVE_TOPOLOGY_LINESTRIP, + D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST, + D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, + D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED, // D3D11 doesn't do triangle fans. + D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST, +}; + +/* +static const D3D11_LOGIC_OP logicOps[] = { + D3D11_LOGIC_OP_CLEAR, + D3D11_LOGIC_OP_AND, + D3D11_LOGIC_OP_AND_REVERSE, + D3D11_LOGIC_OP_COPY, + D3D11_LOGIC_OP_AND_INVERTED, + D3D11_LOGIC_OP_NO_OP, + D3D11_LOGIC_OP_XOR, + D3D11_LOGIC_OP_OR, + D3D11_LOGIC_OP_NOR, + D3D11_LOGIC_OP_EQUIVALENT, + D3D11_LOGIC_OP_INVERT, + D3D11_LOGIC_OP_OR_REVERSE, + D3D11_LOGIC_OP_COPY_INVERTED, + D3D11_LOGIC_OP_OR_INVERTED, + D3D11_LOGIC_OP_NAND, + D3D11_LOGIC_OP_SET, +}; +*/ + +static bool ApplyShaderBlending() { + return false; +} + +static void ResetShaderBlending() { + // +} + +class FramebufferManagerD3D11; +class ShaderManagerD3D11; + +void DrawEngineD3D11::ApplyDrawState(int prim) { + memset(&keys_, 0, sizeof(keys_)); + memset(&dynState_, 0, sizeof(dynState_)); + + // Unfortunately, this isn't implemented yet. + gstate_c.allowShaderBlend = false; + + // Set blend - unless we need to do it in the shader. + GenericBlendState blendState; + ConvertBlendState(blendState, gstate_c.allowShaderBlend); + + bool useBufferedRendering = g_Config.iRenderingMode != FB_NON_BUFFERED_MODE; + + if (blendState.applyShaderBlending) { + if (ApplyShaderBlending()) { + // We may still want to do something about stencil -> alpha. + ApplyStencilReplaceAndLogicOp(blendState.replaceAlphaWithStencil, blendState); + } + else { + // Until next time, force it off. + ResetShaderBlending(); + gstate_c.allowShaderBlend = false; + } + } + else if (blendState.resetShaderBlending) { + ResetShaderBlending(); + } + + if (blendState.enabled) { + keys_.blend.blendEnable = true; + keys_.blend.blendOpColor = d3d11BlendEqLookup[(size_t)blendState.eqColor]; + keys_.blend.blendOpAlpha = d3d11BlendEqLookup[(size_t)blendState.eqAlpha]; + keys_.blend.srcColor = d3d11BlendFactorLookup[(size_t)blendState.srcColor]; + keys_.blend.srcAlpha = d3d11BlendFactorLookup[(size_t)blendState.srcAlpha]; + keys_.blend.destColor = d3d11BlendFactorLookup[(size_t)blendState.dstColor]; + keys_.blend.destAlpha = d3d11BlendFactorLookup[(size_t)blendState.dstAlpha]; + if (blendState.dirtyShaderBlend) { + gstate_c.Dirty(DIRTY_SHADERBLEND); + } + dynState_.useBlendColor = blendState.useBlendColor; + if (blendState.useBlendColor) { + dynState_.blendColor = blendState.blendColor; + } + } + else { + keys_.blend.blendEnable = false; + dynState_.useBlendColor = false; + } + + dynState_.useStencil = false; + + // Set ColorMask/Stencil/Depth + if (gstate.isModeClear()) { + keys_.blend.logicOpEnable = false; + keys_.raster.cullMode = D3D11_CULL_NONE; + + keys_.depthStencil.depthTestEnable = true; + keys_.depthStencil.depthCompareOp = D3D11_COMPARISON_ALWAYS; + keys_.depthStencil.depthWriteEnable = gstate.isClearModeDepthMask(); + if (gstate.isClearModeDepthMask()) { + framebufferManager_->SetDepthUpdated(); + } + + // Color Test + bool colorMask = gstate.isClearModeColorMask(); + bool alphaMask = gstate.isClearModeAlphaMask(); + keys_.blend.colorWriteMask = (colorMask ? (1 | 2 | 4) : 0) | (alphaMask ? 8 : 0); + + // Stencil Test + if (alphaMask) { + keys_.depthStencil.stencilTestEnable = true; + keys_.depthStencil.stencilCompareFunc = D3D11_COMPARISON_ALWAYS; + keys_.depthStencil.stencilPassOp = D3D11_STENCIL_OP_REPLACE; + keys_.depthStencil.stencilFailOp = D3D11_STENCIL_OP_REPLACE; + keys_.depthStencil.stencilDepthFailOp = D3D11_STENCIL_OP_REPLACE; + dynState_.useStencil = true; + // In clear mode, the stencil value is set to the alpha value of the vertex. + // A normal clear will be 2 points, the second point has the color. + // We override this value in the pipeline from software transform for clear rectangles. + dynState_.stencilRef = 0xFF; + keys_.depthStencil.stencilWriteMask = 0xFF; + } + else { + keys_.depthStencil.stencilTestEnable = false; + dynState_.useStencil = false; + } + } + else { + if (gstate_c.Supports(GPU_SUPPORTS_LOGIC_OP)) { + // Logic Ops + if (gstate.isLogicOpEnabled() && gstate.getLogicOp() != GE_LOGIC_COPY) { + keys_.blend.logicOpEnable = true; + // key.blendKey.logicOp = logicOps[gstate.getLogicOp()]; + } + else { + keys_.blend.logicOpEnable = false; + } + } + + // Set cull + bool wantCull = !gstate.isModeThrough() && prim != GE_PRIM_RECTANGLES && gstate.isCullEnabled(); + keys_.raster.cullMode = wantCull ? (gstate.getCullMode() ? D3D11_CULL_FRONT : D3D11_CULL_BACK) : D3D11_CULL_NONE; + + // Depth Test + if (gstate.isDepthTestEnabled()) { + keys_.depthStencil.depthTestEnable = true; + keys_.depthStencil.depthCompareOp = compareOps[gstate.getDepthTestFunction()]; + keys_.depthStencil.depthWriteEnable = gstate.isDepthWriteEnabled(); + if (gstate.isDepthWriteEnabled()) { + framebufferManager_->SetDepthUpdated(); + } + } + else { + keys_.depthStencil.depthTestEnable = false; + keys_.depthStencil.depthWriteEnable = false; + keys_.depthStencil.depthCompareOp = D3D11_COMPARISON_ALWAYS; + } + + // PSP color/alpha mask is per bit but we can only support per byte. + // But let's do that, at least. And let's try a threshold. + bool rmask = (gstate.pmskc & 0xFF) < 128; + bool gmask = ((gstate.pmskc >> 8) & 0xFF) < 128; + bool bmask = ((gstate.pmskc >> 16) & 0xFF) < 128; + bool amask = (gstate.pmska & 0xFF) < 128; + +#ifndef MOBILE_DEVICE + u8 abits = (gstate.pmska >> 0) & 0xFF; + u8 rbits = (gstate.pmskc >> 0) & 0xFF; + u8 gbits = (gstate.pmskc >> 8) & 0xFF; + u8 bbits = (gstate.pmskc >> 16) & 0xFF; + if ((rbits != 0 && rbits != 0xFF) || (gbits != 0 && gbits != 0xFF) || (bbits != 0 && bbits != 0xFF)) { + WARN_LOG_REPORT_ONCE(rgbmask, G3D, "Unsupported RGB mask: r=%02x g=%02x b=%02x", rbits, gbits, bbits); + } + if (abits != 0 && abits != 0xFF) { + // The stencil part of the mask is supported. + WARN_LOG_REPORT_ONCE(amask, G3D, "Unsupported alpha/stencil mask: %02x", abits); + } +#endif + + // Let's not write to alpha if stencil isn't enabled. + if (!gstate.isStencilTestEnabled()) { + amask = false; + } + else { + // If the stencil type is set to KEEP, we shouldn't write to the stencil/alpha channel. + if (ReplaceAlphaWithStencilType() == STENCIL_VALUE_KEEP) { + amask = false; + } + } + + keys_.blend.colorWriteMask = (rmask ? 1 : 0) | (gmask ? 2 : 0) | (bmask ? 4 : 0) | (amask ? 8 : 0); + + GenericStencilFuncState stencilState; + ConvertStencilFuncState(stencilState); + + // Stencil Test + if (stencilState.enabled) { + keys_.depthStencil.stencilTestEnable = true; + keys_.depthStencil.stencilCompareFunc = compareOps[stencilState.testFunc]; + keys_.depthStencil.stencilPassOp = stencilOps[stencilState.zPass]; + keys_.depthStencil.stencilFailOp = stencilOps[stencilState.sFail]; + keys_.depthStencil.stencilDepthFailOp = stencilOps[stencilState.zFail]; + keys_.depthStencil.stencilCompareMask = stencilState.testMask; + keys_.depthStencil.stencilWriteMask = stencilState.writeMask; + dynState_.useStencil = true; + dynState_.stencilRef = stencilState.testRef; + } else { + keys_.depthStencil.stencilTestEnable = false; + dynState_.useStencil = false; + } + } + + dynState_.topology = primToD3D11[prim]; + + ViewportAndScissor vpAndScissor; + ConvertViewportAndScissor(useBufferedRendering, + framebufferManager_->GetRenderWidth(), framebufferManager_->GetRenderHeight(), + framebufferManager_->GetTargetBufferWidth(), framebufferManager_->GetTargetBufferHeight(), + vpAndScissor); + + float depthMin = vpAndScissor.depthRangeMin; + float depthMax = vpAndScissor.depthRangeMax; + + if (depthMin < 0.0f) depthMin = 0.0f; + if (depthMax > 1.0f) depthMax = 1.0f; + if (vpAndScissor.dirtyDepth) { + gstate_c.Dirty(DIRTY_DEPTHRANGE); + } + + D3D11_VIEWPORT &vp = dynState_.viewport; + vp.TopLeftX = vpAndScissor.viewportX; + vp.TopLeftY = vpAndScissor.viewportY; + vp.Width = vpAndScissor.viewportW; + vp.Height = vpAndScissor.viewportH; + vp.MinDepth = depthMin; + vp.MaxDepth = depthMax; + if (vpAndScissor.dirtyProj) { + gstate_c.Dirty(DIRTY_PROJMATRIX); + } + context_->RSSetViewports(1, &vp); + + D3D11_RECT &scissor = dynState_.scissor; + if (vpAndScissor.scissorEnable) { + scissor.left = vpAndScissor.scissorX; + scissor.top = vpAndScissor.scissorY; + scissor.right = vpAndScissor.scissorX + vpAndScissor.scissorW; + scissor.bottom = vpAndScissor.scissorY + vpAndScissor.scissorH; + } + else { + scissor.left = 0; + scissor.top = 0; + scissor.right = framebufferManager_->GetRenderWidth(); + scissor.bottom = framebufferManager_->GetRenderHeight(); + } + context_->RSSetScissorRects(1, &scissor); + + if (gstate_c.IsDirty(DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS) && !gstate.isModeClear() && gstate.isTextureMapEnabled()) { + textureCache_->SetTexture(); + gstate_c.Clean(DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS); + if (gstate_c.needShaderTexClamp) { + // We will rarely need to set this, so let's do it every time on use rather than in runloop. + // Most of the time non-framebuffer textures will be used which can be clamped themselves. + gstate_c.Dirty(DIRTY_TEXCLAMP); + } + } + + ID3D11BlendState *bs = nullptr; + ID3D11DepthStencilState *ds = nullptr; + ID3D11RasterizerState *rs = nullptr; + + auto blendIter = blendCache_.find(keys_.blend.value); + if (blendIter == blendCache_.end()) { + D3D11_BLEND_DESC desc{}; + D3D11_RENDER_TARGET_BLEND_DESC &rt = desc.RenderTarget[0]; + rt.BlendEnable = keys_.blend.blendEnable; + rt.BlendOp = (D3D11_BLEND_OP)keys_.blend.blendOpColor; + rt.BlendOpAlpha = (D3D11_BLEND_OP)keys_.blend.blendOpAlpha; + rt.SrcBlend = (D3D11_BLEND)keys_.blend.srcColor; + rt.DestBlend = (D3D11_BLEND)keys_.blend.destColor; + rt.SrcBlendAlpha = (D3D11_BLEND)keys_.blend.srcAlpha; + rt.DestBlendAlpha = (D3D11_BLEND)keys_.blend.destAlpha; + rt.RenderTargetWriteMask = keys_.blend.colorWriteMask; + device_->CreateBlendState(&desc, &bs); + blendCache_.insert(std::pair(keys_.blend.value, bs)); + } else { + bs = blendIter->second; + } + + float blendColor[4]; + Uint8x4ToFloat4(blendColor, dynState_.blendColor); + context_->OMSetBlendState(bs, blendColor, 0xFFFFFFFF); + + auto depthIter = depthStencilCache_.find(keys_.depthStencil.value); + if (depthIter == depthStencilCache_.end()) { + D3D11_DEPTH_STENCIL_DESC desc{}; + desc.DepthEnable = keys_.depthStencil.depthTestEnable; + desc.DepthWriteMask = keys_.depthStencil.depthWriteEnable ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO; + desc.DepthFunc = (D3D11_COMPARISON_FUNC)keys_.depthStencil.depthCompareOp; + desc.StencilEnable = keys_.depthStencil.stencilTestEnable; + desc.StencilReadMask = keys_.depthStencil.stencilCompareMask; + desc.StencilWriteMask = keys_.depthStencil.stencilWriteMask; + desc.FrontFace.StencilFailOp = (D3D11_STENCIL_OP)keys_.depthStencil.stencilFailOp; + desc.FrontFace.StencilPassOp = (D3D11_STENCIL_OP)keys_.depthStencil.stencilPassOp; + desc.FrontFace.StencilDepthFailOp = (D3D11_STENCIL_OP)keys_.depthStencil.stencilDepthFailOp; + desc.FrontFace.StencilFunc = (D3D11_COMPARISON_FUNC)keys_.depthStencil.stencilCompareFunc; + desc.BackFace = desc.FrontFace; + device_->CreateDepthStencilState(&desc, &ds); + depthStencilCache_.insert(std::pair(keys_.depthStencil.value, ds)); + } else { + ds = depthIter->second; + } + depthStencilState_ = ds; + + auto rasterIter = rasterCache_.find(keys_.raster.value); + if (rasterIter == rasterCache_.end()) { + D3D11_RASTERIZER_DESC desc{}; + desc.CullMode = (D3D11_CULL_MODE)(keys_.raster.cullMode); + desc.FillMode = D3D11_FILL_SOLID; + desc.ScissorEnable = TRUE; + desc.FrontCounterClockwise = TRUE; + device_->CreateRasterizerState(&desc, &rs); + rasterCache_.insert(std::pair(keys_.raster.value, rs)); + } else { + rs = rasterIter->second; + } + context_->RSSetState(rs); +} + +void DrawEngineD3D11::ApplyDrawStateLate(bool applyStencilRef, uint8_t stencilRef) { + textureCache_->ApplyTexture(); + context_->OMSetDepthStencilState(depthStencilState_, applyStencilRef ? stencilRef : dynState_.stencilRef); +} \ No newline at end of file diff --git a/GPU/D3D11/StateMappingD3D11.h b/GPU/D3D11/StateMappingD3D11.h new file mode 100644 index 0000000000..33505d98dd --- /dev/null +++ b/GPU/D3D11/StateMappingD3D11.h @@ -0,0 +1,72 @@ +#pragma once + +#include +#include + +// TODO: Do this more progressively. No need to compute the entire state if the entire state hasn't changed. +// In Vulkan, we simply collect all the state together into a "pipeline key" - we don't actually set any state here +// (the caller is responsible for setting the little dynamic state that is supported, dynState). + +struct D3D11BlendKey { + union { + uint64_t value; + struct { + // Blend + unsigned int blendEnable : 1; + unsigned int srcColor : 5; // D3D11_BLEND + unsigned int destColor : 5; // D3D11_BLEND + unsigned int srcAlpha : 5; // D3D11_BLEND + unsigned int destAlpha : 5; // D3D11_BLEND + unsigned int blendOpColor : 3; // D3D11_BLEND_OP + unsigned int blendOpAlpha : 3; // D3D11_BLEND_OP + unsigned int logicOpEnable : 1; + unsigned int logicOp : 4; // D3D11_LOGIC_OP + unsigned int colorWriteMask : 4; + }; + }; +}; + +struct D3D11DepthStencilKey { + union { + uint64_t value; + struct { + // Depth/Stencil + unsigned int depthTestEnable : 1; + unsigned int depthWriteEnable : 1; + unsigned int depthCompareOp : 4; // D3D11_COMPARISON (-1 and we could fit it in 3 bits) + unsigned int stencilTestEnable : 1; + unsigned int stencilCompareFunc : 4; // D3D11_COMPARISON + unsigned int stencilPassOp : 4; // D3D11_STENCIL_OP + unsigned int stencilFailOp : 4; // D3D11_STENCIL_OP + unsigned int stencilDepthFailOp : 4; // D3D11_STENCIL_OP + unsigned int stencilWriteMask : 8; // Unfortunately these are baked into the state on D3D11 + unsigned int stencilCompareMask : 8; + }; + }; +}; + +struct D3D11RasterKey { + union { + uint32_t value; + struct { + unsigned int cullMode : 2; // D3D11_CULL_MODE + }; + }; +}; + +// In D3D11 we cache blend state objects etc, and we simply emit keys, which are then also used to create these objects. +struct D3D11StateKeys { + D3D11BlendKey blend; + D3D11DepthStencilKey depthStencil; + D3D11RasterKey raster; +}; + +struct D3D11DynamicState { + int topology; + bool useBlendColor; + uint32_t blendColor; + bool useStencil; + uint8_t stencilRef; + D3D11_VIEWPORT viewport; + D3D11_RECT scissor; +}; diff --git a/GPU/D3D11/StencilBufferD3D11.cpp b/GPU/D3D11/StencilBufferD3D11.cpp new file mode 100644 index 0000000000..b3064982e9 --- /dev/null +++ b/GPU/D3D11/StencilBufferD3D11.cpp @@ -0,0 +1,283 @@ +// Copyright (c) 2014- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program 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 General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include + +#include "base/logging.h" + +#include "gfx/d3d9_state.h" +#include "ext/native/thin3d/thin3d.h" +#include "Core/Reporting.h" +#include "GPU/D3D11/FramebufferManagerD3D11.h" +#include "GPU/D3D11/FragmentShaderGeneratorD3D11.h" +#include "GPU/D3D11/ShaderManagerD3D11.h" +#include "GPU/D3D11/TextureCacheD3D11.h" +#include "GPU/D3D11/D3D11Util.h" + +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) + +struct StencilValueUB { + uint32_t u_stencilValue[4]; +}; + +static const char *stencil_ps = R"( +SamplerState samp : register(s0); +Texture2D tex : register(t0); +cbuffer base : register(b0) { + uint4 u_stencilValue; +}; +struct PS_IN { + float2 v_texcoord0 : TEXCOORD0; +}; +float4 main(PS_IN In) : SV_Target { + float4 index = tex.Sample(samp, In.v_texcoord0); + uint indexBits = uint(index.a * 256.0); + if ((indexBits & u_stencilValue.x) == 0) + discard; + return index.aaaa; +} +)"; + +// static const char *stencil_ps_fast; + +static const char *stencil_vs = R"( +struct VS_IN { + float4 a_position : POSITION; + float2 a_texcoord0 : TEXCOORD0; +}; +struct VS_OUT { + float2 v_texcoord0 : TEXCOORD0; + float4 position : SV_Position; +}; +VS_OUT main(VS_IN In) { + VS_OUT Out; + Out.position = In.a_position; + Out.v_texcoord0 = In.a_texcoord0; + return Out; +} +)"; + +static u8 StencilBits5551(const u8 *ptr8, u32 numPixels) { + const u32 *ptr = (const u32 *)ptr8; + + for (u32 i = 0; i < numPixels / 2; ++i) { + if (ptr[i] & 0x80008000) { + return 1; + } + } + return 0; +} + +static u8 StencilBits4444(const u8 *ptr8, u32 numPixels) { + const u32 *ptr = (const u32 *)ptr8; + u32 bits = 0; + + for (u32 i = 0; i < numPixels / 2; ++i) { + bits |= ptr[i]; + } + + return ((bits >> 12) & 0xF) | (bits >> 28); +} + +static u8 StencilBits8888(const u8 *ptr8, u32 numPixels) { + const u32 *ptr = (const u32 *)ptr8; + u32 bits = 0; + + for (u32 i = 0; i < numPixels; ++i) { + bits |= ptr[i]; + } + + return bits >> 24; +} + +// TODO : If SV_StencilRef is available (D3D11.3) then this can be done in a single pass. +bool FramebufferManagerD3D11::NotifyStencilUpload(u32 addr, int size, bool skipZero) { + if (!MayIntersectFramebuffer(addr)) { + return false; + } + + VirtualFramebuffer *dstBuffer = 0; + for (size_t i = 0; i < vfbs_.size(); ++i) { + VirtualFramebuffer *vfb = vfbs_[i]; + if (MaskedEqual(vfb->fb_address, addr)) { + dstBuffer = vfb; + } + } + if (!dstBuffer) { + return false; + } + + int values = 0; + u8 usedBits = 0; + + const u8 *src = Memory::GetPointer(addr); + if (!src) { + return false; + } + + switch (dstBuffer->format) { + case GE_FORMAT_565: + // Well, this doesn't make much sense. + return false; + case GE_FORMAT_5551: + usedBits = StencilBits5551(src, dstBuffer->fb_stride * dstBuffer->bufferHeight); + values = 2; + break; + case GE_FORMAT_4444: + usedBits = StencilBits4444(src, dstBuffer->fb_stride * dstBuffer->bufferHeight); + values = 16; + break; + case GE_FORMAT_8888: + usedBits = StencilBits8888(src, dstBuffer->fb_stride * dstBuffer->bufferHeight); + values = 256; + break; + case GE_FORMAT_INVALID: + // Impossible. + break; + } + + if (usedBits == 0) { + if (skipZero) { + // Common when creating buffers, it's already 0. We're done. + return false; + } + + // Clear stencil+alpha but not color. Only way is to draw a quad. + context_->OMSetBlendState(stockD3D11.blendStateDisabledWithColorMask[0x8], nullptr, 0xFFFFFFFF); + context_->RSSetState(stockD3D11.rasterStateNoCull); + context_->OMSetDepthStencilState(stockD3D11.depthDisabledStencilWrite, 0); + context_->IASetVertexBuffers(0, 1, &fsQuadBuffer_, &quadStride_, &quadOffset_); + context_->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + context_->Draw(4, 0); + return true; + } + + // TODO: Helper with logging? + if (!stencilUploadPS_) { + std::string errorMessage; + stencilUploadPS_ = CreatePixelShaderD3D11(device_, stencil_ps, strlen(stencil_ps)); + } + if (!stencilUploadVS_) { + std::string errorMessage; + std::vector byteCode; + stencilUploadVS_ = CreateVertexShaderD3D11(device_, stencil_vs, strlen(stencil_vs), &byteCode); + device_->CreateInputLayout(g_QuadVertexElements, 2, byteCode.data(), byteCode.size(), &stencilUploadInputLayout_); + } + if (!stencilValueBuffer_) { + D3D11_BUFFER_DESC desc{}; + desc.ByteWidth = sizeof(StencilValueUB); + desc.Usage = D3D11_USAGE_DYNAMIC; + desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; + device_->CreateBuffer(&desc, nullptr, &stencilValueBuffer_); + } + + shaderManager_->DirtyLastShader(); + + u16 w = dstBuffer->renderWidth; + u16 h = dstBuffer->renderHeight; + MakePixelTexture(src, dstBuffer->format, dstBuffer->fb_stride, dstBuffer->bufferWidth, dstBuffer->bufferHeight); + if (dstBuffer->fbo) { + draw_->BindFramebufferAsRenderTarget(dstBuffer->fbo); + } else { + // something is wrong... + } + D3D11_VIEWPORT vp{ 0.0f, 0.0f, (float)w, (float)h, 0.0f, 1.0f }; + context_->RSSetViewports(1, &vp); + + // Zero stencil + draw_->Clear(Draw::ClearFlag::STENCIL, 0, 0, 0); + + float fw = dstBuffer->width; + float fh = dstBuffer->height; + + float coord[20] = { + 0.0f,0.0f,0.0f, 0.0f,0.0f, + fw,0.0f,0.0f, 1.0f,0.0f, + 0.0f,fh,0.0f, 0.0f,1.0f, + fw,fh,0.0f, 1.0f,1.0f, + }; + // I think all these calculations pretty much cancel out? + float invDestW = 1.0f / (fw * 0.5f); + float invDestH = 1.0f / (fh * 0.5f); + for (int i = 0; i < 4; i++) { + coord[i * 5] = coord[i * 5] * invDestW - 1.0f; + coord[i * 5 + 1] = -(coord[i * 5 + 1] * invDestH - 1.0f); + } + + D3D11_MAPPED_SUBRESOURCE map; + context_->Map(quadBuffer_, 0, D3D11_MAP_WRITE_DISCARD, 0, &map); + memcpy(map.pData, coord, sizeof(float) * 4 * 5); + context_->Unmap(quadBuffer_, 0); + + shaderManager_->DirtyLastShader(); + textureCacheD3D11_->ForgetLastTexture(); + + context_->OMSetBlendState(stockD3D11.blendStateDisabledWithColorMask[0], nullptr, 0xFFFFFFFF); + context_->IASetInputLayout(stencilUploadInputLayout_); + context_->PSSetShader(stencilUploadPS_, nullptr, 0); + context_->VSSetShader(stencilUploadVS_, nullptr, 0); + context_->PSSetShaderResources(0, 1, &drawPixelsTexView_); + context_->RSSetState(stockD3D11.rasterStateNoCull); + context_->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + context_->IASetVertexBuffers(0, 1, &quadBuffer_, &quadStride_, &quadOffset_); + context_->OMSetDepthStencilState(stockD3D11.depthDisabledStencilWrite, 0xFF); + + for (int i = 1; i < values; i += i) { + if (!(usedBits & i)) { + // It's already zero, let's skip it. + continue; + } + uint8_t mask = 0; + uint8_t value = 0; + if (dstBuffer->format == GE_FORMAT_4444) { + mask = i | (i << 4); + value = i * 16; + } else if (dstBuffer->format == GE_FORMAT_5551) { + mask = 0xFF; + value = i * 128; + } else { + mask = i; + value = i; + } + if (!stencilMaskStates_[mask]) { + D3D11_DEPTH_STENCIL_DESC desc{}; + desc.DepthEnable = false; + desc.StencilEnable = true; + desc.StencilReadMask = 0xFF; + desc.StencilWriteMask = mask; + desc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_REPLACE; + desc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_REPLACE; + desc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_REPLACE; + desc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS; + desc.BackFace = desc.FrontFace; + device_->CreateDepthStencilState(&desc, &stencilMaskStates_[mask]); + } + context_->OMSetDepthStencilState(stencilMaskStates_[mask], 0xFF); + + D3D11_MAPPED_SUBRESOURCE map; + context_->Map(stencilValueBuffer_, 0, D3D11_MAP_WRITE_DISCARD, 0, &map); + StencilValueUB ub{ (uint32_t)value }; + memcpy(map.pData, &ub, sizeof(ub)); + context_->Unmap(stencilValueBuffer_, 0); + context_->PSSetConstantBuffers(0, 1, &stencilValueBuffer_); + context_->Draw(4, 0); + } + RebindFramebuffer(); + return true; +} diff --git a/GPU/D3D11/TextureCacheD3D11.cpp b/GPU/D3D11/TextureCacheD3D11.cpp new file mode 100644 index 0000000000..d1a8da24b7 --- /dev/null +++ b/GPU/D3D11/TextureCacheD3D11.cpp @@ -0,0 +1,1203 @@ +// Copyright (c) 2012- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program 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 General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include +#include +#include +#include + +#include + +#include "Core/MemMap.h" +#include "Core/Reporting.h" +#include "GPU/ge_constants.h" +#include "GPU/GPUState.h" +#include "GPU/D3D11/FragmentShaderGeneratorD3D11.h" +#include "GPU/D3D11/TextureCacheD3D11.h" +#include "GPU/D3D11/FramebufferManagerD3D11.h" +#include "GPU/D3D11/ShaderManagerD3D11.h" +#include "GPU/D3D11/DepalettizeShaderD3D11.h" +#include "GPU/D3D11/D3D11Util.h" +#include "GPU/Common/FramebufferCommon.h" +#include "GPU/Common/TextureDecoder.h" +#include "Core/Config.h" +#include "Core/Host.h" + +#include "ext/xxhash.h" +#include "math/math_util.h" + + +#define INVALID_TEX (ID3D11ShaderResourceView *)(-1LL) + + // If a texture hasn't been seen for this many frames, get rid of it. +#define TEXTURE_KILL_AGE 200 +#define TEXTURE_KILL_AGE_LOWMEM 60 + // Not used in lowmem mode. +#define TEXTURE_SECOND_KILL_AGE 100 + + // Try to be prime to other decimation intervals. +#define TEXCACHE_DECIMATION_INTERVAL 13 + +#define TEXCACHE_MAX_TEXELS_SCALED (256*256) // Per frame + +#define TEXCACHE_MIN_PRESSURE 16 * 1024 * 1024 // Total in VRAM +#define TEXCACHE_SECOND_MIN_PRESSURE 4 * 1024 * 1024 + +static const D3D11_INPUT_ELEMENT_DESC g_QuadVertexElements[] = { + { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, }, + { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12,}, +}; + +SamplerCacheD3D11::~SamplerCacheD3D11() { + for (auto &iter : cache_) { + iter.second->Release(); + } +} + +ID3D11SamplerState *SamplerCacheD3D11::GetOrCreateSampler(ID3D11Device *device, const SamplerCacheKey &key) { + auto iter = cache_.find(key); + if (iter != cache_.end()) { + return iter->second; + } + + D3D11_SAMPLER_DESC samp{}; + samp.AddressU = key.sClamp ? D3D11_TEXTURE_ADDRESS_CLAMP : D3D11_TEXTURE_ADDRESS_WRAP; + samp.AddressV = key.tClamp ? D3D11_TEXTURE_ADDRESS_CLAMP : D3D11_TEXTURE_ADDRESS_WRAP; + samp.AddressW = D3D11_TEXTURE_ADDRESS_WRAP; + if (gstate_c.Supports(GPU_SUPPORTS_ANISOTROPY) && g_Config.iAnisotropyLevel > 0) { + samp.MaxAnisotropy = (float)(1 << g_Config.iAnisotropyLevel); + } else { + samp.MaxAnisotropy = 1.0f; + } + int filterKey = ((int)key.minFilt << 2) | ((int)key.magFilt << 1) | ((int)key.mipFilt); + static const D3D11_FILTER filters[8] = { + D3D11_FILTER_MIN_MAG_MIP_POINT, + D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR, + D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT, + D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR, + D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT, + D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR, + D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT, + D3D11_FILTER_MIN_MAG_MIP_LINEAR, + }; + samp.Filter = filters[filterKey]; + samp.MaxLOD = key.maxLevel; + samp.MinLOD = 0.0f; + samp.MipLODBias = 0.0f; + + ID3D11SamplerState *sampler; + device->CreateSamplerState(&samp, &sampler); + cache_[key] = sampler; + return sampler; +} + +TextureCacheD3D11::TextureCacheD3D11(Draw::DrawContext *draw) + : TextureCacheCommon(draw) { + device_ = (ID3D11Device *)draw->GetNativeObject(Draw::NativeObject::DEVICE); + context_ = (ID3D11DeviceContext *)draw->GetNativeObject(Draw::NativeObject::CONTEXT); + + lastBoundTexture = INVALID_TEX; + decimationCounter_ = TEXCACHE_DECIMATION_INTERVAL; + + HRESULT result = 0; + + maxAnisotropyLevel = 16; + SetupTextureDecoder(); + + nextTexture_ = nullptr; +} + +TextureCacheD3D11::~TextureCacheD3D11() { + // pFramebufferVertexDecl->Release(); + Clear(true); +} + +void TextureCacheD3D11::SetFramebufferManager(FramebufferManagerD3D11 *fbManager) { + framebufferManagerD3D11_ = fbManager; + framebufferManager_ = fbManager; +} + +void TextureCacheD3D11::Clear(bool delete_them) { + // ID3D11ShaderResourceView *srv = nullptr; + // context_->PSSetShaderResources(0, 1, &srv); + lastBoundTexture = INVALID_TEX; + if (delete_them) { + for (TexCache::iterator iter = cache.begin(); iter != cache.end(); ++iter) { + DEBUG_LOG(G3D, "Deleting texture %p", iter->second.texturePtr); + ReleaseTexture(&iter->second); + } + for (TexCache::iterator iter = secondCache.begin(); iter != secondCache.end(); ++iter) { + DEBUG_LOG(G3D, "Deleting texture %p", iter->second.texturePtr); + ReleaseTexture(&iter->second); + } + } + if (cache.size() + secondCache.size()) { + INFO_LOG(G3D, "Texture cached cleared from %i textures", (int)(cache.size() + secondCache.size())); + cache.clear(); + secondCache.clear(); + cacheSizeEstimate_ = 0; + secondCacheSizeEstimate_ = 0; + } + fbTexInfo_.clear(); + videos_.clear(); +} + +void TextureCacheD3D11::DeleteTexture(TexCache::iterator it) { + ReleaseTexture(&it->second); + auto fbInfo = fbTexInfo_.find(it->first); + if (fbInfo != fbTexInfo_.end()) { + fbTexInfo_.erase(fbInfo); + } + + cacheSizeEstimate_ -= EstimateTexMemoryUsage(&it->second); + cache.erase(it); +} + +void TextureCacheD3D11::ForgetLastTexture() { + lastBoundTexture = INVALID_TEX; + gstate_c.Dirty(DIRTY_TEXTURE_PARAMS); +} + +// Removes old textures. +void TextureCacheD3D11::Decimate() { + if (--decimationCounter_ <= 0) { + decimationCounter_ = TEXCACHE_DECIMATION_INTERVAL; + } else { + return; + } + + if (cacheSizeEstimate_ >= TEXCACHE_MIN_PRESSURE) { + const u32 had = cacheSizeEstimate_; + + ID3D11ShaderResourceView *srv = nullptr; + context_->PSSetShaderResources(0, 1, &srv); + lastBoundTexture = INVALID_TEX; + int killAge = lowMemoryMode_ ? TEXTURE_KILL_AGE_LOWMEM : TEXTURE_KILL_AGE; + for (TexCache::iterator iter = cache.begin(); iter != cache.end(); ) { + if (iter->second.lastFrame + killAge < gpuStats.numFlips) { + DeleteTexture(iter++); + } else { + ++iter; + } + } + + VERBOSE_LOG(G3D, "Decimated texture cache, saved %d estimated bytes - now %d bytes", had - cacheSizeEstimate_, cacheSizeEstimate_); + } + + if (g_Config.bTextureSecondaryCache && secondCacheSizeEstimate_ >= TEXCACHE_SECOND_MIN_PRESSURE) { + const u32 had = secondCacheSizeEstimate_; + + for (TexCache::iterator iter = secondCache.begin(); iter != secondCache.end(); ) { + // In low memory mode, we kill them all. + if (lowMemoryMode_ || iter->second.lastFrame + TEXTURE_KILL_AGE < gpuStats.numFlips) { + ReleaseTexture(&iter->second); + secondCache.erase(iter++); + } else { + ++iter; + } + } + + VERBOSE_LOG(G3D, "Decimated second texture cache, saved %d estimated bytes - now %d bytes", had - secondCacheSizeEstimate_, secondCacheSizeEstimate_); + } + + DecimateVideos(); +} + +DXGI_FORMAT getClutDestFormatD3D11(GEPaletteFormat format) { + switch (format) { + case GE_CMODE_16BIT_ABGR4444: + return DXGI_FORMAT_B4G4R4A4_UNORM; + case GE_CMODE_16BIT_ABGR5551: + return DXGI_FORMAT_B5G5R5A1_UNORM; + case GE_CMODE_16BIT_BGR5650: + return DXGI_FORMAT_B5G6R5_UNORM; + case GE_CMODE_32BIT_ABGR8888: + return DXGI_FORMAT_B8G8R8A8_UNORM; + } + // Should never be here ! + return DXGI_FORMAT_B8G8R8A8_UNORM; +} + +void TextureCacheD3D11::UpdateSamplingParams(TexCacheEntry &entry, SamplerCacheKey &key) { + // TODO: Make GetSamplingParams write SamplerCacheKey directly + int minFilt; + int magFilt; + bool sClamp; + bool tClamp; + float lodBias; + GetSamplingParams(minFilt, magFilt, sClamp, tClamp, lodBias, entry.maxLevel, entry.addr); + key.minFilt = minFilt & 1; + key.mipEnable = (minFilt >> 2) & 1; + key.mipFilt = (minFilt >> 1) & 1; + key.magFilt = magFilt & 1; + key.sClamp = sClamp; + key.tClamp = tClamp; + key.maxLevel = entry.maxLevel; + + if (entry.framebuffer) { + WARN_LOG_REPORT_ONCE(wrongFramebufAttach, G3D, "Framebuffer still attached in UpdateSamplingParams()?"); + } +} + +void TextureCacheD3D11::SetFramebufferSamplingParams(u16 bufferWidth, u16 bufferHeight, SamplerCacheKey &key) { + int minFilt; + int magFilt; + bool sClamp; + bool tClamp; + float lodBias; + GetSamplingParams(minFilt, magFilt, sClamp, tClamp, lodBias, 0, 0); + + key.minFilt = minFilt & 1; + key.mipFilt = 0; + key.magFilt = magFilt & 1; + key.sClamp = sClamp; + key.tClamp = tClamp; + + // Often the framebuffer will not match the texture size. We'll wrap/clamp in the shader in that case. + // This happens whether we have OES_texture_npot or not. + int w = gstate.getTextureWidth(0); + int h = gstate.getTextureHeight(0); + if (w != bufferWidth || h != bufferHeight) { + key.sClamp = true; + key.tClamp = true; + } +} + +void TextureCacheD3D11::StartFrame() { + lastBoundTexture = INVALID_TEX; + timesInvalidatedAllThisFrame_ = 0; + + if (texelsScaledThisFrame_) { + // INFO_LOG(G3D, "Scaled %i texels", texelsScaledThisFrame_); + } + texelsScaledThisFrame_ = 0; + if (clearCacheNextFrame_) { + Clear(true); + clearCacheNextFrame_ = false; + } else { + Decimate(); + } +} + +static inline u32 MiniHash(const u32 *ptr) { + return ptr[0]; +} + +void TextureCacheD3D11::UpdateCurrentClut(GEPaletteFormat clutFormat, u32 clutBase, bool clutIndexIsSimple) { + const u32 clutBaseBytes = clutBase * (clutFormat == GE_CMODE_32BIT_ABGR8888 ? sizeof(u32) : sizeof(u16)); + // Technically, these extra bytes weren't loaded, but hopefully it was loaded earlier. + // If not, we're going to hash random data, which hopefully doesn't cause a performance issue. + // + // TODO: Actually, this seems like a hack. The game can upload part of a CLUT and reference other data. + // clutTotalBytes_ is the last amount uploaded. We should hash clutMaxBytes_, but this will often hash + // unrelated old entries for small palettes. + // Adding clutBaseBytes may just be mitigating this for some usage patterns. + const u32 clutExtendedBytes = std::min(clutTotalBytes_ + clutBaseBytes, clutMaxBytes_); + + clutHash_ = DoReliableHash32((const char *)clutBufRaw_, clutExtendedBytes, 0xC0108888); + clutBuf_ = clutBufRaw_; + + // Special optimization: fonts typically draw clut4 with just alpha values in a single color. + clutAlphaLinear_ = false; + clutAlphaLinearColor_ = 0; + if (clutFormat == GE_CMODE_16BIT_ABGR4444 && clutIndexIsSimple) { + const u16_le *clut = GetCurrentClut(); + clutAlphaLinear_ = true; + clutAlphaLinearColor_ = clut[15] & 0x0FFF; + for (int i = 0; i < 16; ++i) { + u16 step = clutAlphaLinearColor_ | (i << 12); + if (clut[i] != step) { + clutAlphaLinear_ = false; + break; + } + } + } + + clutLastFormat_ = gstate.clutformat; +} + +inline u32 TextureCacheD3D11::GetCurrentClutHash() { + return clutHash_; +} + +void TextureCacheD3D11::Unbind() { + ID3D11ShaderResourceView *nullView = nullptr; + context_->PSSetShaderResources(0, 1, &nullView); +} + +void TextureCacheD3D11::ApplyTexture() { + TexCacheEntry *entry = nextTexture_; + if (entry == nullptr) { + return; + } + nextTexture_ = nullptr; + + UpdateMaxSeenV(entry, gstate.isModeThrough()); + + bool replaceImages = false; + if (nextNeedsRebuild_) { + if (nextNeedsRehash_) { + // Update the hash on the texture. + int w = gstate.getTextureWidth(0); + int h = gstate.getTextureHeight(0); + entry->fullhash = QuickTexHash(replacer, entry->addr, entry->bufw, w, h, GETextureFormat(entry->format), entry); + } + if (nextNeedsChange_) { + // This texture existed previously, let's handle the change. + replaceImages = HandleTextureChange(entry, nextChangeReason_, false, true); + } + + // We actually build afterward (shared with rehash rebuild.) + } else if (nextNeedsRehash_) { + // Okay, this matched and didn't change - but let's check the hash. Maybe it will change. + bool doDelete = true; + if (!CheckFullHash(entry, doDelete)) { + replaceImages = HandleTextureChange(entry, "hash fail", true, doDelete); + nextNeedsRebuild_ = true; + } else if (nextTexture_ != nullptr) { + // Secondary cache picked a different texture, use it. + entry = nextTexture_; + nextTexture_ = nullptr; + UpdateMaxSeenV(entry, gstate.isModeThrough()); + } + } + + // Okay, now actually rebuild the texture if needed. + if (nextNeedsRebuild_) { + BuildTexture(entry, replaceImages); + } + + entry->lastFrame = gpuStats.numFlips; + if (entry->framebuffer) { + ApplyTextureFramebuffer(entry, entry->framebuffer); + } else { + ID3D11ShaderResourceView *textureView = DxView(entry); + if (textureView != lastBoundTexture) { + context_->PSSetShaderResources(0, 1, &textureView); + lastBoundTexture = textureView; + } + SamplerCacheKey key; + UpdateSamplingParams(*entry, key); + ID3D11SamplerState *state = samplerCache_.GetOrCreateSampler(device_, key); + context_->PSSetSamplers(0, 1, &state); + gstate_c.textureFullAlpha = entry->GetAlphaStatus() == TexCacheEntry::STATUS_ALPHA_FULL; + gstate_c.textureSimpleAlpha = entry->GetAlphaStatus() != TexCacheEntry::STATUS_ALPHA_UNKNOWN; + } +} + +class TextureShaderApplierD3D11 { +public: + struct Pos { + Pos(float x_, float y_, float z_) : x(x_), y(y_), z(z_) { + } + Pos() { + } + + float x; + float y; + float z; + }; + struct UV { + UV(float u_, float v_) : u(u_), v(v_) { + } + UV() { + } + + float u; + float v; + }; + + struct PosUV { + Pos pos; + UV uv; + }; + + TextureShaderApplierD3D11(ID3D11DeviceContext *context, ID3D11PixelShader *pshader, ID3D11Buffer *dynamicBuffer, float bufferW, float bufferH, int renderW, int renderH, float xoff, float yoff) + : context_(context), pshader_(pshader), bufferW_(bufferW), bufferH_(bufferH), renderW_(renderW), renderH_(renderH) { + static const Pos pos[4] = { + { -1, 1, 0 }, + { 1, 1, 0 }, + { 1, -1, 0 }, + { -1, -1, 0 }, + }; + static const UV uv[4] = { + { 0, 0 }, + { 1, 0 }, + { 1, 1 }, + { 0, 1 }, + }; + + for (int i = 0; i < 4; ++i) { + verts_[i].pos = pos[i]; + verts_[i].pos.x += xoff; + verts_[i].pos.y += yoff; + verts_[i].uv = uv[i]; + } + D3D11_MAPPED_SUBRESOURCE map; + context->Map(dynamicBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &map); + memcpy(map.pData, &verts_[0], 4 * 5 * sizeof(float)); + context->Unmap(dynamicBuffer, 0); + vbuffer_ = dynamicBuffer; + } + + void ApplyBounds(const KnownVertexBounds &bounds, u32 uoff, u32 voff, float xoff, float yoff) { + // If min is not < max, then we don't have values (wasn't set during decode.) + if (bounds.minV < bounds.maxV) { + const float invWidth = 1.0f / bufferW_; + const float invHeight = 1.0f / bufferH_; + // Inverse of half = double. + const float invHalfWidth = invWidth * 2.0f; + const float invHalfHeight = invHeight * 2.0f; + + const int u1 = bounds.minU + uoff; + const int v1 = bounds.minV + voff; + const int u2 = bounds.maxU + uoff; + const int v2 = bounds.maxV + voff; + + const float left = u1 * invHalfWidth - 1.0f + xoff; + const float right = u2 * invHalfWidth - 1.0f + xoff; + const float top = v1 * invHalfHeight - 1.0f + yoff; + const float bottom = v2 * invHalfHeight - 1.0f + yoff; + // Points are: BL, BR, TR, TL. + verts_[0].pos = Pos(left, bottom, -1.0f); + verts_[1].pos = Pos(right, bottom, -1.0f); + verts_[2].pos = Pos(right, top, -1.0f); + verts_[3].pos = Pos(left, top, -1.0f); + + // And also the UVs, same order. + const float uvleft = u1 * invWidth; + const float uvright = u2 * invWidth; + const float uvtop = v1 * invHeight; + const float uvbottom = v2 * invHeight; + verts_[0].uv = UV(uvleft, uvbottom); + verts_[1].uv = UV(uvright, uvbottom); + verts_[2].uv = UV(uvright, uvtop); + verts_[3].uv = UV(uvleft, uvtop); + } + } + + void Use(ID3D11VertexShader *vshader, ID3D11InputLayout *decl) { + context_->PSSetShader(pshader_, 0, 0); + context_->VSSetShader(vshader, 0, 0); + context_->IASetInputLayout(decl); + } + + void Shade() { + D3D11_VIEWPORT vp{ 0.0f, 0.0f, (float)renderW_, (float)renderH_, 0.0f, 1.0f }; + context_->OMSetBlendState(stockD3D11.blendStateDisabledWithColorMask[0xF], nullptr, 0xFFFFFFFF); + context_->OMSetDepthStencilState(stockD3D11.depthStencilDisabled, 0xFF); + context_->RSSetState(stockD3D11.rasterStateNoCull); + context_->RSSetViewports(1, &vp); + context_->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + context_->IASetVertexBuffers(0, 1, &vbuffer_, &stride_, &offset_); + context_->Draw(2, 0); + } + +protected: + ID3D11DeviceContext *context_; + ID3D11PixelShader *pshader_; + ID3D11Buffer *vbuffer_; + PosUV verts_[4]; + UINT stride_ = sizeof(PosUV); + UINT offset_ = 0; + float bufferW_; + float bufferH_; + int renderW_; + int renderH_; +}; + +void TextureCacheD3D11::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFramebuffer *framebuffer) { + ID3D11PixelShader *pshader = nullptr; + const GEPaletteFormat clutFormat = gstate.getClutPaletteFormat(); + if ((entry->status & TexCacheEntry::STATUS_DEPALETTIZE) && !g_Config.bDisableSlowFramebufEffects) { + pshader = depalShaderCache_->GetDepalettizePixelShader(clutFormat, framebuffer->drawnFormat); + } + + if (pshader) { + ID3D11ShaderResourceView *clutTexture = depalShaderCache_->GetClutTexture(clutFormat, clutHash_, clutBuf_); + + Draw::Framebuffer *depalFBO = framebufferManagerD3D11_->GetTempFBO(framebuffer->renderWidth, framebuffer->renderHeight, Draw::FBO_8888); + draw_->BindFramebufferAsRenderTarget(depalFBO); + shaderManager_->DirtyLastShader(); + draw_->BindPipeline(nullptr); + + float xoff = -0.5f / framebuffer->renderWidth; + float yoff = 0.5f / framebuffer->renderHeight; + + TextureShaderApplierD3D11 shaderApply(context_, pshader, framebufferManagerD3D11_->GetDynamicQuadBuffer(), framebuffer->bufferWidth, framebuffer->bufferHeight, framebuffer->renderWidth, framebuffer->renderHeight, xoff, yoff); + shaderApply.ApplyBounds(gstate_c.vertBounds, gstate_c.curTextureXOffset, gstate_c.curTextureYOffset, xoff, yoff); + shaderApply.Use(depalShaderCache_->GetDepalettizeVertexShader(), depalShaderCache_->GetInputLayout()); + + context_->PSSetShaderResources(1, 1, &clutTexture); + context_->PSSetSamplers(1, 1, &stockD3D11.samplerPoint2DWrap); + framebufferManagerD3D11_->BindFramebufferColor(0, framebuffer, BINDFBCOLOR_SKIP_COPY); + context_->PSSetSamplers(0, 1, &stockD3D11.samplerPoint2DWrap); + shaderApply.Shade(); + + draw_->BindFramebufferAsTexture(depalFBO, 0, Draw::FB_COLOR_BIT, 0); + + const u32 bytesPerColor = clutFormat == GE_CMODE_32BIT_ABGR8888 ? sizeof(u32) : sizeof(u16); + const u32 clutTotalColors = clutMaxBytes_ / bytesPerColor; + + TexCacheEntry::Status alphaStatus = CheckAlpha(clutBuf_, getClutDestFormatD3D11(clutFormat), clutTotalColors, clutTotalColors, 1); + gstate_c.textureFullAlpha = alphaStatus == TexCacheEntry::STATUS_ALPHA_FULL; + gstate_c.textureSimpleAlpha = alphaStatus == TexCacheEntry::STATUS_ALPHA_SIMPLE; + } else { + entry->status &= ~TexCacheEntry::STATUS_DEPALETTIZE; + + framebufferManagerD3D11_->BindFramebufferColor(0, framebuffer, BINDFBCOLOR_MAY_COPY_WITH_UV | BINDFBCOLOR_APPLY_TEX_OFFSET); + + gstate_c.textureFullAlpha = gstate.getTextureFormat() == GE_TFMT_5650; + gstate_c.textureSimpleAlpha = gstate_c.textureFullAlpha; + } + + framebufferManagerD3D11_->RebindFramebuffer(); + SamplerCacheKey samplerKey; + SetFramebufferSamplingParams(framebuffer->bufferWidth, framebuffer->bufferHeight, samplerKey); + ID3D11SamplerState *state = samplerCache_.GetOrCreateSampler(device_, samplerKey); + context_->PSSetSamplers(0, 1, &state); + lastBoundTexture = INVALID_TEX; +} + +void TextureCacheD3D11::SetTexture(bool force) { +#ifdef DEBUG_TEXTURES + if (SetDebugTexture()) { + // A different texture was bound, let's rebind next time. + lastBoundTexture = INVALID_TEX; + return; + } +#endif + + if (force) { + lastBoundTexture = INVALID_TEX; + } + + u32 texaddr = gstate.getTextureAddress(0); + if (!Memory::IsValidAddress(texaddr)) { + // Bind a null texture and return. + ID3D11ShaderResourceView *srv = nullptr; + context_->PSSetShaderResources(0, 1, &srv); + lastBoundTexture = INVALID_TEX; + return; + } + + const u16 dim = gstate.getTextureDimension(0); + int w = gstate.getTextureWidth(0); + int h = gstate.getTextureHeight(0); + + GETextureFormat format = gstate.getTextureFormat(); + if (format >= 11) { + ERROR_LOG_REPORT(G3D, "Unknown texture format %i", format); + // TODO: Better assumption? + format = GE_TFMT_5650; + } + bool hasClut = gstate.isTextureFormatIndexed(); + + // Ignore uncached/kernel when caching. + u32 cluthash; + if (hasClut) { + if (clutLastFormat_ != gstate.clutformat) { + // We update here because the clut format can be specified after the load. + UpdateCurrentClut(gstate.getClutPaletteFormat(), gstate.getClutIndexStartPos(), gstate.isClutIndexSimple()); + } + cluthash = GetCurrentClutHash() ^ gstate.clutformat; + } else { + cluthash = 0; + } + u64 cachekey = TexCacheEntry::CacheKey(texaddr, format, dim, cluthash); + + int bufw = GetTextureBufw(0, texaddr, format); + u8 maxLevel = gstate.getTextureMaxLevel(); + + u32 texhash = MiniHash((const u32 *)Memory::GetPointer(texaddr)); + + TexCache::iterator iter = cache.find(cachekey); + TexCacheEntry *entry = NULL; + gstate_c.needShaderTexClamp = false; + gstate_c.bgraTexture = true; + gstate_c.skipDrawReason &= ~SKIPDRAW_BAD_FB_TEXTURE; + bool useBufferedRendering = g_Config.iRenderingMode != FB_NON_BUFFERED_MODE; + + if (iter != cache.end()) { + entry = &iter->second; + // Validate the texture still matches the cache entry. + bool match = entry->Matches(dim, format, maxLevel); + const char *reason = "different params"; + + // Check for FBO - slow! + if (entry->framebuffer) { + if (match) { + if (hasClut && clutRenderAddress_ != 0xFFFFFFFF) { + WARN_LOG_REPORT_ONCE(clutAndTexRender, G3D, "Using rendered texture with rendered CLUT: texfmt=%d, clutfmt=%d", gstate.getTextureFormat(), gstate.getClutPaletteFormat()); + } + + SetTextureFramebuffer(entry, entry->framebuffer); + return; + } else { + // Make sure we re-evaluate framebuffers. + DetachFramebuffer(entry, texaddr, entry->framebuffer); + reason = "detached framebuf"; + match = false; + } + } + + bool rehash = entry->GetHashStatus() == TexCacheEntry::STATUS_UNRELIABLE; + + // First let's see if another texture with the same address had a hashfail. + if (entry->status & TexCacheEntry::STATUS_CLUT_RECHECK) { + // Always rehash in this case, if one changed the rest all probably did. + rehash = true; + entry->status &= ~TexCacheEntry::STATUS_CLUT_RECHECK; + } else if (!gstate_c.IsDirty(DIRTY_TEXTURE_IMAGE)) { + // Okay, just some parameter change - the data didn't change, no need to rehash. + rehash = false; + } + + if (match) { + if (entry->lastFrame != gpuStats.numFlips) { + u32 diff = gpuStats.numFlips - entry->lastFrame; + entry->numFrames++; + + if (entry->framesUntilNextFullHash < diff) { + // Exponential backoff up to 512 frames. Textures are often reused. + if (entry->numFrames > 32) { + // Also, try to add some "randomness" to avoid rehashing several textures the same frame. + entry->framesUntilNextFullHash = std::min(512, entry->numFrames) + ((intptr_t)entry->texturePtr & 15); + } else { + entry->framesUntilNextFullHash = entry->numFrames; + } + rehash = true; + } else { + entry->framesUntilNextFullHash -= diff; + } + } + + // If it's not huge or has been invalidated many times, recheck the whole texture. + if (entry->invalidHint > 180 || (entry->invalidHint > 15 && (dim >> 8) < 9 && (dim & 0xF) < 9)) { + entry->invalidHint = 0; + rehash = true; + } + + if (texhash != entry->hash) { + match = false; + } else if (entry->GetHashStatus() == TexCacheEntry::STATUS_RELIABLE) { + rehash = false; + } + } + + if (match && (entry->status & TexCacheEntry::STATUS_TO_SCALE) && standardScaleFactor_ != 1 && texelsScaledThisFrame_ < TEXCACHE_MAX_TEXELS_SCALED) { + if ((entry->status & TexCacheEntry::STATUS_CHANGE_FREQUENT) == 0) { + // INFO_LOG(G3D, "Reloading texture to do the scaling we skipped.."); + match = false; + reason = "scaling"; + } + } + + if (match) { + // TODO: Mark the entry reliable if it's been safe for long enough? + //got one! + if (entry->texturePtr != lastBoundTexture) { + gstate_c.curTextureWidth = w; + gstate_c.curTextureHeight = h; + } + if (rehash) { + // Update in case any of these changed. + entry->sizeInRAM = (textureBitsPerPixel[format] * bufw * h / 2) / 8; + entry->bufw = bufw; + entry->cluthash = cluthash; + } + + nextTexture_ = entry; + nextNeedsRehash_ = rehash; + nextNeedsChange_ = false; + // Might need a rebuild if the hash fails. + nextNeedsRebuild_ = false; + VERBOSE_LOG(G3D, "Texture at %08x Found in Cache, applying", texaddr); + return; //Done! + } else { + nextChangeReason_ = reason; + nextNeedsChange_ = true; + } + } else { + VERBOSE_LOG(G3D, "No texture in cache, decoding..."); + TexCacheEntry entryNew = { 0 }; + cache[cachekey] = entryNew; + + if (hasClut && clutRenderAddress_ != 0xFFFFFFFF) { + WARN_LOG_REPORT_ONCE(clutUseRender, G3D, "Using texture with rendered CLUT: texfmt=%d, clutfmt=%d", gstate.getTextureFormat(), gstate.getClutPaletteFormat()); + } + + entry = &cache[cachekey]; + if (g_Config.bTextureBackoffCache) { + entry->status = TexCacheEntry::STATUS_HASHING; + } else { + entry->status = TexCacheEntry::STATUS_UNRELIABLE; + } + + nextNeedsChange_ = false; + } + + // We have to decode it, let's setup the cache entry first. + entry->addr = texaddr; + entry->hash = texhash; + entry->dim = dim; + entry->format = format; + entry->maxLevel = maxLevel; + + // This would overestimate the size in many case so we underestimate instead + // to avoid excessive clearing caused by cache invalidations. + entry->sizeInRAM = (textureBitsPerPixel[format] * bufw * h / 2) / 8; + entry->bufw = bufw; + + entry->cluthash = cluthash; + + gstate_c.curTextureWidth = w; + gstate_c.curTextureHeight = h; + + // Before we go reading the texture from memory, let's check for render-to-texture. + // We must do this early so we have the right w/h. + entry->framebuffer = 0; + for (size_t i = 0, n = fbCache_.size(); i < n; ++i) { + auto framebuffer = fbCache_[i]; + AttachFramebuffer(entry, framebuffer->fb_address, framebuffer); + } + + // If we ended up with a framebuffer, attach it - no texture decoding needed. + if (entry->framebuffer) { + SetTextureFramebuffer(entry, entry->framebuffer); + } + + nextTexture_ = entry; + nextNeedsRehash_ = entry->framebuffer == nullptr; + // We still need to rebuild, to allocate a texture. But we'll bail early. + nextNeedsRebuild_ = true; +} + +bool TextureCacheD3D11::CheckFullHash(TexCacheEntry *const entry, bool &doDelete) { + bool hashFail = false; + int w = gstate.getTextureWidth(0); + int h = gstate.getTextureHeight(0); + u32 fullhash = QuickTexHash(replacer, entry->addr, entry->bufw, w, h, GETextureFormat(entry->format), entry); + if (fullhash != entry->fullhash) { + hashFail = true; + } else { + if (g_Config.bTextureBackoffCache) { + if (entry->GetHashStatus() != TexCacheEntry::STATUS_HASHING && entry->numFrames > TexCacheEntry::FRAMES_REGAIN_TRUST) { + // Reset to STATUS_HASHING. + entry->SetHashStatus(TexCacheEntry::STATUS_HASHING); + entry->status &= ~TexCacheEntry::STATUS_CHANGE_FREQUENT; + } + } else if (entry->numFrames > TEXCACHE_FRAME_CHANGE_FREQUENT_REGAIN_TRUST) { + entry->status &= ~TexCacheEntry::STATUS_CHANGE_FREQUENT; + } + } + + if (hashFail) { + entry->status |= TexCacheEntry::STATUS_UNRELIABLE; + if (entry->numFrames < TEXCACHE_FRAME_CHANGE_FREQUENT) { + if (entry->status & TexCacheEntry::STATUS_FREE_CHANGE) { + entry->status &= ~TexCacheEntry::STATUS_FREE_CHANGE; + } else { + entry->status |= TexCacheEntry::STATUS_CHANGE_FREQUENT; + } + } + entry->numFrames = 0; + + // Don't give up just yet. Let's try the secondary cache if it's been invalidated before. + // If it's failed a bunch of times, then the second cache is just wasting time and VRAM. + if (g_Config.bTextureSecondaryCache) { + if (entry->numInvalidated > 2 && entry->numInvalidated < 128 && !lowMemoryMode_) { + u64 secondKey = fullhash | (u64)entry->cluthash << 32; + TexCache::iterator secondIter = secondCache.find(secondKey); + if (secondIter != secondCache.end()) { + TexCacheEntry *secondEntry = &secondIter->second; + if (secondEntry->Matches(entry->dim, entry->format, entry->maxLevel)) { + // Reset the numInvalidated value lower, we got a match. + if (entry->numInvalidated > 8) { + --entry->numInvalidated; + } + nextTexture_ = secondEntry; + return true; + } + } else { + secondKey = entry->fullhash | ((u64)entry->cluthash << 32); + secondCacheSizeEstimate_ += EstimateTexMemoryUsage(entry); + secondCache[secondKey] = *entry; + doDelete = false; + } + } + } + + // We know it failed, so update the full hash right away. + entry->fullhash = fullhash; + return false; + } + + return true; +} + +bool TextureCacheD3D11::HandleTextureChange(TexCacheEntry *const entry, const char *reason, bool initialMatch, bool doDelete) { + bool replaceImages = false; + + cacheSizeEstimate_ -= EstimateTexMemoryUsage(entry); + entry->numInvalidated++; + gpuStats.numTextureInvalidations++; + DEBUG_LOG(G3D, "Texture different or overwritten, reloading at %08x: %s", entry->addr, reason); + if (doDelete) { + if (initialMatch && standardScaleFactor_ == 1 && (entry->status & TexCacheEntry::STATUS_IS_SCALED) == 0) { + // Actually, if size and number of levels match, let's try to avoid deleting and recreating. + // Instead, let's use glTexSubImage to replace the images. + replaceImages = true; + } else { + if (entry->texturePtr == lastBoundTexture) { + lastBoundTexture = INVALID_TEX; + } + ReleaseTexture(entry); + entry->status &= ~TexCacheEntry::STATUS_IS_SCALED; + } + } + // Clear the reliable bit if set. + if (entry->GetHashStatus() == TexCacheEntry::STATUS_RELIABLE) { + entry->SetHashStatus(TexCacheEntry::STATUS_HASHING); + } + + // Also, mark any textures with the same address but different clut. They need rechecking. + if (entry->cluthash != 0) { + const u64 cachekeyMin = (u64)(entry->addr & 0x3FFFFFFF) << 32; + const u64 cachekeyMax = cachekeyMin + (1ULL << 32); + for (auto it = cache.lower_bound(cachekeyMin), end = cache.upper_bound(cachekeyMax); it != end; ++it) { + if (it->second.cluthash != entry->cluthash) { + it->second.status |= TexCacheEntry::STATUS_CLUT_RECHECK; + } + } + } + + return replaceImages; +} + +void TextureCacheD3D11::BuildTexture(TexCacheEntry *const entry, bool replaceImages) { + entry->status &= ~TexCacheEntry::STATUS_ALPHA_MASK; + + // For the estimate, we assume cluts always point to 8888 for simplicity. + cacheSizeEstimate_ += EstimateTexMemoryUsage(entry); + + // TODO: If a framebuffer is attached here, might end up with a bad entry.texture. + // Should just always create one here or something (like GLES.) + + if (entry->framebuffer) { + // Nothing else to do here. + return; + } + + if ((entry->bufw == 0 || (gstate.texbufwidth[0] & 0xf800) != 0) && entry->addr >= PSP_GetKernelMemoryEnd()) { + ERROR_LOG_REPORT(G3D, "Texture with unexpected bufw (full=%d)", gstate.texbufwidth[0] & 0xffff); + // Proceeding here can cause a crash. + return; + } + + // Adjust maxLevel to actually present levels.. + bool badMipSizes = false; + int maxLevel = entry->maxLevel; + for (int i = 0; i <= maxLevel; i++) { + // If encountering levels pointing to nothing, adjust max level. + u32 levelTexaddr = gstate.getTextureAddress(i); + if (!Memory::IsValidAddress(levelTexaddr)) { + maxLevel = i - 1; + break; + } + + if (i > 0 && gstate_c.Supports(GPU_SUPPORTS_TEXTURE_LOD_CONTROL)) { + int tw = gstate.getTextureWidth(i); + int th = gstate.getTextureHeight(i); + if (tw != 1 && tw != (gstate.getTextureWidth(i - 1) >> 1)) + badMipSizes = true; + else if (th != 1 && th != (gstate.getTextureHeight(i - 1) >> 1)) + badMipSizes = true; + } + } + + // In addition, simply don't load more than level 0 if g_Config.bMipMap is false. + if (!g_Config.bMipMap) { + maxLevel = 0; + } + + // If GLES3 is available, we can preallocate the storage, which makes texture loading more efficient. + DXGI_FORMAT dstFmt = GetDestFormat(GETextureFormat(entry->format), gstate.getClutPaletteFormat()); + + int scaleFactor = standardScaleFactor_; + + // Rachet down scale factor in low-memory mode. + if (lowMemoryMode_) { + // Keep it even, though, just in case of npot troubles. + scaleFactor = scaleFactor > 4 ? 4 : (scaleFactor > 2 ? 2 : 1); + } + + u64 cachekey = replacer.Enabled() ? entry->CacheKey() : 0; + int w = gstate.getTextureWidth(0); + int h = gstate.getTextureHeight(0); + ReplacedTexture &replaced = replacer.FindReplacement(cachekey, entry->fullhash, w, h); + if (replaced.GetSize(0, w, h)) { + if (replaceImages) { + // Since we're replacing the texture, we can't replace the image inside. + ReleaseTexture(entry); + replaceImages = false; + } + // We're replacing, so we won't scale. + scaleFactor = 1; + entry->status |= TexCacheEntry::STATUS_IS_SCALED; + if (g_Config.bMipMap) { + maxLevel = replaced.MaxLevel(); + badMipSizes = false; + } + } + + // Don't scale the PPGe texture. + if (entry->addr > 0x05000000 && entry->addr < PSP_GetKernelMemoryEnd()) + scaleFactor = 1; + if ((entry->status & TexCacheEntry::STATUS_CHANGE_FREQUENT) != 0 && scaleFactor != 1) { + // Remember for later that we /wanted/ to scale this texture. + entry->status |= TexCacheEntry::STATUS_TO_SCALE; + scaleFactor = 1; + } + + if (scaleFactor != 1) { + if (texelsScaledThisFrame_ >= TEXCACHE_MAX_TEXELS_SCALED) { + entry->status |= TexCacheEntry::STATUS_TO_SCALE; + scaleFactor = 1; + } else { + entry->status &= ~TexCacheEntry::STATUS_TO_SCALE; + entry->status |= TexCacheEntry::STATUS_IS_SCALED; + texelsScaledThisFrame_ += w * h; + } + } + + if (replaceImages) { + // Make sure it's not currently set. + ID3D11ShaderResourceView *srv = nullptr; + context_->PSSetShaderResources(0, 1, &srv); + } + + // Seems to cause problems in Tactics Ogre. + if (badMipSizes) { + maxLevel = 0; + } + + LoadTextureLevel(*entry, replaced, 0, maxLevel, replaceImages, scaleFactor, dstFmt); + ID3D11ShaderResourceView *textureView = DxView(entry); + if (!textureView) { + return; + } + + // Mipmapping is only enabled when texture scaling is disabled. + if (maxLevel > 0 && scaleFactor == 1) { + for (int i = 1; i <= maxLevel; i++) { + LoadTextureLevel(*entry, replaced, i, maxLevel, replaceImages, scaleFactor, dstFmt); + } + } + + if (replaced.Valid()) { + entry->SetAlphaStatus(TexCacheEntry::Status(replaced.AlphaStatus())); + } +} + +DXGI_FORMAT TextureCacheD3D11::GetDestFormat(GETextureFormat format, GEPaletteFormat clutFormat) const { + switch (format) { + case GE_TFMT_CLUT4: + case GE_TFMT_CLUT8: + case GE_TFMT_CLUT16: + case GE_TFMT_CLUT32: + return getClutDestFormatD3D11(clutFormat); + case GE_TFMT_4444: + return DXGI_FORMAT_B4G4R4A4_UNORM; + case GE_TFMT_5551: + return DXGI_FORMAT_B5G5R5A1_UNORM; + case GE_TFMT_5650: + return DXGI_FORMAT_B5G6R5_UNORM; + case GE_TFMT_8888: + case GE_TFMT_DXT1: + case GE_TFMT_DXT3: + case GE_TFMT_DXT5: + default: + return DXGI_FORMAT_B8G8R8A8_UNORM; + } +} + +TextureCacheD3D11::TexCacheEntry::Status TextureCacheD3D11::CheckAlpha(const u32 *pixelData, u32 dstFmt, int stride, int w, int h) { + CheckAlphaResult res; + switch (dstFmt) { + case DXGI_FORMAT_B4G4R4A4_UNORM: + res = CheckAlphaRGBA4444Basic(pixelData, stride, w, h); + break; + case DXGI_FORMAT_B5G5R5A1_UNORM: + res = CheckAlphaRGBA5551Basic(pixelData, stride, w, h); + break; + case DXGI_FORMAT_B5G6R5_UNORM: + // Never has any alpha. + res = CHECKALPHA_FULL; + break; + default: + res = CheckAlphaRGBA8888Basic(pixelData, stride, w, h); + break; + } + + return (TexCacheEntry::Status)res; +} + +ReplacedTextureFormat FromD3D11Format(u32 fmt) { + switch (fmt) { + case DXGI_FORMAT_B5G6R5_UNORM: return ReplacedTextureFormat::F_5650; + case DXGI_FORMAT_B5G5R5A1_UNORM: return ReplacedTextureFormat::F_5551; + case DXGI_FORMAT_B4G4R4A4_UNORM: return ReplacedTextureFormat::F_4444; + case DXGI_FORMAT_B8G8R8A8_UNORM: default: return ReplacedTextureFormat::F_8888; + } +} + +DXGI_FORMAT ToDXGIFormat(ReplacedTextureFormat fmt) { + switch (fmt) { + case ReplacedTextureFormat::F_5650: return DXGI_FORMAT_B5G6R5_UNORM; + case ReplacedTextureFormat::F_5551: return DXGI_FORMAT_B5G5R5A1_UNORM; + case ReplacedTextureFormat::F_4444: return DXGI_FORMAT_B4G4R4A4_UNORM; + case ReplacedTextureFormat::F_8888: default: return DXGI_FORMAT_B8G8R8A8_UNORM; + } +} + +void TextureCacheD3D11::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &replaced, int level, int maxLevel, bool replaceImages, int scaleFactor, u32 dstFmt) { + int w = gstate.getTextureWidth(level); + int h = gstate.getTextureHeight(level); + + ID3D11Texture2D *texture = DxTex(&entry); + if (level == 0 && (!replaceImages || texture == nullptr)) { + // Create texture + int levels = scaleFactor == 1 ? maxLevel + 1 : 1; + int tw = w, th = h; + DXGI_FORMAT tfmt = (DXGI_FORMAT)(dstFmt); + if (replaced.GetSize(level, tw, th)) { + tfmt = ToDXGIFormat(replaced.Format(level)); + } else { + tw *= scaleFactor; + th *= scaleFactor; + if (scaleFactor > 1) { + tfmt = DXGI_FORMAT_B8G8R8A8_UNORM; + } + } + + D3D11_TEXTURE2D_DESC desc{}; + // TODO: Make it DEFAULT or immutable, required for multiple mip levels. Will require some code restructuring though. + desc.CPUAccessFlags = 0; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.ArraySize = 1; + desc.SampleDesc.Count = 1; + desc.Width = tw; + desc.Height = th; + desc.Format = tfmt; + desc.MipLevels = levels; + desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + + HRESULT hr = device_->CreateTexture2D(&desc, nullptr, &texture); + if (FAILED(hr)) { + INFO_LOG(G3D, "Failed to create D3D texture"); + ReleaseTexture(&entry); + return; + } + ID3D11ShaderResourceView *view; + hr = device_->CreateShaderResourceView(texture, nullptr, &view); + entry.texturePtr = texture; + entry.textureView = view; + } + + gpuStats.numTexturesDecoded++; + // For UpdateSubresource, we can't decode directly into the texture so we allocate a buffer :( + u32 *mapData; + int mapRowPitch; + + if (replaced.GetSize(level, w, h)) { + mapData = new u32[w * h]{}; + mapRowPitch = w * 4; + replaced.Load(level, mapData, mapRowPitch); + dstFmt = ToDXGIFormat(replaced.Format(level)); + } else { + GETextureFormat tfmt = (GETextureFormat)entry.format; + GEPaletteFormat clutformat = gstate.getClutPaletteFormat(); + u32 texaddr = gstate.getTextureAddress(level); + int bufw = GetTextureBufw(level, texaddr, tfmt); + int bpp = dstFmt == DXGI_FORMAT_B8G8R8A8_UNORM ? 4 : 2; + mapRowPitch = std::max(bufw, w) * 4; + mapData = new u32[mapRowPitch / 4 * h]{}; + + u32 *pixelData = (u32 *)mapData; + int decPitch = mapRowPitch; + if (scaleFactor > 1) { + tmpTexBufRearrange.resize(std::max(bufw, w) * h); + pixelData = tmpTexBufRearrange.data(); + // We want to end up with a neatly packed texture for scaling. + decPitch = w * bpp; + } + + bool decSuccess = DecodeTextureLevel((u8 *)pixelData, decPitch, tfmt, clutformat, texaddr, level, bufw, false, false); + if (!decSuccess) { + memset(pixelData, 0, decPitch * h); + } + + if (scaleFactor > 1) { + scaler.ScaleAlways((u32 *)mapData, pixelData, dstFmt, w, h, scaleFactor); + pixelData = (u32 *)mapData; + + // We always end up at 8888. Other parts assume this. + assert(dstFmt == DXGI_FORMAT_B8G8R8A8_UNORM); + bpp = sizeof(u32); + decPitch = w * bpp; + + if (decPitch != mapRowPitch) { + // Rearrange in place to match the requested pitch. + // (it can only be larger than w * bpp, and a match is likely.) + // Note! This is bad because it reads the mapped memory! TODO: Look into if DX9 does this right. + for (int y = h - 1; y >= 0; --y) { + memcpy((u8 *)mapData + mapRowPitch * y, (u8 *)mapData + decPitch * y, w * bpp); + } + decPitch = mapRowPitch; + } + } + + if ((entry.status & TexCacheEntry::STATUS_CHANGE_FREQUENT) == 0) { + TexCacheEntry::Status alphaStatus = CheckAlpha(pixelData, dstFmt, decPitch / bpp, w, h); + entry.SetAlphaStatus(alphaStatus, level); + } else { + entry.SetAlphaStatus(TexCacheEntry::STATUS_ALPHA_UNKNOWN); + } + + if (replacer.Enabled()) { + ReplacedTextureDecodeInfo replacedInfo; + replacedInfo.cachekey = entry.CacheKey(); + replacedInfo.hash = entry.fullhash; + replacedInfo.addr = entry.addr; + replacedInfo.isVideo = videos_.find(entry.addr & 0x3FFFFFFF) != videos_.end(); + replacedInfo.isFinal = (entry.status & TexCacheEntry::STATUS_TO_SCALE) == 0; + replacedInfo.scaleFactor = scaleFactor; + replacedInfo.fmt = FromD3D11Format(dstFmt); + + replacer.NotifyTextureDecoded(replacedInfo, pixelData, decPitch, level, w, h); + } + } + + context_->UpdateSubresource(texture, level, nullptr, mapData, mapRowPitch, 0); + delete[] mapData; +} + +bool TextureCacheD3D11::DecodeTexture(u8 *output, const GPUgstate &state) { + OutputDebugStringA("TextureCache::DecodeTexture : FixMe\r\n"); + return true; +} diff --git a/GPU/D3D11/TextureCacheD3D11.h b/GPU/D3D11/TextureCacheD3D11.h new file mode 100644 index 0000000000..8d5effafd0 --- /dev/null +++ b/GPU/D3D11/TextureCacheD3D11.h @@ -0,0 +1,129 @@ +// Copyright (c) 2017- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program 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 General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +#include + +#include "../Globals.h" +#include "GPU/GPU.h" +#include "GPU/GPUInterface.h" +#include "GPU/D3D11/TextureScalerD3D11.h" +#include "GPU/Common/TextureCacheCommon.h" + +struct VirtualFramebuffer; + +class FramebufferManagerD3D11; +class DepalShaderCacheD3D11; +class ShaderManagerD3D11; + +class SamplerCacheD3D11 { +public: + SamplerCacheD3D11() {} + ~SamplerCacheD3D11(); + ID3D11SamplerState *GetOrCreateSampler(ID3D11Device *device, const SamplerCacheKey &key); + +private: + std::map cache_; +}; + +class TextureCacheD3D11 : public TextureCacheCommon { +public: + TextureCacheD3D11(Draw::DrawContext *draw); + ~TextureCacheD3D11(); + + void SetTexture(bool force = false); + + void Clear(bool delete_them); + void StartFrame(); + + void SetFramebufferManager(FramebufferManagerD3D11 *fbManager); + void SetDepalShaderCache(DepalShaderCacheD3D11 *dpCache) { + depalShaderCache_ = dpCache; + } + void SetShaderManager(ShaderManagerD3D11 *sm) { + shaderManager_ = sm; + } + + // Only used by Qt UI? + bool DecodeTexture(u8 *output, const GPUgstate &state); + + void ForgetLastTexture() override; + + void SetFramebufferSamplingParams(u16 bufferWidth, u16 bufferHeight, SamplerCacheKey &key); + + void ApplyTexture(); + +protected: + void Unbind() override; + +private: + void Decimate(); // Run this once per frame to get rid of old textures. + void DeleteTexture(TexCache::iterator it); + void UpdateSamplingParams(TexCacheEntry &entry, SamplerCacheKey &key); + void LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &replaced, int level, int maxLevel, bool replaceImages, int scaleFactor, u32 dstFmt); + DXGI_FORMAT GetDestFormat(GETextureFormat format, GEPaletteFormat clutFormat) const; + TexCacheEntry::Status CheckAlpha(const u32 *pixelData, u32 dstFmt, int stride, int w, int h); + u32 GetCurrentClutHash(); + void UpdateCurrentClut(GEPaletteFormat clutFormat, u32 clutBase, bool clutIndexIsSimple); + void ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFramebuffer *framebuffer); + + bool CheckFullHash(TexCacheEntry *const entry, bool &doDelete); + bool HandleTextureChange(TexCacheEntry *const entry, const char *reason, bool initialMatch, bool doDelete); + void BuildTexture(TexCacheEntry *const entry, bool replaceImages); + + ID3D11Device *device_; + ID3D11DeviceContext *context_; + + ID3D11Texture2D *DxTex(TexCacheEntry *entry) { + return (ID3D11Texture2D *)entry->texturePtr; + } + ID3D11ShaderResourceView *DxView(TexCacheEntry *entry) { + return (ID3D11ShaderResourceView *)entry->textureView; + } + void ReleaseTexture(TexCacheEntry *entry) { + ID3D11Texture2D *texture = (ID3D11Texture2D *)entry->texturePtr; + ID3D11ShaderResourceView *view = (ID3D11ShaderResourceView *)entry->textureView; + if (texture) { + texture->Release(); + entry->texturePtr = nullptr; + } + if (view) { + view->Release(); + entry->textureView = nullptr; + } + } + + TextureScalerD3D11 scaler; + + SamplerCacheD3D11 samplerCache_; + + u32 clutHash_; + + ID3D11ShaderResourceView *lastBoundTexture; + float maxAnisotropyLevel; + + int decimationCounter_; + int texelsScaledThisFrame_; + int timesInvalidatedAllThisFrame_; + + FramebufferManagerD3D11 *framebufferManagerD3D11_; + DepalShaderCacheD3D11 *depalShaderCache_; + ShaderManagerD3D11 *shaderManager_; +}; + +DXGI_FORMAT getClutDestFormatD3D11(GEPaletteFormat format); diff --git a/GPU/D3D11/TextureScalerD3D11.cpp b/GPU/D3D11/TextureScalerD3D11.cpp new file mode 100644 index 0000000000..a71ca42c8f --- /dev/null +++ b/GPU/D3D11/TextureScalerD3D11.cpp @@ -0,0 +1,57 @@ +// Copyright (c) 2012- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program 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 General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include + +#include +#include "Common/ColorConv.h" +#include "Common/ThreadPools.h" +#include "GPU/Common/TextureScalerCommon.h" +#include "GPU/D3D11/TextureScalerD3D11.h" +#include "GPU/D3D11/GPU_D3D11.h" + +int TextureScalerD3D11::BytesPerPixel(u32 format) { + return format == GE_FORMAT_8888 ? 4 : 2; +} + +u32 TextureScalerD3D11::Get8888Format() { + return DXGI_FORMAT_R8G8B8A8_UNORM; +} + +void TextureScalerD3D11::ConvertTo8888(u32 format, u32* source, u32* &dest, int width, int height) { + switch (format) { + case GE_FORMAT_8888: + dest = source; // already fine + break; + + case GE_FORMAT_4444: + GlobalThreadPool::Loop(std::bind(&convert4444_dx9, (u16*)source, dest, width, std::placeholders::_1, std::placeholders::_2), 0, height); + break; + + case GE_FORMAT_565: + GlobalThreadPool::Loop(std::bind(&convert565_dx9, (u16*)source, dest, width, std::placeholders::_1, std::placeholders::_2), 0, height); + break; + + case GE_FORMAT_5551: + GlobalThreadPool::Loop(std::bind(&convert5551_dx9, (u16*)source, dest, width, std::placeholders::_1, std::placeholders::_2), 0, height); + break; + + default: + dest = source; + ERROR_LOG(G3D, "iXBRZTexScaling: unsupported texture format"); + } +} \ No newline at end of file diff --git a/GPU/D3D11/TextureScalerD3D11.h b/GPU/D3D11/TextureScalerD3D11.h new file mode 100644 index 0000000000..298045342a --- /dev/null +++ b/GPU/D3D11/TextureScalerD3D11.h @@ -0,0 +1,29 @@ +// Copyright (c) 2012- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program 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 General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +#include "Common/CommonTypes.h" +#include "GPU/Common/TextureScalerCommon.h" + +class TextureScalerD3D11 : public TextureScalerCommon { +private: + // NOTE: We use GE formats, D3D11 doesn't support 4444 + void ConvertTo8888(u32 format, u32* source, u32* &dest, int width, int height) override; + int BytesPerPixel(u32 format) override; + u32 Get8888Format() override; +}; \ No newline at end of file diff --git a/GPU/D3D11/VertexShaderGeneratorD3D11.cpp b/GPU/D3D11/VertexShaderGeneratorD3D11.cpp new file mode 100644 index 0000000000..d069225b3b --- /dev/null +++ b/GPU/D3D11/VertexShaderGeneratorD3D11.cpp @@ -0,0 +1,25 @@ +// Copyright (c) 2017- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program 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 General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "GPU/Common/ShaderCommon.h" +#include "GPU/D3D11/VertexShaderGeneratorD3D11.h" +#include "GPU/Directx9/VertexShaderGeneratorDX9.h" + +void GenerateVertexShaderD3D11(const ShaderID &id, char *buffer, bool *usesLighting) { + *usesLighting = true; + DX9::GenerateVertexShaderHLSL(id, buffer, HLSL_D3D11); +} diff --git a/GPU/D3D11/VertexShaderGeneratorD3D11.h b/GPU/D3D11/VertexShaderGeneratorD3D11.h new file mode 100644 index 0000000000..d8936e9722 --- /dev/null +++ b/GPU/D3D11/VertexShaderGeneratorD3D11.h @@ -0,0 +1,22 @@ +// Copyright (c) 2017- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program 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 General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +#include "GPU/Common/ShaderId.h" + +void GenerateVertexShaderD3D11(const ShaderID &id, char *buffer, bool *usesLighting); diff --git a/GPU/Directx9/DepalettizeShaderDX9.h b/GPU/Directx9/DepalettizeShaderDX9.h index a0450c1785..6a3ede46cb 100644 --- a/GPU/Directx9/DepalettizeShaderDX9.h +++ b/GPU/Directx9/DepalettizeShaderDX9.h @@ -16,7 +16,6 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include -#include #include #include "Common/CommonTypes.h" diff --git a/GPU/Directx9/DrawEngineDX9.cpp b/GPU/Directx9/DrawEngineDX9.cpp index f7ff990953..d68d1d1804 100644 --- a/GPU/Directx9/DrawEngineDX9.cpp +++ b/GPU/Directx9/DrawEngineDX9.cpp @@ -869,16 +869,14 @@ rotateVBO: } dxstate.colorMask.set((mask & D3DCLEAR_TARGET) != 0, (mask & D3DCLEAR_TARGET) != 0, (mask & D3DCLEAR_TARGET) != 0, (mask & D3DCLEAR_STENCIL) != 0); - device_->Clear(0, NULL, mask, SwapRB(clearColor), clearDepth, clearColor >> 24); - int scissorX1 = gstate.getScissorX1(); - int scissorY1 = gstate.getScissorY1(); int scissorX2 = gstate.getScissorX2() + 1; int scissorY2 = gstate.getScissorY2() + 1; framebufferManager_->SetSafeSize(scissorX2, scissorY2); - if (g_Config.bBlockTransferGPU && (gstate_c.featureFlags & GPU_USE_CLEAR_RAM_HACK) && gstate.isClearModeColorMask() && (gstate.isClearModeAlphaMask() || gstate.FrameBufFormat() == GE_FORMAT_565)) { + int scissorX1 = gstate.getScissorX1(); + int scissorY1 = gstate.getScissorY1(); ApplyClearToMemory(scissorX1, scissorY1, scissorX2, scissorY2, clearColor); } } @@ -920,7 +918,7 @@ bool DrawEngineDX9::IsCodePtrVertexDecoder(const u8 *ptr) const { return decJitCache_->IsInSpace(ptr); } -void DX9::DrawEngineDX9::TessellationDataTransferDX9::SendDataToShader(const float * pos, const float * tex, const float * col, int size, bool hasColor, bool hasTexCoords) +void DrawEngineDX9::TessellationDataTransferDX9::SendDataToShader(const float * pos, const float * tex, const float * col, int size, bool hasColor, bool hasTexCoords) { } diff --git a/GPU/Directx9/FramebufferDX9.cpp b/GPU/Directx9/FramebufferDX9.cpp index 1b37e4ec13..bb872c1f2f 100644 --- a/GPU/Directx9/FramebufferDX9.cpp +++ b/GPU/Directx9/FramebufferDX9.cpp @@ -982,7 +982,7 @@ static void DXSetViewport(float x, float y, float w, float h, float minZ, float // TODO: Optimize. for (int yp = 0; yp < h; ++yp) { for (int xp = 0; xp < w; ++xp) { - const int offset = (yp + y) & vfb->z_stride + x + xp; + const int offset = (yp + y) * vfb->z_stride + x + xp; float scaled = FromScaledDepth((packed[offset] & 0x00FFFFFF) * (1.0f / 16777215.0f)); if (scaled <= 0.0f) { diff --git a/GPU/Directx9/GPU_DX9.cpp b/GPU/Directx9/GPU_DX9.cpp index ef582c1e18..3f87dc2e0f 100644 --- a/GPU/Directx9/GPU_DX9.cpp +++ b/GPU/Directx9/GPU_DX9.cpp @@ -513,6 +513,8 @@ void GPU_DX9::CheckGPUFeatures() { GPU_DX9::~GPU_DX9() { framebufferManagerDX9_->DestroyAllFBOs(true); + delete framebufferManagerDX9_; + delete textureCache_; shaderManagerDX9_->ClearCache(true); delete shaderManagerDX9_; } @@ -942,8 +944,8 @@ void GPU_DX9::GetStats(char *buffer, size_t bufsize) { (int)textureCacheDX9_->NumLoadedTextures(), gpuStats.numTexturesDecoded, gpuStats.numTextureInvalidations, - shaderManagerDX9_->NumVertexShaders(), - shaderManagerDX9_->NumFragmentShaders() + shaderManagerDX9_->GetNumVertexShaders(), + shaderManagerDX9_->GetNumFragmentShaders() ); } diff --git a/GPU/Directx9/PixelShaderGeneratorDX9.cpp b/GPU/Directx9/PixelShaderGeneratorDX9.cpp index ace6be1069..b392804c3b 100644 --- a/GPU/Directx9/PixelShaderGeneratorDX9.cpp +++ b/GPU/Directx9/PixelShaderGeneratorDX9.cpp @@ -24,6 +24,7 @@ #include "GPU/ge_constants.h" #include "GPU/Common/GPUStateUtils.h" #include "GPU/GPUState.h" +#include "GPU/Common/ShaderUniforms.h" #define WRITE p+=sprintf @@ -33,7 +34,7 @@ namespace DX9 { // Missing: Z depth range // Also, logic ops etc, of course, as they are not supported in DX9. -bool GenerateFragmentShaderDX9(const ShaderID &id, char *buffer) { +bool GenerateFragmentShaderHLSL(const ShaderID &id, char *buffer, ShaderLanguage lang) { char *p = buffer; bool lmode = id.Bit(FS_BIT_LMODE); @@ -68,46 +69,60 @@ bool GenerateFragmentShaderDX9(const ShaderID &id, char *buffer) { StencilValueType replaceAlphaWithStencilType = (StencilValueType)id.Bits(FS_BIT_REPLACE_ALPHA_WITH_STENCIL_TYPE, 4); - if (doTexture) - WRITE(p, "sampler tex : register(s0);\n"); - if (!isModeClear && replaceBlend > REPLACE_BLEND_STANDARD) { - if (replaceBlend == REPLACE_BLEND_COPY_FBO) { - WRITE(p, "float2 u_fbotexSize : register(c%i);\n", CONST_PS_FBOTEXSIZE); - WRITE(p, "sampler fbotex : register(s1);\n"); + if (lang == HLSL_DX9) { + if (doTexture) + WRITE(p, "sampler tex : register(s0);\n"); + if (!isModeClear && replaceBlend > REPLACE_BLEND_STANDARD) { + if (replaceBlend == REPLACE_BLEND_COPY_FBO) { + WRITE(p, "float2 u_fbotexSize : register(c%i);\n", CONST_PS_FBOTEXSIZE); + WRITE(p, "sampler fbotex : register(s1);\n"); + } + if (replaceBlendFuncA >= GE_SRCBLEND_FIXA) { + WRITE(p, "float3 u_blendFixA : register(c%i);\n", CONST_PS_BLENDFIXA); + } + if (replaceBlendFuncB >= GE_DSTBLEND_FIXB) { + WRITE(p, "float3 u_blendFixB : register(c%i);\n", CONST_PS_BLENDFIXB); + } } - if (replaceBlendFuncA >= GE_SRCBLEND_FIXA) { - WRITE(p, "float3 u_blendFixA : register(c%i);\n", CONST_PS_BLENDFIXA); + if (gstate_c.needShaderTexClamp && doTexture) { + WRITE(p, "float4 u_texclamp : register(c%i);\n", CONST_PS_TEXCLAMP); + if (textureAtOffset) { + WRITE(p, "float2 u_texclampoff : register(c%i);\n", CONST_PS_TEXCLAMPOFF); + } } - if (replaceBlendFuncB >= GE_DSTBLEND_FIXB) { - WRITE(p, "float3 u_blendFixB : register(c%i);\n", CONST_PS_BLENDFIXB); - } - } - if (gstate_c.needShaderTexClamp && doTexture) { - WRITE(p, "float4 u_texclamp : register(c%i);\n", CONST_PS_TEXCLAMP); - if (textureAtOffset) { - WRITE(p, "float2 u_texclampoff : register(c%i);\n", CONST_PS_TEXCLAMPOFF); - } - } - if (enableAlphaTest || enableColorTest) { - WRITE(p, "float4 u_alphacolorref : register(c%i);\n", CONST_PS_ALPHACOLORREF); - WRITE(p, "float4 u_alphacolormask : register(c%i);\n", CONST_PS_ALPHACOLORMASK); - } - if (stencilToAlpha && replaceAlphaWithStencilType == STENCIL_VALUE_UNIFORM) { - WRITE(p, "float u_stencilReplaceValue : register(c%i);\n", CONST_PS_STENCILREPLACE); - } - if (doTexture && texFunc == GE_TEXFUNC_BLEND) { - WRITE(p, "float3 u_texenv : register(c%i);\n", CONST_PS_TEXENV); - } - if (enableFog) { - WRITE(p, "float3 u_fogcolor : register(c%i);\n", CONST_PS_FOGCOLOR); + if (enableAlphaTest || enableColorTest) { + WRITE(p, "float4 u_alphacolorref : register(c%i);\n", CONST_PS_ALPHACOLORREF); + WRITE(p, "float4 u_alphacolormask : register(c%i);\n", CONST_PS_ALPHACOLORMASK); + } + if (stencilToAlpha && replaceAlphaWithStencilType == STENCIL_VALUE_UNIFORM) { + WRITE(p, "float u_stencilReplaceValue : register(c%i);\n", CONST_PS_STENCILREPLACE); + } + if (doTexture && texFunc == GE_TEXFUNC_BLEND) { + WRITE(p, "float3 u_texenv : register(c%i);\n", CONST_PS_TEXENV); + } + if (enableFog) { + WRITE(p, "float3 u_fogcolor : register(c%i);\n", CONST_PS_FOGCOLOR); + } + } else { + WRITE(p, "SamplerState samp : register(s0);\n"); + WRITE(p, "Texture2D tex : register(t0);\n"); + WRITE(p, "cbuffer base : register(b0) {\n%s};\n", cb_baseStr); } if (enableAlphaTest) { - WRITE(p, "float roundAndScaleTo255f(float x) { return floor(x * 255.0f + 0.5f); }\n"); + if (lang == HLSL_D3D11) { + WRITE(p, "uint roundAndScaleTo255i(float x) { return uint(floor(x * 255.0f + 0.5f)); }\n"); + } else { + WRITE(p, "float roundAndScaleTo255f(float x) { return floor(x * 255.0f + 0.5f); }\n"); + } } if (enableColorTest) { - WRITE(p, "float3 roundAndScaleTo255v(float3 x) { return floor(x * 255.0f + 0.5f); }\n"); + if (lang == HLSL_D3D11) { + WRITE(p, "uint3 roundAndScaleTo255iv(float3 x) { return uint3(floor(x * 255.0f + 0.5f)); }\n"); + } else { + WRITE(p, "float3 roundAndScaleTo255v(float3 x) { return floor(x * 255.0f + 0.5f); }\n"); + } } WRITE(p, "struct PS_IN {\n"); @@ -119,11 +134,25 @@ bool GenerateFragmentShaderDX9(const ShaderID &id, char *buffer) { WRITE(p, " float3 v_color1: COLOR1;\n"); } if (enableFog) { - WRITE(p, " float2 v_fogdepth: TEXCOORD1;\n"); + WRITE(p, " float v_fogdepth: TEXCOORD1;\n"); } WRITE(p, "};\n"); - WRITE(p, "float4 main( PS_IN In ) : COLOR\n"); - WRITE(p, "{\n"); + + if (lang == HLSL_DX9) { + WRITE(p, "float4 main( PS_IN In ) : COLOR {\n"); + } else { + WRITE(p, "struct PS_OUT {\n"); + if (stencilToAlpha == REPLACE_ALPHA_DUALSOURCE) { + WRITE(p, " float4 target : SV_Target0;\n"); + WRITE(p, " float4 target1 : SV_Target1;\n"); + } + else { + WRITE(p, " float4 target : SV_Target;\n"); + } + WRITE(p, "};\n"); + WRITE(p, "PS_OUT main( PS_IN In ) {\n"); + WRITE(p, " PS_OUT outfragment;\n"); + } if (isModeClear) { // Clear mode does not allow any fancy shading. @@ -173,10 +202,18 @@ bool GenerateFragmentShaderDX9(const ShaderID &id, char *buffer) { doTextureProjection = false; } - if (doTextureProjection) { - WRITE(p, " float4 t = tex2Dproj(tex, float4(In.v_texcoord.x, In.v_texcoord.y, 0, In.v_texcoord.z))%s;\n", bgraTexture ? ".bgra" : ""); + if (lang == HLSL_D3D11) { + if (doTextureProjection) { + WRITE(p, " float4 t = tex.Sample(samp, In.v_texcoord.xy / In.v_texcoord.z)%s;\n", bgraTexture ? ".bgra" : ""); + } else { + WRITE(p, " float4 t = tex.Sample(samp, %s.xy)%s;\n", texcoord, bgraTexture ? ".bgra" : ""); + } } else { - WRITE(p, " float4 t = tex2D(tex, %s.xy)%s;\n", texcoord, bgraTexture ? ".bgra" : ""); + if (doTextureProjection) { + WRITE(p, " float4 t = tex2Dproj(tex, float4(In.v_texcoord.x, In.v_texcoord.y, 0, In.v_texcoord.z))%s;\n", bgraTexture ? ".bgra" : ""); + } else { + WRITE(p, " float4 t = tex2D(tex, %s.xy)%s;\n", texcoord, bgraTexture ? ".bgra" : ""); + } } WRITE(p, " float4 p = In.v_color0;\n"); @@ -241,10 +278,17 @@ bool GenerateFragmentShaderDX9(const ShaderID &id, char *buffer) { const char *alphaTestFuncs[] = { "#", "#", " != ", " == ", " >= ", " > ", " <= ", " < " }; // never/always don't make sense if (alphaTestFuncs[alphaTestFunc][0] != '#') { // TODO: Rewrite this to use clip() appropriately (like, clip(v.a - u_alphacolorref.a)) - WRITE(p, " if (roundAndScaleTo255f(v.a) %s u_alphacolorref.a) clip(-1);\n", alphaTestFuncs[alphaTestFunc]); + if (lang == HLSL_D3D11) { + WRITE(p, " if ((roundAndScaleTo255i(v.a) & u_alphacolormask.a) %s u_alphacolorref.a) discard;\n", alphaTestFuncs[alphaTestFunc]); + } else { + WRITE(p, " if (roundAndScaleTo255f(v.a) %s u_alphacolorref.a) clip(-1);\n", alphaTestFuncs[alphaTestFunc]); + } } else { // This means NEVER. See above. - WRITE(p, " clip(-1);\n"); + if (lang == HLSL_D3D11) + WRITE(p, " discard;\n"); + else + WRITE(p, " clip(-1);\n"); } } } @@ -266,9 +310,15 @@ bool GenerateFragmentShaderDX9(const ShaderID &id, char *buffer) { const char *colorTestFuncs[] = { "#", "#", " != ", " == " }; // never/always don't make sense if (colorTestFuncs[colorTestFunc][0] != '#') { const char * test = colorTestFuncs[colorTestFunc]; - WRITE(p, " float3 colortest = roundAndScaleTo255v(v.rgb);\n"); - WRITE(p, " if ((colortest.r %s u_alphacolorref.r) && (colortest.g %s u_alphacolorref.g) && (colortest.b %s u_alphacolorref.b )) clip(-1);\n", test, test, test); - } else { + if (lang == HLSL_D3D11) { + WRITE(p, " uint3 v_scaled = roundAndScaleTo255iv(v.rgb);\n"); + WRITE(p, " if ((v_scaled & u_alphacolormask.rgb) %s (u_alphacolorref.rgb & u_alphacolormask.rgb)) discard;\n", colorTestFuncs[colorTestFunc]); + } else { + WRITE(p, " float3 colortest = roundAndScaleTo255v(v.rgb);\n"); + WRITE(p, " if ((colortest.r %s u_alphacolorref.r) && (colortest.g %s u_alphacolorref.g) && (colortest.b %s u_alphacolorref.b )) clip(-1);\n", test, test, test); + } + } + else { WRITE(p, " clip(-1);\n"); } } @@ -282,7 +332,7 @@ bool GenerateFragmentShaderDX9(const ShaderID &id, char *buffer) { } if (enableFog) { - WRITE(p, " float fogCoef = clamp(In.v_fogdepth.x, 0.0, 1.0);\n"); + WRITE(p, " float fogCoef = clamp(In.v_fogdepth, 0.0, 1.0);\n"); WRITE(p, " v = lerp(float4(u_fogcolor, v.a), v, fogCoef);\n"); } @@ -321,7 +371,7 @@ bool GenerateFragmentShaderDX9(const ShaderID &id, char *buffer) { if (stencilToAlpha != REPLACE_ALPHA_NO) { switch (replaceAlphaWithStencilType) { case STENCIL_VALUE_UNIFORM: - replacedAlpha = "u_stencilReplaceValue"; + replacedAlpha = lang == HLSL_D3D11 ? "u_fogcoef_stencilreplace.z" : "u_stencilReplaceValue"; break; case STENCIL_VALUE_ZERO: @@ -355,11 +405,6 @@ bool GenerateFragmentShaderDX9(const ShaderID &id, char *buffer) { } switch (stencilToAlpha) { - case REPLACE_ALPHA_DUALSOURCE: - WRITE(p, " v.a = %s;\n", replacedAlpha.c_str()); - // TODO: Output the second color as well using original v.a. - break; - case REPLACE_ALPHA_YES: WRITE(p, " v.a = %s;\n", replacedAlpha.c_str()); break; @@ -381,7 +426,19 @@ bool GenerateFragmentShaderDX9(const ShaderID &id, char *buffer) { break; } - WRITE(p, " return v;\n"); + if (lang == HLSL_D3D11) { + if (stencilToAlpha == REPLACE_ALPHA_DUALSOURCE) { + WRITE(p, " outfragment.target = float4(v.rgb, %s);\n", replacedAlpha.c_str()); + WRITE(p, " outfragment.target1 = float4(0.0, 0.0, 0.0, v.a);\n"); + WRITE(p, " return outfragment;\n"); + } + else { + WRITE(p, " outfragment.target = v;\n"); + WRITE(p, " return outfragment;\n"); + } + } else { + WRITE(p, " return v;\n"); + } WRITE(p, "}\n"); return true; } diff --git a/GPU/Directx9/PixelShaderGeneratorDX9.h b/GPU/Directx9/PixelShaderGeneratorDX9.h index 44914dfd2a..19318248d9 100644 --- a/GPU/Directx9/PixelShaderGeneratorDX9.h +++ b/GPU/Directx9/PixelShaderGeneratorDX9.h @@ -17,13 +17,12 @@ #pragma once -#include "Globals.h" - #include "GPU/Common/ShaderId.h" +#include "GPU/Common/ShaderCommon.h" namespace DX9 { -bool GenerateFragmentShaderDX9(const ShaderID &id, char *buffer); +bool GenerateFragmentShaderHLSL(const ShaderID &id, char *buffer, ShaderLanguage lang = HLSL_DX9); #define CONST_PS_TEXENV 0 #define CONST_PS_ALPHACOLORREF 1 diff --git a/GPU/Directx9/ShaderManagerDX9.cpp b/GPU/Directx9/ShaderManagerDX9.cpp index dc57958fa9..1e852631e0 100644 --- a/GPU/Directx9/ShaderManagerDX9.cpp +++ b/GPU/Directx9/ShaderManagerDX9.cpp @@ -576,7 +576,7 @@ VSShader *ShaderManagerDX9::ApplyShader(int prim, u32 vertType) { VSShader *vs; if (vsIter == vsCache_.end()) { // Vertex shader not in cache. Let's compile it. - GenerateVertexShaderDX9(VSID, codeBuffer_); + GenerateVertexShaderHLSL(VSID, codeBuffer_); vs = new VSShader(device_, VSID, codeBuffer_, useHWTransform); if (vs->Failed()) { @@ -592,7 +592,7 @@ VSShader *ShaderManagerDX9::ApplyShader(int prim, u32 vertType) { // next time and we'll do this over and over... // Can still work with software transform. - GenerateVertexShaderDX9(VSID, codeBuffer_); + GenerateVertexShaderHLSL(VSID, codeBuffer_); vs = new VSShader(device_, VSID, codeBuffer_, false); } @@ -606,7 +606,7 @@ VSShader *ShaderManagerDX9::ApplyShader(int prim, u32 vertType) { PSShader *fs; if (fsIter == fsCache_.end()) { // Fragment shader not in cache. Let's compile it. - GenerateFragmentShaderDX9(FSID, codeBuffer_); + GenerateFragmentShaderHLSL(FSID, codeBuffer_); fs = new PSShader(device_, FSID, codeBuffer_); fsCache_[FSID] = fs; } else { diff --git a/GPU/Directx9/ShaderManagerDX9.h b/GPU/Directx9/ShaderManagerDX9.h index e721cd9f46..364802a539 100644 --- a/GPU/Directx9/ShaderManagerDX9.h +++ b/GPU/Directx9/ShaderManagerDX9.h @@ -86,8 +86,8 @@ public: void DirtyShader(); void DirtyLastShader(); - int NumVertexShaders() const { return (int)vsCache_.size(); } - int NumFragmentShaders() const { return (int)fsCache_.size(); } + int GetNumVertexShaders() const { return (int)vsCache_.size(); } + int GetNumFragmentShaders() const { return (int)fsCache_.size(); } std::vector DebugGetShaderIDs(DebugShaderType type); std::string DebugGetShaderString(std::string id, DebugShaderType type, DebugShaderStringType stringType); diff --git a/GPU/Directx9/TextureCacheDX9.cpp b/GPU/Directx9/TextureCacheDX9.cpp index cab431d1e5..eccb387ec8 100644 --- a/GPU/Directx9/TextureCacheDX9.cpp +++ b/GPU/Directx9/TextureCacheDX9.cpp @@ -1027,8 +1027,6 @@ void TextureCacheDX9::BuildTexture(TexCacheEntry *const entry, bool replaceImage if (replaced.Valid()) { entry->SetAlphaStatus(TexCacheEntry::Status(replaced.AlphaStatus())); } - - UpdateSamplingParams(*entry, true); } D3DFORMAT TextureCacheDX9::GetDestFormat(GETextureFormat format, GEPaletteFormat clutFormat) const { diff --git a/GPU/Directx9/TextureCacheDX9.h b/GPU/Directx9/TextureCacheDX9.h index 06df6117a9..2c5a8a8401 100644 --- a/GPU/Directx9/TextureCacheDX9.h +++ b/GPU/Directx9/TextureCacheDX9.h @@ -18,6 +18,7 @@ #pragma once #include + #include #include "../Globals.h" diff --git a/GPU/Directx9/VertexShaderGeneratorDX9.cpp b/GPU/Directx9/VertexShaderGeneratorDX9.cpp index 86a7a92ce3..e4b558e75d 100644 --- a/GPU/Directx9/VertexShaderGeneratorDX9.cpp +++ b/GPU/Directx9/VertexShaderGeneratorDX9.cpp @@ -29,6 +29,7 @@ #include "GPU/Directx9/VertexShaderGeneratorDX9.h" #include "GPU/Common/VertexDecoderCommon.h" +#include "GPU/Common/ShaderUniforms.h" #undef WRITE @@ -54,7 +55,7 @@ enum DoLightComputation { LIGHT_FULL, }; -void GenerateVertexShaderDX9(const ShaderID &id, char *buffer) { +void GenerateVertexShaderHLSL(const ShaderID &id, char *buffer, ShaderLanguage lang) { char *p = buffer; const u32 vertType = gstate.vertType; @@ -103,76 +104,81 @@ void GenerateVertexShaderDX9(const ShaderID &id, char *buffer) { numBoneWeights = 1 + id.Bits(VS_BIT_BONES, 3); } - WRITE(p, "#pragma warning( disable : 3571 )\n"); + if (lang == HLSL_DX9) { + WRITE(p, "#pragma warning( disable : 3571 )\n"); + if (isModeThrough) { + WRITE(p, "float4x4 u_proj_through : register(c%i);\n", CONST_VS_PROJ_THROUGH); + } else { + WRITE(p, "float4x4 u_proj : register(c%i);\n", CONST_VS_PROJ); + // Add all the uniforms we'll need to transform properly. + } - if (isModeThrough) { - WRITE(p, "float4x4 u_proj_through : register(c%i);\n", CONST_VS_PROJ_THROUGH); - } else { - WRITE(p, "float4x4 u_proj : register(c%i);\n", CONST_VS_PROJ); - // Add all the uniforms we'll need to transform properly. - } + if (enableFog) { + WRITE(p, "float2 u_fogcoef : register(c%i);\n", CONST_VS_FOGCOEF); + } + if (useHWTransform || !hasColor) + WRITE(p, "float4 u_matambientalpha : register(c%i);\n", CONST_VS_MATAMBIENTALPHA); // matambient + matalpha - if (enableFog) { - WRITE(p, "float2 u_fogcoef : register(c%i);\n", CONST_VS_FOGCOEF); - } - if (useHWTransform || !hasColor) - WRITE(p, "float4 u_matambientalpha : register(c%i);\n", CONST_VS_MATAMBIENTALPHA); // matambient + matalpha - - if (useHWTransform) { - // When transforming by hardware, we need a great deal more uniforms... - WRITE(p, "float4x3 u_world : register(c%i);\n", CONST_VS_WORLD); - WRITE(p, "float4x3 u_view : register(c%i);\n", CONST_VS_VIEW); - if (doTextureTransform) - WRITE(p, "float4x3 u_texmtx : register(c%i);\n", CONST_VS_TEXMTX); - if (enableBones) { + if (useHWTransform) { + // When transforming by hardware, we need a great deal more uniforms... + WRITE(p, "float4x3 u_world : register(c%i);\n", CONST_VS_WORLD); + WRITE(p, "float4x3 u_view : register(c%i);\n", CONST_VS_VIEW); + if (doTextureTransform) + WRITE(p, "float4x3 u_tex : register(c%i);\n", CONST_VS_TEXMTX); + if (enableBones) { #ifdef USE_BONE_ARRAY - WRITE(p, "float4x3 u_bone[%i] : register(c%i);\n", numBones, CONST_VS_BONE0); + WRITE(p, "float4x3 u_bone[%i] : register(c%i);\n", numBones, CONST_VS_BONE0); #else - for (int i = 0; i < numBoneWeights; i++) { - WRITE(p, "float4x3 u_bone%i : register(c%i);\n", i, CONST_VS_BONE0 + i * 3); - } + for (int i = 0; i < numBoneWeights; i++) { + WRITE(p, "float4x3 u_bone%i : register(c%i);\n", i, CONST_VS_BONE0 + i * 3); + } #endif - } - if (doTexture) { - WRITE(p, "float4 u_uvscaleoffset : register(c%i);\n", CONST_VS_UVSCALEOFFSET); - } - for (int i = 0; i < 4; i++) { - if (doLight[i] != LIGHT_OFF) { - // This is needed for shade mapping - WRITE(p, "float3 u_lightpos%i : register(c%i);\n", i, CONST_VS_LIGHTPOS + i); } - if (doLight[i] == LIGHT_FULL) { - GELightType type = static_cast(id.Bits(VS_BIT_LIGHT0_TYPE + 4 * i, 2)); - GELightComputation comp = static_cast(id.Bits(VS_BIT_LIGHT0_COMP + 4 * i, 2)); - - if (type != GE_LIGHTTYPE_DIRECTIONAL) - WRITE(p, "float3 u_lightatt%i : register(c%i);\n", i, CONST_VS_LIGHTATT + i); - - if (type == GE_LIGHTTYPE_SPOT || type == GE_LIGHTTYPE_UNKNOWN) { - WRITE(p, "float3 u_lightdir%i : register(c%i);\n", i, CONST_VS_LIGHTDIR + i); - WRITE(p, "float u_lightangle%i : register(c%i);\n", i, CONST_VS_LIGHTANGLE + i); - WRITE(p, "float u_lightspotCoef%i : register(c%i);\n", i, CONST_VS_LIGHTSPOTCOEF + i); + if (doTexture) { + WRITE(p, "float4 u_uvscaleoffset : register(c%i);\n", CONST_VS_UVSCALEOFFSET); + } + for (int i = 0; i < 4; i++) { + if (doLight[i] != LIGHT_OFF) { + // This is needed for shade mapping + WRITE(p, "float3 u_lightpos%i : register(c%i);\n", i, CONST_VS_LIGHTPOS + i); } - WRITE(p, "float3 u_lightambient%i : register(c%i);\n", i, CONST_VS_LIGHTAMBIENT + i); - WRITE(p, "float3 u_lightdiffuse%i : register(c%i);\n", i, CONST_VS_LIGHTDIFFUSE + i); + if (doLight[i] == LIGHT_FULL) { + GELightType type = static_cast(id.Bits(VS_BIT_LIGHT0_TYPE + 4 * i, 2)); + GELightComputation comp = static_cast(id.Bits(VS_BIT_LIGHT0_COMP + 4 * i, 2)); - if (comp != GE_LIGHTCOMP_ONLYDIFFUSE) { - WRITE(p, "float3 u_lightspecular%i : register(c%i);\n", i, CONST_VS_LIGHTSPECULAR + i); + if (type != GE_LIGHTTYPE_DIRECTIONAL) + WRITE(p, "float3 u_lightatt%i : register(c%i);\n", i, CONST_VS_LIGHTATT + i); + + if (type == GE_LIGHTTYPE_SPOT || type == GE_LIGHTTYPE_UNKNOWN) { + WRITE(p, "float3 u_lightdir%i : register(c%i);\n", i, CONST_VS_LIGHTDIR + i); + WRITE(p, "float4 u_lightangle%i : register(c%i);\n", i, CONST_VS_LIGHTANGLE + i); + WRITE(p, "float4 u_lightspotCoef%i : register(c%i);\n", i, CONST_VS_LIGHTSPOTCOEF + i); + } + WRITE(p, "float3 u_lightambient%i : register(c%i);\n", i, CONST_VS_LIGHTAMBIENT + i); + WRITE(p, "float3 u_lightdiffuse%i : register(c%i);\n", i, CONST_VS_LIGHTDIFFUSE + i); + + if (comp != GE_LIGHTCOMP_ONLYDIFFUSE) { + WRITE(p, "float3 u_lightspecular%i : register(c%i);\n", i, CONST_VS_LIGHTSPECULAR + i); + } } } + if (enableLighting) { + WRITE(p, "float4 u_ambient : register(c%i);\n", CONST_VS_AMBIENT); + if ((gstate.materialupdate & 2) == 0 || !hasColor) + WRITE(p, "float3 u_matdiffuse : register(c%i);\n", CONST_VS_MATDIFFUSE); + // if ((gstate.materialupdate & 4) == 0) + WRITE(p, "float4 u_matspecular : register(c%i);\n", CONST_VS_MATSPECULAR); // Specular coef is contained in alpha + WRITE(p, "float3 u_matemissive : register(c%i);\n", CONST_VS_MATEMISSIVE); + } } - if (enableLighting) { - WRITE(p, "float4 u_ambient : register(c%i);\n", CONST_VS_AMBIENT); - if ((gstate.materialupdate & 2) == 0 || !hasColor) - WRITE(p, "float3 u_matdiffuse : register(c%i);\n", CONST_VS_MATDIFFUSE); - // if ((gstate.materialupdate & 4) == 0) - WRITE(p, "float4 u_matspecular : register(c%i);\n", CONST_VS_MATSPECULAR); // Specular coef is contained in alpha - WRITE(p, "float3 u_matemissive : register(c%i);\n", CONST_VS_MATEMISSIVE); - } - } - if (!isModeThrough && gstate_c.Supports(GPU_ROUND_DEPTH_TO_16BIT)) { - WRITE(p, "float4 u_depthRange : register(c%i);\n", CONST_VS_DEPTHRANGE); + if (!isModeThrough && gstate_c.Supports(GPU_ROUND_DEPTH_TO_16BIT)) { + WRITE(p, "float4 u_depthRange : register(c%i);\n", CONST_VS_DEPTHRANGE); + } + } else { + WRITE(p, "cbuffer base : register(b0) {\n%s};\n", cb_baseStr); + WRITE(p, "cbuffer lights: register(b1) {\n%s};\n", cb_vs_lightsStr); + WRITE(p, "cbuffer bones : register(b2) {\n%s};\n", cb_vs_bonesStr); } // And the "varyings". @@ -216,7 +222,6 @@ void GenerateVertexShaderDX9(const ShaderID &id, char *buffer) { } WRITE(p, "struct VS_OUT {\n"); - WRITE(p, " float4 gl_Position : POSITION;\n"); if (doTexture) { WRITE(p, " float3 v_texcoord : TEXCOORD0;\n"); } @@ -225,7 +230,12 @@ void GenerateVertexShaderDX9(const ShaderID &id, char *buffer) { WRITE(p, " float3 v_color1 : COLOR1;\n"); if (enableFog) { - WRITE(p, " float2 v_fogdepth: TEXCOORD1;\n"); + WRITE(p, " float v_fogdepth: TEXCOORD1;\n"); + } + if (lang == HLSL_DX9) { + WRITE(p, " float4 gl_Position : POSITION;\n"); + } else { + WRITE(p, " float4 gl_Position : SV_Position;\n"); } WRITE(p, "};\n"); @@ -244,7 +254,7 @@ void GenerateVertexShaderDX9(const ShaderID &id, char *buffer) { } WRITE(p, "VS_OUT main(VS_IN In) {\n"); - WRITE(p, " VS_OUT Out = (VS_OUT)0; \n"); + WRITE(p, " VS_OUT Out;\n"); if (!useHWTransform) { // Simple pass-through of vertex data to fragment shader if (doTexture) { @@ -264,26 +274,46 @@ void GenerateVertexShaderDX9(const ShaderID &id, char *buffer) { WRITE(p, " Out.v_color1 = float3(0.0);\n"); } if (enableFog) { - WRITE(p, " Out.v_fogdepth.x = In.position.w;\n"); + WRITE(p, " Out.v_fogdepth = In.position.w;\n"); } - if (gstate.isModeThrough()) { - WRITE(p, " Out.gl_Position = mul(float4(In.position.xyz, 1.0), u_proj_through);\n"); - } else { - if (gstate_c.Supports(GPU_ROUND_DEPTH_TO_16BIT)) { - WRITE(p, " Out.gl_Position = depthRoundZVP(mul(float4(In.position.xyz, 1.0), u_proj));\n"); + if (lang == HLSL_D3D11) { + if (gstate.isModeThrough()) { + WRITE(p, " Out.gl_Position = mul(u_proj_through, float4(In.position.xyz, 1.0));\n"); } else { - WRITE(p, " Out.gl_Position = mul(float4(In.position.xyz, 1.0), u_proj);\n"); + if (gstate_c.Supports(GPU_ROUND_DEPTH_TO_16BIT)) { + WRITE(p, " Out.gl_Position = depthRoundZVP(mul(u_proj, float4(In.position.xyz, 1.0)));\n"); + } else { + WRITE(p, " Out.gl_Position = mul(u_proj, float4(In.position.xyz, 1.0));\n"); + } + } + } else { + if (gstate.isModeThrough()) { + WRITE(p, " Out.gl_Position = mul(float4(In.position.xyz, 1.0), u_proj_through);\n"); + } else { + if (gstate_c.Supports(GPU_ROUND_DEPTH_TO_16BIT)) { + WRITE(p, " Out.gl_Position = depthRoundZVP(mul(float4(In.position.xyz, 1.0), u_proj));\n"); + } else { + WRITE(p, " Out.gl_Position = mul(float4(In.position.xyz, 1.0), u_proj);\n"); + } } } } else { // Step 1: World Transform / Skinning if (!enableBones) { // No skinning, just standard T&L. - WRITE(p, " float3 worldpos = mul(float4(In.position.xyz, 1.0), u_world);\n"); - if (hasNormal) - WRITE(p, " float3 worldnormal = normalize( mul(float4(%sIn.normal, 0.0), u_world));\n", flipNormal ? "-" : ""); - else - WRITE(p, " float3 worldnormal = float3(0.0, 0.0, 1.0);\n"); + if (lang == HLSL_D3D11) { + WRITE(p, " float3 worldpos = mul(u_world, float4(In.position.xyz, 1.0)).xyz;\n"); + if (hasNormal) + WRITE(p, " float3 worldnormal = normalize(mul(u_world, float4(%sIn.normal, 0.0))).xyz;\n", flipNormal ? "-" : ""); + else + WRITE(p, " float3 worldnormal = float3(0.0, 0.0, 1.0);\n"); + } else { + WRITE(p, " float3 worldpos = mul(float4(In.position.xyz, 1.0), u_world);\n"); + if (hasNormal) + WRITE(p, " float3 worldnormal = normalize(mul(float4(%sIn.normal, 0.0), u_world));\n", flipNormal ? "-" : ""); + else + WRITE(p, " float3 worldnormal = float3(0.0, 0.0, 1.0);\n"); + } } else { static const char * const boneWeightAttr[8] = { "a_w1.x", "a_w1.y", "a_w1.z", "a_w1.w", @@ -357,13 +387,24 @@ void GenerateVertexShaderDX9(const ShaderID &id, char *buffer) { WRITE(p, " float3 worldnormal = normalize(mul(float4(skinnednormal, 0.0), u_world));\n"); } - WRITE(p, " float4 viewPos = float4(mul(float4(worldpos, 1.0), u_view), 1.0);\n"); + if (lang == HLSL_D3D11) { + WRITE(p, " float4 viewPos = mul(u_view, float4(worldpos, 1.0));\n"); - // Final view and projection transforms. - if (gstate_c.Supports(GPU_ROUND_DEPTH_TO_16BIT)) { - WRITE(p, " Out.gl_Position = depthRoundZVP(mul(viewPos, u_proj));\n"); + // Final view and projection transforms. + if (gstate_c.Supports(GPU_ROUND_DEPTH_TO_16BIT)) { + WRITE(p, " Out.gl_Position = depthRoundZVP(mul(u_proj, viewPos));\n"); + } else { + WRITE(p, " Out.gl_Position = mul(u_proj, viewPos);\n"); + } } else { - WRITE(p, " Out.gl_Position = mul(viewPos, u_proj);\n"); + WRITE(p, " float4 viewPos = float4(mul(float4(worldpos, 1.0), u_view), 1.0);\n"); + + // Final view and projection transforms. + if (gstate_c.Supports(GPU_ROUND_DEPTH_TO_16BIT)) { + WRITE(p, " Out.gl_Position = depthRoundZVP(mul(viewPos, u_proj));\n"); + } else { + WRITE(p, " Out.gl_Position = mul(viewPos, u_proj);\n"); + } } // TODO: Declare variables for dots for shade mapping if needed. @@ -446,8 +487,8 @@ void GenerateVertexShaderDX9(const ShaderID &id, char *buffer) { case GE_LIGHTTYPE_SPOT: case GE_LIGHTTYPE_UNKNOWN: WRITE(p, " float angle%i = dot(normalize(u_lightdir%i), toLight);\n", i, i); - WRITE(p, " if (angle%i >= u_lightangle%i) {\n", i, i); - WRITE(p, " lightScale = clamp(1.0 / dot(u_lightatt%i, float3(1.0, distance, distance*distance)), 0.0, 1.0) * pow(angle%i, u_lightspotCoef%i);\n", i, i, i); + WRITE(p, " if (angle%i >= u_lightangle%i.x) {\n", i, i); + WRITE(p, " lightScale = clamp(1.0 / dot(u_lightatt%i, float3(1.0, distance, distance*distance)), 0.0, 1.0) * pow(angle%i, u_lightspotCoef%i.x);\n", i, i, i); WRITE(p, " } else {\n"); WRITE(p, " lightScale = 0.0;\n"); WRITE(p, " }\n"); @@ -544,7 +585,11 @@ void GenerateVertexShaderDX9(const ShaderID &id, char *buffer) { break; } // Transform by texture matrix. XYZ as we are doing projection mapping. - WRITE(p, " Out.v_texcoord.xyz = mul(%s,u_texmtx) * float3(u_uvscaleoffset.xy, 1.0);\n", temp_tc.c_str()); + if (lang == HLSL_D3D11) { + WRITE(p, " Out.v_texcoord.xyz = (mul(u_tex, %s) * float4(u_uvscaleoffset.xy, 1.0, 0.0)).xyz;\n", temp_tc.c_str()); + } else { + WRITE(p, " Out.v_texcoord.xyz = mul(%s, u_tex) * float3(u_uvscaleoffset.xy, 1.0);\n", temp_tc.c_str()); + } } break; @@ -559,8 +604,13 @@ void GenerateVertexShaderDX9(const ShaderID &id, char *buffer) { } // Compute fogdepth - if (enableFog) - WRITE(p, " Out.v_fogdepth.x = (viewPos.z + u_fogcoef.x) * u_fogcoef.y;\n"); + if (enableFog) { + if (lang == HLSL_D3D11) { + WRITE(p, " Out.v_fogdepth = (viewPos.z + u_fogcoef_stencilreplace.x) * u_fogcoef_stencilreplace.y;\n"); + } else { + WRITE(p, " Out.v_fogdepth = (viewPos.z + u_fogcoef.x) * u_fogcoef.y;\n"); + } + } } WRITE(p, " return Out;\n"); diff --git a/GPU/Directx9/VertexShaderGeneratorDX9.h b/GPU/Directx9/VertexShaderGeneratorDX9.h index ea43973b19..21601b989d 100644 --- a/GPU/Directx9/VertexShaderGeneratorDX9.h +++ b/GPU/Directx9/VertexShaderGeneratorDX9.h @@ -25,7 +25,7 @@ namespace DX9 { // #define USE_BONE_ARRAY - void GenerateVertexShaderDX9(const ShaderID &id, char *buffer); + void GenerateVertexShaderHLSL(const ShaderID &id, char *buffer, ShaderLanguage lang = HLSL_DX9); #define CONST_VS_PROJ 0 #define CONST_VS_PROJ_THROUGH 4 diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index 04b568b6f5..a22737938b 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -1021,7 +1021,7 @@ void FramebufferManagerGLES::BlitFramebuffer(VirtualFramebuffer *dst, int dstX, const bool xOverlap = src == dst && srcX2 > dstX1 && srcX1 < dstX2; const bool yOverlap = src == dst && srcY2 > dstY1 && srcY1 < dstY2; if (sameSize && sameDepth && srcInsideBounds && dstInsideBounds && !(xOverlap && yOverlap)) { - draw_->CopyFramebufferImage(src->fbo, 0, srcX1, srcY1, 0, dst->fbo, 0, dstX1, dstY1, 0, dstX2 - dstX1, dstY2 - dstY1, 1); + draw_->CopyFramebufferImage(src->fbo, 0, srcX1, srcY1, 0, dst->fbo, 0, dstX1, dstY1, 0, dstX2 - dstX1, dstY2 - dstY1, 1, Draw::FB_COLOR_BIT); return; } } diff --git a/GPU/GLES/GPU_GLES.cpp b/GPU/GLES/GPU_GLES.cpp index 4942dbc9fd..60ff75517d 100644 --- a/GPU/GLES/GPU_GLES.cpp +++ b/GPU/GLES/GPU_GLES.cpp @@ -495,7 +495,8 @@ GPU_GLES::~GPU_GLES() { } delete shaderManagerGL_; shaderManagerGL_ = nullptr; - + delete framebufferManagerGL_; + delete textureCacheGL_; #ifdef _WIN32 gfxCtx_->SwapInterval(0); #endif @@ -1188,9 +1189,9 @@ void GPU_GLES::GetStats(char *buffer, size_t bufsize) { (int)textureCacheGL_->NumLoadedTextures(), gpuStats.numTexturesDecoded, gpuStats.numTextureInvalidations, - shaderManagerGL_->NumVertexShaders(), - shaderManagerGL_->NumFragmentShaders(), - shaderManagerGL_->NumPrograms()); + shaderManagerGL_->GetNumVertexShaders(), + shaderManagerGL_->GetNumFragmentShaders(), + shaderManagerGL_->GetNumPrograms()); } void GPU_GLES::ClearCacheNextFrame() { diff --git a/GPU/GLES/ShaderManagerGLES.cpp b/GPU/GLES/ShaderManagerGLES.cpp index 3cc287305d..1da2b93a8b 100644 --- a/GPU/GLES/ShaderManagerGLES.cpp +++ b/GPU/GLES/ShaderManagerGLES.cpp @@ -1084,9 +1084,9 @@ void ShaderManagerGLES::Save(const std::string &filename) { header.version = CACHE_VERSION; header.reserved = 0; header.featureFlags = gstate_c.featureFlags; - header.numVertexShaders = NumVertexShaders(); - header.numFragmentShaders = NumFragmentShaders(); - header.numLinkedPrograms = NumPrograms(); + header.numVertexShaders = GetNumVertexShaders(); + header.numFragmentShaders = GetNumFragmentShaders(); + header.numLinkedPrograms = GetNumPrograms(); fwrite(&header, 1, sizeof(header), f); for (auto iter : vsCache_) { ShaderID id = iter.first; diff --git a/GPU/GLES/ShaderManagerGLES.h b/GPU/GLES/ShaderManagerGLES.h index cc7944bd18..7010acddca 100644 --- a/GPU/GLES/ShaderManagerGLES.h +++ b/GPU/GLES/ShaderManagerGLES.h @@ -155,9 +155,9 @@ public: void DirtyShader(); void DirtyLastShader(); // disables vertex arrays - int NumVertexShaders() const { return (int)vsCache_.size(); } - int NumFragmentShaders() const { return (int)fsCache_.size(); } - int NumPrograms() const { return (int)linkedShaderCache_.size(); } + int GetNumVertexShaders() const { return (int)vsCache_.size(); } + int GetNumFragmentShaders() const { return (int)fsCache_.size(); } + int GetNumPrograms() const { return (int)linkedShaderCache_.size(); } std::vector DebugGetShaderIDs(DebugShaderType type); std::string DebugGetShaderString(std::string id, DebugShaderType type, DebugShaderStringType stringType); diff --git a/GPU/GPU.cpp b/GPU/GPU.cpp index 26c222da2a..797c8cbb63 100644 --- a/GPU/GPU.cpp +++ b/GPU/GPU.cpp @@ -29,6 +29,7 @@ #if defined(_WIN32) #include "GPU/Directx9/GPU_DX9.h" +#include "GPU/D3D11/GPU_D3D11.h" #endif GPUStatistics gpuStats; @@ -59,10 +60,17 @@ bool GPU_Init(GraphicsContext *ctx, Draw::DrawContext *draw) { case GPUCORE_DIRECTX9: #if defined(_WIN32) SetGPU(new DIRECTX9_GPU(ctx, draw)); -#endif break; - case GPUCORE_DIRECTX11: +#else return false; +#endif + case GPUCORE_DIRECTX11: +#if defined(_WIN32) + SetGPU(new GPU_D3D11(ctx, draw)); + break; +#else + return false; +#endif case GPUCORE_VULKAN: #ifndef NO_VULKAN if (!ctx) { diff --git a/GPU/GPU.vcxproj b/GPU/GPU.vcxproj index 8e9d23ad04..12e66fe50b 100644 --- a/GPU/GPU.vcxproj +++ b/GPU/GPU.vcxproj @@ -83,7 +83,7 @@ Level3 - ../common;..;../ext;../ext/native;../ext/glew;../ext/snappy; + ..\dx9sdk\Include\DX11;../common;..;../ext;../ext/native;../ext/glew;../ext/snappy; _CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_SECURE_NO_WARNINGS;WIN32;_ARCH_32=1;_M_IX86=1;_DEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions) StreamingSIMDExtensions2 Fast @@ -105,7 +105,7 @@ Level3 - ../common;..;../ext;../ext/native;../ext/glew;../ext/snappy; + ..\dx9sdk\Include\DX11;../common;..;../ext;../ext/native;../ext/glew;../ext/snappy; NotSet Fast false @@ -131,7 +131,7 @@ MaxSpeed true true - ../common;..;../ext;../ext/native;../ext/glew;../ext/snappy; + ..\dx9sdk\Include\DX11;../common;..;../ext;../ext/native;../ext/glew;../ext/snappy; false StreamingSIMDExtensions2 Fast @@ -157,7 +157,7 @@ MaxSpeed true true - ../common;..;../ext;../ext/native;../ext/glew;../ext/snappy; + ..\dx9sdk\Include\DX11;../common;..;../ext;../ext/native;../ext/glew;../ext/snappy; false NotSet Fast @@ -190,6 +190,7 @@ + @@ -202,6 +203,17 @@ + + + + + + + + + + + @@ -261,6 +273,7 @@ + true @@ -286,6 +299,18 @@ + + + + + + + + + + + + @@ -344,4 +369,4 @@ - + \ No newline at end of file diff --git a/GPU/GPU.vcxproj.filters b/GPU/GPU.vcxproj.filters index 58706bbffa..d3d36e2508 100644 --- a/GPU/GPU.vcxproj.filters +++ b/GPU/GPU.vcxproj.filters @@ -16,15 +16,15 @@ {88629970-4774-4122-b031-2128244b795c} - - {ba434472-5d5e-4b08-ab16-bfb7ec8e7068} - {0cbddc00-4aa3-41d0-bed2-a454d37f838e} {3c621896-140c-4c8b-8e4d-a478bfdeca8a} + + {88eb5cea-ec25-4881-89da-02f9f2fa8f3f} + @@ -222,6 +222,40 @@ GLES + + Common + + + D3D11 + + + D3D11 + + + D3D11 + + + D3D11 + + + D3D11 + + + D3D11 + + + D3D11 + + + D3D11 + + + D3D11 + + + D3D11 + + @@ -428,5 +462,44 @@ GLES + + D3D11 + + + Common + + + D3D11 + + + D3D11 + + + D3D11 + + + D3D11 + + + D3D11 + + + D3D11 + + + D3D11 + + + D3D11 + + + D3D11 + + + D3D11 + + + D3D11 + - + \ No newline at end of file diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index 1058f109b3..e9a0836ba6 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -54,7 +54,6 @@ GPUCommon::GPUCommon(GraphicsContext *gfxCtx, Draw::DrawContext *draw) : } GPUCommon::~GPUCommon() { - delete framebufferManager_; } void GPUCommon::BeginHostFrame() { diff --git a/GPU/Vulkan/FramebufferVulkan.cpp b/GPU/Vulkan/FramebufferVulkan.cpp index 71fe336a45..92d62e98a8 100644 --- a/GPU/Vulkan/FramebufferVulkan.cpp +++ b/GPU/Vulkan/FramebufferVulkan.cpp @@ -22,6 +22,7 @@ #include "base/timeutil.h" #include "math/lin/matrix4x4.h" +#include "math/dataconv.h" #include "ext/native/thin3d/thin3d.h" #include "Common/Vulkan/VulkanContext.h" @@ -256,10 +257,7 @@ void FramebufferManagerVulkan::NotifyClear(bool clearColor, bool clearAlpha, boo CenterDisplayOutputRect(&x, &y, &w, &h, 480.0f, 272.0f, (float)pixelWidth_, (float)pixelHeight_, ROTATION_LOCKED_HORIZONTAL); VkClearValue colorValue, depthValue; - colorValue.color.float32[0] = (color & 0xFF) * (1.0f / 255.0f); - colorValue.color.float32[1] = ((color >> 8) & 0xFF) * (1.0f / 255.0f); - colorValue.color.float32[2] = ((color >> 16) & 0xFF) * (1.0f / 255.0f); - colorValue.color.float32[3] = ((color >> 24) & 0xFF) * (1.0f / 255.0f); + Uint8x4ToFloat4(colorValue.color.float32, color); depthValue.depthStencil.depth = depth; depthValue.depthStencil.stencil = (color >> 24) & 0xFF; diff --git a/GPU/Vulkan/ShaderManagerVulkan.cpp b/GPU/Vulkan/ShaderManagerVulkan.cpp index 18f7bf9192..ec2d62baf3 100644 --- a/GPU/Vulkan/ShaderManagerVulkan.cpp +++ b/GPU/Vulkan/ShaderManagerVulkan.cpp @@ -150,12 +150,6 @@ std::string VulkanVertexShader::GetShaderString(DebugShaderStringType type) cons } } -static void ConvertProjMatrixToVulkan(Matrix4x4 &in, bool invertedX, bool invertedY) { - const Vec3 trans(0, 0, gstate_c.vpZOffset * 0.5f + 0.5f); - const Vec3 scale(gstate_c.vpWidthScale, gstate_c.vpHeightScale, gstate_c.vpDepthScale * 0.5f); - in.translateAndScale(trans, scale); -} - ShaderManagerVulkan::ShaderManagerVulkan(VulkanContext *vulkan) : vulkan_(vulkan), lastVShader_(nullptr), lastFShader_(nullptr) { codeBuffer_ = new char[16384]; @@ -187,207 +181,6 @@ uint32_t ShaderManagerVulkan::PushBoneBuffer(VulkanPushBuffer *dest, VkBuffer *b return dest->PushAligned(&ub_bones, sizeof(ub_bones), uboAlignment_, buf); } -void ShaderManagerVulkan::BaseUpdateUniforms(uint64_t dirtyUniforms) { - if (dirtyUniforms & DIRTY_TEXENV) { - Uint8x3ToFloat4(ub_base.texEnvColor, gstate.texenvcolor); - } - if (dirtyUniforms & DIRTY_ALPHACOLORREF) { - Uint8x3ToInt4_Alpha(ub_base.alphaColorRef, gstate.getColorTestRef(), gstate.getAlphaTestRef() & gstate.getAlphaTestMask()); - } - if (dirtyUniforms & DIRTY_ALPHACOLORMASK) { - Uint8x3ToInt4_Alpha(ub_base.colorTestMask, gstate.getColorTestMask(), gstate.getAlphaTestMask()); - } - if (dirtyUniforms & DIRTY_FOGCOLOR) { - Uint8x3ToFloat4(ub_base.fogColor, gstate.fogcolor); - } - if (dirtyUniforms & DIRTY_SHADERBLEND) { - Uint8x3ToFloat4(ub_base.blendFixA, gstate.getFixA()); - Uint8x3ToFloat4(ub_base.blendFixB, gstate.getFixB()); - } - if (dirtyUniforms & DIRTY_TEXCLAMP) { - const float invW = 1.0f / (float)gstate_c.curTextureWidth; - const float invH = 1.0f / (float)gstate_c.curTextureHeight; - const int w = gstate.getTextureWidth(0); - const int h = gstate.getTextureHeight(0); - const float widthFactor = (float)w * invW; - const float heightFactor = (float)h * invH; - - // First wrap xy, then half texel xy (for clamp.) - ub_base.texClamp[0] = widthFactor; - ub_base.texClamp[1] = heightFactor; - ub_base.texClamp[2] = invW * 0.5f; - ub_base.texClamp[3] = invH * 0.5f; - ub_base.texClampOffset[0] = gstate_c.curTextureXOffset * invW; - ub_base.texClampOffset[1] = gstate_c.curTextureYOffset * invH; - } - - if (dirtyUniforms & DIRTY_PROJMATRIX) { - Matrix4x4 flippedMatrix; - memcpy(&flippedMatrix, gstate.projMatrix, 16 * sizeof(float)); - - const bool invertedY = gstate_c.vpHeight < 0; - if (invertedY) { - flippedMatrix[1] = -flippedMatrix[1]; - flippedMatrix[5] = -flippedMatrix[5]; - flippedMatrix[9] = -flippedMatrix[9]; - flippedMatrix[13] = -flippedMatrix[13]; - } - const bool invertedX = gstate_c.vpWidth < 0; - if (invertedX) { - flippedMatrix[0] = -flippedMatrix[0]; - flippedMatrix[4] = -flippedMatrix[4]; - flippedMatrix[8] = -flippedMatrix[8]; - flippedMatrix[12] = -flippedMatrix[12]; - } - ConvertProjMatrixToVulkan(flippedMatrix, invertedX, invertedY); - CopyMatrix4x4(ub_base.proj, flippedMatrix.getReadPtr()); - } - - if (dirtyUniforms & DIRTY_PROJTHROUGHMATRIX) { - Matrix4x4 proj_through; - proj_through.setOrthoVulkan(0.0f, gstate_c.curRTWidth, 0, gstate_c.curRTHeight, 0, 1); - CopyMatrix4x4(ub_base.proj_through, proj_through.getReadPtr()); - } - - // Transform - if (dirtyUniforms & DIRTY_WORLDMATRIX) { - ConvertMatrix4x3To4x4(ub_base.world, gstate.worldMatrix); - } - if (dirtyUniforms & DIRTY_VIEWMATRIX) { - ConvertMatrix4x3To4x4(ub_base.view, gstate.viewMatrix); - } - if (dirtyUniforms & DIRTY_TEXMATRIX) { - ConvertMatrix4x3To4x4(ub_base.tex, gstate.tgenMatrix); - } - - // Combined two small uniforms - if (dirtyUniforms & (DIRTY_FOGCOEF | DIRTY_STENCILREPLACEVALUE)) { - float fogcoef_stencil[3] = { - getFloat24(gstate.fog1), - getFloat24(gstate.fog2), - (float)gstate.getStencilTestRef() - }; - if (my_isinf(fogcoef_stencil[1])) { - // not really sure what a sensible value might be. - fogcoef_stencil[1] = fogcoef_stencil[1] < 0.0f ? -10000.0f : 10000.0f; - } else if (my_isnan(fogcoef_stencil[1])) { - // Workaround for https://github.com/hrydgard/ppsspp/issues/5384#issuecomment-38365988 - // Just put the fog far away at a large finite distance. - // Infinities and NaNs are rather unpredictable in shaders on many GPUs - // so it's best to just make it a sane calculation. - fogcoef_stencil[0] = 100000.0f; - fogcoef_stencil[1] = 1.0f; - } -#ifndef MOBILE_DEVICE - else if (my_isnanorinf(fogcoef_stencil[1]) || my_isnanorinf(fogcoef_stencil[0])) { - ERROR_LOG_REPORT_ONCE(fognan, G3D, "Unhandled fog NaN/INF combo: %f %f", fogcoef_stencil[0], fogcoef_stencil[1]); - } -#endif - CopyFloat3(ub_base.fogCoef_stencil, fogcoef_stencil); - } - - // Note - this one is not in lighting but in transformCommon as it has uses beyond lighting - if (dirtyUniforms & DIRTY_MATAMBIENTALPHA) { - Uint8x3ToFloat4_AlphaUint8(ub_base.matAmbient, gstate.materialambient, gstate.getMaterialAmbientA()); - } - - // Texturing - if (dirtyUniforms & DIRTY_UVSCALEOFFSET) { - const float invW = 1.0f / (float)gstate_c.curTextureWidth; - const float invH = 1.0f / (float)gstate_c.curTextureHeight; - const int w = gstate.getTextureWidth(0); - const int h = gstate.getTextureHeight(0); - const float widthFactor = (float)w * invW; - const float heightFactor = (float)h * invH; - ub_base.uvScaleOffset[0] = widthFactor; - ub_base.uvScaleOffset[1] = heightFactor; - ub_base.uvScaleOffset[2] = 0.0f; - ub_base.uvScaleOffset[3] = 0.0f; - } - - if (dirtyUniforms & DIRTY_DEPTHRANGE) { - float viewZScale = gstate.getViewportZScale(); - float viewZCenter = gstate.getViewportZCenter(); - float viewZInvScale; - - // We had to scale and translate Z to account for our clamped Z range. - // Therefore, we also need to reverse this to round properly. - // - // Example: scale = 65535.0, center = 0.0 - // Resulting range = -65535 to 65535, clamped to [0, 65535] - // gstate_c.vpDepthScale = 2.0f - // gstate_c.vpZOffset = -1.0f - // - // The projection already accounts for those, so we need to reverse them. - // - // Additionally, D3D9 uses a range from [0, 1]. We double and move the center. - viewZScale *= (1.0f / gstate_c.vpDepthScale) * 2.0f; - viewZCenter -= 65535.0f * gstate_c.vpZOffset + 32768.5f; - - if (viewZScale != 0.0) { - viewZInvScale = 1.0f / viewZScale; - } else { - viewZInvScale = 0.0; - } - - ub_base.depthRange[0] = viewZScale; - ub_base.depthRange[1] = viewZCenter; - ub_base.depthRange[2] = viewZCenter; - ub_base.depthRange[3] = viewZInvScale; - } -} - -void ShaderManagerVulkan::LightUpdateUniforms(uint64_t dirtyUniforms) { - // Lighting - if (dirtyUniforms & DIRTY_AMBIENT) { - Uint8x3ToFloat4_AlphaUint8(ub_lights.ambientColor, gstate.ambientcolor, gstate.getAmbientA()); - } - if (dirtyUniforms & DIRTY_MATDIFFUSE) { - Uint8x3ToFloat4(ub_lights.materialDiffuse, gstate.materialdiffuse); - } - if (dirtyUniforms & DIRTY_MATEMISSIVE) { - Uint8x3ToFloat4(ub_lights.materialEmissive, gstate.materialemissive); - } - if (dirtyUniforms & DIRTY_MATSPECULAR) { - Uint8x3ToFloat4_Alpha(ub_lights.materialSpecular, gstate.materialspecular, getFloat24(gstate.materialspecularcoef)); - } - - for (int i = 0; i < 4; i++) { - if (dirtyUniforms & (DIRTY_LIGHT0 << i)) { - if (gstate.isDirectionalLight(i)) { - // Prenormalize - float x = getFloat24(gstate.lpos[i * 3 + 0]); - float y = getFloat24(gstate.lpos[i * 3 + 1]); - float z = getFloat24(gstate.lpos[i * 3 + 2]); - float len = sqrtf(x*x + y*y + z*z); - if (len == 0.0f) - len = 1.0f; - else - len = 1.0f / len; - float vec[3] = { x * len, y * len, z * len }; - CopyFloat3To4(ub_lights.lpos[i], vec); - } else { - ExpandFloat24x3ToFloat4(ub_lights.lpos[i], &gstate.lpos[i * 3]); - } - ExpandFloat24x3ToFloat4(ub_lights.ldir[i], &gstate.ldir[i * 3]); - ExpandFloat24x3ToFloat4(ub_lights.latt[i], &gstate.latt[i * 3]); - CopyFloat1To4(ub_lights.lightAngle[i], getFloat24(gstate.lcutoff[i])); - CopyFloat1To4(ub_lights.lightSpotCoef[i], getFloat24(gstate.lconv[i])); - Uint8x3ToFloat4(ub_lights.lightAmbient[i], gstate.lcolor[i * 3]); - Uint8x3ToFloat4(ub_lights.lightDiffuse[i], gstate.lcolor[i * 3 + 1]); - Uint8x3ToFloat4(ub_lights.lightSpecular[i], gstate.lcolor[i * 3 + 2]); - } - } -} - -void ShaderManagerVulkan::BoneUpdateUniforms(uint64_t dirtyUniforms) { - for (int i = 0; i < 8; i++) { - if (dirtyUniforms & (DIRTY_BONEMATRIX0 << i)) { - ConvertMatrix4x3To4x4(ub_bones.bones[i], gstate.boneMatrix + 12 * i); - } - } -} - void ShaderManagerVulkan::DeviceRestore(VulkanContext *vulkan) { vulkan_ = vulkan; uboAlignment_ = vulkan_->GetPhysicalDeviceProperties().limits.minUniformBufferOffsetAlignment; @@ -429,11 +222,11 @@ uint64_t ShaderManagerVulkan::UpdateUniforms() { uint64_t dirty = gstate_c.GetDirtyUniforms(); if (dirty != 0) { if (dirty & DIRTY_BASE_UNIFORMS) - BaseUpdateUniforms(dirty); + BaseUpdateUniforms(&ub_base, dirty, false); if (dirty & DIRTY_LIGHT_UNIFORMS) - LightUpdateUniforms(dirty); + LightUpdateUniforms(&ub_lights, dirty); if (dirty & DIRTY_BONE_UNIFORMS) - BoneUpdateUniforms(dirty); + BoneUpdateUniforms(&ub_bones, dirty); } gstate_c.CleanUniforms(); return dirty; diff --git a/GPU/Vulkan/ShaderManagerVulkan.h b/GPU/Vulkan/ShaderManagerVulkan.h index aa185a0479..2c8d6f646e 100644 --- a/GPU/Vulkan/ShaderManagerVulkan.h +++ b/GPU/Vulkan/ShaderManagerVulkan.h @@ -27,104 +27,7 @@ #include "GPU/Vulkan/FragmentShaderGeneratorVulkan.h" #include "GPU/Vulkan/VulkanUtil.h" #include "math/lin/matrix4x4.h" - -void ConvertProjMatrixToVulkan(Matrix4x4 & in); - -// Pretty much full. Will need more bits for more fine grained dirty tracking for lights. -enum : uint64_t { - DIRTY_BASE_UNIFORMS = - DIRTY_WORLDMATRIX | DIRTY_PROJTHROUGHMATRIX | DIRTY_VIEWMATRIX | DIRTY_TEXMATRIX | DIRTY_ALPHACOLORREF | - DIRTY_PROJMATRIX | DIRTY_FOGCOLOR | DIRTY_FOGCOEF | DIRTY_TEXENV | DIRTY_STENCILREPLACEVALUE | - DIRTY_ALPHACOLORMASK | DIRTY_SHADERBLEND | DIRTY_UVSCALEOFFSET | DIRTY_TEXCLAMP | DIRTY_DEPTHRANGE | DIRTY_MATAMBIENTALPHA, - DIRTY_LIGHT_UNIFORMS = - DIRTY_LIGHT0 | DIRTY_LIGHT1 | DIRTY_LIGHT2 | DIRTY_LIGHT3 | - DIRTY_MATDIFFUSE | DIRTY_MATSPECULAR | DIRTY_MATEMISSIVE | DIRTY_AMBIENT, -}; - -// TODO: Split into two structs, one for software transform and one for hardware transform, to save space. -// 512 bytes. Probably can't get to 256 (nVidia's UBO alignment). -struct UB_VS_FS_Base { - float proj[16]; - float proj_through[16]; - float view[16]; - float world[16]; - float tex[16]; // not that common, may want to break out - float uvScaleOffset[4]; - float depthRange[4]; - float fogCoef_stencil[4]; - float matAmbient[4]; - // Fragment data - float fogColor[4]; - float texEnvColor[4]; - int alphaColorRef[4]; - int colorTestMask[4]; - float blendFixA[4]; - float blendFixB[4]; - float texClamp[4]; - float texClampOffset[4]; -}; - -static const char *ub_baseStr = -R"( mat4 proj_mtx; - mat4 proj_through_mtx; - mat4 view_mtx; - mat4 world_mtx; - mat4 tex_mtx; - vec4 uvscaleoffset; - vec4 depthRange; - vec3 fogcoef_stencilreplace; - vec4 matambientalpha; - vec3 fogcolor; - vec3 texenv; - ivec4 alphacolorref; - ivec4 alphacolormask; - vec3 blendFixA; - vec3 blendFixB; - vec4 texclamp; - vec2 texclampoff; -)"; - -// 576 bytes. Can we get down to 512? -struct UB_VS_Lights { - float ambientColor[4]; - float materialDiffuse[4]; - float materialSpecular[4]; - float materialEmissive[4]; - float lpos[4][4]; - float ldir[4][4]; - float latt[4][4]; - float lightAngle[4][4]; // TODO: Merge with lightSpotCoef, use .xy - float lightSpotCoef[4][4]; - float lightAmbient[4][4]; - float lightDiffuse[4][4]; - float lightSpecular[4][4]; -}; - -static const char *ub_vs_lightsStr = -R"( vec4 globalAmbient; - vec3 matdiffuse; - vec4 matspecular; - vec3 matemissive; - vec3 pos[4]; - vec3 dir[4]; - vec3 att[4]; - float angle[4]; - float spotCoef[4]; - vec3 ambient[4]; - vec3 diffuse[4]; - vec3 specular[4]; -)"; - -// With some cleverness, we could get away with uploading just half this when only the four first -// bones are being used. This is 512b, 256b would be great. -// Could also move to 4x3 matrices - would let us fit 5 bones into 256b. -struct UB_VS_Bones { - float bones[8][16]; -}; - -static const char *ub_vs_bonesStr = -R"( mat4 m[8]; -)"; +#include "GPU/Common/ShaderUniforms.h" class VulkanContext; class VulkanPushBuffer; @@ -215,10 +118,6 @@ public: uint32_t PushBoneBuffer(VulkanPushBuffer *dest, VkBuffer *buf); private: - void BaseUpdateUniforms(uint64_t dirtyUniforms); - void LightUpdateUniforms(uint64_t dirtyUniforms); - void BoneUpdateUniforms(uint64_t dirtyUniforms); - void Clear(); VulkanContext *vulkan_; diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index 77d1145e68..d3cf4472c3 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -78,16 +78,17 @@ static const VkComponentMapping VULKAN_1555_SWIZZLE = { VK_COMPONENT_SWIZZLE_B, static const VkComponentMapping VULKAN_565_SWIZZLE = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A }; static const VkComponentMapping VULKAN_8888_SWIZZLE = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A }; + +CachedTextureVulkan::~CachedTextureVulkan() { + delete texture_; +} + SamplerCache::~SamplerCache() { for (auto iter : cache_) { vulkan_->Delete().QueueDeleteSampler(iter.second); } } -CachedTextureVulkan::~CachedTextureVulkan() { - delete texture_; -} - VkSampler SamplerCache::GetOrCreateSampler(const SamplerCacheKey &key) { auto iter = cache_.find(key); if (iter != cache_.end()) { diff --git a/GPU/Vulkan/TextureCacheVulkan.h b/GPU/Vulkan/TextureCacheVulkan.h index 7a7b34c5c9..f0318a3f26 100644 --- a/GPU/Vulkan/TextureCacheVulkan.h +++ b/GPU/Vulkan/TextureCacheVulkan.h @@ -37,28 +37,6 @@ class VulkanTexture; class VulkanPushBuffer; class VulkanDeviceAllocator; -struct SamplerCacheKey { - SamplerCacheKey() : fullKey(0) {} - - union { - u32 fullKey; - struct { - bool mipEnable : 1; - bool minFilt : 1; - bool mipFilt : 1; - bool magFilt : 1; - bool sClamp : 1; - bool tClamp : 1; - int lodBias : 4; - int maxLevel : 4; - }; - }; - - bool operator < (const SamplerCacheKey &other) const { - return fullKey < other.fullKey; - } -}; - class CachedTextureVulkan { public: CachedTextureVulkan() : texture_(nullptr) { diff --git a/GPU/Vulkan/VertexShaderGeneratorVulkan.cpp b/GPU/Vulkan/VertexShaderGeneratorVulkan.cpp index d121ad6431..32a4326d87 100644 --- a/GPU/Vulkan/VertexShaderGeneratorVulkan.cpp +++ b/GPU/Vulkan/VertexShaderGeneratorVulkan.cpp @@ -304,7 +304,7 @@ bool GenerateVulkanGLSLVertexShader(const ShaderID &id, char *buffer, bool *uses bool distanceNeeded = false; if (enableLighting) { - WRITE(p, " vec4 lightSum0 = light.globalAmbient * %s + vec4(light.matemissive, 0.0);\n", ambientStr); + WRITE(p, " vec4 lightSum0 = light.u_ambient * %s + vec4(light.matemissive, 0.0);\n", ambientStr); for (int i = 0; i < 4; i++) { GELightType type = static_cast(id.Bits(VS_BIT_LIGHT0_TYPE + 4 * i, 2)); diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index b8aa467d82..a60ccdc04c 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -44,6 +44,7 @@ #include "Common/KeyMap.h" #include "Common/FileUtil.h" +#include "Common/OSVersion.h" #include "Core/Config.h" #include "Core/Host.h" #include "Core/System.h" @@ -153,14 +154,19 @@ void GameSettingsScreen::CreateViews() { tabHolder->AddTab(ms->T("Graphics"), graphicsSettingsScroll); graphicsSettings->Add(new ItemHeader(gr->T("Rendering Mode"))); - static const char *renderingBackend[] = { "OpenGL", "Direct3D 9", "Direct3D 11", "Vulkan (experimental)" }; + static const char *renderingBackend[] = { "OpenGL", "Direct3D 9", "Direct3D 11 (experimental)", "Vulkan (experimental)" }; PopupMultiChoice *renderingBackendChoice = graphicsSettings->Add(new PopupMultiChoice(&g_Config.iGPUBackend, gr->T("Backend"), renderingBackend, GPU_BACKEND_OPENGL, ARRAY_SIZE(renderingBackend), gr->GetName(), screenManager())); renderingBackendChoice->OnChoice.Handle(this, &GameSettingsScreen::OnRenderingBackend); #if !defined(_WIN32) renderingBackendChoice->HideChoice(1); // D3D9 renderingBackendChoice->HideChoice(2); // D3D11 #else - renderingBackendChoice->HideChoice(2); // D3D11 + if (!DoesVersionMatchWindows(6, 2)) { + // Hide the D3D11 choice if Windows version is older than Windows 8. + // We will later be able to support Windows 7 and Vista (unofficially) as well, + // but we currently depend on a texture format that's only available in Win8+. + renderingBackendChoice->HideChoice(2); // D3D11 + } #endif #if !defined(_WIN32) // TODO: Add dynamic runtime check for Vulkan support on Android diff --git a/Windows/GEDebugger/CtrlDisplayListView.cpp b/Windows/GEDebugger/CtrlDisplayListView.cpp index 26086144ba..7b71b7f3f1 100644 --- a/Windows/GEDebugger/CtrlDisplayListView.cpp +++ b/Windows/GEDebugger/CtrlDisplayListView.cpp @@ -1,3 +1,4 @@ +#include "base/display.h" #include "Windows/GEDebugger/CtrlDisplayListView.h" #include "Windows/GEDebugger/GEDebugger.h" #include "Windows/InputBox.h" @@ -43,12 +44,15 @@ CtrlDisplayListView::CtrlDisplayListView(HWND _wnd) SetScrollRange(wnd, SB_VERT, -1,1,TRUE); instructionSize = 4; - rowHeight = g_Config.iFontHeight+2; - charWidth = g_Config.iFontWidth; - font = CreateFont(rowHeight-2,charWidth,0,0,FW_DONTCARE,FALSE,FALSE,FALSE,DEFAULT_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,DEFAULT_PITCH, + int fontHeight = g_Config.iFontHeight / g_dpi_scale; + int charWidth = g_Config.iFontWidth / g_dpi_scale; + + rowHeight = fontHeight +2; + + font = CreateFont(fontHeight,charWidth,0,0,FW_DONTCARE,FALSE,FALSE,FALSE,DEFAULT_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,DEFAULT_PITCH, L"Lucida Console"); - boldfont = CreateFont(rowHeight-2,charWidth,0,0,FW_DEMIBOLD,FALSE,FALSE,FALSE,DEFAULT_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,DEFAULT_PITCH, + boldfont = CreateFont(fontHeight,charWidth,0,0,FW_DEMIBOLD,FALSE,FALSE,FALSE,DEFAULT_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,DEFAULT_PITCH, L"Lucida Console"); pixelPositions.addressStart = 16; @@ -58,9 +62,9 @@ CtrlDisplayListView::CtrlDisplayListView(HWND _wnd) validDisplayList = false; } -CtrlDisplayListView::~CtrlDisplayListView() -{ - +CtrlDisplayListView::~CtrlDisplayListView() { + DeleteObject(font); + DeleteObject(boldfont); } CtrlDisplayListView *CtrlDisplayListView::getFrom(HWND hwnd) diff --git a/Windows/GEDebugger/CtrlDisplayListView.h b/Windows/GEDebugger/CtrlDisplayListView.h index d8e79498a1..5f9748fb4a 100644 --- a/Windows/GEDebugger/CtrlDisplayListView.h +++ b/Windows/GEDebugger/CtrlDisplayListView.h @@ -20,7 +20,6 @@ class CtrlDisplayListView u32 selectRangeEnd; int visibleRows; - int charWidth; int rowHeight; int instructionSize; bool hasFocus; diff --git a/Windows/GPU/D3D11Context.cpp b/Windows/GPU/D3D11Context.cpp index 6c09cfb67d..d4a9286193 100644 --- a/Windows/GPU/D3D11Context.cpp +++ b/Windows/GPU/D3D11Context.cpp @@ -22,6 +22,9 @@ D3D11Context::~D3D11Context() { void D3D11Context::SwapBuffers() { draw_->HandleEvent(Draw::Event::PRESENT_REQUESTED); + // Might be a good idea. + // context_->ClearState(); + // } void D3D11Context::SwapInterval(int interval) { @@ -70,52 +73,46 @@ bool D3D11Context::Init(HINSTANCE hInst, HWND wnd, std::string *error_message) { if (FAILED(hr)) return false; +#ifdef _DEBUG + if (SUCCEEDED(device_->QueryInterface(__uuidof(ID3D11Debug), (void**)&d3dDebug_))) { + if (SUCCEEDED(d3dDebug_->QueryInterface(__uuidof(ID3D11InfoQueue), (void**)&d3dInfoQueue_))) { + d3dInfoQueue_->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_CORRUPTION, true); + d3dInfoQueue_->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_ERROR, true); + //d3dInfoQueue_->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_WARNING, true); + } + } +#endif + draw_ = Draw::T3DCreateD3D11Context(device_, context_, hWnd_); + draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER); return true; } void D3D11Context::Resize() { draw_->HandleEvent(Draw::Event::LOST_BACKBUFFER); - // This should only be called from the emu thread. - /* - int xres, yres; - GetRes(hWnd, xres, yres); - bool w_changed = pp.BackBufferWidth != xres; - bool h_changed = pp.BackBufferHeight != yres; - - if (device && (w_changed || h_changed)) { - pp.BackBufferWidth = xres; - pp.BackBufferHeight = yres; - HRESULT hr = device_->Reset(&pp); - if (FAILED(hr)) { - // Had to remove DXGetErrorStringA calls here because dxerr.lib is deprecated and will not link with VS 2015. - ERROR_LOG_REPORT(G3D, "Unable to reset D3D device"); - PanicAlert("Unable to reset D3D11 device"); - } - } - */ + draw_->HandleEvent(Draw::Event::RESIZED); draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER); } void D3D11Context::Shutdown() { + draw_->HandleEvent(Draw::Event::LOST_BACKBUFFER); delete draw_; draw_ = nullptr; + context_->ClearState(); + context_->Flush(); + +#ifdef _DEBUG + d3dInfoQueue_->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_CORRUPTION, false); + d3dInfoQueue_->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_ERROR, false); + d3dInfoQueue_->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_WARNING, false); + d3dDebug_->ReportLiveDeviceObjects(D3D11_RLDO_SUMMARY | D3D11_RLDO_DETAIL); + d3dDebug_->Release(); + d3dInfoQueue_->Release(); +#endif context_->Release(); context_ = nullptr; device_->Release(); device_ = nullptr; - /* - DX9::DestroyShaders(); - device->EndScene(); - device->Release(); - d3d->Release(); - UnloadD3DXDynamic(); - DX9::pD3Ddevice = nullptr; - DX9::pD3DdeviceEx = nullptr; - DX9::pD3D = nullptr; - */ hWnd_ = nullptr; - // FreeLibrary(hD3D11); - // hD3D11 = nullptr; } diff --git a/Windows/GPU/D3D11Context.h b/Windows/GPU/D3D11Context.h index 7139ece4d6..2b71ffbc63 100644 --- a/Windows/GPU/D3D11Context.h +++ b/Windows/GPU/D3D11Context.h @@ -39,9 +39,14 @@ public: Draw::DrawContext *GetDrawContext() override { return draw_; } private: - Draw::DrawContext *draw_; - ID3D11Device *device_; - ID3D11DeviceContext *context_; + Draw::DrawContext *draw_ = nullptr; + ID3D11Device *device_ = nullptr; + ID3D11DeviceContext *context_ = nullptr; + +#ifdef _DEBUG + ID3D11Debug *d3dDebug_ = nullptr; + ID3D11InfoQueue *d3dInfoQueue_ = nullptr; +#endif D3D_DRIVER_TYPE driverType_; D3D_FEATURE_LEVEL featureLevel_ = D3D_FEATURE_LEVEL_11_0; diff --git a/Windows/main.cpp b/Windows/main.cpp index 3df8e75021..3b6a10a307 100644 --- a/Windows/main.cpp +++ b/Windows/main.cpp @@ -19,6 +19,8 @@ #include #include "Common/CommonWindows.h" +#include "Common/OSVersion.h" + #include #include #include @@ -85,86 +87,6 @@ void Vibrate(int length_ms) { // Ignore on PC } -bool DoesVersionMatchWindows(const u32 major, const u32 minor, const u32 spMajor = 0, const u32 spMinor = 0) { - u64 conditionMask = 0; - OSVERSIONINFOEX osvi; - ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); - - osvi.dwOSVersionInfoSize = sizeof(osvi); - osvi.dwMajorVersion = major; - osvi.dwMinorVersion = minor; - osvi.wServicePackMajor = spMajor; - osvi.wServicePackMinor = spMinor; - u32 op = VER_EQUAL; - - VER_SET_CONDITION(conditionMask, VER_MAJORVERSION, op); - VER_SET_CONDITION(conditionMask, VER_MINORVERSION, op); - VER_SET_CONDITION(conditionMask, VER_SERVICEPACKMAJOR, op); - VER_SET_CONDITION(conditionMask, VER_SERVICEPACKMINOR, op); - - const u32 typeMask = VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR; - - return VerifyVersionInfo(&osvi, typeMask, conditionMask) != FALSE; -} - -std::string GetWindowsVersion() { - const bool IsWindowsXPSP2 = DoesVersionMatchWindows(5, 1, 2, 0); - const bool IsWindowsXPSP3 = DoesVersionMatchWindows(5, 1, 3, 0); - const bool IsWindowsVista = DoesVersionMatchWindows(6, 0); - const bool IsWindowsVistaSP1 = DoesVersionMatchWindows(6, 0, 1, 0); - const bool IsWindowsVistaSP2 = DoesVersionMatchWindows(6, 0, 2, 0); - const bool IsWindows7 = DoesVersionMatchWindows(6, 1); - const bool IsWindows7SP1 = DoesVersionMatchWindows(6, 1, 1, 0); - const bool IsWindows8 = DoesVersionMatchWindows(6, 2); - const bool IsWindows8_1 = DoesVersionMatchWindows(6, 3); - - - if (IsWindowsXPSP2) - return "Microsoft Windows XP, Service Pack 2"; - - if (IsWindowsXPSP3) - return "Microsoft Windows XP, Service Pack 3"; - - if (IsWindowsVista) - return "Microsoft Windows Vista"; - - if (IsWindowsVistaSP1) - return "Microsoft Windows Vista, Service Pack 1"; - - if (IsWindowsVistaSP2) - return "Microsoft Windows Vista, Service Pack 2"; - - if (IsWindows7) - return "Microsoft Windows 7"; - - if (IsWindows7SP1) - return "Microsoft Windows 7, Service Pack 1"; - - if (IsWindows8) - return "Microsoft Windows 8 or greater"; // "Applications not manifested for Windows 10 will return the Windows 8 OS version value (6.2)." - - if (IsWindows8_1) - return "Microsoft Windows 8.1"; - - return "Unsupported version of Microsoft Windows."; -} - -std::string GetWindowsSystemArchitecture() { - SYSTEM_INFO sysinfo; - ZeroMemory(&sysinfo, sizeof(SYSTEM_INFO)); - GetNativeSystemInfo(&sysinfo); - - if (sysinfo.wProcessorArchitecture & PROCESSOR_ARCHITECTURE_AMD64) - return "(x64)"; - // Need to check for equality here, since ANDing with 0 is always 0. - else if (sysinfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL) - return "(x86)"; - else if (sysinfo.wProcessorArchitecture & PROCESSOR_ARCHITECTURE_ARM) - return "(ARM)"; - else - return "(Unknown)"; -} - // Adapted mostly as-is from http://www.gamedev.net/topic/495075-how-to-retrieve-info-about-videocard/?view=findpost&p=4229170 // so credit goes to that post's author, and in turn, the author of the site mentioned in that post (which seems to be down?). std::string GetVideoCardDriverVersion() { diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 24c0b6dc1e..9a73162e85 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -224,6 +224,7 @@ EXEC_AND_LIB_FILES := \ $(SRC)/GPU/Common/TransformCommon.cpp.arm \ $(SRC)/GPU/Common/TextureDecoder.cpp \ $(SRC)/GPU/Common/PostShader.cpp \ + $(SRC)/GPU/Common/ShaderUniforms.cpp \ $(SRC)/GPU/Debugger/Breakpoints.cpp \ $(SRC)/GPU/Debugger/Stepping.cpp \ $(SRC)/GPU/GLES/FramebufferManagerGLES.cpp \ diff --git a/dx9sdk b/dx9sdk index a2e5de9de3..7324553f8f 160000 --- a/dx9sdk +++ b/dx9sdk @@ -1 +1 @@ -Subproject commit a2e5de9de341a8659aa1199e03636f5a4cea1318 +Subproject commit 7324553f8f4952a576c7f2d42df0accb0335d23c diff --git a/ext/native/base/stringutil.cpp b/ext/native/base/stringutil.cpp index d2dc16d22b..4c72959b39 100644 --- a/ext/native/base/stringutil.cpp +++ b/ext/native/base/stringutil.cpp @@ -39,6 +39,19 @@ void OutputDebugStringUTF8(const char *p) { #endif +std::string LineNumberString(const std::string &str) { + std::stringstream input(str); + std::stringstream output; + std::string line; + + int lineNumber = 1; + while (std::getline(input, line)) { + output << std::setw(4) << lineNumber++ << ": " << line << std::endl; + } + + return output.str(); +} + void StringTrimEndNonAlphaNum(char *str) { ssize_t n = strlen(str); while (!isalnum(str[n]) && n >= 0) { diff --git a/ext/native/base/stringutil.h b/ext/native/base/stringutil.h index 34ceb6076d..e001f42532 100644 --- a/ext/native/base/stringutil.h +++ b/ext/native/base/stringutil.h @@ -25,6 +25,9 @@ public: } }; +// Useful for shaders with error messages.. +std::string LineNumberString(const std::string &str); + // Other simple string utilities. inline bool startsWith(const std::string &str, const std::string &what) { diff --git a/ext/native/math/dataconv.h b/ext/native/math/dataconv.h index 46ea8b84be..5edf0d0d04 100644 --- a/ext/native/math/dataconv.h +++ b/ext/native/math/dataconv.h @@ -3,8 +3,11 @@ #include #include -// Utilities useful for filling in std140-layout uniform buffers. +#ifdef _M_SSE +#include > +#endif +// Utilities useful for filling in std140-layout uniform buffers, and similar. // LSBs in f[0], etc. // Could be SSE optimized. diff --git a/ext/native/thin3d/thin3d.h b/ext/native/thin3d/thin3d.h index b57f21ed12..b5b9c3311f 100644 --- a/ext/native/thin3d/thin3d.h +++ b/ext/native/thin3d/thin3d.h @@ -316,6 +316,8 @@ enum class NativeObject { CONTEXT, DEVICE, DEVICE_EX, + BACKBUFFER_COLOR_VIEW, + BACKBUFFER_DEPTH_VIEW, }; enum FBColorDepth { @@ -345,6 +347,7 @@ enum class Event { // These happen on D3D resize LOST_BACKBUFFER, GOT_BACKBUFFER, + RESIZED, PRESENT_REQUESTED, }; @@ -456,6 +459,7 @@ public: class Pipeline : public RefCountedObject { public: + virtual ~Pipeline() {} virtual bool RequiresBuffer() = 0; }; @@ -532,6 +536,8 @@ struct DeviceCaps { bool tesselationShaderSupported; bool multiViewport; bool dualSourceBlend; + bool framebufferCopySupported; + bool framebufferBlitSupported; }; struct TextureDesc { @@ -578,7 +584,7 @@ public: // Copies data from the CPU over into the buffer, at a specific offset. This does not change the size of the buffer and cannot write outside it. virtual void UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t offset, size_t size, UpdateBufferFlags flags) = 0; - virtual void CopyFramebufferImage(Framebuffer *src, int level, int x, int y, int z, Framebuffer *dst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth) = 0; + virtual void CopyFramebufferImage(Framebuffer *src, int level, int x, int y, int z, Framebuffer *dst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits) = 0; virtual bool BlitFramebuffer(Framebuffer *src, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *dst, int dstX1, int dstY1, int dstX2, int dstY2, int channelBits, FBBlitFilter filter) = 0; // These functions should be self explanatory. @@ -610,6 +616,7 @@ public: BindTextures(stage, 1, &texture); } // from sampler 0 and upwards + // Call this with 0 to signal that you have been drawing on your own, and need the state reset on the next pipeline bind. virtual void BindPipeline(Pipeline *pipeline) = 0; // TODO: Add more sophisticated draws with buffer offsets, and multidraws. diff --git a/ext/native/thin3d/thin3d_d3d11.cpp b/ext/native/thin3d/thin3d_d3d11.cpp index 138b55ecf1..ab89fb0854 100644 --- a/ext/native/thin3d/thin3d_d3d11.cpp +++ b/ext/native/thin3d/thin3d_d3d11.cpp @@ -45,7 +45,7 @@ public: void UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t offset, size_t size, UpdateBufferFlags flags) override; - void CopyFramebufferImage(Framebuffer *src, int level, int x, int y, int z, Framebuffer *dst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth) override; + void CopyFramebufferImage(Framebuffer *src, int level, int x, int y, int z, Framebuffer *dst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits) override; bool BlitFramebuffer(Framebuffer *src, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *dst, int dstX1, int dstY1, int dstX2, int dstY2, int channelBits, FBBlitFilter filter) override; // These functions should be self explanatory. @@ -100,18 +100,16 @@ public: return (uintptr_t)device_; case NativeObject::CONTEXT: return (uintptr_t)context_; + case NativeObject::BACKBUFFER_COLOR_VIEW: + return (uintptr_t)bbRenderTargetView_; + case NativeObject::BACKBUFFER_DEPTH_VIEW: + return (uintptr_t)bbDepthStencilView_; default: return 0; } } - void HandleEvent(Event ev) override { - switch (ev) { - case Event::PRESENT_REQUESTED: - swapChain_->Present(0, 0); - break; - } - } + void HandleEvent(Event ev) override; private: void ApplyCurrentState(); @@ -126,6 +124,9 @@ private: ID3D11Texture2D *bbDepthStencilTex_ = nullptr; ID3D11DepthStencilView *bbDepthStencilView_ = nullptr; + ID3D11RenderTargetView *curRenderTargetView_ = nullptr; + ID3D11DepthStencilView *curDepthStencilView_ = nullptr; + D3D11Pipeline *curPipeline_; DeviceCaps caps_; @@ -179,6 +180,10 @@ D3D11DrawContext::D3D11DrawContext(ID3D11Device *device, ID3D11DeviceContext *co dxgiDevice->Release(); } + caps_.dualSourceBlend = true; + caps_.depthRangeMinusOneToOne = false; + caps_.framebufferBlitSupported = false; + int width; int height; GetRes(hWnd_, width, height); @@ -202,44 +207,6 @@ D3D11DrawContext::D3D11DrawContext(ID3D11Device *device, ID3D11DeviceContext *co dxgiFactory->MakeWindowAssociation(hWnd_, DXGI_MWA_NO_ALT_ENTER); dxgiFactory->Release(); - if (FAILED(hr)) - return; - - // Create a render target view - ID3D11Texture2D* pBackBuffer = nullptr; - hr = swapChain_->GetBuffer(0, __uuidof(ID3D11Texture2D), reinterpret_cast(&pBackBuffer)); - if (FAILED(hr)) - return; - - hr = device_->CreateRenderTargetView(pBackBuffer, nullptr, &bbRenderTargetView_); - pBackBuffer->Release(); - if (FAILED(hr)) - return; - - // Create depth stencil texture - D3D11_TEXTURE2D_DESC descDepth{}; - descDepth.Width = width; - descDepth.Height = height; - descDepth.MipLevels = 1; - descDepth.ArraySize = 1; - descDepth.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; - descDepth.SampleDesc.Count = 1; - descDepth.SampleDesc.Quality = 0; - descDepth.Usage = D3D11_USAGE_DEFAULT; - descDepth.BindFlags = D3D11_BIND_DEPTH_STENCIL; - descDepth.CPUAccessFlags = 0; - descDepth.MiscFlags = 0; - hr = device_->CreateTexture2D(&descDepth, nullptr, &bbDepthStencilTex_); - - // Create the depth stencil view - D3D11_DEPTH_STENCIL_VIEW_DESC descDSV{}; - descDSV.Format = descDepth.Format; - descDSV.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D; - descDSV.Texture2D.MipSlice = 0; - hr = device_->CreateDepthStencilView(bbDepthStencilTex_, &descDSV, &bbDepthStencilView_); - - context_->OMSetRenderTargets(1, &bbRenderTargetView_, bbDepthStencilView_); - CreatePresets(); } @@ -247,6 +214,79 @@ D3D11DrawContext::~D3D11DrawContext() { } +void D3D11DrawContext::HandleEvent(Event ev) { + switch (ev) { + case Event::PRESENT_REQUESTED: + swapChain_->Present(0, 0); + break; + case Event::RESIZED: { + int width; + int height; + GetRes(hWnd_, width, height); + swapChain_->ResizeBuffers(0, width, height, DXGI_FORMAT_UNKNOWN, 0); + break; + } + case Event::LOST_BACKBUFFER: { + if (curRenderTargetView_ == bbRenderTargetView_ || curDepthStencilView_ == bbDepthStencilView_) { + ID3D11RenderTargetView *view = nullptr; + context_->OMSetRenderTargets(1, &view, nullptr); + curRenderTargetView_ = nullptr; + curDepthStencilView_ = nullptr; + } + bbRenderTargetView_->Release(); + bbRenderTargetView_ = nullptr; + bbDepthStencilView_->Release(); + bbDepthStencilView_ = nullptr; + bbDepthStencilTex_->Release(); + bbDepthStencilTex_ = nullptr; + break; + } + case Event::GOT_BACKBUFFER: { + int width; + int height; + GetRes(hWnd_, width, height); + // Create a render target view + ID3D11Texture2D* pBackBuffer = nullptr; + HRESULT hr = swapChain_->GetBuffer(0, __uuidof(ID3D11Texture2D), reinterpret_cast(&pBackBuffer)); + if (FAILED(hr)) + return; + + hr = device_->CreateRenderTargetView(pBackBuffer, nullptr, &bbRenderTargetView_); + pBackBuffer->Release(); + if (FAILED(hr)) + return; + + // Create depth stencil texture + D3D11_TEXTURE2D_DESC descDepth{}; + descDepth.Width = width; + descDepth.Height = height; + descDepth.MipLevels = 1; + descDepth.ArraySize = 1; + descDepth.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; + descDepth.SampleDesc.Count = 1; + descDepth.SampleDesc.Quality = 0; + descDepth.Usage = D3D11_USAGE_DEFAULT; + descDepth.BindFlags = D3D11_BIND_DEPTH_STENCIL; + descDepth.CPUAccessFlags = 0; + descDepth.MiscFlags = 0; + hr = device_->CreateTexture2D(&descDepth, nullptr, &bbDepthStencilTex_); + + // Create the depth stencil view + D3D11_DEPTH_STENCIL_VIEW_DESC descDSV{}; + descDSV.Format = descDepth.Format; + descDSV.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D; + descDSV.Texture2D.MipSlice = 0; + hr = device_->CreateDepthStencilView(bbDepthStencilTex_, &descDSV, &bbDepthStencilView_); + + context_->OMSetRenderTargets(1, &bbRenderTargetView_, bbDepthStencilView_); + + curRenderTargetView_ = bbRenderTargetView_; + curDepthStencilView_ = bbDepthStencilView_; + break; + } + } +} + void D3D11DrawContext::SetViewports(int count, Viewport *viewports) { D3D11_VIEWPORT vp[4]; for (int i = 0; i < count; i++) { @@ -792,6 +832,18 @@ void D3D11DrawContext::UpdateDynamicUniformBuffer(const void *ub, size_t size) { } void D3D11DrawContext::BindPipeline(Pipeline *pipeline) { + if (pipeline == nullptr) { + // This is a signal to forget all our caching. + curBlend_ = nullptr; + curDepth_ = nullptr; + curRaster_ = nullptr; + curPS_ = nullptr; + curVS_ = nullptr; + curGS_ = nullptr; + curInputLayout_ = nullptr; + curTopology_ = D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED; + } + D3D11Pipeline *dPipeline = (D3D11Pipeline *)pipeline; if (curPipeline_ == dPipeline) return; @@ -840,7 +892,6 @@ void D3D11DrawContext::ApplyCurrentState() { if (nextIndexBuffer_) { context_->IASetIndexBuffer(nextIndexBuffer_, DXGI_FORMAT_R32_UINT, nextIndexBufferOffset_); } - if (curPipeline_->dynamicUniforms) { context_->VSSetConstantBuffers(0, 1, &curPipeline_->dynamicUniforms); } @@ -1081,33 +1132,57 @@ void D3D11DrawContext::BindSamplerStates(int start, int count, SamplerState **st } void D3D11DrawContext::Clear(int mask, uint32_t colorval, float depthVal, int stencilVal) { - if (mask & ClearFlag::COLOR) { + if ((mask & ClearFlag::COLOR) && curRenderTargetView_) { float colorRGBA[4]; - Uint8x1ToFloat4(colorRGBA, colorval); - context_->ClearRenderTargetView(bbRenderTargetView_, colorRGBA); + Uint8x4ToFloat4(colorRGBA, colorval); + context_->ClearRenderTargetView(curRenderTargetView_, colorRGBA); } - if (mask & (ClearFlag::DEPTH | ClearFlag::STENCIL)) { + if ((mask & (ClearFlag::DEPTH | ClearFlag::STENCIL)) && curDepthStencilView_) { UINT clearFlag = 0; if (mask & ClearFlag::DEPTH) clearFlag |= D3D11_CLEAR_DEPTH; if (mask & ClearFlag::STENCIL) clearFlag |= D3D11_CLEAR_STENCIL; - context_->ClearDepthStencilView(bbDepthStencilView_, clearFlag, depthVal, stencilVal); + context_->ClearDepthStencilView(curDepthStencilView_, clearFlag, depthVal, stencilVal); } } -void D3D11DrawContext::CopyFramebufferImage(Framebuffer *srcfb, int level, int x, int y, int z, Framebuffer *dstfb, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth) { +void D3D11DrawContext::CopyFramebufferImage(Framebuffer *srcfb, int level, int x, int y, int z, Framebuffer *dstfb, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits) { D3D11Framebuffer *src = (D3D11Framebuffer *)srcfb; D3D11Framebuffer *dst = (D3D11Framebuffer *)dstfb; - // CopySubResource ? + ID3D11Texture2D *srcTex = nullptr; + ID3D11Texture2D *dstTex = nullptr; + switch (channelBits) { + case FB_COLOR_BIT: + srcTex = src->colorTex; + dstTex = dst->colorTex; + break; + case FB_DEPTH_BIT: + srcTex = src->depthStencilTex; + dstTex = dst->depthStencilTex; + break; + } + + // TODO: Check for level too! + if (width == src->width && width == dst->width && height == src->height && height == dst->height && x == 0 && y == 0 && z == 0 && dstX == 0 && dstY == 0 && dstZ == 0) { + // Don't need to specify region. This might be faster, too. + context_->CopyResource(dstTex, srcTex); + return; + } + + if (channelBits != FB_DEPTH_BIT) { + // Non-full copies are not supported for the depth channel. + D3D11_BOX srcBox{ (UINT)x, (UINT)y, (UINT)z, (UINT)(x + width), (UINT)(y + height), (UINT)(z + depth) }; + context_->CopySubresourceRegion(dstTex, dstLevel, dstX, dstY, dstZ, srcTex, level, &srcBox); + } } bool D3D11DrawContext::BlitFramebuffer(Framebuffer *srcfb, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *dstfb, int dstX1, int dstY1, int dstX2, int dstY2, int channelBits, FBBlitFilter filter) { D3D11Framebuffer *src = (D3D11Framebuffer *)srcfb; D3D11Framebuffer *dst = (D3D11Framebuffer *)dstfb; - // Unfortunately D3D11 has no equivalent to this, gotta render a quad. + // Unfortunately D3D11 has no equivalent to this, gotta render a quad. Well, in some cases we can issue a copy instead. return true; } @@ -1115,6 +1190,8 @@ bool D3D11DrawContext::BlitFramebuffer(Framebuffer *srcfb, int srcX1, int srcY1, void D3D11DrawContext::BindFramebufferAsRenderTarget(Framebuffer *fbo) { D3D11Framebuffer *fb = (D3D11Framebuffer *)fbo; context_->OMSetRenderTargets(1, &fb->colorRTView, fb->depthStencilRTView); + curRenderTargetView_ = fb->colorRTView; + curDepthStencilView_ = fb->depthStencilRTView; } // color must be 0, for now. @@ -1129,6 +1206,8 @@ void D3D11DrawContext::BindFramebufferForRead(Framebuffer *fbo) { void D3D11DrawContext::BindBackbufferAsRenderTarget() { context_->OMSetRenderTargets(1, &bbRenderTargetView_, bbDepthStencilView_); + curRenderTargetView_ = bbRenderTargetView_; + curDepthStencilView_ = bbDepthStencilView_; } uintptr_t D3D11DrawContext::GetFramebufferAPITexture(Framebuffer *fbo, int channelBit, int attachment) { diff --git a/ext/native/thin3d/thin3d_d3d9.cpp b/ext/native/thin3d/thin3d_d3d9.cpp index 811e7d0a2a..306497ec4e 100644 --- a/ext/native/thin3d/thin3d_d3d9.cpp +++ b/ext/native/thin3d/thin3d_d3d9.cpp @@ -478,7 +478,7 @@ public: void UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t offset, size_t size, UpdateBufferFlags flags) override; - void CopyFramebufferImage(Framebuffer *src, int level, int x, int y, int z, Framebuffer *dst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth) override {} + void CopyFramebufferImage(Framebuffer *src, int level, int x, int y, int z, Framebuffer *dst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits) override {} bool BlitFramebuffer(Framebuffer *src, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *dst, int dstX1, int dstY1, int dstX2, int dstY2, int channelBits, FBBlitFilter filter) override; // These functions should be self explanatory. @@ -595,6 +595,8 @@ D3D9Context::D3D9Context(IDirect3D9 *d3d, IDirect3D9Ex *d3dEx, int adapterId, ID caps_.preferredDepthBufferFormat = DataFormat::D24_S8; caps_.dualSourceBlend = false; caps_.tesselationShaderSupported = false; + caps_.framebufferBlitSupported = true; + caps_.framebufferCopySupported = false; if (d3d) { D3DDISPLAYMODE displayMode; d3d->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &displayMode); diff --git a/ext/native/thin3d/thin3d_gl.cpp b/ext/native/thin3d/thin3d_gl.cpp index b3fba72245..75ad9259af 100644 --- a/ext/native/thin3d/thin3d_gl.cpp +++ b/ext/native/thin3d/thin3d_gl.cpp @@ -469,7 +469,7 @@ public: void UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t offset, size_t size, UpdateBufferFlags flags) override; - void CopyFramebufferImage(Framebuffer *src, int level, int x, int y, int z, Framebuffer *dst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth) override; + void CopyFramebufferImage(Framebuffer *src, int level, int x, int y, int z, Framebuffer *dst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits) override; bool BlitFramebuffer(Framebuffer *src, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *dst, int dstX1, int dstY1, int dstX2, int dstY2, int channelBits, FBBlitFilter filter) override; // These functions should be self explanatory. @@ -615,6 +615,7 @@ OpenGLContext::OpenGLContext() { } else { caps_.preferredDepthBufferFormat = DataFormat::D24_S8; } + caps_.framebufferBlitSupported = gl_extensions.NV_framebuffer_blit || gl_extensions.ARB_framebuffer_object; } OpenGLContext::~OpenGLContext() { @@ -1506,29 +1507,41 @@ void OpenGLContext::BindFramebufferForRead(Framebuffer *fbo) { fbo_bind_fb_target(true, fb->handle); } -void OpenGLContext::CopyFramebufferImage(Framebuffer *fbsrc, int srcLevel, int srcX, int srcY, int srcZ, Framebuffer *fbdst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth) { +void OpenGLContext::CopyFramebufferImage(Framebuffer *fbsrc, int srcLevel, int srcX, int srcY, int srcZ, Framebuffer *fbdst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits) { OpenGLFramebuffer *src = (OpenGLFramebuffer *)fbsrc; OpenGLFramebuffer *dst = (OpenGLFramebuffer *)fbdst; + GLuint srcTex = 0; + GLuint dstTex = 0; + switch (channelBits) { + case FB_COLOR_BIT: + srcTex = src->color_texture; + dstTex = dst->color_texture; + break; + case FB_DEPTH_BIT: + srcTex = src->z_buffer ? src->z_buffer : src->z_stencil_buffer; + dstTex = dst->z_buffer ? dst->z_buffer : dst->z_stencil_buffer; + break; + } #if defined(USING_GLES2) #ifndef IOS glCopyImageSubDataOES( - src->color_texture, GL_TEXTURE_2D, srcLevel, srcX, srcY, srcZ, - dst->color_texture, GL_TEXTURE_2D, dstLevel, dstX, dstY, dstZ, + srcTex, GL_TEXTURE_2D, srcLevel, srcX, srcY, srcZ, + dstTex, GL_TEXTURE_2D, dstLevel, dstX, dstY, dstZ, width, height, depth); return; #endif #else if (gl_extensions.ARB_copy_image) { glCopyImageSubData( - src->color_texture, GL_TEXTURE_2D, srcLevel, srcX, srcY, srcZ, - dst->color_texture, GL_TEXTURE_2D, dstLevel, dstX, dstY, dstZ, + srcTex, GL_TEXTURE_2D, srcLevel, srcX, srcY, srcZ, + dstTex, GL_TEXTURE_2D, dstLevel, dstX, dstY, dstZ, width, height, depth); return; } else if (gl_extensions.NV_copy_image) { // Older, pre GL 4.x NVIDIA cards. glCopyImageSubDataNV( - src->color_texture, GL_TEXTURE_2D, srcLevel, srcX, srcY, srcZ, - dst->color_texture, GL_TEXTURE_2D, dstLevel, dstX, dstY, dstZ, + srcTex, GL_TEXTURE_2D, srcLevel, srcX, srcY, srcZ, + dstTex, GL_TEXTURE_2D, dstLevel, dstX, dstY, dstZ, width, height, depth); return; } diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index 5563f36453..45d2ea3044 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -340,7 +340,7 @@ public: void UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t offset, size_t size, UpdateBufferFlags flags) override; - void CopyFramebufferImage(Framebuffer *src, int level, int x, int y, int z, Framebuffer *dst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth) override; + void CopyFramebufferImage(Framebuffer *src, int level, int x, int y, int z, Framebuffer *dst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits) override; bool BlitFramebuffer(Framebuffer *src, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *dst, int dstX1, int dstY1, int dstX2, int dstY2, int channelBits, FBBlitFilter filter) override; // These functions should be self explanatory. @@ -639,7 +639,9 @@ VKContext::VKContext(VulkanContext *vulkan) caps_.tesselationShaderSupported = vulkan->GetFeaturesAvailable().tessellationShader != 0; caps_.multiViewport = vulkan->GetFeaturesAvailable().multiViewport != 0; caps_.dualSourceBlend = vulkan->GetFeaturesAvailable().dualSrcBlend != 0; - + caps_.framebufferBlitSupported = true; + caps_.framebufferCopySupported = true; + caps_.preferredDepthBufferFormat = DataFormat::D24_S8; device_ = vulkan->GetDevice(); queue_ = vulkan->GetGraphicsQueue(); @@ -903,7 +905,7 @@ Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc) { return nullptr; } if (desc.uniformDesc) { - pipeline->dynamicUniformSize = desc.uniformDesc->uniformBufferSize; + pipeline->dynamicUniformSize = (int)desc.uniformDesc->uniformBufferSize; } return pipeline; @@ -1280,17 +1282,34 @@ Framebuffer *VKContext::CreateFramebuffer(const FramebufferDesc &desc) { return fb; } -void VKContext::CopyFramebufferImage(Framebuffer *src, int level, int x, int y, int z, Framebuffer *dst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth) {} -bool VKContext::BlitFramebuffer(Framebuffer *src, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *dst, int dstX1, int dstY1, int dstX2, int dstY2, int channelBits, FBBlitFilter filter) { return true; } +void VKContext::CopyFramebufferImage(Framebuffer *srcfb, int level, int x, int y, int z, Framebuffer *dstfb, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits) { + VKFramebuffer *src = (VKFramebuffer *)srcfb; + VKFramebuffer *dst = (VKFramebuffer *)dstfb; +} +bool VKContext::BlitFramebuffer(Framebuffer *srcfb, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *dstfb, int dstX1, int dstY1, int dstX2, int dstY2, int channelBits, FBBlitFilter filter) { + VKFramebuffer *src = (VKFramebuffer *)srcfb; + VKFramebuffer *dst = (VKFramebuffer *)dstfb; + return true; +} // These functions should be self explanatory. -void VKContext::BindFramebufferAsRenderTarget(Framebuffer *fbo) {} +void VKContext::BindFramebufferAsRenderTarget(Framebuffer *fbo) { + VKFramebuffer *fb = (VKFramebuffer *)fbo; +} // color must be 0, for now. -void VKContext::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int attachment) {} -void VKContext::BindFramebufferForRead(Framebuffer *fbo) {} +void VKContext::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int attachment) { + VKFramebuffer *fb = (VKFramebuffer *)fbo; -void VKContext::BindBackbufferAsRenderTarget() {} -uintptr_t VKContext::GetFramebufferAPITexture(Framebuffer *fbo, int channelBit, int attachment) { return 0; } +} +void VKContext::BindFramebufferForRead(Framebuffer *fbo) { /* noop */ } + +void VKContext::BindBackbufferAsRenderTarget() { + +} + +uintptr_t VKContext::GetFramebufferAPITexture(Framebuffer *fbo, int channelBit, int attachment) { + return 0; +} void VKContext::GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) { VKFramebuffer *fb = (VKFramebuffer *)fbo;