diff options
-rw-r--r-- | src/core/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/core/hle/applets/applet.cpp | 5 | ||||
-rw-r--r-- | src/core/hle/applets/erreula.cpp | 72 | ||||
-rw-r--r-- | src/core/hle/applets/erreula.h | 31 | ||||
-rw-r--r-- | src/core/hle/result.h | 26 | ||||
-rw-r--r-- | src/core/hle/service/apt/apt.h | 8 | ||||
-rw-r--r-- | src/core/hle/service/fs/archive.cpp | 24 | ||||
-rw-r--r-- | src/core/hle/service/fs/fs_user.cpp | 53 | ||||
-rw-r--r-- | src/video_core/pica.h | 24 | ||||
-rw-r--r-- | src/video_core/rasterizer.cpp | 23 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/gl_rasterizer.cpp | 34 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/gl_rasterizer.h | 13 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/gl_shader_gen.cpp | 20 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/pica_to_gl.h | 1 |
14 files changed, 306 insertions, 30 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 02d902bb5..0773339a9 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -27,6 +27,7 @@ set(SRCS hle/config_mem.cpp hle/hle.cpp hle/applets/applet.cpp + hle/applets/erreula.cpp hle/applets/mii_selector.cpp hle/applets/swkbd.cpp hle/kernel/address_arbiter.cpp @@ -168,6 +169,7 @@ set(HEADERS hle/function_wrappers.h hle/hle.h hle/applets/applet.h + hle/applets/erreula.h hle/applets/mii_selector.h hle/applets/swkbd.h hle/kernel/address_arbiter.h diff --git a/src/core/hle/applets/applet.cpp b/src/core/hle/applets/applet.cpp index 90e134437..ccf35fa07 100644 --- a/src/core/hle/applets/applet.cpp +++ b/src/core/hle/applets/applet.cpp @@ -12,6 +12,7 @@ #include "core/core_timing.h" #include "core/hle/applets/applet.h" +#include "core/hle/applets/erreula.h" #include "core/hle/applets/mii_selector.h" #include "core/hle/applets/swkbd.h" #include "core/hle/result.h" @@ -52,6 +53,10 @@ ResultCode Applet::Create(Service::APT::AppletId id) { case Service::APT::AppletId::Ed2: applets[id] = std::make_shared<MiiSelector>(id); break; + case Service::APT::AppletId::Error: + case Service::APT::AppletId::Error2: + applets[id] = std::make_shared<ErrEula>(id); + break; default: LOG_ERROR(Service_APT, "Could not create applet %u", id); // TODO(Subv): Find the right error code diff --git a/src/core/hle/applets/erreula.cpp b/src/core/hle/applets/erreula.cpp new file mode 100644 index 000000000..92a4b2323 --- /dev/null +++ b/src/core/hle/applets/erreula.cpp @@ -0,0 +1,72 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/string_util.h" + +#include "core/hle/applets/erreula.h" +#include "core/hle/service/apt/apt.h" + +namespace HLE { +namespace Applets { + +ResultCode ErrEula::ReceiveParameter(const Service::APT::MessageParameter& parameter) { + if (parameter.signal != static_cast<u32>(Service::APT::SignalType::LibAppJustStarted)) { + LOG_ERROR(Service_APT, "unsupported signal %u", parameter.signal); + UNIMPLEMENTED(); + // TODO(Subv): Find the right error code + return ResultCode(-1); + } + + // The LibAppJustStarted message contains a buffer with the size of the framebuffer shared memory. + // Create the SharedMemory that will hold the framebuffer data + Service::APT::CaptureBufferInfo capture_info; + ASSERT(sizeof(capture_info) == parameter.buffer.size()); + + memcpy(&capture_info, parameter.buffer.data(), sizeof(capture_info)); + + // TODO: allocated memory never released + using Kernel::MemoryPermission; + // Allocate a heap block of the required size for this applet. + heap_memory = std::make_shared<std::vector<u8>>(capture_info.size); + // Create a SharedMemory that directly points to this heap block. + framebuffer_memory = Kernel::SharedMemory::CreateForApplet(heap_memory, 0, heap_memory->size(), + MemoryPermission::ReadWrite, MemoryPermission::ReadWrite, + "ErrEula Memory"); + + // Send the response message with the newly created SharedMemory + Service::APT::MessageParameter result; + result.signal = static_cast<u32>(Service::APT::SignalType::LibAppFinished); + result.buffer.clear(); + result.destination_id = static_cast<u32>(Service::APT::AppletId::Application); + result.sender_id = static_cast<u32>(id); + result.object = framebuffer_memory; + + Service::APT::SendParameter(result); + return RESULT_SUCCESS; +} + +ResultCode ErrEula::StartImpl(const Service::APT::AppletStartupParameter& parameter) { + started = true; + + // TODO(Subv): Set the expected fields in the response buffer before resending it to the application. + // TODO(Subv): Reverse the parameter format for the ErrEula applet + + // Let the application know that we're closing + Service::APT::MessageParameter message; + message.buffer.resize(parameter.buffer.size()); + std::fill(message.buffer.begin(), message.buffer.end(), 0); + message.signal = static_cast<u32>(Service::APT::SignalType::LibAppClosed); + message.destination_id = static_cast<u32>(Service::APT::AppletId::Application); + message.sender_id = static_cast<u32>(id); + Service::APT::SendParameter(message); + + started = false; + return RESULT_SUCCESS; +} + +void ErrEula::Update() { +} + +} // namespace Applets +} // namespace HLE diff --git a/src/core/hle/applets/erreula.h b/src/core/hle/applets/erreula.h new file mode 100644 index 000000000..9fe72ae07 --- /dev/null +++ b/src/core/hle/applets/erreula.h @@ -0,0 +1,31 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/applets/applet.h" +#include "core/hle/kernel/shared_memory.h" + +namespace HLE { +namespace Applets { + +class ErrEula final : public Applet { +public: + explicit ErrEula(Service::APT::AppletId id): Applet(id) { } + + ResultCode ReceiveParameter(const Service::APT::MessageParameter& parameter) override; + ResultCode StartImpl(const Service::APT::AppletStartupParameter& parameter) override; + void Update() override; + bool IsRunning() const override { return started; } + + /// This SharedMemory will be created when we receive the LibAppJustStarted message. + /// It holds the framebuffer info retrieved by the application with GSPGPU::ImportDisplayCaptureInfo + Kernel::SharedPtr<Kernel::SharedMemory> framebuffer_memory; +private: + /// Whether this applet is currently running instead of the host application or not. + bool started = false; +}; + +} // namespace Applets +} // namespace HLE diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 57dedcb22..268a8dad2 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h @@ -20,6 +20,7 @@ enum class ErrorDescription : u32 { WrongPermission = 46, OS_InvalidBufferDescriptor = 48, WrongAddress = 53, + FS_ArchiveNotMounted = 101, FS_NotFound = 120, FS_AlreadyExists = 190, FS_InvalidOpenFlags = 230, @@ -135,15 +136,28 @@ enum class ErrorModule : u32 { MCU = 72, NS = 73, News = 74, - RO_1 = 75, + RO = 75, GD = 76, CardSPI = 77, EC = 78, - RO_2 = 79, - WebBrowser = 80, - Test = 81, - ENC = 82, - PIA = 83, + WebBrowser = 79, + Test = 80, + ENC = 81, + PIA = 82, + ACT = 83, + VCTL = 84, + OLV = 85, + NEIA = 86, + NPNS = 87, + + AVD = 90, + L2B = 91, + MVD = 92, + NFC = 93, + UART = 94, + SPM = 95, + QTM = 96, + NFP = 97, Application = 254, InvalidResult = 255 diff --git a/src/core/hle/service/apt/apt.h b/src/core/hle/service/apt/apt.h index 76b3a3807..53cee4867 100644 --- a/src/core/hle/service/apt/apt.h +++ b/src/core/hle/service/apt/apt.h @@ -66,6 +66,8 @@ enum class AppletId : u32 { InstructionManual = 0x115, Notifications = 0x116, Miiverse = 0x117, + MiiversePost = 0x118, + AmiiboSettings = 0x119, SoftwareKeyboard1 = 0x201, Ed1 = 0x202, PnoteApp = 0x204, @@ -78,6 +80,12 @@ enum class AppletId : u32 { AnyLibraryApplet = 0x400, SoftwareKeyboard2 = 0x401, Ed2 = 0x402, + PnoteApp2 = 0x404, + SnoteApp2 = 0x405, + Error2 = 0x406, + Mint2 = 0x407, + Extrapad2 = 0x408, + Memolib2 = 0x409, }; enum class StartupArgumentType : u32 { diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp index cc7af7218..4c7aaa7f2 100644 --- a/src/core/hle/service/fs/archive.cpp +++ b/src/core/hle/service/fs/archive.cpp @@ -58,6 +58,10 @@ namespace FS { const ResultCode ERR_INVALID_HANDLE(ErrorDescription::InvalidHandle, ErrorModule::FS, ErrorSummary::InvalidArgument, ErrorLevel::Permanent); +/// Returned when a function is passed an invalid archive handle. +const ResultCode ERR_INVALID_ARCHIVE_HANDLE(ErrorDescription::FS_ArchiveNotMounted, ErrorModule::FS, + ErrorSummary::NotFound, ErrorLevel::Status); // 0xC8804465 + // Command to access archive file enum class FileCommand : u32 { Dummy1 = 0x000100C6, @@ -292,7 +296,7 @@ ResultVal<ArchiveHandle> OpenArchive(ArchiveIdCode id_code, FileSys::Path& archi ResultCode CloseArchive(ArchiveHandle handle) { if (handle_map.erase(handle) == 0) - return ERR_INVALID_HANDLE; + return ERR_INVALID_ARCHIVE_HANDLE; else return RESULT_SUCCESS; } @@ -314,7 +318,7 @@ ResultVal<Kernel::SharedPtr<File>> OpenFileFromArchive(ArchiveHandle archive_han const FileSys::Path& path, const FileSys::Mode mode) { ArchiveBackend* archive = GetArchive(archive_handle); if (archive == nullptr) - return ERR_INVALID_HANDLE; + return ERR_INVALID_ARCHIVE_HANDLE; auto backend = archive->OpenFile(path, mode); if (backend.Failed()) @@ -327,7 +331,7 @@ ResultVal<Kernel::SharedPtr<File>> OpenFileFromArchive(ArchiveHandle archive_han ResultCode DeleteFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) { ArchiveBackend* archive = GetArchive(archive_handle); if (archive == nullptr) - return ERR_INVALID_HANDLE; + return ERR_INVALID_ARCHIVE_HANDLE; return archive->DeleteFile(path); } @@ -337,7 +341,7 @@ ResultCode RenameFileBetweenArchives(ArchiveHandle src_archive_handle, const Fil ArchiveBackend* src_archive = GetArchive(src_archive_handle); ArchiveBackend* dest_archive = GetArchive(dest_archive_handle); if (src_archive == nullptr || dest_archive == nullptr) - return ERR_INVALID_HANDLE; + return ERR_INVALID_ARCHIVE_HANDLE; if (src_archive == dest_archive) { if (src_archive->RenameFile(src_path, dest_path)) @@ -356,7 +360,7 @@ ResultCode RenameFileBetweenArchives(ArchiveHandle src_archive_handle, const Fil ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) { ArchiveBackend* archive = GetArchive(archive_handle); if (archive == nullptr) - return ERR_INVALID_HANDLE; + return ERR_INVALID_ARCHIVE_HANDLE; if (archive->DeleteDirectory(path)) return RESULT_SUCCESS; @@ -367,7 +371,7 @@ ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSy ResultCode CreateFileInArchive(ArchiveHandle archive_handle, const FileSys::Path& path, u64 file_size) { ArchiveBackend* archive = GetArchive(archive_handle); if (archive == nullptr) - return ERR_INVALID_HANDLE; + return ERR_INVALID_ARCHIVE_HANDLE; return archive->CreateFile(path, file_size); } @@ -375,7 +379,7 @@ ResultCode CreateFileInArchive(ArchiveHandle archive_handle, const FileSys::Path ResultCode CreateDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) { ArchiveBackend* archive = GetArchive(archive_handle); if (archive == nullptr) - return ERR_INVALID_HANDLE; + return ERR_INVALID_ARCHIVE_HANDLE; if (archive->CreateDirectory(path)) return RESULT_SUCCESS; @@ -388,7 +392,7 @@ ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle, cons ArchiveBackend* src_archive = GetArchive(src_archive_handle); ArchiveBackend* dest_archive = GetArchive(dest_archive_handle); if (src_archive == nullptr || dest_archive == nullptr) - return ERR_INVALID_HANDLE; + return ERR_INVALID_ARCHIVE_HANDLE; if (src_archive == dest_archive) { if (src_archive->RenameDirectory(src_path, dest_path)) @@ -408,7 +412,7 @@ ResultVal<Kernel::SharedPtr<Directory>> OpenDirectoryFromArchive(ArchiveHandle a const FileSys::Path& path) { ArchiveBackend* archive = GetArchive(archive_handle); if (archive == nullptr) - return ERR_INVALID_HANDLE; + return ERR_INVALID_ARCHIVE_HANDLE; std::unique_ptr<FileSys::DirectoryBackend> backend = archive->OpenDirectory(path); if (backend == nullptr) { @@ -423,7 +427,7 @@ ResultVal<Kernel::SharedPtr<Directory>> OpenDirectoryFromArchive(ArchiveHandle a ResultVal<u64> GetFreeBytesInArchive(ArchiveHandle archive_handle) { ArchiveBackend* archive = GetArchive(archive_handle); if (archive == nullptr) - return ERR_INVALID_HANDLE; + return ERR_INVALID_ARCHIVE_HANDLE; return MakeResult<u64>(archive->GetFreeBytes()); } diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp index 7df7da5a4..937868747 100644 --- a/src/core/hle/service/fs/fs_user.cpp +++ b/src/core/hle/service/fs/fs_user.cpp @@ -645,20 +645,19 @@ static void DeleteSystemSaveData(Service::Interface* self) { * FS_User::CreateSystemSaveData service function. * Inputs: * 0 : 0x08560240 - * 1 : High word of the SystemSaveData id to create - * 2 : Low word of the SystemSaveData id to create - * 3 : Unknown - * 4 : Unknown - * 5 : Unknown - * 6 : Unknown - * 7 : Unknown - * 8 : Unknown - * 9 : Unknown (Memory address) + * 1 : u8 MediaType of the system save data + * 2 : SystemSaveData id to create + * 3 : Total size + * 4 : Block size + * 5 : Number of directories + * 6 : Number of files + * 7 : Directory bucket count + * 8 : File bucket count + * 9 : u8 Whether to duplicate data or not * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ static void CreateSystemSaveData(Service::Interface* self) { - // TODO(Subv): Figure out the other parameters. u32* cmd_buff = Kernel::GetCommandBuffer(); u32 savedata_high = cmd_buff[1]; u32 savedata_low = cmd_buff[2]; @@ -672,6 +671,38 @@ static void CreateSystemSaveData(Service::Interface* self) { } /** + * FS_User::CreateLegacySystemSaveData service function. + * This function appears to be obsolete and seems to have been replaced by + * command 0x08560240 (CreateSystemSaveData). + * + * Inputs: + * 0 : 0x08100200 + * 1 : SystemSaveData id to create + * 2 : Total size + * 3 : Block size + * 4 : Number of directories + * 5 : Number of files + * 6 : Directory bucket count + * 7 : File bucket count + * 8 : u8 Duplicate data + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void CreateLegacySystemSaveData(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + u32 savedata_id = cmd_buff[1]; + + LOG_WARNING(Service_FS, "(STUBBED) savedata_id=%08X cmd_buff[3]=%08X " + "cmd_buff[4]=%08X cmd_buff[5]=%08X cmd_buff[6]=%08X cmd_buff[7]=%08X cmd_buff[8]=%08X " + "cmd_buff[9]=%08X", savedata_id, cmd_buff[3], cmd_buff[4], cmd_buff[5], + cmd_buff[6], cmd_buff[7], cmd_buff[8], cmd_buff[9]); + + cmd_buff[0] = IPC::MakeHeader(0x810, 0x1, 0); + // With this command, the SystemSaveData always has save_high = 0 (Always created in the NAND) + cmd_buff[1] = CreateSystemSaveData(0, savedata_id).raw; +} + +/** * FS_User::InitializeWithSdkVersion service function. * Inputs: * 0 : 0x08610042 @@ -820,7 +851,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x080D0144, nullptr, "ControlArchive"}, {0x080E0080, CloseArchive, "CloseArchive"}, {0x080F0180, FormatThisUserSaveData, "FormatThisUserSaveData"}, - {0x08100200, nullptr, "CreateSystemSaveData"}, + {0x08100200, CreateLegacySystemSaveData, "CreateLegacySystemSaveData"}, {0x08110040, nullptr, "DeleteSystemSaveData"}, {0x08120080, GetFreeBytes, "GetFreeBytes"}, {0x08130000, nullptr, "GetCardType"}, diff --git a/src/video_core/pica.h b/src/video_core/pica.h index 09702d46a..7099c31a0 100644 --- a/src/video_core/pica.h +++ b/src/video_core/pica.h @@ -115,7 +115,28 @@ struct Regs { BitField<24, 5, Semantic> map_w; } vs_output_attributes[7]; - INSERT_PADDING_WORDS(0x11); + INSERT_PADDING_WORDS(0xe); + + enum class ScissorMode : u32 { + Disabled = 0, + Exclude = 1, // Exclude pixels inside the scissor box + + Include = 3 // Exclude pixels outside the scissor box + }; + + struct { + BitField<0, 2, ScissorMode> mode; + + union { + BitField< 0, 16, u32> x1; + BitField<16, 16, u32> y1; + }; + + union { + BitField< 0, 16, u32> x2; + BitField<16, 16, u32> y2; + }; + } scissor_test; union { BitField< 0, 10, s32> x; @@ -1328,6 +1349,7 @@ ASSERT_REG_POSITION(viewport_depth_range, 0x4d); ASSERT_REG_POSITION(viewport_depth_near_plane, 0x4e); ASSERT_REG_POSITION(vs_output_attributes[0], 0x50); ASSERT_REG_POSITION(vs_output_attributes[1], 0x51); +ASSERT_REG_POSITION(scissor_test, 0x65); ASSERT_REG_POSITION(viewport_corner, 0x68); ASSERT_REG_POSITION(depthmap_enable, 0x6D); ASSERT_REG_POSITION(texture0_enable, 0x80); diff --git a/src/video_core/rasterizer.cpp b/src/video_core/rasterizer.cpp index a84170094..6f369a00e 100644 --- a/src/video_core/rasterizer.cpp +++ b/src/video_core/rasterizer.cpp @@ -338,12 +338,26 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, return; } - // TODO: Proper scissor rect test! u16 min_x = std::min({vtxpos[0].x, vtxpos[1].x, vtxpos[2].x}); u16 min_y = std::min({vtxpos[0].y, vtxpos[1].y, vtxpos[2].y}); u16 max_x = std::max({vtxpos[0].x, vtxpos[1].x, vtxpos[2].x}); u16 max_y = std::max({vtxpos[0].y, vtxpos[1].y, vtxpos[2].y}); + // Convert the scissor box coordinates to 12.4 fixed point + u16 scissor_x1 = (u16)( regs.scissor_test.x1 << 4); + u16 scissor_y1 = (u16)( regs.scissor_test.y1 << 4); + // x2,y2 have +1 added to cover the entire sub-pixel area + u16 scissor_x2 = (u16)((regs.scissor_test.x2 + 1) << 4); + u16 scissor_y2 = (u16)((regs.scissor_test.y2 + 1) << 4); + + if (regs.scissor_test.mode == Regs::ScissorMode::Include) { + // Calculate the new bounds + min_x = std::max(min_x, scissor_x1); + min_y = std::max(min_y, scissor_y1); + max_x = std::min(max_x, scissor_x2); + max_y = std::min(max_y, scissor_y2); + } + min_x &= Fix12P4::IntMask(); min_y &= Fix12P4::IntMask(); max_x = ((max_x + Fix12P4::FracMask()) & Fix12P4::IntMask()); @@ -383,6 +397,13 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, for (u16 y = min_y + 8; y < max_y; y += 0x10) { for (u16 x = min_x + 8; x < max_x; x += 0x10) { + // Do not process the pixel if it's inside the scissor box and the scissor mode is set to Exclude + if (regs.scissor_test.mode == Regs::ScissorMode::Exclude) { + if (x >= scissor_x1 && x < scissor_x2 && + y >= scissor_y1 && y < scissor_y2) + continue; + } + // Calculate the barycentric coordinates w0, w1 and w2 int w0 = bias0 + SignedArea(vtxpos[1].xy(), vtxpos[2].xy(), {x, y}); int w1 = bias1 + SignedArea(vtxpos[2].xy(), vtxpos[0].xy(), {x, y}); diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 328a4f66b..f8393c618 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -196,6 +196,14 @@ void RasterizerOpenGL::DrawTriangles() { (GLint)(rect.bottom + regs.viewport_corner.y * color_surface->res_scale_height), (GLsizei)(viewport_width * color_surface->res_scale_width), (GLsizei)(viewport_height * color_surface->res_scale_height)); + if (uniform_block_data.data.framebuffer_scale[0] != color_surface->res_scale_width || + uniform_block_data.data.framebuffer_scale[1] != color_surface->res_scale_height) { + + uniform_block_data.data.framebuffer_scale[0] = color_surface->res_scale_width; + uniform_block_data.data.framebuffer_scale[1] = color_surface->res_scale_height; + uniform_block_data.dirty = true; + } + // Sync and bind the texture surfaces const auto pica_textures = regs.GetTextures(); for (unsigned texture_index = 0; texture_index < pica_textures.size(); ++texture_index) { @@ -353,6 +361,15 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) { SyncColorWriteMask(); break; + // Scissor test + case PICA_REG_INDEX(scissor_test.mode): + shader_dirty = true; + break; + case PICA_REG_INDEX(scissor_test.x1): // and y1 + case PICA_REG_INDEX(scissor_test.x2): // and y2 + SyncScissorTest(); + break; + // Logic op case PICA_REG_INDEX(output_merger.logic_op): SyncLogicOp(); @@ -1002,6 +1019,7 @@ void RasterizerOpenGL::SetShader() { SyncDepthOffset(); SyncAlphaTest(); SyncCombinerColor(); + SyncScissorTest(); auto& tev_stages = Pica::g_state.regs.GetTevStages(); for (int index = 0; index < tev_stages.size(); ++index) SyncTevConstColor(index, tev_stages[index]); @@ -1166,6 +1184,22 @@ void RasterizerOpenGL::SyncDepthTest() { PicaToGL::CompareFunc(regs.output_merger.depth_test_func) : GL_ALWAYS; } +void RasterizerOpenGL::SyncScissorTest() { + const auto& regs = Pica::g_state.regs; + + if (uniform_block_data.data.scissor_x1 != regs.scissor_test.x1 || + uniform_block_data.data.scissor_y1 != regs.scissor_test.y1 || + uniform_block_data.data.scissor_x2 != regs.scissor_test.x2 || + uniform_block_data.data.scissor_y2 != regs.scissor_test.y2) { + + uniform_block_data.data.scissor_x1 = regs.scissor_test.x1; + uniform_block_data.data.scissor_y1 = regs.scissor_test.y1; + uniform_block_data.data.scissor_x2 = regs.scissor_test.x2; + uniform_block_data.data.scissor_y2 = regs.scissor_test.y2; + uniform_block_data.dirty = true; + } +} + void RasterizerOpenGL::SyncCombinerColor() { auto combiner_color = PicaToGL::ColorRGBA8(Pica::g_state.regs.tev_combiner_buffer_color.raw); if (combiner_color != uniform_block_data.data.tev_combiner_buffer_color) { diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 42482df4b..c5029432b 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -56,6 +56,8 @@ union PicaShaderConfig { const auto& regs = Pica::g_state.regs; + state.scissor_test_mode = regs.scissor_test.mode; + state.depthmap_enable = regs.depthmap_enable; state.alpha_test_func = regs.output_merger.alpha_test.enable ? @@ -172,6 +174,7 @@ union PicaShaderConfig { struct State { Pica::Regs::CompareFunc alpha_test_func; + Pica::Regs::ScissorMode scissor_test_mode; Pica::Regs::TextureConfig::TextureType texture0_type; std::array<TevStageConfigRaw, 6> tev_stages; u8 combiner_buffer_input; @@ -325,9 +328,14 @@ private: // the end of a uniform block is included in UNIFORM_BLOCK_DATA_SIZE or not. // Not following that rule will cause problems on some AMD drivers. struct UniformData { + alignas(8) GLvec2 framebuffer_scale; GLint alphatest_ref; GLfloat depth_scale; GLfloat depth_offset; + GLint scissor_x1; + GLint scissor_y1; + GLint scissor_x2; + GLint scissor_y2; alignas(16) GLvec3 fog_color; alignas(16) GLvec3 lighting_global_ambient; LightSrc light_src[8]; @@ -335,7 +343,7 @@ private: alignas(16) GLvec4 tev_combiner_buffer_color; }; - static_assert(sizeof(UniformData) == 0x3A0, "The size of the UniformData structure has changed, update the structure in the shader"); + static_assert(sizeof(UniformData) == 0x3C0, "The size of the UniformData structure has changed, update the structure in the shader"); static_assert(sizeof(UniformData) < 16384, "UniformData structure must be less than 16kb as per the OpenGL spec"); /// Sets the OpenGL shader in accordance with the current PICA register state @@ -384,6 +392,9 @@ private: /// Syncs the depth test states to match the PICA register void SyncDepthTest(); + /// Syncs the scissor test state to match the PICA register + void SyncScissorTest(); + /// Syncs the TEV combiner color buffer to match the PICA register void SyncCombinerColor(); diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index 3bace7f01..36513dedc 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -539,6 +539,8 @@ in float texcoord0_w; in vec4 normquat; in vec3 view; +in vec4 gl_FragCoord; + out vec4 color; struct LightSrc { @@ -552,9 +554,14 @@ struct LightSrc { }; layout (std140) uniform shader_data { + vec2 framebuffer_scale; int alphatest_ref; float depth_scale; float depth_offset; + int scissor_x1; + int scissor_y1; + int scissor_x2; + int scissor_y2; vec3 fog_color; vec3 lighting_global_ambient; LightSrc light_src[NUM_LIGHTS]; @@ -582,6 +589,19 @@ vec4 secondary_fragment_color = vec4(0.0); return out; } + // Append the scissor test + if (state.scissor_test_mode != Regs::ScissorMode::Disabled) { + out += "if ("; + // Negate the condition if we have to keep only the pixels outside the scissor box + if (state.scissor_test_mode == Regs::ScissorMode::Include) + out += "!"; + // x2,y2 have +1 added to cover the entire pixel area + out += "(gl_FragCoord.x >= scissor_x1 * framebuffer_scale.x && " + "gl_FragCoord.y >= scissor_y1 * framebuffer_scale.y && " + "gl_FragCoord.x < (scissor_x2 + 1) * framebuffer_scale.x && " + "gl_FragCoord.y < (scissor_y2 + 1) * framebuffer_scale.y)) discard;\n"; + } + out += "float z_over_w = 1.0 - gl_FragCoord.z * 2.0;\n"; out += "float depth = z_over_w * depth_scale + depth_offset;\n"; if (state.depthmap_enable == Pica::Regs::DepthBuffering::WBuffering) { diff --git a/src/video_core/renderer_opengl/pica_to_gl.h b/src/video_core/renderer_opengl/pica_to_gl.h index 6dc2758c5..d9b9c9cc2 100644 --- a/src/video_core/renderer_opengl/pica_to_gl.h +++ b/src/video_core/renderer_opengl/pica_to_gl.h @@ -17,6 +17,7 @@ #include "video_core/pica.h" +using GLvec2 = std::array<GLfloat, 2>; using GLvec3 = std::array<GLfloat, 3>; using GLvec4 = std::array<GLfloat, 4>; |