Merge branch 'master' into Texture2DArray

This commit is contained in:
greggameplayer 2018-11-02 14:26:32 +01:00 committed by GitHub
commit cb8e4a4633
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 1480 additions and 1069 deletions

View file

@ -153,6 +153,7 @@ struct VisitorInterface : NonCopyable {
/// Completion method, called once all fields have been visited /// Completion method, called once all fields have been visited
virtual void Complete() = 0; virtual void Complete() = 0;
virtual bool SubmitTestcase() = 0;
}; };
/** /**
@ -178,6 +179,9 @@ struct NullVisitor : public VisitorInterface {
void Visit(const Field<std::chrono::microseconds>& /*field*/) override {} void Visit(const Field<std::chrono::microseconds>& /*field*/) override {}
void Complete() override {} void Complete() override {}
bool SubmitTestcase() override {
return false;
}
}; };
/// Appends build-specific information to the given FieldCollection, /// Appends build-specific information to the given FieldCollection,

View file

@ -57,7 +57,8 @@ struct UUID {
}; };
static_assert(sizeof(UUID) == 16, "UUID is an invalid size!"); static_assert(sizeof(UUID) == 16, "UUID is an invalid size!");
using ProfileUsername = std::array<u8, 0x20>; constexpr std::size_t profile_username_size = 32;
using ProfileUsername = std::array<u8, profile_username_size>;
using ProfileData = std::array<u8, MAX_DATA>; using ProfileData = std::array<u8, MAX_DATA>;
using UserIDArray = std::array<UUID, MAX_USERS>; using UserIDArray = std::array<UUID, MAX_USERS>;

View file

@ -132,11 +132,11 @@ public:
// clang-format off // clang-format off
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
{0, nullptr, "BindNoticeEvent"}, {0, nullptr, "BindNoticeEvent"},
{1, nullptr, "Unknown1"}, {1, nullptr, "UnbindNoticeEvent"},
{2, nullptr, "GetStatus"}, {2, nullptr, "GetStatus"},
{3, nullptr, "GetNotice"}, {3, nullptr, "GetNotice"},
{4, nullptr, "Unknown2"}, {4, nullptr, "EnablePowerRequestNotice"},
{5, nullptr, "Unknown3"}, {5, nullptr, "DisablePowerRequestNotice"},
{6, nullptr, "ReplyPowerRequest"}, {6, nullptr, "ReplyPowerRequest"},
}; };
// clang-format on // clang-format on

View file

@ -184,4 +184,13 @@ TelemetrySession::~TelemetrySession() {
backend = nullptr; backend = nullptr;
} }
bool TelemetrySession::SubmitTestcase() {
#ifdef ENABLE_WEB_SERVICE
field_collection.Accept(*backend);
return backend->SubmitTestcase();
#else
return false;
#endif
}
} // namespace Core } // namespace Core

View file

@ -31,6 +31,12 @@ public:
field_collection.AddField(type, name, std::move(value)); field_collection.AddField(type, name, std::move(value));
} }
/**
* Submits a Testcase.
* @returns A bool indicating whether the submission succeeded
*/
bool SubmitTestcase();
private: private:
Telemetry::FieldCollection field_collection; ///< Tracks all added fields for the session Telemetry::FieldCollection field_collection; ///< Tracks all added fields for the session
std::unique_ptr<Telemetry::VisitorInterface> backend; ///< Backend interface that logs fields std::unique_ptr<Telemetry::VisitorInterface> backend; ///< Backend interface that logs fields

View file

