Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 144 additions & 18 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,58 @@ find_program(SLANGC_BIN slangc.exe
${CMAKE_CURRENT_SOURCE_DIR}/bin/
REQUIRED)
message("SLANGC_BIN: " ${SLANGC_BIN})
set(SLANGC_FLAGS_LIST -O3 -g0 -entry main -Wno-30056 -Wno-15205)
list(JOIN SLANGC_FLAGS_LIST " " SLANGC_FLAGS_STR)
file(TO_CMAKE_PATH "${SLANGC_BIN}" SLANGC_BIN_CMAKE_PATH)

if(DEFINED ENV{VULKAN_SDK})
set(VULKAN_SDK_PATH "$ENV{VULKAN_SDK}")
endif()

set(SPIRV_VAL_FLAGS_LIST --target-env vulkan1.3)
list(JOIN SPIRV_VAL_FLAGS_LIST " " SPIRV_VAL_FLAGS_STR)

set(SPIRV_CROSS_FLAGS_LIST --version 450 --vulkan-semantics --no-es)
list(JOIN SPIRV_CROSS_FLAGS_LIST " " SPIRV_CROSS_FLAGS_STR)

find_program(SPIRV_VAL_BIN spirv-val.exe
HINTS
${VULKAN_SDK_PATH}/Bin
${CMAKE_CURRENT_SOURCE_DIR}/bin/)
message("SPIRV_VAL_BIN: " ${SPIRV_VAL_BIN})

find_program(SPIRV_DIS_BIN spirv-dis.exe
HINTS
${VULKAN_SDK_PATH}/Bin
${CMAKE_CURRENT_SOURCE_DIR}/bin/)
message("SPIRV_DIS_BIN: " ${SPIRV_DIS_BIN})

find_program(SPIRV_CROSS_BIN spirv-cross.exe
HINTS
${VULKAN_SDK_PATH}/Bin
${CMAKE_CURRENT_SOURCE_DIR}/bin/)
message("SPIRV_CROSS_BIN: " ${SPIRV_CROSS_BIN})

file(TO_CMAKE_PATH "${SPIRV_CROSS_BIN}" SPIRV_CROSS_BIN_CMAKE_PATH)

set(SPIRV_VAL_SCRIPT ${CMAKE_CURRENT_BINARY_DIR}/spirv_val.cmake)
set(SPIRV_VAL_SCRIPT_CONTENT [==[
if(NOT DEFINED VALIDATOR OR "${VALIDATOR}" STREQUAL "")
message(FATAL_ERROR "spirv-val validator path missing")
endif()
if(NOT DEFINED FILE OR "${FILE}" STREQUAL "")
message(FATAL_ERROR "spirv-val file missing")
endif()
set(VAL_FLAGS "${FLAGS}")
if(NOT VAL_FLAGS STREQUAL "")
separate_arguments(VAL_FLAGS NATIVE_COMMAND "${VAL_FLAGS}")
endif()
execute_process(COMMAND "${VALIDATOR}" ${VAL_FLAGS} "${FILE}" RESULT_VARIABLE RESULT OUTPUT_VARIABLE OUT ERROR_VARIABLE ERR)
if(NOT RESULT EQUAL 0)
message(STATUS "spirv-val: ${FILE}\n${OUT}\n${ERR}")
endif()
]==])
file(WRITE ${SPIRV_VAL_SCRIPT} "${SPIRV_VAL_SCRIPT_CONTENT}")

project(renodx VERSION 0.1.0 LANGUAGES CXX)

Expand Down Expand Up @@ -150,6 +202,7 @@ endfunction()
function(build_shader_target ADDON ADDON_PATH)
set(EMBED_FOLDER ${CMAKE_CURRENT_BINARY_DIR}/${ADDON}.include/embed)
unset(SHADER_BINARIES)
unset(SHADER_AUX_OUTPUTS)

