feat(agc): add standalone shader test for libSceAgcDriver
This commit is contained in:
parent
9c78d10044
commit
4b2fe93080
|
|
@ -12,6 +12,8 @@ Execution reaches `_start`, stack unwinding and exception handling tables are bu
|
|||
Audio output and video output initialization pass.
|
||||
Reaches the `main` function with render loop (deep call chain, ~64KB stack frame function) - init and shader loading from disk fully complete.
|
||||
|
||||
A standalone [SPIR-V triangle test](core/libs/prx/libSceAgcDriver/tests/Visual.cpp) passes GPU readback checks and presents a frame through the [libSceAgcDriver](core/libs/prx/libSceAgcDriver). The first application draw passes driver state validation and reaches shader recompilation. Application shader execution remains unverified without [recompiler](core/shader/recompiler/Recompiler.cpp).
|
||||
|
||||
Now: `ShaderRecompiler::Recompile: InstructionTranslator::Translate not implementedgi`.
|
||||
|
||||
## Disclaimer
|
||||
|
|
|
|||
|
|
@ -67,3 +67,61 @@ target_include_directories(agc_driver_graphics_tests PRIVATE ${LIBS_INCLUDE_DIR}
|
|||
target_link_libraries(agc_driver_graphics_tests PRIVATE libc)
|
||||
set_target_properties(agc_driver_graphics_tests PROPERTIES CXX_EXTENSIONS OFF)
|
||||
configure_windows_unwind(agc_driver_graphics_tests)
|
||||
|
||||
option(AGC_BUILD_VISUAL_TEST "Enable the standalone AGC SPIR-V visual test" OFF)
|
||||
if(AGC_BUILD_VISUAL_TEST)
|
||||
function(add_agc_test_glslang)
|
||||
set(BUILD_SHARED_LIBS OFF)
|
||||
set(BUILD_EXTERNAL OFF)
|
||||
set(GLSLANG_TESTS OFF)
|
||||
set(GLSLANG_ENABLE_INSTALL OFF)
|
||||
set(ENABLE_GLSLANG_BINARIES ON)
|
||||
set(ENABLE_SPIRV ON)
|
||||
set(ENABLE_SPVREMAPPER OFF)
|
||||
set(ENABLE_OPT OFF)
|
||||
add_subdirectory("${CMAKE_SOURCE_DIR}/3rdparty/glslang" "${CMAKE_BINARY_DIR}/3rdparty/glslang" EXCLUDE_FROM_ALL)
|
||||
endfunction()
|
||||
add_agc_test_glslang()
|
||||
set(visualShaders)
|
||||
foreach(stage IN ITEMS vert frag)
|
||||
set(shaderOutput "${CMAKE_CURRENT_BINARY_DIR}/Triangle.${stage}.spv")
|
||||
add_custom_command(OUTPUT "${shaderOutput}"
|
||||
COMMAND "$<TARGET_FILE:glslang-standalone>" -V --target-env vulkan1.1 -o "${shaderOutput}" "${CMAKE_CURRENT_SOURCE_DIR}/tests/shaders/Triangle.${stage}"
|
||||
DEPENDS glslang-standalone "${CMAKE_CURRENT_SOURCE_DIR}/tests/shaders/Triangle.${stage}"
|
||||
VERBATIM)
|
||||
list(APPEND visualShaders "${shaderOutput}")
|
||||
endforeach()
|
||||
add_executable(agc_driver_visual_test EXCLUDE_FROM_ALL
|
||||
tests/Visual.cpp
|
||||
Execution/src/VulkanDevice.cpp
|
||||
Execution/src/GuestMemory.cpp
|
||||
Graphics/src/Draw.cpp
|
||||
Graphics/src/Pipeline.cpp
|
||||
Graphics/src/Resources.cpp
|
||||
Graphics/src/ShaderResources.cpp
|
||||
Graphics/src/ShaderValidation.cpp
|
||||
${visualShaders})
|
||||
target_include_directories(agc_driver_visual_test PRIVATE ${LIBS_INCLUDE_DIR} ${CMAKE_SOURCE_DIR}/core/shader/recompiler ${CMAKE_SOURCE_DIR}/3rdparty/Vulkan-Headers/include ${CMAKE_SOURCE_DIR}/3rdparty/SPIRV-Headers/include)
|
||||
target_link_libraries(agc_driver_visual_test PRIVATE SDL2-static libc)
|
||||
set_target_properties(agc_driver_visual_test PROPERTIES CXX_EXTENSIONS OFF)
|
||||
configure_windows_unwind(agc_driver_visual_test)
|
||||
if(WIN32)
|
||||
set(visualRuntimeFiles "$<TARGET_FILE:libc>")
|
||||
if(MINGW)
|
||||
get_filename_component(visualCompilerDirectory "${CMAKE_CXX_COMPILER}" DIRECTORY)
|
||||
foreach(runtimeName IN ITEMS libgcc_s_seh-1.dll libstdc++-6.dll libwinpthread-1.dll)
|
||||
set(runtimePath "${visualCompilerDirectory}/${runtimeName}")
|
||||
if(NOT EXISTS "${runtimePath}")
|
||||
message(FATAL_ERROR "Cannot locate MinGW runtime: ${runtimeName}")
|
||||
endif()
|
||||
list(APPEND visualRuntimeFiles "${runtimePath}")
|
||||
endforeach()
|
||||
endif()
|
||||
add_custom_target(agc_driver_visual_runtime
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_if_different ${visualRuntimeFiles} "$<TARGET_FILE_DIR:agc_driver_visual_test>"
|
||||
DEPENDS libc
|
||||
COMMAND_EXPAND_LISTS
|
||||
VERBATIM)
|
||||
add_dependencies(agc_driver_visual_test agc_driver_visual_runtime)
|
||||
endif()
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,106 @@
|
|||
#define SDL_MAIN_HANDLED
|
||||
#include "prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp"
|
||||
#include <SDL.h>
|
||||
#include <SDL_vulkan.h>
|
||||
#include <array>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
|
||||
namespace {
|
||||
|
||||
using AgcDriver::Graphics::Require;
|
||||
constexpr std::uint32_t Width = 640;
|
||||
constexpr std::uint32_t Height = 480;
|
||||
alignas(256) std::array<std::byte, Width * Height * 4> Pixels{};
|
||||
|
||||
ShaderRecompiler::RecompileResult LoadShader(const std::filesystem::path& path) {
|
||||
std::ifstream file(path, std::ios::binary | std::ios::ate);
|
||||
Require(file.is_open(), "cannot open test SPIR-V: " + path.string());
|
||||
const auto size = file.tellg();
|
||||
Require(size >= 20 && size <= 1024 * 1024 && size % 4 == 0, "invalid test SPIR-V size");
|
||||
ShaderRecompiler::RecompileResult result;
|
||||
result.spirv.resize(static_cast<std::size_t>(size) / 4);
|
||||
file.seekg(0);
|
||||
Require(static_cast<bool>(file.read(reinterpret_cast<char*>(result.spirv.data()), size)), "cannot read test SPIR-V");
|
||||
return result;
|
||||
}
|
||||
|
||||
void Run(SDL_Window* window, const std::filesystem::path& directory, bool verifyOnly) {
|
||||
unsigned count = 0;
|
||||
Require(SDL_Vulkan_GetInstanceExtensions(window, &count, nullptr) == SDL_TRUE, SDL_GetError());
|
||||
std::vector<const char*> extensions(count);
|
||||
Require(SDL_Vulkan_GetInstanceExtensions(window, &count, extensions.data()) == SDL_TRUE, SDL_GetError());
|
||||
const AgcDriver::PresentationWindow presentation{window, extensions, [](void* context, VkInstance instance) {
|
||||
VkSurfaceKHR surface = VK_NULL_HANDLE;
|
||||
Require(SDL_Vulkan_CreateSurface(static_cast<SDL_Window*>(context), instance, &surface) == SDL_TRUE, SDL_GetError());
|
||||
return surface;
|
||||
}, Width, Height};
|
||||
AgcDriver::VulkanDevice device(&presentation);
|
||||
auto vertex = LoadShader(directory / "Triangle.vert.spv");
|
||||
auto fragment = LoadShader(directory / "Triangle.frag.spv");
|
||||
const std::array<AgcDriver::Graphics::CompiledShader, 2> shaders{{
|
||||
{ShaderRecompiler::ShaderStage::Vertex, &vertex, 0},
|
||||
{ShaderRecompiler::ShaderStage::Fragment, &fragment, AgcDriver::Graphics::StagePushConstantBytes}
|
||||
}};
|
||||
for (std::size_t i = 0; i < Pixels.size(); i += 4) {
|
||||
Pixels[i] = std::byte{16};
|
||||
Pixels[i + 1] = std::byte{24};
|
||||
Pixels[i + 2] = std::byte{40};
|
||||
Pixels[i + 3] = std::byte{255};
|
||||
}
|
||||
AgcDriver::Graphics::State state{};
|
||||
state.stages.path = AgcDriver::Graphics::ShaderPath::Vertex;
|
||||
state.color = {reinterpret_cast<std::uintptr_t>(Pixels.data()), {Width, Height}, VK_FORMAT_R8G8B8A8_UNORM, Pixels.size()};
|
||||
state.hasColorTarget = true;
|
||||
state.renderExtent = {Width, Height};
|
||||
state.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
|
||||
state.viewport = {0, static_cast<float>(Height), static_cast<float>(Width), -static_cast<float>(Height), 0, 1};
|
||||
state.scissor = {{0, 0}, {Width, Height}};
|
||||
state.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
|
||||
state.blend.colorWriteMask = 15;
|
||||
const std::array<std::uint16_t, 3> indices{0, 1, 2};
|
||||
const AgcDriver::Pm4::IndexedDraw draw{reinterpret_cast<std::uintptr_t>(indices.data()), 3, 2, 1, 0};
|
||||
device.DrawIndexed(state, draw, shaders);
|
||||
const auto center = (Height / 2 * Width + Width / 2) * 4;
|
||||
Require(std::to_integer<unsigned>(Pixels[center]) > 30 && std::to_integer<unsigned>(Pixels[center + 1]) > 30 && std::to_integer<unsigned>(Pixels[center + 2]) > 30, "GPU readback: triangle center was not rendered");
|
||||
Require(Pixels[0] == std::byte{16} && Pixels[1] == std::byte{24} && Pixels[2] == std::byte{40}, "GPU readback: background changed");
|
||||
device.WaitPresented(device.PresentPixels(Width, Height, Pixels));
|
||||
std::cout << "SPIR-V triangle rendered, GPU readback verified, frame presented. Close the window or press Escape.\n" << std::flush;
|
||||
if (!verifyOnly) {
|
||||
bool running = true;
|
||||
while (running) {
|
||||
SDL_Event event{};
|
||||
if (SDL_WaitEventTimeout(&event, 100)) {
|
||||
if (event.type == SDL_QUIT || (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_ESCAPE)) running = false;
|
||||
if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_EXPOSED) device.WaitPresented(device.PresentPixels(Width, Height, Pixels));
|
||||
}
|
||||
}
|
||||
}
|
||||
device.WaitIdle();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
try {
|
||||
Require(argc == 1 || (argc == 2 && std::string_view(argv[1]) == "--verify"), "usage: agc_driver_visual_test [--verify]");
|
||||
SDL_SetMainReady();
|
||||
Require(SDL_Init(SDL_INIT_VIDEO) == 0, SDL_GetError());
|
||||
{
|
||||
const auto window = std::unique_ptr<SDL_Window, decltype(&SDL_DestroyWindow)>(SDL_CreateWindow("AnyPS5 AGC - SPIR-V triangle test", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, Width, Height, SDL_WINDOW_VULKAN | SDL_WINDOW_SHOWN), SDL_DestroyWindow);
|
||||
Require(window != nullptr, SDL_GetError());
|
||||
const auto base = std::unique_ptr<char, decltype(&SDL_free)>(SDL_GetBasePath(), SDL_free);
|
||||
Require(base != nullptr, SDL_GetError());
|
||||
Run(window.get(), std::filesystem::path(base.get()), argc == 2);
|
||||
}
|
||||
SDL_Quit();
|
||||
return 0;
|
||||
} catch (const std::exception& error) {
|
||||
std::cerr << error.what() << '\n';
|
||||
SDL_Quit();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
#version 450
|
||||
layout(location = 0) in vec3 color;
|
||||
layout(location = 0) out vec4 outputColor;
|
||||
void main() {
|
||||
outputColor = vec4(color, 1.0);
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
#version 450
|
||||
out gl_PerVertex { vec4 gl_Position; };
|
||||
layout(location = 0) out vec3 color;
|
||||
void main() {
|
||||
vec2 positions[3] = vec2[](vec2(-0.8, -0.7), vec2(0.8, -0.7), vec2(0.0, 0.8));
|
||||
vec3 colors[3] = vec3[](vec3(1.0, 0.15, 0.1), vec3(0.1, 1.0, 0.2), vec3(0.15, 0.3, 1.0));
|
||||
gl_Position = vec4(positions[gl_VertexIndex], 0.5, 1.0);
|
||||
color = colors[gl_VertexIndex];
|
||||
}
|
||||
Loading…
Reference in New Issue