@ -53,6 +53,8 @@ add_library(video_core STATIC
renderer_opengl/renderer_opengl.h renderer_opengl/renderer_opengl.h
renderer_opengl/utils.cpp renderer_opengl/utils.cpp
renderer_opengl/utils.h renderer_opengl/utils.h
surface.cpp
surface.h
textures/astc.cpp textures/astc.cpp
textures/astc.h textures/astc.h
textures/decoders.cpp textures/decoders.cpp

View file

@ -43,15 +43,17 @@ void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) {
// Reset the current macro. // Reset the current macro.
executing_macro = 0; executing_macro = 0;
// The requested macro must have been uploaded already. // Lookup the macro offset
auto macro_code = uploaded_macros.find(method); const u32 entry{(method - MacroRegistersStart) >> 1};
if (macro_code == uploaded_macros.end()) { const auto& search{macro_offsets.find(entry)};
LOG_ERROR(HW_GPU, "Macro {:04X} was not uploaded", method); if (search == macro_offsets.end()) {
LOG_CRITICAL(HW_GPU, "macro not found for method 0x{:X}!", method);
UNREACHABLE();
return; return;
} }
// Execute the current macro. // Execute the current macro.
macro_interpreter.Execute(macro_code->second, std::move(parameters)); macro_interpreter.Execute(search->second, std::move(parameters));
} }
void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) { void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) {
@ -97,6 +99,10 @@ void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) {
ProcessMacroUpload(value); ProcessMacroUpload(value);
break; break;
} }
case MAXWELL3D_REG_INDEX(macros.bind): {
ProcessMacroBind(value);
break;
}
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[0]): case MAXWELL3D_REG_INDEX(const_buffer.cb_data[0]):
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[1]): case MAXWELL3D_REG_INDEX(const_buffer.cb_data[1]):
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[2]): case MAXWELL3D_REG_INDEX(const_buffer.cb_data[2]):
@ -158,9 +164,13 @@ void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) {
} }
void Maxwell3D::ProcessMacroUpload(u32 data) { void Maxwell3D::ProcessMacroUpload(u32 data) {
// Store the uploaded macro code to interpret them when they're called. ASSERT_MSG(regs.macros.upload_address < macro_memory.size(),
auto& macro = uploaded_macros[regs.macros.entry * 2 + MacroRegistersStart]; "upload_address exceeded macro_memory size!");
macro.push_back(data); macro_memory[regs.macros.upload_address++] = data;
}
void Maxwell3D::ProcessMacroBind(u32 data) {
macro_offsets[regs.macros.entry] = data;
} }
void Maxwell3D::ProcessQueryGet() { void Maxwell3D::ProcessQueryGet() {

View file

@ -475,12 +475,13 @@ public:
INSERT_PADDING_WORDS(0x45); INSERT_PADDING_WORDS(0x45);
struct { struct {
INSERT_PADDING_WORDS(1); u32 upload_address;
u32 data; u32 data;
u32 entry; u32 entry;
u32 bind;
} macros; } macros;
INSERT_PADDING_WORDS(0x189); INSERT_PADDING_WORDS(0x188);
u32 tfb_enabled; u32 tfb_enabled;
@ -994,12 +995,25 @@ public:
/// Returns the texture information for a specific texture in a specific shader stage. /// Returns the texture information for a specific texture in a specific shader stage.
Texture::FullTextureInfo GetStageTexture(Regs::ShaderStage stage, std::size_t offset) const; Texture::FullTextureInfo GetStageTexture(Regs::ShaderStage stage, std::size_t offset) const;
/// Memory for macro code - it's undetermined how big this is, however 1MB is much larger than
/// we've seen used.
using MacroMemory = std::array<u32, 0x40000>;
/// Gets a reference to macro memory.
const MacroMemory& GetMacroMemory() const {
return macro_memory;
}
private: private:
void InitializeRegisterDefaults(); void InitializeRegisterDefaults();
VideoCore::RasterizerInterface& rasterizer; VideoCore::RasterizerInterface& rasterizer;
std::unordered_map<u32, std::vector<u32>> uploaded_macros; /// Start offsets of each macro in macro_memory
std::unordered_map<u32, u32> macro_offsets;
/// Memory for macro code
MacroMemory macro_memory;
/// Macro method that is currently being executed / being fed parameters. /// Macro method that is currently being executed / being fed parameters.
u32 executing_macro = 0; u32 executing_macro = 0;
@ -1022,9 +1036,12 @@ private:
*/ */
void CallMacroMethod(u32 method, std::vector<u32> parameters); void CallMacroMethod(u32 method, std::vector<u32> parameters);
/// Handles writes to the macro uploading registers. /// Handles writes to the macro uploading register.
void ProcessMacroUpload(u32 data); void ProcessMacroUpload(u32 data);
/// Handles writes to the macro bind register.
void ProcessMacroBind(u32 data);
/// Handles a write to the CLEAR_BUFFERS register. /// Handles a write to the CLEAR_BUFFERS register.
void ProcessClearBuffers(); void ProcessClearBuffers();

View file

@ -577,6 +577,10 @@ union Instruction {
BitField<55, 1, u64> saturate; BitField<55, 1, u64> saturate;
} fmul32; } fmul32;
union {
BitField<52, 1, u64> generates_cc;
} op_32;
union { union {
BitField<48, 1, u64> is_signed; BitField<48, 1, u64> is_signed;
} shift; } shift;
@ -1231,6 +1235,7 @@ union Instruction {
BitField<60, 1, u64> is_b_gpr; BitField<60, 1, u64> is_b_gpr;
BitField<59, 1, u64> is_c_gpr; BitField<59, 1, u64> is_c_gpr;
BitField<20, 24, s64> smem_imm; BitField<20, 24, s64> smem_imm;
BitField<0, 5, ControlCode> flow_control_code;
Attribute attribute; Attribute attribute;
Sampler sampler; Sampler sampler;

View file

@ -11,7 +11,7 @@ namespace Tegra {
MacroInterpreter::MacroInterpreter(Engines::Maxwell3D& maxwell3d) : maxwell3d(maxwell3d) {} MacroInterpreter::MacroInterpreter(Engines::Maxwell3D& maxwell3d) : maxwell3d(maxwell3d) {}
void MacroInterpreter::Execute(const std::vector<u32>& code, std::vector<u32> parameters) { void MacroInterpreter::Execute(u32 offset, std::vector<u32> parameters) {
Reset(); Reset();
registers[1] = parameters[0]; registers[1] = parameters[0];
this->parameters = std::move(parameters); this->parameters = std::move(parameters);
@ -19,7 +19,7 @@ void MacroInterpreter::Execute(const std::vector<u32>& code, std::vector<u32> pa
// Execute the code until we hit an exit condition. // Execute the code until we hit an exit condition.
bool keep_executing = true; bool keep_executing = true;
while (keep_executing) { while (keep_executing) {
keep_executing = Step(code, false); keep_executing = Step(offset, false);
} }
// Assert the the macro used all the input parameters // Assert the the macro used all the input parameters
@ -37,10 +37,10 @@ void MacroInterpreter::Reset() {
next_parameter_index = 1; next_parameter_index = 1;
} }
bool MacroInterpreter::Step(const std::vector<u32>& code, bool is_delay_slot) { bool MacroInterpreter::Step(u32 offset, bool is_delay_slot) {
u32 base_address = pc; u32 base_address = pc;
Opcode opcode = GetOpcode(code); Opcode opcode = GetOpcode(offset);
pc += 4; pc += 4;
// Update the program counter if we were delayed // Update the program counter if we were delayed
@ -108,7 +108,7 @@ bool MacroInterpreter::Step(const std::vector<u32>& code, bool is_delay_slot) {
delayed_pc = base_address + opcode.GetBranchTarget(); delayed_pc = base_address + opcode.GetBranchTarget();
// Execute one more instruction due to the delay slot. // Execute one more instruction due to the delay slot.
return Step(code, true); return Step(offset, true);
} }
break; break;
} }
@ -121,17 +121,18 @@ bool MacroInterpreter::Step(const std::vector<u32>& code, bool is_delay_slot) {
// Exit has a delay slot, execute the next instruction // Exit has a delay slot, execute the next instruction
// Note: Executing an exit during a branch delay slot will cause the instruction at the // Note: Executing an exit during a branch delay slot will cause the instruction at the
// branch target to be executed before exiting. // branch target to be executed before exiting.
Step(code, true); Step(offset, true);
return false; return false;
} }
return true; return true;
} }
MacroInterpreter::Opcode MacroInterpreter::GetOpcode(const std::vector<u32>& code) const { MacroInterpreter::Opcode MacroInterpreter::GetOpcode(u32 offset) const {
const auto& macro_memory{maxwell3d.GetMacroMemory()};
ASSERT((pc % sizeof(u32)) == 0); ASSERT((pc % sizeof(u32)) == 0);
ASSERT(pc < code.size() * sizeof(u32)); ASSERT((pc + offset) < macro_memory.size() * sizeof(u32));
return {code[pc / sizeof(u32)]}; return {macro_memory[offset + pc / sizeof(u32)]};
} }
u32 MacroInterpreter::GetALUResult(ALUOperation operation, u32 src_a, u32 src_b) const { u32 MacroInterpreter::GetALUResult(ALUOperation operation, u32 src_a, u32 src_b) const {

View file

@ -22,10 +22,10 @@ public:
/** /**
* Executes the macro code with the specified input parameters. * Executes the macro code with the specified input parameters.
* @param code The macro byte code to execute * @param offset Offset to start execution at.
* @param parameters The parameters of the macro * @param parameters The parameters of the macro.
*/ */
void Execute(const std::vector<u32>& code, std::vector<u32> parameters); void Execute(u32 offset, std::vector<u32> parameters);
private: private:
enum class Operation : u32 { enum class Operation : u32 {
@ -110,11 +110,11 @@ private:
/** /**
* Executes a single macro instruction located at the current program counter. Returns whether * Executes a single macro instruction located at the current program counter. Returns whether
* the interpreter should keep running. * the interpreter should keep running.
* @param code The macro code to execute. * @param offset Offset to start execution at.
* @param is_delay_slot Whether the current step is being executed due to a delay slot in a * @param is_delay_slot Whether the current step is being executed due to a delay slot in a
* previous instruction. * previous instruction.
*/ */
bool Step(const std::vector<u32>& code, bool is_delay_slot); bool Step(u32 offset, bool is_delay_slot);
/// Calculates the result of an ALU operation. src_a OP src_b; /// Calculates the result of an ALU operation. src_a OP src_b;
u32 GetALUResult(ALUOperation operation, u32 src_a, u32 src_b) const; u32 GetALUResult(ALUOperation operation, u32 src_a, u32 src_b) const;
@ -127,7 +127,7 @@ private:
bool EvaluateBranchCondition(BranchCondition cond, u32 value) const; bool EvaluateBranchCondition(BranchCondition cond, u32 value) const;
/// Reads an opcode at the current program counter location. /// Reads an opcode at the current program counter location.
Opcode GetOpcode(const std::vector<u32>& code) const; Opcode GetOpcode(u32 offset) const;
/// Returns the specified register's value. Register 0 is hardcoded to always return 0. /// Returns the specified register's value. Register 0 is hardcoded to always return 0.
u32 GetRegister(u32 register_id) const; u32 GetRegister(u32 register_id) const;

View file

@ -30,8 +30,8 @@
namespace OpenGL { namespace OpenGL {
using Maxwell = Tegra::Engines::Maxwell3D::Regs; using Maxwell = Tegra::Engines::Maxwell3D::Regs;
using PixelFormat = SurfaceParams::PixelFormat; using PixelFormat = VideoCore::Surface::PixelFormat;
using SurfaceType = SurfaceParams::SurfaceType; using SurfaceType = VideoCore::Surface::SurfaceType;
MICROPROFILE_DEFINE(OpenGL_VAO, "OpenGL", "Vertex Array Setup", MP_RGB(128, 128, 192)); MICROPROFILE_DEFINE(OpenGL_VAO, "OpenGL", "Vertex Array Setup", MP_RGB(128, 128, 192));
MICROPROFILE_DEFINE(OpenGL_Shader, "OpenGL", "Shader Setup", MP_RGB(128, 128, 192)); MICROPROFILE_DEFINE(OpenGL_Shader, "OpenGL", "Shader Setup", MP_RGB(128, 128, 192));
@ -104,7 +104,7 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo
} }
ASSERT_MSG(has_ARB_separate_shader_objects, "has_ARB_separate_shader_objects is unsupported"); ASSERT_MSG(has_ARB_separate_shader_objects, "has_ARB_separate_shader_objects is unsupported");
OpenGLState::ApplyDefaultState();
// Clipping plane 0 is always enabled for PICA fixed clip plane z <= 0 // Clipping plane 0 is always enabled for PICA fixed clip plane z <= 0
state.clip_distance[0] = true; state.clip_distance[0] = true;
@ -115,8 +115,6 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo
state.draw.shader_program = 0; state.draw.shader_program = 0;
state.Apply(); state.Apply();
glEnable(GL_BLEND);
glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &uniform_buffer_alignment); glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &uniform_buffer_alignment);
LOG_CRITICAL(Render_OpenGL, "Sync fixed function OpenGL state here!"); LOG_CRITICAL(Render_OpenGL, "Sync fixed function OpenGL state here!");
@ -703,7 +701,8 @@ bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& config,
// Verify that the cached surface is the same size and format as the requested framebuffer // Verify that the cached surface is the same size and format as the requested framebuffer
const auto& params{surface->GetSurfaceParams()}; const auto& params{surface->GetSurfaceParams()};
const auto& pixel_format{SurfaceParams::PixelFormatFromGPUPixelFormat(config.pixel_format)}; const auto& pixel_format{
VideoCore::Surface::PixelFormatFromGPUPixelFormat(config.pixel_format)};
ASSERT_MSG(params.width == config.width, "Framebuffer width is different"); ASSERT_MSG(params.width == config.width, "Framebuffer width is different");
ASSERT_MSG(params.height == config.height, "Framebuffer height is different"); ASSERT_MSG(params.height == config.height, "Framebuffer height is different");
ASSERT_MSG(params.pixel_format == pixel_format, "Framebuffer pixel_format is different"); ASSERT_MSG(params.pixel_format == pixel_format, "Framebuffer pixel_format is different");

View file

@ -17,15 +17,20 @@
#include "video_core/engines/maxwell_3d.h" #include "video_core/engines/maxwell_3d.h"
#include "video_core/renderer_opengl/gl_rasterizer_cache.h" #include "video_core/renderer_opengl/gl_rasterizer_cache.h"
#include "video_core/renderer_opengl/utils.h" #include "video_core/renderer_opengl/utils.h"
#include "video_core/surface.h"
#include "video_core/textures/astc.h" #include "video_core/textures/astc.h"
#include "video_core/textures/decoders.h" #include "video_core/textures/decoders.h"
#include "video_core/utils.h" #include "video_core/utils.h"
namespace OpenGL { namespace OpenGL {
using SurfaceType = SurfaceParams::SurfaceType; using VideoCore::Surface::ComponentTypeFromDepthFormat;
using PixelFormat = SurfaceParams::PixelFormat; using VideoCore::Surface::ComponentTypeFromRenderTarget;
using ComponentType = SurfaceParams::ComponentType; using VideoCore::Surface::ComponentTypeFromTexture;
using VideoCore::Surface::PixelFormatFromDepthFormat;
using VideoCore::Surface::PixelFormatFromRenderTargetFormat;
using VideoCore::Surface::PixelFormatFromTextureFormat;
using VideoCore::Surface::SurfaceTargetFromTextureType;
struct FormatTuple { struct FormatTuple {
GLint internal_format; GLint internal_format;
@ -35,46 +40,6 @@ struct FormatTuple {
bool compressed; bool compressed;
}; };
static bool IsPixelFormatASTC(PixelFormat format) {
switch (format) {
case PixelFormat::ASTC_2D_4X4:
case PixelFormat::ASTC_2D_5X4:
case PixelFormat::ASTC_2D_8X8:
case PixelFormat::ASTC_2D_8X5:
case PixelFormat::ASTC_2D_4X4_SRGB:
case PixelFormat::ASTC_2D_5X4_SRGB:
case PixelFormat::ASTC_2D_8X8_SRGB:
case PixelFormat::ASTC_2D_8X5_SRGB:
return true;
default:
return false;
}
}
static std::pair<u32, u32> GetASTCBlockSize(PixelFormat format) {
switch (format) {
case PixelFormat::ASTC_2D_4X4:
return {4, 4};
case PixelFormat::ASTC_2D_5X4:
return {5, 4};
case PixelFormat::ASTC_2D_8X8:
return {8, 8};
case PixelFormat::ASTC_2D_8X5:
return {8, 5};
case PixelFormat::ASTC_2D_4X4_SRGB:
return {4, 4};
case PixelFormat::ASTC_2D_5X4_SRGB:
return {5, 4};
case PixelFormat::ASTC_2D_8X8_SRGB:
return {8, 8};
case PixelFormat::ASTC_2D_8X5_SRGB:
return {8, 5};
default:
LOG_CRITICAL(HW_GPU, "Unhandled format: {}", static_cast<u32>(format));
UNREACHABLE();
}
}
void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) { void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) {
auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()}; auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()};
const auto cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr_)}; const auto cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr_)};
@ -267,7 +232,7 @@ std::size_t SurfaceParams::InnerMemorySize(bool force_gl, bool layer_only,
return params; return params;
} }
static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_format_tuples = {{ static constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> tex_format_tuples = {{
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, ComponentType::UNorm, false}, // ABGR8U {GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, ComponentType::UNorm, false}, // ABGR8U
{GL_RGBA8, GL_RGBA, GL_BYTE, ComponentType::SNorm, false}, // ABGR8S {GL_RGBA8, GL_RGBA, GL_BYTE, ComponentType::SNorm, false}, // ABGR8S
{GL_RGBA8UI, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE, ComponentType::UInt, false}, // ABGR8UI {GL_RGBA8UI, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE, ComponentType::UInt, false}, // ABGR8UI
@ -355,19 +320,19 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form
ComponentType::Float, false}, // Z32FS8 ComponentType::Float, false}, // Z32FS8
}}; }};
static GLenum SurfaceTargetToGL(SurfaceParams::SurfaceTarget target) { static GLenum SurfaceTargetToGL(SurfaceTarget target) {
switch (target) { switch (target) {
case SurfaceParams::SurfaceTarget::Texture1D: case SurfaceTarget::Texture1D:
return GL_TEXTURE_1D; return GL_TEXTURE_1D;
case SurfaceParams::SurfaceTarget::Texture2D: case SurfaceTarget::Texture2D:
return GL_TEXTURE_2D; return GL_TEXTURE_2D;
case SurfaceParams::SurfaceTarget::Texture3D: case SurfaceTarget::Texture3D:
return GL_TEXTURE_3D; return GL_TEXTURE_3D;
case SurfaceParams::SurfaceTarget::Texture1DArray: case SurfaceTarget::Texture1DArray:
return GL_TEXTURE_1D_ARRAY; return GL_TEXTURE_1D_ARRAY;
case SurfaceParams::SurfaceTarget::Texture2DArray: case SurfaceTarget::Texture2DArray:
return GL_TEXTURE_2D_ARRAY; return GL_TEXTURE_2D_ARRAY;
case SurfaceParams::SurfaceTarget::TextureCubemap: case SurfaceTarget::TextureCubemap:
return GL_TEXTURE_CUBE_MAP; return GL_TEXTURE_CUBE_MAP;
} }
LOG_CRITICAL(Render_OpenGL, "Unimplemented texture target={}", static_cast<u32>(target)); LOG_CRITICAL(Render_OpenGL, "Unimplemented texture target={}", static_cast<u32>(target));
@ -392,31 +357,10 @@ MathUtil::Rectangle<u32> SurfaceParams::GetRect(u32 mip_level) const {
return {0, actual_height, MipWidth(mip_level), 0}; return {0, actual_height, MipWidth(mip_level), 0};
} }
/// Returns true if the specified PixelFormat is a BCn format, e.g. DXT or DXN
static bool IsFormatBCn(PixelFormat format) {
switch (format) {
case PixelFormat::DXT1:
case PixelFormat::DXT23:
case PixelFormat::DXT45:
case PixelFormat::DXN1:
case PixelFormat::DXN2SNORM:
case PixelFormat::DXN2UNORM:
case PixelFormat::BC7U:
case PixelFormat::BC6H_UF16:
case PixelFormat::BC6H_SF16:
case PixelFormat::DXT1_SRGB:
case PixelFormat::DXT23_SRGB:
case PixelFormat::DXT45_SRGB:
case PixelFormat::BC7U_SRGB:
return true;
}
return false;
}
template <bool morton_to_gl, PixelFormat format> template <bool morton_to_gl, PixelFormat format>
void MortonCopy(u32 stride, u32 block_height, u32 height, u32 block_depth, u32 depth, u8* gl_buffer, void MortonCopy(u32 stride, u32 block_height, u32 height, u32 block_depth, u32 depth, u8* gl_buffer,
std::size_t gl_buffer_size, VAddr addr) { std::size_t gl_buffer_size, VAddr addr) {
constexpr u32 bytes_per_pixel = SurfaceParams::GetBytesPerPixel(format); constexpr u32 bytes_per_pixel = GetBytesPerPixel(format);
// With the BCn formats (DXT and DXN), each 4x4 tile is swizzled instead of just individual // With the BCn formats (DXT and DXN), each 4x4 tile is swizzled instead of just individual
// pixel values. // pixel values.
@ -435,7 +379,7 @@ void MortonCopy(u32 stride, u32 block_height, u32 height, u32 block_depth, u32 d
} }
using GLConversionArray = std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, VAddr), using GLConversionArray = std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, VAddr),
SurfaceParams::MaxPixelFormat>; VideoCore::Surface::MaxPixelFormat>;
static constexpr GLConversionArray morton_to_gl_fns = { static constexpr GLConversionArray morton_to_gl_fns = {
// clang-format off // clang-format off
@ -575,7 +519,7 @@ static constexpr GLConversionArray gl_to_morton_fns = {
void SwizzleFunc(const GLConversionArray& functions, const SurfaceParams& params, void SwizzleFunc(const GLConversionArray& functions, const SurfaceParams& params,
std::vector<u8>& gl_buffer, u32 mip_level) { std::vector<u8>& gl_buffer, u32 mip_level) {
u32 depth = params.MipDepth(mip_level); u32 depth = params.MipDepth(mip_level);
if (params.target == SurfaceParams::SurfaceTarget::Texture2D) { if (params.target == SurfaceTarget::Texture2D) {
// TODO(Blinkhawk): Eliminate this condition once all texture types are implemented. // TODO(Blinkhawk): Eliminate this condition once all texture types are implemented.
depth = 1U; depth = 1U;
} }
@ -622,13 +566,13 @@ static bool BlitSurface(const Surface& src_surface, const Surface& dst_surface,
if (src_params.type == SurfaceType::ColorTexture) { if (src_params.type == SurfaceType::ColorTexture) {
switch (src_params.target) { switch (src_params.target) {
case SurfaceParams::SurfaceTarget::Texture2D: case SurfaceTarget::Texture2D:
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + src_attachment, glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + src_attachment,
GL_TEXTURE_2D, src_surface->Texture().handle, 0); GL_TEXTURE_2D, src_surface->Texture().handle, 0);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
0, 0); 0, 0);
break; break;
case SurfaceParams::SurfaceTarget::TextureCubemap: case SurfaceTarget::TextureCubemap:
glFramebufferTexture2D( glFramebufferTexture2D(
GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + src_attachment, GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + src_attachment,
static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + cubemap_face), static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + cubemap_face),
@ -637,12 +581,12 @@ static bool BlitSurface(const Surface& src_surface, const Surface& dst_surface,
GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + cubemap_face), 0, 0); static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + cubemap_face), 0, 0);
break; break;
case SurfaceParams::SurfaceTarget::Texture2DArray: case SurfaceTarget::Texture2DArray:
glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + src_attachment, glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + src_attachment,
src_surface->Texture().handle, 0, 0); src_surface->Texture().handle, 0, 0);
glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, 0, 0, 0); glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, 0, 0, 0);
break; break;
case SurfaceParams::SurfaceTarget::Texture3D: case SurfaceTarget::Texture3D:
glFramebufferTexture3D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + src_attachment, glFramebufferTexture3D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + src_attachment,
SurfaceTargetToGL(src_params.target), SurfaceTargetToGL(src_params.target),
src_surface->Texture().handle, 0, 0); src_surface->Texture().handle, 0, 0);
@ -658,13 +602,13 @@ static bool BlitSurface(const Surface& src_surface, const Surface& dst_surface,
} }
switch (dst_params.target) { switch (dst_params.target) {
case SurfaceParams::SurfaceTarget::Texture2D: case SurfaceTarget::Texture2D:
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + dst_attachment, glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + dst_attachment,
GL_TEXTURE_2D, dst_surface->Texture().handle, 0); GL_TEXTURE_2D, dst_surface->Texture().handle, 0);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
0, 0); 0, 0);
break; break;
case SurfaceParams::SurfaceTarget::TextureCubemap: case SurfaceTarget::TextureCubemap:
glFramebufferTexture2D( glFramebufferTexture2D(
GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + dst_attachment, GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + dst_attachment,
static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + cubemap_face), static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + cubemap_face),
@ -673,13 +617,13 @@ static bool BlitSurface(const Surface& src_surface, const Surface& dst_surface,
GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + cubemap_face), 0, 0); static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + cubemap_face), 0, 0);
break; break;
case SurfaceParams::SurfaceTarget::Texture2DArray: case SurfaceTarget::Texture2DArray:
glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + dst_attachment, glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + dst_attachment,
dst_surface->Texture().handle, 0, 0); dst_surface->Texture().handle, 0, 0);
glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, 0, 0, 0); glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, 0, 0, 0);
break; break;
case SurfaceParams::SurfaceTarget::Texture3D: case SurfaceTarget::Texture3D:
glFramebufferTexture3D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + dst_attachment, glFramebufferTexture3D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + dst_attachment,
SurfaceTargetToGL(dst_params.target), SurfaceTargetToGL(dst_params.target),
dst_surface->Texture().handle, 0, 0); dst_surface->Texture().handle, 0, 0);
@ -800,21 +744,21 @@ static void CopySurface(const Surface& src_surface, const Surface& dst_surface,
UNREACHABLE(); UNREACHABLE();
} else { } else {
switch (dst_params.target) { switch (dst_params.target) {
case SurfaceParams::SurfaceTarget::Texture1D: case SurfaceTarget::Texture1D:
glTextureSubImage1D(dst_surface->Texture().handle, 0, 0, width, dest_format.format, glTextureSubImage1D(dst_surface->Texture().handle, 0, 0, width, dest_format.format,
dest_format.type, nullptr); dest_format.type, nullptr);
break; break;
case SurfaceParams::SurfaceTarget::Texture2D: case SurfaceTarget::Texture2D:
glTextureSubImage2D(dst_surface->Texture().handle, 0, 0, 0, width, height, glTextureSubImage2D(dst_surface->Texture().handle, 0, 0, 0, width, height,
dest_format.format, dest_format.type, nullptr); dest_format.format, dest_format.type, nullptr);
break; break;
case SurfaceParams::SurfaceTarget::Texture3D: case SurfaceTarget::Texture3D:
case SurfaceParams::SurfaceTarget::Texture2DArray: case SurfaceTarget::Texture2DArray:
glTextureSubImage3D(dst_surface->Texture().handle, 0, 0, 0, 0, width, height, glTextureSubImage3D(dst_surface->Texture().handle, 0, 0, 0, 0, width, height,
static_cast<GLsizei>(dst_params.depth), dest_format.format, static_cast<GLsizei>(dst_params.depth), dest_format.format,
dest_format.type, nullptr); dest_format.type, nullptr);
break; break;
case SurfaceParams::SurfaceTarget::TextureCubemap: case SurfaceTarget::TextureCubemap:
glTextureSubImage3D(dst_surface->Texture().handle, 0, 0, 0, glTextureSubImage3D(dst_surface->Texture().handle, 0, 0, 0,
static_cast<GLint>(cubemap_face), width, height, 1, static_cast<GLint>(cubemap_face), width, height, 1,
dest_format.format, dest_format.type, nullptr); dest_format.format, dest_format.type, nullptr);
@ -851,17 +795,17 @@ CachedSurface::CachedSurface(const SurfaceParams& params)
if (!format_tuple.compressed) { if (!format_tuple.compressed) {
// Only pre-create the texture for non-compressed textures. // Only pre-create the texture for non-compressed textures.
switch (params.target) { switch (params.target) {
case SurfaceParams::SurfaceTarget::Texture1D: case SurfaceTarget::Texture1D:
glTexStorage1D(SurfaceTargetToGL(params.target), params.max_mip_level, glTexStorage1D(SurfaceTargetToGL(params.target), params.max_mip_level,
format_tuple.internal_format, rect.GetWidth()); format_tuple.internal_format, rect.GetWidth());
break; break;
case SurfaceParams::SurfaceTarget::Texture2D: case SurfaceTarget::Texture2D:
case SurfaceParams::SurfaceTarget::TextureCubemap: case SurfaceTarget::TextureCubemap:
glTexStorage2D(SurfaceTargetToGL(params.target), params.max_mip_level, glTexStorage2D(SurfaceTargetToGL(params.target), params.max_mip_level,
format_tuple.internal_format, rect.GetWidth(), rect.GetHeight()); format_tuple.internal_format, rect.GetWidth(), rect.GetHeight());
break; break;
case SurfaceParams::SurfaceTarget::Texture3D: case SurfaceTarget::Texture3D:
case SurfaceParams::SurfaceTarget::Texture2DArray: case SurfaceTarget::Texture2DArray:
glTexStorage3D(SurfaceTargetToGL(params.target), params.max_mip_level, glTexStorage3D(SurfaceTargetToGL(params.target), params.max_mip_level,
format_tuple.internal_format, rect.GetWidth(), rect.GetHeight(), format_tuple.internal_format, rect.GetWidth(), rect.GetHeight(),
params.depth); params.depth);
@ -916,7 +860,7 @@ static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height, bo
S8Z24 s8z24_pixel{}; S8Z24 s8z24_pixel{};
Z24S8 z24s8_pixel{}; Z24S8 z24s8_pixel{};
constexpr auto bpp{SurfaceParams::GetBytesPerPixel(PixelFormat::S8Z24)}; constexpr auto bpp{GetBytesPerPixel(PixelFormat::S8Z24)};
for (std::size_t y = 0; y < height; ++y) { for (std::size_t y = 0; y < height; ++y) {
for (std::size_t x = 0; x < width; ++x) { for (std::size_t x = 0; x < width; ++x) {
const std::size_t offset{bpp * (y * width + x)}; const std::size_t offset{bpp * (y * width + x)};
@ -936,7 +880,7 @@ static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height, bo
} }
static void ConvertG8R8ToR8G8(std::vector<u8>& data, u32 width, u32 height) { static void ConvertG8R8ToR8G8(std::vector<u8>& data, u32 width, u32 height) {
constexpr auto bpp{SurfaceParams::GetBytesPerPixel(PixelFormat::G8R8U)}; constexpr auto bpp{GetBytesPerPixel(PixelFormat::G8R8U)};
for (std::size_t y = 0; y < height; ++y) { for (std::size_t y = 0; y < height; ++y) {
for (std::size_t x = 0; x < width; ++x) { for (std::size_t x = 0; x < width; ++x) {
const std::size_t offset{bpp * (y * width + x)}; const std::size_t offset{bpp * (y * width + x)};
@ -1042,7 +986,7 @@ void CachedSurface::FlushGLBuffer() {
const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type); const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type);
// Ensure no bad interactions with GL_UNPACK_ALIGNMENT // Ensure no bad interactions with GL_UNPACK_ALIGNMENT
ASSERT(params.width * SurfaceParams::GetBytesPerPixel(params.pixel_format) % 4 == 0); ASSERT(params.width * GetBytesPerPixel(params.pixel_format) % 4 == 0);
glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(params.width)); glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(params.width));
ASSERT(!tuple.compressed); ASSERT(!tuple.compressed);
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
@ -1074,7 +1018,7 @@ void CachedSurface::UploadGLMipmapTexture(u32 mip_map, GLuint read_fb_handle,
std::size_t buffer_offset = std::size_t buffer_offset =
static_cast<std::size_t>(static_cast<std::size_t>(y0) * params.MipWidth(mip_map) + static_cast<std::size_t>(static_cast<std::size_t>(y0) * params.MipWidth(mip_map) +
static_cast<std::size_t>(x0)) * static_cast<std::size_t>(x0)) *
SurfaceParams::GetBytesPerPixel(params.pixel_format); GetBytesPerPixel(params.pixel_format);
const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type); const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type);
const GLuint target_tex = texture.handle; const GLuint target_tex = texture.handle;
@ -1090,35 +1034,34 @@ void CachedSurface::UploadGLMipmapTexture(u32 mip_map, GLuint read_fb_handle,
cur_state.Apply(); cur_state.Apply();
// Ensure no bad interactions with GL_UNPACK_ALIGNMENT // Ensure no bad interactions with GL_UNPACK_ALIGNMENT
ASSERT(params.MipWidth(mip_map) * SurfaceParams::GetBytesPerPixel(params.pixel_format) % 4 == ASSERT(params.MipWidth(mip_map) * GetBytesPerPixel(params.pixel_format) % 4 == 0);
0);
glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(params.MipWidth(mip_map))); glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(params.MipWidth(mip_map)));
GLsizei image_size = static_cast<GLsizei>(params.GetMipmapSizeGL(mip_map, false)); GLsizei image_size = static_cast<GLsizei>(params.GetMipmapSizeGL(mip_map, false));
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
if (tuple.compressed) { if (tuple.compressed) {
switch (params.target) { switch (params.target) {
case SurfaceParams::SurfaceTarget::Texture2D: case SurfaceTarget::Texture2D:
glCompressedTexImage2D(SurfaceTargetToGL(params.target), mip_map, tuple.internal_format, glCompressedTexImage2D(SurfaceTargetToGL(params.target), mip_map, tuple.internal_format,
static_cast<GLsizei>(params.MipWidth(mip_map)), static_cast<GLsizei>(params.MipWidth(mip_map)),
static_cast<GLsizei>(params.MipHeight(mip_map)), 0, image_size, static_cast<GLsizei>(params.MipHeight(mip_map)), 0, image_size,
&gl_buffer[mip_map][buffer_offset]); &gl_buffer[mip_map][buffer_offset]);
break; break;
case SurfaceParams::SurfaceTarget::Texture3D: case SurfaceTarget::Texture3D:
glCompressedTexImage3D(SurfaceTargetToGL(params.target), mip_map, tuple.internal_format, glCompressedTexImage3D(SurfaceTargetToGL(params.target), mip_map, tuple.internal_format,
static_cast<GLsizei>(params.MipWidth(mip_map)), static_cast<GLsizei>(params.MipWidth(mip_map)),
static_cast<GLsizei>(params.MipHeight(mip_map)), static_cast<GLsizei>(params.MipHeight(mip_map)),
static_cast<GLsizei>(params.MipDepth(mip_map)), 0, image_size, static_cast<GLsizei>(params.MipDepth(mip_map)), 0, image_size,
&gl_buffer[mip_map][buffer_offset]); &gl_buffer[mip_map][buffer_offset]);
break; break;
case SurfaceParams::SurfaceTarget::Texture2DArray: case SurfaceTarget::Texture2DArray:
glCompressedTexImage3D(SurfaceTargetToGL(params.target), mip_map, tuple.internal_format, glCompressedTexImage3D(SurfaceTargetToGL(params.target), mip_map, tuple.internal_format,
static_cast<GLsizei>(params.MipWidth(mip_map)), static_cast<GLsizei>(params.MipWidth(mip_map)),
static_cast<GLsizei>(params.MipHeight(mip_map)), static_cast<GLsizei>(params.MipHeight(mip_map)),
static_cast<GLsizei>(params.depth), 0, image_size, static_cast<GLsizei>(params.depth), 0, image_size,
&gl_buffer[mip_map][buffer_offset]); &gl_buffer[mip_map][buffer_offset]);
break; break;
case SurfaceParams::SurfaceTarget::TextureCubemap: { case SurfaceTarget::TextureCubemap: {
GLsizei layer_size = static_cast<GLsizei>(params.LayerSizeGL(mip_map)); GLsizei layer_size = static_cast<GLsizei>(params.LayerSizeGL(mip_map));
for (std::size_t face = 0; face < params.depth; ++face) { for (std::size_t face = 0; face < params.depth; ++face) {
glCompressedTexImage2D(static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face), glCompressedTexImage2D(static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face),
@ -1143,30 +1086,30 @@ void CachedSurface::UploadGLMipmapTexture(u32 mip_map, GLuint read_fb_handle,
} else { } else {
switch (params.target) { switch (params.target) {
case SurfaceParams::SurfaceTarget::Texture1D: case SurfaceTarget::Texture1D:
glTexSubImage1D(SurfaceTargetToGL(params.target), mip_map, x0, glTexSubImage1D(SurfaceTargetToGL(params.target), mip_map, x0,
static_cast<GLsizei>(rect.GetWidth()), tuple.format, tuple.type, static_cast<GLsizei>(rect.GetWidth()), tuple.format, tuple.type,
&gl_buffer[mip_map][buffer_offset]); &gl_buffer[mip_map][buffer_offset]);
break; break;
case SurfaceParams::SurfaceTarget::Texture2D: case SurfaceTarget::Texture2D:
glTexSubImage2D(SurfaceTargetToGL(params.target), mip_map, x0, y0, glTexSubImage2D(SurfaceTargetToGL(params.target), mip_map, x0, y0,
static_cast<GLsizei>(rect.GetWidth()), static_cast<GLsizei>(rect.GetWidth()),
static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type, static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type,
&gl_buffer[mip_map][buffer_offset]); &gl_buffer[mip_map][buffer_offset]);
break; break;
case SurfaceParams::SurfaceTarget::Texture3D: case SurfaceTarget::Texture3D:
glTexSubImage3D(SurfaceTargetToGL(params.target), mip_map, x0, y0, 0, glTexSubImage3D(SurfaceTargetToGL(params.target), mip_map, x0, y0, 0,
static_cast<GLsizei>(rect.GetWidth()), static_cast<GLsizei>(rect.GetWidth()),
static_cast<GLsizei>(rect.GetHeight()), params.MipDepth(mip_map), static_cast<GLsizei>(rect.GetHeight()), params.MipDepth(mip_map),
tuple.format, tuple.type, &gl_buffer[mip_map][buffer_offset]); tuple.format, tuple.type, &gl_buffer[mip_map][buffer_offset]);
break; break;
case SurfaceParams::SurfaceTarget::Texture2DArray: case SurfaceTarget::Texture2DArray:
glTexSubImage3D(SurfaceTargetToGL(params.target), mip_map, x0, y0, 0, glTexSubImage3D(SurfaceTargetToGL(params.target), mip_map, x0, y0, 0,
static_cast<GLsizei>(rect.GetWidth()), static_cast<GLsizei>(rect.GetWidth()),
static_cast<GLsizei>(rect.GetHeight()), params.depth, tuple.format, static_cast<GLsizei>(rect.GetHeight()), params.depth, tuple.format,
tuple.type, &gl_buffer[mip_map][buffer_offset]); tuple.type, &gl_buffer[mip_map][buffer_offset]);
break; break;
case SurfaceParams::SurfaceTarget::TextureCubemap: { case SurfaceTarget::TextureCubemap: {
std::size_t start = buffer_offset; std::size_t start = buffer_offset;
for (std::size_t face = 0; face < params.depth; ++face) { for (std::size_t face = 0; face < params.depth; ++face) {
glTexSubImage2D(static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face), mip_map, glTexSubImage2D(static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face), mip_map,
@ -1341,8 +1284,7 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
// For compatible surfaces, we can just do fast glCopyImageSubData based copy // For compatible surfaces, we can just do fast glCopyImageSubData based copy
if (old_params.target == new_params.target && old_params.type == new_params.type && if (old_params.target == new_params.target && old_params.type == new_params.type &&
old_params.depth == new_params.depth && old_params.depth == 1 && old_params.depth == new_params.depth && old_params.depth == 1 &&
SurfaceParams::GetFormatBpp(old_params.pixel_format) == GetFormatBpp(old_params.pixel_format) == GetFormatBpp(new_params.pixel_format)) {
SurfaceParams::GetFormatBpp(new_params.pixel_format)) {
FastCopySurface(old_surface, new_surface); FastCopySurface(old_surface, new_surface);
return new_surface; return new_surface;
} }
@ -1355,13 +1297,14 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
const bool is_blit{old_params.pixel_format == new_params.pixel_format}; const bool is_blit{old_params.pixel_format == new_params.pixel_format};
switch (new_params.target) { switch (new_params.target) {
case SurfaceParams::SurfaceTarget::Texture2D: case SurfaceTarget::Texture2D:
if (is_blit) { if (is_blit) {
BlitSurface(old_surface, new_surface, read_framebuffer.handle, draw_framebuffer.handle); BlitSurface(old_surface, new_surface, read_framebuffer.handle, draw_framebuffer.handle);
} else { } else {
CopySurface(old_surface, new_surface, copy_pbo.handle); CopySurface(old_surface, new_surface, copy_pbo.handle);
} }
break; break;
case SurfaceParams::SurfaceTarget::TextureCubemap: case SurfaceParams::SurfaceTarget::TextureCubemap:
case SurfaceParams::SurfaceTarget::Texture3D: case SurfaceParams::SurfaceTarget::Texture3D:
case SurfaceParams::SurfaceTarget::Texture2DArray: case SurfaceParams::SurfaceTarget::Texture2DArray:
@ -1374,7 +1317,7 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
} }
return new_surface; return new_surface;
} // namespace OpenGL }
Surface RasterizerCacheOpenGL::TryFindFramebufferSurface(VAddr addr) const { Surface RasterizerCacheOpenGL::TryFindFramebufferSurface(VAddr addr) const {
return TryGet(addr); return TryGet(addr);

View file

@ -7,6 +7,7 @@
#include <array> #include <array>
#include <map> #include <map>
#include <memory> #include <memory>
#include <string>
#include <vector> #include <vector>
#include "common/alignment.h" #include "common/alignment.h"
@ -18,6 +19,7 @@
#include "video_core/rasterizer_cache.h" #include "video_core/rasterizer_cache.h"
#include "video_core/renderer_opengl/gl_resource_manager.h" #include "video_core/renderer_opengl/gl_resource_manager.h"
#include "video_core/renderer_opengl/gl_shader_gen.h" #include "video_core/renderer_opengl/gl_shader_gen.h"
#include "video_core/surface.h"
#include "video_core/textures/decoders.h" #include "video_core/textures/decoders.h"
#include "video_core/textures/texture.h" #include "video_core/textures/texture.h"
@ -27,135 +29,12 @@ class CachedSurface;
using Surface = std::shared_ptr<CachedSurface>; using Surface = std::shared_ptr<CachedSurface>;
using SurfaceSurfaceRect_Tuple = std::tuple<Surface, Surface, MathUtil::Rectangle<u32>>; using SurfaceSurfaceRect_Tuple = std::tuple<Surface, Surface, MathUtil::Rectangle<u32>>;
using SurfaceTarget = VideoCore::Surface::SurfaceTarget;
using SurfaceType = VideoCore::Surface::SurfaceType;
using PixelFormat = VideoCore::Surface::PixelFormat;
using ComponentType = VideoCore::Surface::ComponentType;
struct SurfaceParams { struct SurfaceParams {
enum class PixelFormat {
ABGR8U = 0,
ABGR8S = 1,
ABGR8UI = 2,
B5G6R5U = 3,
A2B10G10R10U = 4,
A1B5G5R5U = 5,
R8U = 6,
R8UI = 7,
RGBA16F = 8,
RGBA16U = 9,
RGBA16UI = 10,
R11FG11FB10F = 11,
RGBA32UI = 12,
DXT1 = 13,
DXT23 = 14,
DXT45 = 15,
DXN1 = 16, // This is also known as BC4
DXN2UNORM = 17,
DXN2SNORM = 18,
BC7U = 19,
BC6H_UF16 = 20,
BC6H_SF16 = 21,
ASTC_2D_4X4 = 22,
G8R8U = 23,
G8R8S = 24,
BGRA8 = 25,
RGBA32F = 26,
RG32F = 27,
R32F = 28,
R16F = 29,
R16U = 30,
R16S = 31,
R16UI = 32,
R16I = 33,
RG16 = 34,
RG16F = 35,
RG16UI = 36,
RG16I = 37,
RG16S = 38,
RGB32F = 39,
RGBA8_SRGB = 40,
RG8U = 41,
RG8S = 42,
RG32UI = 43,
R32UI = 44,
ASTC_2D_8X8 = 45,
ASTC_2D_8X5 = 46,
ASTC_2D_5X4 = 47,
BGRA8_SRGB = 48,
DXT1_SRGB = 49,
DXT23_SRGB = 50,
DXT45_SRGB = 51,
BC7U_SRGB = 52,
ASTC_2D_4X4_SRGB = 53,
ASTC_2D_8X8_SRGB = 54,
ASTC_2D_8X5_SRGB = 55,
ASTC_2D_5X4_SRGB = 56,
MaxColorFormat,
// Depth formats
Z32F = 57,
Z16 = 58,
MaxDepthFormat,
// DepthStencil formats
Z24S8 = 59,
S8Z24 = 60,
Z32FS8 = 61,
MaxDepthStencilFormat,
Max = MaxDepthStencilFormat,
Invalid = 255,
};
static constexpr std::size_t MaxPixelFormat = static_cast<std::size_t>(PixelFormat::Max);
enum class ComponentType {
Invalid = 0,
SNorm = 1,
UNorm = 2,
SInt = 3,
UInt = 4,
Float = 5,
};
enum class SurfaceType {
ColorTexture = 0,
Depth = 1,
DepthStencil = 2,
Fill = 3,
Invalid = 4,
};
enum class SurfaceTarget {
Texture1D,
Texture2D,
Texture3D,
Texture1DArray,
Texture2DArray,
TextureCubemap,
};
static SurfaceTarget SurfaceTargetFromTextureType(Tegra::Texture::TextureType texture_type) {
switch (texture_type) {
case Tegra::Texture::TextureType::Texture1D:
return SurfaceTarget::Texture1D;
case Tegra::Texture::TextureType::Texture2D:
case Tegra::Texture::TextureType::Texture2DNoMipmap:
return SurfaceTarget::Texture2D;
case Tegra::Texture::TextureType::Texture3D:
return SurfaceTarget::Texture3D;
case Tegra::Texture::TextureType::TextureCubemap:
return SurfaceTarget::TextureCubemap;
case Tegra::Texture::TextureType::Texture1DArray:
return SurfaceTarget::Texture1DArray;
case Tegra::Texture::TextureType::Texture2DArray:
return SurfaceTarget::Texture2DArray;
default:
LOG_CRITICAL(HW_GPU, "Unimplemented texture_type={}", static_cast<u32>(texture_type));
UNREACHABLE();
return SurfaceTarget::Texture2D;
}
}
static std::string SurfaceTargetName(SurfaceTarget target) { static std::string SurfaceTargetName(SurfaceTarget target) {
switch (target) { switch (target) {
case SurfaceTarget::Texture1D: case SurfaceTarget::Texture1D:
@ -177,660 +56,8 @@ struct SurfaceParams {
} }
} }
static bool SurfaceTargetIsLayered(SurfaceTarget target) {
switch (target) {
case SurfaceTarget::Texture1D:
case SurfaceTarget::Texture2D:
case SurfaceTarget::Texture3D:
return false;
case SurfaceTarget::Texture1DArray:
case SurfaceTarget::Texture2DArray:
case SurfaceTarget::TextureCubemap:
return true;
default:
LOG_CRITICAL(HW_GPU, "Unimplemented surface_target={}", static_cast<u32>(target));
UNREACHABLE();
return false;
}
}
/**
* Gets the compression factor for the specified PixelFormat. This applies to just the
* "compressed width" and "compressed height", not the overall compression factor of a
* compressed image. This is used for maintaining proper surface sizes for compressed
* texture formats.
*/
static constexpr u32 GetCompressionFactor(PixelFormat format) {
if (format == PixelFormat::Invalid)
return 0;
constexpr std::array<u32, MaxPixelFormat> compression_factor_table = {{
1, // ABGR8U
1, // ABGR8S
1, // ABGR8UI
1, // B5G6R5U
1, // A2B10G10R10U
1, // A1B5G5R5U
1, // R8U
1, // R8UI
1, // RGBA16F
1, // RGBA16U
1, // RGBA16UI
1, // R11FG11FB10F
1, // RGBA32UI
4, // DXT1
4, // DXT23
4, // DXT45
4, // DXN1
4, // DXN2UNORM
4, // DXN2SNORM
4, // BC7U
4, // BC6H_UF16
4, // BC6H_SF16
4, // ASTC_2D_4X4
1, // G8R8U
1, // G8R8S
1, // BGRA8
1, // RGBA32F
1, // RG32F
1, // R32F
1, // R16F
1, // R16U
1, // R16S
1, // R16UI
1, // R16I
1, // RG16
1, // RG16F
1, // RG16UI
1, // RG16I
1, // RG16S
1, // RGB32F
1, // RGBA8_SRGB
1, // RG8U
1, // RG8S
1, // RG32UI
1, // R32UI
4, // ASTC_2D_8X8
4, // ASTC_2D_8X5
4, // ASTC_2D_5X4
1, // BGRA8_SRGB
4, // DXT1_SRGB
4, // DXT23_SRGB
4, // DXT45_SRGB
4, // BC7U_SRGB
4, // ASTC_2D_4X4_SRGB
4, // ASTC_2D_8X8_SRGB
4, // ASTC_2D_8X5_SRGB
4, // ASTC_2D_5X4_SRGB
1, // Z32F
1, // Z16
1, // Z24S8
1, // S8Z24
1, // Z32FS8
}};
ASSERT(static_cast<std::size_t>(format) < compression_factor_table.size());
return compression_factor_table[static_cast<std::size_t>(format)];
}
static constexpr u32 GetDefaultBlockHeight(PixelFormat format) {
if (format == PixelFormat::Invalid)
return 0;
constexpr std::array<u32, MaxPixelFormat> block_height_table = {{
1, // ABGR8U
1, // ABGR8S
1, // ABGR8UI
1, // B5G6R5U
1, // A2B10G10R10U
1, // A1B5G5R5U
1, // R8U
1, // R8UI
1, // RGBA16F
1, // RGBA16U
1, // RGBA16UI
1, // R11FG11FB10F
1, // RGBA32UI
4, // DXT1
4, // DXT23
4, // DXT45
4, // DXN1
4, // DXN2UNORM
4, // DXN2SNORM
4, // BC7U
4, // BC6H_UF16
4, // BC6H_SF16
4, // ASTC_2D_4X4
1, // G8R8U
1, // G8R8S
1, // BGRA8
1, // RGBA32F
1, // RG32F
1, // R32F
1, // R16F
1, // R16U
1, // R16S
1, // R16UI
1, // R16I
1, // RG16
1, // RG16F
1, // RG16UI
1, // RG16I
1, // RG16S
1, // RGB32F
1, // RGBA8_SRGB
1, // RG8U
1, // RG8S
1, // RG32UI
1, // R32UI
8, // ASTC_2D_8X8
5, // ASTC_2D_8X5
4, // ASTC_2D_5X4
1, // BGRA8_SRGB
4, // DXT1_SRGB
4, // DXT23_SRGB
4, // DXT45_SRGB
4, // BC7U_SRGB
4, // ASTC_2D_4X4_SRGB
8, // ASTC_2D_8X8_SRGB
5, // ASTC_2D_8X5_SRGB
4, // ASTC_2D_5X4_SRGB
1, // Z32F
1, // Z16
1, // Z24S8
1, // S8Z24
1, // Z32FS8
}};
ASSERT(static_cast<std::size_t>(format) < block_height_table.size());
return block_height_table[static_cast<std::size_t>(format)];
}
static constexpr u32 GetFormatBpp(PixelFormat format) {
if (format == PixelFormat::Invalid)
return 0;
constexpr std::array<u32, MaxPixelFormat> bpp_table = {{
32, // ABGR8U
32, // ABGR8S
32, // ABGR8UI
16, // B5G6R5U
32, // A2B10G10R10U
16, // A1B5G5R5U
8, // R8U
8, // R8UI
64, // RGBA16F
64, // RGBA16U
64, // RGBA16UI
32, // R11FG11FB10F
128, // RGBA32UI
64, // DXT1
128, // DXT23
128, // DXT45
64, // DXN1
128, // DXN2UNORM
128, // DXN2SNORM
128, // BC7U
128, // BC6H_UF16
128, // BC6H_SF16
32, // ASTC_2D_4X4
16, // G8R8U
16, // G8R8S
32, // BGRA8
128, // RGBA32F
64, // RG32F
32, // R32F
16, // R16F
16, // R16U
16, // R16S
16, // R16UI
16, // R16I
32, // RG16
32, // RG16F
32, // RG16UI
32, // RG16I
32, // RG16S
96, // RGB32F
32, // RGBA8_SRGB
16, // RG8U
16, // RG8S
64, // RG32UI
32, // R32UI
16, // ASTC_2D_8X8
16, // ASTC_2D_8X5
32, // ASTC_2D_5X4
32, // BGRA8_SRGB
64, // DXT1_SRGB
128, // DXT23_SRGB
128, // DXT45_SRGB
128, // BC7U
32, // ASTC_2D_4X4_SRGB
16, // ASTC_2D_8X8_SRGB
16, // ASTC_2D_8X5_SRGB
32, // ASTC_2D_5X4_SRGB
32, // Z32F
16, // Z16
32, // Z24S8
32, // S8Z24
64, // Z32FS8
}};
ASSERT(static_cast<std::size_t>(format) < bpp_table.size());
return bpp_table[static_cast<std::size_t>(format)];
}
u32 GetFormatBpp() const { u32 GetFormatBpp() const {
return GetFormatBpp(pixel_format); return VideoCore::Surface::GetFormatBpp(pixel_format);
}
static PixelFormat PixelFormatFromDepthFormat(Tegra::DepthFormat format) {
switch (format) {
case Tegra::DepthFormat::S8_Z24_UNORM:
return PixelFormat::S8Z24;
case Tegra::DepthFormat::Z24_S8_UNORM:
return PixelFormat::Z24S8;
case Tegra::DepthFormat::Z32_FLOAT:
return PixelFormat::Z32F;
case Tegra::DepthFormat::Z16_UNORM:
return PixelFormat::Z16;
case Tegra::DepthFormat::Z32_S8_X24_FLOAT:
return PixelFormat::Z32FS8;
default:
LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
UNREACHABLE();
}
}
static PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format) {
switch (format) {
// TODO (Hexagon12): Converting SRGBA to RGBA is a hack and doesn't completely correct the
// gamma.
case Tegra::RenderTargetFormat::RGBA8_SRGB:
return PixelFormat::RGBA8_SRGB;
case Tegra::RenderTargetFormat::RGBA8_UNORM:
return PixelFormat::ABGR8U;
case Tegra::RenderTargetFormat::RGBA8_SNORM:
return PixelFormat::ABGR8S;
case Tegra::RenderTargetFormat::RGBA8_UINT:
return PixelFormat::ABGR8UI;
case Tegra::RenderTargetFormat::BGRA8_SRGB:
return PixelFormat::BGRA8_SRGB;
case Tegra::RenderTargetFormat::BGRA8_UNORM:
return PixelFormat::BGRA8;
case Tegra::RenderTargetFormat::RGB10_A2_UNORM:
return PixelFormat::A2B10G10R10U;
case Tegra::RenderTargetFormat::RGBA16_FLOAT:
return PixelFormat::RGBA16F;
case Tegra::RenderTargetFormat::RGBA16_UNORM:
return PixelFormat::RGBA16U;
case Tegra::RenderTargetFormat::RGBA16_UINT:
return PixelFormat::RGBA16UI;
case Tegra::RenderTargetFormat::RGBA32_FLOAT:
return PixelFormat::RGBA32F;
case Tegra::RenderTargetFormat::RG32_FLOAT:
return PixelFormat::RG32F;
case Tegra::RenderTargetFormat::R11G11B10_FLOAT:
return PixelFormat::R11FG11FB10F;
case Tegra::RenderTargetFormat::B5G6R5_UNORM:
return PixelFormat::B5G6R5U;
case Tegra::RenderTargetFormat::BGR5A1_UNORM:
return PixelFormat::A1B5G5R5U;
case Tegra::RenderTargetFormat::RGBA32_UINT:
return PixelFormat::RGBA32UI;
case Tegra::RenderTargetFormat::R8_UNORM:
return PixelFormat::R8U;
case Tegra::RenderTargetFormat::R8_UINT:
return PixelFormat::R8UI;
case Tegra::RenderTargetFormat::RG16_FLOAT:
return PixelFormat::RG16F;
case Tegra::RenderTargetFormat::RG16_UINT:
return PixelFormat::RG16UI;
case Tegra::RenderTargetFormat::RG16_SINT:
return PixelFormat::RG16I;
case Tegra::RenderTargetFormat::RG16_UNORM:
return PixelFormat::RG16;
case Tegra::RenderTargetFormat::RG16_SNORM:
return PixelFormat::RG16S;
case Tegra::RenderTargetFormat::RG8_UNORM:
return PixelFormat::RG8U;
case Tegra::RenderTargetFormat::RG8_SNORM:
return PixelFormat::RG8S;
case Tegra::RenderTargetFormat::R16_FLOAT:
return PixelFormat::R16F;
case Tegra::RenderTargetFormat::R16_UNORM:
return PixelFormat::R16U;
case Tegra::RenderTargetFormat::R16_SNORM:
return PixelFormat::R16S;
case Tegra::RenderTargetFormat::R16_UINT:
return PixelFormat::R16UI;
case Tegra::RenderTargetFormat::R16_SINT:
return PixelFormat::R16I;
case Tegra::RenderTargetFormat::R32_FLOAT:
return PixelFormat::R32F;
case Tegra::RenderTargetFormat::R32_UINT:
return PixelFormat::R32UI;
case Tegra::RenderTargetFormat::RG32_UINT:
return PixelFormat::RG32UI;
default:
LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
UNREACHABLE();
}
}
static PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format,
Tegra::Texture::ComponentType component_type,
bool is_srgb) {
// TODO(Subv): Properly implement this
switch (format) {
case Tegra::Texture::TextureFormat::A8R8G8B8:
if (is_srgb) {
return PixelFormat::RGBA8_SRGB;
}
switch (component_type) {
case Tegra::Texture::ComponentType::UNORM:
return PixelFormat::ABGR8U;
case Tegra::Texture::ComponentType::SNORM:
return PixelFormat::ABGR8S;
case Tegra::Texture::ComponentType::UINT:
return PixelFormat::ABGR8UI;
}
LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}",
static_cast<u32>(component_type));
UNREACHABLE();
case Tegra::Texture::TextureFormat::B5G6R5:
switch (component_type) {
case Tegra::Texture::ComponentType::UNORM:
return PixelFormat::B5G6R5U;
}
LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}",
static_cast<u32>(component_type));
UNREACHABLE();
case Tegra::Texture::TextureFormat::A2B10G10R10:
switch (component_type) {
case Tegra::Texture::ComponentType::UNORM:
return PixelFormat::A2B10G10R10U;
}
LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}",
static_cast<u32>(component_type));
UNREACHABLE();
case Tegra::Texture::TextureFormat::A1B5G5R5:
switch (component_type) {
case Tegra::Texture::ComponentType::UNORM:
return PixelFormat::A1B5G5R5U;
}
LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}",
static_cast<u32>(component_type));
UNREACHABLE();
case Tegra::Texture::TextureFormat::R8:
switch (component_type) {
case Tegra::Texture::ComponentType::UNORM:
return PixelFormat::R8U;
case Tegra::Texture::ComponentType::UINT:
return PixelFormat::R8UI;
}
LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}",
static_cast<u32>(component_type));
UNREACHABLE();
case Tegra::Texture::TextureFormat::G8R8:
switch (component_type) {
case Tegra::Texture::ComponentType::UNORM:
return PixelFormat::G8R8U;
case Tegra::Texture::ComponentType::SNORM:
return PixelFormat::G8R8S;
}
LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}",
static_cast<u32>(component_type));
UNREACHABLE();
case Tegra::Texture::TextureFormat::R16_G16_B16_A16:
switch (component_type) {
case Tegra::Texture::ComponentType::UNORM:
return PixelFormat::RGBA16U;
case Tegra::Texture::ComponentType::FLOAT:
return PixelFormat::RGBA16F;
}
LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}",
static_cast<u32>(component_type));
UNREACHABLE();
case Tegra::Texture::TextureFormat::BF10GF11RF11:
switch (component_type) {
case Tegra::Texture::ComponentType::FLOAT:
return PixelFormat::R11FG11FB10F;
}
LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}",
static_cast<u32>(component_type));
UNREACHABLE();
case Tegra::Texture::TextureFormat::R32_G32_B32_A32:
switch (component_type) {
case Tegra::Texture::ComponentType::FLOAT:
return PixelFormat::RGBA32F;
case Tegra::Texture::ComponentType::UINT:
return PixelFormat::RGBA32UI;
}
LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}",
static_cast<u32>(component_type));
UNREACHABLE();
case Tegra::Texture::TextureFormat::R32_G32:
switch (component_type) {
case Tegra::Texture::ComponentType::FLOAT:
return PixelFormat::RG32F;
case Tegra::Texture::ComponentType::UINT:
return PixelFormat::RG32UI;
}
LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}",
static_cast<u32>(component_type));
UNREACHABLE();
case Tegra::Texture::TextureFormat::R32_G32_B32:
switch (component_type) {
case Tegra::Texture::ComponentType::FLOAT:
return PixelFormat::RGB32F;
}
LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}",
static_cast<u32>(component_type));
UNREACHABLE();
case Tegra::Texture::TextureFormat::R16:
switch (component_type) {
case Tegra::Texture::ComponentType::FLOAT:
return PixelFormat::R16F;
case Tegra::Texture::ComponentType::UNORM:
return PixelFormat::R16U;
case Tegra::Texture::ComponentType::SNORM:
return PixelFormat::R16S;
case Tegra::Texture::ComponentType::UINT:
return PixelFormat::R16UI;
case Tegra::Texture::ComponentType::SINT:
return PixelFormat::R16I;
}
LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}",
static_cast<u32>(component_type));
UNREACHABLE();
case Tegra::Texture::TextureFormat::R32:
switch (component_type) {
case Tegra::Texture::ComponentType::FLOAT:
return PixelFormat::R32F;
case Tegra::Texture::ComponentType::UINT:
return PixelFormat::R32UI;
}
LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}",
static_cast<u32>(component_type));
UNREACHABLE();
case Tegra::Texture::TextureFormat::ZF32:
return PixelFormat::Z32F;
case Tegra::Texture::TextureFormat::Z16:
return PixelFormat::Z16;
case Tegra::Texture::TextureFormat::Z24S8:
return PixelFormat::Z24S8;
case Tegra::Texture::TextureFormat::DXT1:
return is_srgb ? PixelFormat::DXT1_SRGB : PixelFormat::DXT1;
case Tegra::Texture::TextureFormat::DXT23:
return is_srgb ? PixelFormat::DXT23_SRGB : PixelFormat::DXT23;
case Tegra::Texture::TextureFormat::DXT45:
return is_srgb ? PixelFormat::DXT45_SRGB : PixelFormat::DXT45;
case Tegra::Texture::TextureFormat::DXN1:
return PixelFormat::DXN1;
case Tegra::Texture::TextureFormat::DXN2:
switch (component_type) {
case Tegra::Texture::ComponentType::UNORM:
return PixelFormat::DXN2UNORM;
case Tegra::Texture::ComponentType::SNORM:
return PixelFormat::DXN2SNORM;
}
LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}",
static_cast<u32>(component_type));
UNREACHABLE();
case Tegra::Texture::TextureFormat::BC7U:
return is_srgb ? PixelFormat::BC7U_SRGB : PixelFormat::BC7U;
case Tegra::Texture::TextureFormat::BC6H_UF16:
return PixelFormat::BC6H_UF16;
case Tegra::Texture::TextureFormat::BC6H_SF16:
return PixelFormat::BC6H_SF16;
case Tegra::Texture::TextureFormat::ASTC_2D_4X4:
return is_srgb ? PixelFormat::ASTC_2D_4X4_SRGB : PixelFormat::ASTC_2D_4X4;
case Tegra::Texture::TextureFormat::ASTC_2D_5X4:
return is_srgb ? PixelFormat::ASTC_2D_5X4_SRGB : PixelFormat::ASTC_2D_5X4;
case Tegra::Texture::TextureFormat::ASTC_2D_8X8:
return is_srgb ? PixelFormat::ASTC_2D_8X8_SRGB : PixelFormat::ASTC_2D_8X8;
case Tegra::Texture::TextureFormat::ASTC_2D_8X5:
return is_srgb ? PixelFormat::ASTC_2D_8X5_SRGB : PixelFormat::ASTC_2D_8X5;
case Tegra::Texture::TextureFormat::R16_G16:
switch (component_type) {
case Tegra::Texture::ComponentType::FLOAT:
return PixelFormat::RG16F;
case Tegra::Texture::ComponentType::UNORM:
return PixelFormat::RG16;
case Tegra::Texture::ComponentType::SNORM:
return PixelFormat::RG16S;
case Tegra::Texture::ComponentType::UINT:
return PixelFormat::RG16UI;
case Tegra::Texture::ComponentType::SINT:
return PixelFormat::RG16I;
}
LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}",
static_cast<u32>(component_type));
UNREACHABLE();
default:
LOG_CRITICAL(HW_GPU, "Unimplemented format={}, component_type={}",
static_cast<u32>(format), static_cast<u32>(component_type));
UNREACHABLE();
}
}
static ComponentType ComponentTypeFromTexture(Tegra::Texture::ComponentType type) {
// TODO(Subv): Implement more component types
switch (type) {
case Tegra::Texture::ComponentType::UNORM:
return ComponentType::UNorm;
case Tegra::Texture::ComponentType::FLOAT:
return ComponentType::Float;
case Tegra::Texture::ComponentType::SNORM:
return ComponentType::SNorm;
case Tegra::Texture::ComponentType::UINT:
return ComponentType::UInt;
case Tegra::Texture::ComponentType::SINT:
return ComponentType::SInt;
default:
LOG_CRITICAL(HW_GPU, "Unimplemented component type={}", static_cast<u32>(type));
UNREACHABLE();
}
}
static ComponentType ComponentTypeFromRenderTarget(Tegra::RenderTargetFormat format) {
// TODO(Subv): Implement more render targets
switch (format) {
case Tegra::RenderTargetFormat::RGBA8_UNORM:
case Tegra::RenderTargetFormat::RGBA8_SRGB:
case Tegra::RenderTargetFormat::BGRA8_UNORM:
case Tegra::RenderTargetFormat::BGRA8_SRGB:
case Tegra::RenderTargetFormat::RGB10_A2_UNORM:
case Tegra::RenderTargetFormat::R8_UNORM:
case Tegra::RenderTargetFormat::RG16_UNORM:
case Tegra::RenderTargetFormat::R16_UNORM:
case Tegra::RenderTargetFormat::B5G6R5_UNORM:
case Tegra::RenderTargetFormat::BGR5A1_UNORM:
case Tegra::RenderTargetFormat::RG8_UNORM:
case Tegra::RenderTargetFormat::RGBA16_UNORM:
return ComponentType::UNorm;
case Tegra::RenderTargetFormat::RGBA8_SNORM:
case Tegra::RenderTargetFormat::RG16_SNORM:
case Tegra::RenderTargetFormat::R16_SNORM:
case Tegra::RenderTargetFormat::RG8_SNORM:
return ComponentType::SNorm;
case Tegra::RenderTargetFormat::RGBA16_FLOAT:
case Tegra::RenderTargetFormat::R11G11B10_FLOAT:
case Tegra::RenderTargetFormat::RGBA32_FLOAT:
case Tegra::RenderTargetFormat::RG32_FLOAT:
case Tegra::RenderTargetFormat::RG16_FLOAT:
case Tegra::RenderTargetFormat::R16_FLOAT:
case Tegra::RenderTargetFormat::R32_FLOAT:
return ComponentType::Float;
case Tegra::RenderTargetFormat::RGBA32_UINT:
case Tegra::RenderTargetFormat::RGBA16_UINT:
case Tegra::RenderTargetFormat::RG16_UINT:
case Tegra::RenderTargetFormat::R8_UINT:
case Tegra::RenderTargetFormat::R16_UINT:
case Tegra::RenderTargetFormat::RG32_UINT:
case Tegra::RenderTargetFormat::R32_UINT:
case Tegra::RenderTargetFormat::RGBA8_UINT:
return ComponentType::UInt;
case Tegra::RenderTargetFormat::RG16_SINT:
case Tegra::RenderTargetFormat::R16_SINT:
return ComponentType::SInt;
default:
LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
UNREACHABLE();
}
}
static PixelFormat PixelFormatFromGPUPixelFormat(Tegra::FramebufferConfig::PixelFormat format) {
switch (format) {
case Tegra::FramebufferConfig::PixelFormat::ABGR8:
return PixelFormat::ABGR8U;
default:
LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
UNREACHABLE();
}
}
static ComponentType ComponentTypeFromDepthFormat(Tegra::DepthFormat format) {
switch (format) {
case Tegra::DepthFormat::Z16_UNORM:
case Tegra::DepthFormat::S8_Z24_UNORM:
case Tegra::DepthFormat::Z24_S8_UNORM:
return ComponentType::UNorm;
case Tegra::DepthFormat::Z32_FLOAT:
case Tegra::DepthFormat::Z32_S8_X24_FLOAT:
return ComponentType::Float;
default:
LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
UNREACHABLE();
}
}
static SurfaceType GetFormatType(PixelFormat pixel_format) {
if (static_cast<std::size_t>(pixel_format) <
static_cast<std::size_t>(PixelFormat::MaxColorFormat)) {
return SurfaceType::ColorTexture;
}
if (static_cast<std::size_t>(pixel_format) <
static_cast<std::size_t>(PixelFormat::MaxDepthFormat)) {
return SurfaceType::Depth;
}
if (static_cast<std::size_t>(pixel_format) <
static_cast<std::size_t>(PixelFormat::MaxDepthStencilFormat)) {
return SurfaceType::DepthStencil;
}
// TODO(Subv): Implement the other formats
ASSERT(false);
return SurfaceType::Invalid;
}
/// Returns the sizer in bytes of the specified pixel format
static constexpr u32 GetBytesPerPixel(PixelFormat pixel_format) {
if (pixel_format == SurfaceParams::PixelFormat::Invalid) {
return 0;
}
return GetFormatBpp(pixel_format) / CHAR_BIT;
} }
/// Returns the rectangle corresponding to this surface /// Returns the rectangle corresponding to this surface

