mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-11-15 02:29:51 +01:00
Merge pull request #16142 from unknownbrackets/geo-shader
Implement geometry shader for range culling
This commit is contained in:
33 files changed
+585
-53
No files matched your search
@@ -1562,6 +1562,8 @@ set(GPU_SOURCES
|
||||
GPU/Common/FragmentShaderGenerator.h
|
||||
GPU/Common/VertexShaderGenerator.cpp
|
||||
GPU/Common/VertexShaderGenerator.h
|
||||
GPU/Common/GeometryShaderGenerator.cpp
|
||||
GPU/Common/GeometryShaderGenerator.h
|
||||
GPU/Common/FramebufferManagerCommon.cpp
|
||||
GPU/Common/FramebufferManagerCommon.h
|
||||
GPU/Common/GPUDebugInterface.cpp
|
||||
|
||||
@@ -600,8 +600,6 @@ void VulkanContext::ChooseDevice(int physical_device) {
|
||||
deviceFeatures_.enabled.samplerAnisotropy = deviceFeatures_.available.samplerAnisotropy;
|
||||
deviceFeatures_.enabled.shaderClipDistance = deviceFeatures_.available.shaderClipDistance;
|
||||
deviceFeatures_.enabled.shaderCullDistance = deviceFeatures_.available.shaderCullDistance;
|
||||
// For easy wireframe mode, someday.
|
||||
deviceFeatures_.enabled.fillModeNonSolid = deviceFeatures_.available.fillModeNonSolid;
|
||||
deviceFeatures_.enabled.geometryShader = deviceFeatures_.available.geometryShader;
|
||||
|
||||
GetDeviceLayerExtensionList(nullptr, device_extension_properties_);
|
||||
|
||||
@@ -30,8 +30,9 @@ bool VKRGraphicsPipeline::Create(VulkanContext *vulkan, VkRenderPass compatibleR
|
||||
// Fill in the last part of the desc since now it's time to block.
|
||||
VkShaderModule vs = desc->vertexShader->BlockUntilReady();
|
||||
VkShaderModule fs = desc->fragmentShader->BlockUntilReady();
|
||||
VkShaderModule gs = desc->geometryShader ? desc->geometryShader->BlockUntilReady() : VK_NULL_HANDLE;
|
||||
|
||||
if (!vs || !fs) {
|
||||
if (!vs || !fs || (!gs && desc->geometryShader)) {
|
||||
ERROR_LOG(G3D, "Failed creating graphics pipeline - missing shader modules");
|
||||
// We're kinda screwed here?
|
||||
return false;
|
||||
@@ -49,6 +50,14 @@ bool VKRGraphicsPipeline::Create(VulkanContext *vulkan, VkRenderPass compatibleR
|
||||
ss[1].pSpecializationInfo = nullptr;
|
||||
ss[1].module = fs;
|
||||
ss[1].pName = "main";
|
||||
if (gs) {
|
||||
stageCount++;
|
||||
ss[2].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||
ss[2].stage = VK_SHADER_STAGE_GEOMETRY_BIT;
|
||||
ss[2].pSpecializationInfo = nullptr;
|
||||
ss[2].module = gs;
|
||||
ss[2].pName = "main";
|
||||
}
|
||||
|
||||
VkGraphicsPipelineCreateInfo pipe{ VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO };
|
||||
pipe.pStages = ss;
|
||||
|
||||
@@ -128,6 +128,7 @@ struct VKRGraphicsPipelineDesc {
|
||||
// Replaced the ShaderStageInfo with promises here so we can wait for compiles to finish.
|
||||
Promise<VkShaderModule> *vertexShader = nullptr;
|
||||
Promise<VkShaderModule> *fragmentShader = nullptr;
|
||||
Promise<VkShaderModule> *geometryShader = nullptr;
|
||||
|
||||
VkPipelineInputAssemblyStateCreateInfo inputAssembly{ VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO };
|
||||
VkVertexInputAttributeDescription attrs[8]{};
|
||||
|
||||
@@ -177,9 +177,9 @@ VkShaderStageFlagBits StageToVulkan(ShaderStage stage) {
|
||||
case ShaderStage::Vertex: return VK_SHADER_STAGE_VERTEX_BIT;
|
||||
case ShaderStage::Geometry: return VK_SHADER_STAGE_GEOMETRY_BIT;
|
||||
case ShaderStage::Compute: return VK_SHADER_STAGE_COMPUTE_BIT;
|
||||
default:
|
||||
case ShaderStage::Fragment: return VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
}
|
||||
return VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
}
|
||||
|
||||
// Not registering this as a resource holder, instead the pipeline is registered. It will
|
||||
@@ -846,6 +846,11 @@ VKContext::VKContext(VulkanContext *vulkan)
|
||||
if (majorVersion >= 32) {
|
||||
bugs_.Infest(Bugs::MALI_CONSTANT_LOAD_BUG); // See issue #15661
|
||||
}
|
||||
|
||||
// Older ARM devices have very slow geometry shaders, not worth using. At least before 15.
|
||||
if (majorVersion <= 15) {
|
||||
bugs_.Infest(Bugs::GEOMETRY_SHADERS_SLOW);
|
||||
}
|
||||
}
|
||||
|
||||
// Limited, through input attachments and self-dependencies.
|
||||
|
||||
@@ -333,11 +333,14 @@ public:
|
||||
RASPBERRY_SHADER_COMP_HANG = 8,
|
||||
MALI_CONSTANT_LOAD_BUG = 9,
|
||||
SUBPASS_FEEDBACK_BROKEN = 10,
|
||||
GEOMETRY_SHADERS_SLOW = 11,
|
||||
MAX_BUG,
|
||||
};
|
||||
|
||||
protected:
|
||||
uint32_t flags_ = 0;
|
||||
|
||||
static_assert(sizeof(flags_) * 8 > MAX_BUG, "Ran out of space for bugs.");
|
||||
};
|
||||
|
||||
class RefCountedObject {
|
||||
|
||||
@@ -874,6 +874,7 @@ static ConfigSetting graphicsSettings[] = {
|
||||
#endif
|
||||
ConfigSetting("CameraDevice", &g_Config.sCameraDevice, "", true, false),
|
||||
ConfigSetting("VendorBugChecksEnabled", &g_Config.bVendorBugChecksEnabled, true, false, false),
|
||||
ConfigSetting("UseGeometryShader", &g_Config.bUseGeometryShader, true, true, true),
|
||||
ReportedConfigSetting("RenderingMode", &g_Config.iRenderingMode, 1, true, true),
|
||||
ConfigSetting("SoftwareRenderer", &g_Config.bSoftwareRendering, false, true, true),
|
||||
ConfigSetting("SoftwareRendererJit", &g_Config.bSoftwareRenderingJit, true, true, true),
|
||||
|
||||
@@ -164,6 +164,7 @@ public:
|
||||
bool bHardwareTransform; // only used in the GLES backend
|
||||
bool bSoftwareSkinning; // may speed up some games
|
||||
bool bVendorBugChecksEnabled;
|
||||
bool bUseGeometryShader;
|
||||
|
||||
int iRenderingMode; // 0 = non-buffered rendering 1 = buffered rendering
|
||||
int iTexFiltering; // 1 = auto , 2 = nearest , 3 = linear , 4 = auto max quality
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
// 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 <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <locale.h>
|
||||
|
||||
#include "Common/StringUtils.h"
|
||||
#include "Common/GPU/OpenGL/GLFeatures.h"
|
||||
#include "Common/GPU/ShaderWriter.h"
|
||||
#include "Common/GPU/thin3d.h"
|
||||
#include "Core/Config.h"
|
||||
#include "GPU/ge_constants.h"
|
||||
#include "GPU/GPUState.h"
|
||||
#include "GPU/Common/ShaderId.h"
|
||||
#include "GPU/Common/ShaderUniforms.h"
|
||||
#include "GPU/Common/GeometryShaderGenerator.h"
|
||||
|
||||
#undef WRITE
|
||||
|
||||
#define WRITE(p, ...) p.F(__VA_ARGS__)
|
||||
|
||||
// TODO: Could support VK_NV_geometry_shader_passthrough, though the hardware that supports
|
||||
// it is already pretty fast at geometry shaders..
|
||||
|
||||
|
||||
bool GenerateGeometryShader(const GShaderID &id, char *buffer, const ShaderLanguageDesc &compat, const Draw::Bugs bugs, std::string *errorString) {
|
||||
std::vector<const char*> gl_exts;
|
||||
if (ShaderLanguageIsOpenGL(compat.shaderLanguage)) {
|
||||
if (gl_extensions.EXT_gpu_shader4) {
|
||||
gl_exts.push_back("#extension GL_EXT_gpu_shader4 : enable");
|
||||
}
|
||||
}
|
||||
|
||||
ShaderWriter p(buffer, compat, ShaderStage::Geometry, gl_exts.data(), gl_exts.size());
|
||||
p.C("layout(triangles) in;\n");
|
||||
p.C("layout(triangle_strip, max_vertices = 3) out;\n");
|
||||
|
||||
if (compat.shaderLanguage == GLSL_VULKAN) {
|
||||
WRITE(p, "\n");
|
||||
WRITE(p, "layout (std140, set = 0, binding = 3) uniform baseVars {\n%s};\n", ub_baseStr);
|
||||
} else if (compat.shaderLanguage == HLSL_D3D11) {
|
||||
WRITE(p, "cbuffer base : register(b0) {\n%s};\n", ub_baseStr);
|
||||
}
|
||||
|
||||
std::vector<VaryingDef> varyings, outVaryings;
|
||||
|
||||
if (id.Bit(GS_BIT_DO_TEXTURE)) {
|
||||
varyings.push_back(VaryingDef{ "vec3", "v_texcoord", Draw::SEM_TEXCOORD0, 0, "highp" });
|
||||
outVaryings.push_back(VaryingDef{ "vec3", "v_texcoordOut", Draw::SEM_TEXCOORD0, 0, "highp" });
|
||||
}
|
||||
varyings.push_back(VaryingDef{ "vec4", "v_color0", Draw::SEM_COLOR0, 1, "lowp" });
|
||||
outVaryings.push_back(VaryingDef{ "vec4", "v_color0Out", Draw::SEM_COLOR0, 1, "lowp" });
|
||||
if (id.Bit(GS_BIT_LMODE)) {
|
||||
varyings.push_back(VaryingDef{ "vec3", "v_color1", Draw::SEM_COLOR1, 2, "lowp" });
|
||||
outVaryings.push_back(VaryingDef{ "vec3", "v_color1Out", Draw::SEM_COLOR1, 2, "lowp" });
|
||||
}
|
||||
varyings.push_back(VaryingDef{ "float", "v_fogdepth", Draw::SEM_TEXCOORD1, 3, "highp" });
|
||||
outVaryings.push_back(VaryingDef{ "float", "v_fogdepthOut", Draw::SEM_TEXCOORD1, 3, "highp" });
|
||||
|
||||
p.BeginGSMain(varyings, outVaryings);
|
||||
|
||||
// Apply culling
|
||||
p.C(" bool anyInside = false;\n");
|
||||
|
||||
p.C(" for (int i = 0; i < 3; i++) {\n"); // TODO: 3 or gl_in.length()? which will be faster?
|
||||
p.C(" vec4 outPos = gl_in[i].gl_Position;\n");
|
||||
p.C(" vec3 projPos = outPos.xyz / outPos.w;\n");
|
||||
p.C(" float projZ = (projPos.z - u_depthRange.z) * u_depthRange.w;\n");
|
||||
// Vertex range culling doesn't happen when Z clips, note sign of w is important.
|
||||
p.C(" if (u_cullRangeMin.w <= 0.0 || projZ * outPos.w > -outPos.w) {\n");
|
||||
const char *outMin = "projPos.x < u_cullRangeMin.x || projPos.y < u_cullRangeMin.y";
|
||||
const char *outMax = "projPos.x > u_cullRangeMax.x || projPos.y > u_cullRangeMax.y";
|
||||
p.F(" if ((%s) || (%s)) {\n", outMin, outMax);
|
||||
p.C(" return;\n"); // Cull!
|
||||
p.C(" }\n");
|
||||
p.C(" }\n");
|
||||
p.C(" if (u_cullRangeMin.w <= 0.0) {\n");
|
||||
p.C(" if (projPos.z < u_cullRangeMin.z || projPos.z > u_cullRangeMax.z) {\n");
|
||||
// When not clamping depth, cull the triangle of Z is outside the valid range (not based on clip Z.)
|
||||
p.C(" return;\n");
|
||||
p.C(" }\n");
|
||||
p.C(" } else {\n");
|
||||
p.C(" if (projPos.z >= u_cullRangeMin.z) { anyInside = true; }\n");
|
||||
p.C(" if (projPos.z <= u_cullRangeMax.z) { anyInside = true; }\n");
|
||||
p.C(" }\n");
|
||||
p.C(" } // for\n");
|
||||
|
||||
// Cull any triangle fully outside in the same direction when depth clamp enabled.
|
||||
// Basically simulate cull distances.
|
||||
p.C(" if (u_cullRangeMin.w > 0.0 && !anyInside) {\n");
|
||||
p.C(" return;\n");
|
||||
p.C(" }\n");
|
||||
|
||||
const char *clip0 = compat.shaderLanguage == HLSL_D3D11 ? "" : "[0]";
|
||||
|
||||
p.C(" for (int i = 0; i < 3; i++) {\n"); // TODO: 3 or gl_in.length()? which will be faster?
|
||||
p.C(" vec4 outPos = gl_in[i].gl_Position;\n");
|
||||
p.C(" gl_Position = outPos;\n");
|
||||
// TODO: Not rectangles...
|
||||
if (gstate_c.Supports(GPU_SUPPORTS_CLIP_DISTANCE)) {
|
||||
p.C(" vec3 projPos = outPos.xyz / outPos.w;\n");
|
||||
p.C(" float projZ = (projPos.z - u_depthRange.z) * u_depthRange.w;\n");
|
||||
p.F(" gl_ClipDistance%s = projZ * outPos.w + outPos.w;\n", clip0);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < varyings.size(); i++) {
|
||||
VaryingDef &in = varyings[i];
|
||||
VaryingDef &out = outVaryings[i];
|
||||
p.F(" %s = %s[i];\n", outVaryings[i].name, varyings[i].name);
|
||||
}
|
||||
// Debug - null the red channel
|
||||
//p.C(" if (i == 0) v_color0Out.x = 0.0;\n");
|
||||
p.C(" EmitVertex();\n");
|
||||
p.C(" }\n");
|
||||
|
||||
p.EndGSMain();
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "GPU/Common/ShaderId.h"
|
||||
|
||||
bool GenerateGeometryShader(const GShaderID &id, char *buffer, const ShaderLanguageDesc &compat, const Draw::Bugs bugs, std::string *errorString);
|
||||
@@ -109,10 +109,11 @@ enum : uint64_t {
|
||||
DIRTY_VIEWPORTSCISSOR_STATE = 1ULL << 46,
|
||||
DIRTY_VERTEXSHADER_STATE = 1ULL << 47,
|
||||
DIRTY_FRAGMENTSHADER_STATE = 1ULL << 48,
|
||||
DIRTY_GEOMETRYSHADER_STATE = 1ULL << 49,
|
||||
|
||||
// Everything that's not uniforms. Use this after using thin3d.
|
||||
// TODO: Should we also add DIRTY_FRAMEBUF here? It kinda generally takes care of itself.
|
||||
DIRTY_ALL_RENDER_STATE = DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS,
|
||||
DIRTY_ALL_RENDER_STATE = DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE | DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS,
|
||||
|
||||
// Note that the top 8 bits (54-63) cannot be dirtied through the commonCommandTable due to packing of other flags.
|
||||
|
||||
|
||||
@@ -366,3 +366,42 @@ void ComputeFragmentShaderID(FShaderID *id_out, const ComputedPipelineState &pip
|
||||
|
||||
*id_out = id;
|
||||
}
|
||||
|
||||
std::string GeometryShaderDesc(const GShaderID &id) {
|
||||
std::stringstream desc;
|
||||
desc << StringFromFormat("%08x:%08x ", id.d[1], id.d[0]);
|
||||
if (id.Bit(GS_BIT_ENABLED)) desc << "ENABLED ";
|
||||
if (id.Bit(GS_BIT_DO_TEXTURE)) desc << "TEX ";
|
||||
if (id.Bit(GS_BIT_LMODE)) desc << "LMODE ";
|
||||
return desc.str();
|
||||
}
|
||||
|
||||
void ComputeGeometryShaderID(GShaderID *id_out, const Draw::Bugs &bugs, int prim) {
|
||||
GShaderID id;
|
||||
|
||||
bool vertexRangeCulling =
|
||||
!gstate.isModeThrough() && gstate_c.submitType == SubmitType::DRAW; // neither hw nor sw spline/bezier. See #11692
|
||||
|
||||
// If we're not using GS culling, return a zero ID.
|
||||
// Also, only use this for triangle primitives.
|
||||
if (!vertexRangeCulling || !gstate_c.Supports(GPU_SUPPORTS_GS_CULLING) || (prim != GE_PRIM_TRIANGLES && prim != GE_PRIM_TRIANGLE_FAN && prim != GE_PRIM_TRIANGLE_STRIP)) {
|
||||
*id_out = id;
|
||||
return;
|
||||
}
|
||||
|
||||
id.SetBit(GS_BIT_ENABLED, true);
|
||||
|
||||
if (gstate.isModeClear()) {
|
||||
// No attribute bits.
|
||||
} else {
|
||||
bool isModeThrough = gstate.isModeThrough();
|
||||
bool lmode = gstate.isUsingSecondaryColor() && gstate.isLightingEnabled() && !isModeThrough;
|
||||
|
||||
id.SetBit(GS_BIT_LMODE, lmode);
|
||||
if (gstate.isTextureMapEnabled()) {
|
||||
id.SetBit(GS_BIT_DO_TEXTURE);
|
||||
}
|
||||
}
|
||||
|
||||
*id_out = id;
|
||||
}
|
||||
@@ -104,6 +104,17 @@ static inline FShaderBit operator +(FShaderBit bit, int i) {
|
||||
return FShaderBit((int)bit + i);
|
||||
}
|
||||
|
||||
// Some of these bits are straight from FShaderBit, since they essentially enable attributes directly.
|
||||
enum GShaderBit : uint8_t {
|
||||
GS_BIT_ENABLED = 0, // If not set, we don't use a geo shader.
|
||||
GS_BIT_DO_TEXTURE = 1, // presence of texcoords
|
||||
GS_BIT_LMODE = 2, // presence of specular color (regular color always present)
|
||||
};
|
||||
|
||||
static inline GShaderBit operator +(GShaderBit bit, int i) {
|
||||
return GShaderBit((int)bit + i);
|
||||
}
|
||||
|
||||
struct ShaderID {
|
||||
ShaderID() {
|
||||
clear();
|
||||
@@ -232,6 +243,31 @@ struct FShaderID : ShaderID {
|
||||
}
|
||||
};
|
||||
|
||||
struct GShaderID : ShaderID {
|
||||
GShaderID() : ShaderID() {
|
||||
}
|
||||
|
||||
explicit GShaderID(ShaderID &src) {
|
||||
memcpy(d, src.d, sizeof(d));
|
||||
}
|
||||
|
||||
bool Bit(GShaderBit bit) const {
|
||||
return ShaderID::Bit((int)bit);
|
||||
}
|
||||
|
||||
int Bits(GShaderBit bit, int count) const {
|
||||
return ShaderID::Bits((int)bit, count);
|
||||
}
|
||||
|
||||
void SetBit(GShaderBit bit, bool value = true) {
|
||||
ShaderID::SetBit((int)bit, value);
|
||||
}
|
||||
|
||||
void SetBits(GShaderBit bit, int count, int value) {
|
||||
ShaderID::SetBits((int)bit, count, value);
|
||||
}
|
||||
};
|
||||
|
||||
namespace Draw {
|
||||
class Bugs;
|
||||
}
|
||||
@@ -244,3 +280,6 @@ std::string VertexShaderDesc(const VShaderID &id);
|
||||
struct ComputedPipelineState;
|
||||
void ComputeFragmentShaderID(FShaderID *id, const ComputedPipelineState &pipelineState, const Draw::Bugs &bugs);
|
||||
std::string FragmentShaderDesc(const FShaderID &id);
|
||||
|
||||
void ComputeGeometryShaderID(GShaderID *id, const Draw::Bugs &bugs, int prim);
|
||||
std::string GeometryShaderDesc(const GShaderID &id);
|
||||
@@ -1299,7 +1299,7 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag
|
||||
WRITE(p, " if (u_cullRangeMin.w <= 0.0 || projZ * outPos.w > -outPos.w) {\n");
|
||||
const char *outMin = "projPos.x < u_cullRangeMin.x || projPos.y < u_cullRangeMin.y";
|
||||
const char *outMax = "projPos.x > u_cullRangeMax.x || projPos.y > u_cullRangeMax.y";
|
||||
WRITE(p, " if (%s || %s) {\n", outMin, outMax);
|
||||
WRITE(p, " if ((%s) || (%s)) {\n", outMin, outMax);
|
||||
WRITE(p, " outPos.xyzw = u_cullRangeMax.wwww;\n");
|
||||
WRITE(p, " }\n");
|
||||
WRITE(p, " }\n");
|
||||
|
||||
@@ -340,6 +340,7 @@
|
||||
<ClInclude Include="..\ext\xbrz\xbrz.h" />
|
||||
<ClInclude Include="Common\TextureShaderCommon.h" />
|
||||
<ClInclude Include="Common\Draw2D.h" />
|
||||
<ClInclude Include="Common\GeometryShaderGenerator.h" />
|
||||
<ClInclude Include="Common\ReinterpretFramebuffer.h" />
|
||||
<ClInclude Include="Common\DepalettizeShaderCommon.h" />
|
||||
<ClInclude Include="Common\DrawEngineCommon.h" />
|
||||
@@ -455,6 +456,7 @@
|
||||
<ClCompile Include="..\ext\xbrz\xbrz.cpp" />
|
||||
<ClCompile Include="Common\TextureShaderCommon.cpp" />
|
||||
<ClCompile Include="Common\Draw2D.cpp" />
|
||||
<ClCompile Include="Common\GeometryShaderGenerator.cpp" />
|
||||
<ClCompile Include="Common\ReinterpretFramebuffer.cpp" />
|
||||
<ClCompile Include="Common\DepalettizeShaderCommon.cpp" />
|
||||
<ClCompile Include="Common\DrawEngineCommon.cpp" />
|
||||
|
||||
@@ -261,6 +261,9 @@
|
||||
<ClInclude Include="Debugger\GECommandTable.h">
|
||||
<Filter>Debugger</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\GeometryShaderGenerator.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Math3D.cpp">
|
||||
@@ -512,6 +515,9 @@
|
||||
<ClCompile Include="Debugger\GECommandTable.cpp">
|
||||
<Filter>Debugger</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Common\GeometryShaderGenerator.cpp">
|
||||
<Filter>Common</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<FxCompile Include="..\assets\shaders\tex_4xbrz.csh">
|
||||
|
||||
+10
-10
@@ -87,8 +87,8 @@ const CommonCommandTableEntry commonCommandTable[] = {
|
||||
{ GE_CMD_FOG2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FOGCOEF },
|
||||
|
||||
// These affect the fragment shader so need flushing.
|
||||
{ GE_CMD_CLEARMODE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE },
|
||||
{ GE_CMD_TEXTUREMAPENABLE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE },
|
||||
{ GE_CMD_CLEARMODE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE },
|
||||
{ GE_CMD_TEXTUREMAPENABLE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE },
|
||||
{ GE_CMD_FOGENABLE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAGMENTSHADER_STATE },
|
||||
{ GE_CMD_TEXMODE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS | DIRTY_FRAGMENTSHADER_STATE },
|
||||
{ GE_CMD_TEXSHADELS, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE },
|
||||
@@ -102,7 +102,7 @@ const CommonCommandTableEntry commonCommandTable[] = {
|
||||
|
||||
// These change the vertex shader so need flushing.
|
||||
{ GE_CMD_REVERSENORMAL, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE },
|
||||
{ GE_CMD_LIGHTINGENABLE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE },
|
||||
{ GE_CMD_LIGHTINGENABLE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE },
|
||||
{ GE_CMD_LIGHTENABLE0, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE },
|
||||
{ GE_CMD_LIGHTENABLE1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE },
|
||||
{ GE_CMD_LIGHTENABLE2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE },
|
||||
@@ -114,7 +114,7 @@ const CommonCommandTableEntry commonCommandTable[] = {
|
||||
{ GE_CMD_MATERIALUPDATE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE },
|
||||
|
||||
// These change both shaders so need flushing.
|
||||
{ GE_CMD_LIGHTMODE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE },
|
||||
{ GE_CMD_LIGHTMODE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE },
|
||||
|
||||
{ GE_CMD_TEXFILTER, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS },
|
||||
{ GE_CMD_TEXWRAP, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS | DIRTY_FRAGMENTSHADER_STATE },
|
||||
@@ -1683,7 +1683,7 @@ void GPUCommon::Execute_VertexType(u32 op, u32 diff) {
|
||||
if (diff & (GE_VTYPE_TC_MASK | GE_VTYPE_THROUGH_MASK)) {
|
||||
gstate_c.Dirty(DIRTY_UVSCALEOFFSET);
|
||||
if (diff & GE_VTYPE_THROUGH_MASK)
|
||||
gstate_c.Dirty(DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_CULLRANGE);
|
||||
gstate_c.Dirty(DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE | DIRTY_CULLRANGE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1710,7 +1710,7 @@ void GPUCommon::Execute_VertexTypeSkinning(u32 op, u32 diff) {
|
||||
gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE);
|
||||
}
|
||||
if (diff & GE_VTYPE_THROUGH_MASK)
|
||||
gstate_c.Dirty(DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_CULLRANGE);
|
||||
gstate_c.Dirty(DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE | DIRTY_CULLRANGE);
|
||||
}
|
||||
|
||||
void GPUCommon::CheckDepthUsage(VirtualFramebuffer *vfb) {
|
||||
@@ -2044,8 +2044,8 @@ void GPUCommon::Execute_Bezier(u32 op, u32 diff) {
|
||||
|
||||
SetDrawType(DRAW_BEZIER, PatchPrimToPrim(surface.primType));
|
||||
|
||||
gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE);
|
||||
if (drawEngineCommon_->CanUseHardwareTessellation(surface.primType)) {
|
||||
gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE);
|
||||
gstate_c.submitType = SubmitType::HW_BEZIER;
|
||||
if (gstate_c.spline_num_points_u != surface.num_points_u) {
|
||||
gstate_c.Dirty(DIRTY_BEZIERSPLINE);
|
||||
@@ -2059,7 +2059,7 @@ void GPUCommon::Execute_Bezier(u32 op, u32 diff) {
|
||||
UpdateUVScaleOffset();
|
||||
drawEngineCommon_->SubmitCurve(control_points, indices, surface, gstate.vertType, &bytesRead, "bezier");
|
||||
|
||||
gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE);
|
||||
gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE);
|
||||
gstate_c.submitType = SubmitType::DRAW;
|
||||
|
||||
// After drawing, we advance pointers - see SubmitPrim which does the same.
|
||||
@@ -2119,8 +2119,8 @@ void GPUCommon::Execute_Spline(u32 op, u32 diff) {
|
||||
|
||||
SetDrawType(DRAW_SPLINE, PatchPrimToPrim(surface.primType));
|
||||
|
||||
gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE);
|
||||
if (drawEngineCommon_->CanUseHardwareTessellation(surface.primType)) {
|
||||
gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE);
|
||||
gstate_c.submitType = SubmitType::HW_SPLINE;
|
||||
if (gstate_c.spline_num_points_u != surface.num_points_u) {
|
||||
gstate_c.Dirty(DIRTY_BEZIERSPLINE);
|
||||
@@ -2134,7 +2134,7 @@ void GPUCommon::Execute_Spline(u32 op, u32 diff) {
|
||||
UpdateUVScaleOffset();
|
||||
drawEngineCommon_->SubmitCurve(control_points, indices, surface, gstate.vertType, &bytesRead, "spline");
|
||||
|
||||
gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE);
|
||||
gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE);
|
||||
gstate_c.submitType = SubmitType::DRAW;
|
||||
|
||||
// After drawing, we advance pointers - see SubmitPrim which does the same.
|
||||
|
||||
+2
-2
@@ -269,14 +269,14 @@ protected:
|
||||
void SetDrawType(DrawType type, GEPrimitiveType prim) {
|
||||
if (type != lastDraw_) {
|
||||
// We always flush when drawing splines/beziers so no need to do so here
|
||||
gstate_c.Dirty(DIRTY_UVSCALEOFFSET | DIRTY_VERTEXSHADER_STATE);
|
||||
gstate_c.Dirty(DIRTY_UVSCALEOFFSET | DIRTY_VERTEXSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE);
|
||||
lastDraw_ = type;
|
||||
}
|
||||
// Prim == RECTANGLES can cause CanUseHardwareTransform to flip, so we need to dirty.
|
||||
// Also, culling may be affected so dirty the raster state.
|
||||
if (IsTrianglePrim(prim) != IsTrianglePrim(lastPrim_)) {
|
||||
Flush();
|
||||
gstate_c.Dirty(DIRTY_RASTER_STATE | DIRTY_VERTEXSHADER_STATE);
|
||||
gstate_c.Dirty(DIRTY_RASTER_STATE | DIRTY_VERTEXSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE);
|
||||
lastPrim_ = prim;
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -486,7 +486,8 @@ enum {
|
||||
// Free bit: 15
|
||||
GPU_SUPPORTS_DEPTH_TEXTURE = FLAG_BIT(16),
|
||||
GPU_SUPPORTS_ACCURATE_DEPTH = FLAG_BIT(17),
|
||||
// Free bits: 18-19
|
||||
GPU_SUPPORTS_GS_CULLING = FLAG_BIT(18), // Geometry shader
|
||||
// Free bit: 19
|
||||
GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH = FLAG_BIT(20),
|
||||
GPU_SCALE_DEPTH_FROM_24BIT_TO_16BIT = FLAG_BIT(21),
|
||||
GPU_ROUND_FRAGMENT_DEPTH_TO_16BIT = FLAG_BIT(22),
|
||||
|
||||
@@ -89,8 +89,6 @@ DrawEngineVulkan::DrawEngineVulkan(Draw::DrawContext *draw)
|
||||
decIndex = (u16 *)AllocateMemoryPages(DECODED_INDEX_BUFFER_SIZE, MEM_PROT_READ | MEM_PROT_WRITE);
|
||||
|
||||
indexGen.Setup(decIndex);
|
||||
|
||||
InitDeviceObjects();
|
||||
}
|
||||
|
||||
void DrawEngineVulkan::InitDeviceObjects() {
|
||||
@@ -114,6 +112,8 @@ void DrawEngineVulkan::InitDeviceObjects() {
|
||||
bindings[3].descriptorCount = 1;
|
||||
bindings[3].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
|
||||
bindings[3].stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
if (gstate_c.Supports(GPU_SUPPORTS_GS_CULLING))
|
||||
bindings[3].stageFlags |= VK_SHADER_STAGE_GEOMETRY_BIT;
|
||||
bindings[3].binding = DRAW_BINDING_DYNUBO_BASE;
|
||||
bindings[4].descriptorCount = 1;
|
||||
bindings[4].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
|
||||
@@ -581,6 +581,7 @@ void DrawEngineVulkan::DoFlush() {
|
||||
|
||||
VulkanVertexShader *vshader = nullptr;
|
||||
VulkanFragmentShader *fshader = nullptr;
|
||||
VulkanGeometryShader *gshader = nullptr;
|
||||
|
||||
uint32_t ibOffset;
|
||||
uint32_t vbOffset;
|
||||
@@ -770,19 +771,19 @@ void DrawEngineVulkan::DoFlush() {
|
||||
sampler = nullSampler_;
|
||||
}
|
||||
|
||||
if (!lastPipeline_ || gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE) || prim != lastPrim_) {
|
||||
if (!lastPipeline_ || gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE) || prim != lastPrim_) {
|
||||
if (prim != lastPrim_ || gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE)) {
|
||||
ConvertStateToVulkanKey(*framebufferManager_, shaderManager_, prim, pipelineKey_, dynState_);
|
||||
}
|
||||
|
||||
shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, pipelineState_, true, useHWTessellation_, decOptions_.expandAllWeightsToFloat); // usehwtransform
|
||||
shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, &gshader, pipelineState_, true, useHWTessellation_, decOptions_.expandAllWeightsToFloat); // usehwtransform
|
||||
if (!vshader) {
|
||||
// We're screwed.
|
||||
return;
|
||||
}
|
||||
_dbg_assert_msg_(vshader->UseHWTransform(), "Bad vshader");
|
||||
|
||||
VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(renderManager, pipelineLayout_, pipelineKey_, &dec_->decFmt, vshader, fshader, true, 0);
|
||||
VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(renderManager, pipelineLayout_, pipelineKey_, &dec_->decFmt, vshader, fshader, gshader, true, 0);
|
||||
if (!pipeline || !pipeline->pipeline) {
|
||||
// Already logged, let's bail out.
|
||||
return;
|
||||
@@ -901,13 +902,13 @@ void DrawEngineVulkan::DoFlush() {
|
||||
if (sampler == VK_NULL_HANDLE)
|
||||
sampler = nullSampler_;
|
||||
}
|
||||
if (!lastPipeline_ || gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE) || prim != lastPrim_) {
|
||||
if (!lastPipeline_ || gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE) || prim != lastPrim_) {
|
||||
if (prim != lastPrim_ || gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE)) {
|
||||
ConvertStateToVulkanKey(*framebufferManager_, shaderManager_, prim, pipelineKey_, dynState_);
|
||||
}
|
||||
shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, pipelineState_, false, false, decOptions_.expandAllWeightsToFloat); // usehwtransform
|
||||
shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, &gshader, pipelineState_, false, false, decOptions_.expandAllWeightsToFloat); // usehwtransform
|
||||
_dbg_assert_msg_(!vshader->UseHWTransform(), "Bad vshader");
|
||||
VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(renderManager, pipelineLayout_, pipelineKey_, &dec_->decFmt, vshader, fshader, false, 0);
|
||||
VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(renderManager, pipelineLayout_, pipelineKey_, &dec_->decFmt, vshader, fshader, gshader, false, 0);
|
||||
if (!pipeline || !pipeline->pipeline) {
|
||||
// Already logged, let's bail out.
|
||||
decodedVerts_ = 0;
|
||||
|
||||
@@ -127,6 +127,9 @@ public:
|
||||
DrawEngineVulkan(Draw::DrawContext *draw);
|
||||
virtual ~DrawEngineVulkan();
|
||||
|
||||
// We reference feature flags, so this is called after construction.
|
||||
void InitDeviceObjects();
|
||||
|
||||
void SetShaderManager(ShaderManagerVulkan *shaderManager) {
|
||||
shaderManager_ = shaderManager;
|
||||
}
|
||||
@@ -196,7 +199,6 @@ private:
|
||||
void ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManager, ShaderManagerVulkan *shaderManager, int prim, VulkanPipelineRasterStateKey &key, VulkanDynamicState &dynState);
|
||||
void BindShaderBlendTex();
|
||||
|
||||
void InitDeviceObjects();
|
||||
void DestroyDeviceObjects();
|
||||
|
||||
void DecodeVertsToPushBuffer(VulkanPushBuffer *push, uint32_t *bindOffset, VkBuffer *vkbuf);
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
GPU_Vulkan::GPU_Vulkan(GraphicsContext *gfxCtx, Draw::DrawContext *draw)
|
||||
: GPUCommon(gfxCtx, draw), drawEngine_(draw) {
|
||||
gstate_c.featureFlags = CheckGPUFeatures();
|
||||
drawEngine_.InitDeviceObjects();
|
||||
|
||||
VulkanContext *vulkan = (VulkanContext *)gfxCtx->GetAPIContext();
|
||||
|
||||
@@ -231,6 +232,17 @@ u32 GPU_Vulkan::CheckGPUFeatures() const {
|
||||
features |= GPU_SUPPORTS_DEPTH_CLAMP;
|
||||
}
|
||||
|
||||
// Fall back to geometry shader culling if we can't do vertex range culling.
|
||||
if (enabledFeatures.geometryShader) {
|
||||
const bool useGeometry = g_Config.bUseGeometryShader && !draw_->GetBugs().Has(Draw::Bugs::GEOMETRY_SHADERS_SLOW);
|
||||
const bool vertexSupported = draw_->GetDeviceCaps().clipDistanceSupported && draw_->GetDeviceCaps().cullDistanceSupported;
|
||||
if (useGeometry && (!vertexSupported || (features & GPU_SUPPORTS_VS_RANGE_CULLING) == 0)) {
|
||||
// Switch to culling via the geometry shader if not fully supported in vertex.
|
||||
features |= GPU_SUPPORTS_GS_CULLING;
|
||||
features &= ~GPU_SUPPORTS_VS_RANGE_CULLING;
|
||||
}
|
||||
}
|
||||
|
||||
// These are VULKAN_4444_FORMAT and friends.
|
||||
uint32_t fmt4444 = draw_->GetDataFormatSupport(Draw::DataFormat::B4G4R4A4_UNORM_PACK16);
|
||||
uint32_t fmt1555 = draw_->GetDataFormatSupport(Draw::DataFormat::A1R5G5B5_UNORM_PACK16);
|
||||
|
||||
@@ -171,7 +171,7 @@ static std::string CutFromMain(std::string str) {
|
||||
|
||||
static VulkanPipeline *CreateVulkanPipeline(VulkanRenderManager *renderManager, VkPipelineCache pipelineCache,
|
||||
VkPipelineLayout layout, PipelineFlags pipelineFlags, const VulkanPipelineRasterStateKey &key,
|
||||
const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform, u32 variantBitmask) {
|
||||
const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, VulkanGeometryShader *gs, bool useHwTransform, u32 variantBitmask) {
|
||||
VulkanPipeline *vulkanPipeline = new VulkanPipeline();
|
||||
VKRGraphicsPipelineDesc *desc = &vulkanPipeline->desc;
|
||||
desc->pipelineCache = pipelineCache;
|
||||
@@ -254,6 +254,7 @@ static VulkanPipeline *CreateVulkanPipeline(VulkanRenderManager *renderManager,
|
||||
|
||||
desc->fragmentShader = fs->GetModule();
|
||||
desc->vertexShader = vs->GetModule();
|
||||
desc->geometryShader = gs ? gs->GetModule() : nullptr;
|
||||
|
||||
VkPipelineInputAssemblyStateCreateInfo &inputAssembly = desc->inputAssembly;
|
||||
inputAssembly.flags = 0;
|
||||
@@ -301,6 +302,9 @@ static VulkanPipeline *CreateVulkanPipeline(VulkanRenderManager *renderManager,
|
||||
if (useBlendConstant) {
|
||||
pipelineFlags |= PipelineFlags::USES_BLEND_CONSTANT;
|
||||
}
|
||||
if (gs) {
|
||||
pipelineFlags |= PipelineFlags::USES_GEOMETRY_SHADER;
|
||||
}
|
||||
if (dss.depthTestEnable || dss.stencilTestEnable) {
|
||||
pipelineFlags |= PipelineFlags::USES_DEPTH_STENCIL;
|
||||
}
|
||||
@@ -308,7 +312,7 @@ static VulkanPipeline *CreateVulkanPipeline(VulkanRenderManager *renderManager,
|
||||
return vulkanPipeline;
|
||||
}
|
||||
|
||||
VulkanPipeline *PipelineManagerVulkan::GetOrCreatePipeline(VulkanRenderManager *renderManager, VkPipelineLayout layout, const VulkanPipelineRasterStateKey &rasterKey, const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform, u32 variantBitmask) {
|
||||
VulkanPipeline *PipelineManagerVulkan::GetOrCreatePipeline(VulkanRenderManager *renderManager, VkPipelineLayout layout, const VulkanPipelineRasterStateKey &rasterKey, const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, VulkanGeometryShader *gs, bool useHwTransform, u32 variantBitmask) {
|
||||
if (!pipelineCache_) {
|
||||
VkPipelineCacheCreateInfo pc{ VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO };
|
||||
VkResult res = vkCreatePipelineCache(vulkan_->GetDevice(), &pc, nullptr, &pipelineCache_);
|
||||
@@ -321,6 +325,7 @@ VulkanPipeline *PipelineManagerVulkan::GetOrCreatePipeline(VulkanRenderManager *
|
||||
key.useHWTransform = useHwTransform;
|
||||
key.vShader = vs->GetModule();
|
||||
key.fShader = fs->GetModule();
|
||||
key.gShader = gs ? gs->GetModule() : VK_NULL_HANDLE;
|
||||
key.vtxFmtId = useHwTransform ? decFmt->id : 0;
|
||||
|
||||
auto iter = pipelines_.Get(key);
|
||||
@@ -334,7 +339,7 @@ VulkanPipeline *PipelineManagerVulkan::GetOrCreatePipeline(VulkanRenderManager *
|
||||
|
||||
VulkanPipeline *pipeline = CreateVulkanPipeline(
|
||||
renderManager, pipelineCache_, layout, pipelineFlags,
|
||||
rasterKey, decFmt, vs, fs, useHwTransform, variantBitmask);
|
||||
rasterKey, decFmt, vs, fs, gs, useHwTransform, variantBitmask);
|
||||
pipelines_.Insert(key, pipeline);
|
||||
|
||||
// Don't return placeholder null pipelines.
|
||||
@@ -539,6 +544,7 @@ struct StoredVulkanPipelineKey {
|
||||
VulkanPipelineRasterStateKey raster;
|
||||
VShaderID vShaderID;
|
||||
FShaderID fShaderID;
|
||||
GShaderID gShaderID;
|
||||
uint32_t vtxFmtId;
|
||||
uint32_t variants;
|
||||
bool useHWTransform; // TODO: Still needed?
|
||||
@@ -589,7 +595,13 @@ void PipelineManagerVulkan::SaveCache(FILE *file, bool saveRawPipelineCache, Sha
|
||||
return;
|
||||
VulkanVertexShader *vshader = shaderManager->GetVertexShaderFromModule(pkey.vShader->BlockUntilReady());
|
||||
VulkanFragmentShader *fshader = shaderManager->GetFragmentShaderFromModule(pkey.fShader->BlockUntilReady());
|
||||
if (!vshader || !fshader) {
|
||||
VulkanGeometryShader *gshader = nullptr;
|
||||
if (pkey.gShader) {
|
||||
gshader = shaderManager->GetGeometryShaderFromModule(pkey.gShader->BlockUntilReady());
|
||||
if (!gshader)
|
||||
failed = true;
|
||||
}
|
||||
if (!vshader || !fshader || failed) {
|
||||
failed = true;
|
||||
return;
|
||||
}
|
||||
@@ -598,6 +610,7 @@ void PipelineManagerVulkan::SaveCache(FILE *file, bool saveRawPipelineCache, Sha
|
||||
key.useHWTransform = pkey.useHWTransform;
|
||||
key.fShaderID = fshader->GetID();
|
||||
key.vShaderID = vshader->GetID();
|
||||
key.gShaderID = gshader ? gshader->GetID() : GShaderID();
|
||||
key.variants = value->GetVariantsBitmask();
|
||||
if (key.useHWTransform) {
|
||||
// NOTE: This is not a vtype, but a decoded vertex format.
|
||||
@@ -702,7 +715,8 @@ bool PipelineManagerVulkan::LoadCache(FILE *file, bool loadRawPipelineCache, Sha
|
||||
}
|
||||
VulkanVertexShader *vs = shaderManager->GetVertexShaderFromID(key.vShaderID);
|
||||
VulkanFragmentShader *fs = shaderManager->GetFragmentShaderFromID(key.fShaderID);
|
||||
if (!vs || !fs) {
|
||||
VulkanGeometryShader *gs = shaderManager->GetGeometryShaderFromID(key.gShaderID);
|
||||
if (!vs || !fs || (!gs && key.gShaderID.Bit(GS_BIT_ENABLED))) {
|
||||
failed = true;
|
||||
ERROR_LOG(G3D, "Failed to find vs or fs in of pipeline %d in cache", (int)i);
|
||||
continue;
|
||||
@@ -710,7 +724,7 @@ bool PipelineManagerVulkan::LoadCache(FILE *file, bool loadRawPipelineCache, Sha
|
||||
|
||||
DecVtxFormat fmt;
|
||||
fmt.InitializeFromID(key.vtxFmtId);
|
||||
VulkanPipeline *pipeline = GetOrCreatePipeline(rm, layout, key.raster, key.useHWTransform ? &fmt : 0, vs, fs, key.useHWTransform, key.variants);
|
||||
VulkanPipeline *pipeline = GetOrCreatePipeline(rm, layout, key.raster, key.useHWTransform ? &fmt : 0, vs, fs, gs, key.useHWTransform, key.variants);
|
||||
if (!pipeline) {
|
||||
pipelineCreateFailCount += 1;
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ struct VulkanPipelineKey {
|
||||
VKRRenderPass *renderPass;
|
||||
Promise<VkShaderModule> *vShader;
|
||||
Promise<VkShaderModule> *fShader;
|
||||
Promise<VkShaderModule> *gShader;
|
||||
uint32_t vtxFmtId;
|
||||
bool useHWTransform;
|
||||
|
||||
@@ -68,6 +69,7 @@ struct VulkanPipeline {
|
||||
class VulkanContext;
|
||||
class VulkanVertexShader;
|
||||
class VulkanFragmentShader;
|
||||
class VulkanGeometryShader;
|
||||
class ShaderManagerVulkan;
|
||||
class DrawEngineCommon;
|
||||
|
||||
@@ -77,7 +79,7 @@ public:
|
||||
~PipelineManagerVulkan();
|
||||
|
||||
// variantMask is only used when loading pipelines from cache.
|
||||
VulkanPipeline *GetOrCreatePipeline(VulkanRenderManager *renderManager, VkPipelineLayout layout, const VulkanPipelineRasterStateKey &rasterKey, const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform, u32 variantMask);
|
||||
VulkanPipeline *GetOrCreatePipeline(VulkanRenderManager *renderManager, VkPipelineLayout layout, const VulkanPipelineRasterStateKey &rasterKey, const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, VulkanGeometryShader *gs, bool useHwTransform, u32 variantMask);
|
||||
int GetNumPipelines() const { return (int)pipelines_.size(); }
|
||||
|
||||
void Clear();
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#include "GPU/ge_constants.h"
|
||||
#include "GPU/Common/FragmentShaderGenerator.h"
|
||||
#include "GPU/Common/VertexShaderGenerator.h"
|
||||
#include "GPU/Common/GeometryShaderGenerator.h"
|
||||
#include "GPU/Vulkan/ShaderManagerVulkan.h"
|
||||
#include "GPU/Vulkan/DrawEngineVulkan.h"
|
||||
#include "GPU/Vulkan/FramebufferManagerVulkan.h"
|
||||
@@ -73,14 +74,22 @@ static Promise<VkShaderModule> *CompileShaderModuleAsync(VulkanContext *vulkan,
|
||||
|
||||
VkShaderModule shaderModule = VK_NULL_HANDLE;
|
||||
if (success) {
|
||||
success = vulkan->CreateShaderModule(spirv, &shaderModule, stage == VK_SHADER_STAGE_VERTEX_BIT ? "game_vertex" : "game_fragment");
|
||||
const char *createTag = tag ? tag->c_str() : nullptr;
|
||||
if (!createTag) {
|
||||
switch (stage) {
|
||||
case VK_SHADER_STAGE_VERTEX_BIT: createTag = "game_vertex"; break;
|
||||
case VK_SHADER_STAGE_FRAGMENT_BIT: createTag = "game_fragment"; break;
|
||||
case VK_SHADER_STAGE_GEOMETRY_BIT: createTag = "game_geometry"; break;
|
||||
case VK_SHADER_STAGE_COMPUTE_BIT: createTag = "game_compute"; break;
|
||||
}
|
||||
}
|
||||
|
||||
success = vulkan->CreateShaderModule(spirv, &shaderModule, createTag);
|
||||
#ifdef SHADERLOG
|
||||
OutputDebugStringA("OK");
|
||||
#endif
|
||||
if (tag) {
|
||||
vulkan->SetDebugName(shaderModule, VK_OBJECT_TYPE_SHADER_MODULE, tag->c_str());
|
||||
if (tag)
|
||||
delete tag;
|
||||
}
|
||||
}
|
||||
|
||||
return shaderModule;
|
||||
@@ -128,7 +137,7 @@ std::string VulkanFragmentShader::GetShaderString(DebugShaderStringType type) co
|
||||
VulkanVertexShader::VulkanVertexShader(VulkanContext *vulkan, VShaderID id, const char *code, bool useHWTransform)
|
||||
: vulkan_(vulkan), useHWTransform_(useHWTransform), id_(id) {
|
||||
source_ = code;
|
||||
module_ = CompileShaderModuleAsync(vulkan, VK_SHADER_STAGE_VERTEX_BIT, source_.c_str(), new std::string(VertexShaderDesc(id).c_str()));
|
||||
module_ = CompileShaderModuleAsync(vulkan, VK_SHADER_STAGE_VERTEX_BIT, source_.c_str(), new std::string(VertexShaderDesc(id)));
|
||||
if (!module_) {
|
||||
failed_ = true;
|
||||
} else {
|
||||
@@ -155,8 +164,38 @@ std::string VulkanVertexShader::GetShaderString(DebugShaderStringType type) cons
|
||||
}
|
||||
}
|
||||
|
||||
VulkanGeometryShader::VulkanGeometryShader(VulkanContext *vulkan, GShaderID id, const char *code)
|
||||
: vulkan_(vulkan), id_(id) {
|
||||
source_ = code;
|
||||
module_ = CompileShaderModuleAsync(vulkan, VK_SHADER_STAGE_GEOMETRY_BIT, source_.c_str(), new std::string(GeometryShaderDesc(id).c_str()));
|
||||
if (!module_) {
|
||||
failed_ = true;
|
||||
} else {
|
||||
VERBOSE_LOG(G3D, "Compiled geometry shader:\n%s\n", (const char *)code);
|
||||
}
|
||||
}
|
||||
|
||||
VulkanGeometryShader::~VulkanGeometryShader() {
|
||||
if (module_) {
|
||||
VkShaderModule shaderModule = module_->BlockUntilReady();
|
||||
vulkan_->Delete().QueueDeleteShaderModule(shaderModule);
|
||||
delete module_;
|
||||
}
|
||||
}
|
||||
|
||||
std::string VulkanGeometryShader::GetShaderString(DebugShaderStringType type) const {
|
||||
switch (type) {
|
||||
case SHADER_STRING_SOURCE_CODE:
|
||||
return source_;
|
||||
case SHADER_STRING_SHORT_DESC:
|
||||
return GeometryShaderDesc(id_);
|
||||
default:
|
||||
return "N/A";
|
||||
}
|
||||
}
|
||||
|
||||
ShaderManagerVulkan::ShaderManagerVulkan(Draw::DrawContext *draw)
|
||||
: ShaderManagerCommon(draw), compat_(GLSL_VULKAN), fsCache_(16), vsCache_(16) {
|
||||
: ShaderManagerCommon(draw), compat_(GLSL_VULKAN), fsCache_(16), vsCache_(16), gsCache_(16) {
|
||||
codeBuffer_ = new char[16384];
|
||||
VulkanContext *vulkan = (VulkanContext *)draw->GetNativeObject(Draw::NativeObject::CONTEXT);
|
||||
uboAlignment_ = vulkan->GetPhysicalDeviceProperties().properties.limits.minUniformBufferOffsetAlignment;
|
||||
@@ -191,30 +230,37 @@ void ShaderManagerVulkan::Clear() {
|
||||
vsCache_.Iterate([&](const VShaderID &key, VulkanVertexShader *shader) {
|
||||
delete shader;
|
||||
});
|
||||
gsCache_.Iterate([&](const GShaderID &key, VulkanGeometryShader *shader) {
|
||||
delete shader;
|
||||
});
|
||||
fsCache_.Clear();
|
||||
vsCache_.Clear();
|
||||
gsCache_.Clear();
|
||||
lastFSID_.set_invalid();
|
||||
lastVSID_.set_invalid();
|
||||
gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE);
|
||||
lastGSID_.set_invalid();
|
||||
gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE);
|
||||
}
|
||||
|
||||
void ShaderManagerVulkan::ClearShaders() {
|
||||
Clear();
|
||||
DirtyShader();
|
||||
gstate_c.Dirty(DIRTY_ALL_UNIFORMS | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE);
|
||||
gstate_c.Dirty(DIRTY_ALL_UNIFORMS | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE);
|
||||
}
|
||||
|
||||
void ShaderManagerVulkan::DirtyShader() {
|
||||
// Forget the last shader ID
|
||||
lastFSID_.set_invalid();
|
||||
lastVSID_.set_invalid();
|
||||
lastGSID_.set_invalid();
|
||||
DirtyLastShader();
|
||||
}
|
||||
|
||||
void ShaderManagerVulkan::DirtyLastShader() {
|
||||
lastVShader_ = nullptr;
|
||||
lastFShader_ = nullptr;
|
||||
gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE);
|
||||
lastGShader_ = nullptr;
|
||||
gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE);
|
||||
}
|
||||
|
||||
uint64_t ShaderManagerVulkan::UpdateUniforms(bool useBufferedRendering) {
|
||||
@@ -231,7 +277,7 @@ uint64_t ShaderManagerVulkan::UpdateUniforms(bool useBufferedRendering) {
|
||||
return dirty;
|
||||
}
|
||||
|
||||
void ShaderManagerVulkan::GetShaders(int prim, u32 vertType, VulkanVertexShader **vshader, VulkanFragmentShader **fshader, const ComputedPipelineState &pipelineState, bool useHWTransform, bool useHWTessellation, bool weightsAsFloat) {
|
||||
void ShaderManagerVulkan::GetShaders(int prim, u32 vertType, VulkanVertexShader **vshader, VulkanFragmentShader **fshader, VulkanGeometryShader **gshader, const ComputedPipelineState &pipelineState, bool useHWTransform, bool useHWTessellation, bool weightsAsFloat) {
|
||||
VShaderID VSID;
|
||||
if (gstate_c.IsDirty(DIRTY_VERTEXSHADER_STATE)) {
|
||||
gstate_c.Clean(DIRTY_VERTEXSHADER_STATE);
|
||||
@@ -248,14 +294,28 @@ void ShaderManagerVulkan::GetShaders(int prim, u32 vertType, VulkanVertexShader
|
||||
FSID = lastFSID_;
|
||||
}
|
||||
|
||||
GShaderID GSID;
|
||||
if (gstate_c.IsDirty(DIRTY_GEOMETRYSHADER_STATE)) {
|
||||
gstate_c.Clean(DIRTY_GEOMETRYSHADER_STATE);
|
||||
ComputeGeometryShaderID(&GSID, draw_->GetBugs(), prim);
|
||||
} else {
|
||||
GSID = lastGSID_;
|
||||
}
|
||||
|
||||
_dbg_assert_(FSID.Bit(FS_BIT_LMODE) == VSID.Bit(VS_BIT_LMODE));
|
||||
_dbg_assert_(FSID.Bit(FS_BIT_DO_TEXTURE) == VSID.Bit(VS_BIT_DO_TEXTURE));
|
||||
_dbg_assert_(FSID.Bit(FS_BIT_FLATSHADE) == VSID.Bit(VS_BIT_FLATSHADE));
|
||||
|
||||
if (GSID.Bit(GS_BIT_ENABLED)) {
|
||||
_dbg_assert_(GSID.Bit(GS_BIT_LMODE) == VSID.Bit(VS_BIT_LMODE));
|
||||
_dbg_assert_(GSID.Bit(GS_BIT_DO_TEXTURE) == VSID.Bit(VS_BIT_DO_TEXTURE));
|
||||
}
|
||||
|
||||
// Just update uniforms if this is the same shader as last time.
|
||||
if (lastVShader_ != nullptr && lastFShader_ != nullptr && VSID == lastVSID_ && FSID == lastFSID_) {
|
||||
if (lastVShader_ != nullptr && lastFShader_ != nullptr && VSID == lastVSID_ && FSID == lastFSID_ && GSID == lastGSID_) {
|
||||
*vshader = lastVShader_;
|
||||
*fshader = lastFShader_;
|
||||
*gshader = lastGShader_;
|
||||
_dbg_assert_msg_((*vshader)->UseHWTransform() == useHWTransform, "Bad vshader was cached");
|
||||
// Already all set, no need to look up in shader maps.
|
||||
return;
|
||||
@@ -273,11 +333,9 @@ void ShaderManagerVulkan::GetShaders(int prim, u32 vertType, VulkanVertexShader
|
||||
vs = new VulkanVertexShader(vulkan, VSID, codeBuffer_, useHWTransform);
|
||||
vsCache_.Insert(VSID, vs);
|
||||
}
|
||||
lastVSID_ = VSID;
|
||||
|
||||
VulkanFragmentShader *fs = fsCache_.Get(FSID);
|
||||
if (!fs) {
|
||||
// uint32_t vendorID = vulkan->GetPhysicalDeviceProperties().properties.vendorID;
|
||||
// Fragment shader not in cache. Let's compile it.
|
||||
std::string genErrorString;
|
||||
uint64_t uniformMask = 0; // Not used
|
||||
@@ -288,13 +346,32 @@ void ShaderManagerVulkan::GetShaders(int prim, u32 vertType, VulkanVertexShader
|
||||
fsCache_.Insert(FSID, fs);
|
||||
}
|
||||
|
||||
VulkanGeometryShader *gs;
|
||||
if (GSID.Bit(GS_BIT_ENABLED)) {
|
||||
gs = gsCache_.Get(GSID);
|
||||
if (!gs) {
|
||||
// Geometry shader not in cache. Let's compile it.
|
||||
std::string genErrorString;
|
||||
bool success = GenerateGeometryShader(GSID, codeBuffer_, compat_, draw_->GetBugs(), &genErrorString);
|
||||
_assert_msg_(success, "GS gen error: %s", genErrorString.c_str());
|
||||
gs = new VulkanGeometryShader(vulkan, GSID, codeBuffer_);
|
||||
gsCache_.Insert(GSID, gs);
|
||||
}
|
||||
} else {
|
||||
gs = nullptr;
|
||||
}
|
||||
|
||||
lastVSID_ = VSID;
|
||||
lastFSID_ = FSID;
|
||||
lastGSID_ = GSID;
|
||||
|
||||
lastVShader_ = vs;
|
||||
lastFShader_ = fs;
|
||||
lastGShader_ = gs;
|
||||
|
||||
*vshader = vs;
|
||||
*fshader = fs;
|
||||
*gshader = gs;
|
||||
_dbg_assert_msg_((*vshader)->UseHWTransform() == useHWTransform, "Bad vshader was computed");
|
||||
}
|
||||
|
||||
@@ -319,6 +396,15 @@ std::vector<std::string> ShaderManagerVulkan::DebugGetShaderIDs(DebugShaderType
|
||||
});
|
||||
break;
|
||||
}
|
||||
case SHADER_TYPE_GEOMETRY:
|
||||
{
|
||||
gsCache_.Iterate([&](const GShaderID &id, VulkanGeometryShader *shader) {
|
||||
std::string idstr;
|
||||
id.ToString(&idstr);
|
||||
ids.push_back(idstr);
|
||||
});
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -334,12 +420,16 @@ std::string ShaderManagerVulkan::DebugGetShaderString(std::string id, DebugShade
|
||||
VulkanVertexShader *vs = vsCache_.Get(VShaderID(shaderId));
|
||||
return vs ? vs->GetShaderString(stringType) : "";
|
||||
}
|
||||
|
||||
case SHADER_TYPE_FRAGMENT:
|
||||
{
|
||||
VulkanFragmentShader *fs = fsCache_.Get(FShaderID(shaderId));
|
||||
return fs ? fs->GetShaderString(stringType) : "";
|
||||
}
|
||||
case SHADER_TYPE_GEOMETRY:
|
||||
{
|
||||
VulkanGeometryShader *gs = gsCache_.Get(GShaderID(shaderId));
|
||||
return gs ? gs->GetShaderString(stringType) : "";
|
||||
}
|
||||
default:
|
||||
return "N/A";
|
||||
}
|
||||
@@ -367,6 +457,17 @@ VulkanFragmentShader *ShaderManagerVulkan::GetFragmentShaderFromModule(VkShaderM
|
||||
return fs;
|
||||
}
|
||||
|
||||
VulkanGeometryShader *ShaderManagerVulkan::GetGeometryShaderFromModule(VkShaderModule module) {
|
||||
VulkanGeometryShader *gs = nullptr;
|
||||
gsCache_.Iterate([&](const GShaderID &id, VulkanGeometryShader *shader) {
|
||||
Promise<VkShaderModule> *p = shader->GetModule();
|
||||
VkShaderModule m = p->BlockUntilReady();
|
||||
if (m == module)
|
||||
gs = shader;
|
||||
});
|
||||
return gs;
|
||||
}
|
||||
|
||||
// Shader cache.
|
||||
//
|
||||
// We simply store the IDs of the shaders used during gameplay. On next startup of
|
||||
@@ -376,7 +477,7 @@ VulkanFragmentShader *ShaderManagerVulkan::GetFragmentShaderFromModule(VkShaderM
|
||||
// instantaneous.
|
||||
|
||||
#define CACHE_HEADER_MAGIC 0xff51f420
|
||||
#define CACHE_VERSION 28
|
||||
#define CACHE_VERSION 29
|
||||
struct VulkanCacheHeader {
|
||||
uint32_t magic;
|
||||
uint32_t version;
|
||||
@@ -384,6 +485,7 @@ struct VulkanCacheHeader {
|
||||
uint32_t reserved;
|
||||
int numVertexShaders;
|
||||
int numFragmentShaders;
|
||||
int numGeometryShaders;
|
||||
};
|
||||
|
||||
bool ShaderManagerVulkan::LoadCache(FILE *f) {
|
||||
@@ -431,6 +533,20 @@ bool ShaderManagerVulkan::LoadCache(FILE *f) {
|
||||
fsCache_.Insert(id, fs);
|
||||
}
|
||||
|
||||
for (int i = 0; i < header.numGeometryShaders; i++) {
|
||||
GShaderID id;
|
||||
if (fread(&id, sizeof(id), 1, f) != 1) {
|
||||
ERROR_LOG(G3D, "Vulkan shader cache truncated");
|
||||
break;
|
||||
}
|
||||
std::string genErrorString;
|
||||
if (!GenerateGeometryShader(id, codeBuffer_, compat_, draw_->GetBugs(), &genErrorString)) {
|
||||
return false;
|
||||
}
|
||||
VulkanGeometryShader *gs = new VulkanGeometryShader(vulkan, id, codeBuffer_);
|
||||
gsCache_.Insert(id, gs);
|
||||
}
|
||||
|
||||
NOTICE_LOG(G3D, "Loaded %d vertex and %d fragment shaders", header.numVertexShaders, header.numFragmentShaders);
|
||||
return true;
|
||||
}
|
||||
@@ -443,6 +559,7 @@ void ShaderManagerVulkan::SaveCache(FILE *f) {
|
||||
header.reserved = 0;
|
||||
header.numVertexShaders = (int)vsCache_.size();
|
||||
header.numFragmentShaders = (int)fsCache_.size();
|
||||
header.numGeometryShaders = (int)gsCache_.size();
|
||||
bool writeFailed = fwrite(&header, sizeof(header), 1, f) != 1;
|
||||
vsCache_.Iterate([&](const VShaderID &id, VulkanVertexShader *vs) {
|
||||
writeFailed = writeFailed || fwrite(&id, sizeof(id), 1, f) != 1;
|
||||
@@ -450,6 +567,9 @@ void ShaderManagerVulkan::SaveCache(FILE *f) {
|
||||
fsCache_.Iterate([&](const FShaderID &id, VulkanFragmentShader *fs) {
|
||||
writeFailed = writeFailed || fwrite(&id, sizeof(id), 1, f) != 1;
|
||||
});
|
||||
gsCache_.Iterate([&](const GShaderID &id, VulkanGeometryShader *gs) {
|
||||
writeFailed = writeFailed || fwrite(&id, sizeof(id), 1, f) != 1;
|
||||
});
|
||||
if (writeFailed) {
|
||||
ERROR_LOG(G3D, "Failed to write Vulkan shader cache, disk full?");
|
||||
} else {
|
||||
|
||||
@@ -83,7 +83,27 @@ protected:
|
||||
VShaderID id_;
|
||||
};
|
||||
|
||||
class VulkanPushBuffer;
|
||||
class VulkanGeometryShader {
|
||||
public:
|
||||
VulkanGeometryShader(VulkanContext *vulkan, GShaderID id, const char *code);
|
||||
~VulkanGeometryShader();
|
||||
|
||||
const std::string &source() const { return source_; }
|
||||
|
||||
bool Failed() const { return failed_; }
|
||||
|
||||
std::string GetShaderString(DebugShaderStringType type) const;
|
||||
Promise<VkShaderModule> *GetModule() const { return module_; }
|
||||
const GShaderID &GetID() { return id_; }
|
||||
|
||||
protected:
|
||||
Promise<VkShaderModule> *module_ = nullptr;
|
||||
|
||||
VulkanContext *vulkan_;
|
||||
std::string source_;
|
||||
bool failed_ = false;
|
||||
GShaderID id_;
|
||||
};
|
||||
|
||||
class ShaderManagerVulkan : public ShaderManagerCommon {
|
||||
public:
|
||||
@@ -93,19 +113,22 @@ public:
|
||||
void DeviceLost();
|
||||
void DeviceRestore(Draw::DrawContext *draw);
|
||||
|
||||
void GetShaders(int prim, u32 vertType, VulkanVertexShader **vshader, VulkanFragmentShader **fshader, const ComputedPipelineState &pipelineState, bool useHWTransform, bool useHWTessellation, bool weightsAsFloat);
|
||||
void GetShaders(int prim, u32 vertType, VulkanVertexShader **vshader, VulkanFragmentShader **fshader, VulkanGeometryShader **gshader, const ComputedPipelineState &pipelineState, bool useHWTransform, bool useHWTessellation, bool weightsAsFloat);
|
||||
void ClearShaders();
|
||||
void DirtyShader();
|
||||
void DirtyLastShader() override;
|
||||
|
||||
int GetNumVertexShaders() const { return (int)vsCache_.size(); }
|
||||
int GetNumFragmentShaders() const { return (int)fsCache_.size(); }
|
||||
int GetNumGeometryShaders() const { return (int)gsCache_.size(); }
|
||||
|
||||
// Used for saving/loading the cache. Don't need to be particularly fast.
|
||||
VulkanVertexShader *GetVertexShaderFromID(VShaderID id) { return vsCache_.Get(id); }
|
||||
VulkanFragmentShader *GetFragmentShaderFromID(FShaderID id) { return fsCache_.Get(id); }
|
||||
VulkanGeometryShader *GetGeometryShaderFromID(GShaderID id) { return gsCache_.Get(id); }
|
||||
VulkanVertexShader *GetVertexShaderFromModule(VkShaderModule module);
|
||||
VulkanFragmentShader *GetFragmentShaderFromModule(VkShaderModule module);
|
||||
VulkanGeometryShader *GetGeometryShaderFromModule(VkShaderModule module);
|
||||
|
||||
std::vector<std::string> DebugGetShaderIDs(DebugShaderType type);
|
||||
std::string DebugGetShaderString(std::string id, DebugShaderType type, DebugShaderStringType stringType);
|
||||
@@ -143,6 +166,9 @@ private:
|
||||
typedef DenseHashMap<VShaderID, VulkanVertexShader *, nullptr> VSCache;
|
||||
VSCache vsCache_;
|
||||
|
||||
typedef DenseHashMap<GShaderID, VulkanGeometryShader *, nullptr> GSCache;
|
||||
GSCache gsCache_;
|
||||
|
||||
char *codeBuffer_;
|
||||
|
||||
uint64_t uboAlignment_;
|
||||
@@ -153,7 +179,9 @@ private:
|
||||
|
||||
VulkanFragmentShader *lastFShader_ = nullptr;
|
||||
VulkanVertexShader *lastVShader_ = nullptr;
|
||||
VulkanGeometryShader *lastGShader_ = nullptr;
|
||||
|
||||
FShaderID lastFSID_;
|
||||
VShaderID lastVSID_;
|
||||
GShaderID lastGSID_;
|
||||
};
|
||||
+1
-1
@@ -1156,7 +1156,7 @@ int ShaderListScreen::ListShaders(DebugShaderType shaderType, UI::LinearLayout *
|
||||
struct { DebugShaderType type; const char *name; } shaderTypes[] = {
|
||||
{ SHADER_TYPE_VERTEX, "Vertex" },
|
||||
{ SHADER_TYPE_FRAGMENT, "Fragment" },
|
||||
// { SHADER_TYPE_GEOMETRY, "Geometry" },
|
||||
{ SHADER_TYPE_GEOMETRY, "Geometry" },
|
||||
{ SHADER_TYPE_VERTEXLOADER, "VertexLoader" },
|
||||
{ SHADER_TYPE_PIPELINE, "Pipeline" },
|
||||
{ SHADER_TYPE_TEXTURE, "Texture" },
|
||||
|
||||
@@ -473,6 +473,15 @@ void GameSettingsScreen::CreateViews() {
|
||||
inflightChoice->OnChoice.Handle(this, &GameSettingsScreen::OnInflightFramesChoice);
|
||||
}
|
||||
|
||||
if (GetGPUBackend() == GPUBackend::VULKAN) {
|
||||
const bool usable = !draw->GetBugs().Has(Draw::Bugs::GEOMETRY_SHADERS_SLOW);
|
||||
const bool vertexSupported = draw->GetDeviceCaps().clipDistanceSupported && draw->GetDeviceCaps().cullDistanceSupported;
|
||||
if (usable && !vertexSupported) {
|
||||
CheckBox *geometryCulling = graphicsSettings->Add(new CheckBox(&g_Config.bUseGeometryShader, gr->T("Geometry shader culling")));
|
||||
geometryCulling->SetDisabledPtr(&g_Config.bSoftwareRendering);
|
||||
}
|
||||
}
|
||||
|
||||
if (deviceType != DEVICE_TYPE_VR) {
|
||||
CheckBox *hwTransform = graphicsSettings->Add(new CheckBox(&g_Config.bHardwareTransform, gr->T("Hardware Transform")));
|
||||
hwTransform->SetDisabledPtr(&g_Config.bSoftwareRendering);
|
||||
|
||||
@@ -385,6 +385,7 @@
|
||||
<ClInclude Include="..\..\GPU\Common\DrawEngineCommon.h" />
|
||||
<ClInclude Include="..\..\GPU\Common\FragmentShaderGenerator.h" />
|
||||
<ClInclude Include="..\..\GPU\Common\FramebufferManagerCommon.h" />
|
||||
<ClInclude Include="..\..\GPU\Common\GeometryShaderGenerator.h" />
|
||||
<ClInclude Include="..\..\GPU\Common\PresentationCommon.h" />
|
||||
<ClInclude Include="..\..\GPU\Common\GPUDebugInterface.h" />
|
||||
<ClInclude Include="..\..\GPU\Common\GPUStateUtils.h" />
|
||||
@@ -446,6 +447,7 @@
|
||||
<ClCompile Include="..\..\GPU\Common\DrawEngineCommon.cpp" />
|
||||
<ClCompile Include="..\..\GPU\Common\FragmentShaderGenerator.cpp" />
|
||||
<ClCompile Include="..\..\GPU\Common\FramebufferManagerCommon.cpp" />
|
||||
<ClCompile Include="..\..\GPU\Common\GeometryShaderGenerator.cpp" />
|
||||
<ClCompile Include="..\..\GPU\Common\PresentationCommon.cpp" />
|
||||
<ClCompile Include="..\..\GPU\Common\GPUDebugInterface.cpp" />
|
||||
<ClCompile Include="..\..\GPU\Common\GPUStateUtils.cpp" />
|
||||
|
||||
@@ -55,6 +55,7 @@
|
||||
<ClCompile Include="..\..\GPU\Software\RasterizerRectangle.cpp" />
|
||||
<ClCompile Include="..\..\GPU\Software\RasterizerRegCache.cpp" />
|
||||
<ClCompile Include="..\..\GPU\Common\FragmentShaderGenerator.cpp" />
|
||||
<ClCompile Include="..\..\GPU\Common\GeometryShaderGenerator.cpp" />
|
||||
<ClCompile Include="..\..\GPU\Common\VertexShaderGenerator.cpp" />
|
||||
<ClCompile Include="..\..\GPU\Common\ReinterpretFramebuffer.cpp" />
|
||||
<ClCompile Include="..\..\GPU\Common\Draw2D.cpp" />
|
||||
@@ -116,6 +117,7 @@
|
||||
<ClInclude Include="..\..\GPU\Software\RasterizerRectangle.h" />
|
||||
<ClInclude Include="..\..\GPU\Software\RasterizerRegCache.h" />
|
||||
<ClInclude Include="..\..\GPU\Common\FragmentShaderGenerator.h" />
|
||||
<ClInclude Include="..\..\GPU\Common\GeometryShaderGenerator.h" />
|
||||
<ClInclude Include="..\..\GPU\Common\VertexShaderGenerator.h" />
|
||||
<ClInclude Include="..\..\GPU\Common\ReinterpretFramebuffer.h" />
|
||||
<ClInclude Include="..\..\GPU\Common\Draw2D.h" />
|
||||
|
||||
@@ -350,6 +350,7 @@ EXEC_AND_LIB_FILES := \
|
||||
$(SRC)/GPU/Common/PostShader.cpp \
|
||||
$(SRC)/GPU/Common/ShaderUniforms.cpp \
|
||||
$(SRC)/GPU/Common/VertexShaderGenerator.cpp \
|
||||
$(SRC)/GPU/Common/GeometryShaderGenerator.cpp \
|
||||
$(SRC)/GPU/Debugger/Breakpoints.cpp \
|
||||
$(SRC)/GPU/Debugger/Debugger.cpp \
|
||||
$(SRC)/GPU/Debugger/GECommandTable.cpp \
|
||||
|
||||
@@ -342,6 +342,7 @@ SOURCES_CXX += \
|
||||
$(GPUDIR)/Debugger/Stepping.cpp \
|
||||
$(GPUDIR)/Common/FragmentShaderGenerator.cpp \
|
||||
$(GPUDIR)/Common/VertexShaderGenerator.cpp \
|
||||
$(GPUDIR)/Common/GeometryShaderGenerator.cpp \
|
||||
$(GPUDIR)/Common/TextureCacheCommon.cpp \
|
||||
$(GPUDIR)/Common/TextureScalerCommon.cpp \
|
||||
$(GPUDIR)/Common/SoftwareTransformCommon.cpp \
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
#include "GPU/Common/FragmentShaderGenerator.h"
|
||||
#include "GPU/Common/VertexShaderGenerator.h"
|
||||
#include "GPU/Common/GeometryShaderGenerator.h"
|
||||
#include "GPU/Common/ReinterpretFramebuffer.h"
|
||||
#include "GPU/Common/StencilCommon.h"
|
||||
#include "GPU/Common/DepalettizeShaderCommon.h"
|
||||
@@ -91,6 +92,32 @@ bool GenerateVShader(VShaderID id, char *buffer, ShaderLanguage lang, Draw::Bugs
|
||||
}
|
||||
}
|
||||
|
||||
bool GenerateGShader(GShaderID id, char *buffer, ShaderLanguage lang, Draw::Bugs bugs, std::string *errorString) {
|
||||
errorString->clear();
|
||||
|
||||
switch (lang) {
|
||||
case ShaderLanguage::GLSL_VULKAN:
|
||||
{
|
||||
ShaderLanguageDesc compat(ShaderLanguage::GLSL_VULKAN);
|
||||
return GenerateGeometryShader(id, buffer, compat, bugs, errorString);
|
||||
}
|
||||
/*
|
||||
case ShaderLanguage::GLSL_3xx:
|
||||
{
|
||||
ShaderLanguageDesc compat(ShaderLanguage::GLSL_3xx);
|
||||
return GenerateGeometryShader(id, buffer, compat, bugs, errorString);
|
||||
}
|
||||
case ShaderLanguage::HLSL_D3D11:
|
||||
{
|
||||
ShaderLanguageDesc compat(ShaderLanguage::HLSL_D3D11);
|
||||
return GenerateGeometryShader(id, buffer, compat, bugs, errorString);
|
||||
}
|
||||
*/
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static VkShaderStageFlagBits StageToVulkan(ShaderStage stage) {
|
||||
switch (stage) {
|
||||
case ShaderStage::Vertex: return VK_SHADER_STAGE_VERTEX_BIT;
|
||||
@@ -369,6 +396,9 @@ bool TestVertexShaders() {
|
||||
if (!id.Bit(VS_BIT_USE_HW_TRANSFORM)) {
|
||||
id.SetBit(VS_BIT_ENABLE_BONES, 0);
|
||||
}
|
||||
if (id.Bit(VS_BIT_VERTEX_RANGE_CULLING)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool generateSuccess[numLanguages]{};
|
||||
std::string genErrorString[numLanguages];
|
||||
@@ -463,6 +493,63 @@ bool TestFragmentShaders() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool TestGeometryShaders() {
|
||||
char *buffer[numLanguages];
|
||||
|
||||
for (int i = 0; i < numLanguages; i++) {
|
||||
buffer[i] = new char[65536];
|
||||
}
|
||||
GMRng rng;
|
||||
int successes = 0;
|
||||
int count = 30;
|
||||
|
||||
Draw::Bugs bugs;
|
||||
|
||||
// Generate a bunch of random fragment shader IDs, try to generate shader source.
|
||||
// Then compile it and check that it's ok.
|
||||
for (int i = 0; i < count; i++) {
|
||||
uint32_t bottom = i << 1;
|
||||
GShaderID id;
|
||||
id.d[0] = bottom;
|
||||
id.d[1] = 0;
|
||||
|
||||
id.SetBit(GS_BIT_ENABLED, true);
|
||||
|
||||
bool generateSuccess[numLanguages]{};
|
||||
std::string genErrorString[numLanguages];
|
||||
|
||||
for (int j = 0; j < numLanguages; j++) {
|
||||
generateSuccess[j] = GenerateGShader(id, buffer[j], languages[j], bugs, &genErrorString[j]);
|
||||
if (!genErrorString[j].empty()) {
|
||||
printf("%s\n", genErrorString[j].c_str());
|
||||
}
|
||||
// We ignore the contents of the error string here, not even gonna try to compile if it errors.
|
||||
}
|
||||
|
||||
// Now that we have the strings ready for easy comparison (buffer,4 in the watch window),
|
||||
// let's try to compile them.
|
||||
for (int j = 0; j < numLanguages; j++) {
|
||||
if (generateSuccess[j]) {
|
||||
std::string errorMessage;
|
||||
if (!TestCompileShader(buffer[j], languages[j], ShaderStage::Geometry, &errorMessage)) {
|
||||
printf("Error compiling geometry shader:\n\n%s\n\n%s\n", LineNumberString(buffer[j]).c_str(), errorMessage.c_str());
|
||||
return false;
|
||||
}
|
||||
successes++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
printf("%d/%d geometry shaders generated (it's normal that it's not all, there are invalid bit combos)\n", successes, count * numLanguages);
|
||||
|
||||
for (int i = 0; i < numLanguages; i++) {
|
||||
delete[] buffer[i];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool TestShaderGenerators() {
|
||||
#if PPSSPP_PLATFORM(WINDOWS)
|
||||
LoadD3D11();
|
||||
@@ -476,6 +563,10 @@ bool TestShaderGenerators() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TestGeometryShaders()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TestReinterpretShaders()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user