GPUDevice: Support transpiling shaders at compile time

And use it for GLSL postprocessing shaders.
This commit is contained in:
Stenzek 2024-06-14 14:37:33 +10:00
parent f0c2832d03
commit ef69c31e9f
No known key found for this signature in database
37 changed files with 918 additions and 470 deletions

View file

@ -34,21 +34,20 @@ if(ENABLE_WAYLAND)
find_package(Wayland REQUIRED Egl)
endif()
if(ENABLE_VULKAN OR APPLE)
if(ENABLE_VULKAN)
find_package(Shaderc REQUIRED)
find_package(spirv_cross_c_shared REQUIRED)
if(LINUX AND ENABLE_VULKAN)
if(LINUX)
# We need to add the rpath for shaderc to the executable.
get_filename_component(SHADERC_LIBRARY_DIRECTORY ${SHADERC_LIBRARY} DIRECTORY)
list(APPEND CMAKE_BUILD_RPATH ${SHADERC_LIBRARY_DIRECTORY})
get_target_property(SPIRV_CROSS_LIBRARY spirv-cross-c-shared IMPORTED_LOCATION)
get_filename_component(SPIRV_CROSS_LIBRARY_DIRECTORY ${SPIRV_CROSS_LIBRARY} DIRECTORY)
list(APPEND CMAKE_BUILD_RPATH ${SPIRV_CROSS_LIBRARY_DIRECTORY})
endif()
endif()
if(APPLE)
# SPIRV-Cross is currently only used on MacOS.
find_package(spirv_cross_c_shared REQUIRED)
endif()
if(LINUX)
find_package(UDEV REQUIRED)
endif()

View file

