Files
parallel-gx1/Source/Core/VideoBackends/Vulkan/ShaderCompiler.cpp
T
LibretroAdmin 2323f96cf4 File I/O: one path, libretro-common's filestream and VFS
Every file the core touches goes through libretro-common's filestream,
path and dirent layers, taken from RetroArch: vfs_hybrid opens local
paths with libretro-common's own implementation and hands URI paths
(and, on Android, anything a local open cannot reach) to the
frontend's VFS. File::IOFile and File::DirectIOFile sit on RFILE, and
FileUtil, FileSearch and the directory walks on path_stat, path_mkdir
and retro_opendir. Dolphin's fopen, POSIX and Win32 file code,
std::filesystem and std::fstream are gone, with the dual VFS paths and
the core option that chose between them.

Files opened for reading alone are memory-mapped where the platform
can: reads copy straight out of the page cache, DirectIOFile's offset
reads on one mapped object are thread safe, and GetMappedPtr(), View()
and Prefetch() lend and warm without a copy. Text files are read a
line at a time through File::LineReader, lent from the mapping or a
window refilled a block at a time, never a byte at a time; JSON is
parsed straight from the mapping. Ini files and the cheat file are
built in memory and written once, an ini only when it changed and
then atomically.
2026-11-07 03:27:01 +01:00

175 lines
6.2 KiB
C++

