video_core: Move pixel format functions to cpp file
This commit is contained in:
@@ -29,6 +29,7 @@ add_library(video_core STATIC
|
||||
renderer_base.cpp
|
||||
renderer_base.h
|
||||
rasterizer_cache/morton_swizzle.h
|
||||
rasterizer_cache/pixel_format.cpp
|
||||
rasterizer_cache/pixel_format.h
|
||||
rasterizer_cache/rasterizer_cache.cpp
|
||||
rasterizer_cache/rasterizer_cache.h
|
||||
@@ -41,6 +42,8 @@ add_library(video_core STATIC
|
||||
renderer_opengl/frame_dumper_opengl.h
|
||||
renderer_opengl/gl_driver.cpp
|
||||
renderer_opengl/gl_driver.h
|
||||
renderer_opengl/gl_format_reinterpreter.cpp
|
||||
renderer_opengl/gl_format_reinterpreter.h
|
||||
renderer_opengl/gl_rasterizer.cpp
|
||||
renderer_opengl/gl_rasterizer.h
|
||||
renderer_opengl/gl_resource_manager.cpp
|
||||
@@ -68,8 +71,6 @@ add_library(video_core STATIC
|
||||
renderer_opengl/post_processing_opengl.h
|
||||
renderer_opengl/renderer_opengl.cpp
|
||||
renderer_opengl/renderer_opengl.h
|
||||
renderer_opengl/texture_downloader_es.cpp
|
||||
renderer_opengl/texture_downloader_es.h
|
||||
renderer_opengl/texture_filters/anime4k/anime4k_ultrafast.cpp
|
||||
renderer_opengl/texture_filters/anime4k/anime4k_ultrafast.h
|
||||
renderer_opengl/texture_filters/bicubic/bicubic.cpp
|
||||
@@ -83,9 +84,6 @@ add_library(video_core STATIC
|
||||
renderer_opengl/texture_filters/texture_filterer.h
|
||||
renderer_opengl/texture_filters/xbrz/xbrz_freescale.cpp
|
||||
renderer_opengl/texture_filters/xbrz/xbrz_freescale.h
|
||||
#temporary, move these back in alphabetical order before merging
|
||||
renderer_opengl/gl_format_reinterpreter.cpp
|
||||
renderer_opengl/gl_format_reinterpreter.h
|
||||
renderer_vulkan/pica_to_vk.h
|
||||
renderer_vulkan/renderer_vulkan.cpp
|
||||
renderer_vulkan/renderer_vulkan.h
|
||||
|
98
src/video_core/rasterizer_cache/pixel_format.cpp
Normal file
98
src/video_core/rasterizer_cache/pixel_format.cpp
Normal file
@@ -0,0 +1,98 @@
|
||||
// Copyright 2022 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "video_core/rasterizer_cache/pixel_format.h"
|
||||
|
||||
namespace VideoCore {
|
||||
|
||||
std::string_view PixelFormatAsString(PixelFormat format) {
|
||||
switch (format) {
|
||||
case PixelFormat::RGBA8:
|
||||
return "RGBA8";
|
||||
case PixelFormat::RGB8:
|
||||
return "RGB8";
|
||||
case PixelFormat::RGB5A1:
|
||||
return "RGB5A1";
|
||||
case PixelFormat::RGB565:
|
||||
return "RGB565";
|
||||
case PixelFormat::RGBA4:
|
||||
return "RGBA4";
|
||||
case PixelFormat::IA8:
|
||||
return "IA8";
|
||||
case PixelFormat::RG8:
|
||||
return "RG8";
|
||||
case PixelFormat::I8:
|
||||
return "I8";
|
||||
case PixelFormat::A8:
|
||||
return "A8";
|
||||
case PixelFormat::IA4:
|
||||
return "IA4";
|
||||
case PixelFormat::I4:
|
||||
return "I4";
|
||||
case PixelFormat::A4:
|
||||
return "A4";
|
||||
case PixelFormat::ETC1:
|
||||
return "ETC1";
|
||||
case PixelFormat::ETC1A4:
|
||||
return "ETC1A4";
|
||||
case PixelFormat::D16:
|
||||
return "D16";
|
||||
case PixelFormat::D24:
|
||||
return "D24";
|
||||
case PixelFormat::D24S8:
|
||||
return "D24S8";
|
||||
default:
|
||||
return "NotReal";
|
||||
}
|
||||
}
|
||||
|
||||
bool CheckFormatsBlittable(PixelFormat source_format, PixelFormat dest_format) {
|
||||
SurfaceType source_type = GetFormatType(source_format);
|
||||
SurfaceType dest_type = GetFormatType(dest_format);
|
||||
|
||||
if ((source_type == SurfaceType::Color || source_type == SurfaceType::Texture) &&
|
||||
(dest_type == SurfaceType::Color || dest_type == SurfaceType::Texture)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (source_type == SurfaceType::Depth && dest_type == SurfaceType::Depth) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (source_type == SurfaceType::DepthStencil && dest_type == SurfaceType::DepthStencil) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
PixelFormat PixelFormatFromTextureFormat(Pica::TexturingRegs::TextureFormat format) {
|
||||
const u32 format_index = static_cast<u32>(format);
|
||||
return (format_index < 14) ? static_cast<PixelFormat>(format) : PixelFormat::Invalid;
|
||||
}
|
||||
|
||||
PixelFormat PixelFormatFromColorFormat(Pica::FramebufferRegs::ColorFormat format) {
|
||||
const u32 format_index = static_cast<u32>(format);
|
||||
return (format_index < 5) ? static_cast<PixelFormat>(format) : PixelFormat::Invalid;
|
||||
}
|
||||
|
||||
PixelFormat PixelFormatFromDepthFormat(Pica::FramebufferRegs::DepthFormat format) {
|
||||
const u32 format_index = static_cast<u32>(format);
|
||||
return (format_index < 4) ? static_cast<PixelFormat>(format_index + 14) : PixelFormat::Invalid;
|
||||
}
|
||||
|
||||
PixelFormat PixelFormatFromGPUPixelFormat(GPU::Regs::PixelFormat format) {
|
||||
const u32 format_index = static_cast<u32>(format);
|
||||
switch (format) {
|
||||
// RGB565 and RGB5A1 are switched in PixelFormat compared to ColorFormat
|
||||
case GPU::Regs::PixelFormat::RGB565:
|
||||
return PixelFormat::RGB565;
|
||||
case GPU::Regs::PixelFormat::RGB5A1:
|
||||
return PixelFormat::RGB5A1;
|
||||
default:
|
||||
return (format_index < 5) ? static_cast<PixelFormat>(format) : PixelFormat::Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace VideoCore
|
@@ -11,16 +11,14 @@
|
||||
|
||||
namespace VideoCore {
|
||||
|
||||
constexpr u32 PIXEL_FORMAT_COUNT = 18;
|
||||
constexpr std::size_t PIXEL_FORMAT_COUNT = 18;
|
||||
|
||||
enum class PixelFormat : u32 {
|
||||
// First 5 formats are shared between textures and color buffers
|
||||
RGBA8 = 0,
|
||||
RGB8 = 1,
|
||||
RGB5A1 = 2,
|
||||
RGB565 = 3,
|
||||
RGBA4 = 4,
|
||||
// Texture-only formats
|
||||
IA8 = 5,
|
||||
RG8 = 6,
|
||||
I8 = 7,
|
||||
@@ -30,159 +28,77 @@ enum class PixelFormat : u32 {
|
||||
A4 = 11,
|
||||
ETC1 = 12,
|
||||
ETC1A4 = 13,
|
||||
// Depth buffer-only formats
|
||||
D16 = 14,
|
||||
D24 = 16,
|
||||
D24S8 = 17,
|
||||
Max = 18,
|
||||
Invalid = 255,
|
||||
};
|
||||
|
||||
enum class SurfaceType {
|
||||
enum class SurfaceType : u32 {
|
||||
Color = 0,
|
||||
Texture = 1,
|
||||
Depth = 2,
|
||||
DepthStencil = 3,
|
||||
Fill = 4,
|
||||
Invalid = 5
|
||||
Invalid = 5,
|
||||
};
|
||||
|
||||
constexpr std::string_view PixelFormatAsString(PixelFormat format) {
|
||||
switch (format) {
|
||||
case PixelFormat::RGBA8:
|
||||
return "RGBA8";
|
||||
case PixelFormat::RGB8:
|
||||
return "RGB8";
|
||||
case PixelFormat::RGB5A1:
|
||||
return "RGB5A1";
|
||||
case PixelFormat::RGB565:
|
||||
return "RGB565";
|
||||
case PixelFormat::RGBA4:
|
||||
return "RGBA4";
|
||||
case PixelFormat::IA8:
|
||||
return "IA8";
|
||||
case PixelFormat::RG8:
|
||||
return "RG8";
|
||||
case PixelFormat::I8:
|
||||
return "I8";
|
||||
case PixelFormat::A8:
|
||||
return "A8";
|
||||
case PixelFormat::IA4:
|
||||
return "IA4";
|
||||
case PixelFormat::I4:
|
||||
return "I4";
|
||||
case PixelFormat::A4:
|
||||
return "A4";
|
||||
case PixelFormat::ETC1:
|
||||
return "ETC1";
|
||||
case PixelFormat::ETC1A4:
|
||||
return "ETC1A4";
|
||||
case PixelFormat::D16:
|
||||
return "D16";
|
||||
case PixelFormat::D24:
|
||||
return "D24";
|
||||
case PixelFormat::D24S8:
|
||||
return "D24S8";
|
||||
default:
|
||||
return "NotReal";
|
||||
}
|
||||
}
|
||||
enum class TextureType : u32 {
|
||||
Texture2D = 0,
|
||||
CubeMap = 1,
|
||||
};
|
||||
|
||||
constexpr PixelFormat PixelFormatFromTextureFormat(Pica::TexturingRegs::TextureFormat format) {
|
||||
const u32 format_index = static_cast<u32>(format);
|
||||
return (format_index < 14) ? static_cast<PixelFormat>(format) : PixelFormat::Invalid;
|
||||
}
|
||||
|
||||
constexpr PixelFormat PixelFormatFromColorFormat(Pica::FramebufferRegs::ColorFormat format) {
|
||||
const u32 format_index = static_cast<u32>(format);
|
||||
return (format_index < 5) ? static_cast<PixelFormat>(format) : PixelFormat::Invalid;
|
||||
}
|
||||
|
||||
constexpr PixelFormat PixelFormatFromDepthFormat(Pica::FramebufferRegs::DepthFormat format) {
|
||||
const u32 format_index = static_cast<u32>(format);
|
||||
return (format_index < 4) ? static_cast<PixelFormat>(format_index + 14) : PixelFormat::Invalid;
|
||||
}
|
||||
|
||||
constexpr PixelFormat PixelFormatFromGPUPixelFormat(GPU::Regs::PixelFormat format) {
|
||||
const u32 format_index = static_cast<u32>(format);
|
||||
switch (format) {
|
||||
// RGB565 and RGB5A1 are switched in PixelFormat compared to ColorFormat
|
||||
case GPU::Regs::PixelFormat::RGB565:
|
||||
return PixelFormat::RGB565;
|
||||
case GPU::Regs::PixelFormat::RGB5A1:
|
||||
return PixelFormat::RGB5A1;
|
||||
default:
|
||||
return (format_index < 5) ? static_cast<PixelFormat>(format) : PixelFormat::Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr SurfaceType GetFormatType(PixelFormat pixel_format) {
|
||||
const u32 format_index = static_cast<u32>(pixel_format);
|
||||
if (format_index < 5) {
|
||||
return SurfaceType::Color;
|
||||
}
|
||||
|
||||
if (format_index < 14) {
|
||||
return SurfaceType::Texture;
|
||||
}
|
||||
|
||||
if (pixel_format == PixelFormat::D16 || pixel_format == PixelFormat::D24) {
|
||||
return SurfaceType::Depth;
|
||||
}
|
||||
|
||||
if (pixel_format == PixelFormat::D24S8) {
|
||||
return SurfaceType::DepthStencil;
|
||||
}
|
||||
|
||||
return SurfaceType::Invalid;
|
||||
}
|
||||
|
||||
constexpr bool CheckFormatsBlittable(PixelFormat source_format, PixelFormat dest_format) {
|
||||
SurfaceType source_type = GetFormatType(source_format);
|
||||
SurfaceType dest_type = GetFormatType(dest_format);
|
||||
|
||||
if ((source_type == SurfaceType::Color || source_type == SurfaceType::Texture) &&
|
||||
(dest_type == SurfaceType::Color || dest_type == SurfaceType::Texture)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (source_type == SurfaceType::Depth && dest_type == SurfaceType::Depth) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (source_type == SurfaceType::DepthStencil && dest_type == SurfaceType::DepthStencil) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
constexpr std::array<u8, PIXEL_FORMAT_COUNT> BITS_PER_BLOCK_TABLE = {{
|
||||
32, // RGBA8
|
||||
24, // RGB8
|
||||
16, // RGB5A1
|
||||
16, // RGB565
|
||||
16, // RGBA4
|
||||
16, // IA8
|
||||
16, // RG8
|
||||
8, // I8
|
||||
8, // A8
|
||||
8, // IA4
|
||||
4, // I4
|
||||
4, // A4
|
||||
4, // ETC1
|
||||
8, // ETC1A4
|
||||
16, // D16
|
||||
0,
|
||||
24, // D24
|
||||
32, // D24S8
|
||||
}};
|
||||
|
||||
constexpr u32 GetFormatBpp(PixelFormat format) {
|
||||
switch (format) {
|
||||
case PixelFormat::RGBA8:
|
||||
case PixelFormat::D24S8:
|
||||
return 32;
|
||||
case PixelFormat::RGB8:
|
||||
case PixelFormat::D24:
|
||||
return 24;
|
||||
case PixelFormat::RGB5A1:
|
||||
case PixelFormat::RGB565:
|
||||
case PixelFormat::RGBA4:
|
||||
case PixelFormat::IA8:
|
||||
case PixelFormat::RG8:
|
||||
case PixelFormat::D16:
|
||||
return 16;
|
||||
case PixelFormat::I8:
|
||||
case PixelFormat::A8:
|
||||
case PixelFormat::IA4:
|
||||
case PixelFormat::ETC1A4:
|
||||
return 8;
|
||||
case PixelFormat::I4:
|
||||
case PixelFormat::A4:
|
||||
case PixelFormat::ETC1:
|
||||
return 4;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
ASSERT(static_cast<std::size_t>(format) < BITS_PER_BLOCK_TABLE.size());
|
||||
return BITS_PER_BLOCK_TABLE[static_cast<std::size_t>(format)];
|
||||
}
|
||||
|
||||
constexpr std::array<SurfaceType, PIXEL_FORMAT_COUNT> FORMAT_TYPE_TABLE = {{
|
||||
SurfaceType::Color, // RGBA8
|
||||
SurfaceType::Color, // RGB8
|
||||
SurfaceType::Color, // RGB5A1
|
||||
SurfaceType::Color, // RGB565
|
||||
SurfaceType::Color, // RGBA4
|
||||
SurfaceType::Texture, // IA8
|
||||
SurfaceType::Texture, // RG8
|
||||
SurfaceType::Texture, // I8
|
||||
SurfaceType::Texture, // A8
|
||||
SurfaceType::Texture, // IA4
|
||||
SurfaceType::Texture, // I4
|
||||
SurfaceType::Texture, // A4
|
||||
SurfaceType::Texture, // ETC1
|
||||
SurfaceType::Texture, // ETC1A4
|
||||
SurfaceType::Depth, // D16
|
||||
SurfaceType::Invalid,
|
||||
SurfaceType::Depth, // D24
|
||||
SurfaceType::DepthStencil, // D24S8
|
||||
}};
|
||||
|
||||
constexpr SurfaceType GetFormatType(PixelFormat format) {
|
||||
ASSERT(static_cast<std::size_t>(format) < FORMAT_TYPE_TABLE.size());
|
||||
return FORMAT_TYPE_TABLE[static_cast<std::size_t>(format)];
|
||||
}
|
||||
|
||||
constexpr u32 GetBytesPerPixel(PixelFormat format) {
|
||||
@@ -194,4 +110,16 @@ constexpr u32 GetBytesPerPixel(PixelFormat format) {
|
||||
return GetFormatBpp(format) / 8;
|
||||
}
|
||||
|
||||
std::string_view PixelFormatAsString(PixelFormat format);
|
||||
|
||||
bool CheckFormatsBlittable(PixelFormat source_format, PixelFormat dest_format);
|
||||
|
||||
PixelFormat PixelFormatFromTextureFormat(Pica::TexturingRegs::TextureFormat format);
|
||||
|
||||
PixelFormat PixelFormatFromColorFormat(Pica::FramebufferRegs::ColorFormat format);
|
||||
|
||||
PixelFormat PixelFormatFromDepthFormat(Pica::FramebufferRegs::DepthFormat format);
|
||||
|
||||
PixelFormat PixelFormatFromGPUPixelFormat(GPU::Regs::PixelFormat format);
|
||||
|
||||
} // namespace VideoCore
|
||||
|
@@ -15,8 +15,6 @@ namespace VideoCore {
|
||||
|
||||
using SurfaceInterval = boost::icl::right_open_interval<PAddr>;
|
||||
|
||||
enum class TextureType { Texture2D = 0, CubeMap = 1 };
|
||||
|
||||
class SurfaceParams {
|
||||
public:
|
||||
/// Surface match traits
|
||||
|
@@ -1,10 +0,0 @@
|
||||
//? #version 320 es
|
||||
|
||||
out highp uint color;
|
||||
|
||||
uniform highp sampler2D depth;
|
||||
uniform int lod;
|
||||
|
||||
void main() {
|
||||
color = uint(texelFetch(depth, ivec2(gl_FragCoord.xy), lod).x * (exp2(32.0) - 1.0));
|
||||
}
|
@@ -1,8 +0,0 @@
|
||||
//? #version 320 es
|
||||
|
||||
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);
|
||||
}
|
@@ -1,9 +0,0 @@
|
||||
//? #version 320 es
|
||||
#extension GL_ARM_shader_framebuffer_fetch_depth_stencil : enable
|
||||
|
||||
out highp uint color;
|
||||
|
||||
void main() {
|
||||
color = uint(gl_LastFragDepthARM * (exp2(24.0) - 1.0)) << 8;
|
||||
color |= uint(gl_LastFragStencilARM);
|
||||
}
|
@@ -59,7 +59,7 @@ constexpr u32 DOWNLOAD_BUFFER_SIZE = 32 * 1024 * 1024;
|
||||
TextureRuntime::TextureRuntime(Driver& driver)
|
||||
: driver{driver}, filterer{Settings::values.texture_filter_name.GetValue(),
|
||||
VideoCore::GetResolutionScaleFactor()},
|
||||
downloader_es{false}, upload_buffer{GL_PIXEL_UNPACK_BUFFER, UPLOAD_BUFFER_SIZE},
|
||||
upload_buffer{GL_PIXEL_UNPACK_BUFFER, UPLOAD_BUFFER_SIZE},
|
||||
download_buffer{GL_PIXEL_PACK_BUFFER, DOWNLOAD_BUFFER_SIZE, true} {
|
||||
|
||||
read_fbo.Create();
|
||||
@@ -466,9 +466,9 @@ void Surface::ScaledDownload(const VideoCore::BufferTextureCopy& download,
|
||||
|
||||
const auto& tuple = runtime.GetFormatTuple(pixel_format);
|
||||
if (driver.IsOpenGLES()) {
|
||||
const auto& downloader_es = runtime.GetDownloaderES();
|
||||
downloader_es.GetTexImage(GL_TEXTURE_2D, 0, tuple.format, tuple.type, rect_height,
|
||||
rect_width, reinterpret_cast<void*>(staging.buffer_offset));
|
||||
runtime.BindFramebuffer(GL_READ_FRAMEBUFFER, 0, GL_TEXTURE_2D, type, unscaled_surface.texture);
|
||||
glReadPixels(0, 0, rect_width, rect_height, tuple.format, tuple.type,
|
||||
reinterpret_cast<void*>(staging.buffer_offset));
|
||||
} else {
|
||||
glGetTexImage(GL_TEXTURE_2D, 0, tuple.format, tuple.type,
|
||||
reinterpret_cast<void*>(staging.buffer_offset));
|
||||
|
@@ -9,7 +9,6 @@
|
||||
#include "video_core/rasterizer_cache/surface_base.h"
|
||||
#include "video_core/renderer_opengl/gl_format_reinterpreter.h"
|
||||
#include "video_core/renderer_opengl/gl_stream_buffer.h"
|
||||
#include "video_core/renderer_opengl/texture_downloader_es.h"
|
||||
#include "video_core/renderer_opengl/texture_filters/texture_filterer.h"
|
||||
|
||||
namespace OpenGL {
|
||||
@@ -87,11 +86,6 @@ private:
|
||||
return driver;
|
||||
}
|
||||
|
||||
/// Returns the class that handles texture downloads for OpenGL ES
|
||||
const TextureDownloaderES& GetDownloaderES() const {
|
||||
return downloader_es;
|
||||
}
|
||||
|
||||
/// Returns the class that handles texture filtering
|
||||
const TextureFilterer& GetFilterer() const {
|
||||
return filterer;
|
||||
@@ -100,7 +94,6 @@ private:
|
||||
private:
|
||||
Driver& driver;
|
||||
TextureFilterer filterer;
|
||||
TextureDownloaderES downloader_es;
|
||||
std::array<ReinterpreterList, VideoCore::PIXEL_FORMAT_COUNT> reinterpreters;
|
||||
std::unordered_multimap<VideoCore::HostTextureTag, OGLTexture> texture_recycler;
|
||||
OGLStreamBuffer upload_buffer, download_buffer;
|
||||
|
@@ -1,250 +0,0 @@
|
||||
// Copyright 2020 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <chrono>
|
||||
#include <vector>
|
||||
#include <fmt/chrono.h>
|
||||
#include "common/logging/log.h"
|
||||
#include "video_core/rasterizer_cache/utils.h"
|
||||
#include "video_core/renderer_opengl/gl_state.h"
|
||||
#include "video_core/renderer_opengl/gl_texture_runtime.h"
|
||||
#include "video_core/renderer_opengl/texture_downloader_es.h"
|
||||
|
||||
#include "shaders/depth_to_color.frag"
|
||||
#include "shaders/depth_to_color.vert"
|
||||
#include "shaders/ds_to_color.frag"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
static constexpr std::array DEPTH_TUPLES_HACK = {
|
||||
FormatTuple{GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT}, // D16
|
||||
FormatTuple{}, FormatTuple{GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT}, // D24
|
||||
FormatTuple{GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8}, // D24S8
|
||||
};
|
||||
|
||||
const FormatTuple& GetFormatTuple(VideoCore::PixelFormat format) {
|
||||
return DEPTH_TUPLES_HACK[static_cast<u32>(format)];
|
||||
}
|
||||
|
||||
void TextureDownloaderES::Test() {
|
||||
auto cur_state = OpenGLState::GetCurState();
|
||||
OpenGLState state;
|
||||
|
||||
{
|
||||
GLint range[2];
|
||||
GLint precision;
|
||||
#define PRECISION_TEST(type) \
|
||||
glGetShaderPrecisionFormat(GL_FRAGMENT_SHADER, type, range, &precision); \
|
||||
LOG_INFO(Render_OpenGL, #type " range: [{}, {}], precision: {}", range[0], range[1], precision);
|
||||
PRECISION_TEST(GL_LOW_INT);
|
||||
PRECISION_TEST(GL_MEDIUM_INT);
|
||||
PRECISION_TEST(GL_HIGH_INT);
|
||||
PRECISION_TEST(GL_LOW_FLOAT);
|
||||
PRECISION_TEST(GL_MEDIUM_FLOAT);
|
||||
PRECISION_TEST(GL_HIGH_FLOAT);
|
||||
#undef PRECISION_TEST
|
||||
}
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
|
||||
const auto test = [this, &state]<typename T>(FormatTuple tuple, std::vector<T> original_data,
|
||||
std::size_t tex_size, auto data_generator) {
|
||||
OGLTexture texture;
|
||||
texture.Create();
|
||||
state.texture_units[0].texture_2d = texture.handle;
|
||||
state.Apply();
|
||||
|
||||
original_data.resize(tex_size * tex_size);
|
||||
for (std::size_t idx = 0; idx < original_data.size(); ++idx) {
|
||||
original_data[idx] = data_generator(idx);
|
||||
}
|
||||
GLsizei tex_sizei = static_cast<GLsizei>(tex_size);
|
||||
glTexStorage2D(GL_TEXTURE_2D, 1, tuple.internal_format, tex_sizei, tex_sizei);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, tex_sizei, tex_sizei, tuple.format, tuple.type,
|
||||
original_data.data());
|
||||
|
||||
std::vector<T> new_data(original_data.size());
|
||||
glFinish();
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
GetTexImage(GL_TEXTURE_2D, 0, tuple.format, tuple.type, tex_sizei, tex_sizei,
|
||||
new_data.data());
|
||||
glFinish();
|
||||
auto time = std::chrono::high_resolution_clock::now() - start;
|
||||
LOG_INFO(Render_OpenGL, "test took {}", std::chrono::duration<double, std::milli>(time));
|
||||
|
||||
int diff = 0;
|
||||
for (std::size_t idx = 0; idx < original_data.size(); ++idx)
|
||||
if (new_data[idx] - original_data[idx] != diff) {
|
||||
diff = new_data[idx] - original_data[idx];
|
||||
// every time the error between the real and expected value changes, log it
|
||||
// some error is expected in D24 due to floating point precision
|
||||
LOG_WARNING(Render_OpenGL, "difference changed at {:#X}: {:#X} -> {:#X}", idx,
|
||||
original_data[idx], new_data[idx]);
|
||||
}
|
||||
};
|
||||
LOG_INFO(Render_OpenGL, "GL_DEPTH24_STENCIL8 download test starting");
|
||||
test(GetFormatTuple(VideoCore::PixelFormat::D24S8), std::vector<u32>{}, 4096,
|
||||
[](std::size_t idx) { return static_cast<u32>((idx << 8) | (idx & 0xFF)); });
|
||||
LOG_INFO(Render_OpenGL, "GL_DEPTH_COMPONENT24 download test starting");
|
||||
test(GetFormatTuple(VideoCore::PixelFormat::D24), std::vector<u32>{}, 4096,
|
||||
[](std::size_t idx) { return static_cast<u32>(idx << 8); });
|
||||
LOG_INFO(Render_OpenGL, "GL_DEPTH_COMPONENT16 download test starting");
|
||||
test(GetFormatTuple(VideoCore::PixelFormat::D16), std::vector<u16>{}, 256,
|
||||
[](std::size_t idx) { return static_cast<u16>(idx); });
|
||||
|
||||
cur_state.Apply();
|
||||
}
|
||||
|
||||
TextureDownloaderES::TextureDownloaderES(bool enable_depth_stencil) {
|
||||
vao.Create();
|
||||
read_fbo_generic.Create();
|
||||
|
||||
depth32_fbo.Create();
|
||||
r32ui_renderbuffer.Create();
|
||||
depth16_fbo.Create();
|
||||
r16_renderbuffer.Create();
|
||||
|
||||
const auto init_program = [](ConversionShader& converter, std::string_view frag) {
|
||||
converter.program.Create(depth_to_color_vert.data(), frag.data());
|
||||
converter.lod_location = glGetUniformLocation(converter.program.handle, "lod");
|
||||
};
|
||||
|
||||
// xperia64: The depth stencil shader currently uses a GLES extension that is not supported
|
||||
// across all devices Reportedly broken on Tegra devices and the Nexus 6P, so enabling it can be
|
||||
// toggled
|
||||
if (enable_depth_stencil) {
|
||||
init_program(d24s8_r32ui_conversion_shader, ds_to_color_frag);
|
||||
}
|
||||
|
||||
init_program(d24_r32ui_conversion_shader, depth_to_color_frag);
|
||||
init_program(d16_r16_conversion_shader, R"(
|
||||
out highp float color;
|
||||
|
||||
uniform highp sampler2D depth;
|
||||
uniform int lod;
|
||||
|
||||
void main(){
|
||||
color = texelFetch(depth, ivec2(gl_FragCoord.xy), lod).x;
|
||||
}
|
||||
)");
|
||||
|
||||
sampler.Create();
|
||||
glSamplerParameteri(sampler.handle, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glSamplerParameteri(sampler.handle, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
|
||||
auto cur_state = OpenGLState::GetCurState();
|
||||
auto state = cur_state;
|
||||
|
||||
state.draw.shader_program = d24s8_r32ui_conversion_shader.program.handle;
|
||||
state.draw.draw_framebuffer = depth32_fbo.handle;
|
||||
state.renderbuffer = r32ui_renderbuffer.handle;
|
||||
state.Apply();
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_R32UI, MAX_SIZE, MAX_SIZE);
|
||||
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
|
||||
r32ui_renderbuffer.handle);
|
||||
|
||||
state.draw.draw_framebuffer = depth16_fbo.handle;
|
||||
state.renderbuffer = r16_renderbuffer.handle;
|
||||
state.Apply();
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_R16, MAX_SIZE, MAX_SIZE);
|
||||
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
|
||||
r16_renderbuffer.handle);
|
||||
|
||||
cur_state.Apply();
|
||||
}
|
||||
|
||||
GLuint TextureDownloaderES::ConvertDepthToColor(GLuint level, GLenum& format, GLenum& type,
|
||||
GLint height, GLint width) const {
|
||||
ASSERT(width <= MAX_SIZE && height <= MAX_SIZE);
|
||||
const OpenGLState cur_state = OpenGLState::GetCurState();
|
||||
OpenGLState state;
|
||||
state.texture_units[0] = {cur_state.texture_units[0].texture_2d, sampler.handle};
|
||||
state.draw.vertex_array = vao.handle;
|
||||
|
||||
OGLTexture texture_view;
|
||||
const ConversionShader* converter = nullptr;
|
||||
switch (type) {
|
||||
case GL_UNSIGNED_SHORT:
|
||||
state.draw.draw_framebuffer = depth16_fbo.handle;
|
||||
converter = &d16_r16_conversion_shader;
|
||||
format = GL_RED;
|
||||
break;
|
||||
case GL_UNSIGNED_INT:
|
||||
state.draw.draw_framebuffer = depth32_fbo.handle;
|
||||
converter = &d24_r32ui_conversion_shader;
|
||||
format = GL_RED_INTEGER;
|
||||
break;
|
||||
case GL_UNSIGNED_INT_24_8:
|
||||
state.draw.draw_framebuffer = depth32_fbo.handle;
|
||||
converter = &d24s8_r32ui_conversion_shader;
|
||||
format = GL_RED_INTEGER;
|
||||
type = GL_UNSIGNED_INT;
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE_MSG("Destination type not recognized");
|
||||
}
|
||||
|
||||
state.draw.shader_program = converter->program.handle;
|
||||
state.viewport = {0, 0, width, height};
|
||||
state.Apply();
|
||||
if (converter->program.handle == d24s8_r32ui_conversion_shader.program.handle) {
|
||||
// TODO BreadFish64: the ARM framebuffer reading extension is probably not the most optimal
|
||||
// way to do this, search for another solution
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
|
||||
state.texture_units[0].texture_2d, level);
|
||||
}
|
||||
|
||||
glUniform1i(converter->lod_location, level);
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
if (texture_view.handle) {
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_DEPTH_COMPONENT);
|
||||
}
|
||||
return state.draw.draw_framebuffer;
|
||||
}
|
||||
|
||||
void TextureDownloaderES::GetTexImage(GLenum target, GLuint level, GLenum format, GLenum type,
|
||||
GLint height, GLint width, void* pixels) const {
|
||||
OpenGLState state = OpenGLState::GetCurState();
|
||||
GLuint texture{};
|
||||
const GLuint old_read_buffer = state.draw.read_framebuffer;
|
||||
switch (target) {
|
||||
case GL_TEXTURE_2D:
|
||||
texture = state.texture_units[0].texture_2d;
|
||||
break;
|
||||
case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
|
||||
case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
|
||||
case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
|
||||
case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
|
||||
case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
|
||||
case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
|
||||
texture = state.texture_cube_unit.texture_cube;
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unexpected target {:x}", target);
|
||||
}
|
||||
|
||||
switch (format) {
|
||||
case GL_DEPTH_COMPONENT:
|
||||
case GL_DEPTH_STENCIL:
|
||||
// unfortunately, the accurate way is too slow for release
|
||||
return;
|
||||
state.draw.read_framebuffer = ConvertDepthToColor(level, format, type, height, width);
|
||||
state.Apply();
|
||||
break;
|
||||
default:
|
||||
state.draw.read_framebuffer = read_fbo_generic.handle;
|
||||
state.Apply();
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture,
|
||||
level);
|
||||
}
|
||||
GLenum status = glCheckFramebufferStatus(GL_READ_FRAMEBUFFER);
|
||||
if (status != GL_FRAMEBUFFER_COMPLETE) {
|
||||
LOG_DEBUG(Render_OpenGL, "Framebuffer is incomplete, status: {:X}", status);
|
||||
}
|
||||
glReadPixels(0, 0, width, height, format, type, pixels);
|
||||
|
||||
state.draw.read_framebuffer = old_read_buffer;
|
||||
state.Apply();
|
||||
}
|
||||
|
||||
} // namespace OpenGL
|
@@ -1,57 +0,0 @@
|
||||
// Copyright 2020 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
class OpenGLState;
|
||||
|
||||
class TextureDownloaderES {
|
||||
static constexpr u16 MAX_SIZE = 1024;
|
||||
|
||||
public:
|
||||
TextureDownloaderES(bool enable_depth_stencil);
|
||||
|
||||
/**
|
||||
* OpenGL ES does not support glGetTexImage. Obtain the pixels by attaching the
|
||||
* texture to a framebuffer.
|
||||
* Originally from https://github.com/apitrace/apitrace/blob/master/retrace/glstate_images.cpp
|
||||
* Depth texture download assumes that the texture's format tuple matches what is found
|
||||
* OpenGL::depth_format_tuples
|
||||
*/
|
||||
void GetTexImage(GLenum target, GLuint level, GLenum format, const GLenum type, GLint height,
|
||||
GLint width, void* pixels) const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* OpenGL ES does not support glReadBuffer for depth/stencil formats.
|
||||
* This gets around it by converting to a Red surface before downloading
|
||||
*/
|
||||
GLuint ConvertDepthToColor(GLuint level, GLenum& format, GLenum& type, GLint height,
|
||||
GLint width) const;
|
||||
|
||||
/// Self tests for the texture downloader
|
||||
void Test();
|
||||
|
||||
private:
|
||||
struct ConversionShader {
|
||||
OGLProgram program;
|
||||
GLint lod_location{-1};
|
||||
};
|
||||
|
||||
OGLVertexArray vao;
|
||||
OGLFramebuffer read_fbo_generic;
|
||||
OGLFramebuffer depth32_fbo, depth16_fbo;
|
||||
OGLRenderbuffer r32ui_renderbuffer, r16_renderbuffer;
|
||||
|
||||
ConversionShader d24_r32ui_conversion_shader;
|
||||
ConversionShader d16_r16_conversion_shader;
|
||||
ConversionShader d24s8_r32ui_conversion_shader;
|
||||
OGLSampler sampler;
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
Reference in New Issue
Block a user