diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 7b8db6997..474aec6e1 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -1,3 +1,5 @@ +add_subdirectory(host_shaders) + add_library(video_core STATIC command_processor.cpp command_processor.h @@ -157,36 +159,6 @@ add_library(video_core STATIC video_core.h ) -set(SHADER_FILES - renderer_opengl/depth_to_color.frag - renderer_opengl/depth_to_color.vert - renderer_opengl/ds_to_color.frag - renderer_opengl/texture_filters/anime4k/refine.frag - renderer_opengl/texture_filters/anime4k/x_gradient.frag - renderer_opengl/texture_filters/anime4k/y_gradient.frag - renderer_opengl/texture_filters/bicubic/bicubic.frag - renderer_opengl/texture_filters/nearest_neighbor/nearest_neighbor.frag - renderer_opengl/texture_filters/scale_force/scale_force.frag - renderer_opengl/texture_filters/tex_coord.vert - renderer_opengl/texture_filters/xbrz/xbrz_freescale.frag - renderer_opengl/texture_filters/xbrz/xbrz_freescale.vert -) - -include(${CMAKE_CURRENT_SOURCE_DIR}/generate_shaders.cmake) - -foreach(shader_file ${SHADER_FILES}) - get_filename_component(shader_file_name ${shader_file} NAME) - GetShaderHeaderFile(${shader_file_name}) - list(APPEND SHADER_HEADERS ${shader_header_file}) -endforeach() - -add_custom_target(shaders - BYPRODUCTS ${SHADER_HEADERS} - COMMAND "${CMAKE_COMMAND}" -P ${CMAKE_CURRENT_SOURCE_DIR}/generate_shaders.cmake - SOURCES ${SHADER_FILES} -) -add_dependencies(video_core shaders) - target_include_directories(video_core PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) if(ARCHITECTURE_x86_64) @@ -202,16 +174,19 @@ endif() create_target_directory_groups(video_core) +# Ignore nullability warnings generated from VMA if (NOT MSVC) - # Ignore nullability warnings generated from VMA target_compile_options(vma INTERFACE -Wno-unused-variable -Wno-nullability-completeness) endif() target_link_libraries(video_core PUBLIC common core) -target_link_libraries(video_core PRIVATE nihstro-headers Boost::serialization glm::glm) -target_link_libraries(video_core PRIVATE vulkan-headers vma sirit SPIRV glslang glad) set_target_properties(video_core PROPERTIES INTERPROCEDURAL_OPTIMIZATION ${ENABLE_LTO}) +add_dependencies(video_core host_shaders) +target_include_directories(video_core PRIVATE ${HOST_SHADERS_INCLUDE}) +target_link_libraries(video_core PRIVATE vulkan-headers vma sirit SPIRV glslang glad) +target_link_libraries(video_core PRIVATE nihstro-headers Boost::serialization glm::glm) + if (ARCHITECTURE_x86_64) target_link_libraries(video_core PUBLIC xbyak) endif() diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt new file mode 100644 index 000000000..83e811c9c --- /dev/null +++ b/src/video_core/host_shaders/CMakeLists.txt @@ -0,0 +1,100 @@ +# Copyright 2022 Citra Emulator Project +# Licensed under GPLv2 or any later version +# Refer to the license.txt file included. + +set(SHADER_FILES + texture_filtering/bicubic.frag + texture_filtering/nearest_neighbor.frag + texture_filtering/refine.frag + texture_filtering/scale_force.frag + texture_filtering/tex_coord.vert + texture_filtering/xbrz_freescale.frag + texture_filtering/xbrz_freescale.vert + texture_filtering/x_gradient.frag + texture_filtering/y_gradient.frag + opengl_present.frag + opengl_present.vert + opengl_present_anaglyph.frag + opengl_present_interlaced.frag + vulkan_present.frag + vulkan_present.vert + vulkan_present_anaglyph.frag + vulkan_present_interlaced.frag +) + +find_program(GLSLANGVALIDATOR "glslangValidator") +if ("${GLSLANGVALIDATOR}" STREQUAL "GLSLANGVALIDATOR-NOTFOUND") + message(FATAL_ERROR "Required program `glslangValidator` not found.") +endif() + +set(MACROS "-Dgl_VertexID=gl_VertexIndex") +set(QUIET_FLAG "--quiet") + +set(SHADER_INCLUDE ${CMAKE_CURRENT_BINARY_DIR}/include) +set(SHADER_DIR ${SHADER_INCLUDE}/video_core/host_shaders) +set(HOST_SHADERS_INCLUDE ${SHADER_INCLUDE} PARENT_SCOPE) + +set(INPUT_FILE ${CMAKE_CURRENT_SOURCE_DIR}/source_shader.h.in) +set(HEADER_GENERATOR ${CMAKE_CURRENT_SOURCE_DIR}/StringShaderHeader.cmake) + +# Check if `--quiet` is available on host's glslangValidator version +# glslangValidator prints to STDERR iff an unrecognized flag is passed to it +execute_process( + COMMAND + ${GLSLANGVALIDATOR} ${QUIET_FLAG} + ERROR_VARIABLE + GLSLANG_ERROR + # STDOUT variable defined to silence unnecessary output during CMake configuration + OUTPUT_VARIABLE + GLSLANG_OUTPUT +) + +if (NOT GLSLANG_ERROR STREQUAL "") + message(WARNING "Refusing to use unavailable flag `${QUIET_FLAG}` on `${GLSLANGVALIDATOR}`") + set(QUIET_FLAG "") +endif() + +foreach(FILENAME IN ITEMS ${SHADER_FILES}) + string(REPLACE "." "_" SHADER_NAME ${FILENAME}) + set(SOURCE_FILE ${CMAKE_CURRENT_SOURCE_DIR}/${FILENAME}) + # Skip generating source headers on Vulkan exclusive files + if (NOT ${FILENAME} MATCHES "vulkan.*") + set(SOURCE_HEADER_FILE ${SHADER_DIR}/${SHADER_NAME}.h) + add_custom_command( + OUTPUT + ${SOURCE_HEADER_FILE} + COMMAND + ${CMAKE_COMMAND} -P ${HEADER_GENERATOR} ${SOURCE_FILE} ${SOURCE_HEADER_FILE} ${INPUT_FILE} + MAIN_DEPENDENCY + ${SOURCE_FILE} + DEPENDS + ${INPUT_FILE} + # HEADER_GENERATOR should be included here but msbuild seems to assume it's always modified + ) + set(SHADER_HEADERS ${SHADER_HEADERS} ${SOURCE_HEADER_FILE}) + endif() + # Skip compiling to SPIR-V OpenGL exclusive files + if (NOT ${FILENAME} MATCHES "opengl.*") + string(TOUPPER ${SHADER_NAME}_SPV SPIRV_VARIABLE_NAME) + set(SPIRV_HEADER_FILE ${SHADER_DIR}/${SHADER_NAME}_spv.h) + add_custom_command( + OUTPUT + ${SPIRV_HEADER_FILE} + COMMAND + ${GLSLANGVALIDATOR} --target-env vulkan1.1 --glsl-version 450 ${QUIET_FLAG} ${MACROS} --variable-name ${SPIRV_VARIABLE_NAME} -o ${SPIRV_HEADER_FILE} ${SOURCE_FILE} + MAIN_DEPENDENCY + ${SOURCE_FILE} + ) + set(SHADER_HEADERS ${SHADER_HEADERS} ${SPIRV_HEADER_FILE}) + endif() +endforeach() + +set(SHADER_SOURCES ${SHADER_FILES}) +list(APPEND SHADER_SOURCES ${GLSL_INCLUDES}) + +add_custom_target(host_shaders + DEPENDS + ${SHADER_HEADERS} + SOURCES + ${SHADER_SOURCES} +) diff --git a/src/video_core/host_shaders/StringShaderHeader.cmake b/src/video_core/host_shaders/StringShaderHeader.cmake new file mode 100644 index 000000000..9f7525535 --- /dev/null +++ b/src/video_core/host_shaders/StringShaderHeader.cmake @@ -0,0 +1,36 @@ +# SPDX-FileCopyrightText: 2020 yuzu Emulator Project +# SPDX-License-Identifier: GPL-2.0-or-later + +set(SOURCE_FILE ${CMAKE_ARGV3}) +set(HEADER_FILE ${CMAKE_ARGV4}) +set(INPUT_FILE ${CMAKE_ARGV5}) + +get_filename_component(CONTENTS_NAME ${SOURCE_FILE} NAME) +string(REPLACE "." "_" CONTENTS_NAME ${CONTENTS_NAME}) +string(TOUPPER ${CONTENTS_NAME} CONTENTS_NAME) + +FILE(READ ${SOURCE_FILE} line_contents) + +# Replace double quotes with single quotes, +# as double quotes will be used to wrap the lines +STRING(REGEX REPLACE "\"" "'" line_contents "${line_contents}") + +# CMake separates list elements with semicolons, but semicolons +# are used extensively in the shader code. +# Replace with a temporary marker, to be reverted later. +STRING(REGEX REPLACE ";" "{{SEMICOLON}}" line_contents "${line_contents}") + +# Make every line an individual element in the CMake list. +STRING(REGEX REPLACE "\n" ";" line_contents "${line_contents}") + +# Build the shader string, wrapping each line in double quotes. +foreach(line IN LISTS line_contents) + string(CONCAT CONTENTS "${CONTENTS}" \"${line}\\n\"\n) +endforeach() + +# Revert the original semicolons in the source. +STRING(REGEX REPLACE "{{SEMICOLON}}" ";" CONTENTS "${CONTENTS}") + +get_filename_component(OUTPUT_DIR ${HEADER_FILE} DIRECTORY) +make_directory(${OUTPUT_DIR}) +configure_file(${INPUT_FILE} ${HEADER_FILE} @ONLY) diff --git a/src/video_core/host_shaders/opengl_present.frag b/src/video_core/host_shaders/opengl_present.frag new file mode 100644 index 000000000..8ba76fc9d --- /dev/null +++ b/src/video_core/host_shaders/opengl_present.frag @@ -0,0 +1,18 @@ +// Copyright 2022 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +//? #version 430 core + +layout(location = 0) in vec2 frag_tex_coord; +layout(location = 0) out vec4 color; + +layout(binding = 0) uniform sampler2D color_texture; + +uniform vec4 i_resolution; +uniform vec4 o_resolution; +uniform int layer; + +void main() { + color = texture(color_texture, frag_tex_coord); +} \ No newline at end of file diff --git a/src/video_core/host_shaders/opengl_present.vert b/src/video_core/host_shaders/opengl_present.vert new file mode 100644 index 000000000..c1947b0c7 --- /dev/null +++ b/src/video_core/host_shaders/opengl_present.vert @@ -0,0 +1,23 @@ +// Copyright 2022 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +//? #version 430 core +layout(location = 0) in vec2 vert_position; +layout(location = 1) in vec2 vert_tex_coord; +layout(location = 0) out vec2 frag_tex_coord; + +// This is a truncated 3x3 matrix for 2D transformations: +// The upper-left 2x2 submatrix performs scaling/rotation/mirroring. +// The third column performs translation. +// The third row could be used for projection, which we don't need in 2D. It hence is assumed to +// implicitly be [0, 0, 1] +uniform mat3x2 modelview_matrix; + +void main() { + // Multiply input position by the rotscale part of the matrix and then manually translate by + // the last column. This is equivalent to using a full 3x3 matrix and expanding the vector + // to `vec3(vert_position.xy, 1.0)` + gl_Position = vec4(mat2(modelview_matrix) * vert_position + modelview_matrix[2], 0.0, 1.0); + frag_tex_coord = vert_tex_coord; +} \ No newline at end of file diff --git a/src/video_core/host_shaders/opengl_present_anaglyph.frag b/src/video_core/host_shaders/opengl_present_anaglyph.frag new file mode 100644 index 000000000..fb7520b44 --- /dev/null +++ b/src/video_core/host_shaders/opengl_present_anaglyph.frag @@ -0,0 +1,32 @@ +// Copyright 2022 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +//? #version 430 core + +// Anaglyph Red-Cyan shader based on Dubois algorithm +// Constants taken from the paper: +// "Conversion of a Stereo Pair to Anaglyph with +// the Least-Squares Projection Method" +// Eric Dubois, March 2009 +const mat3 l = mat3( 0.437, 0.449, 0.164, + -0.062,-0.062,-0.024, + -0.048,-0.050,-0.017); +const mat3 r = mat3(-0.011,-0.032,-0.007, + 0.377, 0.761, 0.009, + -0.026,-0.093, 1.234); + +layout(location = 0) in vec2 frag_tex_coord; +layout(location = 0) out vec4 color; + +layout(binding = 0) uniform sampler2D color_texture; +layout(binding = 1) uniform sampler2D color_texture_r; + +uniform vec4 resolution; +uniform int layer; + +void main() { + vec4 color_tex_l = texture(color_texture, frag_tex_coord); + vec4 color_tex_r = texture(color_texture_r, frag_tex_coord); + color = vec4(color_tex_l.rgb*l+color_tex_r.rgb*r, color_tex_l.a); +} \ No newline at end of file diff --git a/src/video_core/host_shaders/opengl_present_interlaced.frag b/src/video_core/host_shaders/opengl_present_interlaced.frag new file mode 100644 index 000000000..7a2a28a74 --- /dev/null +++ b/src/video_core/host_shaders/opengl_present_interlaced.frag @@ -0,0 +1,22 @@ +// Copyright 2022 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +//? #version 430 core + +layout(location = 0) in vec2 frag_tex_coord; +layout(location = 0) out vec4 color; + +layout(binding = 0) uniform sampler2D color_texture; +layout(binding = 1) uniform sampler2D color_texture_r; + +uniform vec4 o_resolution; +uniform int reverse_interlaced; + +void main() { + float screen_row = o_resolution.x * frag_tex_coord.x; + if (int(screen_row) % 2 == reverse_interlaced) + color = texture(color_texture, frag_tex_coord); + else + color = texture(color_texture_r, frag_tex_coord); +} \ No newline at end of file diff --git a/src/video_core/host_shaders/source_shader.h.in b/src/video_core/host_shaders/source_shader.h.in new file mode 100644 index 000000000..4d1b0702f --- /dev/null +++ b/src/video_core/host_shaders/source_shader.h.in @@ -0,0 +1,15 @@ +// Copyright 2022 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include + +namespace HostShaders { + +constexpr std::string_view @CONTENTS_NAME@ = { +@CONTENTS@ +}; + +} // namespace HostShaders diff --git a/src/video_core/renderer_opengl/texture_filters/bicubic/bicubic.frag b/src/video_core/host_shaders/texture_filtering/bicubic.frag similarity index 62% rename from src/video_core/renderer_opengl/texture_filters/bicubic/bicubic.frag rename to src/video_core/host_shaders/texture_filtering/bicubic.frag index f384c7864..9fba5af75 100644 --- a/src/video_core/renderer_opengl/texture_filters/bicubic/bicubic.frag +++ b/src/video_core/host_shaders/texture_filtering/bicubic.frag @@ -1,11 +1,14 @@ +// Copyright 2022 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + //? #version 330 precision mediump float; -in vec2 tex_coord; +layout(location = 0) in vec2 tex_coord; +layout(location = 0) out vec4 frag_color; -out vec4 frag_color; - -uniform sampler2D input_texture; +layout(binding = 0) uniform sampler2D input_texture; // from http://www.java-gaming.org/index.php?topic=35123.0 vec4 cubic(float v) { @@ -18,9 +21,9 @@ vec4 cubic(float v) { return vec4(x, y, z, w) * (1.0 / 6.0); } -vec4 textureBicubic(sampler2D sampler, vec2 texCoords) { +vec4 textureBicubic(sampler2D tex_sampler, vec2 texCoords) { - vec2 texSize = vec2(textureSize(sampler, 0)); + vec2 texSize = vec2(textureSize(tex_sampler, 0)); vec2 invTexSize = 1.0 / texSize; texCoords = texCoords * texSize - 0.5; @@ -38,10 +41,10 @@ vec4 textureBicubic(sampler2D sampler, vec2 texCoords) { offset *= invTexSize.xxyy; - vec4 sample0 = texture(sampler, offset.xz); - vec4 sample1 = texture(sampler, offset.yz); - vec4 sample2 = texture(sampler, offset.xw); - vec4 sample3 = texture(sampler, offset.yw); + vec4 sample0 = texture(tex_sampler, offset.xz); + vec4 sample1 = texture(tex_sampler, offset.yz); + vec4 sample2 = texture(tex_sampler, offset.xw); + vec4 sample3 = texture(tex_sampler, offset.yw); float sx = s.x / (s.x + s.y); float sy = s.z / (s.z + s.w); diff --git a/src/video_core/host_shaders/texture_filtering/nearest_neighbor.frag b/src/video_core/host_shaders/texture_filtering/nearest_neighbor.frag new file mode 100644 index 000000000..0edab703a --- /dev/null +++ b/src/video_core/host_shaders/texture_filtering/nearest_neighbor.frag @@ -0,0 +1,15 @@ +// Copyright 2022 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +//? #version 430 core +precision mediump float; + +layout(location = 0) in vec2 tex_coord; +layout(location = 0) out vec4 frag_color; + +layout(binding = 0) uniform sampler2D input_texture; + +void main() { + frag_color = texture(input_texture, tex_coord); +} diff --git a/src/video_core/renderer_opengl/texture_filters/anime4k/refine.frag b/src/video_core/host_shaders/texture_filtering/refine.frag similarity index 89% rename from src/video_core/renderer_opengl/texture_filters/anime4k/refine.frag rename to src/video_core/host_shaders/texture_filtering/refine.frag index 569f30078..804f911d6 100644 --- a/src/video_core/renderer_opengl/texture_filters/anime4k/refine.frag +++ b/src/video_core/host_shaders/texture_filtering/refine.frag @@ -1,12 +1,15 @@ -//? #version 330 +// Copyright 2022 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +//? #version 430 core precision mediump float; -in vec2 tex_coord; +layout(location = 0) in vec2 tex_coord; +layout(location = 0) out vec4 frag_color; -out vec4 frag_color; - -uniform sampler2D HOOKED; -uniform sampler2D LUMAD; +layout(binding = 0) uniform sampler2D HOOKED; +layout(binding = 1) uniform sampler2D LUMAD; const float LINE_DETECT_THRESHOLD = 0.4; const float STRENGTH = 0.6; diff --git a/src/video_core/renderer_opengl/texture_filters/scale_force/scale_force.frag b/src/video_core/host_shaders/texture_filtering/scale_force.frag similarity index 95% rename from src/video_core/renderer_opengl/texture_filters/scale_force/scale_force.frag rename to src/video_core/host_shaders/texture_filtering/scale_force.frag index f162278a5..553036e68 100644 --- a/src/video_core/renderer_opengl/texture_filters/scale_force/scale_force.frag +++ b/src/video_core/host_shaders/texture_filtering/scale_force.frag @@ -1,5 +1,3 @@ -//? #version 320 es - // from https://github.com/BreadFish64/ScaleFish/tree/master/scale_force // MIT License @@ -24,13 +22,18 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +// Copyright 2022 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +//? #version 320 es + precision mediump float; -in vec2 tex_coord; +layout(location = 0) in vec2 tex_coord; +layout(location = 0) out vec4 frag_color; -out vec4 frag_color; - -uniform sampler2D input_texture; +layout(binding = 0) uniform sampler2D input_texture; vec2 tex_size; vec2 inv_tex_size; diff --git a/src/video_core/renderer_opengl/texture_filters/tex_coord.vert b/src/video_core/host_shaders/texture_filtering/tex_coord.vert similarity index 54% rename from src/video_core/renderer_opengl/texture_filters/tex_coord.vert rename to src/video_core/host_shaders/texture_filtering/tex_coord.vert index e5e153330..9d41a3245 100644 --- a/src/video_core/renderer_opengl/texture_filters/tex_coord.vert +++ b/src/video_core/host_shaders/texture_filtering/tex_coord.vert @@ -1,5 +1,9 @@ -//? #version 330 -out vec2 tex_coord; +// Copyright 2022 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +//? #version 430 core +layout(location = 0) out vec2 tex_coord; const vec2 vertices[4] = vec2[4](vec2(-1.0, -1.0), vec2(1.0, -1.0), vec2(-1.0, 1.0), vec2(1.0, 1.0)); diff --git a/src/video_core/renderer_opengl/texture_filters/anime4k/x_gradient.frag b/src/video_core/host_shaders/texture_filtering/x_gradient.frag similarity index 55% rename from src/video_core/renderer_opengl/texture_filters/anime4k/x_gradient.frag rename to src/video_core/host_shaders/texture_filtering/x_gradient.frag index 8103cb77c..a9a4e3e8a 100644 --- a/src/video_core/renderer_opengl/texture_filters/anime4k/x_gradient.frag +++ b/src/video_core/host_shaders/texture_filtering/x_gradient.frag @@ -1,11 +1,14 @@ -//? #version 330 +// Copyright 2022 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +//? #version 430 core precision mediump float; -in vec2 tex_coord; +layout(location = 0) in vec2 tex_coord; +layout(location = 0) out vec2 frag_color; -out vec2 frag_color; - -uniform sampler2D tex_input; +layout(binding = 0) uniform sampler2D tex_input; const vec3 K = vec3(0.2627, 0.6780, 0.0593); // TODO: improve handling of alpha channel diff --git a/src/video_core/renderer_opengl/texture_filters/xbrz/xbrz_freescale.frag b/src/video_core/host_shaders/texture_filtering/xbrz_freescale.frag similarity index 96% rename from src/video_core/renderer_opengl/texture_filters/xbrz/xbrz_freescale.frag rename to src/video_core/host_shaders/texture_filtering/xbrz_freescale.frag index 84f1b3503..42cdd7ac1 100644 --- a/src/video_core/renderer_opengl/texture_filters/xbrz/xbrz_freescale.frag +++ b/src/video_core/host_shaders/texture_filtering/xbrz_freescale.frag @@ -1,14 +1,25 @@ -//? #version 330 +// Copyright 2022 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +//? #version 430 core precision mediump float; -in vec2 tex_coord; -in vec2 source_size; -in vec2 output_size; +layout(location = 0) in vec2 tex_coord; +layout(location = 1) in vec2 source_size; +layout(location = 2) in vec2 output_size; -out vec4 frag_color; +layout(location = 0) out vec4 frag_color; -uniform sampler2D tex; +layout(binding = 0) uniform sampler2D tex; + +#ifdef VULKAN +layout(push_constant, std140) uniform XbrzInfo { + lowp float scale; +}; +#else uniform lowp float scale; +#endif const int BLEND_NONE = 0; const int BLEND_NORMAL = 1; diff --git a/src/video_core/host_shaders/texture_filtering/xbrz_freescale.vert b/src/video_core/host_shaders/texture_filtering/xbrz_freescale.vert new file mode 100644 index 000000000..715fa4a45 --- /dev/null +++ b/src/video_core/host_shaders/texture_filtering/xbrz_freescale.vert @@ -0,0 +1,28 @@ +// Copyright 2022 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +//? #version 430 core +layout(location = 0) out vec2 tex_coord; +layout(location = 1) out vec2 source_size; +layout(location = 2) out vec2 output_size; + +layout(binding = 0) uniform sampler2D tex; + +#ifdef VULKAN +layout(push_constant, std140) uniform XbrzInfo { + lowp float scale; +}; +#else +uniform lowp float scale; +#endif + +const vec2 vertices[4] = + vec2[4](vec2(-1.0, -1.0), vec2(1.0, -1.0), vec2(-1.0, 1.0), vec2(1.0, 1.0)); + +void main() { + gl_Position = vec4(vertices[gl_VertexID], 0.0, 1.0); + tex_coord = (vertices[gl_VertexID] + 1.0) / 2.0; + source_size = vec2(textureSize(tex, 0)); + output_size = source_size * scale; +} diff --git a/src/video_core/renderer_opengl/texture_filters/anime4k/y_gradient.frag b/src/video_core/host_shaders/texture_filtering/y_gradient.frag similarity index 54% rename from src/video_core/renderer_opengl/texture_filters/anime4k/y_gradient.frag rename to src/video_core/host_shaders/texture_filtering/y_gradient.frag index 81e0d0f6e..805e428ad 100644 --- a/src/video_core/renderer_opengl/texture_filters/anime4k/y_gradient.frag +++ b/src/video_core/host_shaders/texture_filtering/y_gradient.frag @@ -1,11 +1,14 @@ -//? #version 330 +// Copyright 2022 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +//? #version 430 core precision mediump float; -in vec2 tex_coord; +layout(location = 0) in vec2 tex_coord; +layout(location = 0) out float frag_color; -out float frag_color; - -uniform sampler2D tex_input; +layout(binding = 0) uniform sampler2D tex_input; void main() { vec2 t = textureLodOffset(tex_input, tex_coord, 0.0, ivec2(0, 1)).xy; diff --git a/src/video_core/host_shaders/vulkan_present.frag b/src/video_core/host_shaders/vulkan_present.frag new file mode 100644 index 000000000..24cc4facb --- /dev/null +++ b/src/video_core/host_shaders/vulkan_present.frag @@ -0,0 +1,26 @@ +// Copyright 2022 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#version 450 core +#extension GL_ARB_separate_shader_objects : enable + +layout (location = 0) in vec2 frag_tex_coord; +layout (location = 0) out vec4 color; + +layout (push_constant, std140) uniform DrawInfo { + mat4 modelview_matrix; + vec4 i_resolution; + vec4 o_resolution; + int screen_id_l; + int screen_id_r; + int layer; + int reverse_interlaced; +}; + +layout (set = 0, binding = 0) uniform texture2D screen_textures[3]; +layout (set = 0, binding = 1) uniform sampler screen_sampler; + +void main() { + color = texture(sampler2D(screen_textures[screen_id_l], screen_sampler), frag_tex_coord); +} \ No newline at end of file diff --git a/src/video_core/host_shaders/vulkan_present.vert b/src/video_core/host_shaders/vulkan_present.vert new file mode 100644 index 000000000..26e09260f --- /dev/null +++ b/src/video_core/host_shaders/vulkan_present.vert @@ -0,0 +1,25 @@ +// Copyright 2022 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#version 450 core +#extension GL_ARB_separate_shader_objects : enable + +layout (location = 0) in vec2 vert_position; +layout (location = 1) in vec2 vert_tex_coord; +layout (location = 0) out vec2 frag_tex_coord; + +layout (push_constant, std140) uniform DrawInfo { + mat4 modelview_matrix; + vec4 i_resolution; + vec4 o_resolution; + int screen_id_l; + int screen_id_r; + int layer; +}; + +void main() { + vec4 position = vec4(vert_position, 0.0, 1.0) * modelview_matrix; + gl_Position = vec4(position.x, -position.y, 0.0, 1.0); + frag_tex_coord = vert_tex_coord; +} \ No newline at end of file diff --git a/src/video_core/host_shaders/vulkan_present_anaglyph.frag b/src/video_core/host_shaders/vulkan_present_anaglyph.frag new file mode 100644 index 000000000..eff3e9799 --- /dev/null +++ b/src/video_core/host_shaders/vulkan_present_anaglyph.frag @@ -0,0 +1,40 @@ +// Copyright 2022 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#version 450 core +#extension GL_ARB_separate_shader_objects : enable + +layout (location = 0) in vec2 frag_tex_coord; +layout (location = 0) out vec4 color; + +// Anaglyph Red-Cyan shader based on Dubois algorithm +// Constants taken from the paper: +// "Conversion of a Stereo Pair to Anaglyph with +// the Least-Squares Projection Method" +// Eric Dubois, March 2009 +const mat3 l = mat3( 0.437, 0.449, 0.164, + -0.062,-0.062,-0.024, + -0.048,-0.050,-0.017); +const mat3 r = mat3(-0.011,-0.032,-0.007, + 0.377, 0.761, 0.009, + -0.026,-0.093, 1.234); + +layout (push_constant, std140) uniform DrawInfo { + mat4 modelview_matrix; + vec4 i_resolution; + vec4 o_resolution; + int screen_id_l; + int screen_id_r; + int layer; + int reverse_interlaced; +}; + +layout (set = 0, binding = 0) uniform texture2D screen_textures[3]; +layout (set = 0, binding = 1) uniform sampler screen_sampler; + +void main() { + vec4 color_tex_l = texture(sampler2D(screen_textures[screen_id_l], screen_sampler), frag_tex_coord); + vec4 color_tex_r = texture(sampler2D(screen_textures[screen_id_r], screen_sampler), frag_tex_coord); + color = vec4(color_tex_l.rgb*l+color_tex_r.rgb*r, color_tex_l.a); +} \ No newline at end of file diff --git a/src/video_core/host_shaders/vulkan_present_interlaced.frag b/src/video_core/host_shaders/vulkan_present_interlaced.frag new file mode 100644 index 000000000..a27d560da --- /dev/null +++ b/src/video_core/host_shaders/vulkan_present_interlaced.frag @@ -0,0 +1,30 @@ +// Copyright 2022 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#version 450 core +#extension GL_ARB_separate_shader_objects : enable + +layout (location = 0) in vec2 frag_tex_coord; +layout (location = 0) out vec4 color; + +layout (push_constant, std140) uniform DrawInfo { + mat4 modelview_matrix; + vec4 i_resolution; + vec4 o_resolution; + int screen_id_l; + int screen_id_r; + int layer; + int reverse_interlaced; +}; + +layout (set = 0, binding = 0) uniform texture2D screen_textures[3]; +layout (set = 0, binding = 1) uniform sampler screen_sampler; + +void main() { + float screen_row = o_resolution.x * frag_tex_coord.x; + if (int(screen_row) % 2 == reverse_interlaced) + color = texture(sampler2D(screen_textures[screen_id_l], screen_sampler), frag_tex_coord); + else + color = texture(sampler2D(screen_textures[screen_id_r], screen_sampler), frag_tex_coord); +} \ No newline at end of file diff --git a/src/video_core/renderer_opengl/gl_resource_manager.cpp b/src/video_core/renderer_opengl/gl_resource_manager.cpp index f517e8992..bfaae4204 100644 --- a/src/video_core/renderer_opengl/gl_resource_manager.cpp +++ b/src/video_core/renderer_opengl/gl_resource_manager.cpp @@ -117,7 +117,7 @@ void OGLSampler::Release() { handle = 0; } -void OGLShader::Create(const char* source, GLenum type) { +void OGLShader::Create(std::string_view source, GLenum type) { if (handle != 0) return; if (source == nullptr) @@ -144,7 +144,7 @@ void OGLProgram::Create(bool separable_program, const std::vector& shade handle = LoadProgram(separable_program, shaders); } -void OGLProgram::Create(const char* vert_shader, const char* frag_shader) { +void OGLProgram::Create(std::string_view vert_shader, std::string_view frag_shader) { OGLShader vert, frag; vert.Create(vert_shader, GL_VERTEX_SHADER); frag.Create(frag_shader, GL_FRAGMENT_SHADER); diff --git a/src/video_core/renderer_opengl/gl_resource_manager.h b/src/video_core/renderer_opengl/gl_resource_manager.h index ecb538f1e..ab46b7af4 100644 --- a/src/video_core/renderer_opengl/gl_resource_manager.h +++ b/src/video_core/renderer_opengl/gl_resource_manager.h @@ -109,7 +109,7 @@ public: return *this; } - void Create(const char* source, GLenum type); + void Create(std::string_view source, GLenum type); void Release(); @@ -136,10 +136,10 @@ public: void Create(bool separable_program, const std::vector& shaders); /// Creates a new program from given shader soruce code - void Create(const char* vert_shader, const char* frag_shader); + void Create(std::string_view vert_shader, std::string_view frag_shader); /// Creates a new compute shader program - void Create(const std::string_view compute_shader); + void Create(std::string_view compute_shader); /// Deletes the internal OpenGL resource void Release(); diff --git a/src/video_core/renderer_opengl/gl_shader_util.cpp b/src/video_core/renderer_opengl/gl_shader_util.cpp index 5f73af4e4..3e688d8d3 100644 --- a/src/video_core/renderer_opengl/gl_shader_util.cpp +++ b/src/video_core/renderer_opengl/gl_shader_util.cpp @@ -13,7 +13,7 @@ namespace OpenGL { -GLuint LoadShader(const char* source, GLenum type) { +GLuint LoadShader(std::string_view source, GLenum type) { const std::string version = GLES ? R"( #version 320 es #define CITRA_GLES @@ -46,7 +46,7 @@ GLuint LoadShader(const char* source, GLenum type) { UNREACHABLE(); } - std::array src_arr{version.data(), source}; + std::array src_arr{version.data(), source.data()}; GLuint shader_id = glCreateShader(type); glShaderSource(shader_id, static_cast(src_arr.size()), src_arr.data(), nullptr); LOG_DEBUG(Render_OpenGL, "Compiling {} shader...", debug_type); @@ -71,7 +71,7 @@ GLuint LoadShader(const char* source, GLenum type) { return shader_id; } -GLuint LoadProgram(bool separable_program, const std::vector& shaders) { +GLuint LoadProgram(bool separable_program, std::span shaders) { // Link the program LOG_DEBUG(Render_OpenGL, "Linking program..."); diff --git a/src/video_core/renderer_opengl/gl_shader_util.h b/src/video_core/renderer_opengl/gl_shader_util.h index fef6139d3..2d97044fb 100644 --- a/src/video_core/renderer_opengl/gl_shader_util.h +++ b/src/video_core/renderer_opengl/gl_shader_util.h @@ -4,7 +4,8 @@ #pragma once -#include +#include +#include #include namespace OpenGL { @@ -29,7 +30,7 @@ precision mediump uimage2D; * @param source String of the GLSL shader program * @param type Type of the shader (GL_VERTEX_SHADER, GL_GEOMETRY_SHADER or GL_FRAGMENT_SHADER) */ -GLuint LoadShader(const char* source, GLenum type); +GLuint LoadShader(std::string_view source, GLenum type); /** * Utility function to create and link an OpenGL GLSL shader program @@ -37,6 +38,6 @@ GLuint LoadShader(const char* source, GLenum type); * @param shaders ID of shaders to attach to the program * @returns Handle of the newly created OpenGL program object */ -GLuint LoadProgram(bool separable_program, const std::vector& shaders); +GLuint LoadProgram(bool separable_program, std::span shaders); } // namespace OpenGL diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 67f459a34..c0654e472 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -23,6 +23,14 @@ #include "video_core/renderer_opengl/renderer_opengl.h" #include "video_core/video_core.h" +#include "video_core/host_shaders/opengl_present_vert.h" +#include "video_core/host_shaders/opengl_present_frag.h" +#include "video_core/host_shaders/opengl_present_anaglyph_frag.h" +#include "video_core/host_shaders/opengl_present_interlaced_frag.h" + +MICROPROFILE_DEFINE(OpenGL_RenderFrame, "OpenGL", "Render Frame", MP_RGB(128, 128, 64)); +MICROPROFILE_DEFINE(OpenGL_WaitPresent, "OpenGL", "Wait For Present", MP_RGB(128, 128, 128)); + namespace OpenGL { // If the size of this is too small, it ends up creating a soft cap on FPS as the renderer will have @@ -220,93 +228,6 @@ public: } }; -static const char vertex_shader[] = R"( -in vec2 vert_position; -in vec2 vert_tex_coord; -out vec2 frag_tex_coord; - -// This is a truncated 3x3 matrix for 2D transformations: -// The upper-left 2x2 submatrix performs scaling/rotation/mirroring. -// The third column performs translation. -// The third row could be used for projection, which we don't need in 2D. It hence is assumed to -// implicitly be [0, 0, 1] -uniform mat3x2 modelview_matrix; - -void main() { - // Multiply input position by the rotscale part of the matrix and then manually translate by - // the last column. This is equivalent to using a full 3x3 matrix and expanding the vector - // to `vec3(vert_position.xy, 1.0)` - gl_Position = vec4(mat2(modelview_matrix) * vert_position + modelview_matrix[2], 0.0, 1.0); - frag_tex_coord = vert_tex_coord; -} -)"; - -static const char fragment_shader[] = R"( -in vec2 frag_tex_coord; -layout(location = 0) out vec4 color; - -uniform vec4 i_resolution; -uniform vec4 o_resolution; -uniform int layer; - -uniform sampler2D color_texture; - -void main() { - color = texture(color_texture, frag_tex_coord); -} -)"; - -static const char fragment_shader_anaglyph[] = R"( - -// Anaglyph Red-Cyan shader based on Dubois algorithm -// Constants taken from the paper: -// "Conversion of a Stereo Pair to Anaglyph with -// the Least-Squares Projection Method" -// Eric Dubois, March 2009 -const mat3 l = mat3( 0.437, 0.449, 0.164, - -0.062,-0.062,-0.024, - -0.048,-0.050,-0.017); -const mat3 r = mat3(-0.011,-0.032,-0.007, - 0.377, 0.761, 0.009, - -0.026,-0.093, 1.234); - -in vec2 frag_tex_coord; -out vec4 color; - -uniform vec4 resolution; -uniform int layer; - -uniform sampler2D color_texture; -uniform sampler2D color_texture_r; - -void main() { - vec4 color_tex_l = texture(color_texture, frag_tex_coord); - vec4 color_tex_r = texture(color_texture_r, frag_tex_coord); - color = vec4(color_tex_l.rgb*l+color_tex_r.rgb*r, color_tex_l.a); -} -)"; - -static const char fragment_shader_interlaced[] = R"( - -in vec2 frag_tex_coord; -out vec4 color; - -uniform vec4 o_resolution; - -uniform sampler2D color_texture; -uniform sampler2D color_texture_r; - -uniform int reverse_interlaced; - -void main() { - float screen_row = o_resolution.x * frag_tex_coord.x; - if (int(screen_row) % 2 == reverse_interlaced) - color = texture(color_texture, frag_tex_coord); - else - color = texture(color_texture_r, frag_tex_coord); -} -)"; - /** * Vertex structure that the drawn screen rectangles are composed of. */ @@ -391,9 +312,6 @@ VideoCore::RasterizerInterface* RendererOpenGL::Rasterizer() { /// Shutdown the renderer void RendererOpenGL::ShutDown() {} -MICROPROFILE_DEFINE(OpenGL_RenderFrame, "OpenGL", "Render Frame", MP_RGB(128, 128, 64)); -MICROPROFILE_DEFINE(OpenGL_WaitPresent, "OpenGL", "Wait For Present", MP_RGB(128, 128, 128)); - /// Swap buffers (render frame) void RendererOpenGL::SwapBuffers() { // Maintain the rasterizer's state as a priority @@ -710,13 +628,13 @@ void RendererOpenGL::ReloadShader() { if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::Anaglyph) { if (Settings::values.pp_shader_name.GetValue() == "dubois (builtin)") { - shader_data += fragment_shader_anaglyph; + shader_data += HostShaders::OPENGL_PRESENT_ANAGLYPH_FRAG; } else { std::string shader_text = OpenGL::GetPostProcessingShaderCode( true, Settings::values.pp_shader_name.GetValue()); if (shader_text.empty()) { // Should probably provide some information that the shader couldn't load - shader_data += fragment_shader_anaglyph; + shader_data += HostShaders::OPENGL_PRESENT_ANAGLYPH_FRAG; } else { shader_data += shader_text; } @@ -725,32 +643,32 @@ void RendererOpenGL::ReloadShader() { Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::ReverseInterlaced) { if (Settings::values.pp_shader_name.GetValue() == "horizontal (builtin)") { - shader_data += fragment_shader_interlaced; + shader_data += HostShaders::OPENGL_PRESENT_INTERLACED_FRAG; } else { std::string shader_text = OpenGL::GetPostProcessingShaderCode( false, Settings::values.pp_shader_name.GetValue()); if (shader_text.empty()) { // Should probably provide some information that the shader couldn't load - shader_data += fragment_shader_interlaced; + shader_data += HostShaders::OPENGL_PRESENT_INTERLACED_FRAG; } else { shader_data += shader_text; } } } else { if (Settings::values.pp_shader_name.GetValue() == "none (builtin)") { - shader_data += fragment_shader; + shader_data += HostShaders::OPENGL_PRESENT_FRAG; } else { std::string shader_text = OpenGL::GetPostProcessingShaderCode( false, Settings::values.pp_shader_name.GetValue()); if (shader_text.empty()) { // Should probably provide some information that the shader couldn't load - shader_data += fragment_shader; + shader_data += HostShaders::OPENGL_PRESENT_FRAG; } else { shader_data += shader_text; } } } - shader.Create(vertex_shader, shader_data.c_str()); + shader.Create(HostShaders::OPENGL_PRESENT_VERT, shader_data.c_str()); state.draw.shader_program = shader.handle; state.Apply(); uniform_modelview_matrix = glGetUniformLocation(shader.handle, "modelview_matrix"); diff --git a/src/video_core/renderer_opengl/texture_filters/anime4k/anime4k_ultrafast.cpp b/src/video_core/renderer_opengl/texture_filters/anime4k/anime4k_ultrafast.cpp index 132e9a60f..8c0a5d3cc 100644 --- a/src/video_core/renderer_opengl/texture_filters/anime4k/anime4k_ultrafast.cpp +++ b/src/video_core/renderer_opengl/texture_filters/anime4k/anime4k_ultrafast.cpp @@ -32,10 +32,10 @@ #include "video_core/renderer_opengl/texture_filters/anime4k/anime4k_ultrafast.h" -#include "shaders/refine.frag" -#include "shaders/tex_coord.vert" -#include "shaders/x_gradient.frag" -#include "shaders/y_gradient.frag" +#include "video_core/host_shaders/texture_filtering/refine_frag.h" +#include "video_core/host_shaders/texture_filtering/tex_coord_vert.h" +#include "video_core/host_shaders/texture_filtering/x_gradient_frag.h" +#include "video_core/host_shaders/texture_filtering/y_gradient_frag.h" namespace OpenGL { @@ -56,9 +56,9 @@ Anime4kUltrafast::Anime4kUltrafast(u16 scale_factor) : TextureFilterBase(scale_f } state.draw.vertex_array = vao.handle; - gradient_x_program.Create(tex_coord_vert.data(), x_gradient_frag.data()); - gradient_y_program.Create(tex_coord_vert.data(), y_gradient_frag.data()); - refine_program.Create(tex_coord_vert.data(), refine_frag.data()); + gradient_x_program.Create(HostShaders::TEX_COORD_VERT, HostShaders::X_GRADIENT_FRAG); + gradient_y_program.Create(HostShaders::TEX_COORD_VERT, HostShaders::Y_GRADIENT_FRAG); + refine_program.Create(HostShaders::TEX_COORD_VERT, HostShaders::REFINE_FRAG); state.draw.shader_program = gradient_y_program.handle; state.Apply(); diff --git a/src/video_core/renderer_opengl/texture_filters/bicubic/bicubic.cpp b/src/video_core/renderer_opengl/texture_filters/bicubic/bicubic.cpp index b9981ad07..7093cd8c8 100644 --- a/src/video_core/renderer_opengl/texture_filters/bicubic/bicubic.cpp +++ b/src/video_core/renderer_opengl/texture_filters/bicubic/bicubic.cpp @@ -4,13 +4,13 @@ #include "video_core/renderer_opengl/texture_filters/bicubic/bicubic.h" -#include "shaders/bicubic.frag" -#include "shaders/tex_coord.vert" +#include "video_core/host_shaders/texture_filtering/bicubic_frag.h" +#include "video_core/host_shaders/texture_filtering/tex_coord_vert.h" namespace OpenGL { Bicubic::Bicubic(u16 scale_factor) : TextureFilterBase(scale_factor) { - program.Create(tex_coord_vert.data(), bicubic_frag.data()); + program.Create(HostShaders::TEX_COORD_VERT, HostShaders::BICUBIC_FRAG); vao.Create(); src_sampler.Create(); diff --git a/src/video_core/renderer_opengl/texture_filters/nearest_neighbor/nearest_neighbor.cpp b/src/video_core/renderer_opengl/texture_filters/nearest_neighbor/nearest_neighbor.cpp index c5214c480..bc9cdb3ab 100644 --- a/src/video_core/renderer_opengl/texture_filters/nearest_neighbor/nearest_neighbor.cpp +++ b/src/video_core/renderer_opengl/texture_filters/nearest_neighbor/nearest_neighbor.cpp @@ -4,13 +4,13 @@ #include "video_core/renderer_opengl/texture_filters/nearest_neighbor/nearest_neighbor.h" -#include "shaders/nearest_neighbor.frag" -#include "shaders/tex_coord.vert" +#include "video_core/host_shaders/texture_filtering/nearest_neighbor_frag.h" +#include "video_core/host_shaders/texture_filtering/tex_coord_vert.h" namespace OpenGL { NearestNeighbor::NearestNeighbor(u16 scale_factor) : TextureFilterBase(scale_factor) { - program.Create(tex_coord_vert.data(), nearest_neighbor_frag.data()); + program.Create(HostShaders::TEX_COORD_VERT, HostShaders::NEAREST_NEIGHBOR_FRAG); vao.Create(); src_sampler.Create(); diff --git a/src/video_core/renderer_opengl/texture_filters/nearest_neighbor/nearest_neighbor.frag b/src/video_core/renderer_opengl/texture_filters/nearest_neighbor/nearest_neighbor.frag deleted file mode 100644 index ad5a33eca..000000000 --- a/src/video_core/renderer_opengl/texture_filters/nearest_neighbor/nearest_neighbor.frag +++ /dev/null @@ -1,12 +0,0 @@ -//? #version 330 -precision mediump float; - -in vec2 tex_coord; - -out vec4 frag_color; - -uniform sampler2D input_texture; - -void main() { - frag_color = texture(input_texture, tex_coord); -} diff --git a/src/video_core/renderer_opengl/texture_filters/scale_force/scale_force.cpp b/src/video_core/renderer_opengl/texture_filters/scale_force/scale_force.cpp index 483f3d79a..db2d6df1d 100644 --- a/src/video_core/renderer_opengl/texture_filters/scale_force/scale_force.cpp +++ b/src/video_core/renderer_opengl/texture_filters/scale_force/scale_force.cpp @@ -4,13 +4,13 @@ #include "video_core/renderer_opengl/texture_filters/scale_force/scale_force.h" -#include "shaders/scale_force.frag" -#include "shaders/tex_coord.vert" +#include "video_core/host_shaders/texture_filtering/scale_force_frag.h" +#include "video_core/host_shaders/texture_filtering/tex_coord_vert.h" namespace OpenGL { ScaleForce::ScaleForce(u16 scale_factor) : TextureFilterBase(scale_factor) { - program.Create(tex_coord_vert.data(), scale_force_frag.data()); + program.Create(HostShaders::TEX_COORD_VERT, HostShaders::SCALE_FORCE_FRAG); vao.Create(); src_sampler.Create(); diff --git a/src/video_core/renderer_opengl/texture_filters/xbrz/xbrz_freescale.cpp b/src/video_core/renderer_opengl/texture_filters/xbrz/xbrz_freescale.cpp index 3849bb2c5..bc233cf93 100644 --- a/src/video_core/renderer_opengl/texture_filters/xbrz/xbrz_freescale.cpp +++ b/src/video_core/renderer_opengl/texture_filters/xbrz/xbrz_freescale.cpp @@ -42,8 +42,8 @@ #include "video_core/renderer_opengl/texture_filters/xbrz/xbrz_freescale.h" -#include "shaders/xbrz_freescale.frag" -#include "shaders/xbrz_freescale.vert" +#include "video_core/host_shaders/texture_filtering/xbrz_freescale_frag.h" +#include "video_core/host_shaders/texture_filtering/xbrz_freescale_vert.h" namespace OpenGL { @@ -51,7 +51,7 @@ XbrzFreescale::XbrzFreescale(u16 scale_factor) : TextureFilterBase(scale_factor) const OpenGLState cur_state = OpenGLState::GetCurState(); - program.Create(xbrz_freescale_vert.data(), xbrz_freescale_frag.data()); + program.Create(HostShaders::XBRZ_FREESCALE_VERT, HostShaders::XBRZ_FREESCALE_FRAG); vao.Create(); src_sampler.Create(); diff --git a/src/video_core/renderer_opengl/texture_filters/xbrz/xbrz_freescale.vert b/src/video_core/renderer_opengl/texture_filters/xbrz/xbrz_freescale.vert deleted file mode 100644 index 63905075f..000000000 --- a/src/video_core/renderer_opengl/texture_filters/xbrz/xbrz_freescale.vert +++ /dev/null @@ -1,17 +0,0 @@ -//? #version 330 -out vec2 tex_coord; -out vec2 source_size; -out vec2 output_size; - -uniform sampler2D tex; -uniform lowp float scale; - -const vec2 vertices[4] = - vec2[4](vec2(-1.0, -1.0), vec2(1.0, -1.0), vec2(-1.0, 1.0), vec2(1.0, 1.0)); - -void main() { - gl_Position = vec4(vertices[gl_VertexID], 0.0, 1.0); - tex_coord = (vertices[gl_VertexID] + 1.0) / 2.0; - source_size = vec2(textureSize(tex, 0)); - output_size = source_size * scale; -} diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index 11af783c6..b6d074cee 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -19,127 +19,16 @@ #include "video_core/renderer_vulkan/vk_shader_util.h" #include "video_core/video_core.h" +#include "video_core/host_shaders/vulkan_present_vert_spv.h" +#include "video_core/host_shaders/vulkan_present_frag_spv.h" +#include "video_core/host_shaders/vulkan_present_anaglyph_frag_spv.h" +#include "video_core/host_shaders/vulkan_present_interlaced_frag_spv.h" + namespace Vulkan { -constexpr std::string_view vertex_shader = R"( -#version 450 core -#extension GL_ARB_separate_shader_objects : enable -layout (location = 0) in vec2 vert_position; -layout (location = 1) in vec2 vert_tex_coord; -layout (location = 0) out vec2 frag_tex_coord; - -// This is a truncated 3x3 matrix for 2D transformations: -// The upper-left 2x2 submatrix performs scaling/rotation/mirroring. -// The third column performs translation. -// The third row could be used for projection, which we don't need in 2D. It hence is assumed to -// implicitly be [0, 0, 1] -layout (push_constant, std140) uniform DrawInfo { - mat4 modelview_matrix; - vec4 i_resolution; - vec4 o_resolution; - int screen_id_l; - int screen_id_r; - int layer; -}; - -void main() { - vec4 position = vec4(vert_position, 0.0, 1.0) * modelview_matrix; - gl_Position = vec4(position.x, -position.y, 0.0, 1.0); - frag_tex_coord = vert_tex_coord; -} -)"; - -constexpr std::string_view fragment_shader = R"( -#version 450 core -#extension GL_ARB_separate_shader_objects : enable -layout (location = 0) in vec2 frag_tex_coord; -layout (location = 0) out vec4 color; - -layout (push_constant, std140) uniform DrawInfo { - mat4 modelview_matrix; - vec4 i_resolution; - vec4 o_resolution; - int screen_id_l; - int screen_id_r; - int layer; - int reverse_interlaced; -}; - -layout (set = 0, binding = 0) uniform texture2D screen_textures[3]; -layout (set = 0, binding = 1) uniform sampler screen_sampler; - -void main() { - color = texture(sampler2D(screen_textures[screen_id_l], screen_sampler), frag_tex_coord); -} -)"; - -constexpr std::string_view fragment_shader_anaglyph = R"( -#version 450 core -#extension GL_ARB_separate_shader_objects : enable -layout (location = 0) in vec2 frag_tex_coord; -layout (location = 0) out vec4 color; - -// Anaglyph Red-Cyan shader based on Dubois algorithm -// Constants taken from the paper: -// "Conversion of a Stereo Pair to Anaglyph with -// the Least-Squares Projection Method" -// Eric Dubois, March 2009 -const mat3 l = mat3( 0.437, 0.449, 0.164, - -0.062,-0.062,-0.024, - -0.048,-0.050,-0.017); -const mat3 r = mat3(-0.011,-0.032,-0.007, - 0.377, 0.761, 0.009, - -0.026,-0.093, 1.234); - -layout (push_constant, std140) uniform DrawInfo { - mat4 modelview_matrix; - vec4 i_resolution; - vec4 o_resolution; - int screen_id_l; - int screen_id_r; - int layer; - int reverse_interlaced; -}; - -layout (set = 0, binding = 0) uniform texture2D screen_textures[3]; -layout (set = 0, binding = 1) uniform sampler screen_sampler; - -void main() { - vec4 color_tex_l = texture(sampler2D(screen_textures[screen_id_l], screen_sampler), frag_tex_coord); - vec4 color_tex_r = texture(sampler2D(screen_textures[screen_id_r], screen_sampler), frag_tex_coord); - color = vec4(color_tex_l.rgb*l+color_tex_r.rgb*r, color_tex_l.a); -} -)"; - -constexpr std::string_view fragment_shader_interlaced = R"( -#version 450 core -#extension GL_ARB_separate_shader_objects : enable -layout (location = 0) in vec2 frag_tex_coord; -layout (location = 0) out vec4 color; - -layout (push_constant, std140) uniform DrawInfo { - mat4 modelview_matrix; - vec4 i_resolution; - vec4 o_resolution; - int screen_id_l; - int screen_id_r; - int layer; - int reverse_interlaced; -}; - -layout (set = 0, binding = 0) uniform texture2D screen_textures[3]; -layout (set = 0, binding = 1) uniform sampler screen_sampler; - -void main() { - float screen_row = o_resolution.x * frag_tex_coord.x; - if (int(screen_row) % 2 == reverse_interlaced) - color = texture(sampler2D(screen_textures[screen_id_l], screen_sampler), frag_tex_coord); - else - color = texture(sampler2D(screen_textures[screen_id_r], screen_sampler), frag_tex_coord); -} -)"; - -/// Vertex structure that the drawn screen rectangles are composed of. +/** + * Vertex structure that the drawn screen rectangles are composed of. + */ struct ScreenRectVertex { ScreenRectVertex() = default; ScreenRectVertex(float x, float y, float u, float v) @@ -151,33 +40,13 @@ struct ScreenRectVertex { constexpr u32 VERTEX_BUFFER_SIZE = sizeof(ScreenRectVertex) * 8192; -/** - * Defines a 1:1 pixel ortographic projection matrix with (0,0) on the top-left - * corner and (width, height) on the lower-bottom. - * - * The projection part of the matrix is trivial, hence these operations are represented - * by a 3x2 matrix. - * - * @param flipped Whether the frame should be flipped upside down. - */ -static std::array MakeOrthographicMatrix(float width, float height, bool flipped) { - - std::array matrix; // Laid out in column-major order - - // Last matrix row is implicitly assumed to be [0, 0, 1]. - if (flipped) { - // clang-format off - matrix[0] = 2.f / width; matrix[2] = 0.f; matrix[4] = -1.f; - matrix[1] = 0.f; matrix[3] = 2.f / height; matrix[5] = -1.f; - // clang-format on - } else { - // clang-format off - matrix[0] = 2.f / width; matrix[2] = 0.f; matrix[4] = -1.f; - matrix[1] = 0.f; matrix[3] = -2.f / height; matrix[5] = 1.f; - // clang-format on - } - - return matrix; +constexpr std::array MakeOrthographicMatrix(f32 width, f32 height) { + // clang-format off + return { 2.f / width, 0.f, 0.f, 0.f, + 0.f, 2.f / height, 0.f, 0.f, + 0.f, 0.f, 1.f, 0.f, + -1.f, -1.f, 0.f, 1.f}; + // clang-format on } namespace { @@ -418,14 +287,10 @@ void RendererVulkan::LoadFBToScreenInfo(const GPU::Regs::FramebufferConfig& fram void RendererVulkan::CompileShaders() { vk::Device device = instance.GetDevice(); - present_vertex_shader = - Compile(vertex_shader, vk::ShaderStageFlagBits::eVertex, device, ShaderOptimization::Debug); - present_shaders[0] = Compile(fragment_shader, vk::ShaderStageFlagBits::eFragment, device, - ShaderOptimization::Debug); - present_shaders[1] = Compile(fragment_shader_anaglyph, vk::ShaderStageFlagBits::eFragment, - device, ShaderOptimization::Debug); - present_shaders[2] = Compile(fragment_shader_interlaced, vk::ShaderStageFlagBits::eFragment, - device, ShaderOptimization::Debug); + present_vertex_shader = CompileSPV(VULKAN_PRESENT_VERT_SPV, device); + present_shaders[0] = CompileSPV(VULKAN_PRESENT_FRAG_SPV, device); + present_shaders[1] = CompileSPV(VULKAN_PRESENT_ANAGLYPH_FRAG_SPV, device); + present_shaders[2] = CompileSPV(VULKAN_PRESENT_INTERLACED_FRAG_SPV, device); auto properties = instance.GetPhysicalDevice().getProperties(); for (std::size_t i = 0; i < present_samplers.size(); i++) { @@ -441,7 +306,8 @@ void RendererVulkan::CompileShaders() { .compareEnable = false, .compareOp = vk::CompareOp::eAlways, .borderColor = vk::BorderColor::eIntOpaqueBlack, - .unnormalizedCoordinates = false}; + .unnormalizedCoordinates = false, + }; present_samplers[i] = device.createSampler(sampler_info); } diff --git a/src/video_core/renderer_vulkan/vk_shader_util.cpp b/src/video_core/renderer_vulkan/vk_shader_util.cpp index e50ad3c67..a018990d0 100644 --- a/src/video_core/renderer_vulkan/vk_shader_util.cpp +++ b/src/video_core/renderer_vulkan/vk_shader_util.cpp @@ -12,111 +12,113 @@ namespace Vulkan { -constexpr TBuiltInResource DefaultTBuiltInResource = {.maxLights = 32, - .maxClipPlanes = 6, - .maxTextureUnits = 32, - .maxTextureCoords = 32, - .maxVertexAttribs = 64, - .maxVertexUniformComponents = 4096, - .maxVaryingFloats = 64, - .maxVertexTextureImageUnits = 32, - .maxCombinedTextureImageUnits = 80, - .maxTextureImageUnits = 32, - .maxFragmentUniformComponents = 4096, - .maxDrawBuffers = 32, - .maxVertexUniformVectors = 128, - .maxVaryingVectors = 8, - .maxFragmentUniformVectors = 16, - .maxVertexOutputVectors = 16, - .maxFragmentInputVectors = 15, - .minProgramTexelOffset = -8, - .maxProgramTexelOffset = 7, - .maxClipDistances = 8, - .maxComputeWorkGroupCountX = 65535, - .maxComputeWorkGroupCountY = 65535, - .maxComputeWorkGroupCountZ = 65535, - .maxComputeWorkGroupSizeX = 1024, - .maxComputeWorkGroupSizeY = 1024, - .maxComputeWorkGroupSizeZ = 64, - .maxComputeUniformComponents = 1024, - .maxComputeTextureImageUnits = 16, - .maxComputeImageUniforms = 8, - .maxComputeAtomicCounters = 8, - .maxComputeAtomicCounterBuffers = 1, - .maxVaryingComponents = 60, - .maxVertexOutputComponents = 64, - .maxGeometryInputComponents = 64, - .maxGeometryOutputComponents = 128, - .maxFragmentInputComponents = 128, - .maxImageUnits = 8, - .maxCombinedImageUnitsAndFragmentOutputs = 8, - .maxCombinedShaderOutputResources = 8, - .maxImageSamples = 0, - .maxVertexImageUniforms = 0, - .maxTessControlImageUniforms = 0, - .maxTessEvaluationImageUniforms = 0, - .maxGeometryImageUniforms = 0, - .maxFragmentImageUniforms = 8, - .maxCombinedImageUniforms = 8, - .maxGeometryTextureImageUnits = 16, - .maxGeometryOutputVertices = 256, - .maxGeometryTotalOutputComponents = 1024, - .maxGeometryUniformComponents = 1024, - .maxGeometryVaryingComponents = 64, - .maxTessControlInputComponents = 128, - .maxTessControlOutputComponents = 128, - .maxTessControlTextureImageUnits = 16, - .maxTessControlUniformComponents = 1024, - .maxTessControlTotalOutputComponents = 4096, - .maxTessEvaluationInputComponents = 128, - .maxTessEvaluationOutputComponents = 128, - .maxTessEvaluationTextureImageUnits = 16, - .maxTessEvaluationUniformComponents = 1024, - .maxTessPatchComponents = 120, - .maxPatchVertices = 32, - .maxTessGenLevel = 64, - .maxViewports = 16, - .maxVertexAtomicCounters = 0, - .maxTessControlAtomicCounters = 0, - .maxTessEvaluationAtomicCounters = 0, - .maxGeometryAtomicCounters = 0, - .maxFragmentAtomicCounters = 8, - .maxCombinedAtomicCounters = 8, - .maxAtomicCounterBindings = 1, - .maxVertexAtomicCounterBuffers = 0, - .maxTessControlAtomicCounterBuffers = 0, - .maxTessEvaluationAtomicCounterBuffers = 0, - .maxGeometryAtomicCounterBuffers = 0, - .maxFragmentAtomicCounterBuffers = 1, - .maxCombinedAtomicCounterBuffers = 1, - .maxAtomicCounterBufferSize = 16384, - .maxTransformFeedbackBuffers = 4, - .maxTransformFeedbackInterleavedComponents = - 64, - .maxCullDistances = 8, - .maxCombinedClipAndCullDistances = 8, - .maxSamples = 4, - .maxMeshOutputVerticesNV = 256, - .maxMeshOutputPrimitivesNV = 512, - .maxMeshWorkGroupSizeX_NV = 32, - .maxMeshWorkGroupSizeY_NV = 1, - .maxMeshWorkGroupSizeZ_NV = 1, - .maxTaskWorkGroupSizeX_NV = 32, - .maxTaskWorkGroupSizeY_NV = 1, - .maxTaskWorkGroupSizeZ_NV = 1, - .maxMeshViewCountNV = 4, - .maxDualSourceDrawBuffersEXT = 1, - .limits = TLimits{ - .nonInductiveForLoops = 1, - .whileLoops = 1, - .doWhileLoops = 1, - .generalUniformIndexing = 1, - .generalAttributeMatrixVectorIndexing = 1, - .generalVaryingIndexing = 1, - .generalSamplerIndexing = 1, - .generalVariableIndexing = 1, - .generalConstantMatrixVectorIndexing = 1, - }}; +constexpr TBuiltInResource DefaultTBuiltInResource = { + .maxLights = 32, + .maxClipPlanes = 6, + .maxTextureUnits = 32, + .maxTextureCoords = 32, + .maxVertexAttribs = 64, + .maxVertexUniformComponents = 4096, + .maxVaryingFloats = 64, + .maxVertexTextureImageUnits = 32, + .maxCombinedTextureImageUnits = 80, + .maxTextureImageUnits = 32, + .maxFragmentUniformComponents = 4096, + .maxDrawBuffers = 32, + .maxVertexUniformVectors = 128, + .maxVaryingVectors = 8, + .maxFragmentUniformVectors = 16, + .maxVertexOutputVectors = 16, + .maxFragmentInputVectors = 15, + .minProgramTexelOffset = -8, + .maxProgramTexelOffset = 7, + .maxClipDistances = 8, + .maxComputeWorkGroupCountX = 65535, + .maxComputeWorkGroupCountY = 65535, + .maxComputeWorkGroupCountZ = 65535, + .maxComputeWorkGroupSizeX = 1024, + .maxComputeWorkGroupSizeY = 1024, + .maxComputeWorkGroupSizeZ = 64, + .maxComputeUniformComponents = 1024, + .maxComputeTextureImageUnits = 16, + .maxComputeImageUniforms = 8, + .maxComputeAtomicCounters = 8, + .maxComputeAtomicCounterBuffers = 1, + .maxVaryingComponents = 60, + .maxVertexOutputComponents = 64, + .maxGeometryInputComponents = 64, + .maxGeometryOutputComponents = 128, + .maxFragmentInputComponents = 128, + .maxImageUnits = 8, + .maxCombinedImageUnitsAndFragmentOutputs = 8, + .maxCombinedShaderOutputResources = 8, + .maxImageSamples = 0, + .maxVertexImageUniforms = 0, + .maxTessControlImageUniforms = 0, + .maxTessEvaluationImageUniforms = 0, + .maxGeometryImageUniforms = 0, + .maxFragmentImageUniforms = 8, + .maxCombinedImageUniforms = 8, + .maxGeometryTextureImageUnits = 16, + .maxGeometryOutputVertices = 256, + .maxGeometryTotalOutputComponents = 1024, + .maxGeometryUniformComponents = 1024, + .maxGeometryVaryingComponents = 64, + .maxTessControlInputComponents = 128, + .maxTessControlOutputComponents = 128, + .maxTessControlTextureImageUnits = 16, + .maxTessControlUniformComponents = 1024, + .maxTessControlTotalOutputComponents = 4096, + .maxTessEvaluationInputComponents = 128, + .maxTessEvaluationOutputComponents = 128, + .maxTessEvaluationTextureImageUnits = 16, + .maxTessEvaluationUniformComponents = 1024, + .maxTessPatchComponents = 120, + .maxPatchVertices = 32, + .maxTessGenLevel = 64, + .maxViewports = 16, + .maxVertexAtomicCounters = 0, + .maxTessControlAtomicCounters = 0, + .maxTessEvaluationAtomicCounters = 0, + .maxGeometryAtomicCounters = 0, + .maxFragmentAtomicCounters = 8, + .maxCombinedAtomicCounters = 8, + .maxAtomicCounterBindings = 1, + .maxVertexAtomicCounterBuffers = 0, + .maxTessControlAtomicCounterBuffers = 0, + .maxTessEvaluationAtomicCounterBuffers = 0, + .maxGeometryAtomicCounterBuffers = 0, + .maxFragmentAtomicCounterBuffers = 1, + .maxCombinedAtomicCounterBuffers = 1, + .maxAtomicCounterBufferSize = 16384, + .maxTransformFeedbackBuffers = 4, + .maxTransformFeedbackInterleavedComponents = + 64, + .maxCullDistances = 8, + .maxCombinedClipAndCullDistances = 8, + .maxSamples = 4, + .maxMeshOutputVerticesNV = 256, + .maxMeshOutputPrimitivesNV = 512, + .maxMeshWorkGroupSizeX_NV = 32, + .maxMeshWorkGroupSizeY_NV = 1, + .maxMeshWorkGroupSizeZ_NV = 1, + .maxTaskWorkGroupSizeX_NV = 32, + .maxTaskWorkGroupSizeY_NV = 1, + .maxTaskWorkGroupSizeZ_NV = 1, + .maxMeshViewCountNV = 4, + .maxDualSourceDrawBuffersEXT = 1, + .limits = TLimits{ + .nonInductiveForLoops = 1, + .whileLoops = 1, + .doWhileLoops = 1, + .generalUniformIndexing = 1, + .generalAttributeMatrixVectorIndexing = 1, + .generalVaryingIndexing = 1, + .generalSamplerIndexing = 1, + .generalVariableIndexing = 1, + .generalConstantMatrixVectorIndexing = 1, + }, +}; EShLanguage ToEshShaderStage(vk::ShaderStageFlagBits stage) { switch (stage) { @@ -222,10 +224,13 @@ vk::ShaderModule Compile(std::string_view code, vk::ShaderStageFlagBits stage, v MICROPROFILE_DEFINE(Vulkan_SPVCompilation, "Vulkan", "SPIR-V Shader Compilation", MP_RGB(100, 255, 52)); -vk::ShaderModule CompileSPV(std::vector code, vk::Device device) { +vk::ShaderModule CompileSPV(std::span code, vk::Device device) { MICROPROFILE_SCOPE(Vulkan_SPVCompilation); - const vk::ShaderModuleCreateInfo shader_info = {.codeSize = code.size() * sizeof(u32), - .pCode = code.data()}; + const vk::ShaderModuleCreateInfo shader_info = { + .codeSize = code.size() * sizeof(u32), + .pCode = code.data(), + }; + try { return device.createShaderModule(shader_info); } catch (vk::SystemError& err) { diff --git a/src/video_core/renderer_vulkan/vk_shader_util.h b/src/video_core/renderer_vulkan/vk_shader_util.h index f21299948..7a19cd1d9 100644 --- a/src/video_core/renderer_vulkan/vk_shader_util.h +++ b/src/video_core/renderer_vulkan/vk_shader_util.h @@ -13,6 +13,6 @@ enum class ShaderOptimization { High = 0, Debug = 1 }; vk::ShaderModule Compile(std::string_view code, vk::ShaderStageFlagBits stage, vk::Device device, ShaderOptimization level); -vk::ShaderModule CompileSPV(std::vector code, vk::Device device); +vk::ShaderModule CompileSPV(std::span code, vk::Device device); } // namespace Vulkan diff --git a/src/video_core/shader/shader_uniforms.h b/src/video_core/shader/shader_uniforms.h index 3056f7d53..553d5c579 100644 --- a/src/video_core/shader/shader_uniforms.h +++ b/src/video_core/shader/shader_uniforms.h @@ -13,7 +13,7 @@ struct ShaderRegs; namespace Pica::Shader { -class ShaderSetup; +struct ShaderSetup; enum class UniformBindings : u32 { Common, VS, GS };