// Copyright 2016 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "VideoBackends/Vulkan/ShaderCompiler.h"
#include <cstddef>
#include <string>
#include <cstdio>
#include <cstring>
#include <gfx/shaders/rslc.h>
#include "Common/FileUtil.h"
#include "Common/Logging/Log.h"
#include "VideoBackends/Vulkan/VulkanContext.h"
#include "VideoCommon/DriverDetails.h"
#include "VideoCommon/VideoBackendBase.h"
namespace Vulkan::ShaderCompiler
{
// Regarding the UBO bind points, we subtract one from the binding index because
// the OpenGL backend requires UBO #0 for non-block uniforms (at least on NV).
// This allows us to share the same shaders but use bind point #0 in the Vulkan
// backend. None of the Vulkan-specific shaders use UBOs, instead they use push
// constants, so when/if the GL backend moves to uniform blocks completely this
// subtraction can be removed.
static const char SHADER_HEADER[] = R"(
// Target GLSL 4.5.
#version 450 core
#extension GL_ARB_shading_language_include : enable
#define ATTRIBUTE_LOCATION(x) layout(location = x)
#define FRAGMENT_OUTPUT_LOCATION(x) layout(location = x)
#define FRAGMENT_OUTPUT_LOCATION_INDEXED(x, y) layout(location = x, index = y)
#define UBO_BINDING(packing, x) layout(packing, set = 0, binding = (x - 1))
#define SAMPLER_BINDING(x) layout(set = 1, binding = x)
#define TEXEL_BUFFER_BINDING(x) layout(set = 1, binding = (x + 8))
#define SSBO_BINDING(x) layout(std430, set = 2, binding = x)
#define INPUT_ATTACHMENT_BINDING(x, y, z) layout(set = x, binding = y, input_attachment_index = z)
#define VARYING_LOCATION(x) layout(location = x)
#define FORCE_EARLY_Z layout(early_fragment_tests) in
// Metal framebuffer fetch helpers.
#define FB_FETCH_VALUE subpassLoad(in_ocol0)
// hlsl to glsl function translation
#define API_VULKAN 1
#define float2 vec2
#define float3 vec3
#define float4 vec4
#define uint2 uvec2
#define uint3 uvec3
#define uint4 uvec4
#define int2 ivec2
#define int3 ivec3
#define int4 ivec4
#define frac fract
#define lerp mix
// These were changed in Vulkan
#define gl_VertexID gl_VertexIndex
#define gl_InstanceID gl_InstanceIndex
)";
static const char COMPUTE_SHADER_HEADER[] = R"(
// Target GLSL 4.5.
#version 450 core
// All resources are packed into one descriptor set for compute.
#define UBO_BINDING(packing, x) layout(packing, set = 0, binding = (x - 1))
#define SAMPLER_BINDING(x) layout(set = 0, binding = (1 + x))
#define TEXEL_BUFFER_BINDING(x) layout(set = 0, binding = (9 + x))
#define IMAGE_BINDING(format, x) layout(format, set = 0, binding = (11 + x))
// hlsl to glsl function translation
#define API_VULKAN 1
#define float2 vec2
#define float3 vec3
#define float4 vec4
#define uint2 uvec2
#define uint3 uvec3
#define uint4 uvec4
#define int2 ivec2
#define int3 ivec3
#define int4 ivec4
#define frac fract
#define lerp mix
)";
/* The bounding box's subgroup reduction, where the device runs subgroup
* operations in the pixel stage; the stages that use it are SPIR-V 1.3. */
static const char SUBGROUP_HELPER_HEADER[] =
"#extension GL_KHR_shader_subgroup_basic : enable\n"
"#extension GL_KHR_shader_subgroup_arithmetic : enable\n"
"#define SUPPORTS_SUBGROUP_REDUCTION 1\n"
"#define IS_HELPER_INVOCATION gl_HelperInvocation\n"
"#define IS_FIRST_ACTIVE_INVOCATION (subgroupElect())\n"
"#define SUBGROUP_MIN(value) value = subgroupMin(value)\n"
"#define SUBGROUP_MAX(value) value = subgroupMax(value)\n";
/* The stage's full text: the header, then the generator's source. */
static std::string GetShaderCode(std::string_view source, std::string_view header)
{
std::string full_source_code;
full_source_code.reserve(header.size() + sizeof(SUBGROUP_HELPER_HEADER) + source.size());
full_source_code.append(header);
if (g_vulkan_context->SupportsShaderSubgroupOperations())
full_source_code.append(SUBGROUP_HELPER_HEADER, sizeof(SUBGROUP_HELPER_HEADER) - 1);
if (DriverDetails::HasBug(DriverDetails::BUG_INVERTED_IS_HELPER))
{
full_source_code.append("#define gl_HelperInvocation !gl_HelperInvocation "
"// Work around broken AMD Metal driver\n");
}
if (DriverDetails::HasBug(DriverDetails::BUG_BROKEN_SUBGROUP_OPS_WITH_DISCARD))
full_source_code.append("#define BROKEN_SUBGROUP_WITH_DISCARD 1\n");
full_source_code.append(source);
return full_source_code;
}
/* A failed stage is logged and written to the dump directory with the error. */
static void ReportFailure(const char* stage_name, const std::string& source, const char* err)
{
static int counter;
std::string path = VideoBackendBase::BadShaderFilename(stage_name, counter++);
ERROR_LOG_FMT(VIDEO, "rslc could not compile a {} shader: {}", stage_name, err);
File::CreateFullPath(path);
File::WriteStringToFile(path, fmt::format("{}\n/* rslc: {} */\n", source, err));
}
static std::optional<SPIRVCodeVector> Compile(std::string_view source_code, enum rslc_stage stage,
const char* stage_name)
{
char err[1024];
struct rslc_opts opts;
struct rslc_result result;
std::string source = GetShaderCode(
source_code, stage == RSLC_STAGE_COMPUTE ? COMPUTE_SHADER_HEADER : SHADER_HEADER);
std::optional<SPIRVCodeVector> spv;
memset(&opts, 0, sizeof(opts));
opts.dst = RSLC_DST_SPIRV;
opts.err = err;
opts.err_cap = sizeof(err);
err[0] = '\0';
if (!rslc_compile(source.data(), source.size(), stage, &opts, &result))
{
ReportFailure(stage_name, source, err);
return spv;
}
spv = SPIRVCodeVector(result.spirv, result.spirv + result.spirv_len);
rslc_result_free(&result, &opts);
return spv;
}
std::optional<SPIRVCodeVector> CompileVertexShader(std::string_view source_code)
{
return Compile(source_code, RSLC_STAGE_VERTEX, "vs");
}
std::optional<SPIRVCodeVector> CompileGeometryShader(std::string_view source_code)
{
return Compile(source_code, RSLC_STAGE_GEOMETRY, "gs");
}
std::optional<SPIRVCodeVector> CompileFragmentShader(std::string_view source_code)
{
return Compile(source_code, RSLC_STAGE_FRAGMENT, "ps");
}
std::optional<SPIRVCodeVector> CompileComputeShader(std::string_view source_code)
{
return Compile(source_code, RSLC_STAGE_COMPUTE, "cs");
}
} // namespace Vulkan::ShaderCompiler