View file

@ -373,6 +373,7 @@ public:
if (sets_cc) { if (sets_cc) {
const std::string zero_condition = "( " + ConvertIntegerSize(value, size) + " == 0 )"; const std::string zero_condition = "( " + ConvertIntegerSize(value, size) + " == 0 )";
SetInternalFlag(InternalFlag::ZeroFlag, zero_condition); SetInternalFlag(InternalFlag::ZeroFlag, zero_condition);
LOG_WARNING(HW_GPU, "Control Codes Imcomplete.");
} }
} }
@ -1525,6 +1526,10 @@ private:
regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b, 1, 1, regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b, 1, 1,
instr.alu.saturate_d, 0, true); instr.alu.saturate_d, 0, true);
if (instr.generates_cc) {
LOG_CRITICAL(HW_GPU, "FMUL Generates an unhandled Control Code");
UNREACHABLE();
}
break; break;
} }
case OpCode::Id::FADD_C: case OpCode::Id::FADD_C:
@ -1535,6 +1540,10 @@ private:
regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1, regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1,
instr.alu.saturate_d, 0, true); instr.alu.saturate_d, 0, true);
if (instr.generates_cc) {
LOG_CRITICAL(HW_GPU, "FADD Generates an unhandled Control Code");
UNREACHABLE();
}
break; break;
} }
case OpCode::Id::MUFU: { case OpCode::Id::MUFU: {
@ -1588,6 +1597,10 @@ private:
'(' + condition + ") ? min(" + parameters + ") : max(" + '(' + condition + ") ? min(" + parameters + ") : max(" +
parameters + ')', parameters + ')',
1, 1, false, 0, true); 1, 1, false, 0, true);
if (instr.generates_cc) {
LOG_CRITICAL(HW_GPU, "FMNMX Generates an unhandled Control Code");
UNREACHABLE();
}
break; break;
} }
case OpCode::Id::RRO_C: case OpCode::Id::RRO_C:
@ -1618,6 +1631,10 @@ private:
regs.GetRegisterAsFloat(instr.gpr8) + " * " + regs.GetRegisterAsFloat(instr.gpr8) + " * " +
GetImmediate32(instr), GetImmediate32(instr),
1, 1, instr.fmul32.saturate, 0, true); 1, 1, instr.fmul32.saturate, 0, true);
if (instr.op_32.generates_cc) {
LOG_CRITICAL(HW_GPU, "FMUL32 Generates an unhandled Control Code");
UNREACHABLE();
}
break; break;
} }
case OpCode::Id::FADD32I: { case OpCode::Id::FADD32I: {
@ -1641,6 +1658,10 @@ private:
} }
regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1, false, 0, true); regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1, false, 0, true);
if (instr.op_32.generates_cc) {
LOG_CRITICAL(HW_GPU, "FADD32 Generates an unhandled Control Code");
UNREACHABLE();
}
break; break;
} }
} }
@ -1661,6 +1682,10 @@ private:
std::to_string(instr.bfe.GetLeftShiftValue() + instr.bfe.shift_position) + ')'; std::to_string(instr.bfe.GetLeftShiftValue() + instr.bfe.shift_position) + ')';
regs.SetRegisterToInteger(instr.gpr0, true, 0, outer_shift, 1, 1); regs.SetRegisterToInteger(instr.gpr0, true, 0, outer_shift, 1, 1);
if (instr.generates_cc) {
LOG_CRITICAL(HW_GPU, "BFE Generates an unhandled Control Code");
UNREACHABLE();
}
break; break;
} }
default: { default: {
@ -1698,12 +1723,20 @@ private:
// Cast to int is superfluous for arithmetic shift, it's only for a logical shift // Cast to int is superfluous for arithmetic shift, it's only for a logical shift
regs.SetRegisterToInteger(instr.gpr0, true, 0, "int(" + op_a + " >> " + op_b + ')', regs.SetRegisterToInteger(instr.gpr0, true, 0, "int(" + op_a + " >> " + op_b + ')',
1, 1); 1, 1);
if (instr.generates_cc) {
LOG_CRITICAL(HW_GPU, "SHR Generates an unhandled Control Code");
UNREACHABLE();
}
break; break;
} }
case OpCode::Id::SHL_C: case OpCode::Id::SHL_C:
case OpCode::Id::SHL_R: case OpCode::Id::SHL_R:
case OpCode::Id::SHL_IMM: case OpCode::Id::SHL_IMM:
regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " << " + op_b, 1, 1); regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " << " + op_b, 1, 1);
if (instr.generates_cc) {
LOG_CRITICAL(HW_GPU, "SHL Generates an unhandled Control Code");
UNREACHABLE();
}
break; break;
default: { default: {
LOG_CRITICAL(HW_GPU, "Unhandled shift instruction: {}", opcode->get().GetName()); LOG_CRITICAL(HW_GPU, "Unhandled shift instruction: {}", opcode->get().GetName());
@ -1723,6 +1756,10 @@ private:
regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1, regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1,
instr.iadd32i.saturate != 0); instr.iadd32i.saturate != 0);
if (instr.op_32.generates_cc) {
LOG_CRITICAL(HW_GPU, "IADD32 Generates an unhandled Control Code");
UNREACHABLE();
}
break; break;
case OpCode::Id::LOP32I: { case OpCode::Id::LOP32I: {
if (instr.alu.lop32i.invert_a) if (instr.alu.lop32i.invert_a)
@ -1734,6 +1771,10 @@ private:
WriteLogicOperation(instr.gpr0, instr.alu.lop32i.operation, op_a, op_b, WriteLogicOperation(instr.gpr0, instr.alu.lop32i.operation, op_a, op_b,
Tegra::Shader::PredicateResultMode::None, Tegra::Shader::PredicateResultMode::None,
Tegra::Shader::Pred::UnusedIndex); Tegra::Shader::Pred::UnusedIndex);
if (instr.op_32.generates_cc) {
LOG_CRITICAL(HW_GPU, "LOP32I Generates an unhandled Control Code");
UNREACHABLE();
}
break; break;
} }
default: { default: {
@ -1770,6 +1811,10 @@ private:
regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1, regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1,
instr.alu.saturate_d); instr.alu.saturate_d);
if (instr.generates_cc) {
LOG_CRITICAL(HW_GPU, "IADD Generates an unhandled Control Code");
UNREACHABLE();
}
break; break;
} }
case OpCode::Id::IADD3_C: case OpCode::Id::IADD3_C:
@ -1831,6 +1876,11 @@ private:
} }
regs.SetRegisterToInteger(instr.gpr0, true, 0, result, 1, 1); regs.SetRegisterToInteger(instr.gpr0, true, 0, result, 1, 1);
if (instr.generates_cc) {
LOG_CRITICAL(HW_GPU, "IADD3 Generates an unhandled Control Code");
UNREACHABLE();
}
break; break;
} }
case OpCode::Id::ISCADD_C: case OpCode::Id::ISCADD_C:
@ -1846,6 +1896,10 @@ private:
regs.SetRegisterToInteger(instr.gpr0, true, 0, regs.SetRegisterToInteger(instr.gpr0, true, 0,
"((" + op_a + " << " + shift + ") + " + op_b + ')', 1, 1); "((" + op_a + " << " + shift + ") + " + op_b + ')', 1, 1);
if (instr.generates_cc) {
LOG_CRITICAL(HW_GPU, "ISCADD Generates an unhandled Control Code");
UNREACHABLE();
}
break; break;
} }
case OpCode::Id::POPC_C: case OpCode::Id::POPC_C:
@ -1877,6 +1931,10 @@ private:
WriteLogicOperation(instr.gpr0, instr.alu.lop.operation, op_a, op_b, WriteLogicOperation(instr.gpr0, instr.alu.lop.operation, op_a, op_b,
instr.alu.lop.pred_result_mode, instr.alu.lop.pred48); instr.alu.lop.pred_result_mode, instr.alu.lop.pred48);
if (instr.generates_cc) {
LOG_CRITICAL(HW_GPU, "LOP Generates an unhandled Control Code");
UNREACHABLE();
}
break; break;
} }
case OpCode::Id::LOP3_C: case OpCode::Id::LOP3_C:
@ -1892,6 +1950,10 @@ private:
} }
WriteLop3Instruction(instr.gpr0, op_a, op_b, op_c, lut); WriteLop3Instruction(instr.gpr0, op_a, op_b, op_c, lut);
if (instr.generates_cc) {
LOG_CRITICAL(HW_GPU, "LOP3 Generates an unhandled Control Code");
UNREACHABLE();
}
break; break;
} }
case OpCode::Id::IMNMX_C: case OpCode::Id::IMNMX_C:
@ -1906,6 +1968,10 @@ private:
'(' + condition + ") ? min(" + parameters + ") : max(" + '(' + condition + ") ? min(" + parameters + ") : max(" +
parameters + ')', parameters + ')',
1, 1); 1, 1);
if (instr.generates_cc) {
LOG_CRITICAL(HW_GPU, "IMNMX Generates an unhandled Control Code");
UNREACHABLE();
}
break; break;
} }
case OpCode::Id::LEA_R2: case OpCode::Id::LEA_R2:
@ -2107,6 +2173,10 @@ private:
regs.SetRegisterToFloat(instr.gpr0, 0, "fma(" + op_a + ", " + op_b + ", " + op_c + ')', regs.SetRegisterToFloat(instr.gpr0, 0, "fma(" + op_a + ", " + op_b + ", " + op_c + ')',
1, 1, instr.alu.saturate_d, 0, true); 1, 1, instr.alu.saturate_d, 0, true);
if (instr.generates_cc) {
LOG_CRITICAL(HW_GPU, "FFMA Generates an unhandled Control Code");
UNREACHABLE();
}
break; break;
} }
@ -2212,6 +2282,11 @@ private:
} }
regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1); regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1);
if (instr.generates_cc) {
LOG_CRITICAL(HW_GPU, "I2F Generates an unhandled Control Code");
UNREACHABLE();
}
break; break;
} }
case OpCode::Id::F2F_R: { case OpCode::Id::F2F_R: {
@ -2250,6 +2325,11 @@ private:
} }
regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1, instr.alu.saturate_d); regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1, instr.alu.saturate_d);
if (instr.generates_cc) {
LOG_CRITICAL(HW_GPU, "F2F Generates an unhandled Control Code");
UNREACHABLE();
}
break; break;
} }
case OpCode::Id::F2I_R: case OpCode::Id::F2I_R:
@ -2299,6 +2379,10 @@ private:
regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_output_signed, 0, op_a, 1, regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_output_signed, 0, op_a, 1,
1, false, 0, instr.conversion.dest_size); 1, false, 0, instr.conversion.dest_size);
if (instr.generates_cc) {
LOG_CRITICAL(HW_GPU, "F2I Generates an unhandled Control Code");
UNREACHABLE();
}
break; break;
} }
default: { default: {
@ -3107,6 +3191,11 @@ private:
regs.SetRegisterToFloat(instr.gpr0, 0, value, 1, 1); regs.SetRegisterToFloat(instr.gpr0, 0, value, 1, 1);
} }
if (instr.generates_cc) {
LOG_CRITICAL(HW_GPU, "PSET Generates an unhandled Control Code");
UNREACHABLE();
}
break; break;
} }
case OpCode::Type::PredicateSetPredicate: { case OpCode::Type::PredicateSetPredicate: {
@ -3372,6 +3461,10 @@ private:
} }
regs.SetRegisterToInteger(instr.gpr0, is_signed, 0, sum, 1, 1); regs.SetRegisterToInteger(instr.gpr0, is_signed, 0, sum, 1, 1);
if (instr.generates_cc) {
LOG_CRITICAL(HW_GPU, "XMAD Generates an unhandled Control Code");
UNREACHABLE();
}
break; break;
} }
default: { default: {
@ -3381,6 +3474,12 @@ private:
EmitFragmentOutputsWrite(); EmitFragmentOutputsWrite();
} }
const Tegra::Shader::ControlCode cc = instr.flow_control_code;
if (cc != Tegra::Shader::ControlCode::T) {
LOG_CRITICAL(HW_GPU, "EXIT Control Code used: {}", static_cast<u32>(cc));
UNREACHABLE();
}
switch (instr.flow.cond) { switch (instr.flow.cond) {
case Tegra::Shader::FlowCondition::Always: case Tegra::Shader::FlowCondition::Always:
shader.AddLine("return true;"); shader.AddLine("return true;");
@ -3410,6 +3509,11 @@ private:
// Enclose "discard" in a conditional, so that GLSL compilation does not complain // Enclose "discard" in a conditional, so that GLSL compilation does not complain
// about unexecuted instructions that may follow this. // about unexecuted instructions that may follow this.
const Tegra::Shader::ControlCode cc = instr.flow_control_code;
if (cc != Tegra::Shader::ControlCode::T) {
LOG_CRITICAL(HW_GPU, "KIL Control Code used: {}", static_cast<u32>(cc));
UNREACHABLE();
}
shader.AddLine("if (true) {"); shader.AddLine("if (true) {");
++shader.scope; ++shader.scope;
shader.AddLine("discard;"); shader.AddLine("discard;");
@ -3467,6 +3571,11 @@ private:
case OpCode::Id::BRA: { case OpCode::Id::BRA: {
ASSERT_MSG(instr.bra.constant_buffer == 0, ASSERT_MSG(instr.bra.constant_buffer == 0,
"BRA with constant buffers are not implemented"); "BRA with constant buffers are not implemented");
const Tegra::Shader::ControlCode cc = instr.flow_control_code;
if (cc != Tegra::Shader::ControlCode::T) {
LOG_CRITICAL(HW_GPU, "BRA Control Code used: {}", static_cast<u32>(cc));
UNREACHABLE();
}
const u32 target = offset + instr.bra.GetBranchTarget(); const u32 target = offset + instr.bra.GetBranchTarget();
shader.AddLine("{ jmp_to = " + std::to_string(target) + "u; break; }"); shader.AddLine("{ jmp_to = " + std::to_string(target) + "u; break; }");
break; break;
@ -3507,13 +3616,21 @@ private:
} }
case OpCode::Id::SYNC: { case OpCode::Id::SYNC: {
// The SYNC opcode jumps to the address previously set by the SSY opcode // The SYNC opcode jumps to the address previously set by the SSY opcode
ASSERT(instr.flow.cond == Tegra::Shader::FlowCondition::Always); const Tegra::Shader::ControlCode cc = instr.flow_control_code;
if (cc != Tegra::Shader::ControlCode::T) {
LOG_CRITICAL(HW_GPU, "SYNC Control Code used: {}", static_cast<u32>(cc));
UNREACHABLE();
}
EmitPopFromFlowStack(); EmitPopFromFlowStack();
break; break;
} }
case OpCode::Id::BRK: { case OpCode::Id::BRK: {
// The BRK opcode jumps to the address previously set by the PBK opcode // The BRK opcode jumps to the address previously set by the PBK opcode
ASSERT(instr.flow.cond == Tegra::Shader::FlowCondition::Always); const Tegra::Shader::ControlCode cc = instr.flow_control_code;
if (cc != Tegra::Shader::ControlCode::T) {
LOG_CRITICAL(HW_GPU, "BRK Control Code used: {}", static_cast<u32>(cc));
UNREACHABLE();
}
EmitPopFromFlowStack(); EmitPopFromFlowStack();
break; break;
} }
@ -3543,6 +3660,11 @@ private:
regs.SetRegisterToInteger(instr.gpr0, result_signed, 1, result, 1, 1, regs.SetRegisterToInteger(instr.gpr0, result_signed, 1, result, 1, 1,
instr.vmad.saturate == 1, 0, Register::Size::Word, instr.vmad.saturate == 1, 0, Register::Size::Word,
instr.vmad.cc); instr.vmad.cc);
if (instr.generates_cc) {
LOG_CRITICAL(HW_GPU, "VMAD Generates an unhandled Control Code");
UNREACHABLE();
}
break; break;
} }
case OpCode::Id::VSETP: { case OpCode::Id::VSETP: {

View file

@ -89,7 +89,18 @@ OpenGLState::OpenGLState() {
point.size = 1; point.size = 1;
} }
void OpenGLState::Apply() const { void OpenGLState::ApplyDefaultState() {
glDisable(GL_FRAMEBUFFER_SRGB);
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
glDisable(GL_PRIMITIVE_RESTART);
glDisable(GL_STENCIL_TEST);
glEnable(GL_BLEND);
glDisable(GL_COLOR_LOGIC_OP);
glDisable(GL_SCISSOR_TEST);
}
void OpenGLState::ApplySRgb() const {
// sRGB // sRGB
if (framebuffer_srgb.enabled != cur_state.framebuffer_srgb.enabled) { if (framebuffer_srgb.enabled != cur_state.framebuffer_srgb.enabled) {
if (framebuffer_srgb.enabled) { if (framebuffer_srgb.enabled) {
@ -100,96 +111,122 @@ void OpenGLState::Apply() const {
glDisable(GL_FRAMEBUFFER_SRGB); glDisable(GL_FRAMEBUFFER_SRGB);
} }
} }
}
void OpenGLState::ApplyCulling() const {
// Culling // Culling
if (cull.enabled != cur_state.cull.enabled) { const bool cull_changed = cull.enabled != cur_state.cull.enabled;
if (cull_changed) {
if (cull.enabled) { if (cull.enabled) {
glEnable(GL_CULL_FACE); glEnable(GL_CULL_FACE);
} else { } else {
glDisable(GL_CULL_FACE); glDisable(GL_CULL_FACE);
} }
} }
if (cull.enabled) {
if (cull_changed || cull.mode != cur_state.cull.mode) {
glCullFace(cull.mode);
}
if (cull.mode != cur_state.cull.mode) { if (cull_changed || cull.front_face != cur_state.cull.front_face) {
glCullFace(cull.mode); glFrontFace(cull.front_face);
} }
if (cull.front_face != cur_state.cull.front_face) {
glFrontFace(cull.front_face);
} }
}
void OpenGLState::ApplyDepth() const {
// Depth test // Depth test
if (depth.test_enabled != cur_state.depth.test_enabled) { const bool depth_test_changed = depth.test_enabled != cur_state.depth.test_enabled;
if (depth_test_changed) {
if (depth.test_enabled) { if (depth.test_enabled) {
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
} else { } else {
glDisable(GL_DEPTH_TEST); glDisable(GL_DEPTH_TEST);
} }
} }
if (depth.test_enabled &&
if (depth.test_func != cur_state.depth.test_func) { (depth_test_changed || depth.test_func != cur_state.depth.test_func)) {
glDepthFunc(depth.test_func); glDepthFunc(depth.test_func);
} }
// Depth mask // Depth mask
if (depth.write_mask != cur_state.depth.write_mask) { if (depth.write_mask != cur_state.depth.write_mask) {
glDepthMask(depth.write_mask); glDepthMask(depth.write_mask);
} }
// Depth range // Depth range
if (depth.depth_range_near != cur_state.depth.depth_range_near || if (depth.depth_range_near != cur_state.depth.depth_range_near ||
depth.depth_range_far != cur_state.depth.depth_range_far) { depth.depth_range_far != cur_state.depth.depth_range_far) {
glDepthRange(depth.depth_range_near, depth.depth_range_far); glDepthRange(depth.depth_range_near, depth.depth_range_far);
} }
}
// Primitive restart void OpenGLState::ApplyPrimitiveRestart() const {
if (primitive_restart.enabled != cur_state.primitive_restart.enabled) { const bool primitive_restart_changed =
primitive_restart.enabled != cur_state.primitive_restart.enabled;
if (primitive_restart_changed) {
if (primitive_restart.enabled) { if (primitive_restart.enabled) {
glEnable(GL_PRIMITIVE_RESTART); glEnable(GL_PRIMITIVE_RESTART);
} else { } else {
glDisable(GL_PRIMITIVE_RESTART); glDisable(GL_PRIMITIVE_RESTART);
} }
} }
if (primitive_restart.index != cur_state.primitive_restart.index) { if (primitive_restart_changed ||
(primitive_restart.enabled &&
primitive_restart.index != cur_state.primitive_restart.index)) {
glPrimitiveRestartIndex(primitive_restart.index); glPrimitiveRestartIndex(primitive_restart.index);
} }
}
// Color mask void OpenGLState::ApplyStencilTest() const {
if (color_mask.red_enabled != cur_state.color_mask.red_enabled || const bool stencil_test_changed = stencil.test_enabled != cur_state.stencil.test_enabled;
color_mask.green_enabled != cur_state.color_mask.green_enabled || if (stencil_test_changed) {
color_mask.blue_enabled != cur_state.color_mask.blue_enabled ||
color_mask.alpha_enabled != cur_state.color_mask.alpha_enabled) {
glColorMask(color_mask.red_enabled, color_mask.green_enabled, color_mask.blue_enabled,
color_mask.alpha_enabled);
}
// Stencil test
if (stencil.test_enabled != cur_state.stencil.test_enabled) {
if (stencil.test_enabled) { if (stencil.test_enabled) {
glEnable(GL_STENCIL_TEST); glEnable(GL_STENCIL_TEST);
} else { } else {
glDisable(GL_STENCIL_TEST); glDisable(GL_STENCIL_TEST);
} }
} }
auto config_stencil = [](GLenum face, const auto& config, const auto& prev_config) { if (stencil.test_enabled) {
if (config.test_func != prev_config.test_func || config.test_ref != prev_config.test_ref || auto config_stencil = [stencil_test_changed](GLenum face, const auto& config,
config.test_mask != prev_config.test_mask) { const auto& prev_config) {
glStencilFuncSeparate(face, config.test_func, config.test_ref, config.test_mask); if (stencil_test_changed || config.test_func != prev_config.test_func ||
} config.test_ref != prev_config.test_ref ||
if (config.action_depth_fail != prev_config.action_depth_fail || config.test_mask != prev_config.test_mask) {
config.action_depth_pass != prev_config.action_depth_pass || glStencilFuncSeparate(face, config.test_func, config.test_ref, config.test_mask);
config.action_stencil_fail != prev_config.action_stencil_fail) { }
glStencilOpSeparate(face, config.action_stencil_fail, config.action_depth_fail, if (stencil_test_changed || config.action_depth_fail != prev_config.action_depth_fail ||
config.action_depth_pass); config.action_depth_pass != prev_config.action_depth_pass ||
} config.action_stencil_fail != prev_config.action_stencil_fail) {
if (config.write_mask != prev_config.write_mask) { glStencilOpSeparate(face, config.action_stencil_fail, config.action_depth_fail,
glStencilMaskSeparate(face, config.write_mask); config.action_depth_pass);
} }
}; if (config.write_mask != prev_config.write_mask) {
config_stencil(GL_FRONT, stencil.front, cur_state.stencil.front); glStencilMaskSeparate(face, config.write_mask);
config_stencil(GL_BACK, stencil.back, cur_state.stencil.back); }
};
config_stencil(GL_FRONT, stencil.front, cur_state.stencil.front);
config_stencil(GL_BACK, stencil.back, cur_state.stencil.back);
}
}
// Blending void OpenGLState::ApplyScissorTest() const {
if (blend.enabled != cur_state.blend.enabled) { const bool scissor_changed = scissor.enabled != cur_state.scissor.enabled;
if (scissor_changed) {
if (scissor.enabled) {
glEnable(GL_SCISSOR_TEST);
} else {
glDisable(GL_SCISSOR_TEST);
}
}
if (scissor_changed || scissor_changed || scissor.x != cur_state.scissor.x ||
scissor.y != cur_state.scissor.y || scissor.width != cur_state.scissor.width ||
scissor.height != cur_state.scissor.height) {
glScissor(scissor.x, scissor.y, scissor.width, scissor.height);
}
}
void OpenGLState::ApplyBlending() const {
const bool blend_changed = blend.enabled != cur_state.blend.enabled;
if (blend_changed) {
if (blend.enabled) { if (blend.enabled) {
ASSERT(!logic_op.enabled); ASSERT(!logic_op.enabled);
glEnable(GL_BLEND); glEnable(GL_BLEND);
@ -197,29 +234,32 @@ void OpenGLState::Apply() const {
glDisable(GL_BLEND); glDisable(GL_BLEND);
} }
} }
if (blend.enabled) {
if (blend_changed || blend.color.red != cur_state.blend.color.red ||
blend.color.green != cur_state.blend.color.green ||
blend.color.blue != cur_state.blend.color.blue ||
blend.color.alpha != cur_state.blend.color.alpha) {
glBlendColor(blend.color.red, blend.color.green, blend.color.blue, blend.color.alpha);
}
if (blend.color.red != cur_state.blend.color.red || if (blend_changed || blend.src_rgb_func != cur_state.blend.src_rgb_func ||
blend.color.green != cur_state.blend.color.green || blend.dst_rgb_func != cur_state.blend.dst_rgb_func ||
blend.color.blue != cur_state.blend.color.blue || blend.src_a_func != cur_state.blend.src_a_func ||
blend.color.alpha != cur_state.blend.color.alpha) { blend.dst_a_func != cur_state.blend.dst_a_func) {
glBlendColor(blend.color.red, blend.color.green, blend.color.blue, blend.color.alpha); glBlendFuncSeparate(blend.src_rgb_func, blend.dst_rgb_func, blend.src_a_func,
blend.dst_a_func);
}
if (blend_changed || blend.rgb_equation != cur_state.blend.rgb_equation ||
blend.a_equation != cur_state.blend.a_equation) {
glBlendEquationSeparate(blend.rgb_equation, blend.a_equation);
}
} }
}
if (blend.src_rgb_func != cur_state.blend.src_rgb_func || void OpenGLState::ApplyLogicOp() const {
blend.dst_rgb_func != cur_state.blend.dst_rgb_func || const bool logic_op_changed = logic_op.enabled != cur_state.logic_op.enabled;
blend.src_a_func != cur_state.blend.src_a_func || if (logic_op_changed) {
blend.dst_a_func != cur_state.blend.dst_a_func) {
glBlendFuncSeparate(blend.src_rgb_func, blend.dst_rgb_func, blend.src_a_func,
blend.dst_a_func);
}
if (blend.rgb_equation != cur_state.blend.rgb_equation ||
blend.a_equation != cur_state.blend.a_equation) {
glBlendEquationSeparate(blend.rgb_equation, blend.a_equation);
}
// Logic Operation
if (logic_op.enabled != cur_state.logic_op.enabled) {
if (logic_op.enabled) { if (logic_op.enabled) {
ASSERT(!blend.enabled); ASSERT(!blend.enabled);
glEnable(GL_COLOR_LOGIC_OP); glEnable(GL_COLOR_LOGIC_OP);
@ -228,11 +268,13 @@ void OpenGLState::Apply() const {
} }
} }
if (logic_op.operation != cur_state.logic_op.operation) { if (logic_op.enabled &&
(logic_op_changed || logic_op.operation != cur_state.logic_op.operation)) {
glLogicOp(logic_op.operation); glLogicOp(logic_op.operation);
} }
}
// Textures void OpenGLState::ApplyTextures() const {
for (std::size_t i = 0; i < std::size(texture_units); ++i) { for (std::size_t i = 0; i < std::size(texture_units); ++i) {
const auto& texture_unit = texture_units[i]; const auto& texture_unit = texture_units[i];
const auto& cur_state_texture_unit = cur_state.texture_units[i]; const auto& cur_state_texture_unit = cur_state.texture_units[i];
@ -251,28 +293,29 @@ void OpenGLState::Apply() const {
glTexParameteriv(texture_unit.target, GL_TEXTURE_SWIZZLE_RGBA, mask.data()); glTexParameteriv(texture_unit.target, GL_TEXTURE_SWIZZLE_RGBA, mask.data());
} }
} }
}
// Samplers void OpenGLState::ApplySamplers() const {
{ bool has_delta{};
bool has_delta{}; std::size_t first{}, last{};
std::size_t first{}, last{}; std::array<GLuint, Tegra::Engines::Maxwell3D::Regs::NumTextureSamplers> samplers;
std::array<GLuint, Tegra::Engines::Maxwell3D::Regs::NumTextureSamplers> samplers; for (std::size_t i = 0; i < std::size(samplers); ++i) {
for (std::size_t i = 0; i < std::size(samplers); ++i) { samplers[i] = texture_units[i].sampler;
samplers[i] = texture_units[i].sampler; if (samplers[i] != cur_state.texture_units[i].sampler) {
if (samplers[i] != cur_state.texture_units[i].sampler) { if (!has_delta) {
if (!has_delta) { first = i;
first = i; has_delta = true;
has_delta = true;
}
last = i;
} }
} last = i;
if (has_delta) {
glBindSamplers(static_cast<GLuint>(first), static_cast<GLsizei>(last - first + 1),
samplers.data());
} }
} }
if (has_delta) {
glBindSamplers(static_cast<GLuint>(first), static_cast<GLsizei>(last - first + 1),
samplers.data());
}
}
void OpenGLState::Apply() const {
// Framebuffer // Framebuffer
if (draw.read_framebuffer != cur_state.draw.read_framebuffer) { if (draw.read_framebuffer != cur_state.draw.read_framebuffer) {
glBindFramebuffer(GL_READ_FRAMEBUFFER, draw.read_framebuffer); glBindFramebuffer(GL_READ_FRAMEBUFFER, draw.read_framebuffer);
@ -305,27 +348,12 @@ void OpenGLState::Apply() const {
if (draw.program_pipeline != cur_state.draw.program_pipeline) { if (draw.program_pipeline != cur_state.draw.program_pipeline) {
glBindProgramPipeline(draw.program_pipeline); glBindProgramPipeline(draw.program_pipeline);
} }
// Viewport
// Scissor test
if (scissor.enabled != cur_state.scissor.enabled) {
if (scissor.enabled) {
glEnable(GL_SCISSOR_TEST);
} else {
glDisable(GL_SCISSOR_TEST);
}
}
if (scissor.x != cur_state.scissor.x || scissor.y != cur_state.scissor.y ||
scissor.width != cur_state.scissor.width || scissor.height != cur_state.scissor.height) {
glScissor(scissor.x, scissor.y, scissor.width, scissor.height);
}
if (viewport.x != cur_state.viewport.x || viewport.y != cur_state.viewport.y || if (viewport.x != cur_state.viewport.x || viewport.y != cur_state.viewport.y ||
viewport.width != cur_state.viewport.width || viewport.width != cur_state.viewport.width ||
viewport.height != cur_state.viewport.height) { viewport.height != cur_state.viewport.height) {
glViewport(viewport.x, viewport.y, viewport.width, viewport.height); glViewport(viewport.x, viewport.y, viewport.width, viewport.height);
} }
// Clip distance // Clip distance
for (std::size_t i = 0; i < clip_distance.size(); ++i) { for (std::size_t i = 0; i < clip_distance.size(); ++i) {
if (clip_distance[i] != cur_state.clip_distance[i]) { if (clip_distance[i] != cur_state.clip_distance[i]) {
@ -336,12 +364,28 @@ void OpenGLState::Apply() const {
} }
} }
} }
// Color mask
if (color_mask.red_enabled != cur_state.color_mask.red_enabled ||
color_mask.green_enabled != cur_state.color_mask.green_enabled ||
color_mask.blue_enabled != cur_state.color_mask.blue_enabled ||
color_mask.alpha_enabled != cur_state.color_mask.alpha_enabled) {
glColorMask(color_mask.red_enabled, color_mask.green_enabled, color_mask.blue_enabled,
color_mask.alpha_enabled);
}
// Point // Point
if (point.size != cur_state.point.size) { if (point.size != cur_state.point.size) {
glPointSize(point.size); glPointSize(point.size);
} }
ApplyScissorTest();
ApplyStencilTest();
ApplySRgb();
ApplyCulling();
ApplyDepth();
ApplyPrimitiveRestart();
ApplyBlending();
ApplyLogicOp();
ApplyTextures();
ApplySamplers();
cur_state = *this; cur_state = *this;
} }

View file

@ -173,7 +173,8 @@ public:
} }
/// Apply this state as the current OpenGL state /// Apply this state as the current OpenGL state
void Apply() const; void Apply() const;
/// Set the initial OpenGL state
static void ApplyDefaultState();
/// Resets any references to the given resource /// Resets any references to the given resource
OpenGLState& UnbindTexture(GLuint handle); OpenGLState& UnbindTexture(GLuint handle);
OpenGLState& ResetSampler(GLuint handle); OpenGLState& ResetSampler(GLuint handle);
@ -188,6 +189,16 @@ private:
// Workaround for sRGB problems caused by // Workaround for sRGB problems caused by
// QT not supporting srgb output // QT not supporting srgb output
static bool s_rgb_used; static bool s_rgb_used;
void ApplySRgb() const;
void ApplyCulling() const;
void ApplyDepth() const;
void ApplyPrimitiveRestart() const;
void ApplyStencilTest() const;
void ApplyScissorTest() const;
void ApplyBlending() const;
void ApplyLogicOp() const;
void ApplyTextures() const;
void ApplySamplers() const;
}; };
} // namespace OpenGL } // namespace OpenGL

