Vulkan: Limit stencil workaround to Adreno 5xx.

This commit is contained in:
Unknown W. Brackets 2018-12-23 12:19:34 -08:00
parent 1f594f3fb5
commit f8ce9b08ba
9 changed files with 53 additions and 18 deletions

View file

@ -1,6 +1,7 @@
#include <string>
#include <sstream>
#include "thin3d/thin3d.h"
#include "Common/StringUtils.h"
#include "Core/Config.h"
@ -225,7 +226,7 @@ std::string FragmentShaderDesc(const ShaderID &id) {
// Here we must take all the bits of the gstate that determine what the fragment shader will
// look like, and concatenate them together into an ID.
void ComputeFragmentShaderID(ShaderID *id_out) {
void ComputeFragmentShaderID(ShaderID *id_out, const Draw::Bugs &bugs) {
ShaderID id;
if (gstate.isModeClear()) {
// We only need one clear shader, so let's ignore the rest of the bits.
@ -292,9 +293,6 @@ void ComputeFragmentShaderID(ShaderID *id_out) {
if (stencilToAlpha != REPLACE_ALPHA_NO) {
// 4 bits
id.SetBits(FS_BIT_REPLACE_ALPHA_WITH_STENCIL_TYPE, 4, ReplaceAlphaWithStencilType());
} else {
// Use those bits instead for whether stencil output is disabled.
id.SetBit(FS_BIT_REPLACE_ALPHA_WITH_STENCIL_TYPE, IsStencilTestOutputDisabled());
}
// 2 bits.
@ -312,6 +310,12 @@ void ComputeFragmentShaderID(ShaderID *id_out) {
id.SetBit(FS_BIT_FLATSHADE, doFlatShading);
id.SetBit(FS_BIT_SHADER_DEPAL, useShaderDepal);
if (g_Config.bVendorBugChecksEnabled) {
if (bugs.Has(Draw::Bugs::NO_DEPTH_CANNOT_DISCARD_STENCIL)) {
id.SetBit(FS_BIT_NO_DEPTH_CANNOT_DISCARD_STENCIL, !IsStencilTestOutputDisabled() && !gstate.isDepthWriteEnabled());
}
}
}
*id_out = id;

View file

@ -87,7 +87,8 @@ enum {
FS_BIT_FLATSHADE = 46,
FS_BIT_BGRA_TEXTURE = 47,
FS_BIT_TEST_DISCARD_TO_ZERO = 48,
// 49+ are free.
FS_BIT_NO_DEPTH_CANNOT_DISCARD_STENCIL = 49,
// 50+ are free.
};
struct ShaderID {
@ -175,11 +176,15 @@ struct FShaderID : ShaderID {
}
};
namespace Draw {
class Bugs;
}
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);
void ComputeFragmentShaderID(ShaderID *id);
void ComputeFragmentShaderID(ShaderID *id, const Draw::Bugs &bugs);
std::string FragmentShaderDesc(const ShaderID &id);

View file

@ -191,7 +191,7 @@ void ShaderManagerD3D11::GetShaders(int prim, u32 vertType, D3D11VertexShader **
if (gstate_c.IsDirty(DIRTY_FRAGMENTSHADER_STATE)) {
gstate_c.Clean(DIRTY_FRAGMENTSHADER_STATE);
ComputeFragmentShaderID(&FSID);
ComputeFragmentShaderID(&FSID, draw_->GetBugs());
} else {
FSID = lastFSID_;
}

View file

@ -556,7 +556,7 @@ VSShader *ShaderManagerDX9::ApplyShader(int prim, u32 vertType) {
FShaderID FSID;
if (gstate_c.IsDirty(DIRTY_FRAGMENTSHADER_STATE)) {
gstate_c.Clean(DIRTY_FRAGMENTSHADER_STATE);
ComputeFragmentShaderID(&FSID);
ComputeFragmentShaderID(&FSID, draw_->GetBugs());
} else {
FSID = lastFSID_;
}

View file

@ -703,7 +703,7 @@ LinkedShader *ShaderManagerGLES::ApplyFragmentShader(VShaderID VSID, Shader *vs,
FShaderID FSID;
if (gstate_c.IsDirty(DIRTY_FRAGMENTSHADER_STATE)) {
gstate_c.Clean(DIRTY_FRAGMENTSHADER_STATE);
ComputeFragmentShaderID(&FSID);
ComputeFragmentShaderID(&FSID, draw_->GetBugs());
} else {
FSID = lastFSID_;
}

View file

@ -84,15 +84,11 @@ bool GenerateVulkanGLSLFragmentShader(const FShaderID &id, char *buffer, uint32_
const char *shading = doFlatShading ? "flat" : "";
bool earlyFragmentTests = ((!enableAlphaTest && !enableColorTest) || testForceToZero) && !gstate_c.Supports(GPU_ROUND_FRAGMENT_DEPTH_TO_16BIT);
bool hasStencilOutput = stencilToAlpha != REPLACE_ALPHA_NO || id.Bit(FS_BIT_REPLACE_ALPHA_WITH_STENCIL_TYPE) == 0;
// TODO: This is a bug affecting shader cache generality - we CANNOT check anything but the shader ID and (indirectly) the game ID in here really.
// Need to move this check somehow to the shader ID generator. That's tricky though because it's generic...
bool isAdreno = vulkanVendorId == VULKAN_VENDOR_QUALCOMM && g_Config.bVendorBugChecksEnabled;
bool useAdrenoBugWorkaround = id.Bit(FS_BIT_NO_DEPTH_CANNOT_DISCARD_STENCIL);
if (earlyFragmentTests) {
WRITE(p, "layout (early_fragment_tests) in;\n");
} else if (isAdreno && hasStencilOutput && !gstate_c.Supports(GPU_ROUND_FRAGMENT_DEPTH_TO_16BIT)) {
} else if (useAdrenoBugWorkaround && !gstate_c.Supports(GPU_ROUND_FRAGMENT_DEPTH_TO_16BIT)) {
WRITE(p, "layout (depth_unchanged) out float gl_FragDepth;\n");
}
@ -588,7 +584,7 @@ bool GenerateVulkanGLSLFragmentShader(const FShaderID &id, char *buffer, uint32_
WRITE(p, " z = (1.0/65535.0) * floor(z * 65535.0);\n");
}
WRITE(p, " gl_FragDepth = z;\n");
} else if (!earlyFragmentTests && isAdreno && hasStencilOutput) {
} else if (!earlyFragmentTests && useAdrenoBugWorkaround) {
// Adreno (and possibly MESA/others) apply early frag tests even with discard in the shader.
// Writing depth prevents the bug, even with depth_unchanged specified.
WRITE(p, " gl_FragDepth = gl_FragCoord.z;\n");

View file

@ -240,7 +240,7 @@ void ShaderManagerVulkan::GetShaders(int prim, u32 vertType, VulkanVertexShader
FShaderID FSID;
if (gstate_c.IsDirty(DIRTY_FRAGMENTSHADER_STATE)) {
gstate_c.Clean(DIRTY_FRAGMENTSHADER_STATE);
ComputeFragmentShaderID(&FSID);
ComputeFragmentShaderID(&FSID, draw_->GetBugs());
} else {
FSID = lastFSID_;
}

View file

@ -315,6 +315,23 @@ struct Viewport {
float MaxDepth;
};
class Bugs {
public:
bool Has(uint32_t bug) const {
return (flags_ & (1 << bug)) != 0;
}
void Infest(uint32_t bug) {
flags_ |= (1 << bug);
}
enum : uint32_t {
NO_DEPTH_CANNOT_DISCARD_STENCIL = 0,
};
protected:
uint32_t flags_ = 0;
};
class RefCountedObject {
public:
RefCountedObject() : refcount_(1) {}
@ -524,6 +541,8 @@ public:
bool CreatePresets();
void DestroyPresets();
Bugs GetBugs() const { return bugs_; }
virtual const DeviceCaps &GetDeviceCaps() const = 0;
virtual uint32_t GetDataFormatSupport(DataFormat fmt) const = 0;
virtual std::vector<std::string> GetFeatureList() const { return std::vector<std::string>(); }
@ -647,6 +666,8 @@ protected:
int targetWidth_;
int targetHeight_;
Bugs bugs_;
};
extern const UniformBufferDesc UBPresetDesc;

View file

@ -762,7 +762,8 @@ VKContext::VKContext(VulkanContext *vulkan, bool splitSubmit)
caps_.framebufferDepthCopySupported = true; // Will pretty much always be the case.
caps_.preferredDepthBufferFormat = DataFormat::D24_S8; // TODO: Ask vulkan.
switch (vulkan->GetPhysicalDeviceProperties(vulkan_->GetCurrentPhysicalDevice()).vendorID) {
auto deviceProps = vulkan->GetPhysicalDeviceProperties(vulkan_->GetCurrentPhysicalDevice());
switch (deviceProps.vendorID) {
case VULKAN_VENDOR_AMD: caps_.vendor = GPUVendor::VENDOR_AMD; break;
case VULKAN_VENDOR_ARM: caps_.vendor = GPUVendor::VENDOR_ARM; break;
case VULKAN_VENDOR_IMGTEC: caps_.vendor = GPUVendor::VENDOR_IMGTEC; break;
@ -773,6 +774,14 @@ VKContext::VKContext(VulkanContext *vulkan, bool splitSubmit)
caps_.vendor = GPUVendor::VENDOR_UNKNOWN;
}
if (caps_.vendor == GPUVendor::VENDOR_QUALCOMM) {
// Adreno 5xx devices, all known driver versions, fail to discard stencil when depth write is off.
// See: https://github.com/hrydgard/ppsspp/pull/11684
if (deviceProps.deviceID >= 0x05000000 && deviceProps.deviceID < 0x06000000) {
bugs_.Infest(Bugs::NO_DEPTH_CANNOT_DISCARD_STENCIL);
}
}
device_ = vulkan->GetDevice();
queue_ = vulkan->GetGraphicsQueue();