video_core: Abstract shader generators. (#6990)
* video_core: Abstract shader generators. * shader: Extract common generator structures and move generators to specific namespaces. * shader: Minor fixes and clean-up.
This commit is contained in:
parent
1492d73ccb
commit
50f22d1f59
|
@ -4,24 +4,24 @@ include(GenerateBuildInfo)
|
||||||
# The variable SRC_DIR must be passed into the script (since it uses the current build directory for all values of CMAKE_*_DIR)
|
# The variable SRC_DIR must be passed into the script (since it uses the current build directory for all values of CMAKE_*_DIR)
|
||||||
set(VIDEO_CORE "${SRC_DIR}/src/video_core")
|
set(VIDEO_CORE "${SRC_DIR}/src/video_core")
|
||||||
set(HASH_FILES
|
set(HASH_FILES
|
||||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_decompiler.cpp"
|
|
||||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_decompiler.h"
|
|
||||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_disk_cache.cpp"
|
"${VIDEO_CORE}/renderer_opengl/gl_shader_disk_cache.cpp"
|
||||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_disk_cache.h"
|
"${VIDEO_CORE}/renderer_opengl/gl_shader_disk_cache.h"
|
||||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_gen.cpp"
|
|
||||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_gen.h"
|
|
||||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_util.cpp"
|
"${VIDEO_CORE}/renderer_opengl/gl_shader_util.cpp"
|
||||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_util.h"
|
"${VIDEO_CORE}/renderer_opengl/gl_shader_util.h"
|
||||||
"${VIDEO_CORE}/renderer_vulkan/vk_shader_gen.cpp"
|
|
||||||
"${VIDEO_CORE}/renderer_vulkan/vk_shader_gen.h"
|
|
||||||
"${VIDEO_CORE}/renderer_vulkan/vk_shader_gen_spv.cpp"
|
|
||||||
"${VIDEO_CORE}/renderer_vulkan/vk_shader_gen_spv.h"
|
|
||||||
"${VIDEO_CORE}/renderer_vulkan/vk_shader_util.cpp"
|
"${VIDEO_CORE}/renderer_vulkan/vk_shader_util.cpp"
|
||||||
"${VIDEO_CORE}/renderer_vulkan/vk_shader_util.h"
|
"${VIDEO_CORE}/renderer_vulkan/vk_shader_util.h"
|
||||||
|
"${VIDEO_CORE}/shader/generator/glsl_shader_decompiler.cpp"
|
||||||
|
"${VIDEO_CORE}/shader/generator/glsl_shader_decompiler.h"
|
||||||
|
"${VIDEO_CORE}/shader/generator/glsl_shader_gen.cpp"
|
||||||
|
"${VIDEO_CORE}/shader/generator/glsl_shader_gen.h"
|
||||||
|
"${VIDEO_CORE}/shader/generator/shader_gen.cpp"
|
||||||
|
"${VIDEO_CORE}/shader/generator/shader_gen.h"
|
||||||
|
"${VIDEO_CORE}/shader/generator/shader_uniforms.cpp"
|
||||||
|
"${VIDEO_CORE}/shader/generator/shader_uniforms.h"
|
||||||
|
"${VIDEO_CORE}/shader/generator/spv_shader_gen.cpp"
|
||||||
|
"${VIDEO_CORE}/shader/generator/spv_shader_gen.h"
|
||||||
"${VIDEO_CORE}/shader/shader.cpp"
|
"${VIDEO_CORE}/shader/shader.cpp"
|
||||||
"${VIDEO_CORE}/shader/shader.h"
|
"${VIDEO_CORE}/shader/shader.h"
|
||||||
"${VIDEO_CORE}/shader/shader_uniforms.cpp"
|
|
||||||
"${VIDEO_CORE}/shader/shader_uniforms.h"
|
|
||||||
"${VIDEO_CORE}/pica.cpp"
|
"${VIDEO_CORE}/pica.cpp"
|
||||||
"${VIDEO_CORE}/pica.h"
|
"${VIDEO_CORE}/pica.h"
|
||||||
"${VIDEO_CORE}/regs_framebuffer.h"
|
"${VIDEO_CORE}/regs_framebuffer.h"
|
||||||
|
|
|
@ -15,14 +15,22 @@ add_custom_command(OUTPUT scm_rev.cpp
|
||||||
DEPENDS
|
DEPENDS
|
||||||
# WARNING! It was too much work to try and make a common location for this list,
|
# WARNING! It was too much work to try and make a common location for this list,
|
||||||
# so if you need to change it, please update CMakeModules/GenerateSCMRev.cmake as well
|
# so if you need to change it, please update CMakeModules/GenerateSCMRev.cmake as well
|
||||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_decompiler.cpp"
|
|
||||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_decompiler.h"
|
|
||||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_disk_cache.cpp"
|
"${VIDEO_CORE}/renderer_opengl/gl_shader_disk_cache.cpp"
|
||||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_disk_cache.h"
|
"${VIDEO_CORE}/renderer_opengl/gl_shader_disk_cache.h"
|
||||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_gen.cpp"
|
|
||||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_gen.h"
|
|
||||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_util.cpp"
|
"${VIDEO_CORE}/renderer_opengl/gl_shader_util.cpp"
|
||||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_util.h"
|
"${VIDEO_CORE}/renderer_opengl/gl_shader_util.h"
|
||||||
|
"${VIDEO_CORE}/renderer_vulkan/vk_shader_util.cpp"
|
||||||
|
"${VIDEO_CORE}/renderer_vulkan/vk_shader_util.h"
|
||||||
|
"${VIDEO_CORE}/shader/generator/glsl_shader_decompiler.cpp"
|
||||||
|
"${VIDEO_CORE}/shader/generator/glsl_shader_decompiler.h"
|
||||||
|
"${VIDEO_CORE}/shader/generator/glsl_shader_gen.cpp"
|
||||||
|
"${VIDEO_CORE}/shader/generator/glsl_shader_gen.h"
|
||||||
|
"${VIDEO_CORE}/shader/generator/shader_gen.cpp"
|
||||||
|
"${VIDEO_CORE}/shader/generator/shader_gen.h"
|
||||||
|
"${VIDEO_CORE}/shader/generator/shader_uniforms.cpp"
|
||||||
|
"${VIDEO_CORE}/shader/generator/shader_uniforms.h"
|
||||||
|
"${VIDEO_CORE}/shader/generator/spv_shader_gen.cpp"
|
||||||
|
"${VIDEO_CORE}/shader/generator/spv_shader_gen.h"
|
||||||
"${VIDEO_CORE}/shader/shader.cpp"
|
"${VIDEO_CORE}/shader/shader.cpp"
|
||||||
"${VIDEO_CORE}/shader/shader.h"
|
"${VIDEO_CORE}/shader/shader.h"
|
||||||
"${VIDEO_CORE}/pica.cpp"
|
"${VIDEO_CORE}/pica.cpp"
|
||||||
|
|
|
@ -61,12 +61,8 @@ add_library(video_core STATIC
|
||||||
renderer_opengl/gl_rasterizer_cache.cpp
|
renderer_opengl/gl_rasterizer_cache.cpp
|
||||||
renderer_opengl/gl_resource_manager.cpp
|
renderer_opengl/gl_resource_manager.cpp
|
||||||
renderer_opengl/gl_resource_manager.h
|
renderer_opengl/gl_resource_manager.h
|
||||||
renderer_opengl/gl_shader_decompiler.cpp
|
|
||||||
renderer_opengl/gl_shader_decompiler.h
|
|
||||||
renderer_opengl/gl_shader_disk_cache.cpp
|
renderer_opengl/gl_shader_disk_cache.cpp
|
||||||
renderer_opengl/gl_shader_disk_cache.h
|
renderer_opengl/gl_shader_disk_cache.h
|
||||||
renderer_opengl/gl_shader_gen.cpp
|
|
||||||
renderer_opengl/gl_shader_gen.h
|
|
||||||
renderer_opengl/gl_shader_manager.cpp
|
renderer_opengl/gl_shader_manager.cpp
|
||||||
renderer_opengl/gl_shader_manager.h
|
renderer_opengl/gl_shader_manager.h
|
||||||
renderer_opengl/gl_shader_util.cpp
|
renderer_opengl/gl_shader_util.cpp
|
||||||
|
@ -130,10 +126,6 @@ add_library(video_core STATIC
|
||||||
renderer_vulkan/vk_present_window.h
|
renderer_vulkan/vk_present_window.h
|
||||||
renderer_vulkan/vk_renderpass_cache.cpp
|
renderer_vulkan/vk_renderpass_cache.cpp
|
||||||
renderer_vulkan/vk_renderpass_cache.h
|
renderer_vulkan/vk_renderpass_cache.h
|
||||||
renderer_vulkan/vk_shader_gen.cpp
|
|
||||||
renderer_vulkan/vk_shader_gen.h
|
|
||||||
renderer_vulkan/vk_shader_gen_spv.cpp
|
|
||||||
renderer_vulkan/vk_shader_gen_spv.h
|
|
||||||
renderer_vulkan/vk_shader_util.cpp
|
renderer_vulkan/vk_shader_util.cpp
|
||||||
renderer_vulkan/vk_shader_util.h
|
renderer_vulkan/vk_shader_util.h
|
||||||
renderer_vulkan/vk_stream_buffer.cpp
|
renderer_vulkan/vk_stream_buffer.cpp
|
||||||
|
@ -143,6 +135,16 @@ add_library(video_core STATIC
|
||||||
renderer_vulkan/vk_texture_runtime.cpp
|
renderer_vulkan/vk_texture_runtime.cpp
|
||||||
renderer_vulkan/vk_texture_runtime.h
|
renderer_vulkan/vk_texture_runtime.h
|
||||||
shader/debug_data.h
|
shader/debug_data.h
|
||||||
|
shader/generator/glsl_shader_decompiler.cpp
|
||||||
|
shader/generator/glsl_shader_decompiler.h
|
||||||
|
shader/generator/glsl_shader_gen.cpp
|
||||||
|
shader/generator/glsl_shader_gen.h
|
||||||
|
shader/generator/shader_gen.cpp
|
||||||
|
shader/generator/shader_gen.h
|
||||||
|
shader/generator/shader_uniforms.cpp
|
||||||
|
shader/generator/shader_uniforms.h
|
||||||
|
shader/generator/spv_shader_gen.cpp
|
||||||
|
shader/generator/spv_shader_gen.h
|
||||||
shader/shader.cpp
|
shader/shader.cpp
|
||||||
shader/shader.h
|
shader/shader.h
|
||||||
shader/shader_interpreter.cpp
|
shader/shader_interpreter.cpp
|
||||||
|
@ -151,8 +153,6 @@ add_library(video_core STATIC
|
||||||
shader/shader_jit_x64_compiler.cpp
|
shader/shader_jit_x64_compiler.cpp
|
||||||
shader/shader_jit_x64.h
|
shader/shader_jit_x64.h
|
||||||
shader/shader_jit_x64_compiler.h
|
shader/shader_jit_x64_compiler.h
|
||||||
shader/shader_uniforms.cpp
|
|
||||||
shader/shader_uniforms.h
|
|
||||||
texture/etc1.cpp
|
texture/etc1.cpp
|
||||||
texture/etc1.h
|
texture/etc1.h
|
||||||
texture/texture_decode.cpp
|
texture/texture_decode.cpp
|
||||||
|
|
|
@ -54,7 +54,7 @@ RasterizerAccelerated::HardwareVertex::HardwareVertex(const Pica::Shader::Output
|
||||||
|
|
||||||
RasterizerAccelerated::RasterizerAccelerated(Memory::MemorySystem& memory_)
|
RasterizerAccelerated::RasterizerAccelerated(Memory::MemorySystem& memory_)
|
||||||
: memory{memory_}, regs{Pica::g_state.regs} {
|
: memory{memory_}, regs{Pica::g_state.regs} {
|
||||||
uniform_block_data.lighting_lut_dirty.fill(true);
|
fs_uniform_block_data.lighting_lut_dirty.fill(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -135,7 +135,7 @@ void RasterizerAccelerated::SyncEntireState() {
|
||||||
SyncFixedState();
|
SyncFixedState();
|
||||||
|
|
||||||
// Sync uniforms
|
// Sync uniforms
|
||||||
SyncClipCoef();
|
SyncClipPlane();
|
||||||
SyncDepthScale();
|
SyncDepthScale();
|
||||||
SyncDepthOffset();
|
SyncDepthOffset();
|
||||||
SyncAlphaTest();
|
SyncAlphaTest();
|
||||||
|
@ -199,7 +199,7 @@ void RasterizerAccelerated::NotifyPicaRegisterChanged(u32 id) {
|
||||||
case PICA_REG_INDEX(texturing.fog_lut_data[5]):
|
case PICA_REG_INDEX(texturing.fog_lut_data[5]):
|
||||||
case PICA_REG_INDEX(texturing.fog_lut_data[6]):
|
case PICA_REG_INDEX(texturing.fog_lut_data[6]):
|
||||||
case PICA_REG_INDEX(texturing.fog_lut_data[7]):
|
case PICA_REG_INDEX(texturing.fog_lut_data[7]):
|
||||||
uniform_block_data.fog_lut_dirty = true;
|
fs_uniform_block_data.fog_lut_dirty = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// ProcTex state
|
// ProcTex state
|
||||||
|
@ -227,19 +227,19 @@ void RasterizerAccelerated::NotifyPicaRegisterChanged(u32 id) {
|
||||||
using Pica::TexturingRegs;
|
using Pica::TexturingRegs;
|
||||||
switch (regs.texturing.proctex_lut_config.ref_table.Value()) {
|
switch (regs.texturing.proctex_lut_config.ref_table.Value()) {
|
||||||
case TexturingRegs::ProcTexLutTable::Noise:
|
case TexturingRegs::ProcTexLutTable::Noise:
|
||||||
uniform_block_data.proctex_noise_lut_dirty = true;
|
fs_uniform_block_data.proctex_noise_lut_dirty = true;
|
||||||
break;
|
break;
|
||||||
case TexturingRegs::ProcTexLutTable::ColorMap:
|
case TexturingRegs::ProcTexLutTable::ColorMap:
|
||||||
uniform_block_data.proctex_color_map_dirty = true;
|
fs_uniform_block_data.proctex_color_map_dirty = true;
|
||||||
break;
|
break;
|
||||||
case TexturingRegs::ProcTexLutTable::AlphaMap:
|
case TexturingRegs::ProcTexLutTable::AlphaMap:
|
||||||
uniform_block_data.proctex_alpha_map_dirty = true;
|
fs_uniform_block_data.proctex_alpha_map_dirty = true;
|
||||||
break;
|
break;
|
||||||
case TexturingRegs::ProcTexLutTable::Color:
|
case TexturingRegs::ProcTexLutTable::Color:
|
||||||
uniform_block_data.proctex_lut_dirty = true;
|
fs_uniform_block_data.proctex_lut_dirty = true;
|
||||||
break;
|
break;
|
||||||
case TexturingRegs::ProcTexLutTable::ColorDiff:
|
case TexturingRegs::ProcTexLutTable::ColorDiff:
|
||||||
uniform_block_data.proctex_diff_lut_dirty = true;
|
fs_uniform_block_data.proctex_diff_lut_dirty = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -588,8 +588,8 @@ void RasterizerAccelerated::NotifyPicaRegisterChanged(u32 id) {
|
||||||
case PICA_REG_INDEX(lighting.lut_data[6]):
|
case PICA_REG_INDEX(lighting.lut_data[6]):
|
||||||
case PICA_REG_INDEX(lighting.lut_data[7]): {
|
case PICA_REG_INDEX(lighting.lut_data[7]): {
|
||||||
const auto& lut_config = regs.lighting.lut_config;
|
const auto& lut_config = regs.lighting.lut_config;
|
||||||
uniform_block_data.lighting_lut_dirty[lut_config.type] = true;
|
fs_uniform_block_data.lighting_lut_dirty[lut_config.type] = true;
|
||||||
uniform_block_data.lighting_lut_dirty_any = true;
|
fs_uniform_block_data.lighting_lut_dirty_any = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -616,11 +616,12 @@ void RasterizerAccelerated::NotifyPicaRegisterChanged(u32 id) {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Clipping plane
|
// Clipping plane
|
||||||
|
case PICA_REG_INDEX(rasterizer.clip_enable):
|
||||||
case PICA_REG_INDEX(rasterizer.clip_coef[0]):
|
case PICA_REG_INDEX(rasterizer.clip_coef[0]):
|
||||||
case PICA_REG_INDEX(rasterizer.clip_coef[1]):
|
case PICA_REG_INDEX(rasterizer.clip_coef[1]):
|
||||||
case PICA_REG_INDEX(rasterizer.clip_coef[2]):
|
case PICA_REG_INDEX(rasterizer.clip_coef[2]):
|
||||||
case PICA_REG_INDEX(rasterizer.clip_coef[3]):
|
case PICA_REG_INDEX(rasterizer.clip_coef[3]):
|
||||||
SyncClipCoef();
|
SyncClipPlane();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -631,18 +632,18 @@ void RasterizerAccelerated::NotifyPicaRegisterChanged(u32 id) {
|
||||||
void RasterizerAccelerated::SyncDepthScale() {
|
void RasterizerAccelerated::SyncDepthScale() {
|
||||||
const f32 depth_scale = f24::FromRaw(regs.rasterizer.viewport_depth_range).ToFloat32();
|
const f32 depth_scale = f24::FromRaw(regs.rasterizer.viewport_depth_range).ToFloat32();
|
||||||
|
|
||||||
if (depth_scale != uniform_block_data.data.depth_scale) {
|
if (depth_scale != fs_uniform_block_data.data.depth_scale) {
|
||||||
uniform_block_data.data.depth_scale = depth_scale;
|
fs_uniform_block_data.data.depth_scale = depth_scale;
|
||||||
uniform_block_data.dirty = true;
|
fs_uniform_block_data.dirty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RasterizerAccelerated::SyncDepthOffset() {
|
void RasterizerAccelerated::SyncDepthOffset() {
|
||||||
const f32 depth_offset = f24::FromRaw(regs.rasterizer.viewport_depth_near_plane).ToFloat32();
|
const f32 depth_offset = f24::FromRaw(regs.rasterizer.viewport_depth_near_plane).ToFloat32();
|
||||||
|
|
||||||
if (depth_offset != uniform_block_data.data.depth_offset) {
|
if (depth_offset != fs_uniform_block_data.data.depth_offset) {
|
||||||
uniform_block_data.data.depth_offset = depth_offset;
|
fs_uniform_block_data.data.depth_offset = depth_offset;
|
||||||
uniform_block_data.dirty = true;
|
fs_uniform_block_data.dirty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -654,9 +655,9 @@ void RasterizerAccelerated::SyncFogColor() {
|
||||||
fog_color_regs.b.Value() / 255.0f,
|
fog_color_regs.b.Value() / 255.0f,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (fog_color != uniform_block_data.data.fog_color) {
|
if (fog_color != fs_uniform_block_data.data.fog_color) {
|
||||||
uniform_block_data.data.fog_color = fog_color;
|
fs_uniform_block_data.data.fog_color = fog_color;
|
||||||
uniform_block_data.dirty = true;
|
fs_uniform_block_data.dirty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -674,13 +675,13 @@ void RasterizerAccelerated::SyncProcTexNoise() {
|
||||||
Pica::f16::FromRaw(regs.texturing.proctex_noise_v.phase).ToFloat32(),
|
Pica::f16::FromRaw(regs.texturing.proctex_noise_v.phase).ToFloat32(),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (proctex_noise_f != uniform_block_data.data.proctex_noise_f ||
|
if (proctex_noise_f != fs_uniform_block_data.data.proctex_noise_f ||
|
||||||
proctex_noise_a != uniform_block_data.data.proctex_noise_a ||
|
proctex_noise_a != fs_uniform_block_data.data.proctex_noise_a ||
|
||||||
proctex_noise_p != uniform_block_data.data.proctex_noise_p) {
|
proctex_noise_p != fs_uniform_block_data.data.proctex_noise_p) {
|
||||||
uniform_block_data.data.proctex_noise_f = proctex_noise_f;
|
fs_uniform_block_data.data.proctex_noise_f = proctex_noise_f;
|
||||||
uniform_block_data.data.proctex_noise_a = proctex_noise_a;
|
fs_uniform_block_data.data.proctex_noise_a = proctex_noise_a;
|
||||||
uniform_block_data.data.proctex_noise_p = proctex_noise_p;
|
fs_uniform_block_data.data.proctex_noise_p = proctex_noise_p;
|
||||||
uniform_block_data.dirty = true;
|
fs_uniform_block_data.dirty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -688,25 +689,25 @@ void RasterizerAccelerated::SyncProcTexBias() {
|
||||||
const auto proctex_bias = Pica::f16::FromRaw(regs.texturing.proctex.bias_low |
|
const auto proctex_bias = Pica::f16::FromRaw(regs.texturing.proctex.bias_low |
|
||||||
(regs.texturing.proctex_lut.bias_high << 8))
|
(regs.texturing.proctex_lut.bias_high << 8))
|
||||||
.ToFloat32();
|
.ToFloat32();
|
||||||
if (proctex_bias != uniform_block_data.data.proctex_bias) {
|
if (proctex_bias != fs_uniform_block_data.data.proctex_bias) {
|
||||||
uniform_block_data.data.proctex_bias = proctex_bias;
|
fs_uniform_block_data.data.proctex_bias = proctex_bias;
|
||||||
uniform_block_data.dirty = true;
|
fs_uniform_block_data.dirty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RasterizerAccelerated::SyncAlphaTest() {
|
void RasterizerAccelerated::SyncAlphaTest() {
|
||||||
if (regs.framebuffer.output_merger.alpha_test.ref !=
|
if (regs.framebuffer.output_merger.alpha_test.ref !=
|
||||||
static_cast<u32>(uniform_block_data.data.alphatest_ref)) {
|
static_cast<u32>(fs_uniform_block_data.data.alphatest_ref)) {
|
||||||
uniform_block_data.data.alphatest_ref = regs.framebuffer.output_merger.alpha_test.ref;
|
fs_uniform_block_data.data.alphatest_ref = regs.framebuffer.output_merger.alpha_test.ref;
|
||||||
uniform_block_data.dirty = true;
|
fs_uniform_block_data.dirty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RasterizerAccelerated::SyncCombinerColor() {
|
void RasterizerAccelerated::SyncCombinerColor() {
|
||||||
const auto combiner_color = ColorRGBA8(regs.texturing.tev_combiner_buffer_color.raw);
|
const auto combiner_color = ColorRGBA8(regs.texturing.tev_combiner_buffer_color.raw);
|
||||||
if (combiner_color != uniform_block_data.data.tev_combiner_buffer_color) {
|
if (combiner_color != fs_uniform_block_data.data.tev_combiner_buffer_color) {
|
||||||
uniform_block_data.data.tev_combiner_buffer_color = combiner_color;
|
fs_uniform_block_data.data.tev_combiner_buffer_color = combiner_color;
|
||||||
uniform_block_data.dirty = true;
|
fs_uniform_block_data.dirty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -714,51 +715,51 @@ void RasterizerAccelerated::SyncTevConstColor(
|
||||||
const size_t stage_index, const Pica::TexturingRegs::TevStageConfig& tev_stage) {
|
const size_t stage_index, const Pica::TexturingRegs::TevStageConfig& tev_stage) {
|
||||||
const auto const_color = ColorRGBA8(tev_stage.const_color);
|
const auto const_color = ColorRGBA8(tev_stage.const_color);
|
||||||
|
|
||||||
if (const_color == uniform_block_data.data.const_color[stage_index]) {
|
if (const_color == fs_uniform_block_data.data.const_color[stage_index]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uniform_block_data.data.const_color[stage_index] = const_color;
|
fs_uniform_block_data.data.const_color[stage_index] = const_color;
|
||||||
uniform_block_data.dirty = true;
|
fs_uniform_block_data.dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RasterizerAccelerated::SyncGlobalAmbient() {
|
void RasterizerAccelerated::SyncGlobalAmbient() {
|
||||||
const auto color = LightColor(regs.lighting.global_ambient);
|
const auto color = LightColor(regs.lighting.global_ambient);
|
||||||
if (color != uniform_block_data.data.lighting_global_ambient) {
|
if (color != fs_uniform_block_data.data.lighting_global_ambient) {
|
||||||
uniform_block_data.data.lighting_global_ambient = color;
|
fs_uniform_block_data.data.lighting_global_ambient = color;
|
||||||
uniform_block_data.dirty = true;
|
fs_uniform_block_data.dirty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RasterizerAccelerated::SyncLightSpecular0(int light_index) {
|
void RasterizerAccelerated::SyncLightSpecular0(int light_index) {
|
||||||
const auto color = LightColor(regs.lighting.light[light_index].specular_0);
|
const auto color = LightColor(regs.lighting.light[light_index].specular_0);
|
||||||
if (color != uniform_block_data.data.light_src[light_index].specular_0) {
|
if (color != fs_uniform_block_data.data.light_src[light_index].specular_0) {
|
||||||
uniform_block_data.data.light_src[light_index].specular_0 = color;
|
fs_uniform_block_data.data.light_src[light_index].specular_0 = color;
|
||||||
uniform_block_data.dirty = true;
|
fs_uniform_block_data.dirty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RasterizerAccelerated::SyncLightSpecular1(int light_index) {
|
void RasterizerAccelerated::SyncLightSpecular1(int light_index) {
|
||||||
const auto color = LightColor(regs.lighting.light[light_index].specular_1);
|
const auto color = LightColor(regs.lighting.light[light_index].specular_1);
|
||||||
if (color != uniform_block_data.data.light_src[light_index].specular_1) {
|
if (color != fs_uniform_block_data.data.light_src[light_index].specular_1) {
|
||||||
uniform_block_data.data.light_src[light_index].specular_1 = color;
|
fs_uniform_block_data.data.light_src[light_index].specular_1 = color;
|
||||||
uniform_block_data.dirty = true;
|
fs_uniform_block_data.dirty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RasterizerAccelerated::SyncLightDiffuse(int light_index) {
|
void RasterizerAccelerated::SyncLightDiffuse(int light_index) {
|
||||||
const auto color = LightColor(regs.lighting.light[light_index].diffuse);
|
const auto color = LightColor(regs.lighting.light[light_index].diffuse);
|
||||||
if (color != uniform_block_data.data.light_src[light_index].diffuse) {
|
if (color != fs_uniform_block_data.data.light_src[light_index].diffuse) {
|
||||||
uniform_block_data.data.light_src[light_index].diffuse = color;
|
fs_uniform_block_data.data.light_src[light_index].diffuse = color;
|
||||||
uniform_block_data.dirty = true;
|
fs_uniform_block_data.dirty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RasterizerAccelerated::SyncLightAmbient(int light_index) {
|
void RasterizerAccelerated::SyncLightAmbient(int light_index) {
|
||||||
const auto color = LightColor(regs.lighting.light[light_index].ambient);
|
const auto color = LightColor(regs.lighting.light[light_index].ambient);
|
||||||
if (color != uniform_block_data.data.light_src[light_index].ambient) {
|
if (color != fs_uniform_block_data.data.light_src[light_index].ambient) {
|
||||||
uniform_block_data.data.light_src[light_index].ambient = color;
|
fs_uniform_block_data.data.light_src[light_index].ambient = color;
|
||||||
uniform_block_data.dirty = true;
|
fs_uniform_block_data.dirty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -769,9 +770,9 @@ void RasterizerAccelerated::SyncLightPosition(int light_index) {
|
||||||
Pica::f16::FromRaw(regs.lighting.light[light_index].z).ToFloat32(),
|
Pica::f16::FromRaw(regs.lighting.light[light_index].z).ToFloat32(),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (position != uniform_block_data.data.light_src[light_index].position) {
|
if (position != fs_uniform_block_data.data.light_src[light_index].position) {
|
||||||
uniform_block_data.data.light_src[light_index].position = position;
|
fs_uniform_block_data.data.light_src[light_index].position = position;
|
||||||
uniform_block_data.dirty = true;
|
fs_uniform_block_data.dirty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -780,9 +781,9 @@ void RasterizerAccelerated::SyncLightSpotDirection(int light_index) {
|
||||||
const auto spot_direction =
|
const auto spot_direction =
|
||||||
Common::Vec3f{light.spot_x / 2047.0f, light.spot_y / 2047.0f, light.spot_z / 2047.0f};
|
Common::Vec3f{light.spot_x / 2047.0f, light.spot_y / 2047.0f, light.spot_z / 2047.0f};
|
||||||
|
|
||||||
if (spot_direction != uniform_block_data.data.light_src[light_index].spot_direction) {
|
if (spot_direction != fs_uniform_block_data.data.light_src[light_index].spot_direction) {
|
||||||
uniform_block_data.data.light_src[light_index].spot_direction = spot_direction;
|
fs_uniform_block_data.data.light_src[light_index].spot_direction = spot_direction;
|
||||||
uniform_block_data.dirty = true;
|
fs_uniform_block_data.dirty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -790,9 +791,9 @@ void RasterizerAccelerated::SyncLightDistanceAttenuationBias(int light_index) {
|
||||||
const f32 dist_atten_bias =
|
const f32 dist_atten_bias =
|
||||||
Pica::f20::FromRaw(regs.lighting.light[light_index].dist_atten_bias).ToFloat32();
|
Pica::f20::FromRaw(regs.lighting.light[light_index].dist_atten_bias).ToFloat32();
|
||||||
|
|
||||||
if (dist_atten_bias != uniform_block_data.data.light_src[light_index].dist_atten_bias) {
|
if (dist_atten_bias != fs_uniform_block_data.data.light_src[light_index].dist_atten_bias) {
|
||||||
uniform_block_data.data.light_src[light_index].dist_atten_bias = dist_atten_bias;
|
fs_uniform_block_data.data.light_src[light_index].dist_atten_bias = dist_atten_bias;
|
||||||
uniform_block_data.dirty = true;
|
fs_uniform_block_data.dirty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -800,9 +801,9 @@ void RasterizerAccelerated::SyncLightDistanceAttenuationScale(int light_index) {
|
||||||
const f32 dist_atten_scale =
|
const f32 dist_atten_scale =
|
||||||
Pica::f20::FromRaw(regs.lighting.light[light_index].dist_atten_scale).ToFloat32();
|
Pica::f20::FromRaw(regs.lighting.light[light_index].dist_atten_scale).ToFloat32();
|
||||||
|
|
||||||
if (dist_atten_scale != uniform_block_data.data.light_src[light_index].dist_atten_scale) {
|
if (dist_atten_scale != fs_uniform_block_data.data.light_src[light_index].dist_atten_scale) {
|
||||||
uniform_block_data.data.light_src[light_index].dist_atten_scale = dist_atten_scale;
|
fs_uniform_block_data.data.light_src[light_index].dist_atten_scale = dist_atten_scale;
|
||||||
uniform_block_data.dirty = true;
|
fs_uniform_block_data.dirty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -811,28 +812,28 @@ void RasterizerAccelerated::SyncShadowBias() {
|
||||||
const f32 constant = Pica::f16::FromRaw(shadow.constant).ToFloat32();
|
const f32 constant = Pica::f16::FromRaw(shadow.constant).ToFloat32();
|
||||||
const f32 linear = Pica::f16::FromRaw(shadow.linear).ToFloat32();
|
const f32 linear = Pica::f16::FromRaw(shadow.linear).ToFloat32();
|
||||||
|
|
||||||
if (constant != uniform_block_data.data.shadow_bias_constant ||
|
if (constant != fs_uniform_block_data.data.shadow_bias_constant ||
|
||||||
linear != uniform_block_data.data.shadow_bias_linear) {
|
linear != fs_uniform_block_data.data.shadow_bias_linear) {
|
||||||
uniform_block_data.data.shadow_bias_constant = constant;
|
fs_uniform_block_data.data.shadow_bias_constant = constant;
|
||||||
uniform_block_data.data.shadow_bias_linear = linear;
|
fs_uniform_block_data.data.shadow_bias_linear = linear;
|
||||||
uniform_block_data.dirty = true;
|
fs_uniform_block_data.dirty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RasterizerAccelerated::SyncShadowTextureBias() {
|
void RasterizerAccelerated::SyncShadowTextureBias() {
|
||||||
const s32 bias = regs.texturing.shadow.bias << 1;
|
const s32 bias = regs.texturing.shadow.bias << 1;
|
||||||
if (bias != uniform_block_data.data.shadow_texture_bias) {
|
if (bias != fs_uniform_block_data.data.shadow_texture_bias) {
|
||||||
uniform_block_data.data.shadow_texture_bias = bias;
|
fs_uniform_block_data.data.shadow_texture_bias = bias;
|
||||||
uniform_block_data.dirty = true;
|
fs_uniform_block_data.dirty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RasterizerAccelerated::SyncTextureLodBias(int tex_index) {
|
void RasterizerAccelerated::SyncTextureLodBias(int tex_index) {
|
||||||
const auto pica_textures = regs.texturing.GetTextures();
|
const auto pica_textures = regs.texturing.GetTextures();
|
||||||
const f32 bias = pica_textures[tex_index].config.lod.bias / 256.0f;
|
const f32 bias = pica_textures[tex_index].config.lod.bias / 256.0f;
|
||||||
if (bias != uniform_block_data.data.tex_lod_bias[tex_index]) {
|
if (bias != fs_uniform_block_data.data.tex_lod_bias[tex_index]) {
|
||||||
uniform_block_data.data.tex_lod_bias[tex_index] = bias;
|
fs_uniform_block_data.data.tex_lod_bias[tex_index] = bias;
|
||||||
uniform_block_data.dirty = true;
|
fs_uniform_block_data.dirty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -840,19 +841,22 @@ void RasterizerAccelerated::SyncTextureBorderColor(int tex_index) {
|
||||||
const auto pica_textures = regs.texturing.GetTextures();
|
const auto pica_textures = regs.texturing.GetTextures();
|
||||||
const auto params = pica_textures[tex_index].config;
|
const auto params = pica_textures[tex_index].config;
|
||||||
const Common::Vec4f border_color = ColorRGBA8(params.border_color.raw);
|
const Common::Vec4f border_color = ColorRGBA8(params.border_color.raw);
|
||||||
if (border_color != uniform_block_data.data.tex_border_color[tex_index]) {
|
if (border_color != fs_uniform_block_data.data.tex_border_color[tex_index]) {
|
||||||
uniform_block_data.data.tex_border_color[tex_index] = border_color;
|
fs_uniform_block_data.data.tex_border_color[tex_index] = border_color;
|
||||||
uniform_block_data.dirty = true;
|
fs_uniform_block_data.dirty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RasterizerAccelerated::SyncClipCoef() {
|
void RasterizerAccelerated::SyncClipPlane() {
|
||||||
|
const bool enable_clip1 = regs.rasterizer.clip_enable != 0;
|
||||||
const auto raw_clip_coef = regs.rasterizer.GetClipCoef();
|
const auto raw_clip_coef = regs.rasterizer.GetClipCoef();
|
||||||
const Common::Vec4f new_clip_coef = {raw_clip_coef.x.ToFloat32(), raw_clip_coef.y.ToFloat32(),
|
const Common::Vec4f new_clip_coef = {raw_clip_coef.x.ToFloat32(), raw_clip_coef.y.ToFloat32(),
|
||||||
raw_clip_coef.z.ToFloat32(), raw_clip_coef.w.ToFloat32()};
|
raw_clip_coef.z.ToFloat32(), raw_clip_coef.w.ToFloat32()};
|
||||||
if (new_clip_coef != uniform_block_data.data.clip_coef) {
|
if (enable_clip1 != vs_uniform_block_data.data.enable_clip1 ||
|
||||||
uniform_block_data.data.clip_coef = new_clip_coef;
|
new_clip_coef != vs_uniform_block_data.data.clip_coef) {
|
||||||
uniform_block_data.dirty = true;
|
vs_uniform_block_data.data.enable_clip1 = enable_clip1;
|
||||||
|
vs_uniform_block_data.data.clip_coef = new_clip_coef;
|
||||||
|
vs_uniform_block_data.dirty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
#include "common/vector_math.h"
|
#include "common/vector_math.h"
|
||||||
#include "video_core/rasterizer_interface.h"
|
#include "video_core/rasterizer_interface.h"
|
||||||
#include "video_core/regs_texturing.h"
|
#include "video_core/regs_texturing.h"
|
||||||
#include "video_core/shader/shader_uniforms.h"
|
#include "video_core/shader/generator/shader_uniforms.h"
|
||||||
|
|
||||||
namespace Memory {
|
namespace Memory {
|
||||||
class MemorySystem;
|
class MemorySystem;
|
||||||
|
@ -100,13 +100,19 @@ protected:
|
||||||
/// Syncs the texture border color to match the PICA registers
|
/// Syncs the texture border color to match the PICA registers
|
||||||
void SyncTextureBorderColor(int tex_index);
|
void SyncTextureBorderColor(int tex_index);
|
||||||
|
|
||||||
/// Syncs the clip coefficients to match the PICA register
|
/// Syncs the clip plane state to match the PICA register
|
||||||
void SyncClipCoef();
|
void SyncClipPlane();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/// Structure that keeps tracks of the uniform state
|
/// Structure that keeps tracks of the vertex shader uniform state
|
||||||
struct UniformBlockData {
|
struct VSUniformBlockData {
|
||||||
Pica::Shader::UniformData data{};
|
Pica::Shader::Generator::VSUniformData data{};
|
||||||
|
bool dirty = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Structure that keeps tracks of the fragment shader uniform state
|
||||||
|
struct FSUniformBlockData {
|
||||||
|
Pica::Shader::Generator::FSUniformData data{};
|
||||||
std::array<bool, Pica::LightingRegs::NumLightingSampler> lighting_lut_dirty{};
|
std::array<bool, Pica::LightingRegs::NumLightingSampler> lighting_lut_dirty{};
|
||||||
bool lighting_lut_dirty_any = true;
|
bool lighting_lut_dirty_any = true;
|
||||||
bool fog_lut_dirty = true;
|
bool fog_lut_dirty = true;
|
||||||
|
@ -149,7 +155,8 @@ protected:
|
||||||
std::vector<HardwareVertex> vertex_batch;
|
std::vector<HardwareVertex> vertex_batch;
|
||||||
bool shader_dirty = true;
|
bool shader_dirty = true;
|
||||||
|
|
||||||
UniformBlockData uniform_block_data{};
|
VSUniformBlockData vs_uniform_block_data{};
|
||||||
|
FSUniformBlockData fs_uniform_block_data{};
|
||||||
std::array<std::array<Common::Vec2f, 256>, Pica::LightingRegs::NumLightingSampler>
|
std::array<std::array<Common::Vec2f, 256>, Pica::LightingRegs::NumLightingSampler>
|
||||||
lighting_lut_data{};
|
lighting_lut_data{};
|
||||||
std::array<Common::Vec2f, 128> fog_lut_data{};
|
std::array<Common::Vec2f, 128> fog_lut_data{};
|
||||||
|
|
|
@ -168,7 +168,7 @@ void Driver::CheckExtensionSupport() {
|
||||||
arb_clear_texture = GLAD_GL_ARB_clear_texture;
|
arb_clear_texture = GLAD_GL_ARB_clear_texture;
|
||||||
arb_get_texture_sub_image = GLAD_GL_ARB_get_texture_sub_image;
|
arb_get_texture_sub_image = GLAD_GL_ARB_get_texture_sub_image;
|
||||||
arb_texture_compression_bptc = GLAD_GL_ARB_texture_compression_bptc;
|
arb_texture_compression_bptc = GLAD_GL_ARB_texture_compression_bptc;
|
||||||
ext_clip_cull_distance = GLAD_GL_EXT_clip_cull_distance;
|
clip_cull_distance = !is_gles || GLAD_GL_EXT_clip_cull_distance;
|
||||||
ext_texture_compression_s3tc = GLAD_GL_EXT_texture_compression_s3tc;
|
ext_texture_compression_s3tc = GLAD_GL_EXT_texture_compression_s3tc;
|
||||||
shader_framebuffer_fetch =
|
shader_framebuffer_fetch =
|
||||||
GLAD_GL_EXT_shader_framebuffer_fetch || GLAD_GL_ARM_shader_framebuffer_fetch;
|
GLAD_GL_EXT_shader_framebuffer_fetch || GLAD_GL_ARM_shader_framebuffer_fetch;
|
||||||
|
|
|
@ -100,9 +100,9 @@ public:
|
||||||
return arb_get_texture_sub_image;
|
return arb_get_texture_sub_image;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if the implementation supports EXT_clip_cull_distance
|
/// Returns true if the implementation supports shader-defined clipping planes
|
||||||
bool HasExtClipCullDistance() const {
|
bool HasClipCullDistance() const {
|
||||||
return ext_clip_cull_distance;
|
return clip_cull_distance;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if the implementation supports (EXT/ARM)_shader_framebuffer_fetch
|
/// Returns true if the implementation supports (EXT/ARM)_shader_framebuffer_fetch
|
||||||
|
@ -132,7 +132,7 @@ private:
|
||||||
bool arb_buffer_storage{};
|
bool arb_buffer_storage{};
|
||||||
bool arb_clear_texture{};
|
bool arb_clear_texture{};
|
||||||
bool arb_get_texture_sub_image{};
|
bool arb_get_texture_sub_image{};
|
||||||
bool ext_clip_cull_distance{};
|
bool clip_cull_distance{};
|
||||||
bool ext_texture_compression_s3tc{};
|
bool ext_texture_compression_s3tc{};
|
||||||
bool arb_texture_compression_bptc{};
|
bool arb_texture_compression_bptc{};
|
||||||
bool shader_framebuffer_fetch{};
|
bool shader_framebuffer_fetch{};
|
||||||
|
|
|
@ -11,9 +11,9 @@
|
||||||
#include "video_core/regs_framebuffer.h"
|
#include "video_core/regs_framebuffer.h"
|
||||||
#include "video_core/regs_rasterizer.h"
|
#include "video_core/regs_rasterizer.h"
|
||||||
#include "video_core/renderer_opengl/gl_rasterizer.h"
|
#include "video_core/renderer_opengl/gl_rasterizer.h"
|
||||||
#include "video_core/renderer_opengl/gl_shader_gen.h"
|
|
||||||
#include "video_core/renderer_opengl/pica_to_gl.h"
|
#include "video_core/renderer_opengl/pica_to_gl.h"
|
||||||
#include "video_core/renderer_opengl/renderer_opengl.h"
|
#include "video_core/renderer_opengl/renderer_opengl.h"
|
||||||
|
#include "video_core/shader/generator/glsl_shader_gen.h"
|
||||||
#include "video_core/texture/texture_decode.h"
|
#include "video_core/texture/texture_decode.h"
|
||||||
#include "video_core/video_core.h"
|
#include "video_core/video_core.h"
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@ MICROPROFILE_DEFINE(OpenGL_Drawing, "OpenGL", "Drawing", MP_RGB(128, 128, 192));
|
||||||
MICROPROFILE_DEFINE(OpenGL_Display, "OpenGL", "Display", MP_RGB(128, 128, 192));
|
MICROPROFILE_DEFINE(OpenGL_Display, "OpenGL", "Display", MP_RGB(128, 128, 192));
|
||||||
|
|
||||||
using VideoCore::SurfaceType;
|
using VideoCore::SurfaceType;
|
||||||
|
using namespace Pica::Shader::Generator;
|
||||||
|
|
||||||
constexpr std::size_t VERTEX_BUFFER_SIZE = 16 * 1024 * 1024;
|
constexpr std::size_t VERTEX_BUFFER_SIZE = 16 * 1024 * 1024;
|
||||||
constexpr std::size_t INDEX_BUFFER_SIZE = 2 * 1024 * 1024;
|
constexpr std::size_t INDEX_BUFFER_SIZE = 2 * 1024 * 1024;
|
||||||
|
@ -95,10 +96,12 @@ RasterizerOpenGL::RasterizerOpenGL(Memory::MemorySystem& memory,
|
||||||
hw_vao.Create();
|
hw_vao.Create();
|
||||||
|
|
||||||
glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &uniform_buffer_alignment);
|
glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &uniform_buffer_alignment);
|
||||||
|
uniform_size_aligned_vs_pica =
|
||||||
|
Common::AlignUp<std::size_t>(sizeof(VSPicaUniformData), uniform_buffer_alignment);
|
||||||
uniform_size_aligned_vs =
|
uniform_size_aligned_vs =
|
||||||
Common::AlignUp<std::size_t>(sizeof(Pica::Shader::VSUniformData), uniform_buffer_alignment);
|
Common::AlignUp<std::size_t>(sizeof(VSUniformData), uniform_buffer_alignment);
|
||||||
uniform_size_aligned_fs =
|
uniform_size_aligned_fs =
|
||||||
Common::AlignUp<std::size_t>(sizeof(Pica::Shader::UniformData), uniform_buffer_alignment);
|
Common::AlignUp<std::size_t>(sizeof(FSUniformData), uniform_buffer_alignment);
|
||||||
|
|
||||||
// Set vertex attributes for software shader path
|
// Set vertex attributes for software shader path
|
||||||
state.draw.vertex_array = sw_vao.handle;
|
state.draw.vertex_array = sw_vao.handle;
|
||||||
|
@ -405,16 +408,16 @@ bool RasterizerOpenGL::Draw(bool accelerate, bool is_indexed) {
|
||||||
|
|
||||||
// Update scissor uniforms
|
// Update scissor uniforms
|
||||||
const auto [scissor_x1, scissor_y2, scissor_x2, scissor_y1] = fb_helper.Scissor();
|
const auto [scissor_x1, scissor_y2, scissor_x2, scissor_y1] = fb_helper.Scissor();
|
||||||
if (uniform_block_data.data.scissor_x1 != scissor_x1 ||
|
if (fs_uniform_block_data.data.scissor_x1 != scissor_x1 ||
|
||||||
uniform_block_data.data.scissor_x2 != scissor_x2 ||
|
fs_uniform_block_data.data.scissor_x2 != scissor_x2 ||
|
||||||
uniform_block_data.data.scissor_y1 != scissor_y1 ||
|
fs_uniform_block_data.data.scissor_y1 != scissor_y1 ||
|
||||||
uniform_block_data.data.scissor_y2 != scissor_y2) {
|
fs_uniform_block_data.data.scissor_y2 != scissor_y2) {
|
||||||
|
|
||||||
uniform_block_data.data.scissor_x1 = scissor_x1;
|
fs_uniform_block_data.data.scissor_x1 = scissor_x1;
|
||||||
uniform_block_data.data.scissor_x2 = scissor_x2;
|
fs_uniform_block_data.data.scissor_x2 = scissor_x2;
|
||||||
uniform_block_data.data.scissor_y1 = scissor_y1;
|
fs_uniform_block_data.data.scissor_y1 = scissor_y1;
|
||||||
uniform_block_data.data.scissor_y2 = scissor_y2;
|
fs_uniform_block_data.data.scissor_y2 = scissor_y2;
|
||||||
uniform_block_data.dirty = true;
|
fs_uniform_block_data.dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sync and bind the texture surfaces
|
// Sync and bind the texture surfaces
|
||||||
|
@ -831,9 +834,9 @@ void RasterizerOpenGL::SyncBlendColor() {
|
||||||
state.blend.color.blue = blend_color[2];
|
state.blend.color.blue = blend_color[2];
|
||||||
state.blend.color.alpha = blend_color[3];
|
state.blend.color.alpha = blend_color[3];
|
||||||
|
|
||||||
if (blend_color != uniform_block_data.data.blend_color) {
|
if (blend_color != fs_uniform_block_data.data.blend_color) {
|
||||||
uniform_block_data.data.blend_color = blend_color;
|
fs_uniform_block_data.data.blend_color = blend_color;
|
||||||
uniform_block_data.dirty = true;
|
fs_uniform_block_data.dirty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -921,7 +924,7 @@ void RasterizerOpenGL::SyncAndUploadLUTsLF() {
|
||||||
sizeof(Common::Vec2f) * 256 * Pica::LightingRegs::NumLightingSampler +
|
sizeof(Common::Vec2f) * 256 * Pica::LightingRegs::NumLightingSampler +
|
||||||
sizeof(Common::Vec2f) * 128; // fog
|
sizeof(Common::Vec2f) * 128; // fog
|
||||||
|
|
||||||
if (!uniform_block_data.lighting_lut_dirty_any && !uniform_block_data.fog_lut_dirty) {
|
if (!fs_uniform_block_data.lighting_lut_dirty_any && !fs_uniform_block_data.fog_lut_dirty) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -931,9 +934,9 @@ void RasterizerOpenGL::SyncAndUploadLUTsLF() {
|
||||||
texture_lf_buffer.Map(max_size, sizeof(Common::Vec4f));
|
texture_lf_buffer.Map(max_size, sizeof(Common::Vec4f));
|
||||||
|
|
||||||
// Sync the lighting luts
|
// Sync the lighting luts
|
||||||
if (uniform_block_data.lighting_lut_dirty_any || invalidate) {
|
if (fs_uniform_block_data.lighting_lut_dirty_any || invalidate) {
|
||||||
for (unsigned index = 0; index < uniform_block_data.lighting_lut_dirty.size(); index++) {
|
for (unsigned index = 0; index < fs_uniform_block_data.lighting_lut_dirty.size(); index++) {
|
||||||
if (uniform_block_data.lighting_lut_dirty[index] || invalidate) {
|
if (fs_uniform_block_data.lighting_lut_dirty[index] || invalidate) {
|
||||||
std::array<Common::Vec2f, 256> new_data;
|
std::array<Common::Vec2f, 256> new_data;
|
||||||
const auto& source_lut = Pica::g_state.lighting.luts[index];
|
const auto& source_lut = Pica::g_state.lighting.luts[index];
|
||||||
std::transform(source_lut.begin(), source_lut.end(), new_data.begin(),
|
std::transform(source_lut.begin(), source_lut.end(), new_data.begin(),
|
||||||
|
@ -945,19 +948,19 @@ void RasterizerOpenGL::SyncAndUploadLUTsLF() {
|
||||||
lighting_lut_data[index] = new_data;
|
lighting_lut_data[index] = new_data;
|
||||||
std::memcpy(buffer + bytes_used, new_data.data(),
|
std::memcpy(buffer + bytes_used, new_data.data(),
|
||||||
new_data.size() * sizeof(Common::Vec2f));
|
new_data.size() * sizeof(Common::Vec2f));
|
||||||
uniform_block_data.data.lighting_lut_offset[index / 4][index % 4] =
|
fs_uniform_block_data.data.lighting_lut_offset[index / 4][index % 4] =
|
||||||
static_cast<GLint>((offset + bytes_used) / sizeof(Common::Vec2f));
|
static_cast<GLint>((offset + bytes_used) / sizeof(Common::Vec2f));
|
||||||
uniform_block_data.dirty = true;
|
fs_uniform_block_data.dirty = true;
|
||||||
bytes_used += new_data.size() * sizeof(Common::Vec2f);
|
bytes_used += new_data.size() * sizeof(Common::Vec2f);
|
||||||
}
|
}
|
||||||
uniform_block_data.lighting_lut_dirty[index] = false;
|
fs_uniform_block_data.lighting_lut_dirty[index] = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
uniform_block_data.lighting_lut_dirty_any = false;
|
fs_uniform_block_data.lighting_lut_dirty_any = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sync the fog lut
|
// Sync the fog lut
|
||||||
if (uniform_block_data.fog_lut_dirty || invalidate) {
|
if (fs_uniform_block_data.fog_lut_dirty || invalidate) {
|
||||||
std::array<Common::Vec2f, 128> new_data;
|
std::array<Common::Vec2f, 128> new_data;
|
||||||
|
|
||||||
std::transform(Pica::g_state.fog.lut.begin(), Pica::g_state.fog.lut.end(), new_data.begin(),
|
std::transform(Pica::g_state.fog.lut.begin(), Pica::g_state.fog.lut.end(), new_data.begin(),
|
||||||
|
@ -969,12 +972,12 @@ void RasterizerOpenGL::SyncAndUploadLUTsLF() {
|
||||||
fog_lut_data = new_data;
|
fog_lut_data = new_data;
|
||||||
std::memcpy(buffer + bytes_used, new_data.data(),
|
std::memcpy(buffer + bytes_used, new_data.data(),
|
||||||
new_data.size() * sizeof(Common::Vec2f));
|
new_data.size() * sizeof(Common::Vec2f));
|
||||||
uniform_block_data.data.fog_lut_offset =
|
fs_uniform_block_data.data.fog_lut_offset =
|
||||||
static_cast<int>((offset + bytes_used) / sizeof(Common::Vec2f));
|
static_cast<int>((offset + bytes_used) / sizeof(Common::Vec2f));
|
||||||
uniform_block_data.dirty = true;
|
fs_uniform_block_data.dirty = true;
|
||||||
bytes_used += new_data.size() * sizeof(Common::Vec2f);
|
bytes_used += new_data.size() * sizeof(Common::Vec2f);
|
||||||
}
|
}
|
||||||
uniform_block_data.fog_lut_dirty = false;
|
fs_uniform_block_data.fog_lut_dirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
texture_lf_buffer.Unmap(bytes_used);
|
texture_lf_buffer.Unmap(bytes_used);
|
||||||
|
@ -986,10 +989,10 @@ void RasterizerOpenGL::SyncAndUploadLUTs() {
|
||||||
sizeof(Common::Vec4f) * 256 + // proctex
|
sizeof(Common::Vec4f) * 256 + // proctex
|
||||||
sizeof(Common::Vec4f) * 256; // proctex diff
|
sizeof(Common::Vec4f) * 256; // proctex diff
|
||||||
|
|
||||||
if (!uniform_block_data.proctex_noise_lut_dirty &&
|
if (!fs_uniform_block_data.proctex_noise_lut_dirty &&
|
||||||
!uniform_block_data.proctex_color_map_dirty &&
|
!fs_uniform_block_data.proctex_color_map_dirty &&
|
||||||
!uniform_block_data.proctex_alpha_map_dirty && !uniform_block_data.proctex_lut_dirty &&
|
!fs_uniform_block_data.proctex_alpha_map_dirty &&
|
||||||
!uniform_block_data.proctex_diff_lut_dirty) {
|
!fs_uniform_block_data.proctex_lut_dirty && !fs_uniform_block_data.proctex_diff_lut_dirty) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1012,34 +1015,34 @@ void RasterizerOpenGL::SyncAndUploadLUTs() {
|
||||||
std::memcpy(buffer + bytes_used, new_data.data(),
|
std::memcpy(buffer + bytes_used, new_data.data(),
|
||||||
new_data.size() * sizeof(Common::Vec2f));
|
new_data.size() * sizeof(Common::Vec2f));
|
||||||
lut_offset = static_cast<GLint>((offset + bytes_used) / sizeof(Common::Vec2f));
|
lut_offset = static_cast<GLint>((offset + bytes_used) / sizeof(Common::Vec2f));
|
||||||
uniform_block_data.dirty = true;
|
fs_uniform_block_data.dirty = true;
|
||||||
bytes_used += new_data.size() * sizeof(Common::Vec2f);
|
bytes_used += new_data.size() * sizeof(Common::Vec2f);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Sync the proctex noise lut
|
// Sync the proctex noise lut
|
||||||
if (uniform_block_data.proctex_noise_lut_dirty || invalidate) {
|
if (fs_uniform_block_data.proctex_noise_lut_dirty || invalidate) {
|
||||||
sync_proc_tex_value_lut(Pica::g_state.proctex.noise_table, proctex_noise_lut_data,
|
sync_proc_tex_value_lut(Pica::g_state.proctex.noise_table, proctex_noise_lut_data,
|
||||||
uniform_block_data.data.proctex_noise_lut_offset);
|
fs_uniform_block_data.data.proctex_noise_lut_offset);
|
||||||
uniform_block_data.proctex_noise_lut_dirty = false;
|
fs_uniform_block_data.proctex_noise_lut_dirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sync the proctex color map
|
// Sync the proctex color map
|
||||||
if (uniform_block_data.proctex_color_map_dirty || invalidate) {
|
if (fs_uniform_block_data.proctex_color_map_dirty || invalidate) {
|
||||||
sync_proc_tex_value_lut(Pica::g_state.proctex.color_map_table, proctex_color_map_data,
|
sync_proc_tex_value_lut(Pica::g_state.proctex.color_map_table, proctex_color_map_data,
|
||||||
uniform_block_data.data.proctex_color_map_offset);
|
fs_uniform_block_data.data.proctex_color_map_offset);
|
||||||
uniform_block_data.proctex_color_map_dirty = false;
|
fs_uniform_block_data.proctex_color_map_dirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sync the proctex alpha map
|
// Sync the proctex alpha map
|
||||||
if (uniform_block_data.proctex_alpha_map_dirty || invalidate) {
|
if (fs_uniform_block_data.proctex_alpha_map_dirty || invalidate) {
|
||||||
sync_proc_tex_value_lut(Pica::g_state.proctex.alpha_map_table, proctex_alpha_map_data,
|
sync_proc_tex_value_lut(Pica::g_state.proctex.alpha_map_table, proctex_alpha_map_data,
|
||||||
uniform_block_data.data.proctex_alpha_map_offset);
|
fs_uniform_block_data.data.proctex_alpha_map_offset);
|
||||||
uniform_block_data.proctex_alpha_map_dirty = false;
|
fs_uniform_block_data.proctex_alpha_map_dirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sync the proctex lut
|
// Sync the proctex lut
|
||||||
if (uniform_block_data.proctex_lut_dirty || invalidate) {
|
if (fs_uniform_block_data.proctex_lut_dirty || invalidate) {
|
||||||
std::array<Common::Vec4f, 256> new_data;
|
std::array<Common::Vec4f, 256> new_data;
|
||||||
|
|
||||||
std::transform(Pica::g_state.proctex.color_table.begin(),
|
std::transform(Pica::g_state.proctex.color_table.begin(),
|
||||||
|
@ -1053,16 +1056,16 @@ void RasterizerOpenGL::SyncAndUploadLUTs() {
|
||||||
proctex_lut_data = new_data;
|
proctex_lut_data = new_data;
|
||||||
std::memcpy(buffer + bytes_used, new_data.data(),
|
std::memcpy(buffer + bytes_used, new_data.data(),
|
||||||
new_data.size() * sizeof(Common::Vec4f));
|
new_data.size() * sizeof(Common::Vec4f));
|
||||||
uniform_block_data.data.proctex_lut_offset =
|
fs_uniform_block_data.data.proctex_lut_offset =
|
||||||
static_cast<GLint>((offset + bytes_used) / sizeof(Common::Vec4f));
|
static_cast<GLint>((offset + bytes_used) / sizeof(Common::Vec4f));
|
||||||
uniform_block_data.dirty = true;
|
fs_uniform_block_data.dirty = true;
|
||||||
bytes_used += new_data.size() * sizeof(Common::Vec4f);
|
bytes_used += new_data.size() * sizeof(Common::Vec4f);
|
||||||
}
|
}
|
||||||
uniform_block_data.proctex_lut_dirty = false;
|
fs_uniform_block_data.proctex_lut_dirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sync the proctex difference lut
|
// Sync the proctex difference lut
|
||||||
if (uniform_block_data.proctex_diff_lut_dirty || invalidate) {
|
if (fs_uniform_block_data.proctex_diff_lut_dirty || invalidate) {
|
||||||
std::array<Common::Vec4f, 256> new_data;
|
std::array<Common::Vec4f, 256> new_data;
|
||||||
|
|
||||||
std::transform(Pica::g_state.proctex.color_diff_table.begin(),
|
std::transform(Pica::g_state.proctex.color_diff_table.begin(),
|
||||||
|
@ -1076,12 +1079,12 @@ void RasterizerOpenGL::SyncAndUploadLUTs() {
|
||||||
proctex_diff_lut_data = new_data;
|
proctex_diff_lut_data = new_data;
|
||||||
std::memcpy(buffer + bytes_used, new_data.data(),
|
std::memcpy(buffer + bytes_used, new_data.data(),
|
||||||
new_data.size() * sizeof(Common::Vec4f));
|
new_data.size() * sizeof(Common::Vec4f));
|
||||||
uniform_block_data.data.proctex_diff_lut_offset =
|
fs_uniform_block_data.data.proctex_diff_lut_offset =
|
||||||
static_cast<GLint>((offset + bytes_used) / sizeof(Common::Vec4f));
|
static_cast<GLint>((offset + bytes_used) / sizeof(Common::Vec4f));
|
||||||
uniform_block_data.dirty = true;
|
fs_uniform_block_data.dirty = true;
|
||||||
bytes_used += new_data.size() * sizeof(Common::Vec4f);
|
bytes_used += new_data.size() * sizeof(Common::Vec4f);
|
||||||
}
|
}
|
||||||
uniform_block_data.proctex_diff_lut_dirty = false;
|
fs_uniform_block_data.proctex_diff_lut_dirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
texture_buffer.Unmap(bytes_used);
|
texture_buffer.Unmap(bytes_used);
|
||||||
|
@ -1092,38 +1095,47 @@ void RasterizerOpenGL::UploadUniforms(bool accelerate_draw) {
|
||||||
state.draw.uniform_buffer = uniform_buffer.GetHandle();
|
state.draw.uniform_buffer = uniform_buffer.GetHandle();
|
||||||
state.Apply();
|
state.Apply();
|
||||||
|
|
||||||
const bool sync_vs = accelerate_draw;
|
const bool sync_vs_pica = accelerate_draw;
|
||||||
const bool sync_fs = uniform_block_data.dirty;
|
const bool sync_vs = vs_uniform_block_data.dirty;
|
||||||
if (!sync_vs && !sync_fs) {
|
const bool sync_fs = fs_uniform_block_data.dirty;
|
||||||
|
if (!sync_vs_pica && !sync_vs && !sync_fs) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t uniform_size = uniform_size_aligned_vs + uniform_size_aligned_fs;
|
std::size_t uniform_size =
|
||||||
|
uniform_size_aligned_vs_pica + uniform_size_aligned_vs + uniform_size_aligned_fs;
|
||||||
std::size_t used_bytes = 0;
|
std::size_t used_bytes = 0;
|
||||||
|
|
||||||
const auto [uniforms, offset, invalidate] =
|
const auto [uniforms, offset, invalidate] =
|
||||||
uniform_buffer.Map(uniform_size, uniform_buffer_alignment);
|
uniform_buffer.Map(uniform_size, uniform_buffer_alignment);
|
||||||
|
|
||||||
if (sync_vs) {
|
if (sync_vs || invalidate) {
|
||||||
Pica::Shader::VSUniformData vs_uniforms;
|
std::memcpy(uniforms + used_bytes, &vs_uniform_block_data.data,
|
||||||
vs_uniforms.uniforms.SetFromRegs(regs.vs, Pica::g_state.vs);
|
sizeof(vs_uniform_block_data.data));
|
||||||
std::memcpy(uniforms + used_bytes, &vs_uniforms, sizeof(vs_uniforms));
|
glBindBufferRange(GL_UNIFORM_BUFFER, UniformBindings::VSData, uniform_buffer.GetHandle(),
|
||||||
glBindBufferRange(GL_UNIFORM_BUFFER, static_cast<GLuint>(Pica::Shader::UniformBindings::VS),
|
offset + used_bytes, sizeof(vs_uniform_block_data.data));
|
||||||
uniform_buffer.GetHandle(), offset + used_bytes,
|
vs_uniform_block_data.dirty = false;
|
||||||
sizeof(Pica::Shader::VSUniformData));
|
|
||||||
used_bytes += uniform_size_aligned_vs;
|
used_bytes += uniform_size_aligned_vs;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sync_fs || invalidate) {
|
if (sync_fs || invalidate) {
|
||||||
std::memcpy(uniforms + used_bytes, &uniform_block_data.data,
|
std::memcpy(uniforms + used_bytes, &fs_uniform_block_data.data,
|
||||||
sizeof(Pica::Shader::UniformData));
|
sizeof(fs_uniform_block_data.data));
|
||||||
glBindBufferRange(
|
glBindBufferRange(GL_UNIFORM_BUFFER, UniformBindings::FSData, uniform_buffer.GetHandle(),
|
||||||
GL_UNIFORM_BUFFER, static_cast<GLuint>(Pica::Shader::UniformBindings::Common),
|
offset + used_bytes, sizeof(fs_uniform_block_data.data));
|
||||||
uniform_buffer.GetHandle(), offset + used_bytes, sizeof(Pica::Shader::UniformData));
|
fs_uniform_block_data.dirty = false;
|
||||||
uniform_block_data.dirty = false;
|
|
||||||
used_bytes += uniform_size_aligned_fs;
|
used_bytes += uniform_size_aligned_fs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sync_vs_pica) {
|
||||||
|
VSPicaUniformData vs_uniforms;
|
||||||
|
vs_uniforms.uniforms.SetFromRegs(regs.vs, Pica::g_state.vs);
|
||||||
|
std::memcpy(uniforms + used_bytes, &vs_uniforms, sizeof(vs_uniforms));
|
||||||
|
glBindBufferRange(GL_UNIFORM_BUFFER, UniformBindings::VSPicaData,
|
||||||
|
uniform_buffer.GetHandle(), offset + used_bytes, sizeof(vs_uniforms));
|
||||||
|
used_bytes += uniform_size_aligned_vs_pica;
|
||||||
|
}
|
||||||
|
|
||||||
uniform_buffer.Unmap(used_bytes);
|
uniform_buffer.Unmap(used_bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -148,6 +148,7 @@ private:
|
||||||
OGLStreamBuffer texture_buffer;
|
OGLStreamBuffer texture_buffer;
|
||||||
OGLStreamBuffer texture_lf_buffer;
|
OGLStreamBuffer texture_lf_buffer;
|
||||||
GLint uniform_buffer_alignment;
|
GLint uniform_buffer_alignment;
|
||||||
|
std::size_t uniform_size_aligned_vs_pica;
|
||||||
std::size_t uniform_size_aligned_vs;
|
std::size_t uniform_size_aligned_vs;
|
||||||
std::size_t uniform_size_aligned_fs;
|
std::size_t uniform_size_aligned_fs;
|
||||||
|
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
// Copyright 2017 Citra Emulator Project
|
|
||||||
// Licensed under GPLv2 or any later version
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <functional>
|
|
||||||
#include <optional>
|
|
||||||
#include <string>
|
|
||||||
#include "common/common_types.h"
|
|
||||||
#include "video_core/shader/shader.h"
|
|
||||||
|
|
||||||
namespace OpenGL::ShaderDecompiler {
|
|
||||||
|
|
||||||
using RegGetter = std::function<std::string(u32)>;
|
|
||||||
|
|
||||||
struct ProgramResult {
|
|
||||||
std::string code;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::string GetCommonDeclarations();
|
|
||||||
|
|
||||||
std::optional<ProgramResult> DecompileProgram(const Pica::Shader::ProgramCode& program_code,
|
|
||||||
const Pica::Shader::SwizzleData& swizzle_data,
|
|
||||||
u32 main_offset, const RegGetter& inputreg_getter,
|
|
||||||
const RegGetter& outputreg_getter, bool sanitize_mul);
|
|
||||||
|
|
||||||
} // namespace OpenGL::ShaderDecompiler
|
|
|
@ -297,35 +297,33 @@ std::optional<ShaderDiskCacheDecompiled> ShaderDiskCache::LoadDecompiledEntry()
|
||||||
}
|
}
|
||||||
|
|
||||||
ShaderDiskCacheDecompiled entry;
|
ShaderDiskCacheDecompiled entry;
|
||||||
entry.result.code = std::move(code);
|
entry.code = std::move(code);
|
||||||
entry.sanitize_mul = sanitize_mul;
|
entry.sanitize_mul = sanitize_mul;
|
||||||
|
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShaderDiskCache::SaveDecompiledToFile(FileUtil::IOFile& file, u64 unique_identifier,
|
void ShaderDiskCache::SaveDecompiledToFile(FileUtil::IOFile& file, u64 unique_identifier,
|
||||||
const ShaderDecompiler::ProgramResult& result,
|
const std::string& code, bool sanitize_mul) {
|
||||||
bool sanitize_mul) {
|
|
||||||
if (!IsUsable())
|
if (!IsUsable())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (file.WriteObject(static_cast<u32>(PrecompiledEntryKind::Decompiled)) != 1 ||
|
if (file.WriteObject(static_cast<u32>(PrecompiledEntryKind::Decompiled)) != 1 ||
|
||||||
file.WriteObject(unique_identifier) != 1 || file.WriteObject(sanitize_mul) != 1 ||
|
file.WriteObject(unique_identifier) != 1 || file.WriteObject(sanitize_mul) != 1 ||
|
||||||
file.WriteObject(static_cast<u32>(result.code.size())) != 1 ||
|
file.WriteObject(static_cast<u32>(code.size())) != 1 ||
|
||||||
file.WriteArray(result.code.data(), result.code.size()) != result.code.size()) {
|
file.WriteArray(code.data(), code.size()) != code.size()) {
|
||||||
LOG_ERROR(Render_OpenGL, "Failed to save decompiled cache entry - removing");
|
LOG_ERROR(Render_OpenGL, "Failed to save decompiled cache entry - removing");
|
||||||
file.Close();
|
file.Close();
|
||||||
InvalidatePrecompiled();
|
InvalidatePrecompiled();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ShaderDiskCache::SaveDecompiledToCache(u64 unique_identifier,
|
bool ShaderDiskCache::SaveDecompiledToCache(u64 unique_identifier, const std::string& code,
|
||||||
const ShaderDecompiler::ProgramResult& result,
|
|
||||||
bool sanitize_mul) {
|
bool sanitize_mul) {
|
||||||
if (!SaveObjectToPrecompiled(static_cast<u32>(PrecompiledEntryKind::Decompiled)) ||
|
if (!SaveObjectToPrecompiled(static_cast<u32>(PrecompiledEntryKind::Decompiled)) ||
|
||||||
!SaveObjectToPrecompiled(unique_identifier) || !SaveObjectToPrecompiled(sanitize_mul) ||
|
!SaveObjectToPrecompiled(unique_identifier) || !SaveObjectToPrecompiled(sanitize_mul) ||
|
||||||
!SaveObjectToPrecompiled(static_cast<u32>(result.code.size())) ||
|
!SaveObjectToPrecompiled(static_cast<u32>(code.size())) ||
|
||||||
!SaveArrayToPrecompiled(result.code.data(), result.code.size())) {
|
!SaveArrayToPrecompiled(code.data(), code.size())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -374,8 +372,7 @@ void ShaderDiskCache::SaveRaw(const ShaderDiskCacheRaw& entry) {
|
||||||
transferable_file.Flush();
|
transferable_file.Flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShaderDiskCache::SaveDecompiled(u64 unique_identifier,
|
void ShaderDiskCache::SaveDecompiled(u64 unique_identifier, const std::string& code,
|
||||||
const ShaderDecompiler::ProgramResult& code,
|
|
||||||
bool sanitize_mul) {
|
bool sanitize_mul) {
|
||||||
if (!IsUsable())
|
if (!IsUsable())
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -20,8 +20,7 @@
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/file_util.h"
|
#include "common/file_util.h"
|
||||||
#include "video_core/regs.h"
|
#include "video_core/regs.h"
|
||||||
#include "video_core/renderer_opengl/gl_shader_decompiler.h"
|
#include "video_core/shader/generator/glsl_shader_gen.h"
|
||||||
#include "video_core/renderer_opengl/gl_shader_gen.h"
|
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
class System;
|
class System;
|
||||||
|
@ -38,6 +37,7 @@ struct ShaderDiskCacheDump;
|
||||||
|
|
||||||
using RawShaderConfig = Pica::Regs;
|
using RawShaderConfig = Pica::Regs;
|
||||||
using ProgramCode = std::vector<u32>;
|
using ProgramCode = std::vector<u32>;
|
||||||
|
using ProgramType = Pica::Shader::Generator::ProgramType;
|
||||||
using ShaderDecompiledMap = std::unordered_map<u64, ShaderDiskCacheDecompiled>;
|
using ShaderDecompiledMap = std::unordered_map<u64, ShaderDiskCacheDecompiled>;
|
||||||
using ShaderDumpsMap = std::unordered_map<u64, ShaderDiskCacheDump>;
|
using ShaderDumpsMap = std::unordered_map<u64, ShaderDiskCacheDump>;
|
||||||
|
|
||||||
|
@ -78,7 +78,7 @@ private:
|
||||||
|
|
||||||
/// Contains decompiled data from a shader
|
/// Contains decompiled data from a shader
|
||||||
struct ShaderDiskCacheDecompiled {
|
struct ShaderDiskCacheDecompiled {
|
||||||
ShaderDecompiler::ProgramResult result;
|
std::string code;
|
||||||
bool sanitize_mul;
|
bool sanitize_mul;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -109,8 +109,7 @@ public:
|
||||||
void SaveRaw(const ShaderDiskCacheRaw& entry);
|
void SaveRaw(const ShaderDiskCacheRaw& entry);
|
||||||
|
|
||||||
/// Saves a decompiled entry to the precompiled file. Does not check for collisions.
|
/// Saves a decompiled entry to the precompiled file. Does not check for collisions.
|
||||||
void SaveDecompiled(u64 unique_identifier, const ShaderDecompiler::ProgramResult& code,
|
void SaveDecompiled(u64 unique_identifier, const std::string& code, bool sanitize_mul);
|
||||||
bool sanitize_mul);
|
|
||||||
|
|
||||||
/// Saves a dump entry to the precompiled file. Does not check for collisions.
|
/// Saves a dump entry to the precompiled file. Does not check for collisions.
|
||||||
void SaveDump(u64 unique_identifier, GLuint program);
|
void SaveDump(u64 unique_identifier, GLuint program);
|
||||||
|
@ -132,11 +131,10 @@ private:
|
||||||
|
|
||||||
/// Saves a decompiled entry to the passed file. Does not check for collisions.
|
/// Saves a decompiled entry to the passed file. Does not check for collisions.
|
||||||
void SaveDecompiledToFile(FileUtil::IOFile& file, u64 unique_identifier,
|
void SaveDecompiledToFile(FileUtil::IOFile& file, u64 unique_identifier,
|
||||||
const ShaderDecompiler::ProgramResult& code, bool sanitize_mul);
|
const std::string& code, bool sanitize_mul);
|
||||||
|
|
||||||
/// Saves a decompiled entry to the virtual precompiled cache. Does not check for collisions.
|
/// Saves a decompiled entry to the virtual precompiled cache. Does not check for collisions.
|
||||||
bool SaveDecompiledToCache(u64 unique_identifier, const ShaderDecompiler::ProgramResult& code,
|
bool SaveDecompiledToCache(u64 unique_identifier, const std::string& code, bool sanitize_mul);
|
||||||
bool sanitize_mul);
|
|
||||||
|
|
||||||
/// Returns if the cache can be used
|
/// Returns if the cache can be used
|
||||||
bool IsUsable() const;
|
bool IsUsable() const;
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,266 +0,0 @@
|
||||||
// Copyright 2015 Citra Emulator Project
|
|
||||||
// Licensed under GPLv2 or any later version
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <functional>
|
|
||||||
#include <optional>
|
|
||||||
#include "common/hash.h"
|
|
||||||
#include "video_core/regs.h"
|
|
||||||
#include "video_core/shader/shader.h"
|
|
||||||
|
|
||||||
namespace OpenGL {
|
|
||||||
|
|
||||||
class Driver;
|
|
||||||
|
|
||||||
namespace ShaderDecompiler {
|
|
||||||
struct ProgramResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class ProgramType : u32 { VS, GS, FS };
|
|
||||||
|
|
||||||
enum Attributes {
|
|
||||||
ATTRIBUTE_POSITION,
|
|
||||||
ATTRIBUTE_COLOR,
|
|
||||||
ATTRIBUTE_TEXCOORD0,
|
|
||||||
ATTRIBUTE_TEXCOORD1,
|
|
||||||
ATTRIBUTE_TEXCOORD2,
|
|
||||||
ATTRIBUTE_TEXCOORD0_W,
|
|
||||||
ATTRIBUTE_NORMQUAT,
|
|
||||||
ATTRIBUTE_VIEW,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Doesn't include const_color because we don't sync it, see comment in BuildFromRegs()
|
|
||||||
struct TevStageConfigRaw {
|
|
||||||
u32 sources_raw;
|
|
||||||
u32 modifiers_raw;
|
|
||||||
u32 ops_raw;
|
|
||||||
u32 scales_raw;
|
|
||||||
explicit operator Pica::TexturingRegs::TevStageConfig() const noexcept {
|
|
||||||
Pica::TexturingRegs::TevStageConfig stage;
|
|
||||||
stage.sources_raw = sources_raw;
|
|
||||||
stage.modifiers_raw = modifiers_raw;
|
|
||||||
stage.ops_raw = ops_raw;
|
|
||||||
stage.const_color = 0;
|
|
||||||
stage.scales_raw = scales_raw;
|
|
||||||
return stage;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct PicaFSConfigState {
|
|
||||||
Pica::FramebufferRegs::CompareFunc alpha_test_func;
|
|
||||||
Pica::RasterizerRegs::ScissorMode scissor_test_mode;
|
|
||||||
Pica::TexturingRegs::TextureConfig::TextureType texture0_type;
|
|
||||||
bool texture2_use_coord1;
|
|
||||||
std::array<TevStageConfigRaw, 6> tev_stages;
|
|
||||||
u8 combiner_buffer_input;
|
|
||||||
|
|
||||||
Pica::RasterizerRegs::DepthBuffering depthmap_enable;
|
|
||||||
Pica::TexturingRegs::FogMode fog_mode;
|
|
||||||
bool fog_flip;
|
|
||||||
bool alphablend_enable;
|
|
||||||
Pica::FramebufferRegs::LogicOp logic_op;
|
|
||||||
|
|
||||||
struct {
|
|
||||||
struct {
|
|
||||||
unsigned num;
|
|
||||||
bool directional;
|
|
||||||
bool two_sided_diffuse;
|
|
||||||
bool dist_atten_enable;
|
|
||||||
bool spot_atten_enable;
|
|
||||||
bool geometric_factor_0;
|
|
||||||
bool geometric_factor_1;
|
|
||||||
bool shadow_enable;
|
|
||||||
} light[8];
|
|
||||||
|
|
||||||
bool enable;
|
|
||||||
unsigned src_num;
|
|
||||||
Pica::LightingRegs::LightingBumpMode bump_mode;
|
|
||||||
unsigned bump_selector;
|
|
||||||
bool bump_renorm;
|
|
||||||
bool clamp_highlights;
|
|
||||||
|
|
||||||
Pica::LightingRegs::LightingConfig config;
|
|
||||||
bool enable_primary_alpha;
|
|
||||||
bool enable_secondary_alpha;
|
|
||||||
|
|
||||||
bool enable_shadow;
|
|
||||||
bool shadow_primary;
|
|
||||||
bool shadow_secondary;
|
|
||||||
bool shadow_invert;
|
|
||||||
bool shadow_alpha;
|
|
||||||
unsigned shadow_selector;
|
|
||||||
|
|
||||||
struct {
|
|
||||||
bool enable;
|
|
||||||
bool abs_input;
|
|
||||||
Pica::LightingRegs::LightingLutInput type;
|
|
||||||
float scale;
|
|
||||||
} lut_d0, lut_d1, lut_sp, lut_fr, lut_rr, lut_rg, lut_rb;
|
|
||||||
} lighting;
|
|
||||||
|
|
||||||
struct {
|
|
||||||
bool enable;
|
|
||||||
u32 coord;
|
|
||||||
Pica::TexturingRegs::ProcTexClamp u_clamp, v_clamp;
|
|
||||||
Pica::TexturingRegs::ProcTexCombiner color_combiner, alpha_combiner;
|
|
||||||
bool separate_alpha;
|
|
||||||
bool noise_enable;
|
|
||||||
Pica::TexturingRegs::ProcTexShift u_shift, v_shift;
|
|
||||||
u32 lut_width;
|
|
||||||
u32 lut_offset0;
|
|
||||||
u32 lut_offset1;
|
|
||||||
u32 lut_offset2;
|
|
||||||
u32 lut_offset3;
|
|
||||||
u32 lod_min;
|
|
||||||
u32 lod_max;
|
|
||||||
Pica::TexturingRegs::ProcTexFilter lut_filter;
|
|
||||||
} proctex;
|
|
||||||
|
|
||||||
struct {
|
|
||||||
bool emulate_blending;
|
|
||||||
Pica::FramebufferRegs::BlendEquation eq;
|
|
||||||
Pica::FramebufferRegs::BlendFactor src_factor;
|
|
||||||
Pica::FramebufferRegs::BlendFactor dst_factor;
|
|
||||||
} rgb_blend, alpha_blend;
|
|
||||||
|
|
||||||
bool shadow_rendering;
|
|
||||||
bool shadow_texture_orthographic;
|
|
||||||
bool use_custom_normal_map;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This struct contains all state used to generate the GLSL fragment shader that emulates the
|
|
||||||
* current Pica register configuration. This struct is used as a cache key for generated GLSL shader
|
|
||||||
* programs. The functions in gl_shader_gen.cpp should retrieve state from this struct only, not by
|
|
||||||
* directly accessing Pica registers. This should reduce the risk of bugs in shader generation where
|
|
||||||
* Pica state is not being captured in the shader cache key, thereby resulting in (what should be)
|
|
||||||
* two separate shaders sharing the same key.
|
|
||||||
*/
|
|
||||||
struct PicaFSConfig : Common::HashableStruct<PicaFSConfigState> {
|
|
||||||
|
|
||||||
/// Construct a PicaFSConfig with the given Pica register configuration.
|
|
||||||
static PicaFSConfig BuildFromRegs(const Pica::Regs& regs, bool has_blend_minmax_factor,
|
|
||||||
bool use_normal = false);
|
|
||||||
|
|
||||||
bool TevStageUpdatesCombinerBufferColor(unsigned stage_index) const {
|
|
||||||
return (stage_index < 4) && (state.combiner_buffer_input & (1 << stage_index));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TevStageUpdatesCombinerBufferAlpha(unsigned stage_index) const {
|
|
||||||
return (stage_index < 4) && ((state.combiner_buffer_input >> 4) & (1 << stage_index));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This struct contains common information to identify a GL vertex/geometry shader generated from
|
|
||||||
* PICA vertex/geometry shader.
|
|
||||||
*/
|
|
||||||
struct PicaShaderConfigCommon {
|
|
||||||
void Init(const Pica::ShaderRegs& regs, Pica::Shader::ShaderSetup& setup);
|
|
||||||
|
|
||||||
u64 program_hash;
|
|
||||||
u64 swizzle_hash;
|
|
||||||
u32 main_offset;
|
|
||||||
bool sanitize_mul;
|
|
||||||
|
|
||||||
u32 num_outputs;
|
|
||||||
|
|
||||||
// output_map[output register index] -> output attribute index
|
|
||||||
std::array<u32, 16> output_map;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This struct contains information to identify a GL vertex shader generated from PICA vertex
|
|
||||||
* shader.
|
|
||||||
*/
|
|
||||||
struct PicaVSConfig : Common::HashableStruct<PicaShaderConfigCommon> {
|
|
||||||
explicit PicaVSConfig(const Pica::ShaderRegs& regs, Pica::Shader::ShaderSetup& setup) {
|
|
||||||
state.Init(regs, setup);
|
|
||||||
}
|
|
||||||
explicit PicaVSConfig(const PicaShaderConfigCommon& conf) {
|
|
||||||
state = conf;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct PicaGSConfigCommonRaw {
|
|
||||||
void Init(const Pica::Regs& regs);
|
|
||||||
|
|
||||||
u32 vs_output_attributes;
|
|
||||||
u32 gs_output_attributes;
|
|
||||||
|
|
||||||
struct SemanticMap {
|
|
||||||
u32 attribute_index;
|
|
||||||
u32 component_index;
|
|
||||||
};
|
|
||||||
|
|
||||||
// semantic_maps[semantic name] -> GS output attribute index + component index
|
|
||||||
std::array<SemanticMap, 24> semantic_maps;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This struct contains information to identify a GL geometry shader generated from PICA no-geometry
|
|
||||||
* shader pipeline
|
|
||||||
*/
|
|
||||||
struct PicaFixedGSConfig : Common::HashableStruct<PicaGSConfigCommonRaw> {
|
|
||||||
explicit PicaFixedGSConfig(const Pica::Regs& regs) {
|
|
||||||
state.Init(regs);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates the GLSL vertex shader program source code that accepts vertices from software shader
|
|
||||||
* and directly passes them to the fragment shader.
|
|
||||||
* @param separable_shader generates shader that can be used for separate shader object
|
|
||||||
* @returns String of the shader source code
|
|
||||||
*/
|
|
||||||
ShaderDecompiler::ProgramResult GenerateTrivialVertexShader(bool separable_shader);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates the GLSL vertex shader program source code for the given VS program
|
|
||||||
* @returns String of the shader source code; boost::none on failure
|
|
||||||
*/
|
|
||||||
std::optional<ShaderDecompiler::ProgramResult> GenerateVertexShader(
|
|
||||||
const Pica::Shader::ShaderSetup& setup, const PicaVSConfig& config, bool separable_shader);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Generates the GLSL fixed geometry shader program source code for non-GS PICA pipeline
|
|
||||||
* @returns String of the shader source code
|
|
||||||
*/
|
|
||||||
ShaderDecompiler::ProgramResult GenerateFixedGeometryShader(const PicaFixedGSConfig& config,
|
|
||||||
bool separable_shader);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates the GLSL fragment shader program source code for the current Pica state
|
|
||||||
* @param config ShaderCacheKey object generated for the current Pica state, used for the shader
|
|
||||||
* configuration (NOTE: Use state in this struct only, not the Pica registers!)
|
|
||||||
* @param separable_shader generates shader that can be used for separate shader object
|
|
||||||
* @returns String of the shader source code
|
|
||||||
*/
|
|
||||||
ShaderDecompiler::ProgramResult GenerateFragmentShader(const PicaFSConfig& config,
|
|
||||||
bool separable_shader);
|
|
||||||
|
|
||||||
} // namespace OpenGL
|
|
||||||
|
|
||||||
namespace std {
|
|
||||||
template <>
|
|
||||||
struct hash<OpenGL::PicaFSConfig> {
|
|
||||||
std::size_t operator()(const OpenGL::PicaFSConfig& k) const noexcept {
|
|
||||||
return k.Hash();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct hash<OpenGL::PicaVSConfig> {
|
|
||||||
std::size_t operator()(const OpenGL::PicaVSConfig& k) const noexcept {
|
|
||||||
return k.Hash();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct hash<OpenGL::PicaFixedGSConfig> {
|
|
||||||
std::size_t operator()(const OpenGL::PicaFixedGSConfig& k) const noexcept {
|
|
||||||
return k.Hash();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace std
|
|
|
@ -14,9 +14,11 @@
|
||||||
#include "video_core/renderer_opengl/gl_shader_disk_cache.h"
|
#include "video_core/renderer_opengl/gl_shader_disk_cache.h"
|
||||||
#include "video_core/renderer_opengl/gl_shader_manager.h"
|
#include "video_core/renderer_opengl/gl_shader_manager.h"
|
||||||
#include "video_core/renderer_opengl/gl_state.h"
|
#include "video_core/renderer_opengl/gl_state.h"
|
||||||
#include "video_core/shader/shader_uniforms.h"
|
#include "video_core/shader/generator/shader_uniforms.h"
|
||||||
#include "video_core/video_core.h"
|
#include "video_core/video_core.h"
|
||||||
|
|
||||||
|
using namespace Pica::Shader::Generator;
|
||||||
|
|
||||||
namespace OpenGL {
|
namespace OpenGL {
|
||||||
|
|
||||||
static u64 GetUniqueIdentifier(const Pica::Regs& regs, const ProgramCode& code) {
|
static u64 GetUniqueIdentifier(const Pica::Regs& regs, const ProgramCode& code) {
|
||||||
|
@ -74,7 +76,7 @@ static std::set<GLenum> GetSupportedFormats() {
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::tuple<PicaVSConfig, Pica::Shader::ShaderSetup> BuildVSConfigFromRaw(
|
static std::tuple<PicaVSConfig, Pica::Shader::ShaderSetup> BuildVSConfigFromRaw(
|
||||||
const ShaderDiskCacheRaw& raw) {
|
const ShaderDiskCacheRaw& raw, const Driver& driver) {
|
||||||
Pica::Shader::ProgramCode program_code{};
|
Pica::Shader::ProgramCode program_code{};
|
||||||
Pica::Shader::SwizzleData swizzle_data{};
|
Pica::Shader::SwizzleData swizzle_data{};
|
||||||
std::copy_n(raw.GetProgramCode().begin(), Pica::Shader::MAX_PROGRAM_CODE_LENGTH,
|
std::copy_n(raw.GetProgramCode().begin(), Pica::Shader::MAX_PROGRAM_CODE_LENGTH,
|
||||||
|
@ -84,7 +86,8 @@ static std::tuple<PicaVSConfig, Pica::Shader::ShaderSetup> BuildVSConfigFromRaw(
|
||||||
Pica::Shader::ShaderSetup setup;
|
Pica::Shader::ShaderSetup setup;
|
||||||
setup.program_code = program_code;
|
setup.program_code = program_code;
|
||||||
setup.swizzle_data = swizzle_data;
|
setup.swizzle_data = swizzle_data;
|
||||||
return {PicaVSConfig{raw.GetRawShaderConfig().vs, setup}, setup};
|
return {PicaVSConfig{raw.GetRawShaderConfig(), setup, driver.HasClipCullDistance(), true},
|
||||||
|
setup};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -130,8 +133,10 @@ private:
|
||||||
|
|
||||||
class TrivialVertexShader {
|
class TrivialVertexShader {
|
||||||
public:
|
public:
|
||||||
explicit TrivialVertexShader(bool separable) : program(separable) {
|
explicit TrivialVertexShader(const Driver& driver, bool separable) : program(separable) {
|
||||||
program.Create(GenerateTrivialVertexShader(separable).code.c_str(), GL_VERTEX_SHADER);
|
const auto code =
|
||||||
|
GLSL::GenerateTrivialVertexShader(driver.HasClipCullDistance(), separable);
|
||||||
|
program.Create(code.c_str(), GL_VERTEX_SHADER);
|
||||||
}
|
}
|
||||||
GLuint Get() const {
|
GLuint Get() const {
|
||||||
return program.GetHandle();
|
return program.GetHandle();
|
||||||
|
@ -141,20 +146,18 @@ private:
|
||||||
OGLShaderStage program;
|
OGLShaderStage program;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename KeyConfigType,
|
template <typename KeyConfigType, std::string (*CodeGenerator)(const KeyConfigType&, bool),
|
||||||
ShaderDecompiler::ProgramResult (*CodeGenerator)(const KeyConfigType&, bool),
|
|
||||||
GLenum ShaderType>
|
GLenum ShaderType>
|
||||||
class ShaderCache {
|
class ShaderCache {
|
||||||
public:
|
public:
|
||||||
explicit ShaderCache(bool separable) : separable(separable) {}
|
explicit ShaderCache(bool separable) : separable(separable) {}
|
||||||
std::tuple<GLuint, std::optional<ShaderDecompiler::ProgramResult>> Get(
|
std::tuple<GLuint, std::optional<std::string>> Get(const KeyConfigType& config) {
|
||||||
const KeyConfigType& config) {
|
|
||||||
auto [iter, new_shader] = shaders.emplace(config, OGLShaderStage{separable});
|
auto [iter, new_shader] = shaders.emplace(config, OGLShaderStage{separable});
|
||||||
OGLShaderStage& cached_shader = iter->second;
|
OGLShaderStage& cached_shader = iter->second;
|
||||||
std::optional<ShaderDecompiler::ProgramResult> result{};
|
std::optional<std::string> result{};
|
||||||
if (new_shader) {
|
if (new_shader) {
|
||||||
result = CodeGenerator(config, separable);
|
result = CodeGenerator(config, separable);
|
||||||
cached_shader.Create(result->code.c_str(), ShaderType);
|
cached_shader.Create(result->c_str(), ShaderType);
|
||||||
}
|
}
|
||||||
return {cached_shader.GetHandle(), std::move(result)};
|
return {cached_shader.GetHandle(), std::move(result)};
|
||||||
}
|
}
|
||||||
|
@ -180,29 +183,27 @@ private:
|
||||||
// program buffer from the previous shader, which is hashed into the config, resulting several
|
// program buffer from the previous shader, which is hashed into the config, resulting several
|
||||||
// different config values from the same shader program.
|
// different config values from the same shader program.
|
||||||
template <typename KeyConfigType,
|
template <typename KeyConfigType,
|
||||||
std::optional<ShaderDecompiler::ProgramResult> (*CodeGenerator)(
|
std::string (*CodeGenerator)(const Pica::Shader::ShaderSetup&, const KeyConfigType&,
|
||||||
const Pica::Shader::ShaderSetup&, const KeyConfigType&, bool),
|
bool),
|
||||||
GLenum ShaderType>
|
GLenum ShaderType>
|
||||||
class ShaderDoubleCache {
|
class ShaderDoubleCache {
|
||||||
public:
|
public:
|
||||||
explicit ShaderDoubleCache(bool separable) : separable(separable) {}
|
explicit ShaderDoubleCache(bool separable) : separable(separable) {}
|
||||||
std::tuple<GLuint, std::optional<ShaderDecompiler::ProgramResult>> Get(
|
std::tuple<GLuint, std::optional<std::string>> Get(const KeyConfigType& key,
|
||||||
const KeyConfigType& key, const Pica::Shader::ShaderSetup& setup) {
|
const Pica::Shader::ShaderSetup& setup) {
|
||||||
std::optional<ShaderDecompiler::ProgramResult> result{};
|
std::optional<std::string> result{};
|
||||||
auto map_it = shader_map.find(key);
|
auto map_it = shader_map.find(key);
|
||||||
if (map_it == shader_map.end()) {
|
if (map_it == shader_map.end()) {
|
||||||
auto program_opt = CodeGenerator(setup, key, separable);
|
auto program = CodeGenerator(setup, key, separable);
|
||||||
if (!program_opt) {
|
if (program.empty()) {
|
||||||
shader_map[key] = nullptr;
|
shader_map[key] = nullptr;
|
||||||
return {0, std::nullopt};
|
return {0, std::nullopt};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string& program = program_opt->code;
|
|
||||||
auto [iter, new_shader] = shader_cache.emplace(program, OGLShaderStage{separable});
|
auto [iter, new_shader] = shader_cache.emplace(program, OGLShaderStage{separable});
|
||||||
OGLShaderStage& cached_shader = iter->second;
|
OGLShaderStage& cached_shader = iter->second;
|
||||||
if (new_shader) {
|
if (new_shader) {
|
||||||
result.emplace();
|
result = program;
|
||||||
result->code = program;
|
|
||||||
cached_shader.Create(program.c_str(), ShaderType);
|
cached_shader.Create(program.c_str(), ShaderType);
|
||||||
}
|
}
|
||||||
shader_map[key] = &cached_shader;
|
shader_map[key] = &cached_shader;
|
||||||
|
@ -237,18 +238,19 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
using ProgrammableVertexShaders =
|
using ProgrammableVertexShaders =
|
||||||
ShaderDoubleCache<PicaVSConfig, &GenerateVertexShader, GL_VERTEX_SHADER>;
|
ShaderDoubleCache<PicaVSConfig, &GLSL::GenerateVertexShader, GL_VERTEX_SHADER>;
|
||||||
|
|
||||||
using FixedGeometryShaders =
|
using FixedGeometryShaders =
|
||||||
ShaderCache<PicaFixedGSConfig, &GenerateFixedGeometryShader, GL_GEOMETRY_SHADER>;
|
ShaderCache<PicaFixedGSConfig, &GLSL::GenerateFixedGeometryShader, GL_GEOMETRY_SHADER>;
|
||||||
|
|
||||||
using FragmentShaders = ShaderCache<PicaFSConfig, &GenerateFragmentShader, GL_FRAGMENT_SHADER>;
|
using FragmentShaders =
|
||||||
|
ShaderCache<PicaFSConfig, &GLSL::GenerateFragmentShader, GL_FRAGMENT_SHADER>;
|
||||||
|
|
||||||
class ShaderProgramManager::Impl {
|
class ShaderProgramManager::Impl {
|
||||||
public:
|
public:
|
||||||
explicit Impl(bool separable)
|
explicit Impl(const Driver& driver, bool separable)
|
||||||
: separable(separable), programmable_vertex_shaders(separable),
|
: separable(separable), programmable_vertex_shaders(separable),
|
||||||
trivial_vertex_shader(separable), fixed_geometry_shaders(separable),
|
trivial_vertex_shader(driver, separable), fixed_geometry_shaders(separable),
|
||||||
fragment_shaders(separable), disk_cache(separable) {
|
fragment_shaders(separable), disk_cache(separable) {
|
||||||
if (separable)
|
if (separable)
|
||||||
pipeline.Create();
|
pipeline.Create();
|
||||||
|
@ -299,13 +301,13 @@ ShaderProgramManager::ShaderProgramManager(Frontend::EmuWindow& emu_window_, con
|
||||||
bool separable)
|
bool separable)
|
||||||
: emu_window{emu_window_}, driver{driver_},
|
: emu_window{emu_window_}, driver{driver_},
|
||||||
strict_context_required{emu_window.StrictContextRequired()}, impl{std::make_unique<Impl>(
|
strict_context_required{emu_window.StrictContextRequired()}, impl{std::make_unique<Impl>(
|
||||||
separable)} {}
|
driver_, separable)} {}
|
||||||
|
|
||||||
ShaderProgramManager::~ShaderProgramManager() = default;
|
ShaderProgramManager::~ShaderProgramManager() = default;
|
||||||
|
|
||||||
bool ShaderProgramManager::UseProgrammableVertexShader(const Pica::Regs& regs,
|
bool ShaderProgramManager::UseProgrammableVertexShader(const Pica::Regs& regs,
|
||||||
Pica::Shader::ShaderSetup& setup) {
|
Pica::Shader::ShaderSetup& setup) {
|
||||||
PicaVSConfig config{regs.vs, setup};
|
PicaVSConfig config{regs, setup, driver.HasClipCullDistance(), true};
|
||||||
auto [handle, result] = impl->programmable_vertex_shaders.Get(config, setup);
|
auto [handle, result] = impl->programmable_vertex_shaders.Get(config, setup);
|
||||||
if (handle == 0)
|
if (handle == 0)
|
||||||
return false;
|
return false;
|
||||||
|
@ -333,7 +335,7 @@ void ShaderProgramManager::UseTrivialVertexShader() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShaderProgramManager::UseFixedGeometryShader(const Pica::Regs& regs) {
|
void ShaderProgramManager::UseFixedGeometryShader(const Pica::Regs& regs) {
|
||||||
PicaFixedGSConfig gs_config(regs);
|
PicaFixedGSConfig gs_config(regs, driver.HasClipCullDistance());
|
||||||
auto [handle, _] = impl->fixed_geometry_shaders.Get(gs_config);
|
auto [handle, _] = impl->fixed_geometry_shaders.Get(gs_config);
|
||||||
impl->current.gs = handle;
|
impl->current.gs = handle;
|
||||||
impl->current.gs_hash = gs_config.Hash();
|
impl->current.gs_hash = gs_config.Hash();
|
||||||
|
@ -345,8 +347,8 @@ void ShaderProgramManager::UseTrivialGeometryShader() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShaderProgramManager::UseFragmentShader(const Pica::Regs& regs, bool use_normal) {
|
void ShaderProgramManager::UseFragmentShader(const Pica::Regs& regs, bool use_normal) {
|
||||||
PicaFSConfig config =
|
PicaFSConfig config(regs, false, driver.IsOpenGLES(), false, driver.HasBlendMinMaxFactor(),
|
||||||
PicaFSConfig::BuildFromRegs(regs, driver.HasBlendMinMaxFactor(), use_normal);
|
use_normal);
|
||||||
auto [handle, result] = impl->fragment_shaders.Get(config);
|
auto [handle, result] = impl->fragment_shaders.Get(config);
|
||||||
impl->current.fs = handle;
|
impl->current.fs = handle;
|
||||||
impl->current.fs_hash = config.Hash();
|
impl->current.fs_hash = config.Hash();
|
||||||
|
@ -463,13 +465,13 @@ void ShaderProgramManager::LoadDiskCache(const std::atomic_bool& stop_loading,
|
||||||
// we have both the binary shader and the decompiled, so inject it into the
|
// we have both the binary shader and the decompiled, so inject it into the
|
||||||
// cache
|
// cache
|
||||||
if (raw.GetProgramType() == ProgramType::VS) {
|
if (raw.GetProgramType() == ProgramType::VS) {
|
||||||
auto [conf, setup] = BuildVSConfigFromRaw(raw);
|
auto [conf, setup] = BuildVSConfigFromRaw(raw, driver);
|
||||||
std::scoped_lock lock(mutex);
|
std::scoped_lock lock(mutex);
|
||||||
impl->programmable_vertex_shaders.Inject(conf, decomp->second.result.code,
|
impl->programmable_vertex_shaders.Inject(conf, decomp->second.code,
|
||||||
std::move(shader));
|
std::move(shader));
|
||||||
} else if (raw.GetProgramType() == ProgramType::FS) {
|
} else if (raw.GetProgramType() == ProgramType::FS) {
|
||||||
PicaFSConfig conf = PicaFSConfig::BuildFromRegs(raw.GetRawShaderConfig(),
|
PicaFSConfig conf(raw.GetRawShaderConfig(), false, driver.IsOpenGLES(), false,
|
||||||
driver.HasBlendMinMaxFactor());
|
driver.HasBlendMinMaxFactor());
|
||||||
std::scoped_lock lock(mutex);
|
std::scoped_lock lock(mutex);
|
||||||
impl->fragment_shaders.Inject(conf, std::move(shader));
|
impl->fragment_shaders.Inject(conf, std::move(shader));
|
||||||
} else {
|
} else {
|
||||||
|
@ -566,24 +568,24 @@ void ShaderProgramManager::LoadDiskCache(const std::atomic_bool& stop_loading,
|
||||||
|
|
||||||
bool sanitize_mul = false;
|
bool sanitize_mul = false;
|
||||||
GLuint handle{0};
|
GLuint handle{0};
|
||||||
std::optional<ShaderDecompiler::ProgramResult> result;
|
std::string code;
|
||||||
// Otherwise decompile and build the shader at boot and save the result to the
|
// Otherwise decompile and build the shader at boot and save the result to the
|
||||||
// precompiled file
|
// precompiled file
|
||||||
if (raw.GetProgramType() == ProgramType::VS) {
|
if (raw.GetProgramType() == ProgramType::VS) {
|
||||||
auto [conf, setup] = BuildVSConfigFromRaw(raw);
|
auto [conf, setup] = BuildVSConfigFromRaw(raw, driver);
|
||||||
result = GenerateVertexShader(setup, conf, impl->separable);
|
code = GLSL::GenerateVertexShader(setup, conf, impl->separable);
|
||||||
OGLShaderStage stage{impl->separable};
|
OGLShaderStage stage{impl->separable};
|
||||||
stage.Create(result->code.c_str(), GL_VERTEX_SHADER);
|
stage.Create(code.c_str(), GL_VERTEX_SHADER);
|
||||||
handle = stage.GetHandle();
|
handle = stage.GetHandle();
|
||||||
sanitize_mul = conf.state.sanitize_mul;
|
sanitize_mul = conf.state.sanitize_mul;
|
||||||
std::scoped_lock lock(mutex);
|
std::scoped_lock lock(mutex);
|
||||||
impl->programmable_vertex_shaders.Inject(conf, result->code, std::move(stage));
|
impl->programmable_vertex_shaders.Inject(conf, code, std::move(stage));
|
||||||
} else if (raw.GetProgramType() == ProgramType::FS) {
|
} else if (raw.GetProgramType() == ProgramType::FS) {
|
||||||
PicaFSConfig conf = PicaFSConfig::BuildFromRegs(raw.GetRawShaderConfig(),
|
PicaFSConfig conf(raw.GetRawShaderConfig(), false, driver.IsOpenGLES(), false,
|
||||||
driver.HasBlendMinMaxFactor());
|
driver.HasBlendMinMaxFactor());
|
||||||
result = GenerateFragmentShader(conf, impl->separable);
|
code = GLSL::GenerateFragmentShader(conf, impl->separable);
|
||||||
OGLShaderStage stage{impl->separable};
|
OGLShaderStage stage{impl->separable};
|
||||||
stage.Create(result->code.c_str(), GL_FRAGMENT_SHADER);
|
stage.Create(code.c_str(), GL_FRAGMENT_SHADER);
|
||||||
handle = stage.GetHandle();
|
handle = stage.GetHandle();
|
||||||
std::scoped_lock lock(mutex);
|
std::scoped_lock lock(mutex);
|
||||||
impl->fragment_shaders.Inject(conf, std::move(stage));
|
impl->fragment_shaders.Inject(conf, std::move(stage));
|
||||||
|
@ -602,8 +604,8 @@ void ShaderProgramManager::LoadDiskCache(const std::atomic_bool& stop_loading,
|
||||||
|
|
||||||
std::scoped_lock lock(mutex);
|
std::scoped_lock lock(mutex);
|
||||||
// If this is a new separable shader, add it the precompiled cache
|
// If this is a new separable shader, add it the precompiled cache
|
||||||
if (result) {
|
if (!code.empty()) {
|
||||||
disk_cache.SaveDecompiled(unique_identifier, *result, sanitize_mul);
|
disk_cache.SaveDecompiled(unique_identifier, code, sanitize_mul);
|
||||||
disk_cache.SaveDump(unique_identifier, handle);
|
disk_cache.SaveDump(unique_identifier, handle);
|
||||||
precompiled_cache_altered = true;
|
precompiled_cache_altered = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,12 @@ namespace OpenGL {
|
||||||
class Driver;
|
class Driver;
|
||||||
class OpenGLState;
|
class OpenGLState;
|
||||||
|
|
||||||
|
enum UniformBindings {
|
||||||
|
VSPicaData = 0,
|
||||||
|
VSData = 1,
|
||||||
|
FSData = 2,
|
||||||
|
};
|
||||||
|
|
||||||
/// A class that manage different shader stages and configures them with given config data.
|
/// A class that manage different shader stages and configures them with given config data.
|
||||||
class ShaderProgramManager {
|
class ShaderProgramManager {
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -14,9 +14,9 @@
|
||||||
namespace OpenGL {
|
namespace OpenGL {
|
||||||
|
|
||||||
GLuint LoadShader(std::string_view source, GLenum type) {
|
GLuint LoadShader(std::string_view source, GLenum type) {
|
||||||
const std::string version = GLES ? R"(#version 320 es
|
std::string preamble;
|
||||||
|
if (GLES) {
|
||||||
#define CITRA_GLES
|
preamble = R"(#version 320 es
|
||||||
|
|
||||||
#if defined(GL_ANDROID_extension_pack_es31a)
|
#if defined(GL_ANDROID_extension_pack_es31a)
|
||||||
#extension GL_ANDROID_extension_pack_es31a : enable
|
#extension GL_ANDROID_extension_pack_es31a : enable
|
||||||
|
@ -25,8 +25,10 @@ GLuint LoadShader(std::string_view source, GLenum type) {
|
||||||
#if defined(GL_EXT_clip_cull_distance)
|
#if defined(GL_EXT_clip_cull_distance)
|
||||||
#extension GL_EXT_clip_cull_distance : enable
|
#extension GL_EXT_clip_cull_distance : enable
|
||||||
#endif // defined(GL_EXT_clip_cull_distance)
|
#endif // defined(GL_EXT_clip_cull_distance)
|
||||||
)"
|
)";
|
||||||
: "#version 430 core\n";
|
} else {
|
||||||
|
preamble = "#version 430 core\n";
|
||||||
|
}
|
||||||
|
|
||||||
std::string_view debug_type;
|
std::string_view debug_type;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
@ -43,8 +45,8 @@ GLuint LoadShader(std::string_view source, GLenum type) {
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::array<const GLchar*, 2> src_arr{version.data(), source.data()};
|
std::array<const GLchar*, 2> src_arr{preamble.data(), source.data()};
|
||||||
std::array<GLint, 2> lengths{static_cast<GLint>(version.size()),
|
std::array<GLint, 2> lengths{static_cast<GLint>(preamble.size()),
|
||||||
static_cast<GLint>(source.size())};
|
static_cast<GLint>(source.size())};
|
||||||
GLuint shader_id = glCreateShader(type);
|
GLuint shader_id = glCreateShader(type);
|
||||||
glShaderSource(shader_id, static_cast<GLsizei>(src_arr.size()), src_arr.data(), lengths.data());
|
glShaderSource(shader_id, static_cast<GLsizei>(src_arr.size()), src_arr.data(), lengths.data());
|
||||||
|
|
|
@ -9,21 +9,6 @@
|
||||||
|
|
||||||
namespace OpenGL {
|
namespace OpenGL {
|
||||||
|
|
||||||
// High precision may or may not supported in GLES3. If it isn't, use medium precision instead.
|
|
||||||
static constexpr char fragment_shader_precision_OES[] = R"(
|
|
||||||
#ifdef GL_FRAGMENT_PRECISION_HIGH
|
|
||||||
precision highp int;
|
|
||||||
precision highp float;
|
|
||||||
precision highp samplerBuffer;
|
|
||||||
precision highp uimage2D;
|
|
||||||
#else
|
|
||||||
precision mediump int;
|
|
||||||
precision mediump float;
|
|
||||||
precision mediump samplerBuffer;
|
|
||||||
precision mediump uimage2D;
|
|
||||||
#endif // GL_FRAGMENT_PRECISION_HIGH
|
|
||||||
)";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility function to create and compile an OpenGL GLSL shader
|
* Utility function to create and compile an OpenGL GLSL shader
|
||||||
* @param source String of the GLSL shader program
|
* @param source String of the GLSL shader program
|
||||||
|
|
|
@ -11,12 +11,12 @@
|
||||||
#include "core/hw/hw.h"
|
#include "core/hw/hw.h"
|
||||||
#include "core/hw/lcd.h"
|
#include "core/hw/lcd.h"
|
||||||
#include "core/memory.h"
|
#include "core/memory.h"
|
||||||
#include "video_core/renderer_opengl/gl_shader_util.h"
|
|
||||||
#include "video_core/renderer_opengl/gl_state.h"
|
#include "video_core/renderer_opengl/gl_state.h"
|
||||||
#include "video_core/renderer_opengl/gl_texture_mailbox.h"
|
#include "video_core/renderer_opengl/gl_texture_mailbox.h"
|
||||||
#include "video_core/renderer_opengl/gl_vars.h"
|
#include "video_core/renderer_opengl/gl_vars.h"
|
||||||
#include "video_core/renderer_opengl/post_processing_opengl.h"
|
#include "video_core/renderer_opengl/post_processing_opengl.h"
|
||||||
#include "video_core/renderer_opengl/renderer_opengl.h"
|
#include "video_core/renderer_opengl/renderer_opengl.h"
|
||||||
|
#include "video_core/shader/generator/glsl_shader_gen.h"
|
||||||
#include "video_core/video_core.h"
|
#include "video_core/video_core.h"
|
||||||
|
|
||||||
#include "video_core/host_shaders/opengl_present_anaglyph_frag.h"
|
#include "video_core/host_shaders/opengl_present_anaglyph_frag.h"
|
||||||
|
@ -387,11 +387,7 @@ void RendererOpenGL::InitOpenGLObjects() {
|
||||||
|
|
||||||
void RendererOpenGL::ReloadShader() {
|
void RendererOpenGL::ReloadShader() {
|
||||||
// Link shaders and get variable locations
|
// Link shaders and get variable locations
|
||||||
std::string shader_data;
|
std::string shader_data = fragment_shader_precision_OES;
|
||||||
if (GLES) {
|
|
||||||
shader_data += fragment_shader_precision_OES;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::Anaglyph) {
|
if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::Anaglyph) {
|
||||||
if (Settings::values.anaglyph_shader_name.GetValue() == "dubois (builtin)") {
|
if (Settings::values.anaglyph_shader_name.GetValue() == "dubois (builtin)") {
|
||||||
shader_data += HostShaders::OPENGL_PRESENT_ANAGLYPH_FRAG;
|
shader_data += HostShaders::OPENGL_PRESENT_ANAGLYPH_FRAG;
|
||||||
|
|
|
@ -5,7 +5,8 @@
|
||||||
#include "common/thread_worker.h"
|
#include "common/thread_worker.h"
|
||||||
#include "video_core/rasterizer_cache/pixel_format.h"
|
#include "video_core/rasterizer_cache/pixel_format.h"
|
||||||
#include "video_core/renderer_vulkan/vk_common.h"
|
#include "video_core/renderer_vulkan/vk_common.h"
|
||||||
#include "video_core/renderer_vulkan/vk_shader_gen.h"
|
#include "video_core/shader/generator/glsl_shader_gen.h"
|
||||||
|
#include "video_core/shader/generator/spv_shader_gen.h"
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
|
|
|
@ -14,19 +14,14 @@
|
||||||
#include "video_core/renderer_vulkan/vk_pipeline_cache.h"
|
#include "video_core/renderer_vulkan/vk_pipeline_cache.h"
|
||||||
#include "video_core/renderer_vulkan/vk_renderpass_cache.h"
|
#include "video_core/renderer_vulkan/vk_renderpass_cache.h"
|
||||||
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
||||||
#include "video_core/renderer_vulkan/vk_shader_gen_spv.h"
|
|
||||||
#include "video_core/renderer_vulkan/vk_shader_util.h"
|
#include "video_core/renderer_vulkan/vk_shader_util.h"
|
||||||
|
|
||||||
|
using namespace Pica::Shader::Generator;
|
||||||
|
|
||||||
MICROPROFILE_DEFINE(Vulkan_Bind, "Vulkan", "Pipeline Bind", MP_RGB(192, 32, 32));
|
MICROPROFILE_DEFINE(Vulkan_Bind, "Vulkan", "Pipeline Bind", MP_RGB(192, 32, 32));
|
||||||
|
|
||||||
namespace Vulkan {
|
namespace Vulkan {
|
||||||
|
|
||||||
enum ProgramType : u32 {
|
|
||||||
VS = 0,
|
|
||||||
GS = 2,
|
|
||||||
FS = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
u32 AttribBytes(Pica::PipelineRegs::VertexAttributeFormat format, u32 size) {
|
u32 AttribBytes(Pica::PipelineRegs::VertexAttributeFormat format, u32 size) {
|
||||||
switch (format) {
|
switch (format) {
|
||||||
case Pica::PipelineRegs::VertexAttributeFormat::FLOAT:
|
case Pica::PipelineRegs::VertexAttributeFormat::FLOAT:
|
||||||
|
@ -52,14 +47,14 @@ AttribLoadFlags MakeAttribLoadFlag(Pica::PipelineRegs::VertexAttributeFormat for
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr std::array<vk::DescriptorSetLayoutBinding, 5> BUFFER_BINDINGS = {{
|
constexpr std::array<vk::DescriptorSetLayoutBinding, 6> BUFFER_BINDINGS = {{
|
||||||
{0, vk::DescriptorType::eUniformBufferDynamic, 1, vk::ShaderStageFlagBits::eVertex},
|
{0, vk::DescriptorType::eUniformBufferDynamic, 1, vk::ShaderStageFlagBits::eVertex},
|
||||||
{1, vk::DescriptorType::eUniformBufferDynamic, 1,
|
{1, vk::DescriptorType::eUniformBufferDynamic, 1,
|
||||||
vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eGeometry |
|
vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eGeometry},
|
||||||
vk::ShaderStageFlagBits::eFragment},
|
{2, vk::DescriptorType::eUniformBufferDynamic, 1, vk::ShaderStageFlagBits::eFragment},
|
||||||
{2, vk::DescriptorType::eUniformTexelBuffer, 1, vk::ShaderStageFlagBits::eFragment},
|
|
||||||
{3, vk::DescriptorType::eUniformTexelBuffer, 1, vk::ShaderStageFlagBits::eFragment},
|
{3, vk::DescriptorType::eUniformTexelBuffer, 1, vk::ShaderStageFlagBits::eFragment},
|
||||||
{4, vk::DescriptorType::eUniformTexelBuffer, 1, vk::ShaderStageFlagBits::eFragment},
|
{4, vk::DescriptorType::eUniformTexelBuffer, 1, vk::ShaderStageFlagBits::eFragment},
|
||||||
|
{5, vk::DescriptorType::eUniformTexelBuffer, 1, vk::ShaderStageFlagBits::eFragment},
|
||||||
}};
|
}};
|
||||||
|
|
||||||
constexpr std::array<vk::DescriptorSetLayoutBinding, 4> TEXTURE_BINDINGS = {{
|
constexpr std::array<vk::DescriptorSetLayoutBinding, 4> TEXTURE_BINDINGS = {{
|
||||||
|
@ -88,8 +83,9 @@ PipelineCache::PipelineCache(const Instance& instance_, Scheduler& scheduler_,
|
||||||
descriptor_set_providers{DescriptorSetProvider{instance, pool, BUFFER_BINDINGS},
|
descriptor_set_providers{DescriptorSetProvider{instance, pool, BUFFER_BINDINGS},
|
||||||
DescriptorSetProvider{instance, pool, TEXTURE_BINDINGS},
|
DescriptorSetProvider{instance, pool, TEXTURE_BINDINGS},
|
||||||
DescriptorSetProvider{instance, pool, SHADOW_BINDINGS}},
|
DescriptorSetProvider{instance, pool, SHADOW_BINDINGS}},
|
||||||
trivial_vertex_shader{instance, vk::ShaderStageFlagBits::eVertex,
|
trivial_vertex_shader{
|
||||||
GenerateTrivialVertexShader(instance.IsShaderClipDistanceSupported())} {
|
instance, vk::ShaderStageFlagBits::eVertex,
|
||||||
|
GLSL::GenerateTrivialVertexShader(instance.IsShaderClipDistanceSupported(), true)} {
|
||||||
BuildLayout();
|
BuildLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -294,8 +290,8 @@ bool PipelineCache::BindPipeline(const PipelineInfo& info, bool wait_built) {
|
||||||
bool PipelineCache::UseProgrammableVertexShader(const Pica::Regs& regs,
|
bool PipelineCache::UseProgrammableVertexShader(const Pica::Regs& regs,
|
||||||
Pica::Shader::ShaderSetup& setup,
|
Pica::Shader::ShaderSetup& setup,
|
||||||
const VertexLayout& layout) {
|
const VertexLayout& layout) {
|
||||||
PicaVSConfig config{regs.rasterizer, regs.vs, setup, instance};
|
PicaVSConfig config{regs, setup, instance.IsShaderClipDistanceSupported(),
|
||||||
config.state.use_geometry_shader = instance.UseGeometryShaders();
|
instance.UseGeometryShaders()};
|
||||||
|
|
||||||
for (u32 i = 0; i < layout.attribute_count; i++) {
|
for (u32 i = 0; i < layout.attribute_count; i++) {
|
||||||
const VertexAttribute& attr = layout.attributes[i];
|
const VertexAttribute& attr = layout.attributes[i];
|
||||||
|
@ -313,14 +309,13 @@ bool PipelineCache::UseProgrammableVertexShader(const Pica::Regs& regs,
|
||||||
|
|
||||||
auto [it, new_config] = programmable_vertex_map.try_emplace(config);
|
auto [it, new_config] = programmable_vertex_map.try_emplace(config);
|
||||||
if (new_config) {
|
if (new_config) {
|
||||||
auto code = GenerateVertexShader(setup, config);
|
auto program = GLSL::GenerateVertexShader(setup, config, true);
|
||||||
if (!code) {
|
if (program.empty()) {
|
||||||
LOG_ERROR(Render_Vulkan, "Failed to retrieve programmable vertex shader");
|
LOG_ERROR(Render_Vulkan, "Failed to retrieve programmable vertex shader");
|
||||||
programmable_vertex_map[config] = nullptr;
|
programmable_vertex_map[config] = nullptr;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string& program = code.value();
|
|
||||||
auto [iter, new_program] = programmable_vertex_cache.try_emplace(program, instance);
|
auto [iter, new_program] = programmable_vertex_cache.try_emplace(program, instance);
|
||||||
auto& shader = iter->second;
|
auto& shader = iter->second;
|
||||||
|
|
||||||
|
@ -359,13 +354,13 @@ bool PipelineCache::UseFixedGeometryShader(const Pica::Regs& regs) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const PicaFixedGSConfig gs_config{regs, instance};
|
const PicaFixedGSConfig gs_config{regs, instance.IsShaderClipDistanceSupported()};
|
||||||
auto [it, new_shader] = fixed_geometry_shaders.try_emplace(gs_config, instance);
|
auto [it, new_shader] = fixed_geometry_shaders.try_emplace(gs_config, instance);
|
||||||
auto& shader = it->second;
|
auto& shader = it->second;
|
||||||
|
|
||||||
if (new_shader) {
|
if (new_shader) {
|
||||||
workers.QueueWork([gs_config, device = instance.GetDevice(), &shader]() {
|
workers.QueueWork([gs_config, device = instance.GetDevice(), &shader]() {
|
||||||
const std::string code = GenerateFixedGeometryShader(gs_config);
|
const auto code = GLSL::GenerateFixedGeometryShader(gs_config, true);
|
||||||
shader.module = Compile(code, vk::ShaderStageFlagBits::eGeometry, device);
|
shader.module = Compile(code, vk::ShaderStageFlagBits::eGeometry, device);
|
||||||
shader.MarkDone();
|
shader.MarkDone();
|
||||||
});
|
});
|
||||||
|
@ -383,7 +378,9 @@ void PipelineCache::UseTrivialGeometryShader() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void PipelineCache::UseFragmentShader(const Pica::Regs& regs) {
|
void PipelineCache::UseFragmentShader(const Pica::Regs& regs) {
|
||||||
const PicaFSConfig config{regs, instance};
|
const PicaFSConfig config{regs, instance.IsFragmentShaderInterlockSupported(),
|
||||||
|
instance.NeedsLogicOpEmulation(),
|
||||||
|
!instance.IsCustomBorderColorSupported(), false};
|
||||||
|
|
||||||
const auto [it, new_shader] = fragment_shaders.try_emplace(config, instance);
|
const auto [it, new_shader] = fragment_shaders.try_emplace(config, instance);
|
||||||
auto& shader = it->second;
|
auto& shader = it->second;
|
||||||
|
@ -395,12 +392,12 @@ void PipelineCache::UseFragmentShader(const Pica::Regs& regs) {
|
||||||
texture0_type == Pica::TexturingRegs::TextureConfig::ShadowCube ||
|
texture0_type == Pica::TexturingRegs::TextureConfig::ShadowCube ||
|
||||||
config.state.shadow_rendering.Value();
|
config.state.shadow_rendering.Value();
|
||||||
if (use_spirv && !is_shadow) {
|
if (use_spirv && !is_shadow) {
|
||||||
const std::vector code = GenerateFragmentShaderSPV(config);
|
const std::vector code = SPIRV::GenerateFragmentShader(config);
|
||||||
shader.module = CompileSPV(code, instance.GetDevice());
|
shader.module = CompileSPV(code, instance.GetDevice());
|
||||||
shader.MarkDone();
|
shader.MarkDone();
|
||||||
} else {
|
} else {
|
||||||
workers.QueueWork([config, device = instance.GetDevice(), &shader]() {
|
workers.QueueWork([config, device = instance.GetDevice(), &shader]() {
|
||||||
const std::string code = GenerateFragmentShader(config);
|
const std::string code = GLSL::GenerateFragmentShader(config, true);
|
||||||
shader.module = Compile(code, vk::ShaderStageFlagBits::eFragment, device);
|
shader.module = Compile(code, vk::ShaderStageFlagBits::eFragment, device);
|
||||||
shader.MarkDone();
|
shader.MarkDone();
|
||||||
});
|
});
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
|
|
||||||
#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
|
#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
|
||||||
#include "video_core/renderer_vulkan/vk_graphics_pipeline.h"
|
#include "video_core/renderer_vulkan/vk_graphics_pipeline.h"
|
||||||
|
#include "video_core/shader/generator/glsl_shader_gen.h"
|
||||||
|
#include "video_core/shader/generator/spv_shader_gen.h"
|
||||||
|
|
||||||
namespace Pica {
|
namespace Pica {
|
||||||
struct Regs;
|
struct Regs;
|
||||||
|
@ -22,7 +24,7 @@ class RenderpassCache;
|
||||||
class DescriptorPool;
|
class DescriptorPool;
|
||||||
|
|
||||||
constexpr u32 NUM_RASTERIZER_SETS = 3;
|
constexpr u32 NUM_RASTERIZER_SETS = 3;
|
||||||
constexpr u32 NUM_DYNAMIC_OFFSETS = 2;
|
constexpr u32 NUM_DYNAMIC_OFFSETS = 3;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores a collection of rasterizer pipelines used during rendering.
|
* Stores a collection of rasterizer pipelines used during rendering.
|
||||||
|
@ -113,10 +115,10 @@ private:
|
||||||
|
|
||||||
std::array<u64, MAX_SHADER_STAGES> shader_hashes;
|
std::array<u64, MAX_SHADER_STAGES> shader_hashes;
|
||||||
std::array<Shader*, MAX_SHADER_STAGES> current_shaders;
|
std::array<Shader*, MAX_SHADER_STAGES> current_shaders;
|
||||||
std::unordered_map<PicaVSConfig, Shader*> programmable_vertex_map;
|
std::unordered_map<Pica::Shader::Generator::PicaVSConfig, Shader*> programmable_vertex_map;
|
||||||
std::unordered_map<std::string, Shader> programmable_vertex_cache;
|
std::unordered_map<std::string, Shader> programmable_vertex_cache;
|
||||||
std::unordered_map<PicaFixedGSConfig, Shader> fixed_geometry_shaders;
|
std::unordered_map<Pica::Shader::Generator::PicaFixedGSConfig, Shader> fixed_geometry_shaders;
|
||||||
std::unordered_map<PicaFSConfig, Shader> fragment_shaders;
|
std::unordered_map<Pica::Shader::Generator::PicaFSConfig, Shader> fragment_shaders;
|
||||||
Shader trivial_vertex_shader;
|
Shader trivial_vertex_shader;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,8 @@ MICROPROFILE_DEFINE(Vulkan_Drawing, "Vulkan", "Drawing", MP_RGB(128, 128, 192));
|
||||||
using TriangleTopology = Pica::PipelineRegs::TriangleTopology;
|
using TriangleTopology = Pica::PipelineRegs::TriangleTopology;
|
||||||
using VideoCore::SurfaceType;
|
using VideoCore::SurfaceType;
|
||||||
|
|
||||||
|
using namespace Pica::Shader::Generator;
|
||||||
|
|
||||||
constexpr u64 STREAM_BUFFER_SIZE = 64 * 1024 * 1024;
|
constexpr u64 STREAM_BUFFER_SIZE = 64 * 1024 * 1024;
|
||||||
constexpr u64 UNIFORM_BUFFER_SIZE = 4 * 1024 * 1024;
|
constexpr u64 UNIFORM_BUFFER_SIZE = 4 * 1024 * 1024;
|
||||||
constexpr u64 TEXTURE_BUFFER_SIZE = 2 * 1024 * 1024;
|
constexpr u64 TEXTURE_BUFFER_SIZE = 2 * 1024 * 1024;
|
||||||
|
@ -76,10 +78,10 @@ RasterizerVulkan::RasterizerVulkan(Memory::MemorySystem& memory,
|
||||||
vertex_buffers.fill(stream_buffer.Handle());
|
vertex_buffers.fill(stream_buffer.Handle());
|
||||||
|
|
||||||
uniform_buffer_alignment = instance.UniformMinAlignment();
|
uniform_buffer_alignment = instance.UniformMinAlignment();
|
||||||
uniform_size_aligned_vs =
|
uniform_size_aligned_vs_pica =
|
||||||
Common::AlignUp(sizeof(Pica::Shader::VSUniformData), uniform_buffer_alignment);
|
Common::AlignUp(sizeof(VSPicaUniformData), uniform_buffer_alignment);
|
||||||
uniform_size_aligned_fs =
|
uniform_size_aligned_vs = Common::AlignUp(sizeof(VSUniformData), uniform_buffer_alignment);
|
||||||
Common::AlignUp(sizeof(Pica::Shader::UniformData), uniform_buffer_alignment);
|
uniform_size_aligned_fs = Common::AlignUp(sizeof(FSUniformData), uniform_buffer_alignment);
|
||||||
|
|
||||||
// Define vertex layout for software shaders
|
// Define vertex layout for software shaders
|
||||||
MakeSoftwareVertexLayout();
|
MakeSoftwareVertexLayout();
|
||||||
|
@ -107,11 +109,12 @@ RasterizerVulkan::RasterizerVulkan(Memory::MemorySystem& memory,
|
||||||
|
|
||||||
// Since we don't have access to VK_EXT_descriptor_indexing we need to intiallize
|
// Since we don't have access to VK_EXT_descriptor_indexing we need to intiallize
|
||||||
// all descriptor sets even the ones we don't use.
|
// all descriptor sets even the ones we don't use.
|
||||||
pipeline_cache.BindBuffer(0, uniform_buffer.Handle(), 0, sizeof(Pica::Shader::VSUniformData));
|
pipeline_cache.BindBuffer(0, uniform_buffer.Handle(), 0, sizeof(VSPicaUniformData));
|
||||||
pipeline_cache.BindBuffer(1, uniform_buffer.Handle(), 0, sizeof(Pica::Shader::UniformData));
|
pipeline_cache.BindBuffer(1, uniform_buffer.Handle(), 0, sizeof(VSUniformData));
|
||||||
pipeline_cache.BindTexelBuffer(2, *texture_lf_view);
|
pipeline_cache.BindBuffer(2, uniform_buffer.Handle(), 0, sizeof(FSUniformData));
|
||||||
pipeline_cache.BindTexelBuffer(3, *texture_rg_view);
|
pipeline_cache.BindTexelBuffer(3, *texture_lf_view);
|
||||||
pipeline_cache.BindTexelBuffer(4, *texture_rgba_view);
|
pipeline_cache.BindTexelBuffer(4, *texture_rg_view);
|
||||||
|
pipeline_cache.BindTexelBuffer(5, *texture_rgba_view);
|
||||||
|
|
||||||
Surface& null_surface = res_cache.GetSurface(VideoCore::NULL_SURFACE_ID);
|
Surface& null_surface = res_cache.GetSurface(VideoCore::NULL_SURFACE_ID);
|
||||||
Surface& null_cube_surface = res_cache.GetSurface(VideoCore::NULL_SURFACE_CUBE_ID);
|
Surface& null_cube_surface = res_cache.GetSurface(VideoCore::NULL_SURFACE_CUBE_ID);
|
||||||
|
@ -140,7 +143,6 @@ void RasterizerVulkan::LoadDiskResources(const std::atomic_bool& stop_loading,
|
||||||
}
|
}
|
||||||
|
|
||||||
void RasterizerVulkan::SyncFixedState() {
|
void RasterizerVulkan::SyncFixedState() {
|
||||||
SyncClipEnabled();
|
|
||||||
SyncCullMode();
|
SyncCullMode();
|
||||||
SyncBlendEnabled();
|
SyncBlendEnabled();
|
||||||
SyncBlendFuncs();
|
SyncBlendFuncs();
|
||||||
|
@ -478,16 +480,16 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
|
||||||
|
|
||||||
// Update scissor uniforms
|
// Update scissor uniforms
|
||||||
const auto [scissor_x1, scissor_y2, scissor_x2, scissor_y1] = fb_helper.Scissor();
|
const auto [scissor_x1, scissor_y2, scissor_x2, scissor_y1] = fb_helper.Scissor();
|
||||||
if (uniform_block_data.data.scissor_x1 != scissor_x1 ||
|
if (fs_uniform_block_data.data.scissor_x1 != scissor_x1 ||
|
||||||
uniform_block_data.data.scissor_x2 != scissor_x2 ||
|
fs_uniform_block_data.data.scissor_x2 != scissor_x2 ||
|
||||||
uniform_block_data.data.scissor_y1 != scissor_y1 ||
|
fs_uniform_block_data.data.scissor_y1 != scissor_y1 ||
|
||||||
uniform_block_data.data.scissor_y2 != scissor_y2) {
|
fs_uniform_block_data.data.scissor_y2 != scissor_y2) {
|
||||||
|
|
||||||
uniform_block_data.data.scissor_x1 = scissor_x1;
|
fs_uniform_block_data.data.scissor_x1 = scissor_x1;
|
||||||
uniform_block_data.data.scissor_x2 = scissor_x2;
|
fs_uniform_block_data.data.scissor_x2 = scissor_x2;
|
||||||
uniform_block_data.data.scissor_y1 = scissor_y1;
|
fs_uniform_block_data.data.scissor_y1 = scissor_y1;
|
||||||
uniform_block_data.data.scissor_y2 = scissor_y2;
|
fs_uniform_block_data.data.scissor_y2 = scissor_y2;
|
||||||
uniform_block_data.dirty = true;
|
fs_uniform_block_data.dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sync and bind the texture surfaces
|
// Sync and bind the texture surfaces
|
||||||
|
@ -670,11 +672,6 @@ void RasterizerVulkan::UnbindSpecial() {
|
||||||
|
|
||||||
void RasterizerVulkan::NotifyFixedFunctionPicaRegisterChanged(u32 id) {
|
void RasterizerVulkan::NotifyFixedFunctionPicaRegisterChanged(u32 id) {
|
||||||
switch (id) {
|
switch (id) {
|
||||||
// Clipping plane
|
|
||||||
case PICA_REG_INDEX(rasterizer.clip_enable):
|
|
||||||
SyncClipEnabled();
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Culling
|
// Culling
|
||||||
case PICA_REG_INDEX(rasterizer.cull_mode):
|
case PICA_REG_INDEX(rasterizer.cull_mode):
|
||||||
SyncCullMode();
|
SyncCullMode();
|
||||||
|
@ -831,14 +828,6 @@ void RasterizerVulkan::MakeSoftwareVertexLayout() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RasterizerVulkan::SyncClipEnabled() {
|
|
||||||
bool clip_enabled = regs.rasterizer.clip_enable != 0;
|
|
||||||
if (clip_enabled != uniform_block_data.data.enable_clip1) {
|
|
||||||
uniform_block_data.data.enable_clip1 = clip_enabled;
|
|
||||||
uniform_block_data.dirty = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void RasterizerVulkan::SyncCullMode() {
|
void RasterizerVulkan::SyncCullMode() {
|
||||||
pipeline_info.rasterization.cull_mode.Assign(regs.rasterizer.cull_mode);
|
pipeline_info.rasterization.cull_mode.Assign(regs.rasterizer.cull_mode);
|
||||||
}
|
}
|
||||||
|
@ -946,7 +935,7 @@ void RasterizerVulkan::SyncAndUploadLUTsLF() {
|
||||||
sizeof(Common::Vec2f) * 256 * Pica::LightingRegs::NumLightingSampler +
|
sizeof(Common::Vec2f) * 256 * Pica::LightingRegs::NumLightingSampler +
|
||||||
sizeof(Common::Vec2f) * 128; // fog
|
sizeof(Common::Vec2f) * 128; // fog
|
||||||
|
|
||||||
if (!uniform_block_data.lighting_lut_dirty_any && !uniform_block_data.fog_lut_dirty) {
|
if (!fs_uniform_block_data.lighting_lut_dirty_any && !fs_uniform_block_data.fog_lut_dirty) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -954,9 +943,9 @@ void RasterizerVulkan::SyncAndUploadLUTsLF() {
|
||||||
auto [buffer, offset, invalidate] = texture_lf_buffer.Map(max_size, sizeof(Common::Vec4f));
|
auto [buffer, offset, invalidate] = texture_lf_buffer.Map(max_size, sizeof(Common::Vec4f));
|
||||||
|
|
||||||
// Sync the lighting luts
|
// Sync the lighting luts
|
||||||
if (uniform_block_data.lighting_lut_dirty_any || invalidate) {
|
if (fs_uniform_block_data.lighting_lut_dirty_any || invalidate) {
|
||||||
for (unsigned index = 0; index < uniform_block_data.lighting_lut_dirty.size(); index++) {
|
for (unsigned index = 0; index < fs_uniform_block_data.lighting_lut_dirty.size(); index++) {
|
||||||
if (uniform_block_data.lighting_lut_dirty[index] || invalidate) {
|
if (fs_uniform_block_data.lighting_lut_dirty[index] || invalidate) {
|
||||||
std::array<Common::Vec2f, 256> new_data;
|
std::array<Common::Vec2f, 256> new_data;
|
||||||
const auto& source_lut = Pica::g_state.lighting.luts[index];
|
const auto& source_lut = Pica::g_state.lighting.luts[index];
|
||||||
std::transform(source_lut.begin(), source_lut.end(), new_data.begin(),
|
std::transform(source_lut.begin(), source_lut.end(), new_data.begin(),
|
||||||
|
@ -968,19 +957,19 @@ void RasterizerVulkan::SyncAndUploadLUTsLF() {
|
||||||
lighting_lut_data[index] = new_data;
|
lighting_lut_data[index] = new_data;
|
||||||
std::memcpy(buffer + bytes_used, new_data.data(),
|
std::memcpy(buffer + bytes_used, new_data.data(),
|
||||||
new_data.size() * sizeof(Common::Vec2f));
|
new_data.size() * sizeof(Common::Vec2f));
|
||||||
uniform_block_data.data.lighting_lut_offset[index / 4][index % 4] =
|
fs_uniform_block_data.data.lighting_lut_offset[index / 4][index % 4] =
|
||||||
static_cast<int>((offset + bytes_used) / sizeof(Common::Vec2f));
|
static_cast<int>((offset + bytes_used) / sizeof(Common::Vec2f));
|
||||||
uniform_block_data.dirty = true;
|
fs_uniform_block_data.dirty = true;
|
||||||
bytes_used += new_data.size() * sizeof(Common::Vec2f);
|
bytes_used += new_data.size() * sizeof(Common::Vec2f);
|
||||||
}
|
}
|
||||||
uniform_block_data.lighting_lut_dirty[index] = false;
|
fs_uniform_block_data.lighting_lut_dirty[index] = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
uniform_block_data.lighting_lut_dirty_any = false;
|
fs_uniform_block_data.lighting_lut_dirty_any = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sync the fog lut
|
// Sync the fog lut
|
||||||
if (uniform_block_data.fog_lut_dirty || invalidate) {
|
if (fs_uniform_block_data.fog_lut_dirty || invalidate) {
|
||||||
std::array<Common::Vec2f, 128> new_data;
|
std::array<Common::Vec2f, 128> new_data;
|
||||||
|
|
||||||
std::transform(Pica::g_state.fog.lut.begin(), Pica::g_state.fog.lut.end(), new_data.begin(),
|
std::transform(Pica::g_state.fog.lut.begin(), Pica::g_state.fog.lut.end(), new_data.begin(),
|
||||||
|
@ -992,12 +981,12 @@ void RasterizerVulkan::SyncAndUploadLUTsLF() {
|
||||||
fog_lut_data = new_data;
|
fog_lut_data = new_data;
|
||||||
std::memcpy(buffer + bytes_used, new_data.data(),
|
std::memcpy(buffer + bytes_used, new_data.data(),
|
||||||
new_data.size() * sizeof(Common::Vec2f));
|
new_data.size() * sizeof(Common::Vec2f));
|
||||||
uniform_block_data.data.fog_lut_offset =
|
fs_uniform_block_data.data.fog_lut_offset =
|
||||||
static_cast<int>((offset + bytes_used) / sizeof(Common::Vec2f));
|
static_cast<int>((offset + bytes_used) / sizeof(Common::Vec2f));
|
||||||
uniform_block_data.dirty = true;
|
fs_uniform_block_data.dirty = true;
|
||||||
bytes_used += new_data.size() * sizeof(Common::Vec2f);
|
bytes_used += new_data.size() * sizeof(Common::Vec2f);
|
||||||
}
|
}
|
||||||
uniform_block_data.fog_lut_dirty = false;
|
fs_uniform_block_data.fog_lut_dirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
texture_lf_buffer.Commit(static_cast<u32>(bytes_used));
|
texture_lf_buffer.Commit(static_cast<u32>(bytes_used));
|
||||||
|
@ -1010,10 +999,10 @@ void RasterizerVulkan::SyncAndUploadLUTs() {
|
||||||
sizeof(Common::Vec4f) * 256 + // proctex
|
sizeof(Common::Vec4f) * 256 + // proctex
|
||||||
sizeof(Common::Vec4f) * 256; // proctex diff
|
sizeof(Common::Vec4f) * 256; // proctex diff
|
||||||
|
|
||||||
if (!uniform_block_data.proctex_noise_lut_dirty &&
|
if (!fs_uniform_block_data.proctex_noise_lut_dirty &&
|
||||||
!uniform_block_data.proctex_color_map_dirty &&
|
!fs_uniform_block_data.proctex_color_map_dirty &&
|
||||||
!uniform_block_data.proctex_alpha_map_dirty && !uniform_block_data.proctex_lut_dirty &&
|
!fs_uniform_block_data.proctex_alpha_map_dirty &&
|
||||||
!uniform_block_data.proctex_diff_lut_dirty) {
|
!fs_uniform_block_data.proctex_lut_dirty && !fs_uniform_block_data.proctex_diff_lut_dirty) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1035,34 +1024,34 @@ void RasterizerVulkan::SyncAndUploadLUTs() {
|
||||||
std::memcpy(buffer + bytes_used, new_data.data(),
|
std::memcpy(buffer + bytes_used, new_data.data(),
|
||||||
new_data.size() * sizeof(Common::Vec2f));
|
new_data.size() * sizeof(Common::Vec2f));
|
||||||
lut_offset = static_cast<int>((offset + bytes_used) / sizeof(Common::Vec2f));
|
lut_offset = static_cast<int>((offset + bytes_used) / sizeof(Common::Vec2f));
|
||||||
uniform_block_data.dirty = true;
|
fs_uniform_block_data.dirty = true;
|
||||||
bytes_used += new_data.size() * sizeof(Common::Vec2f);
|
bytes_used += new_data.size() * sizeof(Common::Vec2f);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Sync the proctex noise lut
|
// Sync the proctex noise lut
|
||||||
if (uniform_block_data.proctex_noise_lut_dirty || invalidate) {
|
if (fs_uniform_block_data.proctex_noise_lut_dirty || invalidate) {
|
||||||
sync_proctex_value_lut(proctex.noise_table, proctex_noise_lut_data,
|
sync_proctex_value_lut(proctex.noise_table, proctex_noise_lut_data,
|
||||||
uniform_block_data.data.proctex_noise_lut_offset);
|
fs_uniform_block_data.data.proctex_noise_lut_offset);
|
||||||
uniform_block_data.proctex_noise_lut_dirty = false;
|
fs_uniform_block_data.proctex_noise_lut_dirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sync the proctex color map
|
// Sync the proctex color map
|
||||||
if (uniform_block_data.proctex_color_map_dirty || invalidate) {
|
if (fs_uniform_block_data.proctex_color_map_dirty || invalidate) {
|
||||||
sync_proctex_value_lut(proctex.color_map_table, proctex_color_map_data,
|
sync_proctex_value_lut(proctex.color_map_table, proctex_color_map_data,
|
||||||
uniform_block_data.data.proctex_color_map_offset);
|
fs_uniform_block_data.data.proctex_color_map_offset);
|
||||||
uniform_block_data.proctex_color_map_dirty = false;
|
fs_uniform_block_data.proctex_color_map_dirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sync the proctex alpha map
|
// Sync the proctex alpha map
|
||||||
if (uniform_block_data.proctex_alpha_map_dirty || invalidate) {
|
if (fs_uniform_block_data.proctex_alpha_map_dirty || invalidate) {
|
||||||
sync_proctex_value_lut(proctex.alpha_map_table, proctex_alpha_map_data,
|
sync_proctex_value_lut(proctex.alpha_map_table, proctex_alpha_map_data,
|
||||||
uniform_block_data.data.proctex_alpha_map_offset);
|
fs_uniform_block_data.data.proctex_alpha_map_offset);
|
||||||
uniform_block_data.proctex_alpha_map_dirty = false;
|
fs_uniform_block_data.proctex_alpha_map_dirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sync the proctex lut
|
// Sync the proctex lut
|
||||||
if (uniform_block_data.proctex_lut_dirty || invalidate) {
|
if (fs_uniform_block_data.proctex_lut_dirty || invalidate) {
|
||||||
std::array<Common::Vec4f, 256> new_data;
|
std::array<Common::Vec4f, 256> new_data;
|
||||||
|
|
||||||
std::transform(proctex.color_table.begin(), proctex.color_table.end(), new_data.begin(),
|
std::transform(proctex.color_table.begin(), proctex.color_table.end(), new_data.begin(),
|
||||||
|
@ -1075,16 +1064,16 @@ void RasterizerVulkan::SyncAndUploadLUTs() {
|
||||||
proctex_lut_data = new_data;
|
proctex_lut_data = new_data;
|
||||||
std::memcpy(buffer + bytes_used, new_data.data(),
|
std::memcpy(buffer + bytes_used, new_data.data(),
|
||||||
new_data.size() * sizeof(Common::Vec4f));
|
new_data.size() * sizeof(Common::Vec4f));
|
||||||
uniform_block_data.data.proctex_lut_offset =
|
fs_uniform_block_data.data.proctex_lut_offset =
|
||||||
static_cast<int>((offset + bytes_used) / sizeof(Common::Vec4f));
|
static_cast<int>((offset + bytes_used) / sizeof(Common::Vec4f));
|
||||||
uniform_block_data.dirty = true;
|
fs_uniform_block_data.dirty = true;
|
||||||
bytes_used += new_data.size() * sizeof(Common::Vec4f);
|
bytes_used += new_data.size() * sizeof(Common::Vec4f);
|
||||||
}
|
}
|
||||||
uniform_block_data.proctex_lut_dirty = false;
|
fs_uniform_block_data.proctex_lut_dirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sync the proctex difference lut
|
// Sync the proctex difference lut
|
||||||
if (uniform_block_data.proctex_diff_lut_dirty || invalidate) {
|
if (fs_uniform_block_data.proctex_diff_lut_dirty || invalidate) {
|
||||||
std::array<Common::Vec4f, 256> new_data;
|
std::array<Common::Vec4f, 256> new_data;
|
||||||
|
|
||||||
std::transform(proctex.color_diff_table.begin(), proctex.color_diff_table.end(),
|
std::transform(proctex.color_diff_table.begin(), proctex.color_diff_table.end(),
|
||||||
|
@ -1097,48 +1086,59 @@ void RasterizerVulkan::SyncAndUploadLUTs() {
|
||||||
proctex_diff_lut_data = new_data;
|
proctex_diff_lut_data = new_data;
|
||||||
std::memcpy(buffer + bytes_used, new_data.data(),
|
std::memcpy(buffer + bytes_used, new_data.data(),
|
||||||
new_data.size() * sizeof(Common::Vec4f));
|
new_data.size() * sizeof(Common::Vec4f));
|
||||||
uniform_block_data.data.proctex_diff_lut_offset =
|
fs_uniform_block_data.data.proctex_diff_lut_offset =
|
||||||
static_cast<int>((offset + bytes_used) / sizeof(Common::Vec4f));
|
static_cast<int>((offset + bytes_used) / sizeof(Common::Vec4f));
|
||||||
uniform_block_data.dirty = true;
|
fs_uniform_block_data.dirty = true;
|
||||||
bytes_used += new_data.size() * sizeof(Common::Vec4f);
|
bytes_used += new_data.size() * sizeof(Common::Vec4f);
|
||||||
}
|
}
|
||||||
uniform_block_data.proctex_diff_lut_dirty = false;
|
fs_uniform_block_data.proctex_diff_lut_dirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
texture_buffer.Commit(static_cast<u32>(bytes_used));
|
texture_buffer.Commit(static_cast<u32>(bytes_used));
|
||||||
}
|
}
|
||||||
|
|
||||||
void RasterizerVulkan::UploadUniforms(bool accelerate_draw) {
|
void RasterizerVulkan::UploadUniforms(bool accelerate_draw) {
|
||||||
const bool sync_vs = accelerate_draw;
|
const bool sync_vs_pica = accelerate_draw;
|
||||||
const bool sync_fs = uniform_block_data.dirty;
|
const bool sync_vs = vs_uniform_block_data.dirty;
|
||||||
|
const bool sync_fs = fs_uniform_block_data.dirty;
|
||||||
if (!sync_vs && !sync_fs) {
|
if (!sync_vs_pica && !sync_vs && !sync_fs) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const u64 uniform_size = uniform_size_aligned_vs + uniform_size_aligned_fs;
|
const u64 uniform_size =
|
||||||
|
uniform_size_aligned_vs_pica + uniform_size_aligned_vs + uniform_size_aligned_fs;
|
||||||
auto [uniforms, offset, invalidate] =
|
auto [uniforms, offset, invalidate] =
|
||||||
uniform_buffer.Map(uniform_size, uniform_buffer_alignment);
|
uniform_buffer.Map(uniform_size, uniform_buffer_alignment);
|
||||||
|
|
||||||
u32 used_bytes = 0;
|
u32 used_bytes = 0;
|
||||||
if (sync_vs) {
|
|
||||||
Pica::Shader::VSUniformData vs_uniforms;
|
|
||||||
vs_uniforms.uniforms.SetFromRegs(regs.vs, Pica::g_state.vs);
|
|
||||||
std::memcpy(uniforms, &vs_uniforms, sizeof(vs_uniforms));
|
|
||||||
|
|
||||||
pipeline_cache.SetBufferOffset(0, offset);
|
if (sync_vs || invalidate) {
|
||||||
|
std::memcpy(uniforms + used_bytes, &vs_uniform_block_data.data,
|
||||||
|
sizeof(vs_uniform_block_data.data));
|
||||||
|
|
||||||
|
pipeline_cache.SetBufferOffset(1, offset + used_bytes);
|
||||||
|
vs_uniform_block_data.dirty = false;
|
||||||
used_bytes += static_cast<u32>(uniform_size_aligned_vs);
|
used_bytes += static_cast<u32>(uniform_size_aligned_vs);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sync_fs || invalidate) {
|
if (sync_fs || invalidate) {
|
||||||
std::memcpy(uniforms + used_bytes, &uniform_block_data.data,
|
std::memcpy(uniforms + used_bytes, &fs_uniform_block_data.data,
|
||||||
sizeof(Pica::Shader::UniformData));
|
sizeof(fs_uniform_block_data.data));
|
||||||
|
|
||||||
pipeline_cache.SetBufferOffset(1, offset + used_bytes);
|
pipeline_cache.SetBufferOffset(2, offset + used_bytes);
|
||||||
uniform_block_data.dirty = false;
|
fs_uniform_block_data.dirty = false;
|
||||||
used_bytes += static_cast<u32>(uniform_size_aligned_fs);
|
used_bytes += static_cast<u32>(uniform_size_aligned_fs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sync_vs_pica) {
|
||||||
|
VSPicaUniformData vs_uniforms;
|
||||||
|
vs_uniforms.uniforms.SetFromRegs(regs.vs, Pica::g_state.vs);
|
||||||
|
std::memcpy(uniforms + used_bytes, &vs_uniforms, sizeof(vs_uniforms));
|
||||||
|
|
||||||
|
pipeline_cache.SetBufferOffset(0, offset + used_bytes);
|
||||||
|
used_bytes += static_cast<u32>(uniform_size_aligned_vs_pica);
|
||||||
|
}
|
||||||
|
|
||||||
uniform_buffer.Commit(used_bytes);
|
uniform_buffer.Commit(used_bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -60,9 +60,6 @@ public:
|
||||||
private:
|
private:
|
||||||
void NotifyFixedFunctionPicaRegisterChanged(u32 id) override;
|
void NotifyFixedFunctionPicaRegisterChanged(u32 id) override;
|
||||||
|
|
||||||
/// Syncs the clip enabled status to match the PICA register
|
|
||||||
void SyncClipEnabled();
|
|
||||||
|
|
||||||
/// Syncs the cull mode to match the PICA register
|
/// Syncs the cull mode to match the PICA register
|
||||||
void SyncCullMode();
|
void SyncCullMode();
|
||||||
|
|
||||||
|
@ -163,6 +160,7 @@ private:
|
||||||
vk::UniqueBufferView texture_rg_view;
|
vk::UniqueBufferView texture_rg_view;
|
||||||
vk::UniqueBufferView texture_rgba_view;
|
vk::UniqueBufferView texture_rgba_view;
|
||||||
u64 uniform_buffer_alignment;
|
u64 uniform_buffer_alignment;
|
||||||
|
u64 uniform_size_aligned_vs_pica;
|
||||||
u64 uniform_size_aligned_vs;
|
u64 uniform_size_aligned_vs;
|
||||||
u64 uniform_size_aligned_fs;
|
u64 uniform_size_aligned_fs;
|
||||||
bool async_shaders{false};
|
bool async_shaders{false};
|
||||||
|
|
|
@ -12,9 +12,9 @@
|
||||||
#include <nihstro/shader_bytecode.h>
|
#include <nihstro/shader_bytecode.h>
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "video_core/renderer_opengl/gl_shader_decompiler.h"
|
#include "video_core/shader/generator/glsl_shader_decompiler.h"
|
||||||
|
|
||||||
namespace OpenGL::ShaderDecompiler {
|
namespace Pica::Shader::Generator::GLSL {
|
||||||
|
|
||||||
using nihstro::DestRegister;
|
using nihstro::DestRegister;
|
||||||
using nihstro::Instruction;
|
using nihstro::Instruction;
|
||||||
|
@ -939,34 +939,20 @@ private:
|
||||||
ShaderWriter shader;
|
ShaderWriter shader;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string GetCommonDeclarations() {
|
std::string DecompileProgram(const Pica::Shader::ProgramCode& program_code,
|
||||||
return R"(
|
const Pica::Shader::SwizzleData& swizzle_data, u32 main_offset,
|
||||||
struct pica_uniforms {
|
const RegGetter& inputreg_getter, const RegGetter& outputreg_getter,
|
||||||
bool b[16];
|
bool sanitize_mul) {
|
||||||
uvec4 i[4];
|
|
||||||
vec4 f[96];
|
|
||||||
};
|
|
||||||
|
|
||||||
bool exec_shader();
|
|
||||||
|
|
||||||
)";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<ProgramResult> DecompileProgram(const Pica::Shader::ProgramCode& program_code,
|
|
||||||
const Pica::Shader::SwizzleData& swizzle_data,
|
|
||||||
u32 main_offset, const RegGetter& inputreg_getter,
|
|
||||||
const RegGetter& outputreg_getter,
|
|
||||||
bool sanitize_mul) {
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
auto subroutines = ControlFlowAnalyzer(program_code, main_offset).MoveSubroutines();
|
auto subroutines = ControlFlowAnalyzer(program_code, main_offset).MoveSubroutines();
|
||||||
GLSLGenerator generator(subroutines, program_code, swizzle_data, main_offset,
|
GLSLGenerator generator(subroutines, program_code, swizzle_data, main_offset,
|
||||||
inputreg_getter, outputreg_getter, sanitize_mul);
|
inputreg_getter, outputreg_getter, sanitize_mul);
|
||||||
return {ProgramResult{generator.MoveShaderCode()}};
|
return generator.MoveShaderCode();
|
||||||
} catch (const DecompileFail& exception) {
|
} catch (const DecompileFail& exception) {
|
||||||
LOG_INFO(HW_GPU, "Shader decompilation failed: {}", exception.what());
|
LOG_INFO(HW_GPU, "Shader decompilation failed: {}", exception.what());
|
||||||
return std::nullopt;
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace OpenGL::ShaderDecompiler
|
} // namespace Pica::Shader::Generator::GLSL
|
|
@ -0,0 +1,21 @@
|
||||||
|
// Copyright 2017 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <string>
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "video_core/shader/shader.h"
|
||||||
|
|
||||||
|
namespace Pica::Shader::Generator::GLSL {
|
||||||
|
|
||||||
|
using RegGetter = std::function<std::string(u32)>;
|
||||||
|
|
||||||
|
std::string DecompileProgram(const Pica::Shader::ProgramCode& program_code,
|
||||||
|
const Pica::Shader::SwizzleData& swizzle_data, u32 main_offset,
|
||||||
|
const RegGetter& inputreg_getter, const RegGetter& outputreg_getter,
|
||||||
|
bool sanitize_mul);
|
||||||
|
|
||||||
|
} // namespace Pica::Shader::Generator::GLSL
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,57 @@
|
||||||
|
// Copyright 2023 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "video_core/shader/generator/shader_gen.h"
|
||||||
|
#include "video_core/shader/shader.h"
|
||||||
|
|
||||||
|
// High precision may or may not be supported in GLES3. If it isn't, use medium precision instead.
|
||||||
|
static constexpr char fragment_shader_precision_OES[] = R"(
|
||||||
|
#if GL_ES
|
||||||
|
#ifdef GL_FRAGMENT_PRECISION_HIGH
|
||||||
|
precision highp int;
|
||||||
|
precision highp float;
|
||||||
|
precision highp samplerBuffer;
|
||||||
|
precision highp uimage2D;
|
||||||
|
#else
|
||||||
|
precision mediump int;
|
||||||
|
precision mediump float;
|
||||||
|
precision mediump samplerBuffer;
|
||||||
|
precision mediump uimage2D;
|
||||||
|
#endif // GL_FRAGMENT_PRECISION_HIGH
|
||||||
|
#endif
|
||||||
|
)";
|
||||||
|
|
||||||
|
namespace Pica::Shader::Generator::GLSL {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the GLSL vertex shader program source code that accepts vertices from software shader
|
||||||
|
* and directly passes them to the fragment shader.
|
||||||
|
* @returns String of the shader source code
|
||||||
|
*/
|
||||||
|
std::string GenerateTrivialVertexShader(bool use_clip_planes, bool separable_shader);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the GLSL vertex shader program source code for the given VS program
|
||||||
|
* @returns String of the shader source code; empty on failure
|
||||||
|
*/
|
||||||
|
std::string GenerateVertexShader(const Pica::Shader::ShaderSetup& setup, const PicaVSConfig& config,
|
||||||
|
bool separable_shader);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the GLSL fixed geometry shader program source code for non-GS PICA pipeline
|
||||||
|
* @returns String of the shader source code
|
||||||
|
*/
|
||||||
|
std::string GenerateFixedGeometryShader(const PicaFixedGSConfig& config, bool separable_shader);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the GLSL fragment shader program source code for the current Pica state
|
||||||
|
* @param config ShaderCacheKey object generated for the current Pica state, used for the shader
|
||||||
|
* configuration (NOTE: Use state in this struct only, not the Pica registers!)
|
||||||
|
* @returns String of the shader source code
|
||||||
|
*/
|
||||||
|
std::string GenerateFragmentShader(const PicaFSConfig& config, bool separable_shader);
|
||||||
|
|
||||||
|
} // namespace Pica::Shader::Generator::GLSL
|
|
@ -0,0 +1,281 @@
|
||||||
|
// Copyright 2023 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include "common/bit_set.h"
|
||||||
|
#include "common/logging/log.h"
|
||||||
|
#include "video_core/shader/generator/shader_gen.h"
|
||||||
|
#include "video_core/video_core.h"
|
||||||
|
|
||||||
|
namespace Pica::Shader::Generator {
|
||||||
|
|
||||||
|
PicaFSConfig::PicaFSConfig(const Pica::Regs& regs, bool has_fragment_shader_interlock,
|
||||||
|
bool emulate_logic_op, bool emulate_custom_border_color,
|
||||||
|
bool emulate_blend_minmax_factor, bool use_custom_normal_map) {
|
||||||
|
state.scissor_test_mode.Assign(regs.rasterizer.scissor_test.mode);
|
||||||
|
|
||||||
|
state.depthmap_enable.Assign(regs.rasterizer.depthmap_enable);
|
||||||
|
|
||||||
|
state.alpha_test_func.Assign(regs.framebuffer.output_merger.alpha_test.enable
|
||||||
|
? regs.framebuffer.output_merger.alpha_test.func.Value()
|
||||||
|
: Pica::FramebufferRegs::CompareFunc::Always);
|
||||||
|
|
||||||
|
state.texture0_type.Assign(regs.texturing.texture0.type);
|
||||||
|
|
||||||
|
state.texture2_use_coord1.Assign(regs.texturing.main_config.texture2_use_coord1 != 0);
|
||||||
|
|
||||||
|
const auto pica_textures = regs.texturing.GetTextures();
|
||||||
|
for (u32 tex_index = 0; tex_index < 3; tex_index++) {
|
||||||
|
const auto config = pica_textures[tex_index].config;
|
||||||
|
state.texture_border_color[tex_index].enable_s.Assign(
|
||||||
|
emulate_custom_border_color &&
|
||||||
|
config.wrap_s == Pica::TexturingRegs::TextureConfig::WrapMode::ClampToBorder);
|
||||||
|
state.texture_border_color[tex_index].enable_t.Assign(
|
||||||
|
emulate_custom_border_color &&
|
||||||
|
config.wrap_t == Pica::TexturingRegs::TextureConfig::WrapMode::ClampToBorder);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emulate logic op in the shader if not supported. This is mostly for mobile GPUs
|
||||||
|
const bool needs_emulate_logic_op =
|
||||||
|
emulate_logic_op && !regs.framebuffer.output_merger.alphablend_enable;
|
||||||
|
|
||||||
|
state.emulate_logic_op.Assign(needs_emulate_logic_op);
|
||||||
|
if (needs_emulate_logic_op) {
|
||||||
|
state.logic_op.Assign(regs.framebuffer.output_merger.logic_op);
|
||||||
|
} else {
|
||||||
|
state.logic_op.Assign(Pica::FramebufferRegs::LogicOp::NoOp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy relevant tev stages fields.
|
||||||
|
// We don't sync const_color here because of the high variance, it is a
|
||||||
|
// shader uniform instead.
|
||||||
|
const auto& tev_stages = regs.texturing.GetTevStages();
|
||||||
|
DEBUG_ASSERT(state.tev_stages.size() == tev_stages.size());
|
||||||
|
for (std::size_t i = 0; i < tev_stages.size(); i++) {
|
||||||
|
const auto& tev_stage = tev_stages[i];
|
||||||
|
state.tev_stages[i].sources_raw = tev_stage.sources_raw;
|
||||||
|
state.tev_stages[i].modifiers_raw = tev_stage.modifiers_raw;
|
||||||
|
state.tev_stages[i].ops_raw = tev_stage.ops_raw;
|
||||||
|
state.tev_stages[i].scales_raw = tev_stage.scales_raw;
|
||||||
|
if (tev_stage.color_op == Pica::TexturingRegs::TevStageConfig::Operation::Dot3_RGBA) {
|
||||||
|
state.tev_stages[i].sources_raw &= 0xFFF;
|
||||||
|
state.tev_stages[i].modifiers_raw &= 0xFFF;
|
||||||
|
state.tev_stages[i].ops_raw &= 0xF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
state.fog_mode.Assign(regs.texturing.fog_mode);
|
||||||
|
state.fog_flip.Assign(regs.texturing.fog_flip != 0);
|
||||||
|
|
||||||
|
state.combiner_buffer_input.Assign(
|
||||||
|
regs.texturing.tev_combiner_buffer_input.update_mask_rgb.Value() |
|
||||||
|
regs.texturing.tev_combiner_buffer_input.update_mask_a.Value() << 4);
|
||||||
|
|
||||||
|
// Fragment lighting
|
||||||
|
state.lighting.enable.Assign(!regs.lighting.disable);
|
||||||
|
if (state.lighting.enable) {
|
||||||
|
state.lighting.src_num.Assign(regs.lighting.max_light_index + 1);
|
||||||
|
|
||||||
|
for (u32 light_index = 0; light_index < state.lighting.src_num; ++light_index) {
|
||||||
|
const u32 num = regs.lighting.light_enable.GetNum(light_index);
|
||||||
|
const auto& light = regs.lighting.light[num];
|
||||||
|
state.lighting.light[light_index].num.Assign(num);
|
||||||
|
state.lighting.light[light_index].directional.Assign(light.config.directional != 0);
|
||||||
|
state.lighting.light[light_index].two_sided_diffuse.Assign(
|
||||||
|
light.config.two_sided_diffuse != 0);
|
||||||
|
state.lighting.light[light_index].geometric_factor_0.Assign(
|
||||||
|
light.config.geometric_factor_0 != 0);
|
||||||
|
state.lighting.light[light_index].geometric_factor_1.Assign(
|
||||||
|
light.config.geometric_factor_1 != 0);
|
||||||
|
state.lighting.light[light_index].dist_atten_enable.Assign(
|
||||||
|
!regs.lighting.IsDistAttenDisabled(num));
|
||||||
|
state.lighting.light[light_index].spot_atten_enable.Assign(
|
||||||
|
!regs.lighting.IsSpotAttenDisabled(num));
|
||||||
|
state.lighting.light[light_index].shadow_enable.Assign(
|
||||||
|
!regs.lighting.IsShadowDisabled(num));
|
||||||
|
}
|
||||||
|
|
||||||
|
state.lighting.lut_d0.enable.Assign(regs.lighting.config1.disable_lut_d0 == 0);
|
||||||
|
if (state.lighting.lut_d0.enable) {
|
||||||
|
state.lighting.lut_d0.abs_input.Assign(regs.lighting.abs_lut_input.disable_d0 == 0);
|
||||||
|
state.lighting.lut_d0.type.Assign(regs.lighting.lut_input.d0.Value());
|
||||||
|
state.lighting.lut_d0.scale =
|
||||||
|
regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.d0);
|
||||||
|
}
|
||||||
|
|
||||||
|
state.lighting.lut_d1.enable.Assign(regs.lighting.config1.disable_lut_d1 == 0);
|
||||||
|
if (state.lighting.lut_d1.enable) {
|
||||||
|
state.lighting.lut_d1.abs_input.Assign(regs.lighting.abs_lut_input.disable_d1 == 0);
|
||||||
|
state.lighting.lut_d1.type.Assign(regs.lighting.lut_input.d1.Value());
|
||||||
|
state.lighting.lut_d1.scale =
|
||||||
|
regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.d1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is a dummy field due to lack of the corresponding register
|
||||||
|
state.lighting.lut_sp.enable.Assign(1);
|
||||||
|
state.lighting.lut_sp.abs_input.Assign(regs.lighting.abs_lut_input.disable_sp == 0);
|
||||||
|
state.lighting.lut_sp.type.Assign(regs.lighting.lut_input.sp.Value());
|
||||||
|
state.lighting.lut_sp.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.sp);
|
||||||
|
|
||||||
|
state.lighting.lut_fr.enable.Assign(regs.lighting.config1.disable_lut_fr == 0);
|
||||||
|
if (state.lighting.lut_fr.enable) {
|
||||||
|
state.lighting.lut_fr.abs_input.Assign(regs.lighting.abs_lut_input.disable_fr == 0);
|
||||||
|
state.lighting.lut_fr.type.Assign(regs.lighting.lut_input.fr.Value());
|
||||||
|
state.lighting.lut_fr.scale =
|
||||||
|
regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
state.lighting.lut_rr.enable.Assign(regs.lighting.config1.disable_lut_rr == 0);
|
||||||
|
if (state.lighting.lut_rr.enable) {
|
||||||
|
state.lighting.lut_rr.abs_input.Assign(regs.lighting.abs_lut_input.disable_rr == 0);
|
||||||
|
state.lighting.lut_rr.type.Assign(regs.lighting.lut_input.rr.Value());
|
||||||
|
state.lighting.lut_rr.scale =
|
||||||
|
regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.rr);
|
||||||
|
}
|
||||||
|
|
||||||
|
state.lighting.lut_rg.enable.Assign(regs.lighting.config1.disable_lut_rg == 0);
|
||||||
|
if (state.lighting.lut_rg.enable) {
|
||||||
|
state.lighting.lut_rg.abs_input.Assign(regs.lighting.abs_lut_input.disable_rg == 0);
|
||||||
|
state.lighting.lut_rg.type.Assign(regs.lighting.lut_input.rg.Value());
|
||||||
|
state.lighting.lut_rg.scale =
|
||||||
|
regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.rg);
|
||||||
|
}
|
||||||
|
|
||||||
|
state.lighting.lut_rb.enable.Assign(regs.lighting.config1.disable_lut_rb == 0);
|
||||||
|
if (state.lighting.lut_rb.enable) {
|
||||||
|
state.lighting.lut_rb.abs_input.Assign(regs.lighting.abs_lut_input.disable_rb == 0);
|
||||||
|
state.lighting.lut_rb.type.Assign(regs.lighting.lut_input.rb.Value());
|
||||||
|
state.lighting.lut_rb.scale =
|
||||||
|
regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.rb);
|
||||||
|
}
|
||||||
|
|
||||||
|
state.lighting.config.Assign(regs.lighting.config0.config);
|
||||||
|
state.lighting.enable_primary_alpha.Assign(regs.lighting.config0.enable_primary_alpha);
|
||||||
|
state.lighting.enable_secondary_alpha.Assign(regs.lighting.config0.enable_secondary_alpha);
|
||||||
|
state.lighting.bump_mode.Assign(regs.lighting.config0.bump_mode);
|
||||||
|
state.lighting.bump_selector.Assign(regs.lighting.config0.bump_selector);
|
||||||
|
state.lighting.bump_renorm.Assign(regs.lighting.config0.disable_bump_renorm == 0);
|
||||||
|
state.lighting.clamp_highlights.Assign(regs.lighting.config0.clamp_highlights != 0);
|
||||||
|
|
||||||
|
state.lighting.enable_shadow.Assign(regs.lighting.config0.enable_shadow != 0);
|
||||||
|
if (state.lighting.enable_shadow) {
|
||||||
|
state.lighting.shadow_primary.Assign(regs.lighting.config0.shadow_primary != 0);
|
||||||
|
state.lighting.shadow_secondary.Assign(regs.lighting.config0.shadow_secondary != 0);
|
||||||
|
state.lighting.shadow_invert.Assign(regs.lighting.config0.shadow_invert != 0);
|
||||||
|
state.lighting.shadow_alpha.Assign(regs.lighting.config0.shadow_alpha != 0);
|
||||||
|
state.lighting.shadow_selector.Assign(regs.lighting.config0.shadow_selector);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
state.proctex.enable.Assign(regs.texturing.main_config.texture3_enable);
|
||||||
|
if (state.proctex.enable) {
|
||||||
|
state.proctex.coord.Assign(regs.texturing.main_config.texture3_coordinates);
|
||||||
|
state.proctex.u_clamp.Assign(regs.texturing.proctex.u_clamp);
|
||||||
|
state.proctex.v_clamp.Assign(regs.texturing.proctex.v_clamp);
|
||||||
|
state.proctex.color_combiner.Assign(regs.texturing.proctex.color_combiner);
|
||||||
|
state.proctex.alpha_combiner.Assign(regs.texturing.proctex.alpha_combiner);
|
||||||
|
state.proctex.separate_alpha.Assign(regs.texturing.proctex.separate_alpha);
|
||||||
|
state.proctex.noise_enable.Assign(regs.texturing.proctex.noise_enable);
|
||||||
|
state.proctex.u_shift.Assign(regs.texturing.proctex.u_shift);
|
||||||
|
state.proctex.v_shift.Assign(regs.texturing.proctex.v_shift);
|
||||||
|
state.proctex.lut_width = regs.texturing.proctex_lut.width;
|
||||||
|
state.proctex.lut_offset0 = regs.texturing.proctex_lut_offset.level0;
|
||||||
|
state.proctex.lut_offset1 = regs.texturing.proctex_lut_offset.level1;
|
||||||
|
state.proctex.lut_offset2 = regs.texturing.proctex_lut_offset.level2;
|
||||||
|
state.proctex.lut_offset3 = regs.texturing.proctex_lut_offset.level3;
|
||||||
|
state.proctex.lod_min = regs.texturing.proctex_lut.lod_min;
|
||||||
|
state.proctex.lod_max = regs.texturing.proctex_lut.lod_max;
|
||||||
|
state.proctex.lut_filter.Assign(regs.texturing.proctex_lut.filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto alpha_eq = regs.framebuffer.output_merger.alpha_blending.blend_equation_a.Value();
|
||||||
|
const auto rgb_eq = regs.framebuffer.output_merger.alpha_blending.blend_equation_rgb.Value();
|
||||||
|
if (emulate_blend_minmax_factor && regs.framebuffer.output_merger.alphablend_enable) {
|
||||||
|
if (rgb_eq == Pica::FramebufferRegs::BlendEquation::Max ||
|
||||||
|
rgb_eq == Pica::FramebufferRegs::BlendEquation::Min) {
|
||||||
|
state.rgb_blend.emulate_blending = true;
|
||||||
|
state.rgb_blend.eq = rgb_eq;
|
||||||
|
state.rgb_blend.src_factor =
|
||||||
|
regs.framebuffer.output_merger.alpha_blending.factor_source_rgb;
|
||||||
|
state.rgb_blend.dst_factor =
|
||||||
|
regs.framebuffer.output_merger.alpha_blending.factor_dest_rgb;
|
||||||
|
}
|
||||||
|
if (alpha_eq == Pica::FramebufferRegs::BlendEquation::Max ||
|
||||||
|
alpha_eq == Pica::FramebufferRegs::BlendEquation::Min) {
|
||||||
|
state.alpha_blend.emulate_blending = true;
|
||||||
|
state.alpha_blend.eq = alpha_eq;
|
||||||
|
state.alpha_blend.src_factor =
|
||||||
|
regs.framebuffer.output_merger.alpha_blending.factor_source_a;
|
||||||
|
state.alpha_blend.dst_factor =
|
||||||
|
regs.framebuffer.output_merger.alpha_blending.factor_dest_a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
state.shadow_rendering.Assign(regs.framebuffer.output_merger.fragment_operation_mode ==
|
||||||
|
Pica::FramebufferRegs::FragmentOperationMode::Shadow);
|
||||||
|
state.shadow_texture_orthographic.Assign(regs.texturing.shadow.orthographic != 0);
|
||||||
|
|
||||||
|
// We only need fragment shader interlock when shadow rendering.
|
||||||
|
state.use_fragment_shader_interlock.Assign(state.shadow_rendering &&
|
||||||
|
has_fragment_shader_interlock);
|
||||||
|
state.use_custom_normal_map.Assign(use_custom_normal_map);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PicaGSConfigState::Init(const Pica::Regs& regs, bool use_clip_planes_) {
|
||||||
|
use_clip_planes = use_clip_planes_;
|
||||||
|
|
||||||
|
vs_output_attributes = Common::BitSet<u32>(regs.vs.output_mask).Count();
|
||||||
|
gs_output_attributes = vs_output_attributes;
|
||||||
|
|
||||||
|
semantic_maps.fill({16, 0});
|
||||||
|
for (u32 attrib = 0; attrib < regs.rasterizer.vs_output_total; ++attrib) {
|
||||||
|
const std::array semantics{
|
||||||
|
regs.rasterizer.vs_output_attributes[attrib].map_x.Value(),
|
||||||
|
regs.rasterizer.vs_output_attributes[attrib].map_y.Value(),
|
||||||
|
regs.rasterizer.vs_output_attributes[attrib].map_z.Value(),
|
||||||
|
regs.rasterizer.vs_output_attributes[attrib].map_w.Value(),
|
||||||
|
};
|
||||||
|
for (u32 comp = 0; comp < 4; ++comp) {
|
||||||
|
const auto semantic = semantics[comp];
|
||||||
|
if (static_cast<std::size_t>(semantic) < 24) {
|
||||||
|
semantic_maps[static_cast<std::size_t>(semantic)] = {attrib, comp};
|
||||||
|
} else if (semantic != Pica::RasterizerRegs::VSOutputAttributes::INVALID) {
|
||||||
|
LOG_ERROR(Render, "Invalid/unknown semantic id: {}", semantic);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PicaVSConfigState::Init(const Pica::Regs& regs, Pica::Shader::ShaderSetup& setup,
|
||||||
|
bool use_clip_planes_, bool use_geometry_shader_) {
|
||||||
|
use_clip_planes = use_clip_planes_;
|
||||||
|
use_geometry_shader = use_geometry_shader_;
|
||||||
|
|
||||||
|
program_hash = setup.GetProgramCodeHash();
|
||||||
|
swizzle_hash = setup.GetSwizzleDataHash();
|
||||||
|
main_offset = regs.vs.main_offset;
|
||||||
|
sanitize_mul = VideoCore::g_hw_shader_accurate_mul;
|
||||||
|
|
||||||
|
num_outputs = 0;
|
||||||
|
load_flags.fill(AttribLoadFlags::Float);
|
||||||
|
output_map.fill(16);
|
||||||
|
|
||||||
|
for (int reg : Common::BitSet<u32>(regs.vs.output_mask)) {
|
||||||
|
output_map[reg] = num_outputs++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!use_geometry_shader_) {
|
||||||
|
gs_state.Init(regs, use_clip_planes_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PicaVSConfig::PicaVSConfig(const Pica::Regs& regs, Pica::Shader::ShaderSetup& setup,
|
||||||
|
bool use_clip_planes_, bool use_geometry_shader_) {
|
||||||
|
state.Init(regs, setup, use_clip_planes_, use_geometry_shader_);
|
||||||
|
}
|
||||||
|
|
||||||
|
PicaFixedGSConfig::PicaFixedGSConfig(const Pica::Regs& regs, bool use_clip_planes_) {
|
||||||
|
state.Init(regs, use_clip_planes_);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Pica::Shader::Generator
|
|
@ -4,14 +4,17 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <optional>
|
|
||||||
#include "common/hash.h"
|
#include "common/hash.h"
|
||||||
#include "video_core/regs.h"
|
#include "video_core/regs.h"
|
||||||
#include "video_core/shader/shader.h"
|
#include "video_core/shader/shader.h"
|
||||||
|
|
||||||
namespace Vulkan {
|
namespace Pica::Shader::Generator {
|
||||||
|
|
||||||
class Instance;
|
enum ProgramType : u32 {
|
||||||
|
VS = 0,
|
||||||
|
GS = 2,
|
||||||
|
FS = 1,
|
||||||
|
};
|
||||||
|
|
||||||
enum Attributes {
|
enum Attributes {
|
||||||
ATTRIBUTE_POSITION,
|
ATTRIBUTE_POSITION,
|
||||||
|
@ -31,13 +34,13 @@ struct TevStageConfigRaw {
|
||||||
u32 ops_raw;
|
u32 ops_raw;
|
||||||
u32 scales_raw;
|
u32 scales_raw;
|
||||||
explicit operator Pica::TexturingRegs::TevStageConfig() const noexcept {
|
explicit operator Pica::TexturingRegs::TevStageConfig() const noexcept {
|
||||||
Pica::TexturingRegs::TevStageConfig stage;
|
return {
|
||||||
stage.sources_raw = sources_raw;
|
.sources_raw = sources_raw,
|
||||||
stage.modifiers_raw = modifiers_raw;
|
.modifiers_raw = modifiers_raw,
|
||||||
stage.ops_raw = ops_raw;
|
.ops_raw = ops_raw,
|
||||||
stage.const_color = 0;
|
.const_color = 0,
|
||||||
stage.scales_raw = scales_raw;
|
.scales_raw = scales_raw,
|
||||||
return stage;
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -56,6 +59,7 @@ struct PicaFSConfigState {
|
||||||
BitField<27, 1, u32> shadow_rendering;
|
BitField<27, 1, u32> shadow_rendering;
|
||||||
BitField<28, 1, u32> shadow_texture_orthographic;
|
BitField<28, 1, u32> shadow_texture_orthographic;
|
||||||
BitField<29, 1, u32> use_fragment_shader_interlock;
|
BitField<29, 1, u32> use_fragment_shader_interlock;
|
||||||
|
BitField<30, 1, u32> use_custom_normal_map;
|
||||||
};
|
};
|
||||||
|
|
||||||
union {
|
union {
|
||||||
|
@ -127,24 +131,33 @@ struct PicaFSConfigState {
|
||||||
u8 lod_min;
|
u8 lod_min;
|
||||||
u8 lod_max;
|
u8 lod_max;
|
||||||
} proctex;
|
} proctex;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
bool emulate_blending;
|
||||||
|
Pica::FramebufferRegs::BlendEquation eq;
|
||||||
|
Pica::FramebufferRegs::BlendFactor src_factor;
|
||||||
|
Pica::FramebufferRegs::BlendFactor dst_factor;
|
||||||
|
} rgb_blend, alpha_blend;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This struct contains all state used to generate the GLSL fragment shader that emulates the
|
* This struct contains all state used to generate the GLSL fragment shader that emulates the
|
||||||
* current Pica register configuration. This struct is used as a cache key for generated GLSL shader
|
* current Pica register configuration. This struct is used as a cache key for generated GLSL shader
|
||||||
* programs. The functions in gl_shader_gen.cpp should retrieve state from this struct only, not by
|
* programs. The functions in glsl_shader_gen.cpp should retrieve state from this struct only, not
|
||||||
* directly accessing Pica registers. This should reduce the risk of bugs in shader generation where
|
* by directly accessing Pica registers. This should reduce the risk of bugs in shader generation
|
||||||
* Pica state is not being captured in the shader cache key, thereby resulting in (what should be)
|
* where Pica state is not being captured in the shader cache key, thereby resulting in (what should
|
||||||
* two separate shaders sharing the same key.
|
* be) two separate shaders sharing the same key.
|
||||||
*/
|
*/
|
||||||
struct PicaFSConfig : Common::HashableStruct<PicaFSConfigState> {
|
struct PicaFSConfig : Common::HashableStruct<PicaFSConfigState> {
|
||||||
PicaFSConfig(const Pica::Regs& regs, const Instance& instance);
|
PicaFSConfig(const Pica::Regs& regs, bool has_fragment_shader_interlock, bool emulate_logic_op,
|
||||||
|
bool emulate_custom_border_color, bool emulate_blend_minmax_factor,
|
||||||
|
bool use_custom_normal_map = false);
|
||||||
|
|
||||||
bool TevStageUpdatesCombinerBufferColor(unsigned stage_index) const {
|
[[nodiscard]] bool TevStageUpdatesCombinerBufferColor(unsigned stage_index) const {
|
||||||
return (stage_index < 4) && (state.combiner_buffer_input & (1 << stage_index));
|
return (stage_index < 4) && (state.combiner_buffer_input & (1 << stage_index));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TevStageUpdatesCombinerBufferAlpha(unsigned stage_index) const {
|
[[nodiscard]] bool TevStageUpdatesCombinerBufferAlpha(unsigned stage_index) const {
|
||||||
return (stage_index < 4) && ((state.combiner_buffer_input >> 4) & (1 << stage_index));
|
return (stage_index < 4) && ((state.combiner_buffer_input >> 4) & (1 << stage_index));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -158,12 +171,36 @@ enum class AttribLoadFlags {
|
||||||
DECLARE_ENUM_FLAG_OPERATORS(AttribLoadFlags)
|
DECLARE_ENUM_FLAG_OPERATORS(AttribLoadFlags)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This struct contains common information to identify a GL vertex/geometry shader generated from
|
* This struct contains common information to identify a GLSL geometry shader generated from
|
||||||
* PICA vertex/geometry shader.
|
* PICA geometry shader.
|
||||||
*/
|
*/
|
||||||
struct PicaShaderConfigCommon {
|
struct PicaGSConfigState {
|
||||||
void Init(const Pica::RasterizerRegs& rasterizer, const Pica::ShaderRegs& regs,
|
void Init(const Pica::Regs& regs, bool use_clip_planes_);
|
||||||
Pica::Shader::ShaderSetup& setup);
|
|
||||||
|
bool use_clip_planes;
|
||||||
|
|
||||||
|
u32 vs_output_attributes;
|
||||||
|
u32 gs_output_attributes;
|
||||||
|
|
||||||
|
struct SemanticMap {
|
||||||
|
u32 attribute_index;
|
||||||
|
u32 component_index;
|
||||||
|
};
|
||||||
|
|
||||||
|
// semantic_maps[semantic name] -> GS output attribute index + component index
|
||||||
|
std::array<SemanticMap, 24> semantic_maps;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This struct contains common information to identify a GLSL vertex shader generated from
|
||||||
|
* PICA vertex shader.
|
||||||
|
*/
|
||||||
|
struct PicaVSConfigState {
|
||||||
|
void Init(const Pica::Regs& regs, Pica::Shader::ShaderSetup& setup, bool use_clip_planes_,
|
||||||
|
bool use_geometry_shader_);
|
||||||
|
|
||||||
|
bool use_clip_planes;
|
||||||
|
bool use_geometry_shader;
|
||||||
|
|
||||||
u64 program_hash;
|
u64 program_hash;
|
||||||
u64 swizzle_hash;
|
u64 swizzle_hash;
|
||||||
|
@ -177,103 +214,46 @@ struct PicaShaderConfigCommon {
|
||||||
// output_map[output register index] -> output attribute index
|
// output_map[output register index] -> output attribute index
|
||||||
std::array<u32, 16> output_map;
|
std::array<u32, 16> output_map;
|
||||||
|
|
||||||
bool use_geometry_shader;
|
PicaGSConfigState gs_state;
|
||||||
u32 vs_output_attributes;
|
|
||||||
u32 gs_output_attributes;
|
|
||||||
|
|
||||||
struct SemanticMap {
|
|
||||||
u32 attribute_index;
|
|
||||||
u32 component_index;
|
|
||||||
};
|
|
||||||
|
|
||||||
// semantic_maps[semantic name] -> GS output attribute index + component index
|
|
||||||
std::array<SemanticMap, 24> semantic_maps;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This struct contains information to identify a GL vertex shader generated from PICA vertex
|
* This struct contains information to identify a GL vertex shader generated from PICA vertex
|
||||||
* shader.
|
* shader.
|
||||||
*/
|
*/
|
||||||
struct PicaVSConfig : Common::HashableStruct<PicaShaderConfigCommon> {
|
struct PicaVSConfig : Common::HashableStruct<PicaVSConfigState> {
|
||||||
explicit PicaVSConfig(const Pica::RasterizerRegs& rasterizer, const Pica::ShaderRegs& regs,
|
explicit PicaVSConfig(const Pica::Regs& regs, Pica::Shader::ShaderSetup& setup,
|
||||||
Pica::Shader::ShaderSetup& setup, const Instance& instance);
|
bool use_clip_planes_, bool use_geometry_shader_);
|
||||||
bool use_clip_planes;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct PicaGSConfigCommonRaw {
|
|
||||||
void Init(const Pica::Regs& regs);
|
|
||||||
|
|
||||||
u32 vs_output_attributes;
|
|
||||||
u32 gs_output_attributes;
|
|
||||||
|
|
||||||
struct SemanticMap {
|
|
||||||
u32 attribute_index;
|
|
||||||
u32 component_index;
|
|
||||||
};
|
|
||||||
|
|
||||||
// semantic_maps[semantic name] -> GS output attribute index + component index
|
|
||||||
std::array<SemanticMap, 24> semantic_maps;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This struct contains information to identify a GL geometry shader generated from PICA no-geometry
|
* This struct contains information to identify a GL geometry shader generated from PICA no-geometry
|
||||||
* shader pipeline
|
* shader pipeline
|
||||||
*/
|
*/
|
||||||
struct PicaFixedGSConfig : Common::HashableStruct<PicaGSConfigCommonRaw> {
|
struct PicaFixedGSConfig : Common::HashableStruct<PicaGSConfigState> {
|
||||||
explicit PicaFixedGSConfig(const Pica::Regs& regs, const Instance& instance);
|
explicit PicaFixedGSConfig(const Pica::Regs& regs, bool use_clip_planes_);
|
||||||
bool use_clip_planes;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
} // namespace Pica::Shader::Generator
|
||||||
* Generates the GLSL vertex shader program source code that accepts vertices from software shader
|
|
||||||
* and directly passes them to the fragment shader.
|
|
||||||
* @param separable_shader generates shader that can be used for separate shader object
|
|
||||||
* @returns String of the shader source code
|
|
||||||
*/
|
|
||||||
std::string GenerateTrivialVertexShader(bool use_clip_planes);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates the GLSL vertex shader program source code for the given VS program
|
|
||||||
* @returns String of the shader source code; boost::none on failure
|
|
||||||
*/
|
|
||||||
std::optional<std::string> GenerateVertexShader(const Pica::Shader::ShaderSetup& setup,
|
|
||||||
const PicaVSConfig& config);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates the GLSL fixed geometry shader program source code for non-GS PICA pipeline
|
|
||||||
* @returns String of the shader source code
|
|
||||||
*/
|
|
||||||
std::string GenerateFixedGeometryShader(const PicaFixedGSConfig& config);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates the GLSL fragment shader program source code for the current Pica state
|
|
||||||
* @param config ShaderCacheKey object generated for the current Pica state, used for the shader
|
|
||||||
* configuration (NOTE: Use state in this struct only, not the Pica registers!)
|
|
||||||
* @param separable_shader generates shader that can be used for separate shader object
|
|
||||||
* @returns String of the shader source code
|
|
||||||
*/
|
|
||||||
std::string GenerateFragmentShader(const PicaFSConfig& config);
|
|
||||||
|
|
||||||
} // namespace Vulkan
|
|
||||||
|
|
||||||
namespace std {
|
namespace std {
|
||||||
template <>
|
template <>
|
||||||
struct hash<Vulkan::PicaFSConfig> {
|
struct hash<Pica::Shader::Generator::PicaFSConfig> {
|
||||||
std::size_t operator()(const Vulkan::PicaFSConfig& k) const noexcept {
|
std::size_t operator()(const Pica::Shader::Generator::PicaFSConfig& k) const noexcept {
|
||||||
return k.Hash();
|
return k.Hash();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct hash<Vulkan::PicaVSConfig> {
|
struct hash<Pica::Shader::Generator::PicaVSConfig> {
|
||||||
std::size_t operator()(const Vulkan::PicaVSConfig& k) const noexcept {
|
std::size_t operator()(const Pica::Shader::Generator::PicaVSConfig& k) const noexcept {
|
||||||
return k.Hash();
|
return k.Hash();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct hash<Vulkan::PicaFixedGSConfig> {
|
struct hash<Pica::Shader::Generator::PicaFixedGSConfig> {
|
||||||
std::size_t operator()(const Vulkan::PicaFixedGSConfig& k) const noexcept {
|
std::size_t operator()(const Pica::Shader::Generator::PicaFixedGSConfig& k) const noexcept {
|
||||||
return k.Hash();
|
return k.Hash();
|
||||||
}
|
}
|
||||||
};
|
};
|
|
@ -0,0 +1,26 @@
|
||||||
|
// Copyright 2023 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include "video_core/shader/generator/shader_uniforms.h"
|
||||||
|
#include "video_core/shader/shader.h"
|
||||||
|
|
||||||
|
namespace Pica::Shader::Generator {
|
||||||
|
|
||||||
|
void PicaUniformsData::SetFromRegs(const Pica::ShaderRegs& regs,
|
||||||
|
const Pica::Shader::ShaderSetup& setup) {
|
||||||
|
std::transform(std::begin(setup.uniforms.b), std::end(setup.uniforms.b), std::begin(bools),
|
||||||
|
[](bool value) -> BoolAligned { return {value ? 1 : 0}; });
|
||||||
|
std::transform(std::begin(regs.int_uniforms), std::end(regs.int_uniforms), std::begin(i),
|
||||||
|
[](const auto& value) -> Common::Vec4u {
|
||||||
|
return {value.x.Value(), value.y.Value(), value.z.Value(), value.w.Value()};
|
||||||
|
});
|
||||||
|
std::transform(std::begin(setup.uniforms.f), std::end(setup.uniforms.f), std::begin(f),
|
||||||
|
[](const auto& value) -> Common::Vec4f {
|
||||||
|
return {value.x.ToFloat32(), value.y.ToFloat32(), value.z.ToFloat32(),
|
||||||
|
value.w.ToFloat32()};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Pica::Shader::Generator
|
|
@ -12,10 +12,10 @@ struct ShaderRegs;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Pica::Shader {
|
namespace Pica::Shader {
|
||||||
|
|
||||||
struct ShaderSetup;
|
struct ShaderSetup;
|
||||||
|
}
|
||||||
|
|
||||||
enum class UniformBindings : u32 { Common, VS, GS };
|
namespace Pica::Shader::Generator {
|
||||||
|
|
||||||
struct LightSrc {
|
struct LightSrc {
|
||||||
alignas(16) Common::Vec3f specular_0;
|
alignas(16) Common::Vec3f specular_0;
|
||||||
|
@ -34,7 +34,7 @@ struct LightSrc {
|
||||||
* the end of a uniform block is included in UNIFORM_BLOCK_DATA_SIZE or not.
|
* the end of a uniform block is included in UNIFORM_BLOCK_DATA_SIZE or not.
|
||||||
* Not following that rule will cause problems on some AMD drivers.
|
* Not following that rule will cause problems on some AMD drivers.
|
||||||
*/
|
*/
|
||||||
struct UniformData {
|
struct FSUniformData {
|
||||||
int framebuffer_scale;
|
int framebuffer_scale;
|
||||||
int alphatest_ref;
|
int alphatest_ref;
|
||||||
float depth_scale;
|
float depth_scale;
|
||||||
|
@ -53,7 +53,6 @@ struct UniformData {
|
||||||
int proctex_diff_lut_offset;
|
int proctex_diff_lut_offset;
|
||||||
float proctex_bias;
|
float proctex_bias;
|
||||||
int shadow_texture_bias;
|
int shadow_texture_bias;
|
||||||
alignas(4) bool enable_clip1;
|
|
||||||
alignas(16) Common::Vec4i lighting_lut_offset[LightingRegs::NumLightingSampler / 4];
|
alignas(16) Common::Vec4i lighting_lut_offset[LightingRegs::NumLightingSampler / 4];
|
||||||
alignas(16) Common::Vec3f fog_color;
|
alignas(16) Common::Vec3f fog_color;
|
||||||
alignas(8) Common::Vec2f proctex_noise_f;
|
alignas(8) Common::Vec2f proctex_noise_f;
|
||||||
|
@ -65,13 +64,12 @@ struct UniformData {
|
||||||
alignas(16) Common::Vec4f tev_combiner_buffer_color;
|
alignas(16) Common::Vec4f tev_combiner_buffer_color;
|
||||||
alignas(16) Common::Vec3f tex_lod_bias;
|
alignas(16) Common::Vec3f tex_lod_bias;
|
||||||
alignas(16) Common::Vec4f tex_border_color[3];
|
alignas(16) Common::Vec4f tex_border_color[3];
|
||||||
alignas(16) Common::Vec4f clip_coef;
|
|
||||||
alignas(16) Common::Vec4f blend_color;
|
alignas(16) Common::Vec4f blend_color;
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(sizeof(UniformData) == 0x540,
|
static_assert(sizeof(FSUniformData) == 0x530,
|
||||||
"The size of the UniformData does not match the structure in the shader");
|
"The size of the UniformData does not match the structure in the shader");
|
||||||
static_assert(sizeof(UniformData) < 16384,
|
static_assert(sizeof(FSUniformData) < 16384,
|
||||||
"UniformData structure must be less than 16kb as per the OpenGL spec");
|
"UniformData structure must be less than 16kb as per the OpenGL spec");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -91,13 +89,20 @@ struct PicaUniformsData {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VSUniformData {
|
struct VSUniformData {
|
||||||
PicaUniformsData uniforms;
|
bool enable_clip1;
|
||||||
|
alignas(16) Common::Vec4f clip_coef;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(VSUniformData) == 1856,
|
static_assert(sizeof(VSUniformData) == 32,
|
||||||
"The size of the VSUniformData does not match the structure in the shader");
|
"The size of the VSUniformData does not match the structure in the shader");
|
||||||
static_assert(sizeof(VSUniformData) < 16384,
|
static_assert(sizeof(VSUniformData) < 16384,
|
||||||
"VSUniformData structure must be less than 16kb as per the OpenGL spec");
|
"VSUniformData structure must be less than 16kb as per the OpenGL spec");
|
||||||
|
|
||||||
std::string BuildShaderUniformDefinitions(const std::string& extra_layout_parameters = "");
|
struct VSPicaUniformData {
|
||||||
|
alignas(16) PicaUniformsData uniforms;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(VSPicaUniformData) == 1856,
|
||||||
|
"The size of the VSPicaUniformData does not match the structure in the shader");
|
||||||
|
static_assert(sizeof(VSPicaUniformData) < 16384,
|
||||||
|
"VSPicaUniformData structure must be less than 16kb as per the OpenGL spec");
|
||||||
|
|
||||||
} // namespace Pica::Shader
|
} // namespace Pica::Shader::Generator
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/telemetry_session.h"
|
#include "core/telemetry_session.h"
|
||||||
#include "video_core/renderer_vulkan/vk_shader_gen_spv.h"
|
#include "video_core/shader/generator/spv_shader_gen.h"
|
||||||
|
|
||||||
using Pica::FramebufferRegs;
|
using Pica::FramebufferRegs;
|
||||||
using Pica::LightingRegs;
|
using Pica::LightingRegs;
|
||||||
|
@ -12,7 +12,7 @@ using Pica::RasterizerRegs;
|
||||||
using Pica::TexturingRegs;
|
using Pica::TexturingRegs;
|
||||||
using TevStageConfig = TexturingRegs::TevStageConfig;
|
using TevStageConfig = TexturingRegs::TevStageConfig;
|
||||||
|
|
||||||
namespace Vulkan {
|
namespace Pica::Shader::Generator::SPIRV {
|
||||||
|
|
||||||
constexpr u32 SPIRV_VERSION_1_3 = 0x00010300;
|
constexpr u32 SPIRV_VERSION_1_3 = 0x00010300;
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ void FragmentModule::Generate() {
|
||||||
}
|
}
|
||||||
|
|
||||||
combiner_buffer = ConstF32(0.f, 0.f, 0.f, 0.f);
|
combiner_buffer = ConstF32(0.f, 0.f, 0.f, 0.f);
|
||||||
next_combiner_buffer = GetShaderDataMember(vec_ids.Get(4), ConstS32(27));
|
next_combiner_buffer = GetShaderDataMember(vec_ids.Get(4), ConstS32(26));
|
||||||
last_tex_env_out = rounded_primary_color;
|
last_tex_env_out = rounded_primary_color;
|
||||||
|
|
||||||
// Write shader bytecode to emulate PICA TEV stages
|
// Write shader bytecode to emulate PICA TEV stages
|
||||||
|
@ -192,7 +192,7 @@ void FragmentModule::WriteFog() {
|
||||||
// Blend the fog
|
// Blend the fog
|
||||||
const Id tex_env_rgb{
|
const Id tex_env_rgb{
|
||||||
OpVectorShuffle(vec_ids.Get(3), last_tex_env_out, last_tex_env_out, 0, 1, 2)};
|
OpVectorShuffle(vec_ids.Get(3), last_tex_env_out, last_tex_env_out, 0, 1, 2)};
|
||||||
const Id fog_color{GetShaderDataMember(vec_ids.Get(3), ConstS32(20))};
|
const Id fog_color{GetShaderDataMember(vec_ids.Get(3), ConstS32(19))};
|
||||||
const Id fog_factor_rgb{
|
const Id fog_factor_rgb{
|
||||||
OpCompositeConstruct(vec_ids.Get(3), fog_factor, fog_factor, fog_factor)};
|
OpCompositeConstruct(vec_ids.Get(3), fog_factor, fog_factor, fog_factor)};
|
||||||
const Id fog_result{OpFMix(vec_ids.Get(3), fog_color, tex_env_rgb, fog_factor_rgb)};
|
const Id fog_result{OpFMix(vec_ids.Get(3), fog_color, tex_env_rgb, fog_factor_rgb)};
|
||||||
|
@ -202,7 +202,7 @@ void FragmentModule::WriteFog() {
|
||||||
void FragmentModule::WriteGas() {
|
void FragmentModule::WriteGas() {
|
||||||
// TODO: Implement me
|
// TODO: Implement me
|
||||||
telemetry.AddField(Common::Telemetry::FieldType::Session, "VideoCore_Pica_UseGasMode", true);
|
telemetry.AddField(Common::Telemetry::FieldType::Session, "VideoCore_Pica_UseGasMode", true);
|
||||||
LOG_CRITICAL(Render_Vulkan, "Unimplemented gas mode");
|
LOG_CRITICAL(Render, "Unimplemented gas mode");
|
||||||
OpKill();
|
OpKill();
|
||||||
OpFunctionEnd();
|
OpFunctionEnd();
|
||||||
}
|
}
|
||||||
|
@ -380,7 +380,7 @@ void FragmentModule::WriteLighting() {
|
||||||
const auto GetLightMember = [&](s32 member) -> Id {
|
const auto GetLightMember = [&](s32 member) -> Id {
|
||||||
const Id member_type = member < 6 ? vec_ids.Get(3) : f32_id;
|
const Id member_type = member < 6 ? vec_ids.Get(3) : f32_id;
|
||||||
const Id light_num{ConstS32(static_cast<s32>(lighting.light[light_index].num.Value()))};
|
const Id light_num{ConstS32(static_cast<s32>(lighting.light[light_index].num.Value()))};
|
||||||
return GetShaderDataMember(member_type, ConstS32(25), light_num, ConstS32(member));
|
return GetShaderDataMember(member_type, ConstS32(24), light_num, ConstS32(member));
|
||||||
};
|
};
|
||||||
|
|
||||||
// Compute light vector (directional or positional)
|
// Compute light vector (directional or positional)
|
||||||
|
@ -583,7 +583,7 @@ void FragmentModule::WriteLighting() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sum final lighting result
|
// Sum final lighting result
|
||||||
const Id lighting_global_ambient{GetShaderDataMember(vec_ids.Get(3), ConstS32(24))};
|
const Id lighting_global_ambient{GetShaderDataMember(vec_ids.Get(3), ConstS32(23))};
|
||||||
const Id lighting_global_ambient_rgba{
|
const Id lighting_global_ambient_rgba{
|
||||||
PadVectorF32(lighting_global_ambient, vec_ids.Get(4), 0.f)};
|
PadVectorF32(lighting_global_ambient, vec_ids.Get(4), 0.f)};
|
||||||
const Id zero_vec{ConstF32(0.f, 0.f, 0.f, 0.f)};
|
const Id zero_vec{ConstF32(0.f, 0.f, 0.f, 0.f)};
|
||||||
|
@ -706,7 +706,7 @@ void FragmentModule::WriteAlphaTestCondition(FramebufferRegs::CompareFunc func)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
LOG_CRITICAL(Render_Vulkan, "Unknown alpha test condition {}", func);
|
LOG_CRITICAL(Render, "Unknown alpha test condition {}", func);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -791,7 +791,7 @@ Id FragmentModule::AppendProcTexShiftOffset(Id v, ProcTexShift mode, ProcTexClam
|
||||||
case ProcTexShift::Even:
|
case ProcTexShift::Even:
|
||||||
return shift(true);
|
return shift(true);
|
||||||
default:
|
default:
|
||||||
LOG_CRITICAL(Render_Vulkan, "Unknown shift mode {}", mode);
|
LOG_CRITICAL(Render, "Unknown shift mode {}", mode);
|
||||||
return ConstF32(0.f);
|
return ConstF32(0.f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -819,7 +819,7 @@ Id FragmentModule::AppendProcTexClamp(Id var, ProcTexClamp mode) {
|
||||||
case ProcTexClamp::Pulse:
|
case ProcTexClamp::Pulse:
|
||||||
return OpSelect(f32_id, OpFOrdGreaterThan(bool_id, var, ConstF32(0.5f)), one, zero);
|
return OpSelect(f32_id, OpFOrdGreaterThan(bool_id, var, ConstF32(0.5f)), one, zero);
|
||||||
default:
|
default:
|
||||||
LOG_CRITICAL(Render_Vulkan, "Unknown clamp mode {}", mode);
|
LOG_CRITICAL(Render, "Unknown clamp mode {}", mode);
|
||||||
return OpFMin(f32_id, var, one);
|
return OpFMin(f32_id, var, one);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -851,7 +851,7 @@ Id FragmentModule::AppendProcTexCombineAndMap(ProcTexCombiner combiner, Id u, Id
|
||||||
return OpFMin(f32_id, OpFMul(f32_id, r, ConstF32(0.5f)), ConstF32(1.f));
|
return OpFMin(f32_id, OpFMul(f32_id, r, ConstF32(0.5f)), ConstF32(1.f));
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
LOG_CRITICAL(Render_Vulkan, "Unknown combiner {}", combiner);
|
LOG_CRITICAL(Render, "Unknown combiner {}", combiner);
|
||||||
return ConstF32(0.f);
|
return ConstF32(0.f);
|
||||||
}
|
}
|
||||||
}();
|
}();
|
||||||
|
@ -916,7 +916,7 @@ void FragmentModule::DefineTexSampler(u32 texture_unit) {
|
||||||
|
|
||||||
AddLabel(border_label);
|
AddLabel(border_label);
|
||||||
const Id border_color{
|
const Id border_color{
|
||||||
GetShaderDataMember(vec_ids.Get(4), ConstS32(29), ConstU32(texture_unit))};
|
GetShaderDataMember(vec_ids.Get(4), ConstS32(28), ConstU32(texture_unit))};
|
||||||
OpReturnValue(border_color);
|
OpReturnValue(border_color);
|
||||||
|
|
||||||
AddLabel(not_border_label);
|
AddLabel(not_border_label);
|
||||||
|
@ -937,7 +937,7 @@ void FragmentModule::DefineTexSampler(u32 texture_unit) {
|
||||||
const Id dx_dy_max{
|
const Id dx_dy_max{
|
||||||
OpFMax(f32_id, OpCompositeExtract(f32_id, d, 0), OpCompositeExtract(f32_id, d, 1))};
|
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{OpLog2(f32_id, dx_dy_max)};
|
||||||
const Id lod_bias{GetShaderDataMember(f32_id, ConstS32(28), ConstU32(texture_unit))};
|
const Id lod_bias{GetShaderDataMember(f32_id, ConstS32(27), ConstU32(texture_unit))};
|
||||||
const Id biased_lod{OpFAdd(f32_id, lod, lod_bias)};
|
const Id biased_lod{OpFAdd(f32_id, lod, lod_bias)};
|
||||||
return OpImageSampleExplicitLod(vec_ids.Get(4), sampled_image, texcoord,
|
return OpImageSampleExplicitLod(vec_ids.Get(4), sampled_image, texcoord,
|
||||||
spv::ImageOperandsMask::Lod, biased_lod);
|
spv::ImageOperandsMask::Lod, biased_lod);
|
||||||
|
@ -976,7 +976,7 @@ void FragmentModule::DefineTexSampler(u32 texture_unit) {
|
||||||
// return "shadowTextureCube(texcoord0, texcoord0_w)";
|
// return "shadowTextureCube(texcoord0, texcoord0_w)";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
LOG_CRITICAL(Render_Vulkan, "Unhandled texture type {:x}", state.texture0_type.Value());
|
LOG_CRITICAL(Render, "Unhandled texture type {:x}", state.texture0_type.Value());
|
||||||
UNIMPLEMENTED();
|
UNIMPLEMENTED();
|
||||||
ret_val = zero_vec;
|
ret_val = zero_vec;
|
||||||
break;
|
break;
|
||||||
|
@ -1012,7 +1012,7 @@ Id FragmentModule::ProcTexSampler() {
|
||||||
const Id texcoord{OpLoad(vec_ids.Get(2), texcoord_id[config.state.proctex.coord.Value()])};
|
const Id texcoord{OpLoad(vec_ids.Get(2), texcoord_id[config.state.proctex.coord.Value()])};
|
||||||
uv = OpFAbs(vec_ids.Get(2), texcoord);
|
uv = OpFAbs(vec_ids.Get(2), texcoord);
|
||||||
} else {
|
} else {
|
||||||
LOG_CRITICAL(Render_Vulkan, "Unexpected proctex.coord >= 3");
|
LOG_CRITICAL(Render, "Unexpected proctex.coord >= 3");
|
||||||
uv = OpFAbs(vec_ids.Get(2), OpLoad(vec_ids.Get(2), texcoord_id[0]));
|
uv = OpFAbs(vec_ids.Get(2), OpLoad(vec_ids.Get(2), texcoord_id[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1047,7 +1047,7 @@ Id FragmentModule::ProcTexSampler() {
|
||||||
|
|
||||||
// Generate noise
|
// Generate noise
|
||||||
if (config.state.proctex.noise_enable) {
|
if (config.state.proctex.noise_enable) {
|
||||||
const Id proctex_noise_a{GetShaderDataMember(vec_ids.Get(2), ConstS32(22))};
|
const Id proctex_noise_a{GetShaderDataMember(vec_ids.Get(2), ConstS32(21))};
|
||||||
const Id noise_coef{ProcTexNoiseCoef(uv)};
|
const Id noise_coef{ProcTexNoiseCoef(uv)};
|
||||||
uv = OpFAdd(vec_ids.Get(2), uv,
|
uv = OpFAdd(vec_ids.Get(2), uv,
|
||||||
OpVectorTimesScalar(vec_ids.Get(2), proctex_noise_a, noise_coef));
|
OpVectorTimesScalar(vec_ids.Get(2), proctex_noise_a, noise_coef));
|
||||||
|
@ -1158,8 +1158,8 @@ Id FragmentModule::ProcTexNoiseCoef(Id x) {
|
||||||
return OpFma(f32_id, OpConvertSToF(f32_id, v2), ConstF32(2.f / 15.f), ConstF32(-1.f));
|
return OpFma(f32_id, OpConvertSToF(f32_id, v2), ConstF32(2.f / 15.f), ConstF32(-1.f));
|
||||||
};
|
};
|
||||||
|
|
||||||
const Id proctex_noise_f{GetShaderDataMember(vec_ids.Get(2), ConstS32(21))};
|
const Id proctex_noise_f{GetShaderDataMember(vec_ids.Get(2), ConstS32(20))};
|
||||||
const Id proctex_noise_p{GetShaderDataMember(vec_ids.Get(2), ConstS32(23))};
|
const Id proctex_noise_p{GetShaderDataMember(vec_ids.Get(2), ConstS32(22))};
|
||||||
const Id grid{OpFMul(vec_ids.Get(2),
|
const Id grid{OpFMul(vec_ids.Get(2),
|
||||||
OpVectorTimesScalar(vec_ids.Get(2), proctex_noise_f, ConstF32(9.f)),
|
OpVectorTimesScalar(vec_ids.Get(2), proctex_noise_f, ConstF32(9.f)),
|
||||||
OpFAbs(vec_ids.Get(2), OpFAdd(vec_ids.Get(2), x, proctex_noise_p)))};
|
OpFAbs(vec_ids.Get(2), OpFAdd(vec_ids.Get(2), x, proctex_noise_p)))};
|
||||||
|
@ -1245,7 +1245,7 @@ Id FragmentModule::LookupLightingLUT(Id lut_index, Id index, Id delta) {
|
||||||
|
|
||||||
const Id lut_index_x{OpShiftRightArithmetic(i32_id, lut_index, ConstS32(2))};
|
const Id lut_index_x{OpShiftRightArithmetic(i32_id, lut_index, ConstS32(2))};
|
||||||
const Id lut_index_y{OpBitwiseAnd(i32_id, lut_index, ConstS32(3))};
|
const Id lut_index_y{OpBitwiseAnd(i32_id, lut_index, ConstS32(3))};
|
||||||
const Id lut_offset{GetShaderDataMember(i32_id, ConstS32(19), lut_index_x, lut_index_y)};
|
const Id lut_offset{GetShaderDataMember(i32_id, ConstS32(18), lut_index_x, lut_index_y)};
|
||||||
const Id coord{OpIAdd(i32_id, lut_offset, index)};
|
const Id coord{OpIAdd(i32_id, lut_offset, index)};
|
||||||
const Id entry{
|
const Id entry{
|
||||||
OpImageFetch(vec_ids.Get(4), OpImage(image_buffer_id, texture_buffer_lut_lf), coord)};
|
OpImageFetch(vec_ids.Get(4), OpImage(image_buffer_id, texture_buffer_lut_lf), coord)};
|
||||||
|
@ -1274,11 +1274,11 @@ Id FragmentModule::AppendSource(TevStageConfig::Source source, s32 index) {
|
||||||
case Source::PreviousBuffer:
|
case Source::PreviousBuffer:
|
||||||
return combiner_buffer;
|
return combiner_buffer;
|
||||||
case Source::Constant:
|
case Source::Constant:
|
||||||
return GetShaderDataMember(vec_ids.Get(4), ConstS32(26), ConstS32(index));
|
return GetShaderDataMember(vec_ids.Get(4), ConstS32(25), ConstS32(index));
|
||||||
case Source::Previous:
|
case Source::Previous:
|
||||||
return last_tex_env_out;
|
return last_tex_env_out;
|
||||||
default:
|
default:
|
||||||
LOG_CRITICAL(Render_Vulkan, "Unknown source op {}", source);
|
LOG_CRITICAL(Render, "Unknown source op {}", source);
|
||||||
return ConstF32(0.f, 0.f, 0.f, 0.f);
|
return ConstF32(0.f, 0.f, 0.f, 0.f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1315,7 +1315,7 @@ Id FragmentModule::AppendColorModifier(TevStageConfig::ColorModifier modifier,
|
||||||
case ColorModifier::OneMinusSourceAlpha:
|
case ColorModifier::OneMinusSourceAlpha:
|
||||||
return OpFSub(vec_ids.Get(3), one_vec, shuffle(3, 3, 3));
|
return OpFSub(vec_ids.Get(3), one_vec, shuffle(3, 3, 3));
|
||||||
default:
|
default:
|
||||||
LOG_CRITICAL(Render_Vulkan, "Unknown color modifier op {}", modifier);
|
LOG_CRITICAL(Render, "Unknown color modifier op {}", modifier);
|
||||||
return one_vec;
|
return one_vec;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1346,7 +1346,7 @@ Id FragmentModule::AppendAlphaModifier(TevStageConfig::AlphaModifier modifier,
|
||||||
case AlphaModifier::OneMinusSourceBlue:
|
case AlphaModifier::OneMinusSourceBlue:
|
||||||
return OpFSub(f32_id, one_f32, component(2));
|
return OpFSub(f32_id, one_f32, component(2));
|
||||||
default:
|
default:
|
||||||
LOG_CRITICAL(Render_Vulkan, "Unknown alpha modifier op {}", modifier);
|
LOG_CRITICAL(Render, "Unknown alpha modifier op {}", modifier);
|
||||||
return one_f32;
|
return one_f32;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1395,7 +1395,7 @@ Id FragmentModule::AppendColorCombiner(Pica::TexturingRegs::TevStageConfig::Oper
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
color = zero_vec;
|
color = zero_vec;
|
||||||
LOG_CRITICAL(Render_Vulkan, "Unknown color combiner operation: {}", operation);
|
LOG_CRITICAL(Render, "Unknown color combiner operation: {}", operation);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1435,7 +1435,7 @@ Id FragmentModule::AppendAlphaCombiner(TevStageConfig::Operation operation) {
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
color = ConstF32(0.f);
|
color = ConstF32(0.f);
|
||||||
LOG_CRITICAL(Render_Vulkan, "Unknown alpha combiner operation: {}", operation);
|
LOG_CRITICAL(Render, "Unknown alpha combiner operation: {}", operation);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1485,16 +1485,15 @@ void FragmentModule::DefineUniformStructs() {
|
||||||
|
|
||||||
const Id shader_data_struct_id{
|
const Id shader_data_struct_id{
|
||||||
TypeStruct(i32_id, i32_id, f32_id, f32_id, f32_id, f32_id, i32_id, i32_id, i32_id, i32_id,
|
TypeStruct(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,
|
i32_id, i32_id, i32_id, i32_id, i32_id, i32_id, f32_id, i32_id,
|
||||||
lighting_lut_array_id, vec_ids.Get(3), vec_ids.Get(2), vec_ids.Get(2),
|
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(2), vec_ids.Get(3), light_src_array_id, const_color_array_id,
|
||||||
vec_ids.Get(4), vec_ids.Get(3), border_color_array_id, vec_ids.Get(4))};
|
vec_ids.Get(4), vec_ids.Get(3), border_color_array_id, vec_ids.Get(4))};
|
||||||
|
|
||||||
constexpr std::array light_src_offsets{0u, 16u, 32u, 48u, 64u, 80u, 92u, 96u};
|
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,
|
constexpr std::array shader_data_offsets{
|
||||||
32u, 36u, 40u, 44u, 48u, 52u, 56u, 60u,
|
0u, 4u, 8u, 12u, 16u, 20u, 24u, 28u, 32u, 36u, 40u, 44u, 48u, 52u, 56u,
|
||||||
64u, 68u, 72u, 80u, 176u, 192u, 200u, 208u,
|
60u, 64u, 68u, 80u, 176u, 192u, 200u, 208u, 224u, 240u, 1136u, 1232u, 1248u, 1264u, 1312u};
|
||||||
224u, 240u, 1136u, 1232u, 1248u, 1264u, 1312u};
|
|
||||||
|
|
||||||
Decorate(lighting_lut_array_id, spv::Decoration::ArrayStride, 16u);
|
Decorate(lighting_lut_array_id, spv::Decoration::ArrayStride, 16u);
|
||||||
Decorate(light_src_array_id, spv::Decoration::ArrayStride, 112u);
|
Decorate(light_src_array_id, spv::Decoration::ArrayStride, 112u);
|
||||||
|
@ -1511,7 +1510,7 @@ void FragmentModule::DefineUniformStructs() {
|
||||||
shader_data_id = AddGlobalVariable(
|
shader_data_id = AddGlobalVariable(
|
||||||
TypePointer(spv::StorageClass::Uniform, shader_data_struct_id), spv::StorageClass::Uniform);
|
TypePointer(spv::StorageClass::Uniform, shader_data_struct_id), spv::StorageClass::Uniform);
|
||||||
Decorate(shader_data_id, spv::Decoration::DescriptorSet, 0);
|
Decorate(shader_data_id, spv::Decoration::DescriptorSet, 0);
|
||||||
Decorate(shader_data_id, spv::Decoration::Binding, 1);
|
Decorate(shader_data_id, spv::Decoration::Binding, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FragmentModule::DefineInterface() {
|
void FragmentModule::DefineInterface() {
|
||||||
|
@ -1532,9 +1531,9 @@ void FragmentModule::DefineInterface() {
|
||||||
image_r32_id = TypeImage(u32_id, spv::Dim::Dim2D, 0, 0, 0, 2, spv::ImageFormat::R32ui);
|
image_r32_id = TypeImage(u32_id, spv::Dim::Dim2D, 0, 0, 0, 2, spv::ImageFormat::R32ui);
|
||||||
sampler_id = TypeSampler();
|
sampler_id = TypeSampler();
|
||||||
|
|
||||||
texture_buffer_lut_lf_id = DefineUniformConst(TypeSampledImage(image_buffer_id), 0, 2);
|
texture_buffer_lut_lf_id = DefineUniformConst(TypeSampledImage(image_buffer_id), 0, 3);
|
||||||
texture_buffer_lut_rg_id = DefineUniformConst(TypeSampledImage(image_buffer_id), 0, 3);
|
texture_buffer_lut_rg_id = DefineUniformConst(TypeSampledImage(image_buffer_id), 0, 4);
|
||||||
texture_buffer_lut_rgba_id = DefineUniformConst(TypeSampledImage(image_buffer_id), 0, 4);
|
texture_buffer_lut_rgba_id = DefineUniformConst(TypeSampledImage(image_buffer_id), 0, 5);
|
||||||
tex0_id = DefineUniformConst(TypeSampledImage(image2d_id), 1, 0);
|
tex0_id = DefineUniformConst(TypeSampledImage(image2d_id), 1, 0);
|
||||||
tex1_id = DefineUniformConst(TypeSampledImage(image2d_id), 1, 1);
|
tex1_id = DefineUniformConst(TypeSampledImage(image2d_id), 1, 1);
|
||||||
tex2_id = DefineUniformConst(TypeSampledImage(image2d_id), 1, 2);
|
tex2_id = DefineUniformConst(TypeSampledImage(image2d_id), 1, 2);
|
||||||
|
@ -1550,11 +1549,11 @@ void FragmentModule::DefineInterface() {
|
||||||
Decorate(gl_frag_depth_id, spv::Decoration::BuiltIn, spv::BuiltIn::FragDepth);
|
Decorate(gl_frag_depth_id, spv::Decoration::BuiltIn, spv::BuiltIn::FragDepth);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<u32> GenerateFragmentShaderSPV(const PicaFSConfig& config) {
|
std::vector<u32> GenerateFragmentShader(const PicaFSConfig& config) {
|
||||||
auto& telemetry = Core::System::GetInstance().TelemetrySession();
|
auto& telemetry = Core::System::GetInstance().TelemetrySession();
|
||||||
FragmentModule module{telemetry, config};
|
FragmentModule module{telemetry, config};
|
||||||
module.Generate();
|
module.Generate();
|
||||||
return module.Assemble();
|
return module.Assemble();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Vulkan
|
} // namespace Pica::Shader::Generator::SPIRV
|
|
@ -7,13 +7,13 @@
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <sirit/sirit.h>
|
#include <sirit/sirit.h>
|
||||||
|
|
||||||
#include "video_core/renderer_vulkan/vk_shader_gen.h"
|
#include "video_core/shader/generator/shader_gen.h"
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
class TelemetrySession;
|
class TelemetrySession;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Vulkan {
|
namespace Pica::Shader::Generator::SPIRV {
|
||||||
|
|
||||||
using Sirit::Id;
|
using Sirit::Id;
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ public:
|
||||||
void Generate();
|
void Generate();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Undos the vulkan perspective transformation and applies the PICA one
|
/// Undos the host perspective transformation and applies the PICA one
|
||||||
void WriteDepth();
|
void WriteDepth();
|
||||||
|
|
||||||
/// Emits code to emulate the scissor rectangle
|
/// Emits code to emulate the scissor rectangle
|
||||||
|
@ -289,6 +289,6 @@ private:
|
||||||
* @param separable_shader generates shader that can be used for separate shader object
|
* @param separable_shader generates shader that can be used for separate shader object
|
||||||
* @returns String of the shader source code
|
* @returns String of the shader source code
|
||||||
*/
|
*/
|
||||||
std::vector<u32> GenerateFragmentShaderSPV(const PicaFSConfig& config);
|
std::vector<u32> GenerateFragmentShader(const PicaFSConfig& config);
|
||||||
|
|
||||||
} // namespace Vulkan
|
} // namespace Pica::Shader::Generator::SPIRV
|
|
@ -1,80 +0,0 @@
|
||||||
// Copyright 2023 Citra Emulator Project
|
|
||||||
// Licensed under GPLv2 or any later version
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include "video_core/shader/shader.h"
|
|
||||||
#include "video_core/shader/shader_uniforms.h"
|
|
||||||
|
|
||||||
namespace Pica::Shader {
|
|
||||||
|
|
||||||
void PicaUniformsData::SetFromRegs(const Pica::ShaderRegs& regs,
|
|
||||||
const Pica::Shader::ShaderSetup& setup) {
|
|
||||||
std::transform(std::begin(setup.uniforms.b), std::end(setup.uniforms.b), std::begin(bools),
|
|
||||||
[](bool value) -> BoolAligned { return {value ? 1 : 0}; });
|
|
||||||
std::transform(std::begin(regs.int_uniforms), std::end(regs.int_uniforms), std::begin(i),
|
|
||||||
[](const auto& value) -> Common::Vec4u {
|
|
||||||
return {value.x.Value(), value.y.Value(), value.z.Value(), value.w.Value()};
|
|
||||||
});
|
|
||||||
std::transform(std::begin(setup.uniforms.f), std::end(setup.uniforms.f), std::begin(f),
|
|
||||||
[](const auto& value) -> Common::Vec4f {
|
|
||||||
return {value.x.ToFloat32(), value.y.ToFloat32(), value.z.ToFloat32(),
|
|
||||||
value.w.ToFloat32()};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr std::string_view UniformBlockDefFormat = R"(
|
|
||||||
#define NUM_TEV_STAGES 6
|
|
||||||
#define NUM_LIGHTS 8
|
|
||||||
#define NUM_LIGHTING_SAMPLERS 24
|
|
||||||
struct LightSrc {{
|
|
||||||
vec3 specular_0;
|
|
||||||
vec3 specular_1;
|
|
||||||
vec3 diffuse;
|
|
||||||
vec3 ambient;
|
|
||||||
vec3 position;
|
|
||||||
vec3 spot_direction;
|
|
||||||
float dist_atten_bias;
|
|
||||||
float dist_atten_scale;
|
|
||||||
}};
|
|
||||||
layout ({}std140) uniform shader_data {{
|
|
||||||
int framebuffer_scale;
|
|
||||||
int alphatest_ref;
|
|
||||||
float depth_scale;
|
|
||||||
float depth_offset;
|
|
||||||
float shadow_bias_constant;
|
|
||||||
float shadow_bias_linear;
|
|
||||||
int scissor_x1;
|
|
||||||
int scissor_y1;
|
|
||||||
int scissor_x2;
|
|
||||||
int scissor_y2;
|
|
||||||
int fog_lut_offset;
|
|
||||||
int proctex_noise_lut_offset;
|
|
||||||
int proctex_color_map_offset;
|
|
||||||
int proctex_alpha_map_offset;
|
|
||||||
int proctex_lut_offset;
|
|
||||||
int proctex_diff_lut_offset;
|
|
||||||
float proctex_bias;
|
|
||||||
int shadow_texture_bias;
|
|
||||||
bool enable_clip1;
|
|
||||||
ivec4 lighting_lut_offset[NUM_LIGHTING_SAMPLERS / 4];
|
|
||||||
vec3 fog_color;
|
|
||||||
vec2 proctex_noise_f;
|
|
||||||
vec2 proctex_noise_a;
|
|
||||||
vec2 proctex_noise_p;
|
|
||||||
vec3 lighting_global_ambient;
|
|
||||||
LightSrc light_src[NUM_LIGHTS];
|
|
||||||
vec4 const_color[NUM_TEV_STAGES];
|
|
||||||
vec4 tev_combiner_buffer_color;
|
|
||||||
vec3 tex_lod_bias;
|
|
||||||
vec4 tex_border_color[3];
|
|
||||||
vec4 clip_coef;
|
|
||||||
vec4 blend_color;
|
|
||||||
}};
|
|
||||||
)";
|
|
||||||
|
|
||||||
std::string BuildShaderUniformDefinitions(const std::string& extra_layout_parameters) {
|
|
||||||
return fmt::format(UniformBlockDefFormat, extra_layout_parameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Pica::Shader
|
|
Reference in New Issue