499
src/video_core/surface.cpp Normal file
View file

@ -0,0 +1,499 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/common_types.h"
#include "common/math_util.h"
#include "video_core/surface.h"
namespace VideoCore::Surface {
SurfaceTarget SurfaceTargetFromTextureType(Tegra::Texture::TextureType texture_type) {
switch (texture_type) {
case Tegra::Texture::TextureType::Texture1D:
return SurfaceTarget::Texture1D;
case Tegra::Texture::TextureType::Texture2D:
case Tegra::Texture::TextureType::Texture2DNoMipmap:
return SurfaceTarget::Texture2D;
case Tegra::Texture::TextureType::Texture3D:
return SurfaceTarget::Texture3D;
case Tegra::Texture::TextureType::TextureCubemap:
return SurfaceTarget::TextureCubemap;
case Tegra::Texture::TextureType::Texture1DArray:
return SurfaceTarget::Texture1DArray;
case Tegra::Texture::TextureType::Texture2DArray:
return SurfaceTarget::Texture2DArray;
default:
LOG_CRITICAL(HW_GPU, "Unimplemented texture_type={}", static_cast<u32>(texture_type));
UNREACHABLE();
return SurfaceTarget::Texture2D;
}
}
bool SurfaceTargetIsLayered(SurfaceTarget target) {
switch (target) {
case SurfaceTarget::Texture1D:
case SurfaceTarget::Texture2D:
case SurfaceTarget::Texture3D:
return false;
case SurfaceTarget::Texture1DArray:
case SurfaceTarget::Texture2DArray:
case SurfaceTarget::TextureCubemap:
return true;
default:
LOG_CRITICAL(HW_GPU, "Unimplemented surface_target={}", static_cast<u32>(target));
UNREACHABLE();
return false;
}
}
PixelFormat PixelFormatFromDepthFormat(Tegra::DepthFormat format) {
switch (format) {
case Tegra::DepthFormat::S8_Z24_UNORM:
return PixelFormat::S8Z24;
case Tegra::DepthFormat::Z24_S8_UNORM:
return PixelFormat::Z24S8;
case Tegra::DepthFormat::Z32_FLOAT:
return PixelFormat::Z32F;
case Tegra::DepthFormat::Z16_UNORM:
return PixelFormat::Z16;
case Tegra::DepthFormat::Z32_S8_X24_FLOAT:
return PixelFormat::Z32FS8;
default:
LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
UNREACHABLE();
}
}
PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format) {
switch (format) {
// TODO (Hexagon12): Converting SRGBA to RGBA is a hack and doesn't completely correct the
// gamma.
case Tegra::RenderTargetFormat::RGBA8_SRGB:
return PixelFormat::RGBA8_SRGB;
case Tegra::RenderTargetFormat::RGBA8_UNORM:
return PixelFormat::ABGR8U;
case Tegra::RenderTargetFormat::RGBA8_SNORM:
return PixelFormat::ABGR8S;
case Tegra::RenderTargetFormat::RGBA8_UINT:
return PixelFormat::ABGR8UI;
case Tegra::RenderTargetFormat::BGRA8_SRGB:
return PixelFormat::BGRA8_SRGB;
case Tegra::RenderTargetFormat::BGRA8_UNORM:
return PixelFormat::BGRA8;
case Tegra::RenderTargetFormat::RGB10_A2_UNORM:
return PixelFormat::A2B10G10R10U;
case Tegra::RenderTargetFormat::RGBA16_FLOAT:
return PixelFormat::RGBA16F;
case Tegra::RenderTargetFormat::RGBA16_UNORM:
return PixelFormat::RGBA16U;
case Tegra::RenderTargetFormat::RGBA16_UINT:
return PixelFormat::RGBA16UI;
case Tegra::RenderTargetFormat::RGBA32_FLOAT:
return PixelFormat::RGBA32F;
case Tegra::RenderTargetFormat::RG32_FLOAT:
return PixelFormat::RG32F;
case Tegra::RenderTargetFormat::R11G11B10_FLOAT:
return PixelFormat::R11FG11FB10F;
case Tegra::RenderTargetFormat::B5G6R5_UNORM:
return PixelFormat::B5G6R5U;
case Tegra::RenderTargetFormat::BGR5A1_UNORM:
return PixelFormat::A1B5G5R5U;
case Tegra::RenderTargetFormat::RGBA32_UINT:
return PixelFormat::RGBA32UI;
case Tegra::RenderTargetFormat::R8_UNORM:
return PixelFormat::R8U;
case Tegra::RenderTargetFormat::R8_UINT:
return PixelFormat::R8UI;
case Tegra::RenderTargetFormat::RG16_FLOAT:
return PixelFormat::RG16F;
case Tegra::RenderTargetFormat::RG16_UINT:
return PixelFormat::RG16UI;
case Tegra::RenderTargetFormat::RG16_SINT:
return PixelFormat::RG16I;
case Tegra::RenderTargetFormat::RG16_UNORM:
return PixelFormat::RG16;
case Tegra::RenderTargetFormat::RG16_SNORM:
return PixelFormat::RG16S;
case Tegra::RenderTargetFormat::RG8_UNORM:
return PixelFormat::RG8U;
case Tegra::RenderTargetFormat::RG8_SNORM:
return PixelFormat::RG8S;
case Tegra::RenderTargetFormat::R16_FLOAT:
return PixelFormat::R16F;
case Tegra::RenderTargetFormat::R16_UNORM:
return PixelFormat::R16U;
case Tegra::RenderTargetFormat::R16_SNORM:
return PixelFormat::R16S;
case Tegra::RenderTargetFormat::R16_UINT:
return PixelFormat::R16UI;
case Tegra::RenderTargetFormat::R16_SINT:
return PixelFormat::R16I;
case Tegra::RenderTargetFormat::R32_FLOAT:
return PixelFormat::R32F;
case Tegra::RenderTargetFormat::R32_UINT:
return PixelFormat::R32UI;
case Tegra::RenderTargetFormat::RG32_UINT:
return PixelFormat::RG32UI;
default:
LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
UNREACHABLE();
}
}
PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format,
Tegra::Texture::ComponentType component_type,
bool is_srgb) {
// TODO(Subv): Properly implement this
switch (format) {
case Tegra::Texture::TextureFormat::A8R8G8B8:
if (is_srgb) {
return PixelFormat::RGBA8_SRGB;
}
switch (component_type) {
case Tegra::Texture::ComponentType::UNORM:
return PixelFormat::ABGR8U;
case Tegra::Texture::ComponentType::SNORM:
return PixelFormat::ABGR8S;
case Tegra::Texture::ComponentType::UINT:
return PixelFormat::ABGR8UI;
}
LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type));
UNREACHABLE();
case Tegra::Texture::TextureFormat::B5G6R5:
switch (component_type) {
case Tegra::Texture::ComponentType::UNORM:
return PixelFormat::B5G6R5U;
}
LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type));
UNREACHABLE();
case Tegra::Texture::TextureFormat::A2B10G10R10:
switch (component_type) {
case Tegra::Texture::ComponentType::UNORM:
return PixelFormat::A2B10G10R10U;
}
LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type));
UNREACHABLE();
case Tegra::Texture::TextureFormat::A1B5G5R5:
switch (component_type) {
case Tegra::Texture::ComponentType::UNORM:
return PixelFormat::A1B5G5R5U;
}
LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type));
UNREACHABLE();
case Tegra::Texture::TextureFormat::R8:
switch (component_type) {
case Tegra::Texture::ComponentType::UNORM:
return PixelFormat::R8U;
case Tegra::Texture::ComponentType::UINT:
return PixelFormat::R8UI;
}
LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type));
UNREACHABLE();
case Tegra::Texture::TextureFormat::G8R8:
switch (component_type) {
case Tegra::Texture::ComponentType::UNORM:
return PixelFormat::G8R8U;
case Tegra::Texture::ComponentType::SNORM:
return PixelFormat::G8R8S;
}
LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type));
UNREACHABLE();
case Tegra::Texture::TextureFormat::R16_G16_B16_A16:
switch (component_type) {
case Tegra::Texture::ComponentType::UNORM:
return PixelFormat::RGBA16U;
case Tegra::Texture::ComponentType::FLOAT:
return PixelFormat::RGBA16F;
}
LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type));
UNREACHABLE();
case Tegra::Texture::TextureFormat::BF10GF11RF11:
switch (component_type) {
case Tegra::Texture::ComponentType::FLOAT:
return PixelFormat::R11FG11FB10F;
}
LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type));
UNREACHABLE();
case Tegra::Texture::TextureFormat::R32_G32_B32_A32:
switch (component_type) {
case Tegra::Texture::ComponentType::FLOAT:
return PixelFormat::RGBA32F;
case Tegra::Texture::ComponentType::UINT:
return PixelFormat::RGBA32UI;
}
LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type));
UNREACHABLE();
case Tegra::Texture::TextureFormat::R32_G32:
switch (component_type) {
case Tegra::Texture::ComponentType::FLOAT:
return PixelFormat::RG32F;
case Tegra::Texture::ComponentType::UINT:
return PixelFormat::RG32UI;
}
LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type));
UNREACHABLE();
case Tegra::Texture::TextureFormat::R32_G32_B32:
switch (component_type) {
case Tegra::Texture::ComponentType::FLOAT:
return PixelFormat::RGB32F;
}
LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type));
UNREACHABLE();
case Tegra::Texture::TextureFormat::R16:
switch (component_type) {
case Tegra::Texture::ComponentType::FLOAT:
return PixelFormat::R16F;
case Tegra::Texture::ComponentType::UNORM:
return PixelFormat::R16U;
case Tegra::Texture::ComponentType::SNORM:
return PixelFormat::R16S;
case Tegra::Texture::ComponentType::UINT:
return PixelFormat::R16UI;
case Tegra::Texture::ComponentType::SINT:
return PixelFormat::R16I;
}
LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type));
UNREACHABLE();
case Tegra::Texture::TextureFormat::R32:
switch (component_type) {
case Tegra::Texture::ComponentType::FLOAT:
return PixelFormat::R32F;
case Tegra::Texture::ComponentType::UINT:
return PixelFormat::R32UI;
}
LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type));
UNREACHABLE();
case Tegra::Texture::TextureFormat::ZF32:
return PixelFormat::Z32F;
case Tegra::Texture::TextureFormat::Z16:
return PixelFormat::Z16;
case Tegra::Texture::TextureFormat::Z24S8:
return PixelFormat::Z24S8;
case Tegra::Texture::TextureFormat::DXT1:
return is_srgb ? PixelFormat::DXT1_SRGB : PixelFormat::DXT1;
case Tegra::Texture::TextureFormat::DXT23:
return is_srgb ? PixelFormat::DXT23_SRGB : PixelFormat::DXT23;
case Tegra::Texture::TextureFormat::DXT45:
return is_srgb ? PixelFormat::DXT45_SRGB : PixelFormat::DXT45;
case Tegra::Texture::TextureFormat::DXN1:
return PixelFormat::DXN1;
case Tegra::Texture::TextureFormat::DXN2:
switch (component_type) {
case Tegra::Texture::ComponentType::UNORM:
return PixelFormat::DXN2UNORM;
case Tegra::Texture::ComponentType::SNORM:
return PixelFormat::DXN2SNORM;
}
LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type));
UNREACHABLE();
case Tegra::Texture::TextureFormat::BC7U:
return is_srgb ? PixelFormat::BC7U_SRGB : PixelFormat::BC7U;
case Tegra::Texture::TextureFormat::BC6H_UF16:
return PixelFormat::BC6H_UF16;
case Tegra::Texture::TextureFormat::BC6H_SF16:
return PixelFormat::BC6H_SF16;
case Tegra::Texture::TextureFormat::ASTC_2D_4X4:
return is_srgb ? PixelFormat::ASTC_2D_4X4_SRGB : PixelFormat::ASTC_2D_4X4;
case Tegra::Texture::TextureFormat::ASTC_2D_5X4:
return is_srgb ? PixelFormat::ASTC_2D_5X4_SRGB : PixelFormat::ASTC_2D_5X4;
case Tegra::Texture::TextureFormat::ASTC_2D_8X8:
return is_srgb ? PixelFormat::ASTC_2D_8X8_SRGB : PixelFormat::ASTC_2D_8X8;
case Tegra::Texture::TextureFormat::ASTC_2D_8X5:
return is_srgb ? PixelFormat::ASTC_2D_8X5_SRGB : PixelFormat::ASTC_2D_8X5;
case Tegra::Texture::TextureFormat::R16_G16:
switch (component_type) {
case Tegra::Texture::ComponentType::FLOAT:
return PixelFormat::RG16F;
case Tegra::Texture::ComponentType::UNORM:
return PixelFormat::RG16;
case Tegra::Texture::ComponentType::SNORM:
return PixelFormat::RG16S;
case Tegra::Texture::ComponentType::UINT:
return PixelFormat::RG16UI;
case Tegra::Texture::ComponentType::SINT:
return PixelFormat::RG16I;
}
LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type));
UNREACHABLE();
default:
LOG_CRITICAL(HW_GPU, "Unimplemented format={}, component_type={}", static_cast<u32>(format),
static_cast<u32>(component_type));
UNREACHABLE();
}
}
ComponentType ComponentTypeFromTexture(Tegra::Texture::ComponentType type) {
// TODO(Subv): Implement more component types
switch (type) {
case Tegra::Texture::ComponentType::UNORM:
return ComponentType::UNorm;
case Tegra::Texture::ComponentType::FLOAT:
return ComponentType::Float;
case Tegra::Texture::ComponentType::SNORM:
return ComponentType::SNorm;
case Tegra::Texture::ComponentType::UINT:
return ComponentType::UInt;
case Tegra::Texture::ComponentType::SINT:
return ComponentType::SInt;
default:
LOG_CRITICAL(HW_GPU, "Unimplemented component type={}", static_cast<u32>(type));
UNREACHABLE();
}
}
ComponentType ComponentTypeFromRenderTarget(Tegra::RenderTargetFormat format) {
// TODO(Subv): Implement more render targets
switch (format) {
case Tegra::RenderTargetFormat::RGBA8_UNORM:
case Tegra::RenderTargetFormat::RGBA8_SRGB:
case Tegra::RenderTargetFormat::BGRA8_UNORM:
case Tegra::RenderTargetFormat::BGRA8_SRGB:
case Tegra::RenderTargetFormat::RGB10_A2_UNORM:
case Tegra::RenderTargetFormat::R8_UNORM:
case Tegra::RenderTargetFormat::RG16_UNORM:
case Tegra::RenderTargetFormat::R16_UNORM:
case Tegra::RenderTargetFormat::B5G6R5_UNORM:
case Tegra::RenderTargetFormat::BGR5A1_UNORM:
case Tegra::RenderTargetFormat::RG8_UNORM:
case Tegra::RenderTargetFormat::RGBA16_UNORM:
return ComponentType::UNorm;
case Tegra::RenderTargetFormat::RGBA8_SNORM:
case Tegra::RenderTargetFormat::RG16_SNORM:
case Tegra::RenderTargetFormat::R16_SNORM:
case Tegra::RenderTargetFormat::RG8_SNORM:
return ComponentType::SNorm;
case Tegra::RenderTargetFormat::RGBA16_FLOAT:
case Tegra::RenderTargetFormat::R11G11B10_FLOAT:
case Tegra::RenderTargetFormat::RGBA32_FLOAT:
case Tegra::RenderTargetFormat::RG32_FLOAT:
case Tegra::RenderTargetFormat::RG16_FLOAT:
case Tegra::RenderTargetFormat::R16_FLOAT:
case Tegra::RenderTargetFormat::R32_FLOAT:
return ComponentType::Float;
case Tegra::RenderTargetFormat::RGBA32_UINT:
case Tegra::RenderTargetFormat::RGBA16_UINT:
case Tegra::RenderTargetFormat::RG16_UINT:
case Tegra::RenderTargetFormat::R8_UINT:
case Tegra::RenderTargetFormat::R16_UINT:
case Tegra::RenderTargetFormat::RG32_UINT:
case Tegra::RenderTargetFormat::R32_UINT:
case Tegra::RenderTargetFormat::RGBA8_UINT:
return ComponentType::UInt;
case Tegra::RenderTargetFormat::RG16_SINT:
case Tegra::RenderTargetFormat::R16_SINT:
return ComponentType::SInt;
default:
LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
UNREACHABLE();
}
}
PixelFormat PixelFormatFromGPUPixelFormat(Tegra::FramebufferConfig::PixelFormat format) {
switch (format) {
case Tegra::FramebufferConfig::PixelFormat::ABGR8:
return PixelFormat::ABGR8U;
default:
LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
UNREACHABLE();
}
}
ComponentType ComponentTypeFromDepthFormat(Tegra::DepthFormat format) {
switch (format) {
case Tegra::DepthFormat::Z16_UNORM:
case Tegra::DepthFormat::S8_Z24_UNORM:
case Tegra::DepthFormat::Z24_S8_UNORM:
return ComponentType::UNorm;
case Tegra::DepthFormat::Z32_FLOAT:
case Tegra::DepthFormat::Z32_S8_X24_FLOAT:
return ComponentType::Float;
default:
LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
UNREACHABLE();
}
}
SurfaceType GetFormatType(PixelFormat pixel_format) {
if (static_cast<std::size_t>(pixel_format) <
static_cast<std::size_t>(PixelFormat::MaxColorFormat)) {
return SurfaceType::ColorTexture;
}
if (static_cast<std::size_t>(pixel_format) <
static_cast<std::size_t>(PixelFormat::MaxDepthFormat)) {
return SurfaceType::Depth;
}
if (static_cast<std::size_t>(pixel_format) <
static_cast<std::size_t>(PixelFormat::MaxDepthStencilFormat)) {
return SurfaceType::DepthStencil;
}
// TODO(Subv): Implement the other formats
ASSERT(false);
return SurfaceType::Invalid;
}
bool IsPixelFormatASTC(PixelFormat format) {
switch (format) {
case PixelFormat::ASTC_2D_4X4:
case PixelFormat::ASTC_2D_5X4:
case PixelFormat::ASTC_2D_8X8:
case PixelFormat::ASTC_2D_8X5:
case PixelFormat::ASTC_2D_4X4_SRGB:
case PixelFormat::ASTC_2D_5X4_SRGB:
case PixelFormat::ASTC_2D_8X8_SRGB:
case PixelFormat::ASTC_2D_8X5_SRGB:
return true;
default:
return false;
}
}
std::pair<u32, u32> GetASTCBlockSize(PixelFormat format) {
switch (format) {
case PixelFormat::ASTC_2D_4X4:
return {4, 4};
case PixelFormat::ASTC_2D_5X4:
return {5, 4};
case PixelFormat::ASTC_2D_8X8:
return {8, 8};
case PixelFormat::ASTC_2D_8X5:
return {8, 5};
case PixelFormat::ASTC_2D_4X4_SRGB:
return {4, 4};
case PixelFormat::ASTC_2D_5X4_SRGB:
return {5, 4};
case PixelFormat::ASTC_2D_8X8_SRGB:
return {8, 8};
case PixelFormat::ASTC_2D_8X5_SRGB:
return {8, 5};
default:
LOG_CRITICAL(HW_GPU, "Unhandled format: {}", static_cast<u32>(format));
UNREACHABLE();
}
}
bool IsFormatBCn(PixelFormat format) {
switch (format) {
case PixelFormat::DXT1:
case PixelFormat::DXT23:
case PixelFormat::DXT45:
case PixelFormat::DXN1:
case PixelFormat::DXN2SNORM:
case PixelFormat::DXN2UNORM:
case PixelFormat::BC7U:
case PixelFormat::BC6H_UF16:
case PixelFormat::BC6H_SF16:
case PixelFormat::DXT1_SRGB:
case PixelFormat::DXT23_SRGB:
case PixelFormat::DXT45_SRGB:
case PixelFormat::BC7U_SRGB:
return true;
}
return false;
}
} // namespace VideoCore::Surface