file(GLOB_RECURSE SHADER_HLSL_SOURCES CONFIGURE_DEPENDS ${ADDON_PATH}/*.hlsl)
foreach(FILE ${SHADER_HLSL_SOURCES})
Expand Down Expand Up @@ -291,11 +344,7 @@ function(build_shader_target ADDON ADDON_PATH)

set(SHADER_COMPILER ${SLANGC_BIN})
unset(SHADER_FLAGS)
set(SHADER_FLAGS ${SHADER_FLAGS} -O3)
set(SHADER_FLAGS ${SHADER_FLAGS} -g0)
set(SHADER_FLAGS ${SHADER_FLAGS} -entry main)
set(SHADER_FLAGS ${SHADER_FLAGS} -Wno-30056)
set(SHADER_FLAGS ${SHADER_FLAGS} -Wno-15205)
set(SHADER_FLAGS ${SLANGC_FLAGS_LIST})

if(SHADER_TARGET AND (SHADER_HASH OR SHADER_NAME))
if(SHADER_TARGET STREQUAL "frag" OR SHADER_TARGET STREQUAL "vert" OR SHADER_TARGET STREQUAL "comp")
Expand All @@ -316,19 +365,53 @@ function(build_shader_target ADDON ADDON_PATH)
endif()

if(SHADER_HASH)
add_custom_command(
OUTPUT ${EMBED_FOLDER}/${SHADER_HASH}.spv
COMMAND ${SHADER_COMPILER} ${FILE} -stage ${SHADER_STAGE} ${SHADER_FLAGS} -target spirv -o ${EMBED_FOLDER}/${SHADER_HASH}.spv
DEPENDS ${HLSL_DEPENDENCIES}
)
if(SPIRV_VAL_BIN)
add_custom_command(
OUTPUT ${EMBED_FOLDER}/${SHADER_HASH}.spv
COMMAND ${SHADER_COMPILER} ${FILE} -stage ${SHADER_STAGE} ${SHADER_FLAGS} -target spirv -o ${EMBED_FOLDER}/${SHADER_HASH}.spv
COMMAND ${CMAKE_COMMAND} -DVALIDATOR="${SPIRV_VAL_BIN}" -DFILE="${EMBED_FOLDER}/${SHADER_HASH}.spv" -DFLAGS="${SPIRV_VAL_FLAGS_STR}" -P ${SPIRV_VAL_SCRIPT}
DEPENDS ${HLSL_DEPENDENCIES}
)
else()
add_custom_command(
OUTPUT ${EMBED_FOLDER}/${SHADER_HASH}.spv
COMMAND ${SHADER_COMPILER} ${FILE} -stage ${SHADER_STAGE} ${SHADER_FLAGS} -target spirv -o ${EMBED_FOLDER}/${SHADER_HASH}.spv
DEPENDS ${HLSL_DEPENDENCIES}
)
endif()
list(APPEND SHADER_BINARIES ${EMBED_FOLDER}/${SHADER_HASH}.spv)
if(SPIRV_DIS_BIN)
add_custom_command(
OUTPUT ${EMBED_FOLDER}/${SHADER_HASH}.spvasm
COMMAND "${SPIRV_DIS_BIN}" "${EMBED_FOLDER}/${SHADER_HASH}.spv" -o "${EMBED_FOLDER}/${SHADER_HASH}.spvasm"
DEPENDS ${EMBED_FOLDER}/${SHADER_HASH}.spv
)
list(APPEND SHADER_AUX_OUTPUTS ${EMBED_FOLDER}/${SHADER_HASH}.spvasm)
endif()
else()
add_custom_command(
OUTPUT ${EMBED_FOLDER}/${SHADER_NAME}.spv
COMMAND ${SHADER_COMPILER} ${FILE} -stage ${SHADER_STAGE} ${SHADER_FLAGS} -target spirv -o ${EMBED_FOLDER}/${SHADER_NAME}.spv
DEPENDS ${HLSL_DEPENDENCIES}
)
if(SPIRV_VAL_BIN)
add_custom_command(
OUTPUT ${EMBED_FOLDER}/${SHADER_NAME}.spv
COMMAND ${SHADER_COMPILER} ${FILE} -stage ${SHADER_STAGE} ${SHADER_FLAGS} -target spirv -o ${EMBED_FOLDER}/${SHADER_NAME}.spv
COMMAND ${CMAKE_COMMAND} -DVALIDATOR="${SPIRV_VAL_BIN}" -DFILE="${EMBED_FOLDER}/${SHADER_NAME}.spv" -DFLAGS="${SPIRV_VAL_FLAGS_STR}" -P ${SPIRV_VAL_SCRIPT}
DEPENDS ${HLSL_DEPENDENCIES}
)
else()
add_custom_command(
OUTPUT ${EMBED_FOLDER}/${SHADER_NAME}.spv
COMMAND ${SHADER_COMPILER} ${FILE} -stage ${SHADER_STAGE} ${SHADER_FLAGS} -target spirv -o ${EMBED_FOLDER}/${SHADER_NAME}.spv
DEPENDS ${HLSL_DEPENDENCIES}
)
endif()
list(APPEND SHADER_BINARIES ${EMBED_FOLDER}/${SHADER_NAME}.spv)
if(SPIRV_DIS_BIN)
add_custom_command(
OUTPUT ${EMBED_FOLDER}/${SHADER_NAME}.spvasm
COMMAND "${SPIRV_DIS_BIN}" "${EMBED_FOLDER}/${SHADER_NAME}.spv" -o "${EMBED_FOLDER}/${SHADER_NAME}.spvasm"
DEPENDS ${EMBED_FOLDER}/${SHADER_NAME}.spv
)
list(APPEND SHADER_AUX_OUTPUTS ${EMBED_FOLDER}/${SHADER_NAME}.spvasm)
endif()
endif()
else()
string(SUBSTRING ${SHADER_TARGET} 3 1 SHADER_TARGET_MAJOR)
Expand Down Expand Up @@ -441,11 +524,53 @@ function(build_shader_target ADDON ADDON_PATH)
endif()

if(SHADER_HASH)
configure_file(${FILE} ${EMBED_FOLDER}/${SHADER_HASH}.spv COPYONLY)
if(SPIRV_VAL_BIN)
add_custom_command(
OUTPUT ${EMBED_FOLDER}/${SHADER_HASH}.spv
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${FILE} ${EMBED_FOLDER}/${SHADER_HASH}.spv
COMMAND ${CMAKE_COMMAND} -DVALIDATOR="${SPIRV_VAL_BIN}" -DFILE="${EMBED_FOLDER}/${SHADER_HASH}.spv" -DFLAGS="${SPIRV_VAL_FLAGS_STR}" -P ${SPIRV_VAL_SCRIPT}
DEPENDS ${FILE}
)
else()
add_custom_command(
OUTPUT ${EMBED_FOLDER}/${SHADER_HASH}.spv
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${FILE} ${EMBED_FOLDER}/${SHADER_HASH}.spv
DEPENDS ${FILE}
)
endif()
list(APPEND SHADER_BINARIES ${EMBED_FOLDER}/${SHADER_HASH}.spv)
if(SPIRV_DIS_BIN)
add_custom_command(
OUTPUT ${EMBED_FOLDER}/${SHADER_HASH}.spvasm
COMMAND "${SPIRV_DIS_BIN}" "${EMBED_FOLDER}/${SHADER_HASH}.spv" -o "${EMBED_FOLDER}/${SHADER_HASH}.spvasm"
DEPENDS ${FILE}
)
Comment on lines +542 to +547
Comment on lines +542 to +547
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This spirv-dis custom command depends on the original ${FILE} rather than the generated/copied ${EMBED_FOLDER}/${SHADER_HASH}.spv. That can allow CMake to schedule spirv-dis before the .spv exists, causing build failures. Make the .spvasm rule depend on the .spv output (and/or add it as a byproduct of the .spv rule).

Copilot uses AI. Check for mistakes.
list(APPEND SHADER_AUX_OUTPUTS ${EMBED_FOLDER}/${SHADER_HASH}.spvasm)
endif()
elseif(SHADER_NAME)
configure_file(${FILE} ${EMBED_FOLDER}/${SHADER_NAME}.spv COPYONLY)
if(SPIRV_VAL_BIN)
add_custom_command(
OUTPUT ${EMBED_FOLDER}/${SHADER_NAME}.spv
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${FILE} ${EMBED_FOLDER}/${SHADER_NAME}.spv
COMMAND ${CMAKE_COMMAND} -DVALIDATOR="${SPIRV_VAL_BIN}" -DFILE="${EMBED_FOLDER}/${SHADER_NAME}.spv" -DFLAGS="${SPIRV_VAL_FLAGS_STR}" -P ${SPIRV_VAL_SCRIPT}
DEPENDS ${FILE}
)
else()
add_custom_command(
OUTPUT ${EMBED_FOLDER}/${SHADER_NAME}.spv
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${FILE} ${EMBED_FOLDER}/${SHADER_NAME}.spv
DEPENDS ${FILE}
)
endif()
list(APPEND SHADER_BINARIES ${EMBED_FOLDER}/${SHADER_NAME}.spv)
if(SPIRV_DIS_BIN)
add_custom_command(
OUTPUT ${EMBED_FOLDER}/${SHADER_NAME}.spvasm
COMMAND "${SPIRV_DIS_BIN}" "${EMBED_FOLDER}/${SHADER_NAME}.spv" -o "${EMBED_FOLDER}/${SHADER_NAME}.spvasm"
DEPENDS ${FILE}
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue as above for the non-hash path: the spirv-dis .spvasm rule depends on ${FILE} instead of ${EMBED_FOLDER}/${SHADER_NAME}.spv, so the disassembly step may run before the .spv output is generated. Update the dependency to the .spv output to guarantee correct build ordering.

Suggested change
DEPENDS ${FILE}
DEPENDS ${EMBED_FOLDER}/${SHADER_NAME}.spv

Copilot uses AI. Check for mistakes.
)
list(APPEND SHADER_AUX_OUTPUTS ${EMBED_FOLDER}/${SHADER_NAME}.spvasm)
endif()
endif()
endforeach()

Expand Down Expand Up @@ -530,7 +655,7 @@ function(build_shader_target ADDON ADDON_PATH)
endif()

list(JOIN SHADER_HEADER_FILES " " SHADER_HEADER_FILE_ITEMS)
add_custom_target(${ADDON}-shaders DEPENDS ${SHADER_HEADER_FILES})
add_custom_target(${ADDON}-shaders DEPENDS ${SHADER_HEADER_FILES} ${SHADER_AUX_OUTPUTS})

if(SHADERS_H_OUTPUT)
file(GENERATE OUTPUT ${EMBED_FOLDER}/shaders.h CONTENT "${SHADERS_H_OUTPUT}")
Expand Down Expand Up @@ -593,6 +718,7 @@ foreach(FILE ${ADDON_FILES})
generate_resource_rc(${ADDON})
add_dependencies(${ADDON} ${ADDON}-shaders)
add_dependencies(${ADDON} detours)
target_compile_definitions(${ADDON} PRIVATE RENODX_SLANGC_BIN_PATH="${SLANGC_BIN_CMAKE_PATH}" RENODX_SLANGC_FLAGS="${SLANGC_FLAGS_STR}" RENODX_SPIRV_CROSS_BIN_PATH="${SPIRV_CROSS_BIN_CMAKE_PATH}" RENODX_SPIRV_CROSS_FLAGS="${SPIRV_CROSS_FLAGS_STR}")
target_link_libraries(${ADDON} PRIVATE detours)
target_include_directories(${ADDON} SYSTEM PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/external/Detours/include)
target_include_directories(${ADDON} SYSTEM PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/external/frozen/include)
Expand Down
21 changes: 17 additions & 4 deletions src/addons/devkit/addon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
#include "../../utils/pipeline_layout.hpp"
#include "../../utils/shader.hpp"
#include "../../utils/shader_compiler_directx.hpp"
#include "../../utils/shader_compiler_slang.hpp"
#include "../../utils/shader_compiler_watcher.hpp"
#include "../../utils/shader_decompiler_dxc.hpp"
#include "../../utils/shader_dump.hpp"
Expand Down Expand Up @@ -322,6 +323,8 @@ bool ComputeDisassemblyForShaderDetails(reshade::api::device* device, DeviceData
shader_details->disassembly = std::string(
shader_details->shader_data.data(),
shader_details->shader_data.data() + shader_details->shader_data.size());
} else if (device->get_api() == reshade::api::device_api::vulkan) {
shader_details->disassembly = renodx::utils::shader::compiler::slang::DisassembleSpirv(shader_details->shader_data, shader_details->shader_hash);
} else {
throw std::exception("Unsupported device API.");
}
Expand Down Expand Up @@ -1427,6 +1430,7 @@ void ActivateShader(reshade::api::device* device, uint32_t shader_hash, std::spa
}

void LoadDiskShaders(reshade::api::device* device, DeviceData* data, bool activate = true) {
renodx::utils::shader::compiler::watcher::SetDeviceApi(device->get_api());
if (setting_live_reload) {
if (!renodx::utils::shader::compiler::watcher::HasChanged()) return;
} else {
Expand Down Expand Up @@ -2535,11 +2539,13 @@ void RenderCapturePane(reshade::api::device* device, DeviceData* data) {
}

// Creates a selectable with the given label that jumps to the specified snapshot index
inline void CreateDrawIndexLink(const std::string& label, int draw_index) {
inline bool CreateDrawIndexLink(const std::string& label, int draw_index) {
if (ImGui::TextLink(label.c_str())) {
setting_nav_item = 0; // Snapshot is the first nav item
pending_draw_index_focus = draw_index;
return true;
}
return false;
}

enum ShaderPaneColumns : uint8_t {
Expand Down Expand Up @@ -2714,8 +2720,9 @@ void RenderShadersPane(reshade::api::device* device, DeviceData* data) {
if (ImGui::TableSetColumnIndex(SHADER_PANE_COLUMN_SNAPSHOT)) { // Snapshot
ImGui::PushID(cell_index_id++);
if (snapshot_index != -1) {
MakeSelectionCurrent(selection);
CreateDrawIndexLink(std::format("{:03}", snapshot_index), snapshot_index);
if (CreateDrawIndexLink(std::format("{:03}", snapshot_index), snapshot_index)) {
MakeSelectionCurrent(selection);
}
}
ImGui::PopID();
}
Expand Down Expand Up @@ -3074,7 +3081,9 @@ void RenderShaderViewLive(reshade::api::device* device, DeviceData* data, Shader
if (shader_details->disk_shader.has_value()) {
if (!shader_details->disk_shader->IsCompilationOK()) {
live_string = shader_details->disk_shader->GetCompilationException().what();
} else if (shader_details->disk_shader->is_hlsl || shader_details->disk_shader->is_glsl) {
} else if (shader_details->disk_shader->is_hlsl
|| shader_details->disk_shader->is_glsl
|| shader_details->disk_shader->is_slang) {
try {
live_string = renodx::utils::path::ReadTextFile(shader_details->disk_shader->file_path);
} catch (std::exception& e) {
Expand Down Expand Up @@ -3133,6 +3142,10 @@ void RenderShaderViewDecompilation(reshade::api::device* device, DeviceData* dat
{
.flatten = true,
});
} else if (device->get_api() == reshade::api::device_api::vulkan) {
shader_details->decompilation = renodx::utils::shader::compiler::slang::DecompileSpirvToGlsl(
shader_details->shader_data,
shader_details->shader_hash);
} else if (device->get_api() == reshade::api::device_api::opengl) {
shader_details->disassembly = std::string(
shader_details->shader_data.data(),
Expand Down
35 changes: 23 additions & 12 deletions src/utils/shader_compiler_directx.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ static HMODULE fxc_compiler_library = nullptr;
static HMODULE dxc_compiler_library = nullptr;
static std::shared_mutex mutex_fxc_compiler;
static std::shared_mutex mutex_dxc_compiler;
static std::once_flag dxc_compiler_library_once;
constexpr bool USE_FXC_MUTEX = true;
constexpr bool USE_DXC_MUTEX = false;

class FxcD3DInclude : public ID3DInclude {
public:
Expand Down Expand Up @@ -119,9 +122,9 @@ class FxcD3DInclude : public ID3DInclude {
};

inline HMODULE LoadDXCompiler(const std::wstring& path = L"dxcompiler.dll") {
if (dxc_compiler_library == nullptr) {
std::call_once(dxc_compiler_library_once, [&]() {
dxc_compiler_library = LoadLibraryW(path.c_str());
}
});
return dxc_compiler_library;
}

Expand All @@ -132,9 +135,7 @@ inline HMODULE LoadDXCompiler(const std::string& path) {

inline HRESULT CreateLibrary(IDxcLibrary** dxc_library) {
// HMODULE dxil_loader = LoadLibraryW(L"dxil.dll");
if (dxc_compiler_library == nullptr) {
dxc_compiler_library = LoadDXCompiler();
}
dxc_compiler_library = LoadDXCompiler();
if (dxc_compiler_library == nullptr) {
return -1;
}
Expand All @@ -146,9 +147,7 @@ inline HRESULT CreateLibrary(IDxcLibrary** dxc_library) {

inline HRESULT CreateCompiler(IDxcCompiler** dxc_compiler) {
// HMODULE dxil_loader = LoadLibraryW(L"dxil.dll");
if (dxc_compiler_library == nullptr) {
dxc_compiler_library = LoadLibraryW(L"dxcompiler.dll");
}
dxc_compiler_library = LoadDXCompiler();
if (dxc_compiler_library == nullptr) {
return -1;
}
Expand Down Expand Up @@ -378,7 +377,10 @@ inline std::vector<uint8_t> CompileShaderFromFileFXC(
// Create a new Custom D3DInclude that supports relative imports
auto custom_include = FxcD3DInclude(file_path);

std::unique_lock lock(mutex_fxc_compiler);
std::unique_lock lock(mutex_fxc_compiler, std::defer_lock);
if constexpr (USE_FXC_MUTEX) {
lock.lock();
}
ID3DBlobPtr error_blob;
if (FAILED(d3d_compilefromfile(
file_path,
Expand Down Expand Up @@ -411,7 +413,10 @@ inline std::vector<uint8_t> CompileShaderFromFileDXC(

ID3DBlobPtr out_blob;
ID3DBlobPtr error_blob;
std::unique_lock lock(mutex_dxc_compiler);
std::unique_lock lock(mutex_dxc_compiler, std::defer_lock);
if constexpr (USE_DXC_MUTEX) {
lock.lock();
}
if (FAILED(internal::BridgeD3DCompileFromFile(
file_path,
defines,
Expand Down Expand Up @@ -444,7 +449,10 @@ inline std::string DisassembleShaderFXC(std::span<uint8_t> blob) {
if (d3d_disassemble == nullptr) throw std::exception("Could not to load D3DDisassemble in D3DCompiler_47.dll");

// Function may not be thread-safe
std::unique_lock lock(mutex_fxc_compiler);
std::unique_lock lock(mutex_fxc_compiler, std::defer_lock);
if constexpr (USE_FXC_MUTEX) {
lock.lock();
}
ID3DBlobPtr out_blob;
if (FAILED(d3d_disassemble(
blob.data(),
Expand All @@ -467,7 +475,10 @@ inline std::string DisassembleShaderDXC(std::span<uint8_t> blob) {
IDxcBlobEncodingPtr disassembly_text;
ID3DBlobPtr disassembly;

std::unique_lock lock(mutex_dxc_compiler);
std::unique_lock lock(mutex_dxc_compiler, std::defer_lock);
if constexpr (USE_DXC_MUTEX) {
lock.lock();
}
if (FAILED(internal::CreateLibrary(&library))) throw std::exception("Could not create library.");
if (FAILED(library->CreateBlobWithEncodingFromPinned(blob.data(), blob.size(), CP_ACP, &source))) throw std::exception("Could not prepare blob.");
if (FAILED(internal::CreateCompiler(&compiler))) throw std::exception("Could not create compiler object.");
Expand Down
Loading
Loading