renderer_vulkan: Set up and configure VK_KHR_portability_subset extension according to spec. (#12)

* renderer_vulkan: Set up and configure VK_KHR_portability_subset extension according to spec.

* renderer_vulkan: Move mipmap LOD bias to shaders for compatibility.
This commit is contained in:
Steveice10
2023-01-04 00:43:08 -08:00
committed by GitHub
parent c5c041de89
commit 52c41d185b
11 changed files with 71 additions and 40 deletions

View File

@@ -643,6 +643,18 @@ void RasterizerAccelerated::NotifyPicaRegisterChanged(u32 id) {
uniform_block_data.lighting_lut_dirty_any = true;
break;
}
// Texture LOD biases
case PICA_REG_INDEX(texturing.texture0.lod.bias):
SyncTextureLodBias(0);
break;
case PICA_REG_INDEX(texturing.texture1.lod.bias):
SyncTextureLodBias(1);
break;
case PICA_REG_INDEX(texturing.texture2.lod.bias):
SyncTextureLodBias(2);
break;
default:
// Forward registers that map to fixed function API features to the video backend
NotifyFixedFunctionPicaRegisterChanged(id);
@@ -840,4 +852,13 @@ void RasterizerAccelerated::SyncShadowTextureBias() {
}
}
void RasterizerAccelerated::SyncTextureLodBias(int tex_index) {
const auto pica_textures = Pica::g_state.regs.texturing.GetTextures();
const float bias = pica_textures[tex_index].config.lod.bias / 256.0f;
if (bias != uniform_block_data.data.tex_lod_bias[tex_index]) {
uniform_block_data.data.tex_lod_bias[tex_index] = bias;
uniform_block_data.dirty = true;
}
}
} // namespace VideoCore

View File