385
src/video_core/surface.h Normal file
View file

@ -0,0 +1,385 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <climits>
#include <utility>
#include "common/assert.h"
#include "common/common_types.h"
#include "common/logging/log.h"
#include "video_core/gpu.h"
#include "video_core/textures/texture.h"
namespace VideoCore::Surface {
enum class PixelFormat {
ABGR8U = 0,
ABGR8S = 1,
ABGR8UI = 2,
B5G6R5U = 3,
A2B10G10R10U = 4,
A1B5G5R5U = 5,
R8U = 6,
R8UI = 7,
RGBA16F = 8,
RGBA16U = 9,
RGBA16UI = 10,
R11FG11FB10F = 11,
RGBA32UI = 12,
DXT1 = 13,
DXT23 = 14,
DXT45 = 15,
DXN1 = 16, // This is also known as BC4
DXN2UNORM = 17,
DXN2SNORM = 18,
BC7U = 19,
BC6H_UF16 = 20,
BC6H_SF16 = 21,
ASTC_2D_4X4 = 22,
G8R8U = 23,
G8R8S = 24,
BGRA8 = 25,
RGBA32F = 26,
RG32F = 27,
R32F = 28,
R16F = 29,
R16U = 30,
R16S = 31,
R16UI = 32,
R16I = 33,
RG16 = 34,
RG16F = 35,
RG16UI = 36,
RG16I = 37,
RG16S = 38,
RGB32F = 39,
RGBA8_SRGB = 40,
RG8U = 41,
RG8S = 42,
RG32UI = 43,
R32UI = 44,
ASTC_2D_8X8 = 45,
ASTC_2D_8X5 = 46,
ASTC_2D_5X4 = 47,
BGRA8_SRGB = 48,
DXT1_SRGB = 49,
DXT23_SRGB = 50,
DXT45_SRGB = 51,
BC7U_SRGB = 52,
ASTC_2D_4X4_SRGB = 53,
ASTC_2D_8X8_SRGB = 54,
ASTC_2D_8X5_SRGB = 55,
ASTC_2D_5X4_SRGB = 56,
MaxColorFormat,
// Depth formats
Z32F = 57,
Z16 = 58,
MaxDepthFormat,
// DepthStencil formats
Z24S8 = 59,
S8Z24 = 60,
Z32FS8 = 61,
MaxDepthStencilFormat,
Max = MaxDepthStencilFormat,
Invalid = 255,
};
static constexpr std::size_t MaxPixelFormat = static_cast<std::size_t>(PixelFormat::Max);
enum class ComponentType {
Invalid = 0,
SNorm = 1,
UNorm = 2,
SInt = 3,
UInt = 4,
Float = 5,
};
enum class SurfaceType {
ColorTexture = 0,
Depth = 1,
DepthStencil = 2,
Fill = 3,
Invalid = 4,
};
enum class SurfaceTarget {
Texture1D,
Texture2D,
Texture3D,
Texture1DArray,
Texture2DArray,
TextureCubemap,
};
/**
* Gets the compression factor for the specified PixelFormat. This applies to just the
* "compressed width" and "compressed height", not the overall compression factor of a
* compressed image. This is used for maintaining proper surface sizes for compressed
* texture formats.
*/
static constexpr u32 GetCompressionFactor(PixelFormat format) {
if (format == PixelFormat::Invalid)
return 0;
constexpr std::array<u32, MaxPixelFormat> compression_factor_table = {{
1, // ABGR8U
1, // ABGR8S
1, // ABGR8UI
1, // B5G6R5U
1, // A2B10G10R10U
1, // A1B5G5R5U
1, // R8U
1, // R8UI
1, // RGBA16F
1, // RGBA16U
1, // RGBA16UI
1, // R11FG11FB10F
1, // RGBA32UI
4, // DXT1
4, // DXT23
4, // DXT45
4, // DXN1
4, // DXN2UNORM
4, // DXN2SNORM
4, // BC7U
4, // BC6H_UF16
4, // BC6H_SF16
4, // ASTC_2D_4X4
1, // G8R8U
1, // G8R8S
1, // BGRA8
1, // RGBA32F
1, // RG32F
1, // R32F
1, // R16F
1, // R16U
1, // R16S
1, // R16UI
1, // R16I
1, // RG16
1, // RG16F
1, // RG16UI
1, // RG16I
1, // RG16S
1, // RGB32F
1, // RGBA8_SRGB
1, // RG8U
1, // RG8S
1, // RG32UI
1, // R32UI
4, // ASTC_2D_8X8
4, // ASTC_2D_8X5
4, // ASTC_2D_5X4
1, // BGRA8_SRGB
4, // DXT1_SRGB
4, // DXT23_SRGB
4, // DXT45_SRGB
4, // BC7U_SRGB
4, // ASTC_2D_4X4_SRGB
4, // ASTC_2D_8X8_SRGB
4, // ASTC_2D_8X5_SRGB
4, // ASTC_2D_5X4_SRGB
1, // Z32F
1, // Z16
1, // Z24S8
1, // S8Z24
1, // Z32FS8
}};
ASSERT(static_cast<std::size_t>(format) < compression_factor_table.size());
return compression_factor_table[static_cast<std::size_t>(format)];
}
static constexpr u32 GetDefaultBlockHeight(PixelFormat format) {
if (format == PixelFormat::Invalid)
return 0;
constexpr std::array<u32, MaxPixelFormat> block_height_table = {{
1, // ABGR8U
1, // ABGR8S
1, // ABGR8UI
1, // B5G6R5U
1, // A2B10G10R10U
1, // A1B5G5R5U
1, // R8U
1, // R8UI
1, // RGBA16F
1, // RGBA16U
1, // RGBA16UI
1, // R11FG11FB10F
1, // RGBA32UI
4, // DXT1
4, // DXT23
4, // DXT45
4, // DXN1
4, // DXN2UNORM
4, // DXN2SNORM
4, // BC7U
4, // BC6H_UF16
4, // BC6H_SF16
4, // ASTC_2D_4X4
1, // G8R8U
1, // G8R8S
1, // BGRA8
1, // RGBA32F
1, // RG32F
1, // R32F
1, // R16F
1, // R16U
1, // R16S
1, // R16UI
1, // R16I
1, // RG16
1, // RG16F
1, // RG16UI
1, // RG16I
1, // RG16S
1, // RGB32F
1, // RGBA8_SRGB
1, // RG8U
1, // RG8S
1, // RG32UI
1, // R32UI
8, // ASTC_2D_8X8
5, // ASTC_2D_8X5
4, // ASTC_2D_5X4
1, // BGRA8_SRGB
4, // DXT1_SRGB
4, // DXT23_SRGB
4, // DXT45_SRGB
4, // BC7U_SRGB
4, // ASTC_2D_4X4_SRGB
8, // ASTC_2D_8X8_SRGB
5, // ASTC_2D_8X5_SRGB
4, // ASTC_2D_5X4_SRGB
1, // Z32F
1, // Z16
1, // Z24S8
1, // S8Z24
1, // Z32FS8
}};
ASSERT(static_cast<std::size_t>(format) < block_height_table.size());
return block_height_table[static_cast<std::size_t>(format)];
}
static constexpr u32 GetFormatBpp(PixelFormat format) {
if (format == PixelFormat::Invalid)
return 0;
constexpr std::array<u32, MaxPixelFormat> bpp_table = {{
32, // ABGR8U
32, // ABGR8S
32, // ABGR8UI
16, // B5G6R5U
32, // A2B10G10R10U
16, // A1B5G5R5U
8, // R8U
8, // R8UI
64, // RGBA16F
64, // RGBA16U
64, // RGBA16UI
32, // R11FG11FB10F
128, // RGBA32UI
64, // DXT1
128, // DXT23
128, // DXT45
64, // DXN1
128, // DXN2UNORM
128, // DXN2SNORM
128, // BC7U
128, // BC6H_UF16
128, // BC6H_SF16
32, // ASTC_2D_4X4
16, // G8R8U
16, // G8R8S
32, // BGRA8
128, // RGBA32F
64, // RG32F
32, // R32F
16, // R16F
16, // R16U
16, // R16S
16, // R16UI
16, // R16I
32, // RG16
32, // RG16F
32, // RG16UI
32, // RG16I
32, // RG16S
96, // RGB32F
32, // RGBA8_SRGB
16, // RG8U
16, // RG8S
64, // RG32UI
32, // R32UI
16, // ASTC_2D_8X8
16, // ASTC_2D_8X5
32, // ASTC_2D_5X4
32, // BGRA8_SRGB
64, // DXT1_SRGB
128, // DXT23_SRGB
128, // DXT45_SRGB
128, // BC7U
32, // ASTC_2D_4X4_SRGB
16, // ASTC_2D_8X8_SRGB
16, // ASTC_2D_8X5_SRGB
32, // ASTC_2D_5X4_SRGB
32, // Z32F
16, // Z16
32, // Z24S8
32, // S8Z24
64, // Z32FS8
}};
ASSERT(static_cast<std::size_t>(format) < bpp_table.size());
return bpp_table[static_cast<std::size_t>(format)];
}
/// Returns the sizer in bytes of the specified pixel format
static constexpr u32 GetBytesPerPixel(PixelFormat pixel_format) {
if (pixel_format == PixelFormat::Invalid) {
return 0;
}
return GetFormatBpp(pixel_format) / CHAR_BIT;
}
SurfaceTarget SurfaceTargetFromTextureType(Tegra::Texture::TextureType texture_type);
bool SurfaceTargetIsLayered(SurfaceTarget target);
PixelFormat PixelFormatFromDepthFormat(Tegra::DepthFormat format);
PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format);
PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format,
Tegra::Texture::ComponentType component_type,
bool is_srgb);
ComponentType ComponentTypeFromTexture(Tegra::Texture::ComponentType type);
ComponentType ComponentTypeFromRenderTarget(Tegra::RenderTargetFormat format);
PixelFormat PixelFormatFromGPUPixelFormat(Tegra::FramebufferConfig::PixelFormat format);
ComponentType ComponentTypeFromDepthFormat(Tegra::DepthFormat format);
SurfaceType GetFormatType(PixelFormat pixel_format);
bool IsPixelFormatASTC(PixelFormat format);
std::pair<u32, u32> GetASTCBlockSize(PixelFormat format);
/// Returns true if the specified PixelFormat is a BCn format, e.g. DXT or DXN
bool IsFormatBCn(PixelFormat format);
} // namespace VideoCore::Surface

