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