@ -56,7 +56,8 @@ APPDIRNAME=DuckStation.AppDir
STRIP=strip
declare -a MANUAL_LIBS=(
"libshaderc_shared.so.1"
"libshaderc_shared.so"
"libspirv-cross-c-shared.so"
)
declare -a MANUAL_QT_LIBS=(
@ -95,7 +96,7 @@ OUTDIR=$(realpath "./$APPDIRNAME")
rm -fr "$OUTDIR"
echo "Locating extra libraries..."
EXTRA_LIBS_ARGS=""
EXTRA_LIBS_ARGS=()
for lib in "${MANUAL_LIBS[@]}"; do
srcpath=$(find "$DEPSDIR" -name "$lib")
if [ ! -f "$srcpath" ]; then
@ -104,12 +105,7 @@ for lib in "${MANUAL_LIBS[@]}"; do
fi
echo "Found $lib at $srcpath."
if [ "$EXTRA_LIBS_ARGS" == "" ]; then
EXTRA_LIBS_ARGS="--library=$srcpath"
else
EXTRA_LIBS_ARGS="$EXTRA_LIBS_ARGS,$srcpath"
fi
EXTRA_LIBS_ARGS+=("--library=$srcpath")
done
# Why the nastyness? linuxdeploy strips our main binary, and there's no option to turn it off.
@ -134,7 +130,7 @@ EXTRA_PLATFORM_PLUGINS="libqwayland-egl.so;libqwayland-generic.so" \
DEPLOY_PLATFORM_THEMES="1" \
QMAKE="$DEPSDIR/bin/qmake" \
NO_STRIP="1" \
$LINUXDEPLOY --plugin qt --appdir="$OUTDIR" --executable="$BUILDDIR/bin/duckstation-qt" $EXTRA_LIBS_ARGS \
$LINUXDEPLOY --plugin qt --appdir="$OUTDIR" --executable="$BUILDDIR/bin/duckstation-qt" ${EXTRA_LIBS_ARGS[@]} \
--desktop-file="$ROOTDIR/scripts/org.duckstation.DuckStation.desktop" \
--icon-file="$ROOTDIR/scripts/org.duckstation.DuckStation.png" \

View file

@ -92,7 +92,7 @@ eb11e1b3715b2211442b7e5933a1135885b664cc10530a1a022355fe9e1bb4ac SPIRV-Cross-$S
EOF
curl -L \
-O "https://download.savannah.gnu.org/releases/freetype/freetype-$FREETYPE.tar.xz" \
-o "freetype-$FREETYPE.tar.xz" "https://sourceforge.net/projects/freetype/files/freetype2/$FREETYPE/freetype-$FREETYPE.tar.xz/download" \
-o "harfbuzz-$HARFBUZZ.tar.gz" "https://github.com/harfbuzz/harfbuzz/archive/refs/tags/$HARFBUZZ.tar.gz" \
-O "https://libsdl.org/release/$SDL.tar.gz" \
-O "http://zlib.net/zlib-$ZLIB.tar.gz" \
@ -123,16 +123,6 @@ make -C build "-j$NPROCS"
make -C build install
cd ..
# Temporarily disabled, because the updater doesn't get fixup'd, so the dylib doesn't get added to its bundle.
#echo "Installing Zlib..."
#rm -fr "zlib-$ZLIB"
#tar xf "zlib-$ZLIB.tar.gz"
#cd "zlib-$ZLIB"
#cmake -B build "${CMAKE_COMMON[@]}" "$CMAKE_ARCH_UNIVERSAL" -DBUILD_SHARED_LIBS=ON -DZLIB_BUILD_EXAMPLES=OFF
#make -C build "-j$NPROCS"
#make -C build install
#cd ..
echo "Installing Zstd..."
rm -fr "zstd-$ZSTD"
tar xf "zstd-$ZSTD.tar.gz"

View file

@ -0,0 +1,43 @@
{
"name": "spirv-cross",
"buildsystem": "cmake-ninja",
"builddir": true,
"config-opts": [
"-DCMAKE_BUILD_TYPE=Release",
"-DSPIRV_CROSS_SHARED=ON",
"-DSPIRV_CROSS_STATIC=OFF",
"-DSPIRV_CROSS_CLI=OFF",
"-DSPIRV_CROSS_ENABLE_TESTS=OFF",
"-DSPIRV_CROSS_ENABLE_GLSL=ON",
"-DSPIRV_CROSS_ENABLE_HLSL=OFF",
"-DSPIRV_CROSS_ENABLE_MSL=OFF",
"-DSPIRV_CROSS_ENABLE_CPP=OFF",
"-DSPIRV_CROSS_ENABLE_REFLECT=OFF",
"-DSPIRV_CROSS_ENABLE_C_API=ON",
"-DSPIRV_CROSS_ENABLE_UTIL=ON"
],
"build-options": {
"strip": true
},
"sources": [
{
"type": "git",
"url": "https://github.com/KhronosGroup/SPIRV-Cross.git",
"tag": "vulkan-sdk-1.3.280.0",
"commit": "2a7c8184921897ff3d6c6c3f70af4099e2e00331"
},
{
"type": "patch",
"path": "../../spirv-cross-changes.patch"
}
],
"cleanup": [
"/bin",
"/include",
"/lib/*.a",
"/lib/*.la",
"/lib/cmake",
"/lib/pkgconfig",
"/share"
]
}

View file

@ -21,6 +21,7 @@
"modules/20-sdl2.json",
"modules/21-libbacktrace.json",
"modules/22-shaderc.json",
"modules/23-spirv-cross.json",
{
"name": "duckstation",
"buildsystem": "cmake-ninja",

View file

@ -43,7 +43,7 @@ diff --git a/libshaderc/CMakeLists.txt b/libshaderc/CMakeLists.txt
index df9a88d..b15e5d7 100644
--- a/libshaderc/CMakeLists.txt
+++ b/libshaderc/CMakeLists.txt
@@ -24,13 +24,6 @@ set(SHADERC_SOURCES
@@ -24,13 +24,6 @@
src/shaderc_private.h
)
@ -57,7 +57,15 @@ index df9a88d..b15e5d7 100644
add_library(shaderc_shared SHARED ${SHADERC_SOURCES})
shaderc_default_compile_options(shaderc_shared)
target_include_directories(shaderc_shared
@@ -54,7 +47,7 @@ if(SHADERC_ENABLE_INSTALL)
@@ -41,7 +34,6 @@
PRIVATE SHADERC_IMPLEMENTATION
PUBLIC SHADERC_SHAREDLIB
)
-set_target_properties(shaderc_shared PROPERTIES SOVERSION 1)
if(SHADERC_ENABLE_INSTALL)
install(
@@ -54,7 +46,7 @@
DESTINATION
${CMAKE_INSTALL_INCLUDEDIR}/shaderc)
@ -66,14 +74,14 @@ index df9a88d..b15e5d7 100644
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
BUNDLE DESTINATION ${CMAKE_INSTALL_BINDIR}
@@ -69,20 +62,8 @@ set(SHADERC_LIBS
@@ -69,21 +61,9 @@
SPIRV-Tools
)
-target_link_libraries(shaderc PRIVATE ${SHADERC_LIBS})
target_link_libraries(shaderc_shared PRIVATE ${SHADERC_LIBS})
-shaderc_add_tests(
shaderc_add_tests(
- TEST_PREFIX shaderc
- LINK_LIBS shaderc
- INCLUDE_DIRS include ${shaderc_SOURCE_DIR}/libshaderc_util/include ${glslang_SOURCE_DIR}
@ -84,10 +92,11 @@ index df9a88d..b15e5d7 100644
- shaderc_cpp
- shaderc_private)
-
shaderc_add_tests(
-shaderc_add_tests(
TEST_PREFIX shaderc_shared
LINK_LIBS shaderc_shared SPIRV-Tools
@@ -94,22 +75,6 @@ shaderc_add_tests(
INCLUDE_DIRS include ${shaderc_SOURCE_DIR}/libshaderc_util/include ${glslang_SOURCE_DIR}
@@ -94,22 +74,6 @@
shaderc_cpp
shaderc_private)

View file

@ -12,3 +12,16 @@
execute_process(
COMMAND ${GIT_EXECUTABLE} describe --always --tags --dirty=+
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
@@ -409,9 +409,9 @@
target_compile_definitions(spirv-cross-c-shared PRIVATE SPVC_EXPORT_SYMBOLS)
- set_target_properties(spirv-cross-c-shared PROPERTIES
- VERSION ${SPIRV_CROSS_VERSION}
- SOVERSION ${spirv-cross-abi-major})
+ #set_target_properties(spirv-cross-c-shared PROPERTIES
+ # VERSION ${SPIRV_CROSS_VERSION}
+ # SOVERSION ${spirv-cross-abi-major})
endif()
if (SPIRV_CROSS_CLI)

View file

@ -1756,8 +1756,8 @@ bool GPU::CompileDisplayPipelines(bool display, bool deinterlace, bool chroma_sm
break;
}
std::unique_ptr<GPUShader> vso = g_gpu_device->CreateShader(GPUShaderStage::Vertex, vs);
std::unique_ptr<GPUShader> fso = g_gpu_device->CreateShader(GPUShaderStage::Fragment, fs);
std::unique_ptr<GPUShader> vso = g_gpu_device->CreateShader(GPUShaderStage::Vertex, shadergen.GetLanguage(), vs);
std::unique_ptr<GPUShader> fso = g_gpu_device->CreateShader(GPUShaderStage::Fragment, shadergen.GetLanguage(), fs);
if (!vso || !fso)
return false;
GL_OBJECT_NAME(vso, "Display Vertex Shader");
@ -1776,14 +1776,14 @@ bool GPU::CompileDisplayPipelines(bool display, bool deinterlace, bool chroma_sm
{
plconfig.SetTargetFormats(GPUTexture::Format::RGBA8);
std::unique_ptr<GPUShader> vso =
g_gpu_device->CreateShader(GPUShaderStage::Vertex, shadergen.GenerateScreenQuadVertexShader());
std::unique_ptr<GPUShader> vso = g_gpu_device->CreateShader(GPUShaderStage::Vertex, shadergen.GetLanguage(),
shadergen.GenerateScreenQuadVertexShader());
if (!vso)
return false;
GL_OBJECT_NAME(vso, "Deinterlace Vertex Shader");
std::unique_ptr<GPUShader> fso;
if (!(fso = g_gpu_device->CreateShader(GPUShaderStage::Fragment,
if (!(fso = g_gpu_device->CreateShader(GPUShaderStage::Fragment, shadergen.GetLanguage(),
shadergen.GenerateInterleavedFieldExtractFragmentShader())))
{
return false;
@ -1806,7 +1806,7 @@ bool GPU::CompileDisplayPipelines(bool display, bool deinterlace, bool chroma_sm
case DisplayDeinterlacingMode::Weave:
{
if (!(fso = g_gpu_device->CreateShader(GPUShaderStage::Fragment,
if (!(fso = g_gpu_device->CreateShader(GPUShaderStage::Fragment, shadergen.GetLanguage(),
shadergen.GenerateDeinterlaceWeaveFragmentShader())))
{
return false;
@ -1826,7 +1826,7 @@ bool GPU::CompileDisplayPipelines(bool display, bool deinterlace, bool chroma_sm
case DisplayDeinterlacingMode::Blend:
{
if (!(fso = g_gpu_device->CreateShader(GPUShaderStage::Fragment,
if (!(fso = g_gpu_device->CreateShader(GPUShaderStage::Fragment, shadergen.GetLanguage(),
shadergen.GenerateDeinterlaceBlendFragmentShader())))
{
return false;
@ -1846,8 +1846,8 @@ bool GPU::CompileDisplayPipelines(bool display, bool deinterlace, bool chroma_sm
case DisplayDeinterlacingMode::Adaptive:
{
fso =
g_gpu_device->CreateShader(GPUShaderStage::Fragment, shadergen.GenerateFastMADReconstructFragmentShader());
fso = g_gpu_device->CreateShader(GPUShaderStage::Fragment, shadergen.GetLanguage(),
shadergen.GenerateFastMADReconstructFragmentShader());
if (!fso)
return false;
@ -1877,10 +1877,10 @@ bool GPU::CompileDisplayPipelines(bool display, bool deinterlace, bool chroma_sm
plconfig.layout = GPUPipeline::Layout::SingleTextureAndPushConstants;
plconfig.SetTargetFormats(GPUTexture::Format::RGBA8);
std::unique_ptr<GPUShader> vso =
g_gpu_device->CreateShader(GPUShaderStage::Vertex, shadergen.GenerateScreenQuadVertexShader());
std::unique_ptr<GPUShader> fso =
g_gpu_device->CreateShader(GPUShaderStage::Fragment, shadergen.GenerateChromaSmoothingFragmentShader());
std::unique_ptr<GPUShader> vso = g_gpu_device->CreateShader(GPUShaderStage::Vertex, shadergen.GetLanguage(),
shadergen.GenerateScreenQuadVertexShader());
std::unique_ptr<GPUShader> fso = g_gpu_device->CreateShader(GPUShaderStage::Fragment, shadergen.GetLanguage(),
shadergen.GenerateChromaSmoothingFragmentShader());
if (!vso || !fso)
return false;
GL_OBJECT_NAME(vso, "Chroma Smoothing Vertex Shader");

View file

@ -813,8 +813,11 @@ bool GPU_HW::CompilePipelines()
for (u8 textured = 0; textured < 2; textured++)
{
const std::string vs = shadergen.GenerateBatchVertexShader(ConvertToBoolUnchecked(textured), m_pgxp_depth_buffer);
if (!(batch_vertex_shaders[textured] = g_gpu_device->CreateShader(GPUShaderStage::Vertex, vs)))
if (!(batch_vertex_shaders[textured] =
g_gpu_device->CreateShader(GPUShaderStage::Vertex, shadergen.GetLanguage(), vs)))
{
return false;
}
progress.Increment();
}
@ -857,7 +860,8 @@ bool GPU_HW::CompilePipelines()
ConvertToBoolUnchecked(interlacing), ConvertToBoolUnchecked(check_mask));
if (!(batch_fragment_shaders[render_mode][transparency_mode][texture_mode][check_mask][dithering]
[interlacing] = g_gpu_device->CreateShader(GPUShaderStage::Fragment, fs)))
[interlacing] = g_gpu_device->CreateShader(GPUShaderStage::Fragment,
shadergen.GetLanguage(), fs)))
{
return false;
}
@ -1033,10 +1037,10 @@ bool GPU_HW::CompilePipelines()
if (m_wireframe_mode != GPUWireframeMode::Disabled)
{
std::unique_ptr<GPUShader> gs =
g_gpu_device->CreateShader(GPUShaderStage::Geometry, shadergen.GenerateWireframeGeometryShader());
std::unique_ptr<GPUShader> fs =
g_gpu_device->CreateShader(GPUShaderStage::Fragment, shadergen.GenerateWireframeFragmentShader());
std::unique_ptr<GPUShader> gs = g_gpu_device->CreateShader(GPUShaderStage::Geometry, shadergen.GetLanguage(),
shadergen.GenerateWireframeGeometryShader());
std::unique_ptr<GPUShader> fs = g_gpu_device->CreateShader(GPUShaderStage::Fragment, shadergen.GetLanguage(),
shadergen.GenerateWireframeFragmentShader());
if (!gs || !fs)
return false;
@ -1069,8 +1073,8 @@ bool GPU_HW::CompilePipelines()
batch_shader_guard.Run();
// use a depth of 1, that way writes will reset the depth
std::unique_ptr<GPUShader> fullscreen_quad_vertex_shader =
g_gpu_device->CreateShader(GPUShaderStage::Vertex, shadergen.GenerateScreenQuadVertexShader(1.0f));
std::unique_ptr<GPUShader> fullscreen_quad_vertex_shader = g_gpu_device->CreateShader(
GPUShaderStage::Vertex, shadergen.GetLanguage(), shadergen.GenerateScreenQuadVertexShader(1.0f));
if (!fullscreen_quad_vertex_shader)
return false;
@ -1090,7 +1094,7 @@ bool GPU_HW::CompilePipelines()
for (u8 interlaced = 0; interlaced < 2; interlaced++)
{
std::unique_ptr<GPUShader> fs = g_gpu_device->CreateShader(
GPUShaderStage::Fragment,
GPUShaderStage::Fragment, shadergen.GetLanguage(),
shadergen.GenerateVRAMFillFragmentShader(ConvertToBoolUnchecked(wrapped), ConvertToBoolUnchecked(interlaced)));
if (!fs)
return false;
@ -1108,8 +1112,8 @@ bool GPU_HW::CompilePipelines()
// VRAM copy
{
std::unique_ptr<GPUShader> fs =
g_gpu_device->CreateShader(GPUShaderStage::Fragment, shadergen.GenerateVRAMCopyFragmentShader());
std::unique_ptr<GPUShader> fs = g_gpu_device->CreateShader(GPUShaderStage::Fragment, shadergen.GetLanguage(),
shadergen.GenerateVRAMCopyFragmentShader());
if (!fs)
return false;
@ -1136,8 +1140,9 @@ bool GPU_HW::CompilePipelines()
{
const bool use_buffer = features.supports_texture_buffers;
const bool use_ssbo = features.texture_buffers_emulated_with_ssbo;
std::unique_ptr<GPUShader> fs = g_gpu_device->CreateShader(
GPUShaderStage::Fragment, shadergen.GenerateVRAMWriteFragmentShader(use_buffer, use_ssbo));
std::unique_ptr<GPUShader> fs =
g_gpu_device->CreateShader(GPUShaderStage::Fragment, shadergen.GetLanguage(),
shadergen.GenerateVRAMWriteFragmentShader(use_buffer, use_ssbo));
if (!fs)
return false;
@ -1166,8 +1171,8 @@ bool GPU_HW::CompilePipelines()
// VRAM write replacement
{
std::unique_ptr<GPUShader> fs =
g_gpu_device->CreateShader(GPUShaderStage::Fragment, shadergen.GenerateCopyFragmentShader());
std::unique_ptr<GPUShader> fs = g_gpu_device->CreateShader(GPUShaderStage::Fragment, shadergen.GetLanguage(),
shadergen.GenerateCopyFragmentShader());
if (!fs)
return false;
@ -1184,8 +1189,8 @@ bool GPU_HW::CompilePipelines()
// VRAM update depth
if (needs_depth_buffer)
{
std::unique_ptr<GPUShader> fs =
g_gpu_device->CreateShader(GPUShaderStage::Fragment, shadergen.GenerateVRAMUpdateDepthFragmentShader());
std::unique_ptr<GPUShader> fs = g_gpu_device->CreateShader(GPUShaderStage::Fragment, shadergen.GetLanguage(),
shadergen.GenerateVRAMUpdateDepthFragmentShader());
if (!fs)
return false;
@ -1210,8 +1215,8 @@ bool GPU_HW::CompilePipelines()
// VRAM read
{
std::unique_ptr<GPUShader> fs =
g_gpu_device->CreateShader(GPUShaderStage::Fragment, shadergen.GenerateVRAMReadFragmentShader());
std::unique_ptr<GPUShader> fs = g_gpu_device->CreateShader(GPUShaderStage::Fragment, shadergen.GetLanguage(),
shadergen.GenerateVRAMReadFragmentShader());
if (!fs)
return false;
@ -1228,8 +1233,9 @@ bool GPU_HW::CompilePipelines()
{
for (u8 depth_24 = 0; depth_24 < 2; depth_24++)
{
std::unique_ptr<GPUShader> fs = g_gpu_device->CreateShader(
GPUShaderStage::Fragment, shadergen.GenerateVRAMExtractFragmentShader(ConvertToBoolUnchecked(depth_24)));
std::unique_ptr<GPUShader> fs =
g_gpu_device->CreateShader(GPUShaderStage::Fragment, shadergen.GetLanguage(),
shadergen.GenerateVRAMExtractFragmentShader(ConvertToBoolUnchecked(depth_24)));
if (!fs)
return false;
@ -1244,10 +1250,10 @@ bool GPU_HW::CompilePipelines()
if (m_downsample_mode == GPUDownsampleMode::Adaptive)
{
std::unique_ptr<GPUShader> vs =
g_gpu_device->CreateShader(GPUShaderStage::Vertex, shadergen.GenerateAdaptiveDownsampleVertexShader());
std::unique_ptr<GPUShader> fs =
g_gpu_device->CreateShader(GPUShaderStage::Fragment, shadergen.GenerateAdaptiveDownsampleMipFragmentShader(true));
std::unique_ptr<GPUShader> vs = g_gpu_device->CreateShader(GPUShaderStage::Vertex, shadergen.GetLanguage(),
shadergen.GenerateAdaptiveDownsampleVertexShader());
std::unique_ptr<GPUShader> fs = g_gpu_device->CreateShader(
GPUShaderStage::Fragment, shadergen.GetLanguage(), shadergen.GenerateAdaptiveDownsampleMipFragmentShader(true));
if (!vs || !fs)
return false;
GL_OBJECT_NAME(fs, "Downsample Vertex Shader");
@ -1258,7 +1264,7 @@ bool GPU_HW::CompilePipelines()
return false;
GL_OBJECT_NAME(m_downsample_first_pass_pipeline, "Downsample First Pass Pipeline");
fs = g_gpu_device->CreateShader(GPUShaderStage::Fragment,
fs = g_gpu_device->CreateShader(GPUShaderStage::Fragment, shadergen.GetLanguage(),
shadergen.GenerateAdaptiveDownsampleMipFragmentShader(false));
if (!fs)
return false;
@ -1268,7 +1274,8 @@ bool GPU_HW::CompilePipelines()
return false;
GL_OBJECT_NAME(m_downsample_mid_pass_pipeline, "Downsample Mid Pass Pipeline");
fs = g_gpu_device->CreateShader(GPUShaderStage::Fragment, shadergen.GenerateAdaptiveDownsampleBlurFragmentShader());
fs = g_gpu_device->CreateShader(GPUShaderStage::Fragment, shadergen.GetLanguage(),
shadergen.GenerateAdaptiveDownsampleBlurFragmentShader());
if (!fs)
return false;
GL_OBJECT_NAME(fs, "Downsample Blur Pass Fragment Shader");
@ -1278,7 +1285,7 @@ bool GPU_HW::CompilePipelines()
return false;
GL_OBJECT_NAME(m_downsample_blur_pass_pipeline, "Downsample Blur Pass Pipeline");
fs = g_gpu_device->CreateShader(GPUShaderStage::Fragment,
fs = g_gpu_device->CreateShader(GPUShaderStage::Fragment, shadergen.GetLanguage(),
shadergen.GenerateAdaptiveDownsampleCompositeFragmentShader());
if (!fs)
return false;
@ -1304,8 +1311,9 @@ bool GPU_HW::CompilePipelines()
}
else if (m_downsample_mode == GPUDownsampleMode::Box)
{
std::unique_ptr<GPUShader> fs = g_gpu_device->CreateShader(
GPUShaderStage::Fragment, shadergen.GenerateBoxSampleDownsampleFragmentShader(
std::unique_ptr<GPUShader> fs =
g_gpu_device->CreateShader(GPUShaderStage::Fragment, shadergen.GetLanguage(),
shadergen.GenerateBoxSampleDownsampleFragmentShader(
m_resolution_scale / GetBoxDownsampleScale(m_resolution_scale)));
if (!fs)
return false;

View file

@ -10,11 +10,11 @@ GPU_HW_ShaderGen::GPU_HW_ShaderGen(RenderAPI render_api, u32 resolution_scale, u
GPUTextureFilter texture_filtering, bool uv_limits, bool write_mask_as_depth,
bool disable_color_perspective, bool supports_dual_source_blend,
bool supports_framebuffer_fetch, bool debanding)
: ShaderGen(render_api, supports_dual_source_blend, supports_framebuffer_fetch), m_resolution_scale(resolution_scale),
m_multisamples(multisamples), m_per_sample_shading(per_sample_shading), m_true_color(true_color),
m_scaled_dithering(scaled_dithering), m_texture_filter(texture_filtering), m_uv_limits(uv_limits),
m_write_mask_as_depth(write_mask_as_depth), m_disable_color_perspective(disable_color_perspective),
m_debanding(debanding)
: ShaderGen(render_api, GetShaderLanguageForAPI(render_api), supports_dual_source_blend, supports_framebuffer_fetch),
m_resolution_scale(resolution_scale), m_multisamples(multisamples), m_per_sample_shading(per_sample_shading),
m_true_color(true_color), m_scaled_dithering(scaled_dithering), m_texture_filter(texture_filtering),
m_uv_limits(uv_limits), m_write_mask_as_depth(write_mask_as_depth),
m_disable_color_perspective(disable_color_perspective), m_debanding(debanding)
{
}

View file

@ -4,7 +4,7 @@
#include "gpu_shadergen.h"
GPUShaderGen::GPUShaderGen(RenderAPI render_api, bool supports_dual_source_blend, bool supports_framebuffer_fetch)
: ShaderGen(render_api, supports_dual_source_blend, supports_framebuffer_fetch)
: ShaderGen(render_api, GetShaderLanguageForAPI(render_api), supports_dual_source_blend, supports_framebuffer_fetch)
{
}

View file

@ -4,4 +4,4 @@
#pragma once
#include "common/types.h"
static constexpr u32 SHADER_CACHE_VERSION = 14;
static constexpr u32 SHADER_CACHE_VERSION = 15;

View file

@ -209,7 +209,7 @@ if(WIN32)
)
#set_source_files_properties(${TS_FILES} PROPERTIES OUTPUT_LOCATION "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/translations")
set(DEPS_TO_COPY freetype.dll harfbuzz.dll libjpeg.dll libpng16.dll libsharpyuv.dll libwebp.dll SDL2.dll shaderc_shared.dll zlib1.dll zstd.dll)
set(DEPS_TO_COPY freetype.dll harfbuzz.dll libjpeg.dll libpng16.dll libsharpyuv.dll libwebp.dll SDL2.dll shaderc_shared.dll spirv-cross-c-shared.dll zlib1.dll zstd.dll)
foreach(DEP ${DEPS_TO_COPY})
list(APPEND DEP_BINS "${CMAKE_PREFIX_PATH}/bin/${DEP}")
endforeach()
@ -244,8 +244,9 @@ elseif(APPLE)
endif()
# Copy shaderc into the bundle
target_sources(duckstation-qt PRIVATE "${SHADERC_LIBRARY}")
set_source_files_properties("${SHADERC_LIBRARY}" PROPERTIES MACOSX_PACKAGE_LOCATION Frameworks)
get_target_property(SPIRV_CROSS_LIBRARY spirv-cross-c-shared IMPORTED_LOCATION_RELEASE)
target_sources(duckstation-qt PRIVATE "${SHADERC_LIBRARY}" "${SPIRV_CROSS_LIBRARY}")
set_source_files_properties("${SHADERC_LIBRARY}" "${SPIRV_CROSS_LIBRARY}" PROPERTIES MACOSX_PACKAGE_LOCATION Frameworks)
# Copy icon into the bundle
target_sources(duckstation-qt PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/DuckStation.icns")

View file

@ -248,7 +248,7 @@ elseif(APPLE)
find_library(IOK_LIBRARY IOKit REQUIRED)
find_library(METAL_LIBRARY Metal)
find_library(QUARTZCORE_LIBRARY QuartzCore)
target_link_libraries(util PRIVATE ${METAL_LIBRARY} ${QUARTZCORE_LIBRARY} ${IOK_LIBRARY} spirv-cross-c-shared)
target_link_libraries(util PRIVATE ${METAL_LIBRARY} ${QUARTZCORE_LIBRARY} ${IOK_LIBRARY})
set_source_files_properties(${MAC_SOURCES} PROPERTIES SKIP_PRECOMPILE_HEADERS TRUE)
elseif(NOT ANDROID)
target_sources(util PRIVATE

View file

@ -68,9 +68,11 @@ public:
void ClearDepth(GPUTexture* t, float d) override;
void InvalidateRenderTarget(GPUTexture* t) override;
std::unique_ptr<GPUShader> CreateShaderFromBinary(GPUShaderStage stage, std::span<const u8> data) override;
std::unique_ptr<GPUShader> CreateShaderFromSource(GPUShaderStage stage, std::string_view source,
const char* entry_point, DynamicHeapArray<u8>* binary) override;
std::unique_ptr<GPUShader> CreateShaderFromBinary(GPUShaderStage stage, std::span<const u8> data,
Error* error) override;
std::unique_ptr<GPUShader> CreateShaderFromSource(GPUShaderStage stage, GPUShaderLanguage language,
std::string_view source, const char* entry_point,
DynamicHeapArray<u8>* out_binary, Error* error) override;
std::unique_ptr<GPUPipeline> CreatePipeline(const GPUPipeline::GraphicsConfig& config) override;
void PushDebugGroup(const char* name) override;

View file

@ -1,10 +1,11 @@
// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
#include "d3d11_pipeline.h"
#include "d3d11_device.h"
#include "d3d_common.h"
#include "common/error.h"
#include "common/log.h"
#include "fmt/format.h"
@ -51,7 +52,8 @@ void D3D11Shader::SetDebugName(std::string_view name)
SetD3DDebugObjectName(m_shader.Get(), name);
}
std::unique_ptr<GPUShader> D3D11Device::CreateShaderFromBinary(GPUShaderStage stage, std::span<const u8> data)
std::unique_ptr<GPUShader> D3D11Device::CreateShaderFromBinary(GPUShaderStage stage, std::span<const u8> data,
Error* error)
{
ComPtr<ID3D11DeviceChild> shader;
std::vector<u8> bytecode;
@ -87,21 +89,31 @@ std::unique_ptr<GPUShader> D3D11Device::CreateShaderFromBinary(GPUShaderStage st
}
if (FAILED(hr) || !shader)
{
Error::SetHResult(error, "Create[Typed]Shader() failed: ", hr);
return {};
}
return std::unique_ptr<GPUShader>(new D3D11Shader(stage, std::move(shader), std::move(bytecode)));
}
std::unique_ptr<GPUShader> D3D11Device::CreateShaderFromSource(GPUShaderStage stage, std::string_view source,
const char* entry_point,
DynamicHeapArray<u8>* out_binary)
std::unique_ptr<GPUShader> D3D11Device::CreateShaderFromSource(GPUShaderStage stage, GPUShaderLanguage language,
std::string_view source, const char* entry_point,
DynamicHeapArray<u8>* out_binary, Error* error)
{
const u32 shader_model = D3DCommon::GetShaderModelForFeatureLevel(m_device->GetFeatureLevel());
if (language != GPUShaderLanguage::HLSL)
{
return TranspileAndCreateShaderFromSource(stage, language, source, entry_point, GPUShaderLanguage::HLSL,
shader_model, out_binary, error);
}
std::optional<DynamicHeapArray<u8>> bytecode =
D3DCommon::CompileShader(m_device->GetFeatureLevel(), m_debug_device, stage, source, entry_point);
D3DCommon::CompileShader(shader_model, m_debug_device, stage, source, entry_point, error);
if (!bytecode.has_value())
return {};
std::unique_ptr<GPUShader> ret = CreateShaderFromBinary(stage, bytecode.value());
std::unique_ptr<GPUShader> ret = CreateShaderFromBinary(stage, bytecode.value(), error);
if (ret && out_binary)
*out_binary = std::move(bytecode.value());

View file

@ -90,9 +90,11 @@ public:
void ClearDepth(GPUTexture* t, float d) override;
void InvalidateRenderTarget(GPUTexture* t) override;
std::unique_ptr<GPUShader> CreateShaderFromBinary(GPUShaderStage stage, std::span<const u8> data) override;
std::unique_ptr<GPUShader> CreateShaderFromSource(GPUShaderStage stage, std::string_view source,
const char* entry_point, DynamicHeapArray<u8>* out_binary) override;
std::unique_ptr<GPUShader> CreateShaderFromBinary(GPUShaderStage stage, std::span<const u8> data,
Error* error) override;
std::unique_ptr<GPUShader> CreateShaderFromSource(GPUShaderStage stage, GPUShaderLanguage language,
std::string_view source, const char* entry_point,
DynamicHeapArray<u8>* out_binary, Error* error) override;
std::unique_ptr<GPUPipeline> CreatePipeline(const GPUPipeline::GraphicsConfig& config) override;
void PushDebugGroup(const char* name) override;

View file

@ -25,23 +25,31 @@ void D3D12Shader::SetDebugName(std::string_view name)
{
}
std::unique_ptr<GPUShader> D3D12Device::CreateShaderFromBinary(GPUShaderStage stage, std::span<const u8> data)
std::unique_ptr<GPUShader> D3D12Device::CreateShaderFromBinary(GPUShaderStage stage, std::span<const u8> data,
Error* error)
{
// Can't do much at this point.
std::vector bytecode(data.begin(), data.end());
return std::unique_ptr<GPUShader>(new D3D12Shader(stage, std::move(bytecode)));
}
std::unique_ptr<GPUShader> D3D12Device::CreateShaderFromSource(GPUShaderStage stage, std::string_view source,
const char* entry_point,
DynamicHeapArray<u8>* out_binary)
std::unique_ptr<GPUShader> D3D12Device::CreateShaderFromSource(GPUShaderStage stage, GPUShaderLanguage language,
std::string_view source, const char* entry_point,
DynamicHeapArray<u8>* out_binary, Error* error)
{
const u32 shader_model = D3DCommon::GetShaderModelForFeatureLevel(m_feature_level);
if (language != GPUShaderLanguage::HLSL)
{
return TranspileAndCreateShaderFromSource(stage, language, source, entry_point, GPUShaderLanguage::HLSL,
shader_model, out_binary, error);
}
std::optional<DynamicHeapArray<u8>> bytecode =
D3DCommon::CompileShader(m_feature_level, m_debug_device, stage, source, entry_point);
D3DCommon::CompileShader(shader_model, m_debug_device, stage, source, entry_point, error);
if (!bytecode.has_value())
return {};
std::unique_ptr<GPUShader> ret = CreateShaderFromBinary(stage, bytecode.value());
std::unique_ptr<GPUShader> ret = CreateShaderFromBinary(stage, bytecode.value(), error);
if (ret && out_binary)
*out_binary = std::move(bytecode.value());

View file

@ -372,14 +372,33 @@ std::string D3DCommon::GetDriverVersionFromLUID(const LUID& luid)
return ret;
}
std::optional<DynamicHeapArray<u8>> D3DCommon::CompileShader(D3D_FEATURE_LEVEL feature_level, bool debug_device,
GPUShaderStage stage, std::string_view source,
const char* entry_point)
u32 D3DCommon::GetShaderModelForFeatureLevel(D3D_FEATURE_LEVEL feature_level)
{
const char* target;
switch (feature_level)
{
case D3D_FEATURE_LEVEL_10_0:
return 40;
case D3D_FEATURE_LEVEL_10_1:
return 41;
case D3D_FEATURE_LEVEL_11_0:
return 50;
case D3D_FEATURE_LEVEL_11_1:
default:
return 51;
}
}
std::optional<DynamicHeapArray<u8>> D3DCommon::CompileShader(u32 shader_model, bool debug_device, GPUShaderStage stage,
std::string_view source, const char* entry_point,
Error* error)
{
const char* target;
switch (shader_model)
{
case 40:
{
static constexpr std::array<const char*, static_cast<u32>(GPUShaderStage::MaxCount)> targets = {
{"vs_4_0", "ps_4_0", "gs_4_0", "cs_4_0"}};
@ -387,7 +406,7 @@ std::optional<DynamicHeapArray<u8>> D3DCommon::CompileShader(D3D_FEATURE_LEVEL f
}
break;
case D3D_FEATURE_LEVEL_10_1:
case 41:
{
static constexpr std::array<const char*, static_cast<u32>(GPUShaderStage::MaxCount)> targets = {
{"vs_4_1", "ps_4_1", "gs_4_0", "cs_4_1"}};
@ -395,7 +414,7 @@ std::optional<DynamicHeapArray<u8>> D3DCommon::CompileShader(D3D_FEATURE_LEVEL f
}
break;
case D3D_FEATURE_LEVEL_11_0:
case 50:
{
static constexpr std::array<const char*, static_cast<u32>(GPUShaderStage::MaxCount)> targets = {
{"vs_5_0", "ps_5_0", "gs_5_0", "cs_5_0"}};
@ -403,14 +422,17 @@ std::optional<DynamicHeapArray<u8>> D3DCommon::CompileShader(D3D_FEATURE_LEVEL f
}
break;
case D3D_FEATURE_LEVEL_11_1:
default:
case 51:
{
static constexpr std::array<const char*, static_cast<u32>(GPUShaderStage::MaxCount)> targets = {
{"vs_5_1", "ps_5_1", "gs_5_1", "cs_5_1"}};
target = targets[static_cast<int>(stage)];
}
break;
default:
Error::SetStringFmt(error, "Unknown shader model: {}", shader_model);
return {};
}
static constexpr UINT flags_non_debug = D3DCOMPILE_OPTIMIZATION_LEVEL3;
@ -424,13 +446,16 @@ std::optional<DynamicHeapArray<u8>> D3DCommon::CompileShader(D3D_FEATURE_LEVEL f
std::string_view error_string;
if (error_blob)
{
error_string =
std::string_view(static_cast<const char*>(error_blob->GetBufferPointer()), error_blob->GetBufferSize());
}
if (FAILED(hr))
{
ERROR_LOG("Failed to compile '{}':\n{}", target, error_string);
GPUDevice::DumpBadShader(source, error_string);
Error::SetHResult(error, "D3DCompile() failed: ", hr);
return {};
}

View file

@ -60,9 +60,10 @@ std::string GetAdapterName(IDXGIAdapter1* adapter);
// returns the driver version from the registry as a string
std::string GetDriverVersionFromLUID(const LUID& luid);
std::optional<DynamicHeapArray<u8>> CompileShader(D3D_FEATURE_LEVEL feature_level, bool debug_device,
GPUShaderStage stage, std::string_view source,
const char* entry_point);
u32 GetShaderModelForFeatureLevel(D3D_FEATURE_LEVEL feature_level);
std::optional<DynamicHeapArray<u8>> CompileShader(u32 shader_model, bool debug_device, GPUShaderStage stage,
std::string_view source, const char* entry_point, Error* error);
struct DXGIFormatMapping
{

View file

@ -13,11 +13,14 @@
#include "common/file_system.h"
#include "common/log.h"
#include "common/path.h"
#include "common/scoped_guard.h"
#include "common/string_util.h"
#include "common/timer.h"
#include "fmt/format.h"
#include "imgui.h"
#include "shaderc/shaderc.h"
#include "spirv_cross/spirv_cross_c.h"
#include "xxhash.h"
Log_SetChannel(GPUDevice);
@ -37,10 +40,6 @@ Log_SetChannel(GPUDevice);
#include "vulkan_device.h"
#endif
#if defined(ENABLE_VULKAN) || defined(__APPLE__)
#include "shaderc/shaderc.h"
#endif
std::unique_ptr<GPUDevice> g_gpu_device;
static std::string s_pipeline_cache_path;
@ -269,6 +268,24 @@ const char* GPUDevice::RenderAPIToString(RenderAPI api)
}
}
const char* GPUDevice::ShaderLanguageToString(GPUShaderLanguage language)
{
switch (language)
{
// clang-format off
#define CASE(x) case GPUShaderLanguage::x: return #x
CASE(HLSL);
CASE(GLSL);
CASE(GLSLES);
CASE(MSL);
CASE(SPV);
#undef CASE
// clang-format on
default:
return "Unknown";
}
}
bool GPUDevice::IsSameRenderAPI(RenderAPI lhs, RenderAPI rhs)
{
return (lhs == rhs || ((lhs == RenderAPI::OpenGL || lhs == RenderAPI::OpenGLES) &&
@ -300,9 +317,9 @@ bool GPUDevice::Create(std::string_view adapter, std::string_view shader_cache_p
OpenShaderCache(shader_cache_path, shader_cache_version);
if (!CreateResources())
if (!CreateResources(error))
{
Error::SetStringView(error, "Failed to create base resources.");
Error::AddPrefix(error, "Failed to create base resources.");
return false;
}
@ -459,18 +476,23 @@ bool GPUDevice::AcquireWindow(bool recreate_window)
return true;
}
bool GPUDevice::CreateResources()
bool GPUDevice::CreateResources(Error* error)
{
if (!(m_nearest_sampler = CreateSampler(GPUSampler::GetNearestConfig())))
if (!(m_nearest_sampler = CreateSampler(GPUSampler::GetNearestConfig())) ||
!(m_linear_sampler = CreateSampler(GPUSampler::GetLinearConfig())))
{
Error::SetStringView(error, "Failed to create samplers");
return false;
}
if (!(m_linear_sampler = CreateSampler(GPUSampler::GetLinearConfig())))
return false;
const RenderAPI render_api = GetRenderAPI();
ShaderGen shadergen(render_api, ShaderGen::GetShaderLanguageForAPI(render_api), m_features.dual_source_blend,
m_features.framebuffer_fetch);
ShaderGen shadergen(GetRenderAPI(), m_features.dual_source_blend, m_features.framebuffer_fetch);
std::unique_ptr<GPUShader> imgui_vs = CreateShader(GPUShaderStage::Vertex, shadergen.GenerateImGuiVertexShader());
std::unique_ptr<GPUShader> imgui_fs = CreateShader(GPUShaderStage::Fragment, shadergen.GenerateImGuiFragmentShader());
std::unique_ptr<GPUShader> imgui_vs =
CreateShader(GPUShaderStage::Vertex, shadergen.GetLanguage(), shadergen.GenerateImGuiVertexShader(), error);
std::unique_ptr<GPUShader> imgui_fs =
CreateShader(GPUShaderStage::Fragment, shadergen.GetLanguage(), shadergen.GenerateImGuiFragmentShader(), error);
if (!imgui_vs || !imgui_fs)
return false;
GL_OBJECT_NAME(imgui_vs, "ImGui Vertex Shader");
@ -505,7 +527,7 @@ bool GPUDevice::CreateResources()
m_imgui_pipeline = CreatePipeline(plconfig);
if (!m_imgui_pipeline)
{
ERROR_LOG("Failed to compile ImGui pipeline.");
Error::SetStringView(error, "Failed to compile ImGui pipeline.");
return false;
}
GL_OBJECT_NAME(m_imgui_pipeline, "ImGui Pipeline");
@ -642,21 +664,22 @@ void GPUDevice::InvalidateRenderTarget(GPUTexture* t)
t->SetState(GPUTexture::State::Invalidated);
}
std::unique_ptr<GPUShader> GPUDevice::CreateShader(GPUShaderStage stage, std::string_view source,
std::unique_ptr<GPUShader> GPUDevice::CreateShader(GPUShaderStage stage, GPUShaderLanguage language,
std::string_view source, Error* error /* = nullptr */,
const char* entry_point /* = "main" */)
{
std::unique_ptr<GPUShader> shader;
if (!m_shader_cache.IsOpen())
{
shader = CreateShaderFromSource(stage, source, entry_point, nullptr);
shader = CreateShaderFromSource(stage, language, source, entry_point, nullptr, error);
return shader;
}
const GPUShaderCache::CacheIndexKey key = m_shader_cache.GetCacheKey(stage, source, entry_point);
const GPUShaderCache::CacheIndexKey key = m_shader_cache.GetCacheKey(stage, language, source, entry_point);
DynamicHeapArray<u8> binary;
if (m_shader_cache.Lookup(key, &binary))
{
shader = CreateShaderFromBinary(stage, binary);
shader = CreateShaderFromBinary(stage, binary, error);
if (shader)
return shader;
@ -664,7 +687,7 @@ std::unique_ptr<GPUShader> GPUDevice::CreateShader(GPUShaderStage stage, std::st
m_shader_cache.Clear();
}
shader = CreateShaderFromSource(stage, source, entry_point, &binary);
shader = CreateShaderFromSource(stage, language, source, entry_point, &binary, error);
if (!shader)
return shader;
@ -1095,7 +1118,6 @@ std::unique_ptr<GPUDevice> GPUDevice::CreateDeviceForAPI(RenderAPI api)
}
}
#if defined(ENABLE_VULKAN) || defined(__APPLE__)
#define SHADERC_FUNCTIONS(X) \
X(shaderc_compiler_initialize) \
X(shaderc_compiler_release) \
@ -1113,82 +1135,171 @@ std::unique_ptr<GPUDevice> GPUDevice::CreateDeviceForAPI(RenderAPI api)
X(shaderc_result_get_bytes) \
X(shaderc_result_get_error_message)
// TODO: NOT thread safe, yet.
namespace dyn_shaderc {
static bool Open();
static void Close();
#define SPIRV_CROSS_FUNCTIONS(X) \
X(spvc_context_create) \
X(spvc_context_destroy) \
X(spvc_context_set_error_callback) \
X(spvc_context_parse_spirv) \
X(spvc_context_create_compiler) \
X(spvc_compiler_create_compiler_options) \
X(spvc_compiler_create_shader_resources) \
X(spvc_compiler_get_execution_model) \
X(spvc_compiler_options_set_bool) \
X(spvc_compiler_options_set_uint) \
X(spvc_compiler_install_compiler_options) \
X(spvc_compiler_compile) \
X(spvc_resources_get_resource_list_for_type)
static DynamicLibrary s_library;
static shaderc_compiler_t s_compiler = nullptr;
#ifdef _WIN32
#define SPIRV_CROSS_HLSL_FUNCTIONS(X) X(spvc_compiler_hlsl_add_resource_binding)
#else
#define SPIRV_CROSS_HLSL_FUNCTIONS(X)
#endif
#ifdef __APPLE__
#define SPIRV_CROSS_MSL_FUNCTIONS(X) X(spvc_compiler_msl_add_resource_binding)
#else
#define SPIRV_CROSS_MSL_FUNCTIONS(X)
#endif
// TODO: NOT thread safe, yet.
namespace dyn_libs {
static bool OpenShaderc(Error* error);
static void CloseShaderc();
static bool OpenSpirvCross(Error* error);
static void CloseSpirvCross();
static void CloseAll();
static DynamicLibrary s_shaderc_library;
static DynamicLibrary s_spirv_cross_library;
static shaderc_compiler_t s_shaderc_compiler = nullptr;
static bool s_close_registered = false;
#define ADD_FUNC(F) static decltype(&::F) F;
SHADERC_FUNCTIONS(ADD_FUNC)
SPIRV_CROSS_FUNCTIONS(ADD_FUNC)
SPIRV_CROSS_HLSL_FUNCTIONS(ADD_FUNC)
SPIRV_CROSS_MSL_FUNCTIONS(ADD_FUNC)
#undef ADD_FUNC
} // namespace dyn_shaderc
} // namespace dyn_libs
bool dyn_shaderc::Open()
bool dyn_libs::OpenShaderc(Error* error)
{
if (s_library.IsOpen())
if (s_shaderc_library.IsOpen())
return true;
Error error;
#ifdef _WIN32
const std::string libname = DynamicLibrary::GetVersionedFilename("shaderc_shared");
#else
// Use versioned, bundle post-processing adds it..
const std::string libname = DynamicLibrary::GetVersionedFilename("shaderc_shared", 1);
#endif
if (!s_library.Open(libname.c_str(), &error))
if (!s_shaderc_library.Open(libname.c_str(), error))
{
ERROR_LOG("Failed to load shaderc: {}", error.GetDescription());
Error::AddPrefix(error, "Failed to load shaderc: ");
return false;
}
#define LOAD_FUNC(F) \
if (!s_library.GetSymbol(#F, &F)) \
if (!s_shaderc_library.GetSymbol(#F, &F)) \
{ \
ERROR_LOG("Failed to find function {}", #F); \
Close(); \
Error::SetStringFmt(error, "Failed to find function {}", #F); \
CloseShaderc(); \
return false; \
}
SHADERC_FUNCTIONS(LOAD_FUNC)
#undef LOAD_FUNC
s_compiler = shaderc_compiler_initialize();
if (!s_compiler)
s_shaderc_compiler = shaderc_compiler_initialize();
if (!s_shaderc_compiler)
{
ERROR_LOG("shaderc_compiler_initialize() failed");
Close();
Error::SetStringView(error, "shaderc_compiler_initialize() failed");
CloseShaderc();
return false;
}
std::atexit(&dyn_shaderc::Close);
if (!s_close_registered)
{
s_close_registered = true;
std::atexit(&dyn_libs::CloseAll);
}
return true;
}
void dyn_shaderc::Close()
void dyn_libs::CloseShaderc()
{
if (s_compiler)
if (s_shaderc_compiler)
{
shaderc_compiler_release(s_compiler);
s_compiler = nullptr;
shaderc_compiler_release(s_shaderc_compiler);
s_shaderc_compiler = nullptr;
}
#define UNLOAD_FUNC(F) F = nullptr;
SHADERC_FUNCTIONS(UNLOAD_FUNC)
#undef UNLOAD_FUNC
s_library.Close();
s_shaderc_library.Close();
}
#undef SHADERC_FUNCTIONS
#undef SHADERC_INIT_FUNCTIONS
bool dyn_libs::OpenSpirvCross(Error* error)
{
if (s_spirv_cross_library.IsOpen())
return true;
bool GPUDevice::CompileGLSLShaderToVulkanSpv(GPUShaderStage stage, std::string_view source, const char* entry_point,
bool nonsemantic_debug_info, DynamicHeapArray<u8>* out_binary)
const std::string libname = DynamicLibrary::GetVersionedFilename("spirv-cross-c-shared");
if (!s_spirv_cross_library.Open(libname.c_str(), error))
{
Error::AddPrefix(error, "Failed to load spirv-cross: ");
return false;
}
#define LOAD_FUNC(F) \
if (!s_spirv_cross_library.GetSymbol(#F, &F)) \
{ \
Error::SetStringFmt(error, "Failed to find function {}", #F); \
CloseShaderc(); \
return false; \
}
SPIRV_CROSS_FUNCTIONS(LOAD_FUNC)
SPIRV_CROSS_HLSL_FUNCTIONS(LOAD_FUNC)
SPIRV_CROSS_MSL_FUNCTIONS(LOAD_FUNC)
#undef LOAD_FUNC
if (!s_close_registered)
{
s_close_registered = true;
std::atexit(&dyn_libs::CloseAll);
}
return true;
}
void dyn_libs::CloseSpirvCross()
{
#define UNLOAD_FUNC(F) F = nullptr;
SPIRV_CROSS_FUNCTIONS(UNLOAD_FUNC)
SPIRV_CROSS_HLSL_FUNCTIONS(UNLOAD_FUNC)
SPIRV_CROSS_MSL_FUNCTIONS(UNLOAD_FUNC)
#undef UNLOAD_FUNC
s_spirv_cross_library.Close();
}
void dyn_libs::CloseAll()
{
CloseShaderc();
CloseSpirvCross();
}
#undef SPIRV_CROSS_HLSL_FUNCTIONS
#undef SPIRV_CROSS_MSL_FUNCTIONS
#undef SPIRV_CROSS_FUNCTIONS
#undef SHADERC_FUNCTIONS
bool GPUDevice::CompileGLSLShaderToVulkanSpv(GPUShaderStage stage, GPUShaderLanguage source_language,
std::string_view source, const char* entry_point,
bool nonsemantic_debug_info, DynamicHeapArray<u8>* out_binary,
Error* error)
{
static constexpr const std::array<shaderc_shader_kind, static_cast<size_t>(GPUShaderStage::MaxCount)> stage_kinds = {{
shaderc_glsl_vertex_shader,
@ -1197,46 +1308,323 @@ bool GPUDevice::CompileGLSLShaderToVulkanSpv(GPUShaderStage stage, std::string_v
shaderc_glsl_compute_shader,
}};
if (!dyn_shaderc::Open())
if (source_language != GPUShaderLanguage::GLSLVK)
{
Error::SetStringFmt(error, "Unsupported source language for transpile: {}",
ShaderLanguageToString(source_language));
return false;
}
if (!dyn_libs::OpenShaderc(error))
return false;
shaderc_compile_options_t options = dyn_shaderc::shaderc_compile_options_initialize();
shaderc_compile_options_t options = dyn_libs::shaderc_compile_options_initialize();
AssertMsg(options, "shaderc_compile_options_initialize() failed");
dyn_shaderc::shaderc_compile_options_set_source_language(options, shaderc_source_language_glsl);
dyn_shaderc::shaderc_compile_options_set_target_env(options, shaderc_target_env_vulkan, 0);
dyn_shaderc::shaderc_compile_options_set_generate_debug_info(options, m_debug_device,
dyn_libs::shaderc_compile_options_set_source_language(options, shaderc_source_language_glsl);
dyn_libs::shaderc_compile_options_set_target_env(options, shaderc_target_env_vulkan, 0);
dyn_libs::shaderc_compile_options_set_generate_debug_info(options, m_debug_device,
m_debug_device && nonsemantic_debug_info);
dyn_shaderc::shaderc_compile_options_set_optimization_level(
dyn_libs::shaderc_compile_options_set_optimization_level(
options, m_debug_device ? shaderc_optimization_level_zero : shaderc_optimization_level_performance);
shaderc_compilation_result_t result;
const shaderc_compilation_status status = dyn_shaderc::shaderc_compile_into_spv(
dyn_shaderc::s_compiler, source.data(), source.length(), stage_kinds[static_cast<size_t>(stage)], "source",
const shaderc_compilation_status status = dyn_libs::shaderc_compile_into_spv(
dyn_libs::s_shaderc_compiler, source.data(), source.length(), stage_kinds[static_cast<size_t>(stage)], "source",
entry_point, options, &result);
if (status != shaderc_compilation_status_success)
{
const std::string_view errors(result ? dyn_shaderc::shaderc_result_get_error_message(result) :
"null result object");
ERROR_LOG("Failed to compile shader to SPIR-V: {}\n{}", dyn_shaderc::shaderc_compilation_status_to_string(status),
const std::string_view errors(result ? dyn_libs::shaderc_result_get_error_message(result) : "null result object");
Error::SetStringFmt(error, "Failed to compile shader to SPIR-V: {}\n{}",
dyn_libs::shaderc_compilation_status_to_string(status), errors);
ERROR_LOG("Failed to compile shader to SPIR-V: {}\n{}", dyn_libs::shaderc_compilation_status_to_string(status),
errors);
DumpBadShader(source, errors);
}
else
{
const size_t num_warnings = dyn_shaderc::shaderc_result_get_num_warnings(result);
const size_t num_warnings = dyn_libs::shaderc_result_get_num_warnings(result);
if (num_warnings > 0)
WARNING_LOG("Shader compiled with warnings:\n{}", dyn_shaderc::shaderc_result_get_error_message(result));
WARNING_LOG("Shader compiled with warnings:\n{}", dyn_libs::shaderc_result_get_error_message(result));
const size_t spirv_size = dyn_shaderc::shaderc_result_get_length(result);
const size_t spirv_size = dyn_libs::shaderc_result_get_length(result);
DebugAssert(spirv_size > 0);
out_binary->resize(spirv_size);
std::memcpy(out_binary->data(), dyn_shaderc::shaderc_result_get_bytes(result), spirv_size);
std::memcpy(out_binary->data(), dyn_libs::shaderc_result_get_bytes(result), spirv_size);
}
dyn_shaderc::shaderc_result_release(result);
dyn_shaderc::shaderc_compile_options_release(options);
dyn_libs::shaderc_result_release(result);
dyn_libs::shaderc_compile_options_release(options);
return (status == shaderc_compilation_status_success);
}
bool GPUDevice::TranslateVulkanSpvToLanguage(const std::span<const u8> spirv, GPUShaderStage stage,
GPUShaderLanguage target_language, u32 target_version, std::string* output,
Error* error)
{
if (!dyn_libs::OpenSpirvCross(error))
return false;
spvc_context sctx;
spvc_result sres;
if ((sres = dyn_libs::spvc_context_create(&sctx)) != SPVC_SUCCESS)
{
Error::SetStringFmt(error, "spvc_context_create() failed: {}", static_cast<int>(sres));
return false;
}
const ScopedGuard sctx_guard = [&sctx]() { dyn_libs::spvc_context_destroy(sctx); };
dyn_libs::spvc_context_set_error_callback(
sctx,
[](void* error, const char* errormsg) {
ERROR_LOG("SPIRV-Cross reported an error: {}", errormsg);
Error::SetStringView(static_cast<Error*>(error), errormsg);
},
error);
spvc_parsed_ir sir;
if ((sres = dyn_libs::spvc_context_parse_spirv(sctx, reinterpret_cast<const u32*>(spirv.data()), spirv.size() / 4,
&sir)) != SPVC_SUCCESS)
{
Error::SetStringFmt(error, "spvc_context_parse_spirv() failed: {}", static_cast<int>(sres));
return {};
}
static constexpr std::array<spvc_backend, static_cast<size_t>(GPUShaderLanguage::Count)> backends = {
{SPVC_BACKEND_NONE, SPVC_BACKEND_HLSL, SPVC_BACKEND_GLSL, SPVC_BACKEND_GLSL, SPVC_BACKEND_GLSL, SPVC_BACKEND_MSL,
SPVC_BACKEND_NONE}};
spvc_compiler scompiler;
if ((sres = dyn_libs::spvc_context_create_compiler(sctx, backends[static_cast<size_t>(target_language)], sir,
SPVC_CAPTURE_MODE_TAKE_OWNERSHIP, &scompiler)) != SPVC_SUCCESS)
{
Error::SetStringFmt(error, "spvc_context_create_compiler() failed: {}", static_cast<int>(sres));
return {};
}
spvc_compiler_options soptions;
if ((sres = dyn_libs::spvc_compiler_create_compiler_options(scompiler, &soptions)) != SPVC_SUCCESS)
{
Error::SetStringFmt(error, "spvc_compiler_create_compiler_options() failed: {}", static_cast<int>(sres));
return {};
}
spvc_resources resources;
if ((sres = dyn_libs::spvc_compiler_create_shader_resources(scompiler, &resources)) != SPVC_SUCCESS)
{
Error::SetStringFmt(error, "spvc_compiler_create_shader_resources() failed: {}", static_cast<int>(sres));
return {};
}
// Need to know if there's UBOs for mapping.
const spvc_reflected_resource *ubos, *textures;
size_t ubos_count, textures_count;
if ((sres = dyn_libs::spvc_resources_get_resource_list_for_type(resources, SPVC_RESOURCE_TYPE_UNIFORM_BUFFER, &ubos,
&ubos_count)) != SPVC_SUCCESS ||
(sres = dyn_libs::spvc_resources_get_resource_list_for_type(resources, SPVC_RESOURCE_TYPE_SAMPLED_IMAGE,
&textures, &textures_count)) != SPVC_SUCCESS)
{
Error::SetStringFmt(error, "spvc_resources_get_resource_list_for_type() failed: {}", static_cast<int>(sres));
return {};
}
[[maybe_unused]] const SpvExecutionModel execmodel = dyn_libs::spvc_compiler_get_execution_model(scompiler);
switch (target_language)
{
case GPUShaderLanguage::HLSL:
{
#ifdef _WIN32
if ((sres = dyn_libs::spvc_compiler_options_set_uint(soptions, SPVC_COMPILER_OPTION_HLSL_SHADER_MODEL,
target_version)) != SPVC_SUCCESS)
{
Error::SetStringFmt(error, "spvc_compiler_options_set_uint(SPVC_COMPILER_OPTION_HLSL_SHADER_MODEL) failed: {}",
static_cast<int>(sres));
return {};
}
if ((sres = dyn_libs::spvc_compiler_options_set_bool(
soptions, SPVC_COMPILER_OPTION_HLSL_SUPPORT_NONZERO_BASE_VERTEX_BASE_INSTANCE, false)) != SPVC_SUCCESS)
{
Error::SetStringFmt(error,
"spvc_compiler_options_set_bool(SPVC_COMPILER_OPTION_HLSL_SUPPORT_NONZERO_BASE_VERTEX_"
"BASE_INSTANCE) failed: {}",
static_cast<int>(sres));
return {};
}
u32 start_set = 0;
if (ubos_count > 0)
{
const spvc_hlsl_resource_binding rb = {.stage = execmodel,
.desc_set = start_set++,
.binding = 0,
.cbv = {.register_space = 0, .register_binding = 0}};
if ((sres = dyn_libs::spvc_compiler_hlsl_add_resource_binding(scompiler, &rb)) != SPVC_SUCCESS)
{
Error::SetStringFmt(error, "spvc_compiler_hlsl_add_resource_binding() failed: {}", static_cast<int>(sres));
return {};
}
}
if (textures_count > 0)
{
for (u32 i = 0; i < MAX_TEXTURE_SAMPLERS; i++)
{
const spvc_hlsl_resource_binding rb = {.stage = execmodel,
.desc_set = start_set++,
.binding = i,
.srv = {.register_space = 0, .register_binding = i},
.sampler = {.register_space = 0, .register_binding = i}};
if ((sres = dyn_libs::spvc_compiler_hlsl_add_resource_binding(scompiler, &rb)) != SPVC_SUCCESS)
{
Error::SetStringFmt(error, "spvc_compiler_hlsl_add_resource_binding() failed: {}", static_cast<int>(sres));
return {};
}
}
}
#else
Error::SetStringView(error, "Unsupported platform.");
return {};
#endif
}
break;
case GPUShaderLanguage::GLSL:
case GPUShaderLanguage::GLSLES:
{
#ifdef ENABLE_OPENGL
if ((sres = dyn_libs::spvc_compiler_options_set_uint(soptions, SPVC_COMPILER_OPTION_GLSL_VERSION,
target_version)) != SPVC_SUCCESS)
{
Error::SetStringFmt(error, "spvc_compiler_options_set_uint(SPVC_COMPILER_OPTION_GLSL_VERSION) failed: {}",
static_cast<int>(sres));
return {};
}
const bool is_gles = (target_language == GPUShaderLanguage::GLSLES);
if ((sres = dyn_libs::spvc_compiler_options_set_bool(soptions, SPVC_COMPILER_OPTION_GLSL_ES, is_gles)) !=
SPVC_SUCCESS)
{
Error::SetStringFmt(error, "spvc_compiler_options_set_bool(SPVC_COMPILER_OPTION_GLSL_ES) failed: {}",
static_cast<int>(sres));
return {};
}
#else
Error::SetStringView(error, "Unsupported platform.");
return {};
#endif
}
break;
case GPUShaderLanguage::MSL:
{
#ifdef __APPLE__
if ((sres = dyn_libs::spvc_compiler_options_set_bool(
soptions, SPVC_COMPILER_OPTION_MSL_PAD_FRAGMENT_OUTPUT_COMPONENTS, true)) != SPVC_SUCCESS)
{
Error::SetStringFmt(
error, "spvc_compiler_options_set_bool(SPVC_COMPILER_OPTION_MSL_PAD_FRAGMENT_OUTPUT_COMPONENTS) failed: {}",
static_cast<int>(sres));
return {};
}
if ((sres = dyn_libs::spvc_compiler_options_set_bool(soptions, SPVC_COMPILER_OPTION_MSL_FRAMEBUFFER_FETCH_SUBPASS,
m_features.framebuffer_fetch)) != SPVC_SUCCESS)
{
Error::SetStringFmt(
error, "spvc_compiler_options_set_bool(SPVC_COMPILER_OPTION_MSL_FRAMEBUFFER_FETCH_SUBPASS) failed: {}",
static_cast<int>(sres));
return {};
}
if (m_features.framebuffer_fetch &&
((sres = dyn_libs::spvc_compiler_options_set_uint(soptions, SPVC_COMPILER_OPTION_MSL_VERSION,
SPVC_MAKE_MSL_VERSION(2, 3, 0))) != SPVC_SUCCESS))
{
Error::SetStringFmt(error, "spvc_compiler_options_set_uint(SPVC_COMPILER_OPTION_MSL_VERSION) failed: {}",
static_cast<int>(sres));
return {};
}
if (stage == GPUShaderStage::Fragment)
{
for (u32 i = 0; i < MAX_TEXTURE_SAMPLERS; i++)
{
const spvc_msl_resource_binding rb = {.stage = SpvExecutionModelFragment,
.desc_set = 1,
.binding = i,
.msl_buffer = i,
.msl_texture = i,
.msl_sampler = i};
if ((sres = dyn_libs::spvc_compiler_msl_add_resource_binding(scompiler, &rb)) != SPVC_SUCCESS)
{
Error::SetStringFmt(error, "spvc_compiler_msl_add_resource_binding() failed: {}", static_cast<int>(sres));
return {};
}
}
if (!m_features.framebuffer_fetch)
{
const spvc_msl_resource_binding rb = {
.stage = SpvExecutionModelFragment, .desc_set = 2, .binding = 0, .msl_texture = MAX_TEXTURE_SAMPLERS};
if ((sres = dyn_libs::spvc_compiler_msl_add_resource_binding(scompiler, &rb)) != SPVC_SUCCESS)
{
Error::SetStringFmt(error, "spvc_compiler_msl_add_resource_binding() for FB failed: {}",
static_cast<int>(sres));
return {};
}
}
}
#else
Error::SetStringView(error, "Unsupported platform.");
return {};
#endif
}
break;
}
if ((sres = dyn_libs::spvc_compiler_install_compiler_options(scompiler, soptions)) != SPVC_SUCCESS)
{
Error::SetStringFmt(error, "spvc_compiler_install_compiler_options() failed: {}", static_cast<int>(sres));
return false;
}
const char* out_src;
if ((sres = dyn_libs::spvc_compiler_compile(scompiler, &out_src)) != SPVC_SUCCESS)
{
Error::SetStringFmt(error, "spvc_compiler_compile() failed: {}", static_cast<int>(sres));
return false;
}
const size_t out_src_length = out_src ? std::strlen(out_src) : 0;
if (out_src_length == 0)
{
Error::SetStringView(error, "Failed to compile SPIR-V to target language.");
return false;
}
output->assign(out_src, out_src_length);
return true;
}
std::unique_ptr<GPUShader> GPUDevice::TranspileAndCreateShaderFromSource(
GPUShaderStage stage, GPUShaderLanguage source_language, std::string_view source, const char* entry_point,
GPUShaderLanguage target_language, u32 target_version, DynamicHeapArray<u8>* out_binary, Error* error)
{
DynamicHeapArray<u8> spv;
if (!CompileGLSLShaderToVulkanSpv(stage, source_language, source, entry_point, false, &spv, error))
return {};
std::string dest_source;
if (!TranslateVulkanSpvToLanguage(spv.cspan(), stage, target_language, target_version, &dest_source, error))
return {};
// TODO: MSL needs entry point suffixed.
return CreateShaderFromSource(stage, target_language, dest_source, entry_point, out_binary, error);
}

View file

@ -112,6 +112,18 @@ enum class GPUShaderStage : u8
MaxCount
};
enum class GPUShaderLanguage : u8
{
None,
HLSL,
GLSL,
GLSLES,
GLSLVK,
MSL,
SPV,
Count
};
class GPUShader
{
public:
@ -522,6 +534,9 @@ public:
/// Returns a string representing the specified API.
static const char* RenderAPIToString(RenderAPI api);
/// Returns a string representing the specified language.
static const char* ShaderLanguageToString(GPUShaderLanguage language);
/// Returns a new device for the specified API.
static std::unique_ptr<GPUDevice> CreateDeviceForAPI(RenderAPI api);
@ -630,8 +645,8 @@ public:
virtual void InvalidateRenderTarget(GPUTexture* t);
/// Shader abstraction.
std::unique_ptr<GPUShader> CreateShader(GPUShaderStage stage, std::string_view source,
const char* entry_point = "main");
std::unique_ptr<GPUShader> CreateShader(GPUShaderStage stage, GPUShaderLanguage language, std::string_view source,
Error* error = nullptr, const char* entry_point = "main");
virtual std::unique_ptr<GPUPipeline> CreatePipeline(const GPUPipeline::GraphicsConfig& config) = 0;
/// Debug messaging.
@ -716,19 +731,26 @@ protected:
virtual bool ReadPipelineCache(const std::string& filename);
virtual bool GetPipelineCacheData(DynamicHeapArray<u8>* data);
virtual std::unique_ptr<GPUShader> CreateShaderFromBinary(GPUShaderStage stage, std::span<const u8> data) = 0;
virtual std::unique_ptr<GPUShader> CreateShaderFromSource(GPUShaderStage stage, std::string_view source,
const char* entry_point,
DynamicHeapArray<u8>* out_binary) = 0;
virtual std::unique_ptr<GPUShader> CreateShaderFromBinary(GPUShaderStage stage, std::span<const u8> data,
Error* error) = 0;
virtual std::unique_ptr<GPUShader> CreateShaderFromSource(GPUShaderStage stage, GPUShaderLanguage language,
std::string_view source, const char* entry_point,
DynamicHeapArray<u8>* out_binary, Error* error) = 0;
bool AcquireWindow(bool recreate_window);
void TrimTexturePool();
#if defined(ENABLE_VULKAN) || defined(__APPLE__)
bool CompileGLSLShaderToVulkanSpv(GPUShaderStage stage, std::string_view source, const char* entry_point,
bool nonsemantic_debug_info, DynamicHeapArray<u8>* out_binary);
#endif
bool CompileGLSLShaderToVulkanSpv(GPUShaderStage stage, GPUShaderLanguage source_language, std::string_view source,
const char* entry_point, bool nonsemantic_debug_info,
DynamicHeapArray<u8>* out_binary, Error* error);
bool TranslateVulkanSpvToLanguage(const std::span<const u8> spirv, GPUShaderStage stage,
GPUShaderLanguage target_language, u32 target_version, std::string* output,
Error* error);
std::unique_ptr<GPUShader> TranspileAndCreateShaderFromSource(GPUShaderStage stage, GPUShaderLanguage source_language,
std::string_view source, const char* entry_point,
GPUShaderLanguage target_language, u32 target_version,
DynamicHeapArray<u8>* out_binary, Error* error);
Features m_features = {};
u32 m_max_texture_size = 0;
@ -778,7 +800,7 @@ private:
void OpenShaderCache(std::string_view base_path, u32 version);
void CloseShaderCache();
bool CreateResources();
bool CreateResources(Error* error);
void DestroyResources();
static bool IsTexturePoolType(GPUTexture::Type type);

View file

@ -20,7 +20,9 @@ Log_SetChannel(GPUShaderCache);
#pragma pack(push, 1)
struct CacheIndexEntry
{
u32 shader_type;
u8 shader_type;
u8 shader_language;
u8 unused[2];
u32 source_length;
u64 source_hash_low;
u64 source_hash_high;
@ -202,8 +204,9 @@ bool GPUShaderCache::ReadExisting(const std::string& index_filename, const std::
return false;
}
const CacheIndexKey key{entry.shader_type, entry.source_length, entry.source_hash_low,
entry.source_hash_high, entry.entry_point_low, entry.entry_point_high};
const CacheIndexKey key{entry.shader_type, entry.shader_language, {},
entry.source_length, entry.source_hash_low, entry.source_hash_high,
entry.entry_point_low, entry.entry_point_high};
const CacheIndexData data{entry.file_offset, entry.compressed_size, entry.uncompressed_size};
m_index.emplace(key, data);
}
@ -215,8 +218,8 @@ bool GPUShaderCache::ReadExisting(const std::string& index_filename, const std::
return true;
}
GPUShaderCache::CacheIndexKey GPUShaderCache::GetCacheKey(GPUShaderStage stage, std::string_view shader_code,
std::string_view entry_point)
GPUShaderCache::CacheIndexKey GPUShaderCache::GetCacheKey(GPUShaderStage stage, GPUShaderLanguage language,
std::string_view shader_code, std::string_view entry_point)
{
union
{
@ -229,7 +232,8 @@ GPUShaderCache::CacheIndexKey GPUShaderCache::GetCacheKey(GPUShaderStage stage,
} h;
CacheIndexKey key = {};
key.shader_type = static_cast<u32>(stage);
key.shader_type = static_cast<u8>(stage);
key.shader_language = static_cast<u8>(language);
MD5Digest digest;
digest.Update(shader_code.data(), static_cast<u32>(shader_code.length()));
@ -295,7 +299,8 @@ bool GPUShaderCache::Insert(const CacheIndexKey& key, const void* data, u32 data
idata.uncompressed_size = data_size;
CacheIndexEntry entry = {};
entry.shader_type = static_cast<u32>(key.shader_type);
entry.shader_type = static_cast<u8>(key.shader_type);
entry.shader_language = static_cast<u8>(key.shader_language);
entry.source_length = key.source_length;
entry.source_hash_low = key.source_hash_low;
entry.source_hash_high = key.source_hash_high;

View file

@ -13,6 +13,7 @@
#include <vector>
enum class GPUShaderStage : u8;
enum class GPUShaderLanguage : u8;
class GPUShaderCache
{
@ -21,7 +22,9 @@ public:
struct alignas(8) CacheIndexKey
{
u32 shader_type;
u8 shader_type;
u8 shader_language;
u8 unused[2];
u32 source_length;
u64 source_hash_low;
u64 source_hash_high;
@ -50,7 +53,8 @@ public:
bool Create();
void Close();
static CacheIndexKey GetCacheKey(GPUShaderStage stage, std::string_view shader_code, std::string_view entry_point);
static CacheIndexKey GetCacheKey(GPUShaderStage stage, GPUShaderLanguage language, std::string_view shader_code,
std::string_view entry_point);
bool Lookup(const CacheIndexKey& key, ShaderBinary* binary);
bool Insert(const CacheIndexKey& key, const void* data, u32 data_size);

View file

@ -231,10 +231,11 @@ public:
void ClearDepth(GPUTexture* t, float d) override;
void InvalidateRenderTarget(GPUTexture* t) override;
std::unique_ptr<GPUShader> CreateShaderFromBinary(GPUShaderStage stage, std::span<const u8> data) override;
std::unique_ptr<GPUShader> CreateShaderFromSource(GPUShaderStage stage, std::string_view source,
const char* entry_point,
DynamicHeapArray<u8>* out_binary = nullptr) override;
std::unique_ptr<GPUShader> CreateShaderFromBinary(GPUShaderStage stage, std::span<const u8> data,
Error* error) override;
std::unique_ptr<GPUShader> CreateShaderFromSource(GPUShaderStage stage, GPUShaderLanguage language,
std::string_view source, const char* entry_point,
DynamicHeapArray<u8>* out_binary, Error* error) override;
std::unique_ptr<GPUPipeline> CreatePipeline(const GPUPipeline::GraphicsConfig& config) override;
void PushDebugGroup(const char* name) override;
@ -328,7 +329,7 @@ private:
id<MTLRenderPipelineState> GetClearDepthPipeline(const ClearPipelineConfig& config);
std::unique_ptr<GPUShader> CreateShaderFromMSL(GPUShaderStage stage, std::string_view source,
std::string_view entry_point);
std::string_view entry_point, Error* error);
id<MTLDepthStencilState> GetDepthState(const GPUPipeline::DepthState& ds);

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2023 Connor McLaughlin <stenzek@gmail.com>
// SPDX-FileCopyrightText: 2024 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
#include "metal_device.h"
@ -16,8 +16,6 @@
#define FMT_EXCEPTIONS 0
#include "fmt/format.h"
#include "spirv_cross_c.h"
#include <array>
#include <pthread.h>
@ -612,19 +610,21 @@ static void DumpShader(u32 n, std::string_view suffix, std::string_view data)
}
std::unique_ptr<GPUShader> MetalDevice::CreateShaderFromMSL(GPUShaderStage stage, std::string_view source,
std::string_view entry_point)
std::string_view entry_point, Error* error)
{
@autoreleasepool
{
NSString* const ns_source = StringViewToNSString(source);
NSError* error = nullptr;
id<MTLLibrary> library = [m_device newLibraryWithSource:ns_source options:nil error:&error];
NSError* nserror = nullptr;
id<MTLLibrary> library = [m_device newLibraryWithSource:ns_source options:nil error:&nserror];
if (!library)
{
LogNSError(error, TinyString::from_format("Failed to compile {} shader", GPUShader::GetStageName(stage)));
LogNSError(nserror, TinyString::from_format("Failed to compile {} shader", GPUShader::GetStageName(stage)));
const char* utf_error = [error.description UTF8String];
DumpBadShader(source, fmt::format("Error {}: {}", static_cast<u32>(error.code), utf_error ? utf_error : ""));
const char* utf_error = [nserror.description UTF8String];
DumpBadShader(source, fmt::format("Error {}: {}", static_cast<u32>(nserror.code), utf_error ? utf_error : ""));
Error::SetStringFmt(error, "Failed to compile {} shader: Error {}: {}", GPUShader::GetStageName(stage),
static_cast<u32>(nserror.code), utf_error ? utf_error : "");
return {};
}
@ -632,6 +632,7 @@ std::unique_ptr<GPUShader> MetalDevice::CreateShaderFromMSL(GPUShaderStage stage
if (!function)
{
ERROR_LOG("Failed to get main function in compiled library");
Error::SetStringView(error, "Failed to get main function in compiled library");
return {};
}
@ -639,154 +640,42 @@ std::unique_ptr<GPUShader> MetalDevice::CreateShaderFromMSL(GPUShaderStage stage
}
}
std::unique_ptr<GPUShader> MetalDevice::CreateShaderFromBinary(GPUShaderStage stage, std::span<const u8> data)
std::unique_ptr<GPUShader> MetalDevice::CreateShaderFromBinary(GPUShaderStage stage, std::span<const u8> data,
Error* error)
{
const std::string_view str_data(reinterpret_cast<const char*>(data.data()), data.size());
return CreateShaderFromMSL(stage, str_data, "main0");
return CreateShaderFromMSL(stage, str_data, "main0", error);
}
std::unique_ptr<GPUShader> MetalDevice::CreateShaderFromSource(GPUShaderStage stage, std::string_view source,
const char* entry_point,
DynamicHeapArray<u8>* out_binary /* = nullptr */)
std::unique_ptr<GPUShader> MetalDevice::CreateShaderFromSource(GPUShaderStage stage, GPUShaderLanguage language,
std::string_view source, const char* entry_point,
DynamicHeapArray<u8>* out_binary, Error* error)
{
static constexpr bool dump_shaders = false;
DynamicHeapArray<u8> local_binary;
DynamicHeapArray<u8>* dest_binary = out_binary ? out_binary : &local_binary;
if (!CompileGLSLShaderToVulkanSpv(stage, source, entry_point, false, dest_binary))
DynamicHeapArray<u8> spv;
if (!CompileGLSLShaderToVulkanSpv(stage, language, source, entry_point, false, &spv, error))
return {};
AssertMsg((dest_binary->size() % 4) == 0, "Compile result should be 4 byte aligned.");
spvc_context sctx;
spvc_result sres;
if ((sres = spvc_context_create(&sctx)) != SPVC_SUCCESS)
{
ERROR_LOG("spvc_context_create() failed: {}", static_cast<int>(sres));
std::string msl;
if (!TranslateVulkanSpvToLanguage(spv.cspan(), stage, GPUShaderLanguage::MSL, 230, &msl, error))
return {};
}
const ScopedGuard sctx_guard = [&sctx]() { spvc_context_destroy(sctx); };
spvc_context_set_error_callback(
sctx, [](void*, const char* error) { ERROR_LOG("SPIRV-Cross reported an error: {}", error); }, nullptr);
spvc_parsed_ir sir;
if ((sres = spvc_context_parse_spirv(sctx, reinterpret_cast<const u32*>(dest_binary->data()), dest_binary->size() / 4, &sir)) != SPVC_SUCCESS)
{
ERROR_LOG("spvc_context_parse_spirv() failed: {}", static_cast<int>(sres));
DumpBadShader(source, std::string_view());
return {};
}
spvc_compiler scompiler;
if ((sres = spvc_context_create_compiler(sctx, SPVC_BACKEND_MSL, sir, SPVC_CAPTURE_MODE_TAKE_OWNERSHIP,
&scompiler)) != SPVC_SUCCESS)
{
ERROR_LOG("spvc_context_create_compiler() failed: {}", static_cast<int>(sres));
return {};
}
spvc_compiler_options soptions;
if ((sres = spvc_compiler_create_compiler_options(scompiler, &soptions)) != SPVC_SUCCESS)
{
ERROR_LOG("spvc_compiler_create_compiler_options() failed: {}", static_cast<int>(sres));
return {};
}
if ((sres = spvc_compiler_options_set_bool(soptions, SPVC_COMPILER_OPTION_MSL_PAD_FRAGMENT_OUTPUT_COMPONENTS,
true)) != SPVC_SUCCESS)
{
ERROR_LOG("spvc_compiler_options_set_bool(SPVC_COMPILER_OPTION_MSL_PAD_FRAGMENT_OUTPUT_COMPONENTS) failed: {}",
static_cast<int>(sres));
return {};
}
if ((sres = spvc_compiler_options_set_bool(soptions, SPVC_COMPILER_OPTION_MSL_FRAMEBUFFER_FETCH_SUBPASS,
m_features.framebuffer_fetch)) != SPVC_SUCCESS)
{
ERROR_LOG("spvc_compiler_options_set_bool(SPVC_COMPILER_OPTION_MSL_FRAMEBUFFER_FETCH_SUBPASS) failed: {}",
static_cast<int>(sres));
return {};
}
if (m_features.framebuffer_fetch &&
((sres = spvc_compiler_options_set_uint(soptions, SPVC_COMPILER_OPTION_MSL_VERSION,
SPVC_MAKE_MSL_VERSION(2, 3, 0))) != SPVC_SUCCESS))
{
ERROR_LOG("spvc_compiler_options_set_uint(SPVC_COMPILER_OPTION_MSL_VERSION) failed: {}", static_cast<int>(sres));
return {};
}
if (stage == GPUShaderStage::Fragment)
{
for (u32 i = 0; i < MAX_TEXTURE_SAMPLERS; i++)
{
const spvc_msl_resource_binding rb = {.stage = SpvExecutionModelFragment,
.desc_set = 1,
.binding = i,
.msl_buffer = i,
.msl_texture = i,
.msl_sampler = i};
if ((sres = spvc_compiler_msl_add_resource_binding(scompiler, &rb)) != SPVC_SUCCESS)
{
ERROR_LOG("spvc_compiler_msl_add_resource_binding() failed: {}", static_cast<int>(sres));
return {};
}
}
if (!m_features.framebuffer_fetch)
{
const spvc_msl_resource_binding rb = {
.stage = SpvExecutionModelFragment, .desc_set = 2, .binding = 0, .msl_texture = MAX_TEXTURE_SAMPLERS};
if ((sres = spvc_compiler_msl_add_resource_binding(scompiler, &rb)) != SPVC_SUCCESS)
{
ERROR_LOG("spvc_compiler_msl_add_resource_binding() for FB failed: {}", static_cast<int>(sres));
return {};
}
}
}
if ((sres = spvc_compiler_install_compiler_options(scompiler, soptions)) != SPVC_SUCCESS)
{
ERROR_LOG("spvc_compiler_install_compiler_options() failed: {}", static_cast<int>(sres));
return {};
}
const char* msl;
if ((sres = spvc_compiler_compile(scompiler, &msl)) != SPVC_SUCCESS)
{
ERROR_LOG("spvc_compiler_compile() failed: {}", static_cast<int>(sres));
DumpBadShader(source, std::string_view());
return {};
}
const size_t msl_length = msl ? std::strlen(msl) : 0;
if (msl_length == 0)
{
ERROR_LOG("Failed to compile SPIR-V to MSL.");
DumpBadShader(source, std::string_view());
return {};
}
const std::string_view mslv(msl, msl_length);
if constexpr (dump_shaders)
{
static unsigned s_next_id = 0;
++s_next_id;
DumpShader(s_next_id, "_input", source);
DumpShader(s_next_id, "_msl", mslv);
DumpShader(s_next_id, "_msl", msl);
}
if (out_binary)
{
out_binary->resize(mslv.size());
std::memcpy(out_binary->data(), mslv.data(), mslv.size());
out_binary->resize(msl.size());
std::memcpy(out_binary->data(), msl.data(), msl.size());
}
return CreateShaderFromMSL(stage, mslv, "main0");
return CreateShaderFromMSL(stage, msl, "main0", error);
}
MetalPipeline::MetalPipeline(id<MTLRenderPipelineState> pipeline, id<MTLDepthStencilState> depth, MTLCullMode cull_mode,
@ -1282,8 +1171,7 @@ std::unique_ptr<MetalDownloadTexture> MetalDownloadTexture::Create(u32 width, u3
reinterpret_cast<void*>(Common::AlignDownPow2(reinterpret_cast<uintptr_t>(memory), HOST_PAGE_SIZE));
const size_t page_offset = static_cast<size_t>(static_cast<u8*>(memory) - static_cast<u8*>(page_aligned_memory));
const size_t page_aligned_size = Common::AlignUpPow2(page_offset + memory_size, HOST_PAGE_SIZE);
DEV_LOG("Trying to import {} bytes of memory at {} for download texture", page_aligned_memory,
page_aligned_size);
DEV_LOG("Trying to import {} bytes of memory at {} for download texture", page_aligned_memory, page_aligned_size);
buffer = [[dev.m_device newBufferWithBytesNoCopy:page_aligned_memory
length:page_aligned_size

View file

@ -1092,7 +1092,7 @@ void OpenGLDevice::PushUniformBuffer(const void* data, u32 data_size)
std::memcpy(res.pointer, data, data_size);
m_uniform_buffer->Unmap(data_size);
s_stats.buffer_streamed += data_size;
glBindBufferRange(GL_UNIFORM_BUFFER, 1, m_uniform_buffer->GetGLBufferId(), res.buffer_offset, data_size);
glBindBufferRange(GL_UNIFORM_BUFFER, 0, m_uniform_buffer->GetGLBufferId(), res.buffer_offset, data_size);
}
void* OpenGLDevice::MapUniformBuffer(u32 size)
@ -1105,7 +1105,7 @@ void OpenGLDevice::UnmapUniformBuffer(u32 size)
{
const u32 pos = m_uniform_buffer->Unmap(size);
s_stats.buffer_streamed += size;
glBindBufferRange(GL_UNIFORM_BUFFER, 1, m_uniform_buffer->GetGLBufferId(), pos, size);
glBindBufferRange(GL_UNIFORM_BUFFER, 0, m_uniform_buffer->GetGLBufferId(), pos, size);
}
void OpenGLDevice::SetRenderTargets(GPUTexture* const* rts, u32 num_rts, GPUTexture* ds,

View file

@ -72,9 +72,11 @@ public:
void ClearDepth(GPUTexture* t, float d) override;
void InvalidateRenderTarget(GPUTexture* t) override;
std::unique_ptr<GPUShader> CreateShaderFromBinary(GPUShaderStage stage, std::span<const u8> data) override;
std::unique_ptr<GPUShader> CreateShaderFromSource(GPUShaderStage stage, std::string_view source,
const char* entry_point, DynamicHeapArray<u8>* out_binary) override;
std::unique_ptr<GPUShader> CreateShaderFromBinary(GPUShaderStage stage, std::span<const u8> data,
Error* error) override;
std::unique_ptr<GPUShader> CreateShaderFromSource(GPUShaderStage stage, GPUShaderLanguage language,
std::string_view source, const char* entry_point,
DynamicHeapArray<u8>* out_binary, Error* error) override;
std::unique_ptr<GPUPipeline> CreatePipeline(const GPUPipeline::GraphicsConfig& config) override;
void PushDebugGroup(const char* name) override;

View file

@ -158,24 +158,35 @@ bool OpenGLShader::Compile()
return true;
}
std::unique_ptr<GPUShader> OpenGLDevice::CreateShaderFromBinary(GPUShaderStage stage, std::span<const u8> data)
std::unique_ptr<GPUShader> OpenGLDevice::CreateShaderFromBinary(GPUShaderStage stage, std::span<const u8> data,
Error* error)
{
// Not supported.. except spir-v maybe? but no point really...
Error::SetStringView(error, "Not supported.");
return {};
}
std::unique_ptr<GPUShader> OpenGLDevice::CreateShaderFromSource(GPUShaderStage stage, std::string_view source,
const char* entry_point,
DynamicHeapArray<u8>* out_binary)
std::unique_ptr<GPUShader> OpenGLDevice::CreateShaderFromSource(GPUShaderStage stage, GPUShaderLanguage language,
std::string_view source, const char* entry_point,
DynamicHeapArray<u8>* out_binary, Error* error)
{
const GPUShaderLanguage expected_language = IsGLES() ? GPUShaderLanguage::GLSLES : GPUShaderLanguage::GLSL;
if (language != expected_language)
{
return TranspileAndCreateShaderFromSource(
stage, language, source, entry_point, expected_language,
ShaderGen::GetGLSLVersion(IsGLES() ? RenderAPI::OpenGLES : RenderAPI::OpenGL), out_binary, error);
}
if (std::strcmp(entry_point, "main") != 0)
{
ERROR_LOG("Entry point must be 'main', but got '{}' instead.", entry_point);
Error::SetStringFmt(error, "Entry point must be 'main', but got '{}' instead.", entry_point);
return {};
}
return std::unique_ptr<GPUShader>(
new OpenGLShader(stage, GPUShaderCache::GetCacheKey(stage, source, entry_point), std::string(source)));
new OpenGLShader(stage, GPUShaderCache::GetCacheKey(stage, language, source, entry_point), std::string(source)));
}
//////////////////////////////////////////////////////////////////////////
@ -381,11 +392,26 @@ GLuint OpenGLDevice::CompileProgram(const GPUPipeline::GraphicsConfig& plconfig)
if (status == GL_TRUE)
{
ERROR_LOG("Program linked with warnings:\n{}", info_log.c_str());
ERROR_LOG("Program linked with warnings:\n{}", info_log);
}
else
{
ERROR_LOG("Program failed to link:\n{}", info_log.c_str());
{
std::stringstream ss;
ss << "########## VERTEX SHADER ##########\n";
ss << vertex_shader->GetSource();
if (geometry_shader)
{
ss << "\n########## GEOMETRY SHADER ##########\n";
ss << geometry_shader->GetSource();
}
ss << "\n########## FRAGMENT SHADER ##########\n";
ss << fragment_shader->GetSource();
ss << "\n#####################################\n";
DumpBadShader(ss.str(), info_log);
}
ERROR_LOG("Program failed to link:\n{}", info_log);
glDeleteProgram(program_id);
return 0;
}
@ -402,7 +428,7 @@ void OpenGLDevice::PostLinkProgram(const GPUPipeline::GraphicsConfig& plconfig,
{
GLint location = glGetUniformBlockIndex(program_id, "UBOBlock");
if (location >= 0)
glUniformBlockBinding(program_id, location, 1);
glUniformBlockBinding(program_id, location, 0);
glUseProgram(program_id);

View file

@ -22,6 +22,7 @@ public:
ALWAYS_INLINE GLuint GetGLId() const { return m_id.value(); }
ALWAYS_INLINE const GPUShaderCache::CacheIndexKey& GetKey() const { return m_key; }
ALWAYS_INLINE const std::string& GetSource() const { return m_source; }
private:
OpenGLShader(GPUShaderStage stage, const GPUShaderCache::CacheIndexKey& key, std::string source);

View file

@ -1324,7 +1324,7 @@ bool PostProcessing::ReShadeFXShader::CompilePipeline(GPUTexture::Format format,
TinyString version_string = "#version 460 core\n";
#ifdef ENABLE_OPENGL
if (api == RenderAPI::OpenGL || api == RenderAPI::OpenGLES)
version_string = ShaderGen::GetGLSLVersionString(api);
version_string = ShaderGen::GetGLSLVersionString(api, ShaderGen::GetGLSLVersion(api));
#endif
real_code = fmt::format("{}\n#define ENTRY_POINT_{}\n{}\n{}\n{}", version_string, name, defns, precision, code);
@ -1355,10 +1355,11 @@ bool PostProcessing::ReShadeFXShader::CompilePipeline(GPUTexture::Format format,
// FileSystem::WriteStringToFile("D:\\foo.txt", real_code);
std::unique_ptr<GPUShader> sshader =
g_gpu_device->CreateShader(stage, real_code, needs_main_defn ? "main" : name.c_str());
Error error;
std::unique_ptr<GPUShader> sshader = g_gpu_device->CreateShader(
stage, ShaderGen::GetShaderLanguageForAPI(api), real_code, &error, needs_main_defn ? "main" : name.c_str());
if (!sshader)
ERROR_LOG("Failed to compile function '{}'", name);
ERROR_LOG("Failed to compile function '{}': {}", name, error.GetDescription());
return sshader;
};

View file

@ -125,10 +125,10 @@ bool PostProcessing::GLSLShader::CompilePipeline(GPUTexture::Format format, u32
PostProcessingGLSLShaderGen shadergen(g_gpu_device->GetRenderAPI(), g_gpu_device->GetFeatures().dual_source_blend,
g_gpu_device->GetFeatures().framebuffer_fetch);
std::unique_ptr<GPUShader> vs =
g_gpu_device->CreateShader(GPUShaderStage::Vertex, shadergen.GeneratePostProcessingVertexShader(*this));
std::unique_ptr<GPUShader> fs =
g_gpu_device->CreateShader(GPUShaderStage::Fragment, shadergen.GeneratePostProcessingFragmentShader(*this));
std::unique_ptr<GPUShader> vs = g_gpu_device->CreateShader(GPUShaderStage::Vertex, shadergen.GetLanguage(),
shadergen.GeneratePostProcessingVertexShader(*this));
std::unique_ptr<GPUShader> fs = g_gpu_device->CreateShader(GPUShaderStage::Fragment, shadergen.GetLanguage(),
shadergen.GeneratePostProcessingFragmentShader(*this));
if (!vs || !fs)
return false;
@ -328,7 +328,7 @@ void PostProcessing::GLSLShader::LoadOptions()
PostProcessingGLSLShaderGen::PostProcessingGLSLShaderGen(RenderAPI render_api, bool supports_dual_source_blend,
bool supports_framebuffer_fetch)
: ShaderGen(render_api, supports_dual_source_blend, supports_framebuffer_fetch)
: ShaderGen(render_api, GPUShaderLanguage::GLSLVK, supports_dual_source_blend, supports_framebuffer_fetch)
{
}
@ -365,49 +365,13 @@ std::string PostProcessingGLSLShaderGen::GeneratePostProcessingFragmentShader(co
WriteUniformBuffer(ss, shader, false);
DeclareTexture(ss, "samp0", 0);
// Rename main, since we need to set up globals
if (!m_glsl)
{
// TODO: vecn -> floatn
ss << R"(
#define main real_main
static float2 v_tex0;
static float4 v_pos;
static float4 o_col0;
// Wrappers for sampling functions.
#define texture(sampler, coords) sampler.Sample(sampler##_ss, coords)
#define textureOffset(sampler, coords, offset) sampler.Sample(sampler##_ss, coords, offset)
#define gl_FragCoord v_pos
)";
}
else
{
if (m_use_glsl_interface_blocks)
{
if (IsVulkan() || IsMetal())
ss << "layout(location = 0) ";
layout(location = 0) in VertexData {
vec2 v_tex0;
};
ss << "in VertexData {\n";
ss << " float2 v_tex0;\n";
ss << "};\n";
}
else
{
ss << "in float2 v_tex0;\n";
}
layout(location = 0) out float4 o_col0;
if (m_use_glsl_binding_layout)
{
ss << "layout(location = 0) out float4 o_col0;\n";
}
else
{
ss << "out float4 o_col0;\n";
}
}
ss << R"(
float4 Sample() { return texture(samp0, v_tex0); }
float4 SampleLocation(float2 location) { return texture(samp0, location); }
#define SampleOffset(offset) textureOffset(samp0, v_tex0, offset)
@ -439,21 +403,6 @@ float2 GetWindowResolution() { return u_window_size; }
)";
ss << shader.GetCode();
if (!m_glsl)
{
ss << R"(
#undef main
void main(in float2 v_tex0_ : TEXCOORD0, in float4 v_pos_ : SV_Position, out float4 o_col0_ : SV_Target)
{
v_pos = v_pos_;
v_tex0 = v_tex0_;
real_main();
o_col0_ = o_col0;
}
)";
}
return ss.str();
}

View file

@ -17,39 +17,66 @@
Log_SetChannel(ShaderGen);
ShaderGen::ShaderGen(RenderAPI render_api, bool supports_dual_source_blend, bool supports_framebuffer_fetch)
: m_render_api(render_api), m_glsl(render_api != RenderAPI::D3D11 && render_api != RenderAPI::D3D12),
m_spirv(render_api == RenderAPI::Vulkan || render_api == RenderAPI::Metal),
m_supports_dual_source_blend(supports_dual_source_blend), m_supports_framebuffer_fetch(supports_framebuffer_fetch),
m_use_glsl_interface_blocks(false)
ShaderGen::ShaderGen(RenderAPI render_api, GPUShaderLanguage shader_language, bool supports_dual_source_blend,
bool supports_framebuffer_fetch)
: m_render_api(render_api), m_shader_language(shader_language),
m_glsl(shader_language == GPUShaderLanguage::GLSL || shader_language == GPUShaderLanguage::GLSLES ||
shader_language == GPUShaderLanguage::GLSLVK),
m_spirv(shader_language == GPUShaderLanguage::GLSLVK), m_supports_dual_source_blend(supports_dual_source_blend),
m_supports_framebuffer_fetch(supports_framebuffer_fetch), m_use_glsl_interface_blocks(false)
{
#if defined(ENABLE_OPENGL) || defined(ENABLE_VULKAN) || defined(__APPLE__)
if (m_glsl)
{
#ifdef ENABLE_OPENGL
if (m_render_api == RenderAPI::OpenGL || m_render_api == RenderAPI::OpenGLES)
m_glsl_version_string = GetGLSLVersionString(m_render_api);
m_glsl_version_string = GetGLSLVersionString(m_render_api, GetGLSLVersion(render_api));
m_use_glsl_interface_blocks = (IsVulkan() || IsMetal() || GLAD_GL_ES_VERSION_3_2 || GLAD_GL_VERSION_3_2);
m_use_glsl_binding_layout = (IsVulkan() || IsMetal() || UseGLSLBindingLayout());
m_use_glsl_interface_blocks =
(shader_language == GPUShaderLanguage::GLSLVK || GLAD_GL_ES_VERSION_3_2 || GLAD_GL_VERSION_3_2);
m_use_glsl_binding_layout = (shader_language == GPUShaderLanguage::GLSLVK || UseGLSLBindingLayout());
if (m_render_api == RenderAPI::OpenGL)
#ifdef _WIN32
if (m_shader_language == GPUShaderLanguage::GLSL)
{
// SSAA with interface blocks is broken on AMD's OpenGL driver.
const char* gl_vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR));
if (std::strcmp(gl_vendor, "ATI Technologies Inc.") == 0)
m_use_glsl_interface_blocks = false;
}
#endif
#else
m_use_glsl_interface_blocks = true;
m_use_glsl_binding_layout = true;
#endif
}
#endif
}
ShaderGen::~ShaderGen() = default;
GPUShaderLanguage ShaderGen::GetShaderLanguageForAPI(RenderAPI api)
{
switch (api)
{
case RenderAPI::D3D11:
case RenderAPI::D3D12:
return GPUShaderLanguage::HLSL;
case RenderAPI::Vulkan:
case RenderAPI::Metal:
return GPUShaderLanguage::GLSLVK;
case RenderAPI::OpenGL:
return GPUShaderLanguage::GLSL;
case RenderAPI::OpenGLES:
return GPUShaderLanguage::GLSLES;
case RenderAPI::None:
default:
return GPUShaderLanguage::None;
}
}
bool ShaderGen::UseGLSLBindingLayout()
{
#ifdef ENABLE_OPENGL
@ -72,7 +99,7 @@ void ShaderGen::DefineMacro(std::stringstream& ss, const char* name, s32 value)
}
#ifdef ENABLE_OPENGL
TinyString ShaderGen::GetGLSLVersionString(RenderAPI render_api)
u32 ShaderGen::GetGLSLVersion(RenderAPI render_api)
{
const char* glsl_version = reinterpret_cast<const char*>(glGetString(GL_SHADING_LANGUAGE_VERSION));
const bool glsl_es = (render_api == RenderAPI::OpenGLES);
@ -108,6 +135,15 @@ TinyString ShaderGen::GetGLSLVersionString(RenderAPI render_api)
}
}
return (static_cast<u32>(major_version) * 100) + static_cast<u32>(minor_version);
}
TinyString ShaderGen::GetGLSLVersionString(RenderAPI render_api, u32 version)
{
const bool glsl_es = (render_api == RenderAPI::OpenGLES);
const u32 major_version = (version / 100);
const u32 minor_version = (version % 100);
return TinyString::from_format("#version {}{:02d}{}", major_version, minor_version,
(glsl_es && major_version >= 3) ? " es" : "");
}
@ -115,7 +151,7 @@ TinyString ShaderGen::GetGLSLVersionString(RenderAPI render_api)
void ShaderGen::WriteHeader(std::stringstream& ss)
{
if (m_render_api == RenderAPI::OpenGL || m_render_api == RenderAPI::OpenGLES)
if (m_shader_language == GPUShaderLanguage::GLSL || m_shader_language == GPUShaderLanguage::GLSLES)
ss << m_glsl_version_string << "\n\n";
else if (m_spirv)
ss << "#version 450 core\n\n";
@ -131,7 +167,7 @@ void ShaderGen::WriteHeader(std::stringstream& ss)
#ifdef ENABLE_OPENGL
// Extension enabling for OpenGL.
if (m_render_api == RenderAPI::OpenGL || m_render_api == RenderAPI::OpenGLES)
if (m_shader_language == GPUShaderLanguage::GLSL || m_shader_language == GPUShaderLanguage::GLSLES)
{
if (GLAD_GL_EXT_shader_framebuffer_fetch)
ss << "#extension GL_EXT_shader_framebuffer_fetch : require\n";
@ -139,7 +175,7 @@ void ShaderGen::WriteHeader(std::stringstream& ss)
ss << "#extension GL_ARM_shader_framebuffer_fetch : require\n";
}
if (m_render_api == RenderAPI::OpenGLES)
if (m_shader_language == GPUShaderLanguage::GLSLES)
{
// Enable EXT_blend_func_extended for dual-source blend on OpenGL ES.
if (GLAD_GL_EXT_blend_func_extended)
@ -158,7 +194,7 @@ void ShaderGen::WriteHeader(std::stringstream& ss)
ss << "#define DRIVER_POWERVR 1\n";
}
}
else if (m_render_api == RenderAPI::OpenGL)
else if (m_shader_language == GPUShaderLanguage::GLSL)
{
// Need extensions for binding layout if GL<4.3.
if (m_use_glsl_binding_layout && !GLAD_GL_VERSION_4_3)
@ -185,7 +221,7 @@ void ShaderGen::WriteHeader(std::stringstream& ss)
DefineMacro(ss, "API_METAL", m_render_api == RenderAPI::Metal);
#ifdef ENABLE_OPENGL
if (m_render_api == RenderAPI::OpenGLES)
if (m_shader_language == GPUShaderLanguage::GLSLES)
{
ss << "precision highp float;\n";
ss << "precision highp int;\n";
@ -299,9 +335,9 @@ void ShaderGen::WriteHeader(std::stringstream& ss)
void ShaderGen::WriteUniformBufferDeclaration(std::stringstream& ss, bool push_constant_on_vulkan)
{
if (IsVulkan())
if (m_shader_language == GPUShaderLanguage::GLSLVK)
{
if (push_constant_on_vulkan)
if (m_render_api == RenderAPI::Vulkan && push_constant_on_vulkan)
{
ss << "layout(push_constant) uniform PushConstants\n";
}
@ -311,15 +347,10 @@ void ShaderGen::WriteUniformBufferDeclaration(std::stringstream& ss, bool push_c
m_has_uniform_buffer = true;
}
}
else if (IsMetal())
{
ss << "layout(std140, set = 0, binding = 0) uniform UBOBlock\n";
m_has_uniform_buffer = true;
}
else if (m_glsl)
{
if (m_use_glsl_binding_layout)
ss << "layout(std140, binding = 1) uniform UBOBlock\n";
ss << "layout(std140, binding = 0) uniform UBOBlock\n";
else
ss << "layout(std140) uniform UBOBlock\n";

View file

@ -13,15 +13,20 @@
class ShaderGen
{
public:
ShaderGen(RenderAPI render_api, bool supports_dual_source_blend, bool supports_framebuffer_fetch);
ShaderGen(RenderAPI render_api, GPUShaderLanguage language, bool supports_dual_source_blend,
bool supports_framebuffer_fetch);
~ShaderGen();
static GPUShaderLanguage GetShaderLanguageForAPI(RenderAPI api);
static bool UseGLSLBindingLayout();
#ifdef ENABLE_OPENGL
static TinyString GetGLSLVersionString(RenderAPI render_api);
static u32 GetGLSLVersion(RenderAPI render_api);
static TinyString GetGLSLVersionString(RenderAPI render_api, u32 version);
#endif
ALWAYS_INLINE GPUShaderLanguage GetLanguage() const { return m_shader_language; }
std::string GenerateScreenQuadVertexShader(float z = 0.0f);
std::string GenerateUVQuadVertexShader();
std::string GenerateFillFragmentShader();
@ -58,6 +63,7 @@ protected:
bool noperspective_color = false, bool feedback_loop = false);
RenderAPI m_render_api;
GPUShaderLanguage m_shader_language;
bool m_glsl;
bool m_spirv;
bool m_supports_dual_source_blend;

View file

@ -39,6 +39,7 @@
<DepsDLLs Include="$(DepsBinDir)libwebp.dll" />
<DepsDLLs Include="$(DepsBinDir)SDL2.dll" />
<DepsDLLs Include="$(DepsBinDir)shaderc_shared.dll" />
<DepsDLLs Include="$(DepsBinDir)spirv-cross-c-shared.dll" />
<DepsDLLs Include="$(DepsBinDir)zlib1.dll" />
<DepsDLLs Include="$(DepsBinDir)zstd.dll" />
</ItemGroup>

View file

@ -98,9 +98,10 @@ public:
void ClearDepth(GPUTexture* t, float d) override;
void InvalidateRenderTarget(GPUTexture* t) override;
std::unique_ptr<GPUShader> CreateShaderFromBinary(GPUShaderStage stage, std::span<const u8> data) override;
std::unique_ptr<GPUShader> CreateShaderFromSource(GPUShaderStage stage, std::string_view source,
const char* entry_point, DynamicHeapArray<u8>* out_binary) override;
std::unique_ptr<GPUShader> CreateShaderFromBinary(GPUShaderStage stage, std::span<const u8> data, Error* error) override;
std::unique_ptr<GPUShader> CreateShaderFromSource(GPUShaderStage stage, GPUShaderLanguage language,
std::string_view source, const char* entry_point,
DynamicHeapArray<u8>* out_binary, Error* error) override;
std::unique_ptr<GPUPipeline> CreatePipeline(const GPUPipeline::GraphicsConfig& config) override;
void PushDebugGroup(const char* name) override;

View file

@ -6,6 +6,7 @@
#include "vulkan_device.h"
#include "common/assert.h"
#include "common/error.h"
#include "common/log.h"
Log_SetChannel(VulkanDevice);
@ -24,7 +25,8 @@ void VulkanShader::SetDebugName(std::string_view name)
Vulkan::SetObjectName(VulkanDevice::GetInstance().GetVulkanDevice(), m_module, name);
}
std::unique_ptr<GPUShader> VulkanDevice::CreateShaderFromBinary(GPUShaderStage stage, std::span<const u8> data)
std::unique_ptr<GPUShader> VulkanDevice::CreateShaderFromBinary(GPUShaderStage stage, std::span<const u8> data,
Error* error)
{
VkShaderModule mod;
@ -34,27 +36,37 @@ std::unique_ptr<GPUShader> VulkanDevice::CreateShaderFromBinary(GPUShaderStage s
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkCreateShaderModule() failed: ");
Error::SetStringFmt(error, "vkCreateShaderModule() failed: {}", Vulkan::VkResultToString(res));
return {};
}
return std::unique_ptr<GPUShader>(new VulkanShader(stage, mod));
}
std::unique_ptr<GPUShader> VulkanDevice::CreateShaderFromSource(GPUShaderStage stage, std::string_view source,
const char* entry_point,
DynamicHeapArray<u8>* out_binary)
std::unique_ptr<GPUShader> VulkanDevice::CreateShaderFromSource(GPUShaderStage stage, GPUShaderLanguage language,
std::string_view source, const char* entry_point,
DynamicHeapArray<u8>* out_binary, Error* error)
{
if (language == GPUShaderLanguage::SPV)
{
if (out_binary)
out_binary->assign(reinterpret_cast<const u8*>(source.data()), source.length());
return CreateShaderFromBinary(
stage, std::span<const u8>(reinterpret_cast<const u8*>(source.data()), source.length()), error);
}
DynamicHeapArray<u8> local_binary;
DynamicHeapArray<u8>* dest_binary = out_binary ? out_binary : &local_binary;
if (!CompileGLSLShaderToVulkanSpv(stage, source, entry_point, m_optional_extensions.vk_khr_shader_non_semantic_info,
dest_binary))
if (!CompileGLSLShaderToVulkanSpv(stage, language, source, entry_point,
m_optional_extensions.vk_khr_shader_non_semantic_info, dest_binary, error))
{
return {};
}
AssertMsg((dest_binary->size() % 4) == 0, "Compile result should be 4 byte aligned.");
return CreateShaderFromBinary(stage, dest_binary->cspan());
return CreateShaderFromBinary(stage, dest_binary->cspan(), error);
}
//////////////////////////////////////////////////////////////////////////