View file

@ -102,16 +102,27 @@ void TelemetryJson::Complete() {
impl->SerializeSection(Telemetry::FieldType::App, "App"); impl->SerializeSection(Telemetry::FieldType::App, "App");
impl->SerializeSection(Telemetry::FieldType::Session, "Session"); impl->SerializeSection(Telemetry::FieldType::Session, "Session");
impl->SerializeSection(Telemetry::FieldType::Performance, "Performance"); impl->SerializeSection(Telemetry::FieldType::Performance, "Performance");
impl->SerializeSection(Telemetry::FieldType::UserFeedback, "UserFeedback");
impl->SerializeSection(Telemetry::FieldType::UserConfig, "UserConfig"); impl->SerializeSection(Telemetry::FieldType::UserConfig, "UserConfig");
impl->SerializeSection(Telemetry::FieldType::UserSystem, "UserSystem"); impl->SerializeSection(Telemetry::FieldType::UserSystem, "UserSystem");
auto content = impl->TopSection().dump(); auto content = impl->TopSection().dump();
// Send the telemetry async but don't handle the errors since they were written to the log // Send the telemetry async but don't handle the errors since they were written to the log
Common::DetachedTasks::AddTask( Common::DetachedTasks::AddTask([host{impl->host}, content]() {
[host{impl->host}, username{impl->username}, token{impl->token}, content]() { Client{host, "", ""}.PostJson("/telemetry", content, true);
Client{host, username, token}.PostJson("/telemetry", content, true); });
}); }
bool TelemetryJson::SubmitTestcase() {
impl->SerializeSection(Telemetry::FieldType::App, "App");
impl->SerializeSection(Telemetry::FieldType::Session, "Session");
impl->SerializeSection(Telemetry::FieldType::UserFeedback, "UserFeedback");
impl->SerializeSection(Telemetry::FieldType::UserSystem, "UserSystem");
auto content = impl->TopSection().dump();
Client client(impl->host, impl->username, impl->token);
auto value = client.PostJson("/gamedb/testcase", content, false);
return value.result_code == Common::WebResult::Code::Success;
} }
} // namespace WebService } // namespace WebService