@@ -85,6 +85,9 @@ protected:
/// Syncs the shadow texture bias to match the PICA register
void SyncShadowTextureBias();
/// Syncs the texture LOD bias to match the PICA register
void SyncTextureLodBias(int tex_index);
protected:
/// Structure that keeps tracks of the uniform state
struct UniformBlockData {

View File

@@ -176,6 +176,10 @@ void RasterizerOpenGL::SyncEntireState() {
SyncProcTexBias();
SyncShadowBias();
SyncShadowTextureBias();
for (unsigned tex_index = 0; tex_index < 3; tex_index++) {
SyncTextureLodBias(tex_index);
}
}
static constexpr std::array<GLenum, 4> vs_attrib_types{
@@ -991,7 +995,6 @@ void RasterizerOpenGL::SamplerInfo::Create() {
wrap_s = wrap_t = TextureConfig::Repeat;
border_color = 0;
lod_min = lod_max = 0;
lod_bias = 0;
// default is 1000 and -1000
// Other attributes have correct defaults
@@ -1053,11 +1056,6 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(
lod_max = config.lod.max_level;
glSamplerParameterf(s, GL_TEXTURE_MAX_LOD, static_cast<float>(lod_max));
}
if (!GLES && lod_bias != config.lod.bias) {
lod_bias = config.lod.bias;
glSamplerParameterf(s, GL_TEXTURE_LOD_BIAS, lod_bias / 256.0f);
}
}
void RasterizerOpenGL::SetShader() {

View File

@@ -66,7 +66,6 @@ private:
u32 border_color;
u32 lod_min;
u32 lod_max;
s32 lod_bias;
// TODO(wwylele): remove this once mipmap for cube is implemented
bool supress_mipmap_for_cube = false;

View File

@@ -68,6 +68,7 @@ layout (std140) uniform shader_data {
vec4 const_color[NUM_TEV_STAGES];
vec4 tev_combiner_buffer_color;
vec4 clip_coef;
vec3 tex_lod_bias;
};
)";
@@ -307,7 +308,7 @@ static std::string SampleTexture(const PicaFSConfig& config, unsigned texture_un
// Only unit 0 respects the texturing type
switch (state.texture0_type) {
case TexturingRegs::TextureConfig::Texture2D:
return "textureLod(tex0, texcoord0, getLod(texcoord0 * vec2(textureSize(tex0, 0))))";
return "textureLod(tex0, texcoord0, getLod(texcoord0 * vec2(textureSize(tex0, 0))) + tex_lod_bias[0])";
case TexturingRegs::TextureConfig::Projection2D:
// TODO (wwylele): find the exact LOD formula for projection texture
return "textureProj(tex0, vec3(texcoord0, texcoord0_w))";
@@ -325,12 +326,12 @@ static std::string SampleTexture(const PicaFSConfig& config, unsigned texture_un
return "texture(tex0, texcoord0)";
}
case 1:
return "textureLod(tex1, texcoord1, getLod(texcoord1 * vec2(textureSize(tex1, 0))))";
return "textureLod(tex1, texcoord1, getLod(texcoord1 * vec2(textureSize(tex1, 0))) + tex_lod_bias[1])";
case 2:
if (state.texture2_use_coord1)
return "textureLod(tex2, texcoord1, getLod(texcoord1 * vec2(textureSize(tex2, 0))))";
return "textureLod(tex2, texcoord1, getLod(texcoord1 * vec2(textureSize(tex2, 0))) + tex_lod_bias[2])";
else
return "textureLod(tex2, texcoord2, getLod(texcoord2 * vec2(textureSize(tex2, 0))))";
return "textureLod(tex2, texcoord2, getLod(texcoord2 * vec2(textureSize(tex2, 0))) + tex_lod_bias[2])";
case 3:
if (state.proctex.enable) {
return "ProcTex()";

View File

@@ -356,6 +356,7 @@ void Instance::CreateFormatTable() {
bool Instance::CreateDevice() {
const vk::StructureChain feature_chain =
physical_device.getFeatures2<vk::PhysicalDeviceFeatures2,
vk::PhysicalDevicePortabilitySubsetFeaturesKHR,
vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT,
vk::PhysicalDeviceTimelineSemaphoreFeaturesKHR,
vk::PhysicalDeviceCustomBorderColorFeaturesEXT,
@@ -393,6 +394,8 @@ bool Instance::CreateDevice() {
};
AddExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
// According to the Vulkan spec, VK_KHR_portability_subset must be added if supported.
bool portability_subset = AddExtension(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME);
timeline_semaphores = AddExtension(VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME);
extended_dynamic_state = AddExtension(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
push_descriptors = AddExtension(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
@@ -473,12 +476,28 @@ bool Instance::CreateDevice() {
.shaderClipDistance = features.shaderClipDistance,
},
},
feature_chain.get<vk::PhysicalDevicePortabilitySubsetFeaturesKHR>(),
feature_chain.get<vk::PhysicalDeviceTimelineSemaphoreFeaturesKHR>(),
feature_chain.get<vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT>(),
feature_chain.get<vk::PhysicalDeviceCustomBorderColorFeaturesEXT>(),
feature_chain.get<vk::PhysicalDeviceIndexTypeUint8FeaturesEXT>(),
};
if (portability_subset) {
const vk::PhysicalDevicePortabilitySubsetFeaturesKHR portability_features =
feature_chain.get<vk::PhysicalDevicePortabilitySubsetFeaturesKHR>();
triangle_fan_supported = portability_features.triangleFans;
const vk::StructureChain portability_properties_chain =
physical_device.getProperties2<vk::PhysicalDeviceProperties2,
vk::PhysicalDevicePortabilitySubsetPropertiesKHR>();
const vk::PhysicalDevicePortabilitySubsetPropertiesKHR portability_properties =
portability_properties_chain.get<vk::PhysicalDevicePortabilitySubsetPropertiesKHR>();
min_vertex_stride_alignment = portability_properties.minVertexInputBindingStrideAlignment;
} else {
device_chain.unlink<vk::PhysicalDevicePortabilitySubsetFeaturesKHR>();
}
if (!index_type_uint8) {
device_chain.unlink<vk::PhysicalDeviceIndexTypeUint8FeaturesEXT>();
}
@@ -491,22 +510,6 @@ bool Instance::CreateDevice() {
device_chain.unlink<vk::PhysicalDeviceCustomBorderColorFeaturesEXT>();
}
#if __APPLE__
const vk::StructureChain portability_features_chain =
physical_device.getFeatures2<vk::PhysicalDeviceFeatures2,
vk::PhysicalDevicePortabilitySubsetFeaturesKHR>();
const vk::PhysicalDevicePortabilitySubsetFeaturesKHR portability_features =
portability_features_chain.get<vk::PhysicalDevicePortabilitySubsetFeaturesKHR>();
triangle_fan_supported = portability_features.triangleFans;
const vk::StructureChain portability_properties_chain =
physical_device.getProperties2<vk::PhysicalDeviceProperties2,
vk::PhysicalDevicePortabilitySubsetPropertiesKHR>();
const vk::PhysicalDevicePortabilitySubsetPropertiesKHR portability_properties =
portability_properties_chain.get<vk::PhysicalDevicePortabilitySubsetPropertiesKHR>();
min_vertex_stride_alignment = portability_properties.minVertexInputBindingStrideAlignment;
#endif
try {
device = physical_device.createDevice(device_chain.get());
} catch (vk::ExtensionNotPresentError& err) {

View File

@@ -184,6 +184,10 @@ void RasterizerVulkan::SyncEntireState() {
SyncProcTexBias();
SyncShadowBias();
SyncShadowTextureBias();
for (unsigned tex_index = 0; tex_index < 3; tex_index++) {
SyncTextureLodBias(tex_index);
}
}
void RasterizerVulkan::SyncFixedState() {
@@ -593,8 +597,7 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
.wrap_t = config.wrap_t,
.border_color = config.border_color.raw,
.lod_min = skip_mipmap ? 0.f : static_cast<float>(config.lod.min_level),
.lod_max = skip_mipmap ? 0.f : static_cast<float>(config.lod.max_level),
.lod_bias = static_cast<float>(config.lod.bias),
.lod_max = skip_mipmap ? 0.f : static_cast<float>(config.lod.max_level)
};
// Search the cache and bind the appropriate sampler
@@ -1141,7 +1144,7 @@ vk::Sampler RasterizerVulkan::CreateSampler(const SamplerInfo& info) {
.mipmapMode = PicaToVK::TextureMipFilterMode(info.mip_filter),
.addressModeU = PicaToVK::WrapMode(info.wrap_s),
.addressModeV = PicaToVK::WrapMode(info.wrap_t),
.mipLodBias = info.lod_bias / 256.0f,
.mipLodBias = 0,
.anisotropyEnable = instance.IsAnisotropicFilteringSupported(),
.maxAnisotropy = properties.limits.maxSamplerAnisotropy,
.compareEnable = false,

View File

@@ -34,7 +34,6 @@ struct SamplerInfo {
u32 border_color = 0;
float lod_min = 0;
float lod_max = 0;
float lod_bias = 0;
// TODO(wwylele): remove this once mipmap for cube is implemented
bool supress_mipmap_for_cube = false;

View File

@@ -69,6 +69,7 @@ layout (set = 0, binding = 1, std140) uniform shader_data {
vec4 const_color[NUM_TEV_STAGES];
vec4 tev_combiner_buffer_color;
vec4 clip_coef;
vec3 tex_lod_bias;
};
)";
@@ -325,7 +326,7 @@ static std::string SampleTexture(const PicaFSConfig& config, unsigned texture_un
switch (state.texture0_type) {
case TexturingRegs::TextureConfig::Texture2D:
return "textureLod(sampler2D(tex0, tex0_sampler), texcoord0, getLod(texcoord0 * "
"vec2(textureSize(sampler2D(tex0, tex0_sampler), 0))))";
"vec2(textureSize(sampler2D(tex0, tex0_sampler), 0))) + tex_lod_bias[0])";
case TexturingRegs::TextureConfig::Projection2D:
// TODO (wwylele): find the exact LOD formula for projection texture
return "textureProj(sampler2D(tex0, tex0_sampler), vec3(texcoord0, texcoord0_w))";
@@ -344,14 +345,14 @@ static std::string SampleTexture(const PicaFSConfig& config, unsigned texture_un
}
case 1:
return "textureLod(sampler2D(tex1, tex1_sampler), texcoord1, getLod(texcoord1 * "
"vec2(textureSize(sampler2D(tex1, tex1_sampler), 0))))";
"vec2(textureSize(sampler2D(tex1, tex1_sampler), 0))) + tex_lod_bias[1])";
case 2:
if (state.texture2_use_coord1)
return "textureLod(sampler2D(tex2, tex2_sampler), texcoord1, getLod(texcoord1 * "
"vec2(textureSize(sampler2D(tex2, tex2_sampler), 0))))";
"vec2(textureSize(sampler2D(tex2, tex2_sampler), 0))) + tex_lod_bias[2])";
else
return "textureLod(sampler2D(tex2, tex2_sampler), texcoord2, getLod(texcoord2 * "
"vec2(textureSize(sampler2D(tex2, tex2_sampler), 0))))";
"vec2(textureSize(sampler2D(tex2, tex2_sampler), 0))) + tex_lod_bias[2])";
case 3:
if (state.proctex.enable) {
return "ProcTex()";

View File

@@ -610,7 +610,7 @@ Id FragmentModule::SampleTexture(u32 texture_unit) {
// This LOD formula is the same as the LOD lower limit defined in OpenGL.
// f(x, y) >= max{m_u, m_v, m_w}
// (See OpenGL 4.6 spec, 8.14.1 - Scale Factor and Level-of-Detail)
const auto SampleLod = [this](Id tex_id, Id tex_sampler_id, Id texcoord_id) {
const auto SampleLod = [this, texture_unit](Id tex_id, Id tex_sampler_id, Id texcoord_id) {
const Id tex{OpLoad(image2d_id, tex_id)};
const Id tex_sampler{OpLoad(sampler_id, tex_sampler_id)};
const Id sampled_image{OpSampledImage(TypeSampledImage(image2d_id), tex, tex_sampler)};
@@ -624,8 +624,10 @@ Id FragmentModule::SampleTexture(u32 texture_unit) {
const Id dx_dy_max{
OpFMax(f32_id, OpCompositeExtract(f32_id, d, 0), OpCompositeExtract(f32_id, d, 1))};
const Id lod{OpLog2(f32_id, dx_dy_max)};
const Id lod_bias{GetShaderDataMember(f32_id, ConstS32(29), ConstU32(texture_unit))};
const Id biased_lod{OpFAdd(f32_id, lod, lod_bias)};
return OpImageSampleExplicitLod(vec_ids.Get(4), sampled_image, texcoord,
spv::ImageOperandsMask::Lod, lod);
spv::ImageOperandsMask::Lod, biased_lod);
};
const auto Sample = [this](Id tex_id, Id tex_sampler_id, bool projection) {
@@ -1342,12 +1344,12 @@ void FragmentModule::DefineUniformStructs() {
i32_id, i32_id, f32_id, f32_id, f32_id, f32_id, i32_id, i32_id, i32_id, i32_id, i32_id,
i32_id, i32_id, i32_id, i32_id, i32_id, f32_id, i32_id, u32_id, lighting_lut_array_id,
vec_ids.Get(3), vec_ids.Get(2), vec_ids.Get(2), vec_ids.Get(2), vec_ids.Get(3),
light_src_array_id, const_color_array_id, vec_ids.Get(4), vec_ids.Get(4))};
light_src_array_id, const_color_array_id, vec_ids.Get(4), vec_ids.Get(4), vec_ids.Get(3))};
constexpr std::array light_src_offsets{0u, 16u, 32u, 48u, 64u, 80u, 92u, 96u};
constexpr std::array shader_data_offsets{
0u, 4u, 8u, 12u, 16u, 20u, 24u, 28u, 32u, 36u, 40u, 44u, 48u, 52u, 56u,
60u, 64u, 68u, 72u, 80u, 176u, 192u, 200u, 208u, 224u, 240u, 1136u, 1232u, 1248u};
60u, 64u, 68u, 72u, 80u, 176u, 192u, 200u, 208u, 224u, 240u, 1136u, 1232u, 1248u, 1264u};
Decorate(lighting_lut_array_id, spv::Decoration::ArrayStride, 16u);
Decorate(light_src_array_id, spv::Decoration::ArrayStride, 112u);

View File

@@ -64,9 +64,10 @@ struct UniformData {
alignas(16) Common::Vec4f const_color[6]; // A vec4 color for each of the six tev stages
alignas(16) Common::Vec4f tev_combiner_buffer_color;
alignas(16) Common::Vec4f clip_coef;
alignas(16) Common::Vec3f tex_lod_bias;
};
static_assert(sizeof(UniformData) == 0x4F0,
static_assert(sizeof(UniformData) == 0x500,
"The size of the UniformData does not match the structure in the shader");
static_assert(sizeof(UniformData) < 16384,
"UniformData structure must be less than 16kb as per the OpenGL spec");