Files
ppsspp/GPU/Vulkan/StateMappingVulkan.h
T
Henrik RydgårdandClaude Opus 5.5 d0baab5614 Compute FIXA/FIXB blends like the GE
The GE's blend terms are floor((2c+1)(2f+1)/1024), which truncates, while the
hardware multiplies by f/255 and rounds, so every pass of a fixed color blend
chain came out up to a level brighter. God Eater Burst's bloom is many such
passes and ended up far too bright.

For additive FIXA/FIXB blends that aren't just 0 and 0xFFFFFF, the shader
now computes the src term exactly and lowers it by 0.5 - (2B+1)/1024, and the
blend constant is the GE's dst factor (2B+1)/512, so the hardware's rounding
of the sum lands on the GE's floor (except where the src term would go below
zero). Blend constants are passed as floats to make that possible.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 09:09:36 +02:00

72 lines
2.2 KiB
C++

#pragma once
#include "Common/GPU/Vulkan/VulkanLoader.h"
#include <cstring>
class FramebufferManagerVulkan;
struct ScissorRect {
int x, y;
int width, height;
};
struct VulkanDynamicState {
VkViewport viewport;
ScissorRect scissor;
bool useBlendColor;
float blendColor[4];
bool useStencil;
uint8_t stencilRef;
uint8_t stencilWriteMask;
uint8_t stencilCompareMask;
};
// Let's pack this tight using bitfields.
// If an enable flag is set to 0, all the data fields for that section should
// also be set to 0.
// ~64 bits.
// Can't use enums unfortunately, they end up signed and breaking values above half their ranges.
// Stored in the shader cache on disk (as part of VulkanPipelineKey): changing it requires bumping
// CACHE_VERSION in ShaderManagerVulkan.cpp.
struct VulkanPipelineRasterStateKey {
// Blend
unsigned int blendEnable : 1;
unsigned int srcColor : 5; // VkBlendFactor
unsigned int destColor : 5; // VkBlendFactor
unsigned int srcAlpha : 5; // VkBlendFactor
unsigned int destAlpha : 5; // VkBlendFactor
unsigned int blendOpColor : 3; // VkBlendOp
unsigned int blendOpAlpha : 3; // VkBlendOp
unsigned int logicOpEnable : 1;
unsigned int logicOp : 4; // VkLogicOp
unsigned int colorWriteMask : 4;
// Depth/Stencil
unsigned int depthClampEnable : 1;
unsigned int depthTestEnable : 1;
unsigned int depthWriteEnable : 1;
unsigned int depthCompareOp : 3; // VkCompareOp
unsigned int stencilTestEnable : 1;
unsigned int stencilCompareOp : 3; // VkCompareOp
unsigned int stencilPassOp : 4; // VkStencilOp
unsigned int stencilFailOp : 4; // VkStencilOp
unsigned int stencilDepthFailOp : 4; // VkStencilOp
// We'll use dynamic state for writemask, reference and comparemask to start with,
// and viewport/scissor. More dynamic state requires extensions.
// Rasterizer
unsigned int cullMode : 2; // VkCullModeFlagBits
unsigned int topology : 4; // VkPrimitiveTopology
bool operator < (const VulkanPipelineRasterStateKey &other) const {
constexpr size_t size = sizeof(VulkanPipelineRasterStateKey);
return memcmp(this, &other, size) < 0;
}
bool operator == (const VulkanPipelineRasterStateKey &other) const {
constexpr size_t size = sizeof(VulkanPipelineRasterStateKey);
return memcmp(this, &other, size) == 0;
}
};