View file

@ -35,6 +35,7 @@ public:
void Visit(const Telemetry::Field<std::chrono::microseconds>& field) override; void Visit(const Telemetry::Field<std::chrono::microseconds>& field) override;
void Complete() override; void Complete() override;
bool SubmitTestcase() override;
private: private:
struct Impl; struct Impl;

View file

@ -56,6 +56,8 @@ add_executable(yuzu
main.h main.h
ui_settings.cpp ui_settings.cpp
ui_settings.h ui_settings.h
util/limitable_input_dialog.cpp
util/limitable_input_dialog.h
util/spinbox.cpp util/spinbox.cpp
util/spinbox.h util/spinbox.h
util/util.cpp util/util.cpp

View file

@ -5,6 +5,7 @@
#include <QButtonGroup> #include <QButtonGroup>
#include <QMessageBox> #include <QMessageBox>
#include <QPushButton> #include <QPushButton>
#include <QtConcurrent/qtconcurrentrun.h>
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/telemetry.h" #include "common/telemetry.h"
#include "core/core.h" #include "core/core.h"
@ -23,6 +24,8 @@ CompatDB::CompatDB(QWidget* parent)
connect(ui->radioButton_IntroMenu, &QRadioButton::clicked, this, &CompatDB::EnableNext); connect(ui->radioButton_IntroMenu, &QRadioButton::clicked, this, &CompatDB::EnableNext);
connect(ui->radioButton_WontBoot, &QRadioButton::clicked, this, &CompatDB::EnableNext); connect(ui->radioButton_WontBoot, &QRadioButton::clicked, this, &CompatDB::EnableNext);
connect(button(NextButton), &QPushButton::clicked, this, &CompatDB::Submit); connect(button(NextButton), &QPushButton::clicked, this, &CompatDB::Submit);
connect(&testcase_watcher, &QFutureWatcher<bool>::finished, this,
&CompatDB::OnTestcaseSubmitted);
} }
CompatDB::~CompatDB() = default; CompatDB::~CompatDB() = default;
@ -48,18 +51,38 @@ void CompatDB::Submit() {
} }
break; break;
case CompatDBPage::Final: case CompatDBPage::Final:
back();
LOG_DEBUG(Frontend, "Compatibility Rating: {}", compatibility->checkedId()); LOG_DEBUG(Frontend, "Compatibility Rating: {}", compatibility->checkedId());
Core::Telemetry().AddField(Telemetry::FieldType::UserFeedback, "Compatibility", Core::Telemetry().AddField(Telemetry::FieldType::UserFeedback, "Compatibility",
compatibility->checkedId()); compatibility->checkedId());
// older versions of QT don't support the "NoCancelButtonOnLastPage" option, this is a
// workaround button(NextButton)->setEnabled(false);
button(NextButton)->setText(tr("Submitting"));
button(QWizard::CancelButton)->setVisible(false); button(QWizard::CancelButton)->setVisible(false);
testcase_watcher.setFuture(QtConcurrent::run(
[this]() { return Core::System::GetInstance().TelemetrySession().SubmitTestcase(); }));
break; break;
default: default:
LOG_ERROR(Frontend, "Unexpected page: {}", currentId()); LOG_ERROR(Frontend, "Unexpected page: {}", currentId());
} }
} }
void CompatDB::OnTestcaseSubmitted() {
if (!testcase_watcher.result()) {
QMessageBox::critical(this, tr("Communication error"),
tr("An error occured while sending the Testcase"));
button(NextButton)->setEnabled(true);
button(NextButton)->setText(tr("Next"));
button(QWizard::CancelButton)->setVisible(true);
} else {
next();
// older versions of QT don't support the "NoCancelButtonOnLastPage" option, this is a
// workaround
button(QWizard::CancelButton)->setVisible(false);
}
}
void CompatDB::EnableNext() { void CompatDB::EnableNext() {
button(NextButton)->setEnabled(true); button(NextButton)->setEnabled(true);
} }

View file

@ -5,6 +5,7 @@
#pragma once #pragma once
#include <memory> #include <memory>
#include <QFutureWatcher>
#include <QWizard> #include <QWizard>
namespace Ui { namespace Ui {
@ -19,8 +20,11 @@ public:
~CompatDB(); ~CompatDB();
private: private:
QFutureWatcher<bool> testcase_watcher;
std::unique_ptr<Ui::CompatDB> ui; std::unique_ptr<Ui::CompatDB> ui;
void Submit(); void Submit();
void OnTestcaseSubmitted();
void EnableNext(); void EnableNext();
}; };

View file

@ -6,20 +6,20 @@
#include <QFileDialog> #include <QFileDialog>
#include <QGraphicsItem> #include <QGraphicsItem>
#include <QGraphicsScene> #include <QGraphicsScene>
#include <QInputDialog> #include <QHeaderView>
#include <QMessageBox> #include <QMessageBox>
#include <QStandardItemModel> #include <QStandardItemModel>
#include <QTreeView> #include <QTreeView>
#include <QVBoxLayout> #include <QVBoxLayout>
#include "common/common_paths.h" #include "common/assert.h"
#include "common/logging/backend.h" #include "common/file_util.h"
#include "common/string_util.h" #include "common/string_util.h"
#include "core/core.h" #include "core/core.h"
#include "core/hle/service/acc/profile_manager.h" #include "core/hle/service/acc/profile_manager.h"
#include "core/settings.h" #include "core/settings.h"
#include "ui_configure_system.h" #include "ui_configure_system.h"
#include "yuzu/configuration/configure_system.h" #include "yuzu/configuration/configure_system.h"
#include "yuzu/main.h" #include "yuzu/util/limitable_input_dialog.h"
namespace { namespace {
constexpr std::array<int, 12> days_in_month = {{ constexpr std::array<int, 12> days_in_month = {{
@ -83,6 +83,12 @@ QPixmap GetIcon(Service::Account::UUID uuid) {
return icon.scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); return icon.scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
} }
QString GetProfileUsernameFromUser(QWidget* parent, const QString& description_text) {
return LimitableInputDialog::GetText(parent, ConfigureSystem::tr("Enter Username"),
description_text, 1,
static_cast<int>(Service::Account::profile_username_size));
}
} // Anonymous namespace } // Anonymous namespace
ConfigureSystem::ConfigureSystem(QWidget* parent) ConfigureSystem::ConfigureSystem(QWidget* parent)
@ -244,15 +250,13 @@ void ConfigureSystem::SelectUser(const QModelIndex& index) {
} }
void ConfigureSystem::AddUser() { void ConfigureSystem::AddUser() {
const auto uuid = Service::Account::UUID::Generate();
bool ok = false;
const auto username = const auto username =
QInputDialog::getText(this, tr("Enter Username"), tr("Enter a username for the new user:"), GetProfileUsernameFromUser(this, tr("Enter a username for the new user:"));
QLineEdit::Normal, QString(), &ok); if (username.isEmpty()) {
if (!ok)
return; return;
}
const auto uuid = Service::Account::UUID::Generate();
profile_manager->CreateNewUser(uuid, username.toStdString()); profile_manager->CreateNewUser(uuid, username.toStdString());
item_model->appendRow(new QStandardItem{GetIcon(uuid), FormatUserEntryText(username, uuid)}); item_model->appendRow(new QStandardItem{GetIcon(uuid), FormatUserEntryText(username, uuid)});
@ -267,24 +271,15 @@ void ConfigureSystem::RenameUser() {
if (!profile_manager->GetProfileBase(*uuid, profile)) if (!profile_manager->GetProfileBase(*uuid, profile))
return; return;
bool ok = false; const auto new_username = GetProfileUsernameFromUser(this, tr("Enter a new username:"));
const auto old_username = GetAccountUsername(*profile_manager, *uuid); if (new_username.isEmpty()) {
const auto new_username =
QInputDialog::getText(this, tr("Enter Username"), tr("Enter a new username:"),
QLineEdit::Normal, old_username, &ok);
if (!ok)
return; return;
std::fill(profile.username.begin(), profile.username.end(), '\0');
const auto username_std = new_username.toStdString();
if (username_std.size() > profile.username.size()) {
std::copy_n(username_std.begin(), std::min(profile.username.size(), username_std.size()),
profile.username.begin());
} else {
std::copy(username_std.begin(), username_std.end(), profile.username.begin());
} }
const auto username_std = new_username.toStdString();
std::fill(profile.username.begin(), profile.username.end(), '\0');
std::copy(username_std.begin(), username_std.end(), profile.username.begin());
profile_manager->SetProfileBase(*uuid, profile); profile_manager->SetProfileBase(*uuid, profile);
item_model->setItem( item_model->setItem(

View file

@ -0,0 +1,59 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <QDialogButtonBox>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QVBoxLayout>
#include "yuzu/util/limitable_input_dialog.h"
LimitableInputDialog::LimitableInputDialog(QWidget* parent) : QDialog{parent} {
CreateUI();
ConnectEvents();
}
LimitableInputDialog::~LimitableInputDialog() = default;
void LimitableInputDialog::CreateUI() {
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
text_label = new QLabel(this);
text_entry = new QLineEdit(this);
buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
auto* const layout = new QVBoxLayout;
layout->addWidget(text_label);
layout->addWidget(text_entry);
layout->addWidget(buttons);
setLayout(layout);
}
void LimitableInputDialog::ConnectEvents() {
connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
}
QString LimitableInputDialog::GetText(QWidget* parent, const QString& title, const QString& text,
int min_character_limit, int max_character_limit) {
Q_ASSERT(min_character_limit <= max_character_limit);
LimitableInputDialog dialog{parent};
dialog.setWindowTitle(title);
dialog.text_label->setText(text);
dialog.text_entry->setMaxLength(max_character_limit);
auto* const ok_button = dialog.buttons->button(QDialogButtonBox::Ok);
ok_button->setEnabled(false);
connect(dialog.text_entry, &QLineEdit::textEdited, [&](const QString& new_text) {
ok_button->setEnabled(new_text.length() >= min_character_limit);
});
if (dialog.exec() != QDialog::Accepted) {
return {};
}
return dialog.text_entry->text();
}

View file

@ -0,0 +1,31 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <QDialog>
class QDialogButtonBox;
class QLabel;
class QLineEdit;
/// A QDialog that functions similarly to QInputDialog, however, it allows
/// restricting the minimum and total number of characters that can be entered.
class LimitableInputDialog final : public QDialog {
Q_OBJECT
public:
explicit LimitableInputDialog(QWidget* parent = nullptr);
~LimitableInputDialog() override;
static QString GetText(QWidget* parent, const QString& title, const QString& text,
int min_character_limit, int max_character_limit);
private:
void CreateUI();
void ConnectEvents();
QLabel* text_label;
QLineEdit* text_entry;
QDialogButtonBox* buttons;
};