Compare commits

...

114 Commits

Author SHA1 Message Date
MerryMage
88c9608eac arm_dynarmic: Fix timing 2018-03-24 09:02:19 +00:00
bunnei
31816aac38 Merge pull request #274 from Subv/viewport_regs
GPU: Added viewport registers to Maxwell3D's reg structure.
2018-03-24 02:42:16 -04:00
Subv
9b9de30086 GPU: Added viewport registers to Maxwell3D's reg structure. 2018-03-24 01:22:19 -05:00
bunnei
a10baacf9e Merge pull request #265 from bunnei/tegra-progress-2
Tegra progress 2
2018-03-23 23:30:48 -04:00
bunnei
d561e4acc8 gl_rasterizer: Fake render in green, because it's cooler. 2018-03-23 22:27:53 -04:00
bunnei
4ed54738fc gl_rasterizer: Log warning instead of sync'ing unimplemented funcs. 2018-03-23 22:24:16 -04:00
bunnei
a397a9e9a4 Merge pull request #255 from Subv/sd_card
FS: Implemented access to the SD card
2018-03-23 20:48:26 -04:00
bunnei
b7da9d5a54 gl_rasterizer_cache: Add missing include for vm_manager. 2018-03-23 16:54:20 -04:00
bunnei
0f8401906b renderer_opengl: Only invalidate the framebuffer region, not flush. 2018-03-23 15:52:14 -04:00
bunnei
054393917e renderer_opengl: Fixes for properly flushing & rendering the framebuffer. 2018-03-23 15:49:04 -04:00
bunnei
b5f3e7951b memory: Fix typo in RasterizerFlushVirtualRegion. 2018-03-23 15:46:21 -04:00
bunnei
68658ce4b0 Merge pull request #268 from mailwl/ssl
Service/SSL: add ssl service
2018-03-23 15:30:17 -04:00
bunnei
fd0a7c0aaf Merge pull request #270 from N00byKing/patch-2
Remove Option for N/3DS from default.ini
2018-03-23 15:29:35 -04:00
bunnei
7ce4a03188 Merge pull request #269 from N00byKing/icontoyuzu
CITRA_ICON -> YUZU_ICON
2018-03-23 15:29:14 -04:00
Subv
4c06d55a81 FS: Move the file open mode calculation to a separate function. 2018-03-23 14:27:07 -05:00
bunnei
b36b627d4d RasterizerCacheOpenGL: FlushAll should flush full memory region. 2018-03-23 15:25:16 -04:00
bunnei
1a158dfcd6 memory: RasterizerFlushVirtualRegion should also check process image region. 2018-03-23 15:10:02 -04:00
bunnei
11047d7fd5 rasterizer: Flush and invalidate regions should be 64-bit. 2018-03-23 15:01:45 -04:00
bunnei
cdf541fb5b renderer_opengl: Add framebuffer_transform_flags member variable. 2018-03-23 14:59:14 -04:00
bunnei
ec4e1a3685 renderer_opengl: Better handling of framebuffer transform flags. 2018-03-23 14:58:27 -04:00
N00byKing
32fd57f0c8 Remove Option for N/3DS from default.ini 2018-03-23 18:06:55 +01:00
N00byKing
5ded39f5d8 CITRA_ICON -> YUZU_ICON 2018-03-23 16:56:59 +01:00
mailwl
9ee33350de Service/SSL: add ssl service 2018-03-23 09:32:50 +03:00
bunnei
633abd5a94 Merge pull request #264 from valentinvanelslande/cmd-dynarmic
yuzu_cmd: change default cpu core to dynarmic
2018-03-22 23:53:15 -04:00
bunnei
c2c55e0811 renderer_opengl: Use accelerated framebuffer load with LoadFBToScreenInfo. 2018-03-22 23:28:37 -04:00
bunnei
e12c2cf8c6 nvdisp_disp0: Always flush and invalidate framebuffer region.
- Workaround for texture forwarding until we have a better place.
2018-03-22 23:18:04 -04:00
bunnei
a0b1235f82 gl_rasterizer: Implement AccelerateDisplay method from Citra. 2018-03-22 23:06:54 -04:00
bunnei
f61b9f7338 LoadGLBuffer: Use bytes_per_pixel, not bits. 2018-03-22 23:01:57 -04:00
bunnei
63d3924b5b memory: Port RasterizerFlushVirtualRegion from Citra. 2018-03-22 22:56:41 -04:00
bunnei
6ced80bb47 gl_rasterizer_cache: LoadGLBuffer should do a morton copy. 2018-03-22 22:54:04 -04:00
bunnei
740310113b video_core: Move MortonCopyPixels128 to utils header. 2018-03-22 22:52:40 -04:00
Valentin Vanelslande
ee5f5a2c2d yuzu_cmd: change default cpu core to dynarmic 2018-03-22 19:44:34 -06:00
Valentin Vanelslande
47cac816f6 default_ini: change default cpu core to dynarmic 2018-03-22 19:43:59 -06:00
bunnei
8a250de987 video_core: Remove usage of PAddr and replace with VAddr. 2018-03-22 21:13:46 -04:00
bunnei
bfe45774f1 video_core: Move FramebufferInfo to FramebufferConfig in GPU. 2018-03-22 21:04:30 -04:00
bunnei
c6362543d4 gl_rasterizer: Replace a bunch of UNIMPLEMENTED with ASSERT. 2018-03-22 20:19:34 -04:00
bunnei
f707c2dac4 gl_rasterizer: Add a simple passthrough shader in lieu of shader generation. 2018-03-22 20:00:41 -04:00
bunnei
7c3a263839 gpu: Expose Maxwell3D engine. 2018-03-22 19:48:20 -04:00
bunnei
3a6604e8fa maxwell_3d: Add some format decodings and string helper functions. 2018-03-22 19:47:28 -04:00
bunnei
656de23d93 renderer: Create rasterizer and cleanup. 2018-03-22 19:46:37 -04:00
bunnei
4daf91fc69 Merge pull request #263 from N00byKing/non3ds
Remove more N3DS References
2018-03-22 18:02:51 -04:00
N00byKing
8afdbf6a1f Remove more N3DS References 2018-03-22 21:25:06 +01:00
bunnei
b5c03088bc Merge pull request #261 from mailwl/spl
Service/spl: add module and services
2018-03-22 10:28:28 -04:00
mailwl
95e747cd06 Service/spl: add module and services 2018-03-22 09:55:14 +03:00
bunnei
ec5ede68e7 Merge pull request #258 from Subv/gpu_attribs
GPU: Added vertex attrib format and triangle topology registers
2018-03-21 19:36:06 -04:00
bunnei
2ab021da14 Merge pull request #260 from N00byKing/3535
Implement Pull #3535 from citra: CMake: Set EMU_ARCH_BITS in CMakeLists.txt
2018-03-21 18:49:00 -04:00
bunnei
7fa3bf02ca Merge pull request #259 from N00byKing/usehttps
Use HTTPS for lz4
2018-03-21 18:48:39 -04:00
N00byKing
34b733e70e CMake: Set EMU_ARCH_BITS in CMakeLists.txt 2018-03-21 19:03:20 +01:00
N00byKing
3cdf6c536d Use HTTPS for Submodule lz4 2018-03-21 19:01:54 +01:00
Subv
eff3f60b73 FS: Implemented IFileSystem::CreateDirectory. 2018-03-21 09:55:59 -05:00
bunnei
4721ea4523 Merge pull request #257 from mailwl/vi-module
Service/vi: convert services to module
2018-03-21 10:48:15 -04:00
Subv
c450d264eb GPU: Added vertex attribute format registers. 2018-03-21 09:26:47 -05:00
mailwl
6673ed1274 Service/vi: convert services to module 2018-03-21 13:09:40 +03:00
Subv
ae28a52277 GPU: Added registers for the number of vertices to render. 2018-03-20 23:28:06 -05:00
bunnei
0b3ab30762 Merge pull request #254 from bunnei/port-citra-renderer
Port Citra OpenGL rasterizer code
2018-03-20 21:37:43 -04:00
bunnei
8c8de2efe9 Merge pull request #256 from mailwl/fatal
Service: add fatal:u, fatal:p services
2018-03-20 11:12:39 -04:00
mailwl
dca7cfb9cf Service: add fatal:u, fatal:p services 2018-03-20 16:59:02 +03:00
bunnei
6e3222363c renderer_gl: Port boilerplate rasterizer code over from Citra. 2018-03-20 00:07:32 -04:00
bunnei
9c468e0c55 gl_shader_util: Sync latest version with Citra. 2018-03-20 00:07:31 -04:00
bunnei
d7b1ebe4a8 renderer_gl: Port over gl_shader_gen module from Citra. 2018-03-20 00:07:30 -04:00
Subv
0485ee499f FS: Implemented IFileSystem's OpenDirectory function.
Note that the filter parameter is not yet implemented.
2018-03-19 23:02:30 -05:00
Subv
21bac2d7d7 FS: Added the IDirectory IPC interface and implemented its two functions. 2018-03-19 23:01:47 -05:00
Subv
6d90d99d12 FS: Implement DiskFileSystem's OpenDirectory interface. 2018-03-19 23:00:37 -05:00
Subv
e1d7b9fc2c FS: Implement DiskFileSystem::GetEntryType for existing files/directories. 2018-03-19 22:59:38 -05:00
Subv
a9ba2c2000 FS: Updated the Directory Entry structure to match the Switch. 2018-03-19 22:58:55 -05:00
Subv
fc44261dd1 FS: Support the file Append open mode. 2018-03-19 22:57:34 -05:00
Mat M
f4700ccabf Merge pull request #253 from Subv/rt_depth
GPU: Added registers for color and Z buffers.
2018-03-19 23:37:47 -04:00
bunnei
4bdb46e4c2 renderer_gl: Port over gl_shader_decompiler module from Citra. 2018-03-19 23:14:03 -04:00
bunnei
a3e10b1a72 renderer_gl: Port over gl_rasterizer_cache module from Citra. 2018-03-19 23:14:03 -04:00
bunnei
db0cfb8e8b gl_resource_manager: Sync latest version with Citra. 2018-03-19 23:14:02 -04:00
bunnei
0e4b9cdde4 renderer_gl: Port over gl_stream_buffer module from Citra. 2018-03-19 23:14:02 -04:00
bunnei
a1cf5020e6 externals: Update Glad to latest version used by Citra. 2018-03-19 23:14:01 -04:00
bunnei
6a0902e56d gl_state: Sync latest version with Citra. 2018-03-19 23:13:49 -04:00
Subv
808704c78c FS: Implement MountSdCard. 2018-03-19 21:21:49 -05:00
Subv
c4ca802b9d FS: Added an SDMC archive factory and registered it to the SDMC archive on startup. 2018-03-19 21:17:15 -05:00
Subv
7a27a11770 GPU: Added Z buffer registers to Maxwell3D's reg structure. 2018-03-19 16:55:33 -05:00
Subv
21d9519032 GPU: Added the render target (RT) registers to Maxwell3D's reg structure. 2018-03-19 16:46:29 -05:00
bunnei
a90ab1dec7 Merge pull request #252 from N00byKing/3064
Implement Pull #3064 from citra: Clean all format warnings (Yuzu-specific format warnings cleared too)
2018-03-19 16:29:03 -04:00
N00byKing
1d8b6ad13b Clang Fixes 2018-03-19 17:53:35 +01:00
N00byKing
d16e08454d oops 2018-03-19 17:43:04 +01:00
N00byKing
0e72d0d826 More Warning cleanups 2018-03-19 17:27:04 +01:00
N00byKing
ef875d6a35 Clean Warnings (?) 2018-03-19 17:07:08 +01:00
bunnei
b2d7c92cae Merge pull request #251 from Subv/tic_tsc
GPU: Added TIC and TSC registers to the Maxwell3D register structure.
2018-03-19 10:33:21 -04:00
Subv
dcae0c9a4f GPU: Added the TSC registers to the Maxwell3D register structure. 2018-03-19 00:36:25 -05:00
Subv
cff7b29bba GPU: Added the TIC registers to the Maxwell3D register structure. 2018-03-19 00:32:57 -05:00
bunnei
23a0d2d7b7 Merge pull request #193 from N00byKing/3184_2_robotic_boogaloo
Implement Pull #3184 from citra: core/arm: Improve timing accuracy before service calls in JIT (Rebased)
2018-03-18 22:35:47 -04:00
bunnei
2dc3a56e96 Merge pull request #250 from bunnei/buffer-dequeue-wait
vi: TransactParcel DequeueBuffer should wait current thread
2018-03-18 22:25:09 -04:00
bunnei
2332a44b68 Merge pull request #249 from Subv/macro_E1A
GPU: Implement macro 0xE1A BindTextureInfoBuffer in HLE.
2018-03-18 21:04:29 -04:00
bunnei
c1c92c30f9 vi: Remove DequeueBuffer and wait until next available buffer. 2018-03-18 20:56:35 -04:00
bunnei
c86af6939c hle_ipc: Add SleepClientThread to block current thread within HLE routines. 2018-03-18 20:56:34 -04:00
bunnei
2faa83ca13 hle_ipc: Use shared_ptr instead of unique_ptr to allow copies. 2018-03-18 20:56:33 -04:00
bunnei
019f1a0cf0 hle_ipc: Remove GetPointer(..) usage with WriteToOutgoingCommandBuffer. 2018-03-18 20:56:33 -04:00
bunnei
e353b9fb3d thread: Add THREADSTATUS_WAIT_HLE_EVENT, remove THREADSTATUS_WAIT_ARB. 2018-03-18 20:56:32 -04:00
Subv
03156d0c9a GPU: Implement macro 0xE1A BindTextureInfoBuffer in HLE.
This macro simply sets the current CB_ADDRESS to the texture buffer address for the input shader stage.
2018-03-18 19:03:40 -05:00
bunnei
6317a0b2ca Merge pull request #248 from Subv/cb_data
GPU: Handle writes to the CB_DATA method.
2018-03-18 19:45:40 -04:00
Subv
7b6868e908 GPU: Implement the BindStorageBuffer macro method in HLE.
This macro binds the SSBO Info Buffer as the current ConstBuffer.
This buffer is usually bound to c0 during shader execution.
Games seem to use this macro instead of directly writing the address for some reason.
2018-03-18 16:50:42 -05:00
Subv
85d820b1b4 GPU: Handle writes to the CB_DATA method.
Writing to this method will cause the written value to be stored in the currently-set ConstBuffer plus CB_POS.

This method is usually used to upload uniforms or other shader-visible data.
2018-03-18 15:23:24 -05:00
Subv
a64b936cbe GPU: Move the GPU's class constructor and destructors to a cpp file.
This should reduce recompile times when editing the Maxwell3D register structure.
2018-03-18 15:23:24 -05:00
Sebastian Valle
46f9d4b4a3 Merge pull request #246 from Subv/gpu_macro_calls
GPU: Store uploaded GPU macros and keep track of the number of method arguments.
2018-03-18 15:13:40 -05:00
Subv
aa586fa268 GPU: Store uploaded GPU macros and keep track of the number of method parameters. 2018-03-18 11:51:46 -05:00
Subv
7ac8657432 GPU: Macros are specific to the Maxwell3D engine, so handle them internally. 2018-03-18 11:51:45 -05:00
bunnei
29981fa2eb Merge pull request #245 from Subv/set_shader2
GPU: Store shader constbuffer bindings in the GPU state.
2018-03-17 21:19:39 -04:00
Subv
ccb8da1512 GPU: Renamed ShaderType to ShaderStage as that is less confusing. 2018-03-17 18:32:57 -05:00
Subv
88698c156f GPU: Store shader constbuffer bindings in the GPU state. 2018-03-17 18:32:57 -05:00
Subv
66dae22790 GPU: Corrected some register offsets and removed superfluous macro registers. 2018-03-17 18:32:56 -05:00
Subv
1d9d9c16e8 GPU: Make the SetShader macro call do the same as the real macro's code.
It'll now set the CB_SIZE, CB_ADDRESS and CB_BIND registers when it's called.

Presumably this SetShader function is binding the constant shader uniforms to buffer 1 (c1[]).
2018-03-17 18:32:55 -05:00
Subv
579000e747 GPU: Corrected the parameter documentation for the SetShader macro call.
Register 0xE24 is actually a macro that sets some shader parameters in the register structure.

Macros are uploaded to the GPU at startup and have their own ISA, we'll probably write an interpreter for this in the future.
2018-03-17 13:55:42 -05:00
bunnei
516ef4f19f Merge pull request #242 from Subv/set_shader
GPU: Handle the SetShader method call (0xE24) and store the shader config.
2018-03-17 00:34:17 -04:00
bunnei
c286921739 Merge pull request #243 from Subv/vertex_buffer
GPU: Added the vertex array registers.
2018-03-17 00:04:31 -04:00
Subv
f93d769a1c GPU: Handle the SetShader method call (0xE24) and store the shader config. 2018-03-16 22:51:06 -05:00
Subv
d2888f7e90 GPU: Added the vertex array registers. 2018-03-16 22:47:45 -05:00
bunnei
cd4e8a989c Merge pull request #241 from Subv/gpu_method_call
GPU: Process command mode 5 (IncreaseOnce) differently from other commands
2018-03-16 22:28:22 -04:00
Subv
29feece4b8 GPU: Process command mode 5 (IncreaseOnce) differently from other commands.
Accumulate all arguments before calling the desired method.

Note: Maybe we should do the same for the NonIncreasing mode?
2018-03-16 20:32:44 -05:00
N00byKing
bc88cae0c7 Implements citra-emu/citra#3184 2018-02-25 11:44:21 +01:00
103 changed files with 26351 additions and 1923 deletions

2
.gitmodules vendored
View File

@@ -18,7 +18,7 @@
url = https://github.com/fmtlib/fmt.git
[submodule "lz4"]
path = externals/lz4
url = http://github.com/lz4/lz4.git
url = https://github.com/lz4/lz4.git
[submodule "unicorn"]
path = externals/unicorn
url = https://github.com/yuzu-emu/unicorn

View File

@@ -160,6 +160,9 @@ set_property(DIRECTORY APPEND PROPERTY
COMPILE_DEFINITIONS $<$<CONFIG:Debug>:_DEBUG> $<$<NOT:$<CONFIG:Debug>>:NDEBUG>)
math(EXPR EMU_ARCH_BITS ${CMAKE_SIZEOF_VOID_P}*8)
add_definitions(-DEMU_ARCH_BITS=${EMU_ARCH_BITS})
# System imported libraries
# ======================

View File

@@ -1,5 +1,5 @@
These files were generated by the [glad](https://github.com/Dav1dde/glad) OpenGL loader generator and have been checked in as-is. You can re-generate them using glad with the following command:
```
python -m glad --profile core --out-path glad/ --api gl=3.3,gles=3.0
python -m glad --profile core --out-path glad/ --api gl=3.3 --generator=c
```

View File

@@ -26,7 +26,7 @@
/* Khronos platform-specific types and definitions.
*
* $Revision: 23298 $ on $Date: 2013-09-30 17:07:13 -0700 (Mon, 30 Sep 2013) $
* $Revision: 32517 $ on $Date: 2016-03-11 02:41:19 -0800 (Fri, 11 Mar 2016) $
*
* Adopters may modify this file to suit their platform. Adopters are
* encouraged to submit platform specific modifications to the Khronos
@@ -101,6 +101,8 @@
# define KHRONOS_APICALL __declspec(dllimport)
#elif defined (__SYMBIAN32__)
# define KHRONOS_APICALL IMPORT_C
#elif defined(__ANDROID__)
# define KHRONOS_APICALL __attribute__((visibility("default")))
#else
# define KHRONOS_APICALL
#endif
@@ -223,7 +225,7 @@ typedef signed short int khronos_int16_t;
typedef unsigned short int khronos_uint16_t;
/*
* Types that differ between LLP64 and LP64 architectures - in LLP64,
* Types that differ between LLP64 and LP64 architectures - in LLP64,
* pointers are 64 bits, but 'long' is still 32 bits. Win64 appears
* to be the only LLP64 architecture in current use.
*/

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -58,7 +58,6 @@ add_library(common STATIC
misc.cpp
param_package.cpp
param_package.h
platform.h
quaternion.h
scm_rev.cpp
scm_rev.h

View File

@@ -37,6 +37,7 @@ namespace Log {
SUB(Service, AM) \
SUB(Service, AOC) \
SUB(Service, APM) \
SUB(Service, Fatal) \
SUB(Service, Friend) \
SUB(Service, FS) \
SUB(Service, HID) \
@@ -47,6 +48,8 @@ namespace Log {
SUB(Service, PCTL) \
SUB(Service, SET) \
SUB(Service, SM) \
SUB(Service, SPL) \
SUB(Service, SSL) \
SUB(Service, Time) \
SUB(Service, VI) \
CLS(HW) \

View File

@@ -54,6 +54,7 @@ enum class Class : ClassType {
Service_AOC, ///< The AOC (AddOn Content) service
Service_APM, ///< The APM (Performance) service
Service_Audio, ///< The Audio (Audio control) service
Service_Fatal, ///< The Fatal service
Service_Friend, ///< The friend service
Service_FS, ///< The FS (Filesystem) service
Service_HID, ///< The HID (Human interface device) service
@@ -64,6 +65,8 @@ enum class Class : ClassType {
Service_PCTL, ///< The PCTL (Parental control) service
Service_SET, ///< The SET (Settings) service
Service_SM, ///< The SM (Service manager) service
Service_SPL, ///< The SPL service
Service_SSL, ///< The SSL service
Service_Time, ///< The time service
Service_VI, ///< The VI (Video interface) service
HW, ///< Low-level hardware emulation

View File

@@ -1,34 +0,0 @@
/**
* Copyright (C) 2005-2012 Gekko Emulator
*
* @file platform.h
* @author ShizZy <shizzy247@gmail.com>
* @date 2012-02-11
* @brief Platform detection macros for portable compilation
*
* @section LICENSE
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details at
* http://www.gnu.org/copyleft/gpl.html
*
* Official project repository can be found at:
* http://code.google.com/p/gekko-gc-emu/
*/
#pragma once
////////////////////////////////////////////////////////////////////////////////////////////////////
// Platform detection
#if defined(ARCHITECTURE_x86_64) || defined(__aarch64__)
#define EMU_ARCH_BITS 64
#elif defined(__i386) || defined(_M_IX86) || defined(__arm__) || defined(_M_ARM)
#define EMU_ARCH_BITS 32
#endif

View File

@@ -22,6 +22,8 @@ add_library(core STATIC
file_sys/romfs_filesystem.h
file_sys/savedata_factory.cpp
file_sys/savedata_factory.h
file_sys/sdmc_factory.cpp
file_sys/sdmc_factory.h
file_sys/storage.h
frontend/emu_window.cpp
frontend/emu_window.h
@@ -114,6 +116,12 @@ add_library(core STATIC
hle/service/audio/audren_u.h
hle/service/audio/codecctl.cpp
hle/service/audio/codecctl.h
hle/service/fatal/fatal.cpp
hle/service/fatal/fatal.h
hle/service/fatal/fatal_p.cpp
hle/service/fatal/fatal_p.h
hle/service/fatal/fatal_u.cpp
hle/service/fatal/fatal_u.h
hle/service/filesystem/filesystem.cpp
hle/service/filesystem/filesystem.h
hle/service/filesystem/fsp_srv.cpp
@@ -187,6 +195,14 @@ add_library(core STATIC
hle/service/sockets/sfdnsres.h
hle/service/sockets/sockets.cpp
hle/service/sockets/sockets.h
hle/service/spl/csrng.cpp
hle/service/spl/csrng.h
hle/service/spl/module.cpp
hle/service/spl/module.h
hle/service/spl/spl.cpp
hle/service/spl/spl.h
hle/service/ssl/ssl.cpp
hle/service/ssl/ssl.h
hle/service/time/time.cpp
hle/service/time/time.h
hle/service/time/time_s.cpp

View File

@@ -25,19 +25,11 @@ public:
VAddr tls_address;
};
/**
* Runs the CPU for the given number of instructions
* @param num_instructions Number of instructions to run
*/
void Run(int num_instructions) {
ExecuteInstructions(num_instructions);
this->num_instructions += num_instructions;
}
/// Runs the CPU until an event happens
virtual void Run() = 0;
/// Step CPU by one instruction
void Step() {
Run(1);
}
virtual void Step() = 0;
/// Maps a backing memory region for the CPU
virtual void MapBackingMemory(VAddr address, size_t size, u8* memory,
@@ -126,19 +118,4 @@ public:
/// Prepare core for thread reschedule (if needed to correctly handle state)
virtual void PrepareReschedule() = 0;
/// Getter for num_instructions
u64 GetNumInstructions() const {
return num_instructions;
}
protected:
/**
* Executes the given number of instructions
* @param num_instructions Number of instructions to executes
*/
virtual void ExecuteInstructions(int num_instructions) = 0;
private:
u64 num_instructions = 0; ///< Number of instructions executed
};

View File

@@ -86,21 +86,17 @@ public:
}
void AddTicks(u64 ticks) override {
if (ticks > ticks_remaining) {
ticks_remaining = 0;
return;
}
ticks -= ticks_remaining;
CoreTiming::AddTicks(ticks - num_interpreted_instructions);
num_interpreted_instructions = 0;
}
u64 GetTicksRemaining() override {
return ticks_remaining;
return std::max(CoreTiming::GetDowncount(), 0);
}
u64 GetCNTPCT() override {
return CoreTiming::GetTicks();
}
ARM_Dynarmic& parent;
size_t ticks_remaining = 0;
size_t num_interpreted_instructions = 0;
u64 tpidrro_el0 = 0;
u64 tpidr_el0 = 0;
@@ -122,11 +118,22 @@ std::unique_ptr<Dynarmic::A64::Jit> MakeJit(const std::unique_ptr<ARM_Dynarmic_C
return std::make_unique<Dynarmic::A64::Jit>(config);
}
void ARM_Dynarmic::Run() {
ASSERT(Memory::GetCurrentPageTable() == current_page_table);
jit->Run();
}
void ARM_Dynarmic::Step() {
cb->InterpreterFallback(jit->GetPC(), 1);
}
ARM_Dynarmic::ARM_Dynarmic()
: cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), jit(MakeJit(cb)) {
ARM_Interface::ThreadContext ctx;
inner_unicorn.SaveContext(ctx);
LoadContext(ctx);
PageTableChanged();
}
ARM_Dynarmic::~ARM_Dynarmic() = default;
@@ -189,13 +196,6 @@ void ARM_Dynarmic::SetTlsAddress(u64 address) {
cb->tpidrro_el0 = address;
}
void ARM_Dynarmic::ExecuteInstructions(int num_instructions) {
cb->ticks_remaining = num_instructions;
jit->Run();
CoreTiming::AddTicks(num_instructions - cb->num_interpreted_instructions);
cb->num_interpreted_instructions = 0;
}
void ARM_Dynarmic::SaveContext(ARM_Interface::ThreadContext& ctx) {
ctx.cpu_registers = jit->GetRegisters();
ctx.sp = jit->GetSP();
@@ -228,4 +228,5 @@ void ARM_Dynarmic::ClearInstructionCache() {
void ARM_Dynarmic::PageTableChanged() {
jit = MakeJit(cb);
current_page_table = Memory::GetCurrentPageTable();
}

View File

@@ -29,6 +29,8 @@ public:
u32 GetVFPReg(int index) const override;
void SetVFPReg(int index, u32 value) override;
u32 GetCPSR() const override;
void Run() override;
void Step() override;
void SetCPSR(u32 cpsr) override;
VAddr GetTlsAddress() const override;
void SetTlsAddress(VAddr address) override;
@@ -37,7 +39,6 @@ public:
void LoadContext(const ThreadContext& ctx) override;
void PrepareReschedule() override;
void ExecuteInstructions(int num_instructions) override;
void ClearInstructionCache() override;
void PageTableChanged() override;
@@ -47,4 +48,6 @@ private:
std::unique_ptr<ARM_Dynarmic_Callbacks> cb;
std::unique_ptr<Dynarmic::A64::Jit> jit;
ARM_Unicorn inner_unicorn;
Memory::PageTable* current_page_table = nullptr;
};

View File

@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <unicorn/arm64.h>
#include "common/assert.h"
#include "common/microprofile.h"
@@ -52,7 +53,7 @@ static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int si
void* user_data) {
ARM_Interface::ThreadContext ctx{};
Core::CPU().SaveContext(ctx);
ASSERT_MSG(false, "Attempted to read from unmapped memory: 0x%llx, pc=0x%llx, lr=0x%llx", addr,
ASSERT_MSG(false, "Attempted to read from unmapped memory: 0x%lx, pc=0x%lx, lr=0x%lx", addr,
ctx.pc, ctx.cpu_registers[30]);
return {};
}
@@ -153,6 +154,14 @@ void ARM_Unicorn::SetTlsAddress(VAddr base) {
CHECKED(uc_reg_write(uc, UC_ARM64_REG_TPIDRRO_EL0, &base));
}
void ARM_Unicorn::Run() {
ExecuteInstructions(std::max(CoreTiming::GetDowncount(), 0));
}
void ARM_Unicorn::Step() {
ExecuteInstructions(1);
}
MICROPROFILE_DEFINE(ARM_Jit, "ARM JIT", "ARM JIT", MP_RGB(255, 64, 64));
void ARM_Unicorn::ExecuteInstructions(int num_instructions) {

View File

@@ -30,7 +30,9 @@ public:
void SaveContext(ThreadContext& ctx) override;
void LoadContext(const ThreadContext& ctx) override;
void PrepareReschedule() override;
void ExecuteInstructions(int num_instructions) override;
void ExecuteInstructions(int num_instructions);
void Run() override;
void Step() override;
void ClearInstructionCache() override;
void PageTableChanged() override{};

View File

@@ -26,7 +26,7 @@ namespace Core {
/*static*/ System System::s_instance;
System::ResultStatus System::RunLoop(int tight_loop) {
System::ResultStatus System::RunLoop(bool tight_loop) {
status = ResultStatus::Success;
if (!cpu_core) {
return ResultStatus::ErrorNotInitialized;
@@ -40,7 +40,7 @@ System::ResultStatus System::RunLoop(int tight_loop) {
if (GDBStub::GetCpuHaltFlag()) {
if (GDBStub::GetCpuStepFlag()) {
GDBStub::SetCpuStepFlag(false);
tight_loop = 1;
tight_loop = false;
} else {
return ResultStatus::Success;
}
@@ -56,7 +56,11 @@ System::ResultStatus System::RunLoop(int tight_loop) {
PrepareReschedule();
} else {
CoreTiming::Advance();
cpu_core->Run(tight_loop);
if (tight_loop) {
cpu_core->Run();
} else {
cpu_core->Step();
}
}
HW::Update();
@@ -66,7 +70,7 @@ System::ResultStatus System::RunLoop(int tight_loop) {
}
System::ResultStatus System::SingleStep() {
return RunLoop(1);
return RunLoop(false);
}
System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& filepath) {
@@ -95,14 +99,15 @@ System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& file
ResultStatus init_result{Init(emu_window, system_mode.first.get())};
if (init_result != ResultStatus::Success) {
LOG_CRITICAL(Core, "Failed to initialize system (Error %i)!", init_result);
LOG_CRITICAL(Core, "Failed to initialize system (Error %i)!",
static_cast<int>(init_result));
System::Shutdown();
return init_result;
}
const Loader::ResultStatus load_result{app_loader->Load(current_process)};
if (Loader::ResultStatus::Success != load_result) {
LOG_CRITICAL(Core, "Failed to load ROM (Error %i)!", load_result);
LOG_CRITICAL(Core, "Failed to load ROM (Error %i)!", static_cast<int>(load_result));
System::Shutdown();
switch (load_result) {

View File

@@ -53,10 +53,10 @@ public:
* is not required to do a full dispatch with each instruction. NOTE: the number of instructions
* requested is not guaranteed to run, as this will be interrupted preemptively if a hardware
* update is requested (e.g. on a thread switch).
* @param tight_loop Number of instructions to execute.
* @param tight_loop If false, the CPU single-steps.
* @return Result status, indicating whether or not the operation succeeded.
*/
ResultStatus RunLoop(int tight_loop = 100000);
ResultStatus RunLoop(bool tight_loop = true);
/**
* Step the CPU one instruction

View File

@@ -6,34 +6,28 @@
#include <array>
#include <cstddef>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/file_sys/filesystem.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
namespace FileSys {
// Structure of a directory entry, from http://3dbrew.org/wiki/FSDir:Read#Entry_format
const size_t FILENAME_LENGTH = 0x20C / 2;
// Structure of a directory entry, from
// http://switchbrew.org/index.php?title=Filesystem_services#DirectoryEntry
const size_t FILENAME_LENGTH = 0x300;
struct Entry {
char16_t filename[FILENAME_LENGTH]; // Entry name (UTF-16, null-terminated)
std::array<char, 9> short_name; // 8.3 file name ('longfilename' -> 'LONGFI~1', null-terminated)
char unknown1; // unknown (observed values: 0x0A, 0x70, 0xFD)
std::array<char, 4>
extension; // 8.3 file extension (set to spaces for directories, null-terminated)
char unknown2; // unknown (always 0x01)
char unknown3; // unknown (0x00 or 0x08)
char is_directory; // directory flag
char is_hidden; // hidden flag
char is_archive; // archive flag
char is_read_only; // read-only flag
u64 file_size; // file size (for files only)
char filename[FILENAME_LENGTH];
INSERT_PADDING_BYTES(4);
EntryType type;
INSERT_PADDING_BYTES(3);
u64 file_size;
};
static_assert(sizeof(Entry) == 0x228, "Directory Entry struct isn't exactly 0x228 bytes long!");
static_assert(offsetof(Entry, short_name) == 0x20C, "Wrong offset for short_name in Entry.");
static_assert(offsetof(Entry, extension) == 0x216, "Wrong offset for extension in Entry.");
static_assert(offsetof(Entry, is_archive) == 0x21E, "Wrong offset for is_archive in Entry.");
static_assert(offsetof(Entry, file_size) == 0x220, "Wrong offset for file_size in Entry.");
static_assert(sizeof(Entry) == 0x310, "Directory Entry struct isn't exactly 0x310 bytes long!");
static_assert(offsetof(Entry, type) == 0x304, "Wrong offset for type in Entry.");
static_assert(offsetof(Entry, file_size) == 0x308, "Wrong offset for file_size in Entry.");
class DirectoryBackend : NonCopyable {
public:
@@ -46,7 +40,10 @@ public:
* @param entries Buffer to read data into
* @return Number of entries listed
*/
virtual u32 Read(const u32 count, Entry* entries) = 0;
virtual u64 Read(const u64 count, Entry* entries) = 0;
/// Returns the number of entries still left to read.
virtual u64 GetEntryCount() const = 0;
/**
* Close the directory

View File

@@ -11,16 +11,43 @@
namespace FileSys {
static std::string ModeFlagsToString(Mode mode) {
std::string mode_str;
u32 mode_flags = static_cast<u32>(mode);
// Calculate the correct open mode for the file.
if ((mode_flags & static_cast<u32>(Mode::Read)) &&
(mode_flags & static_cast<u32>(Mode::Write))) {
if (mode_flags & static_cast<u32>(Mode::Append))
mode_str = "a+";
else
mode_str = "r+";
} else {
if (mode_flags & static_cast<u32>(Mode::Read))
mode_str = "r";
else if (mode_flags & static_cast<u32>(Mode::Append))
mode_str = "a";
else if (mode_flags & static_cast<u32>(Mode::Write))
mode_str = "w";
}
mode_str += "b";
return mode_str;
}
std::string Disk_FileSystem::GetName() const {
return "Disk";
}
ResultVal<std::unique_ptr<StorageBackend>> Disk_FileSystem::OpenFile(const std::string& path,
Mode mode) const {
ASSERT_MSG(mode == Mode::Read || mode == Mode::Write, "Other file modes are not supported");
// Calculate the correct open mode for the file.
std::string mode_str = ModeFlagsToString(mode);
std::string full_path = base_directory + path;
auto file = std::make_shared<FileUtil::IOFile>(full_path, mode == Mode::Read ? "rb" : "wb");
auto file = std::make_shared<FileUtil::IOFile>(full_path, mode_str.c_str());
if (!file->IsOpen()) {
return ERROR_PATH_NOT_FOUND;
@@ -75,8 +102,15 @@ ResultCode Disk_FileSystem::CreateFile(const std::string& path, u64 size) const
return ResultCode(-1);
}
ResultCode Disk_FileSystem::CreateDirectory(const Path& path) const {
LOG_WARNING(Service_FS, "(STUBBED) called");
ResultCode Disk_FileSystem::CreateDirectory(const std::string& path) const {
// TODO(Subv): Perform path validation to prevent escaping the emulator sandbox.
std::string full_path = base_directory + path;
if (FileUtil::CreateDir(full_path)) {
return RESULT_SUCCESS;
}
LOG_CRITICAL(Service_FS, "(unreachable) Unknown error creating %s", full_path.c_str());
// TODO(wwylele): Use correct error code
return ResultCode(-1);
}
@@ -88,8 +122,17 @@ ResultCode Disk_FileSystem::RenameDirectory(const Path& src_path, const Path& de
}
ResultVal<std::unique_ptr<DirectoryBackend>> Disk_FileSystem::OpenDirectory(
const Path& path) const {
return MakeResult<std::unique_ptr<DirectoryBackend>>(std::make_unique<Disk_Directory>());
const std::string& path) const {
std::string full_path = base_directory + path;
if (!FileUtil::IsDirectory(full_path)) {
// TODO(Subv): Find the correct error code for this.
return ResultCode(-1);
}
auto directory = std::make_unique<Disk_Directory>(full_path);
return MakeResult<std::unique_ptr<DirectoryBackend>>(std::move(directory));
}
u64 Disk_FileSystem::GetFreeSpaceSize() const {
@@ -103,8 +146,10 @@ ResultVal<FileSys::EntryType> Disk_FileSystem::GetEntryType(const std::string& p
return ERROR_PATH_NOT_FOUND;
}
// TODO(Subv): Find out the EntryType values
UNIMPLEMENTED_MSG("Unimplemented GetEntryType");
if (FileUtil::IsDirectory(full_path))
return MakeResult(EntryType::Directory);
return MakeResult(EntryType::File);
}
ResultVal<size_t> Disk_Storage::Read(const u64 offset, const size_t length, u8* buffer) const {
@@ -133,14 +178,50 @@ bool Disk_Storage::SetSize(const u64 size) const {
return false;
}
u32 Disk_Directory::Read(const u32 count, Entry* entries) {
LOG_WARNING(Service_FS, "(STUBBED) called");
return 0;
Disk_Directory::Disk_Directory(const std::string& path) : directory() {
unsigned size = FileUtil::ScanDirectoryTree(path, directory);
directory.size = size;
directory.isDirectory = true;
children_iterator = directory.children.begin();
}
bool Disk_Directory::Close() const {
LOG_WARNING(Service_FS, "(STUBBED) called");
return true;
u64 Disk_Directory::Read(const u64 count, Entry* entries) {
u64 entries_read = 0;
while (entries_read < count && children_iterator != directory.children.cend()) {
const FileUtil::FSTEntry& file = *children_iterator;
const std::string& filename = file.virtualName;
Entry& entry = entries[entries_read];
LOG_TRACE(Service_FS, "File %s: size=%llu dir=%d", filename.c_str(), file.size,
file.isDirectory);
// TODO(Link Mauve): use a proper conversion to UTF-16.
for (size_t j = 0; j < FILENAME_LENGTH; ++j) {
entry.filename[j] = filename[j];
if (!filename[j])
break;
}
if (file.isDirectory) {
entry.file_size = 0;
entry.type = EntryType::Directory;
} else {
entry.file_size = file.size;
entry.type = EntryType::File;
}
++entries_read;
++children_iterator;
}
return entries_read;
}
u64 Disk_Directory::GetEntryCount() const {
// We convert the children iterator into a const_iterator to allow template argument deduction
// in std::distance.
std::vector<FileUtil::FSTEntry>::const_iterator current = children_iterator;
return std::distance(current, directory.children.end());
}
} // namespace FileSys

View File

@@ -30,9 +30,10 @@ public:
ResultCode DeleteDirectory(const Path& path) const override;
ResultCode DeleteDirectoryRecursively(const Path& path) const override;
ResultCode CreateFile(const std::string& path, u64 size) const override;
ResultCode CreateDirectory(const Path& path) const override;
ResultCode CreateDirectory(const std::string& path) const override;
ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const override;
ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const override;
ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(
const std::string& path) const override;
u64 GetFreeSpaceSize() const override;
ResultVal<EntryType> GetEntryType(const std::string& path) const override;
@@ -59,8 +60,26 @@ private:
class Disk_Directory : public DirectoryBackend {
public:
u32 Read(const u32 count, Entry* entries) override;
bool Close() const override;
Disk_Directory(const std::string& path);
~Disk_Directory() override {
Close();
}
u64 Read(const u64 count, Entry* entries) override;
u64 GetEntryCount() const override;
bool Close() const override {
return true;
}
protected:
u32 total_entries_in_directory;
FileUtil::FSTEntry directory;
// We need to remember the last entry we returned, so a subsequent call to Read will continue
// from the next one. This iterator will always point to the next unread entry.
std::vector<FileUtil::FSTEntry>::iterator children_iterator;
};
} // namespace FileSys

View File

@@ -27,7 +27,7 @@ enum LowPathType : u32 {
Wchar = 4,
};
enum EntryType : u32 {
enum EntryType : u8 {
Directory = 0,
File = 1,
};
@@ -35,6 +35,7 @@ enum EntryType : u32 {
enum class Mode : u32 {
Read = 1,
Write = 2,
Append = 4,
};
class Path {
@@ -103,7 +104,7 @@ public:
* @param path Path relative to the archive
* @return Result of the operation
*/
virtual ResultCode CreateDirectory(const Path& path) const = 0;
virtual ResultCode CreateDirectory(const std::string& path) const = 0;
/**
* Delete a directory specified by its path
@@ -149,7 +150,8 @@ public:
* @param path Path relative to the archive
* @return Opened directory, or error code
*/
virtual ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const = 0;
virtual ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(
const std::string& path) const = 0;
/**
* Get the free space

View File

@@ -55,7 +55,7 @@ ResultCode RomFS_FileSystem::CreateFile(const std::string& path, u64 size) const
return ResultCode(-1);
}
ResultCode RomFS_FileSystem::CreateDirectory(const Path& path) const {
ResultCode RomFS_FileSystem::CreateDirectory(const std::string& path) const {
LOG_CRITICAL(Service_FS, "Attempted to create a directory in an ROMFS archive (%s).",
GetName().c_str());
// TODO(wwylele): Use correct error code
@@ -70,7 +70,8 @@ ResultCode RomFS_FileSystem::RenameDirectory(const Path& src_path, const Path& d
}
ResultVal<std::unique_ptr<DirectoryBackend>> RomFS_FileSystem::OpenDirectory(
const Path& path) const {
const std::string& path) const {
LOG_WARNING(Service_FS, "Opening Directory in a ROMFS archive");
return MakeResult<std::unique_ptr<DirectoryBackend>>(std::make_unique<ROMFSDirectory>());
}

View File

@@ -36,9 +36,10 @@ public:
ResultCode DeleteDirectory(const Path& path) const override;
ResultCode DeleteDirectoryRecursively(const Path& path) const override;
ResultCode CreateFile(const std::string& path, u64 size) const override;
ResultCode CreateDirectory(const Path& path) const override;
ResultCode CreateDirectory(const std::string& path) const override;
ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const override;
ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const override;
ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(
const std::string& path) const override;
u64 GetFreeSpaceSize() const override;
ResultVal<EntryType> GetEntryType(const std::string& path) const override;
@@ -70,7 +71,10 @@ private:
class ROMFSDirectory : public DirectoryBackend {
public:
u32 Read(const u32 count, Entry* entries) override {
u64 Read(const u64 count, Entry* entries) override {
return 0;
}
u64 GetEntryCount() const override {
return 0;
}
bool Close() const override {

View File

@@ -0,0 +1,40 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cinttypes>
#include <memory>
#include "common/common_types.h"
#include "common/logging/log.h"
#include "common/string_util.h"
#include "core/core.h"
#include "core/file_sys/disk_filesystem.h"
#include "core/file_sys/sdmc_factory.h"
namespace FileSys {
SDMC_Factory::SDMC_Factory(std::string sd_directory) : sd_directory(std::move(sd_directory)) {}
ResultVal<std::unique_ptr<FileSystemBackend>> SDMC_Factory::Open(const Path& path) {
// Create the SD Card directory if it doesn't already exist.
if (!FileUtil::IsDirectory(sd_directory)) {
FileUtil::CreateFullPath(sd_directory);
}
auto archive = std::make_unique<Disk_FileSystem>(sd_directory);
return MakeResult<std::unique_ptr<FileSystemBackend>>(std::move(archive));
}
ResultCode SDMC_Factory::Format(const Path& path) {
LOG_ERROR(Service_FS, "Unimplemented Format archive %s", GetName().c_str());
// TODO(Subv): Find the right error code for this
return ResultCode(-1);
}
ResultVal<ArchiveFormatInfo> SDMC_Factory::GetFormatInfo(const Path& path) const {
LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive %s", GetName().c_str());
// TODO(bunnei): Find the right error code for this
return ResultCode(-1);
}
} // namespace FileSys

View File

@@ -0,0 +1,31 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include <string>
#include "common/common_types.h"
#include "core/file_sys/filesystem.h"
#include "core/hle/result.h"
namespace FileSys {
/// File system interface to the SDCard archive
class SDMC_Factory final : public FileSystemFactory {
public:
explicit SDMC_Factory(std::string sd_directory);
std::string GetName() const override {
return "SDMC_Factory";
}
ResultVal<std::unique_ptr<FileSystemBackend>> Open(const Path& path) override;
ResultCode Format(const Path& path) override;
ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
private:
std::string sd_directory;
};
} // namespace FileSys

View File

@@ -693,7 +693,7 @@ static void ReadMemory() {
u64 len =
HexToLong(start_offset, static_cast<u64>((command_buffer + command_length) - start_offset));
LOG_DEBUG(Debug_GDBStub, "gdb: addr: %016llx len: %016llx\n", addr, len);
LOG_DEBUG(Debug_GDBStub, "gdb: addr: %016lx len: %016lx\n", addr, len);
if (len * 2 > sizeof(reply)) {
SendReply("E01");

View File

@@ -7,6 +7,7 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/kernel.h"
@@ -26,6 +27,32 @@ void SessionRequestHandler::ClientDisconnected(SharedPtr<ServerSession> server_s
boost::range::remove_erase(connected_sessions, server_session);
}
SharedPtr<Event> HLERequestContext::SleepClientThread(SharedPtr<Thread> thread,
const std::string& reason, u64 timeout,
WakeupCallback&& callback) {
// Put the client thread to sleep until the wait event is signaled or the timeout expires.
thread->wakeup_callback =
[context = *this, callback](ThreadWakeupReason reason, SharedPtr<Thread> thread,
SharedPtr<WaitObject> object, size_t index) mutable -> bool {
ASSERT(thread->status == THREADSTATUS_WAIT_HLE_EVENT);
callback(thread, context, reason);
context.WriteToOutgoingCommandBuffer(*thread);
return true;
};
auto event = Kernel::Event::Create(Kernel::ResetType::OneShot, "HLE Pause Event: " + reason);
thread->status = THREADSTATUS_WAIT_HLE_EVENT;
thread->wait_objects = {event};
event->AddWaitingThread(thread);
if (timeout > 0) {
thread->WakeAfterDelay(timeout);
}
return event;
}
HLERequestContext::HLERequestContext(SharedPtr<Kernel::ServerSession> server_session)
: server_session(std::move(server_session)) {
cmd_buf[0] = 0;
@@ -35,7 +62,7 @@ HLERequestContext::~HLERequestContext() = default;
void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
IPC::RequestParser rp(src_cmdbuf);
command_header = std::make_unique<IPC::CommandHeader>(rp.PopRaw<IPC::CommandHeader>());
command_header = std::make_shared<IPC::CommandHeader>(rp.PopRaw<IPC::CommandHeader>());
if (command_header->type == IPC::CommandType::Close) {
// Close does not populate the rest of the IPC header
@@ -45,7 +72,7 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
// If handle descriptor is present, add size of it
if (command_header->enable_handle_descriptor) {
handle_descriptor_header =
std::make_unique<IPC::HandleDescriptorHeader>(rp.PopRaw<IPC::HandleDescriptorHeader>());
std::make_shared<IPC::HandleDescriptorHeader>(rp.PopRaw<IPC::HandleDescriptorHeader>());
if (handle_descriptor_header->send_current_pid) {
rp.Skip(2, false);
}
@@ -88,7 +115,7 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
// All outgoing domain messages have the domain header, if only incoming has it
if (incoming || domain_message_header) {
domain_message_header =
std::make_unique<IPC::DomainMessageHeader>(rp.PopRaw<IPC::DomainMessageHeader>());
std::make_shared<IPC::DomainMessageHeader>(rp.PopRaw<IPC::DomainMessageHeader>());
} else {
if (Session()->IsDomain())
LOG_WARNING(IPC, "Domain request has no DomainMessageHeader!");
@@ -96,7 +123,7 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
}
data_payload_header =
std::make_unique<IPC::DataPayloadHeader>(rp.PopRaw<IPC::DataPayloadHeader>());
std::make_shared<IPC::DataPayloadHeader>(rp.PopRaw<IPC::DataPayloadHeader>());
data_payload_offset = rp.GetCurrentOffset();
@@ -159,8 +186,11 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(u32_le* src_cmdb
return RESULT_SUCCESS;
}
ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, Process& dst_process,
HandleTable& dst_table) {
ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> dst_cmdbuf;
Memory::ReadBlock(*thread.owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(),
dst_cmdbuf.size() * sizeof(u32));
// The header was already built in the internal command buffer. Attempt to parse it to verify
// the integrity and then copy it over to the target command buffer.
ParseCommandBuffer(cmd_buf.data(), false);
@@ -171,7 +201,7 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, P
if (domain_message_header)
size -= sizeof(IPC::DomainMessageHeader) / sizeof(u32);
std::copy_n(cmd_buf.begin(), size, dst_cmdbuf);
std::copy_n(cmd_buf.begin(), size, dst_cmdbuf.data());
if (command_header->enable_handle_descriptor) {
ASSERT_MSG(!move_objects.empty() || !copy_objects.empty(),
@@ -213,6 +243,11 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, P
dst_cmdbuf[domain_offset++] = static_cast<u32_le>(request_handlers.size());
}
}
// Copy the translated command buffer back into the thread's command buffer area.
Memory::WriteBlock(*thread.owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(),
dst_cmdbuf.size() * sizeof(u32));
return RESULT_SUCCESS;
}
@@ -234,7 +269,7 @@ std::vector<u8> HLERequestContext::ReadBuffer() const {
size_t HLERequestContext::WriteBuffer(const void* buffer, size_t size) const {
const bool is_buffer_b{BufferDescriptorB().size() && BufferDescriptorB()[0].Size()};
ASSERT_MSG(size <= GetWriteBufferSize(), "Size %d is too big", size);
ASSERT_MSG(size <= GetWriteBufferSize(), "Size %lx is too big", size);
if (is_buffer_b) {
Memory::WriteBlock(BufferDescriptorB()[0].Address(), buffer, size);

View File

@@ -6,6 +6,7 @@
#include <array>
#include <memory>
#include <string>
#include <vector>
#include <boost/container/small_vector.hpp>
#include "common/common_types.h"
@@ -13,6 +14,7 @@
#include "core/hle/ipc.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/server_session.h"
#include "core/hle/kernel/thread.h"
namespace Service {
class ServiceFrameworkBase;
@@ -24,6 +26,7 @@ class Domain;
class HandleTable;
class HLERequestContext;
class Process;
class Event;
/**
* Interface implemented by HLE Session handlers.
@@ -102,14 +105,31 @@ public:
return server_session;
}
using WakeupCallback = std::function<void(SharedPtr<Thread> thread, HLERequestContext& context,
ThreadWakeupReason reason)>;
/**
* Puts the specified guest thread to sleep until the returned event is signaled or until the
* specified timeout expires.
* @param thread Thread to be put to sleep.
* @param reason Reason for pausing the thread, to be used for debugging purposes.
* @param timeout Timeout in nanoseconds after which the thread will be awoken and the callback
* invoked with a Timeout reason.
* @param callback Callback to be invoked when the thread is resumed. This callback must write
* the entire command response once again, regardless of the state of it before this function
* was called.
* @returns Event that when signaled will resume the thread and call the callback function.
*/
SharedPtr<Event> SleepClientThread(SharedPtr<Thread> thread, const std::string& reason,
u64 timeout, WakeupCallback&& callback);
void ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming);
/// Populates this context with data from the requesting process/thread.
ResultCode PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf, Process& src_process,
HandleTable& src_table);
/// Writes data from this context back to the requesting process/thread.
ResultCode WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, Process& dst_process,
HandleTable& dst_table);
ResultCode WriteToOutgoingCommandBuffer(Thread& thread);
u32_le GetCommand() const {
return command;
@@ -139,7 +159,7 @@ public:
return buffer_c_desciptors;
}
const std::unique_ptr<IPC::DomainMessageHeader>& GetDomainMessageHeader() const {
const std::shared_ptr<IPC::DomainMessageHeader>& GetDomainMessageHeader() const {
return domain_message_header;
}
@@ -212,10 +232,10 @@ private:
boost::container::small_vector<SharedPtr<Object>, 8> copy_objects;
boost::container::small_vector<std::shared_ptr<SessionRequestHandler>, 8> domain_objects;
std::unique_ptr<IPC::CommandHeader> command_header;
std::unique_ptr<IPC::HandleDescriptorHeader> handle_descriptor_header;
std::unique_ptr<IPC::DataPayloadHeader> data_payload_header;
std::unique_ptr<IPC::DomainMessageHeader> domain_message_header;
std::shared_ptr<IPC::CommandHeader> command_header;
std::shared_ptr<IPC::HandleDescriptorHeader> handle_descriptor_header;
std::shared_ptr<IPC::DataPayloadHeader> data_payload_header;
std::shared_ptr<IPC::DomainMessageHeader> domain_message_header;
std::vector<IPC::BufferDescriptorX> buffer_x_desciptors;
std::vector<IPC::BufferDescriptorABW> buffer_a_desciptors;
std::vector<IPC::BufferDescriptorABW> buffer_b_desciptors;

View File

@@ -10,12 +10,12 @@ namespace Kernel {
ObjectAddressTable g_object_address_table;
void ObjectAddressTable::Insert(VAddr addr, SharedPtr<Object> obj) {
ASSERT_MSG(objects.find(addr) == objects.end(), "Object already exists with addr=0x%llx", addr);
ASSERT_MSG(objects.find(addr) == objects.end(), "Object already exists with addr=0x%lx", addr);
objects[addr] = obj;
}
void ObjectAddressTable::Close(VAddr addr) {
ASSERT_MSG(objects.find(addr) != objects.end(), "Object does not exist with addr=0x%llx", addr);
ASSERT_MSG(objects.find(addr) != objects.end(), "Object does not exist with addr=0x%lx", addr);
objects.erase(addr);
}

View File

@@ -78,7 +78,8 @@ ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& con
}
}
LOG_CRITICAL(IPC, "Unknown domain command=%d", domain_message_header->command.Value());
LOG_CRITICAL(IPC, "Unknown domain command=%d",
static_cast<int>(domain_message_header->command.Value()));
ASSERT(false);
}

View File

@@ -107,7 +107,7 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi
// Error out if the requested permissions don't match what the creator process allows.
if (static_cast<u32>(permissions) & ~static_cast<u32>(own_other_permissions)) {
LOG_ERROR(Kernel, "cannot map id=%u, address=0x%llx name=%s, permissions don't match",
LOG_ERROR(Kernel, "cannot map id=%u, address=0x%lx name=%s, permissions don't match",
GetObjectId(), address, name.c_str());
return ERR_INVALID_COMBINATION;
}
@@ -115,7 +115,7 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi
// Error out if the provided permissions are not compatible with what the creator process needs.
if (other_permissions != MemoryPermission::DontCare &&
static_cast<u32>(this->permissions) & ~static_cast<u32>(other_permissions)) {
LOG_ERROR(Kernel, "cannot map id=%u, address=0x%llx name=%s, permissions don't match",
LOG_ERROR(Kernel, "cannot map id=%u, address=0x%lx name=%s, permissions don't match",
GetObjectId(), address, name.c_str());
return ERR_WRONG_PERMISSION;
}
@@ -126,7 +126,7 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi
if (address != 0) {
// TODO(shinyquagsire23): Check for virtual/mappable memory here too?
if (address >= Memory::HEAP_VADDR && address < Memory::HEAP_VADDR_END) {
LOG_ERROR(Kernel, "cannot map id=%u, address=0x%llx name=%s, invalid address",
LOG_ERROR(Kernel, "cannot map id=%u, address=0x%lx name=%s, invalid address",
GetObjectId(), address, name.c_str());
return ERR_INVALID_ADDRESS;
}
@@ -143,10 +143,9 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi
auto result = target_process->vm_manager.MapMemoryBlock(
target_address, backing_block, backing_block_offset, size, MemoryState::Shared);
if (result.Failed()) {
LOG_ERROR(
Kernel,
"cannot map id=%u, target_address=0x%llx name=%s, error mapping to virtual memory",
GetObjectId(), target_address, name.c_str());
LOG_ERROR(Kernel,
"cannot map id=%u, target_address=0x%lx name=%s, error mapping to virtual memory",
GetObjectId(), target_address, name.c_str());
return result.Code();
}

View File

@@ -39,7 +39,7 @@ static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) {
}
static ResultCode SetMemoryAttribute(VAddr addr, u64 size, u32 state0, u32 state1) {
LOG_WARNING(Kernel_SVC, "(STUBBED) called, addr=0x%llx", addr);
LOG_WARNING(Kernel_SVC, "(STUBBED) called, addr=0x%lx", addr);
return RESULT_SUCCESS;
}
@@ -750,7 +750,7 @@ static ResultCode ResetSignal(Handle handle) {
/// Creates a TransferMemory object
static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32 permissions) {
LOG_WARNING(Kernel_SVC, "(STUBBED) called addr=0x%llx, size=0x%llx, perms=%08X", addr, size,
LOG_WARNING(Kernel_SVC, "(STUBBED) called addr=0x%lx, size=0x%lx, perms=%08X", addr, size,
permissions);
*handle = 0;
return RESULT_SUCCESS;

View File

@@ -55,16 +55,6 @@ inline static u32 const NewThreadId() {
Thread::Thread() {}
Thread::~Thread() {}
/**
* Check if the specified thread is waiting on the specified address to be arbitrated
* @param thread The thread to test
* @param wait_address The address to test against
* @return True if the thread is waiting, false otherwise
*/
static bool CheckWait_AddressArbiter(const Thread* thread, VAddr wait_address) {
return thread->status == THREADSTATUS_WAIT_ARB && wait_address == thread->wait_address;
}
void Thread::Stop() {
// Cancel any outstanding wakeup events for this thread
CoreTiming::UnscheduleEvent(ThreadWakeupEventType, callback_handle);
@@ -102,12 +92,6 @@ void WaitCurrentThread_Sleep() {
thread->status = THREADSTATUS_WAIT_SLEEP;
}
void WaitCurrentThread_ArbitrateAddress(VAddr wait_address) {
Thread* thread = GetCurrentThread();
thread->wait_address = wait_address;
thread->status = THREADSTATUS_WAIT_ARB;
}
void ExitCurrentThread() {
Thread* thread = GetCurrentThread();
thread->Stop();
@@ -129,7 +113,8 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) {
bool resume = true;
if (thread->status == THREADSTATUS_WAIT_SYNCH_ANY ||
thread->status == THREADSTATUS_WAIT_SYNCH_ALL || thread->status == THREADSTATUS_WAIT_ARB) {
thread->status == THREADSTATUS_WAIT_SYNCH_ALL ||
thread->status == THREADSTATUS_WAIT_HLE_EVENT) {
// Remove the thread from each of its waiting objects' waitlists
for (auto& object : thread->wait_objects)
@@ -163,7 +148,7 @@ void Thread::ResumeFromWait() {
switch (status) {
case THREADSTATUS_WAIT_SYNCH_ALL:
case THREADSTATUS_WAIT_SYNCH_ANY:
case THREADSTATUS_WAIT_ARB:
case THREADSTATUS_WAIT_HLE_EVENT:
case THREADSTATUS_WAIT_SLEEP:
case THREADSTATUS_WAIT_IPC:
break;

View File

@@ -38,7 +38,7 @@ enum ThreadProcessorId : s32 {
enum ThreadStatus {
THREADSTATUS_RUNNING, ///< Currently running
THREADSTATUS_READY, ///< Ready to run
THREADSTATUS_WAIT_ARB, ///< Waiting on an address arbiter
THREADSTATUS_WAIT_HLE_EVENT, ///< Waiting for hle event to finish
THREADSTATUS_WAIT_SLEEP, ///< Waiting due to a SleepThread SVC
THREADSTATUS_WAIT_IPC, ///< Waiting for the reply from an IPC request
THREADSTATUS_WAIT_SYNCH_ANY, ///< Waiting due to WaitSynch1 or WaitSynchN with wait_all = false

View File

@@ -39,7 +39,8 @@ SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() {
for (const auto& thread : waiting_threads) {
// The list of waiting threads must not contain threads that are not waiting to be awakened.
ASSERT_MSG(thread->status == THREADSTATUS_WAIT_SYNCH_ANY ||
thread->status == THREADSTATUS_WAIT_SYNCH_ALL,
thread->status == THREADSTATUS_WAIT_SYNCH_ALL ||
thread->status == THREADSTATUS_WAIT_HLE_EVENT,
"Inconsistent thread statuses in waiting_threads");
if (thread->current_priority >= candidate_priority)

View File

@@ -0,0 +1,38 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/fatal/fatal.h"
#include "core/hle/service/fatal/fatal_p.h"
#include "core/hle/service/fatal/fatal_u.h"
namespace Service {
namespace Fatal {
Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
: ServiceFramework(name), module(std::move(module)) {}
void Module::Interface::FatalSimple(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
u32 error_code = rp.Pop<u32>();
LOG_WARNING(Service_Fatal, "(STUBBED) called, error_code=0x%X", error_code);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void Module::Interface::TransitionToFatalError(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_Fatal, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void InstallInterfaces(SM::ServiceManager& service_manager) {
auto module = std::make_shared<Module>();
std::make_shared<Fatal_P>(module)->InstallAsService(service_manager);
std::make_shared<Fatal_U>(module)->InstallAsService(service_manager);
}
} // namespace Fatal
} // namespace Service

View File

@@ -0,0 +1,29 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/service.h"
namespace Service {
namespace Fatal {
class Module final {
public:
class Interface : public ServiceFramework<Interface> {
public:
Interface(std::shared_ptr<Module> module, const char* name);
void FatalSimple(Kernel::HLERequestContext& ctx);
void TransitionToFatalError(Kernel::HLERequestContext& ctx);
protected:
std::shared_ptr<Module> module;
};
};
void InstallInterfaces(SM::ServiceManager& service_manager);
} // namespace Fatal
} // namespace Service

View File

@@ -0,0 +1,14 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/service/fatal/fatal_p.h"
namespace Service {
namespace Fatal {
Fatal_P::Fatal_P(std::shared_ptr<Module> module)
: Module::Interface(std::move(module), "fatal:p") {}
} // namespace Fatal
} // namespace Service

View File

@@ -0,0 +1,18 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/fatal/fatal.h"
namespace Service {
namespace Fatal {
class Fatal_P final : public Module::Interface {
public:
explicit Fatal_P(std::shared_ptr<Module> module);
};
} // namespace Fatal
} // namespace Service

View File

@@ -0,0 +1,19 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/service/fatal/fatal_u.h"
namespace Service {
namespace Fatal {
Fatal_U::Fatal_U(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "fatal:u") {
static const FunctionInfo functions[] = {
{1, &Fatal_U::FatalSimple, "FatalSimple"},
{2, &Fatal_U::TransitionToFatalError, "TransitionToFatalError"},
};
RegisterHandlers(functions);
}
} // namespace Fatal
} // namespace Service

View File

@@ -0,0 +1,18 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/fatal/fatal.h"
namespace Service {
namespace Fatal {
class Fatal_U final : public Module::Interface {
public:
explicit Fatal_U(std::shared_ptr<Module> module);
};
} // namespace Fatal
} // namespace Service

View File

@@ -6,6 +6,7 @@
#include "common/file_util.h"
#include "core/file_sys/filesystem.h"
#include "core/file_sys/savedata_factory.h"
#include "core/file_sys/sdmc_factory.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/filesystem/fsp_srv.h"
@@ -60,9 +61,13 @@ void RegisterFileSystems() {
filesystem_map.clear();
std::string nand_directory = FileUtil::GetUserPath(D_NAND_IDX);
std::string sd_directory = FileUtil::GetUserPath(D_SDMC_IDX);
auto savedata = std::make_unique<FileSys::SaveData_Factory>(std::move(nand_directory));
RegisterFileSystem(std::move(savedata), Type::SaveData);
auto sdcard = std::make_unique<FileSys::SDMC_Factory>(std::move(sd_directory));
RegisterFileSystem(std::move(sdcard), Type::SDMC);
}
void InstallInterfaces(SM::ServiceManager& service_manager) {

View File

@@ -26,6 +26,7 @@ namespace FileSystem {
enum class Type {
RomFS = 1,
SaveData = 2,
SDMC = 3,
};
/**

View File

@@ -5,6 +5,7 @@
#include <cinttypes>
#include "common/logging/log.h"
#include "core/core.h"
#include "core/file_sys/directory.h"
#include "core/file_sys/filesystem.h"
#include "core/file_sys/storage.h"
#include "core/hle/ipc_helpers.h"
@@ -35,7 +36,7 @@ private:
const s64 offset = rp.Pop<s64>();
const s64 length = rp.Pop<s64>();
LOG_DEBUG(Service_FS, "called, offset=0x%llx, length=0x%llx", offset, length);
LOG_DEBUG(Service_FS, "called, offset=0x%ld, length=0x%ld", offset, length);
// Error checking
if (length < 0) {
@@ -86,7 +87,7 @@ private:
const s64 offset = rp.Pop<s64>();
const s64 length = rp.Pop<s64>();
LOG_DEBUG(Service_FS, "called, offset=0x%llx, length=0x%llx", offset, length);
LOG_DEBUG(Service_FS, "called, offset=0x%ld, length=0x%ld", offset, length);
// Error checking
if (length < 0) {
@@ -123,7 +124,7 @@ private:
const s64 offset = rp.Pop<s64>();
const s64 length = rp.Pop<s64>();
LOG_DEBUG(Service_FS, "called, offset=0x%llx, length=0x%llx", offset, length);
LOG_DEBUG(Service_FS, "called, offset=0x%ld, length=0x%ld", offset, length);
// Error checking
if (length < 0) {
@@ -151,14 +152,66 @@ private:
}
};
class IDirectory final : public ServiceFramework<IDirectory> {
public:
explicit IDirectory(std::unique_ptr<FileSys::DirectoryBackend>&& backend)
: ServiceFramework("IDirectory"), backend(std::move(backend)) {
static const FunctionInfo functions[] = {
{0, &IDirectory::Read, "Read"},
{1, &IDirectory::GetEntryCount, "GetEntryCount"},
};
RegisterHandlers(functions);
}
private:
std::unique_ptr<FileSys::DirectoryBackend> backend;
void Read(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u64 unk = rp.Pop<u64>();
LOG_DEBUG(Service_FS, "called, unk=0x%llx", unk);
// Calculate how many entries we can fit in the output buffer
u64 count_entries = ctx.GetWriteBufferSize() / sizeof(FileSys::Entry);
// Read the data from the Directory backend
std::vector<FileSys::Entry> entries(count_entries);
u64 read_entries = backend->Read(count_entries, entries.data());
// Convert the data into a byte array
std::vector<u8> output(entries.size() * sizeof(FileSys::Entry));
std::memcpy(output.data(), entries.data(), output.size());
// Write the data to memory
ctx.WriteBuffer(output);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push(read_entries);
}
void GetEntryCount(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_FS, "called");
u64 count = backend->GetEntryCount();
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push(count);
}
};
class IFileSystem final : public ServiceFramework<IFileSystem> {
public:
explicit IFileSystem(std::unique_ptr<FileSys::FileSystemBackend>&& backend)
: ServiceFramework("IFileSystem"), backend(std::move(backend)) {
static const FunctionInfo functions[] = {
{0, &IFileSystem::CreateFile, "CreateFile"},
{2, &IFileSystem::CreateDirectory, "CreateDirectory"},
{7, &IFileSystem::GetEntryType, "GetEntryType"},
{8, &IFileSystem::OpenFile, "OpenFile"},
{9, &IFileSystem::OpenDirectory, "OpenDirectory"},
{10, &IFileSystem::Commit, "Commit"},
};
RegisterHandlers(functions);
@@ -182,6 +235,20 @@ public:
rb.Push(backend->CreateFile(name, size));
}
void CreateDirectory(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
auto file_buffer = ctx.ReadBuffer();
auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0');
std::string name(file_buffer.begin(), end);
LOG_DEBUG(Service_FS, "called directory %s", name.c_str());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(backend->CreateDirectory(name));
}
void OpenFile(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
@@ -208,6 +275,33 @@ public:
rb.PushIpcInterface<IFile>(std::move(file));
}
void OpenDirectory(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
auto file_buffer = ctx.ReadBuffer();
auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0');
std::string name(file_buffer.begin(), end);
// TODO(Subv): Implement this filter.
u32 filter_flags = rp.Pop<u32>();
LOG_DEBUG(Service_FS, "called directory %s filter %u", name.c_str(), filter_flags);
auto result = backend->OpenDirectory(name);
if (result.Failed()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result.Code());
return;
}
auto directory = std::move(result.Unwrap());
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IDirectory>(std::move(directory));
}
void GetEntryType(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
@@ -274,10 +368,14 @@ void FSP_SRV::Initalize(Kernel::HLERequestContext& ctx) {
}
void FSP_SRV::MountSdCard(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_FS, "(STUBBED) called");
LOG_DEBUG(Service_FS, "called");
IPC::ResponseBuilder rb{ctx, 2};
FileSys::Path unused;
auto filesystem = OpenFileSystem(Type::SDMC, unused).Unwrap();
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IFileSystem>(std::move(filesystem));
}
void FSP_SRV::CreateSaveData(Kernel::HLERequestContext& ctx) {

View File

@@ -23,17 +23,16 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u3
u32 stride, NVFlinger::BufferQueue::BufferTransformFlags transform) {
VAddr addr = nvmap_dev->GetObjectAddress(buffer_handle);
LOG_WARNING(Service,
"Drawing from address %llx offset %08X Width %u Height %u Stride %u Format %u",
addr, offset, width, height, stride, format);
"Drawing from address %lx offset %08X Width %u Height %u Stride %u Format %u", addr,
offset, width, height, stride, format);
using PixelFormat = RendererBase::FramebufferInfo::PixelFormat;
using Flags = NVFlinger::BufferQueue::BufferTransformFlags;
const bool flip_vertical = static_cast<u32>(transform) & static_cast<u32>(Flags::FlipV);
const RendererBase::FramebufferInfo framebuffer_info{
addr, offset, width, height, stride, static_cast<PixelFormat>(format), flip_vertical};
using PixelFormat = Tegra::FramebufferConfig::PixelFormat;
const Tegra::FramebufferConfig framebuffer{
addr, offset, width, height, stride, static_cast<PixelFormat>(format), transform};
Core::System::GetInstance().perf_stats.EndGameFrame();
VideoCore::g_renderer->SwapBuffers(framebuffer_info);
VideoCore::g_renderer->SwapBuffers(framebuffer);
}
} // namespace Devices

View File

@@ -26,24 +26,30 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, IGBPBuffer& igbp_buffer) {
LOG_WARNING(Service, "Adding graphics buffer %u", slot);
queue.emplace_back(buffer);
if (buffer_wait_event) {
buffer_wait_event->Signal();
}
}
u32 BufferQueue::DequeueBuffer(u32 pixel_format, u32 width, u32 height) {
boost::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) {
auto itr = std::find_if(queue.begin(), queue.end(), [&](const Buffer& buffer) {
// Only consider free buffers. Buffers become free once again after they've been Acquired
// and Released by the compositor, see the NVFlinger::Compose method.
if (buffer.status != Buffer::Status::Free)
if (buffer.status != Buffer::Status::Free) {
return false;
}
// Make sure that the parameters match.
return buffer.igbp_buffer.width == width && buffer.igbp_buffer.height == height;
});
if (itr == queue.end()) {
LOG_CRITICAL(Service_NVDRV, "no free buffers for pixel_format=%d, width=%d, height=%d",
pixel_format, width, height);
itr = queue.begin();
return boost::none;
}
buffer_wait_event = nullptr;
itr->status = Buffer::Status::Dequeued;
return itr->slot;
}
@@ -81,6 +87,10 @@ void BufferQueue::ReleaseBuffer(u32 slot) {
ASSERT(itr != queue.end());
ASSERT(itr->status == Buffer::Status::Acquired);
itr->status = Buffer::Status::Free;
if (buffer_wait_event) {
buffer_wait_event->Signal();
}
}
u32 BufferQueue::Query(QueryType type) {
@@ -96,5 +106,10 @@ u32 BufferQueue::Query(QueryType type) {
return 0;
}
void BufferQueue::SetBufferWaitEvent(Kernel::SharedPtr<Kernel::Event>&& wait_event) {
ASSERT_MSG(!buffer_wait_event, "buffer_wait_event only supports a single waiting thread!");
buffer_wait_event = std::move(wait_event);
}
} // namespace NVFlinger
} // namespace Service

View File

@@ -47,6 +47,8 @@ public:
~BufferQueue() = default;
enum class BufferTransformFlags : u32 {
/// No transform flags are set
Unset = 0x00,
/// Flip source image horizontally (around the vertical axis)
FlipH = 0x01,
/// Flip source image vertically (around the horizontal axis)
@@ -69,12 +71,13 @@ public:
};
void SetPreallocatedBuffer(u32 slot, IGBPBuffer& buffer);
u32 DequeueBuffer(u32 pixel_format, u32 width, u32 height);
boost::optional<u32> DequeueBuffer(u32 width, u32 height);
const IGBPBuffer& RequestBuffer(u32 slot) const;
void QueueBuffer(u32 slot, BufferTransformFlags transform);
boost::optional<const Buffer&> AcquireBuffer();
void ReleaseBuffer(u32 slot);
u32 Query(QueryType type);
void SetBufferWaitEvent(Kernel::SharedPtr<Kernel::Event>&& wait_event);
u32 GetId() const {
return id;
@@ -90,6 +93,9 @@ private:
std::vector<Buffer> queue;
Kernel::SharedPtr<Kernel::Event> native_handle;
/// Used to signal waiting thread when no buffers are available
Kernel::SharedPtr<Kernel::Event> buffer_wait_event;
};
} // namespace NVFlinger

View File

@@ -20,6 +20,7 @@
#include "core/hle/service/aoc/aoc_u.h"
#include "core/hle/service/apm/apm.h"
#include "core/hle/service/audio/audio.h"
#include "core/hle/service/fatal/fatal.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/friend/friend.h"
#include "core/hle/service/hid/hid.h"
@@ -33,6 +34,8 @@
#include "core/hle/service/sm/controller.h"
#include "core/hle/service/sm/sm.h"
#include "core/hle/service/sockets/sockets.h"
#include "core/hle/service/spl/module.h"
#include "core/hle/service/ssl/ssl.h"
#include "core/hle/service/time/time.h"
#include "core/hle/service/vi/vi.h"
@@ -149,11 +152,10 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co
break;
}
default:
UNIMPLEMENTED_MSG("command_type=%d", context.GetCommandType());
UNIMPLEMENTED_MSG("command_type=%d", static_cast<int>(context.GetCommandType()));
}
u32* cmd_buf = (u32*)Memory::GetPointer(Kernel::GetCurrentThread()->GetTLSAddress());
context.WriteToOutgoingCommandBuffer(cmd_buf, *Core::CurrentProcess(), Kernel::g_handle_table);
context.WriteToOutgoingCommandBuffer(*Kernel::GetCurrentThread());
return RESULT_SUCCESS;
}
@@ -180,6 +182,7 @@ void Init() {
AOC::InstallInterfaces(*SM::g_service_manager);
APM::InstallInterfaces(*SM::g_service_manager);
Audio::InstallInterfaces(*SM::g_service_manager);
Fatal::InstallInterfaces(*SM::g_service_manager);
FileSystem::InstallInterfaces(*SM::g_service_manager);
Friend::InstallInterfaces(*SM::g_service_manager);
HID::InstallInterfaces(*SM::g_service_manager);
@@ -189,6 +192,8 @@ void Init() {
Nvidia::InstallInterfaces(*SM::g_service_manager);
PCTL::InstallInterfaces(*SM::g_service_manager);
Sockets::InstallInterfaces(*SM::g_service_manager);
SPL::InstallInterfaces(*SM::g_service_manager);
SSL::InstallInterfaces(*SM::g_service_manager);
Time::InstallInterfaces(*SM::g_service_manager);
VI::InstallInterfaces(*SM::g_service_manager, nv_flinger);
Set::InstallInterfaces(*SM::g_service_manager);

View File

@@ -0,0 +1,18 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/service/spl/csrng.h"
namespace Service {
namespace SPL {
CSRNG::CSRNG(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "csrng") {
static const FunctionInfo functions[] = {
{0, &CSRNG::GetRandomBytes, "GetRandomBytes"},
};
RegisterHandlers(functions);
}
} // namespace SPL
} // namespace Service

View File

@@ -0,0 +1,18 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/spl/module.h"
namespace Service {
namespace SPL {
class CSRNG final : public Module::Interface {
public:
explicit CSRNG(std::shared_ptr<Module> module);
};
} // namespace SPL
} // namespace Service

View File

@@ -0,0 +1,42 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <cstdlib>
#include <vector>
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/spl/csrng.h"
#include "core/hle/service/spl/module.h"
#include "core/hle/service/spl/spl.h"
namespace Service {
namespace SPL {
Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
: ServiceFramework(name), module(std::move(module)) {}
void Module::Interface::GetRandomBytes(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
size_t size = ctx.GetWriteBufferSize();
std::vector<u8> data(size);
std::generate(data.begin(), data.end(), std::rand);
ctx.WriteBuffer(data);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_DEBUG(Service_SPL, "called");
}
void InstallInterfaces(SM::ServiceManager& service_manager) {
auto module = std::make_shared<Module>();
std::make_shared<CSRNG>(module)->InstallAsService(service_manager);
std::make_shared<SPL>(module)->InstallAsService(service_manager);
}
} // namespace SPL
} // namespace Service

View File

@@ -0,0 +1,29 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/service.h"
namespace Service {
namespace SPL {
class Module final {
public:
class Interface : public ServiceFramework<Interface> {
public:
Interface(std::shared_ptr<Module> module, const char* name);
void GetRandomBytes(Kernel::HLERequestContext& ctx);
protected:
std::shared_ptr<Module> module;
};
};
/// Registers all SPL services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& service_manager);
} // namespace SPL
} // namespace Service

View File

@@ -0,0 +1,41 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/service/spl/spl.h"
namespace Service {
namespace SPL {
SPL::SPL(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "spl:") {
static const FunctionInfo functions[] = {
{0, nullptr, "GetConfig"},
{1, nullptr, "UserExpMod"},
{2, nullptr, "GenerateAesKek"},
{3, nullptr, "LoadAesKey"},
{4, nullptr, "GenerateAesKey"},
{5, nullptr, "SetConfig"},
{7, &SPL::GetRandomBytes, "GetRandomBytes"},
{9, nullptr, "LoadSecureExpModKey"},
{10, nullptr, "SecureExpMod"},
{11, nullptr, "IsDevelopment"},
{12, nullptr, "GenerateSpecificAesKey"},
{13, nullptr, "DecryptPrivk"},
{14, nullptr, "DecryptAesKey"},
{15, nullptr, "DecryptAesCtr"},
{16, nullptr, "ComputeCmac"},
{17, nullptr, "LoadRsaOaepKey"},
{18, nullptr, "UnwrapRsaOaepWrappedTitleKey"},
{19, nullptr, "LoadTitleKey"},
{20, nullptr, "UnwrapAesWrappedTitleKey"},
{21, nullptr, "LockAesEngine"},
{22, nullptr, "UnlockAesEngine"},
{23, nullptr, "GetSplWaitEvent"},
{24, nullptr, "SetSharedData"},
{25, nullptr, "GetSharedData"},
};
RegisterHandlers(functions);
}
} // namespace SPL
} // namespace Service

View File

@@ -0,0 +1,18 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/spl/module.h"
namespace Service {
namespace SPL {
class SPL final : public Module::Interface {
public:
explicit SPL(std::shared_ptr<Module> module);
};
} // namespace SPL
} // namespace Service

View File

@@ -0,0 +1,17 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/service/ssl/ssl.h"
namespace Service {
namespace SSL {
SSL::SSL() : ServiceFramework("ssl") {}
void InstallInterfaces(SM::ServiceManager& service_manager) {
std::make_shared<SSL>()->InstallAsService(service_manager);
}
} // namespace SSL
} // namespace Service

View File

@@ -0,0 +1,22 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/service.h"
namespace Service {
namespace SSL {
class SSL final : public ServiceFramework<SSL> {
public:
explicit SSL();
~SSL() = default;
};
/// Registers all SSL services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& service_manager);
} // namespace SSL
} // namespace Service

View File

@@ -107,7 +107,7 @@ private:
IPC::RequestParser rp{ctx};
u64 posix_time = rp.Pop<u64>();
LOG_WARNING(Service_Time, "(STUBBED) called, posix_time=0x%016llX", posix_time);
LOG_WARNING(Service_Time, "(STUBBED) called, posix_time=0x%016lX", posix_time);
CalendarTime calendar_time{2018, 1, 1, 0, 0, 0};
CalendarAdditionalInfo additional_info{};

View File

@@ -4,10 +4,13 @@
#include <algorithm>
#include <array>
#include <memory>
#include <boost/optional.hpp>
#include "common/alignment.h"
#include "common/scope_exit.h"
#include "core/core_timing.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/event.h"
#include "core/hle/service/nvdrv/nvdrv.h"
#include "core/hle/service/nvflinger/buffer_queue.h"
#include "core/hle/service/vi/vi.h"
@@ -471,7 +474,7 @@ private:
u32 flags = rp.Pop<u32>();
auto buffer_queue = nv_flinger->GetBufferQueue(id);
LOG_DEBUG(Service_VI, "called, transaction=%x", transaction);
LOG_DEBUG(Service_VI, "called, transaction=%x", static_cast<u32>(transaction));
if (transaction == TransactionId::Connect) {
IGBPConnectRequestParcel request{ctx.ReadBuffer()};
@@ -486,12 +489,30 @@ private:
ctx.WriteBuffer(response.Serialize());
} else if (transaction == TransactionId::DequeueBuffer) {
IGBPDequeueBufferRequestParcel request{ctx.ReadBuffer()};
const u32 width{request.data.width};
const u32 height{request.data.height};
boost::optional<u32> slot = buffer_queue->DequeueBuffer(width, height);
u32 slot = buffer_queue->DequeueBuffer(request.data.pixel_format, request.data.width,
request.data.height);
IGBPDequeueBufferResponseParcel response{slot};
ctx.WriteBuffer(response.Serialize());
if (slot != boost::none) {
// Buffer is available
IGBPDequeueBufferResponseParcel response{*slot};
ctx.WriteBuffer(response.Serialize());
} else {
// Wait the current thread until a buffer becomes available
auto wait_event = ctx.SleepClientThread(
Kernel::GetCurrentThread(), "IHOSBinderDriver::DequeueBuffer", -1,
[=](Kernel::SharedPtr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx,
ThreadWakeupReason reason) {
// Repeat TransactParcel DequeueBuffer when a buffer is available
auto buffer_queue = nv_flinger->GetBufferQueue(id);
boost::optional<u32> slot = buffer_queue->DequeueBuffer(width, height);
IGBPDequeueBufferResponseParcel response{*slot};
ctx.WriteBuffer(response.Serialize());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
});
buffer_queue->SetBufferWaitEvent(std::move(wait_event));
}
} else if (transaction == TransactionId::RequestBuffer) {
IGBPRequestBufferRequestParcel request{ctx.ReadBuffer()};
@@ -628,144 +649,152 @@ private:
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
};
void IApplicationDisplayService::GetRelayService(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_VI, "(STUBBED) called");
class IApplicationDisplayService final : public ServiceFramework<IApplicationDisplayService> {
public:
IApplicationDisplayService(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
~IApplicationDisplayService() = default;
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IHOSBinderDriver>(nv_flinger);
}
private:
void GetRelayService(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_VI, "(STUBBED) called");
void IApplicationDisplayService::GetSystemDisplayService(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_VI, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IHOSBinderDriver>(nv_flinger);
}
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ISystemDisplayService>();
}
void GetSystemDisplayService(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_VI, "(STUBBED) called");
void IApplicationDisplayService::GetManagerDisplayService(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_VI, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ISystemDisplayService>();
}
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IManagerDisplayService>(nv_flinger);
}
void GetManagerDisplayService(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_VI, "(STUBBED) called");
void IApplicationDisplayService::GetIndirectDisplayTransactionService(
Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_VI, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IManagerDisplayService>(nv_flinger);
}
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IHOSBinderDriver>(nv_flinger);
}
void GetIndirectDisplayTransactionService(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_VI, "(STUBBED) called");
void IApplicationDisplayService::OpenDisplay(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_VI, "(STUBBED) called");
IPC::RequestParser rp{ctx};
auto name_buf = rp.PopRaw<std::array<u8, 0x40>>();
auto end = std::find(name_buf.begin(), name_buf.end(), '\0');
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IHOSBinderDriver>(nv_flinger);
}
std::string name(name_buf.begin(), end);
void OpenDisplay(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_VI, "(STUBBED) called");
IPC::RequestParser rp{ctx};
auto name_buf = rp.PopRaw<std::array<u8, 0x40>>();
auto end = std::find(name_buf.begin(), name_buf.end(), '\0');
ASSERT_MSG(name == "Default", "Non-default displays aren't supported yet");
std::string name(name_buf.begin(), end);
IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0);
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(nv_flinger->OpenDisplay(name));
}
ASSERT_MSG(name == "Default", "Non-default displays aren't supported yet");
void IApplicationDisplayService::CloseDisplay(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_VI, "(STUBBED) called");
IPC::RequestParser rp{ctx};
u64 display_id = rp.Pop<u64>();
IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0);
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(nv_flinger->OpenDisplay(name));
}
IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0);
rb.Push(RESULT_SUCCESS);
}
void CloseDisplay(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_VI, "(STUBBED) called");
IPC::RequestParser rp{ctx};
u64 display_id = rp.Pop<u64>();
void IApplicationDisplayService::OpenLayer(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_VI, "called");
IPC::RequestParser rp{ctx};
auto name_buf = rp.PopRaw<std::array<u8, 0x40>>();
auto end = std::find(name_buf.begin(), name_buf.end(), '\0');
IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0);
rb.Push(RESULT_SUCCESS);
}
std::string display_name(name_buf.begin(), end);
void SetLayerScalingMode(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_VI, "(STUBBED) called");
IPC::RequestParser rp{ctx};
u32 scaling_mode = rp.Pop<u32>();
u64 unknown = rp.Pop<u64>();
u64 layer_id = rp.Pop<u64>();
u64 aruid = rp.Pop<u64>();
IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0);
rb.Push(RESULT_SUCCESS);
}
u64 display_id = nv_flinger->OpenDisplay(display_name);
u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id);
void ListDisplays(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
DisplayInfo display_info;
ctx.WriteBuffer(&display_info, sizeof(DisplayInfo));
IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0);
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(1);
LOG_WARNING(Service_VI, "(STUBBED) called");
}
NativeWindow native_window{buffer_queue_id};
IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0);
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(ctx.WriteBuffer(native_window.Serialize()));
}
void OpenLayer(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_VI, "called");
IPC::RequestParser rp{ctx};
auto name_buf = rp.PopRaw<std::array<u8, 0x40>>();
auto end = std::find(name_buf.begin(), name_buf.end(), '\0');
void IApplicationDisplayService::CreateStrayLayer(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_VI, "called");
std::string display_name(name_buf.begin(), end);
IPC::RequestParser rp{ctx};
u32 flags = rp.Pop<u32>();
rp.Pop<u32>(); // padding
u64 display_id = rp.Pop<u64>();
u64 layer_id = rp.Pop<u64>();
u64 aruid = rp.Pop<u64>();
// TODO(Subv): What's the difference between a Stray and a Managed layer?
u64 display_id = nv_flinger->OpenDisplay(display_name);
u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id);
u64 layer_id = nv_flinger->CreateLayer(display_id);
u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id);
NativeWindow native_window{buffer_queue_id};
IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0);
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(ctx.WriteBuffer(native_window.Serialize()));
}
NativeWindow native_window{buffer_queue_id};
IPC::ResponseBuilder rb = rp.MakeBuilder(6, 0, 0);
rb.Push(RESULT_SUCCESS);
rb.Push(layer_id);
rb.Push<u64>(ctx.WriteBuffer(native_window.Serialize()));
}
void CreateStrayLayer(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_VI, "called");
void IApplicationDisplayService::DestroyStrayLayer(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_VI, "(STUBBED) called");
IPC::RequestParser rp{ctx};
u32 flags = rp.Pop<u32>();
rp.Pop<u32>(); // padding
u64 display_id = rp.Pop<u64>();
IPC::RequestParser rp{ctx};
u64 layer_id = rp.Pop<u64>();
// TODO(Subv): What's the difference between a Stray and a Managed layer?
IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0);
rb.Push(RESULT_SUCCESS);
}
u64 layer_id = nv_flinger->CreateLayer(display_id);
u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id);
void IApplicationDisplayService::SetLayerScalingMode(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_VI, "(STUBBED) called");
IPC::RequestParser rp{ctx};
u32 scaling_mode = rp.Pop<u32>();
u64 unknown = rp.Pop<u64>();
NativeWindow native_window{buffer_queue_id};
IPC::ResponseBuilder rb = rp.MakeBuilder(6, 0, 0);
rb.Push(RESULT_SUCCESS);
rb.Push(layer_id);
rb.Push<u64>(ctx.WriteBuffer(native_window.Serialize()));
}
IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0);
rb.Push(RESULT_SUCCESS);
}
void DestroyStrayLayer(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_VI, "(STUBBED) called");
void IApplicationDisplayService::ListDisplays(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
DisplayInfo display_info;
ctx.WriteBuffer(&display_info, sizeof(DisplayInfo));
IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0);
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(1);
LOG_WARNING(Service_VI, "(STUBBED) called");
}
IPC::RequestParser rp{ctx};
u64 layer_id = rp.Pop<u64>();
void IApplicationDisplayService::GetDisplayVsyncEvent(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_VI, "(STUBBED) called");
IPC::RequestParser rp{ctx};
u64 display_id = rp.Pop<u64>();
IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0);
rb.Push(RESULT_SUCCESS);
}
auto vsync_event = nv_flinger->GetVsyncEvent(display_id);
void GetDisplayVsyncEvent(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_VI, "(STUBBED) called");
IPC::RequestParser rp{ctx};
u64 display_id = rp.Pop<u64>();
IPC::ResponseBuilder rb = rp.MakeBuilder(2, 1, 0);
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(vsync_event);
}
auto vsync_event = nv_flinger->GetVsyncEvent(display_id);
IPC::ResponseBuilder rb = rp.MakeBuilder(2, 1, 0);
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(vsync_event);
}
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
};
IApplicationDisplayService::IApplicationDisplayService(
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
@@ -788,11 +817,24 @@ IApplicationDisplayService::IApplicationDisplayService(
RegisterHandlers(functions);
}
Module::Interface::Interface(std::shared_ptr<Module> module, const char* name,
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
: ServiceFramework(name), module(std::move(module)), nv_flinger(std::move(nv_flinger)) {}
void Module::Interface::GetDisplayService(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_VI, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IApplicationDisplayService>(nv_flinger);
}
void InstallInterfaces(SM::ServiceManager& service_manager,
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) {
std::make_shared<VI_M>(nv_flinger)->InstallAsService(service_manager);
std::make_shared<VI_S>(nv_flinger)->InstallAsService(service_manager);
std::make_shared<VI_U>(nv_flinger)->InstallAsService(service_manager);
auto module = std::make_shared<Module>();
std::make_shared<VI_M>(module, nv_flinger)->InstallAsService(service_manager);
std::make_shared<VI_S>(module, nv_flinger)->InstallAsService(service_manager);
std::make_shared<VI_U>(module, nv_flinger)->InstallAsService(service_manager);
}
} // namespace VI

View File

@@ -4,9 +4,6 @@
#pragma once
#include <memory>
#include <boost/optional.hpp>
#include "core/hle/kernel/event.h"
#include "core/hle/service/nvflinger/nvflinger.h"
#include "core/hle/service/service.h"
@@ -17,26 +14,19 @@ struct EventType;
namespace Service {
namespace VI {
class IApplicationDisplayService final : public ServiceFramework<IApplicationDisplayService> {
class Module final {
public:
IApplicationDisplayService(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
~IApplicationDisplayService() = default;
class Interface : public ServiceFramework<Interface> {
public:
Interface(std::shared_ptr<Module> module, const char* name,
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
private:
void GetRelayService(Kernel::HLERequestContext& ctx);
void GetSystemDisplayService(Kernel::HLERequestContext& ctx);
void GetManagerDisplayService(Kernel::HLERequestContext& ctx);
void GetIndirectDisplayTransactionService(Kernel::HLERequestContext& ctx);
void OpenDisplay(Kernel::HLERequestContext& ctx);
void CloseDisplay(Kernel::HLERequestContext& ctx);
void SetLayerScalingMode(Kernel::HLERequestContext& ctx);
void ListDisplays(Kernel::HLERequestContext& ctx);
void OpenLayer(Kernel::HLERequestContext& ctx);
void CreateStrayLayer(Kernel::HLERequestContext& ctx);
void DestroyStrayLayer(Kernel::HLERequestContext& ctx);
void GetDisplayVsyncEvent(Kernel::HLERequestContext& ctx);
void GetDisplayService(Kernel::HLERequestContext& ctx);
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
protected:
std::shared_ptr<Module> module;
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
};
};
/// Registers all VI services with the specified service manager.

View File

@@ -2,24 +2,13 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/vi/vi.h"
#include "core/hle/service/vi/vi_m.h"
namespace Service {
namespace VI {
void VI_M::GetDisplayService(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_VI, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IApplicationDisplayService>(nv_flinger);
}
VI_M::VI_M(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
: ServiceFramework("vi:m"), nv_flinger(std::move(nv_flinger)) {
VI_M::VI_M(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
: Module::Interface(std::move(module), "vi:m", std::move(nv_flinger)) {
static const FunctionInfo functions[] = {
{2, &VI_M::GetDisplayService, "GetDisplayService"},
{3, nullptr, "GetDisplayServiceWithProxyNameExchange"},

View File

@@ -4,25 +4,14 @@
#pragma once
#include <memory>
#include "core/hle/service/service.h"
#include "core/hle/service/vi/vi.h"
namespace Service {
namespace NVFlinger {
class NVFlinger;
}
namespace VI {
class VI_M final : public ServiceFramework<VI_M> {
class VI_M final : public Module::Interface {
public:
VI_M(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
~VI_M() = default;
private:
void GetDisplayService(Kernel::HLERequestContext& ctx);
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
explicit VI_M(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
};
} // namespace VI

View File

@@ -2,24 +2,13 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/vi/vi.h"
#include "core/hle/service/vi/vi_s.h"
namespace Service {
namespace VI {
void VI_S::GetDisplayService(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_VI, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IApplicationDisplayService>(nv_flinger);
}
VI_S::VI_S(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
: ServiceFramework("vi:s"), nv_flinger(std::move(nv_flinger)) {
VI_S::VI_S(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
: Module::Interface(std::move(module), "vi:s", std::move(nv_flinger)) {
static const FunctionInfo functions[] = {
{1, &VI_S::GetDisplayService, "GetDisplayService"},
{3, nullptr, "GetDisplayServiceWithProxyNameExchange"},

View File

@@ -4,25 +4,14 @@
#pragma once
#include <memory>
#include "core/hle/service/service.h"
#include "core/hle/service/vi/vi.h"
namespace Service {
namespace NVFlinger {
class NVFlinger;
}
namespace VI {
class VI_S final : public ServiceFramework<VI_S> {
class VI_S final : public Module::Interface {
public:
VI_S(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
~VI_S() = default;
private:
void GetDisplayService(Kernel::HLERequestContext& ctx);
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
explicit VI_S(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
};
} // namespace VI

View File

@@ -2,24 +2,13 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/vi/vi.h"
#include "core/hle/service/vi/vi_u.h"
namespace Service {
namespace VI {
void VI_U::GetDisplayService(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_VI, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IApplicationDisplayService>(nv_flinger);
}
VI_U::VI_U(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
: ServiceFramework("vi:u"), nv_flinger(std::move(nv_flinger)) {
VI_U::VI_U(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
: Module::Interface(std::move(module), "vi:u", std::move(nv_flinger)) {
static const FunctionInfo functions[] = {
{0, &VI_U::GetDisplayService, "GetDisplayService"},
{3, nullptr, "GetDisplayServiceWithProxyNameExchange"},

View File

@@ -4,25 +4,14 @@
#pragma once
#include <memory>
#include "core/hle/service/service.h"
#include "core/hle/service/vi/vi.h"
namespace Service {
namespace NVFlinger {
class NVFlinger;
}
namespace VI {
class VI_U final : public ServiceFramework<VI_U> {
class VI_U final : public Module::Interface {
public:
VI_U(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
~VI_U() = default;
private:
void GetDisplayService(Kernel::HLERequestContext& ctx);
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
explicit VI_U(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
};
} // namespace VI

View File

@@ -84,7 +84,7 @@ void Linker::WriteRelocations(std::vector<u8>& program_image, const std::vector<
}
break;
default:
LOG_CRITICAL(Loader, "Unknown relocation type: %d", rela.type);
LOG_CRITICAL(Loader, "Unknown relocation type: %d", static_cast<int>(rela.type));
break;
}
}

View File

@@ -23,7 +23,6 @@
namespace Memory {
static std::array<u8, Memory::VRAM_SIZE> vram;
static std::array<u8, Memory::N3DS_EXTRA_RAM_SIZE> n3ds_extra_ram;
static PageTable* current_page_table = nullptr;
@@ -42,6 +41,9 @@ static void MapPages(PageTable& page_table, VAddr base, u64 size, u8* memory, Pa
LOG_DEBUG(HW_Memory, "Mapping %p onto %016" PRIX64 "-%016" PRIX64, memory, base * PAGE_SIZE,
(base + size) * PAGE_SIZE);
RasterizerFlushVirtualRegion(base << PAGE_BITS, size * PAGE_SIZE,
FlushMode::FlushAndInvalidate);
VAddr end = base + size;
while (base != end) {
ASSERT_MSG(base < PAGE_TABLE_NUM_ENTRIES, "out of range mapping at %016" PRIX64, base);
@@ -244,7 +246,6 @@ u8* GetPhysicalPointer(PAddr address) {
{IO_AREA_PADDR, IO_AREA_SIZE},
{DSP_RAM_PADDR, DSP_RAM_SIZE},
{FCRAM_PADDR, FCRAM_N3DS_SIZE},
{N3DS_EXTRA_RAM_PADDR, N3DS_EXTRA_RAM_SIZE},
};
const auto area =
@@ -283,9 +284,6 @@ u8* GetPhysicalPointer(PAddr address) {
}
ASSERT_MSG(target_pointer != nullptr, "Invalid FCRAM address");
break;
case N3DS_EXTRA_RAM_PADDR:
target_pointer = n3ds_extra_ram.data() + offset_into_region;
break;
default:
UNREACHABLE();
}
@@ -293,6 +291,43 @@ u8* GetPhysicalPointer(PAddr address) {
return target_pointer;
}
void RasterizerFlushVirtualRegion(VAddr start, u64 size, FlushMode mode) {
// Since pages are unmapped on shutdown after video core is shutdown, the renderer may be
// null here
if (VideoCore::g_renderer == nullptr) {
return;
}
VAddr end = start + size;
auto CheckRegion = [&](VAddr region_start, VAddr region_end) {
if (start >= region_end || end <= region_start) {
// No overlap with region
return;
}
VAddr overlap_start = std::max(start, region_start);
VAddr overlap_end = std::min(end, region_end);
u64 overlap_size = overlap_end - overlap_start;
auto* rasterizer = VideoCore::g_renderer->Rasterizer();
switch (mode) {
case FlushMode::Flush:
rasterizer->FlushRegion(overlap_start, overlap_size);
break;
case FlushMode::Invalidate:
rasterizer->InvalidateRegion(overlap_start, overlap_size);
break;
case FlushMode::FlushAndInvalidate:
rasterizer->FlushAndInvalidateRegion(overlap_start, overlap_size);
break;
}
};
CheckRegion(PROCESS_IMAGE_VADDR, PROCESS_IMAGE_VADDR_END);
CheckRegion(HEAP_VADDR, HEAP_VADDR_END);
}
u8 Read8(const VAddr addr) {
return Read<u8>(addr);
}
@@ -609,8 +644,6 @@ boost::optional<PAddr> TryVirtualToPhysicalAddress(const VAddr addr) {
return addr - DSP_RAM_VADDR + DSP_RAM_PADDR;
} else if (addr >= IO_AREA_VADDR && addr < IO_AREA_VADDR_END) {
return addr - IO_AREA_VADDR + IO_AREA_PADDR;
} else if (addr >= N3DS_EXTRA_RAM_VADDR && addr < N3DS_EXTRA_RAM_VADDR_END) {
return addr - N3DS_EXTRA_RAM_VADDR + N3DS_EXTRA_RAM_PADDR;
}
return boost::none;
@@ -637,8 +670,6 @@ boost::optional<VAddr> PhysicalToVirtualAddress(const PAddr addr) {
return addr - DSP_RAM_PADDR + DSP_RAM_VADDR;
} else if (addr >= IO_AREA_PADDR && addr < IO_AREA_PADDR_END) {
return addr - IO_AREA_PADDR + IO_AREA_VADDR;
} else if (addr >= N3DS_EXTRA_RAM_PADDR && addr < N3DS_EXTRA_RAM_PADDR_END) {
return addr - N3DS_EXTRA_RAM_PADDR + N3DS_EXTRA_RAM_VADDR;
}
return boost::none;

View File

@@ -36,7 +36,10 @@ enum class PageType : u8 {
Unmapped,
/// Page is mapped to regular memory. This is the only type you can get pointers to.
Memory,
/// Page is mapped to a memory hook, which intercepts read and write requests.
/// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
/// invalidation
RasterizerCachedMemory,
/// Page is mapped to a I/O region. Writing and reading to this page is handled by functions.
Special,
};
@@ -98,12 +101,6 @@ enum : PAddr {
VRAM_SIZE = 0x00600000, ///< VRAM size (6MB)
VRAM_PADDR_END = VRAM_PADDR + VRAM_SIZE,
/// New 3DS additional memory. Supposedly faster than regular FCRAM. Part of it can be used by
/// applications and system modules if mapped via the ExHeader.
N3DS_EXTRA_RAM_PADDR = 0x1F000000,
N3DS_EXTRA_RAM_SIZE = 0x00400000, ///< New 3DS additional memory size (4MB)
N3DS_EXTRA_RAM_PADDR_END = N3DS_EXTRA_RAM_PADDR + N3DS_EXTRA_RAM_SIZE,
/// DSP memory
DSP_RAM_PADDR = 0x1FF00000,
DSP_RAM_SIZE = 0x00080000, ///< DSP memory size (512KB)
@@ -119,7 +116,6 @@ enum : PAddr {
FCRAM_SIZE = 0x08000000, ///< FCRAM size on the Old 3DS (128MB)
FCRAM_N3DS_SIZE = 0x10000000, ///< FCRAM size on the New 3DS (256MB)
FCRAM_PADDR_END = FCRAM_PADDR + FCRAM_SIZE,
FCRAM_N3DS_PADDR_END = FCRAM_PADDR + FCRAM_N3DS_SIZE,
};
/// Virtual user-space memory regions
@@ -135,10 +131,6 @@ enum : VAddr {
LINEAR_HEAP_SIZE = 0x08000000,
LINEAR_HEAP_VADDR_END = LINEAR_HEAP_VADDR + LINEAR_HEAP_SIZE,
/// Maps 1:1 to New 3DS additional memory
N3DS_EXTRA_RAM_VADDR = 0x1E800000,
N3DS_EXTRA_RAM_VADDR_END = N3DS_EXTRA_RAM_VADDR + N3DS_EXTRA_RAM_SIZE,
/// Maps 1:1 to the IO register area.
IO_AREA_VADDR = 0x1EC00000,
IO_AREA_VADDR_END = IO_AREA_VADDR + IO_AREA_SIZE,
@@ -253,4 +245,19 @@ boost::optional<VAddr> PhysicalToVirtualAddress(PAddr addr);
*/
u8* GetPhysicalPointer(PAddr address);
enum class FlushMode {
/// Write back modified surfaces to RAM
Flush,
/// Remove region from the cache
Invalidate,
/// Write back modified surfaces to RAM, and also remove them from the cache
FlushAndInvalidate,
};
/**
* Flushes and invalidates any externally cached rasterizer resources touching the given virtual
* address region.
*/
void RasterizerFlushVirtualRegion(VAddr start, u64 size, FlushMode mode);
} // namespace Memory

View File

@@ -7,16 +7,28 @@ add_library(video_core STATIC
engines/maxwell_3d.h
engines/maxwell_compute.cpp
engines/maxwell_compute.h
gpu.cpp
gpu.h
memory_manager.cpp
memory_manager.h
rasterizer_interface.h
renderer_base.cpp
renderer_base.h
renderer_opengl/gl_rasterizer.cpp
renderer_opengl/gl_rasterizer.h
renderer_opengl/gl_rasterizer_cache.cpp
renderer_opengl/gl_rasterizer_cache.h
renderer_opengl/gl_resource_manager.h
renderer_opengl/gl_shader_decompiler.cpp
renderer_opengl/gl_shader_decompiler.h
renderer_opengl/gl_shader_gen.cpp
renderer_opengl/gl_shader_gen.h
renderer_opengl/gl_shader_util.cpp
renderer_opengl/gl_shader_util.h
renderer_opengl/gl_state.cpp
renderer_opengl/gl_state.h
renderer_opengl/gl_stream_buffer.cpp
renderer_opengl/gl_stream_buffer.h
renderer_opengl/renderer_opengl.cpp
renderer_opengl/renderer_opengl.h
utils.h

View File

@@ -24,12 +24,37 @@ namespace Tegra {
enum class BufferMethods {
BindObject = 0,
SetGraphMacroCode = 0x45,
SetGraphMacroCodeArg = 0x46,
SetGraphMacroEntry = 0x47,
CountBufferMethods = 0x100,
};
void GPU::WriteReg(u32 method, u32 subchannel, u32 value) {
LOG_WARNING(HW_GPU, "Processing method %08X on subchannel %u value %08X", method, subchannel,
value);
void GPU::WriteReg(u32 method, u32 subchannel, u32 value, u32 remaining_params) {
LOG_WARNING(HW_GPU, "Processing method %08X on subchannel %u value %08X remaining params %u",
method, subchannel, value, remaining_params);
if (method == static_cast<u32>(BufferMethods::SetGraphMacroEntry)) {
// Prepare to upload a new macro, reset the upload counter.
LOG_DEBUG(HW_GPU, "Uploading GPU macro %08X", value);
current_macro_entry = value;
current_macro_code.clear();
return;
}
if (method == static_cast<u32>(BufferMethods::SetGraphMacroCodeArg)) {
// Append a new code word to the current macro.
current_macro_code.push_back(value);
// There are no more params remaining, submit the code to the 3D engine.
if (remaining_params == 0) {
maxwell_3d->SubmitMacroCode(current_macro_entry, std::move(current_macro_code));
current_macro_entry = InvalidGraphMacroEntry;
current_macro_code.clear();
}
return;
}
if (method == static_cast<u32>(BufferMethods::BindObject)) {
// Bind the current subchannel to the desired engine id.
@@ -54,7 +79,7 @@ void GPU::WriteReg(u32 method, u32 subchannel, u32 value) {
fermi_2d->WriteReg(method, value);
break;
case EngineID::MAXWELL_B:
maxwell_3d->WriteReg(method, value);
maxwell_3d->WriteReg(method, value, remaining_params);
break;
case EngineID::MAXWELL_COMPUTE_B:
maxwell_compute->WriteReg(method, value);
@@ -78,7 +103,8 @@ void GPU::ProcessCommandList(GPUVAddr address, u32 size) {
case SubmissionMode::Increasing: {
// Increase the method value with each argument.
for (unsigned i = 0; i < header.arg_count; ++i) {
WriteReg(header.method + i, header.subchannel, Memory::Read32(current_addr));
WriteReg(header.method + i, header.subchannel, Memory::Read32(current_addr),
header.arg_count - i - 1);
current_addr += sizeof(u32);
}
break;
@@ -87,27 +113,31 @@ void GPU::ProcessCommandList(GPUVAddr address, u32 size) {
case SubmissionMode::NonIncreasing: {
// Use the same method value for all arguments.
for (unsigned i = 0; i < header.arg_count; ++i) {
WriteReg(header.method, header.subchannel, Memory::Read32(current_addr));
WriteReg(header.method, header.subchannel, Memory::Read32(current_addr),
header.arg_count - i - 1);
current_addr += sizeof(u32);
}
break;
}
case SubmissionMode::IncreaseOnce: {
ASSERT(header.arg_count.Value() >= 1);
// Use the original method for the first argument and then the next method for all other
// arguments.
WriteReg(header.method, header.subchannel, Memory::Read32(current_addr));
WriteReg(header.method, header.subchannel, Memory::Read32(current_addr),
header.arg_count - 1);
current_addr += sizeof(u32);
// Use the same method value for all arguments.
for (unsigned i = 1; i < header.arg_count; ++i) {
WriteReg(header.method + 1, header.subchannel, Memory::Read32(current_addr));
WriteReg(header.method + 1, header.subchannel, Memory::Read32(current_addr),
header.arg_count - i - 1);
current_addr += sizeof(u32);
}
break;
}
case SubmissionMode::Inline: {
// The register value is stored in the bits 16-28 as an immediate
WriteReg(header.method, header.subchannel, header.inline_data);
WriteReg(header.method, header.subchannel, header.inline_data, 0);
break;
}
default:

View File

@@ -34,6 +34,4 @@ static_assert(std::is_standard_layout<CommandHeader>::value == true,
"CommandHeader does not use standard layout");
static_assert(sizeof(CommandHeader) == sizeof(u32), "CommandHeader has incorrect size!");
void ProcessCommandList(VAddr address, u32 size);
} // namespace Tegra

View File

@@ -8,12 +8,70 @@
namespace Tegra {
namespace Engines {
/// First register id that is actually a Macro call.
constexpr u32 MacroRegistersStart = 0xE00;
const std::unordered_map<u32, Maxwell3D::MethodInfo> Maxwell3D::method_handlers = {
{0xE1A, {"BindTextureInfoBuffer", 1, &Maxwell3D::BindTextureInfoBuffer}},
{0xE24, {"SetShader", 5, &Maxwell3D::SetShader}},
{0xE2A, {"BindStorageBuffer", 1, &Maxwell3D::BindStorageBuffer}},
};
Maxwell3D::Maxwell3D(MemoryManager& memory_manager) : memory_manager(memory_manager) {}
void Maxwell3D::WriteReg(u32 method, u32 value) {
void Maxwell3D::SubmitMacroCode(u32 entry, std::vector<u32> code) {
uploaded_macros[entry * 2 + MacroRegistersStart] = std::move(code);
}
void Maxwell3D::CallMacroMethod(u32 method, const std::vector<u32>& parameters) {
// TODO(Subv): Write an interpreter for the macros uploaded via registers 0x45 and 0x47
// The requested macro must have been uploaded already.
ASSERT_MSG(uploaded_macros.find(method) != uploaded_macros.end(), "Macro %08X was not uploaded",
method);
auto itr = method_handlers.find(method);
ASSERT_MSG(itr != method_handlers.end(), "Unhandled method call %08X", method);
ASSERT(itr->second.arguments == parameters.size());
(this->*itr->second.handler)(parameters);
// Reset the current macro and its parameters.
executing_macro = 0;
macro_params.clear();
}
void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) {
ASSERT_MSG(method < Regs::NUM_REGS,
"Invalid Maxwell3D register, increase the size of the Regs structure");
// It is an error to write to a register other than the current macro's ARG register before it
// has finished execution.
if (executing_macro != 0) {
ASSERT(method == executing_macro + 1);
}
// Methods after 0xE00 are special, they're actually triggers for some microcode that was
// uploaded to the GPU during initialization.
if (method >= MacroRegistersStart) {
// We're trying to execute a macro
if (executing_macro == 0) {
// A macro call must begin by writing the macro method's register, not its argument.
ASSERT_MSG((method % 2) == 0,
"Can't start macro execution by writing to the ARGS register");
executing_macro = method;
}
macro_params.push_back(value);
// Call the macro when there are no more parameters in the command buffer
if (remaining_params == 0) {
CallMacroMethod(executing_macro, macro_params);
}
return;
}
regs.reg_array[method] = value;
#define MAXWELL3D_REG_INDEX(field_name) (offsetof(Regs, field_name) / sizeof(u32))
@@ -27,6 +85,45 @@ void Maxwell3D::WriteReg(u32 method, u32 value) {
ASSERT_MSG(regs.code_address.CodeAddress() == 0, "Unexpected CODE_ADDRESS register value.");
break;
}
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[2]):
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[3]):
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[4]):
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[5]):
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[6]):
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[7]):
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[8]):
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[9]):
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[10]):
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[11]):
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[12]):
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[13]):
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[14]):
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[15]): {
ProcessCBData(value);
break;
}
case MAXWELL3D_REG_INDEX(cb_bind[0].raw_config): {
ProcessCBBind(Regs::ShaderStage::Vertex);
break;
}
case MAXWELL3D_REG_INDEX(cb_bind[1].raw_config): {
ProcessCBBind(Regs::ShaderStage::TesselationControl);
break;
}
case MAXWELL3D_REG_INDEX(cb_bind[2].raw_config): {
ProcessCBBind(Regs::ShaderStage::TesselationEval);
break;
}
case MAXWELL3D_REG_INDEX(cb_bind[3].raw_config): {
ProcessCBBind(Regs::ShaderStage::Geometry);
break;
}
case MAXWELL3D_REG_INDEX(cb_bind[4].raw_config): {
ProcessCBBind(Regs::ShaderStage::Fragment);
break;
}
case MAXWELL3D_REG_INDEX(draw.vertex_end_gl): {
DrawArrays();
break;
@@ -56,7 +153,8 @@ void Maxwell3D::ProcessQueryGet() {
break;
}
default:
UNIMPLEMENTED_MSG("Query mode %u not implemented", regs.query.query_get.mode.Value());
UNIMPLEMENTED_MSG("Query mode %u not implemented",
static_cast<u32>(regs.query.query_get.mode.Value()));
}
}
@@ -64,5 +162,113 @@ void Maxwell3D::DrawArrays() {
LOG_WARNING(HW_GPU, "Game requested a DrawArrays, ignoring");
}
void Maxwell3D::BindTextureInfoBuffer(const std::vector<u32>& parameters) {
/**
* Parameters description:
* [0] = Shader stage, usually 4 for FragmentShader
*/
u32 stage = parameters[0];
// Perform the same operations as the real macro code.
GPUVAddr address = static_cast<GPUVAddr>(regs.tex_info_buffers.address[stage]) << 8;
u32 size = regs.tex_info_buffers.size[stage];
regs.const_buffer.cb_size = size;
regs.const_buffer.cb_address_high = address >> 32;
regs.const_buffer.cb_address_low = address & 0xFFFFFFFF;
}
void Maxwell3D::SetShader(const std::vector<u32>& parameters) {
/**
* Parameters description:
* [0] = Shader Program.
* [1] = Unknown, presumably the shader id.
* [2] = Offset to the start of the shader, after the 0x30 bytes header.
* [3] = Shader Stage.
* [4] = Const Buffer Address >> 8.
*/
auto shader_program = static_cast<Regs::ShaderProgram>(parameters[0]);
// TODO(Subv): This address is probably an offset from the CODE_ADDRESS register.
GPUVAddr address = parameters[2];
auto shader_stage = static_cast<Regs::ShaderStage>(parameters[3]);
GPUVAddr cb_address = parameters[4] << 8;
auto& shader = state.shader_programs[static_cast<size_t>(shader_program)];
shader.program = shader_program;
shader.stage = shader_stage;
shader.address = address;
// Perform the same operations as the real macro code.
// TODO(Subv): Early exit if register 0xD1C + shader_program contains the same as params[1].
auto& shader_regs = regs.shader_config[static_cast<size_t>(shader_program)];
shader_regs.start_id = address;
// TODO(Subv): Write params[1] to register 0xD1C + shader_program.
// TODO(Subv): Write params[2] to register 0xD22 + shader_program.
// Note: This value is hardcoded in the macro's code.
static constexpr u32 DefaultCBSize = 0x10000;
regs.const_buffer.cb_size = DefaultCBSize;
regs.const_buffer.cb_address_high = cb_address >> 32;
regs.const_buffer.cb_address_low = cb_address & 0xFFFFFFFF;
// Write a hardcoded 0x11 to CB_BIND, this binds the current const buffer to buffer c1[] in the
// shader. It's likely that these are the constants for the shader.
regs.cb_bind[static_cast<size_t>(shader_stage)].valid.Assign(1);
regs.cb_bind[static_cast<size_t>(shader_stage)].index.Assign(1);
ProcessCBBind(shader_stage);
}
void Maxwell3D::BindStorageBuffer(const std::vector<u32>& parameters) {
/**
* Parameters description:
* [0] = Buffer offset >> 2
*/
u32 buffer_offset = parameters[0] << 2;
// Perform the same operations as the real macro code.
// Note: This value is hardcoded in the macro's code.
static constexpr u32 DefaultCBSize = 0x5F00;
regs.const_buffer.cb_size = DefaultCBSize;
GPUVAddr address = regs.ssbo_info.BufferAddress();
regs.const_buffer.cb_address_high = address >> 32;
regs.const_buffer.cb_address_low = address & 0xFFFFFFFF;
regs.const_buffer.cb_pos = buffer_offset;
}
void Maxwell3D::ProcessCBBind(Regs::ShaderStage stage) {
// Bind the buffer currently in CB_ADDRESS to the specified index in the desired shader stage.
auto& shader = state.shader_stages[static_cast<size_t>(stage)];
auto& bind_data = regs.cb_bind[static_cast<size_t>(stage)];
auto& buffer = shader.const_buffers[bind_data.index];
buffer.enabled = bind_data.valid.Value() != 0;
buffer.index = bind_data.index;
buffer.address = regs.const_buffer.BufferAddress();
buffer.size = regs.const_buffer.cb_size;
}
void Maxwell3D::ProcessCBData(u32 value) {
// Write the input value to the current const buffer at the current position.
GPUVAddr buffer_address = regs.const_buffer.BufferAddress();
ASSERT(buffer_address != 0);
// Don't allow writing past the end of the buffer.
ASSERT(regs.const_buffer.cb_pos + sizeof(u32) <= regs.const_buffer.cb_size);
VAddr address =
memory_manager.PhysicalToVirtualAddress(buffer_address + regs.const_buffer.cb_pos);
Memory::Write32(address, value);
// Increment the current buffer position.
regs.const_buffer.cb_pos = regs.const_buffer.cb_pos + 4;
}
} // namespace Engines
} // namespace Tegra

View File

@@ -4,6 +4,10 @@
#pragma once
#include <array>
#include <unordered_map>
#include <vector>
#include "common/assert.h"
#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
@@ -18,19 +22,31 @@ public:
~Maxwell3D() = default;
/// Write the value to the register identified by method.
void WriteReg(u32 method, u32 value);
void WriteReg(u32 method, u32 value, u32 remaining_params);
/// Uploads the code for a GPU macro program associated with the specified entry.
void SubmitMacroCode(u32 entry, std::vector<u32> code);
/// Register structure of the Maxwell3D engine.
/// TODO(Subv): This structure will need to be made bigger as more registers are discovered.
struct Regs {
static constexpr size_t NUM_REGS = 0xE36;
static constexpr size_t NumRenderTargets = 8;
static constexpr size_t NumViewports = 16;
static constexpr size_t NumCBData = 16;
static constexpr size_t NumVertexArrays = 32;
static constexpr size_t NumVertexAttributes = 32;
static constexpr size_t MaxShaderProgram = 6;
static constexpr size_t MaxShaderStage = 5;
// Maximum number of const buffers per shader stage.
static constexpr size_t MaxConstBuffers = 16;
enum class QueryMode : u32 {
Write = 0,
Sync = 1,
};
static constexpr size_t MaxShaderProgram = 6;
enum class ShaderProgram : u32 {
VertexA = 0,
VertexB = 1,
@@ -40,7 +56,7 @@ public:
Fragment = 5,
};
enum class ShaderType : u32 {
enum class ShaderStage : u32 {
Vertex = 0,
TesselationControl = 1,
TesselationEval = 2,
@@ -48,9 +64,213 @@ public:
Fragment = 4,
};
enum class VertexSize : u32 {
Size_32_32_32_32 = 0x01,
Size_32_32_32 = 0x02,
Size_16_16_16_16 = 0x03,
Size_32_32 = 0x04,
Size_16_16_16 = 0x05,
Size_8_8_8_8 = 0x0a,
Size_16_16 = 0x0f,
Size_32 = 0x12,
Size_8_8_8 = 0x13,
Size_8_8 = 0x18,
Size_16 = 0x1b,
Size_8 = 0x1d,
Size_10_10_10_2 = 0x30,
Size_11_11_10 = 0x31,
};
static std::string VertexSizeToString(VertexSize vertex_size) {
switch (vertex_size) {
case VertexSize::Size_32_32_32_32:
return "32_32_32_32";
case VertexSize::Size_32_32_32:
return "32_32_32";
case VertexSize::Size_16_16_16_16:
return "16_16_16_16";
case VertexSize::Size_32_32:
return "32_32";
case VertexSize::Size_16_16_16:
return "16_16_16";
case VertexSize::Size_8_8_8_8:
return "8_8_8_8";
case VertexSize::Size_16_16:
return "16_16";
case VertexSize::Size_32:
return "32";
case VertexSize::Size_8_8_8:
return "8_8_8";
case VertexSize::Size_8_8:
return "8_8";
case VertexSize::Size_16:
return "16";
case VertexSize::Size_8:
return "8";
case VertexSize::Size_10_10_10_2:
return "10_10_10_2";
case VertexSize::Size_11_11_10:
return "11_11_10";
}
UNIMPLEMENTED();
return {};
}
enum class VertexType : u32 {
SignedNorm = 1,
UnsignedNorm = 2,
SignedInt = 3,
UnsignedInt = 4,
UnsignedScaled = 5,
SignedScaled = 6,
Float = 7,
};
static std::string VertexTypeToString(VertexType vertex_type) {
switch (vertex_type) {
case VertexType::SignedNorm:
return "SignedNorm";
case VertexType::UnsignedNorm:
return "UnsignedNorm";
case VertexType::SignedInt:
return "SignedInt";
case VertexType::UnsignedInt:
return "UnsignedInt";
case VertexType::UnsignedScaled:
return "UnsignedScaled";
case VertexType::SignedScaled:
return "SignedScaled";
case VertexType::Float:
return "Float";
}
UNIMPLEMENTED();
return {};
}
enum class PrimitiveTopology : u32 {
Points = 0x0,
Lines = 0x1,
LineLoop = 0x2,
LineStrip = 0x3,
Triangles = 0x4,
TriangleStrip = 0x5,
TriangleFan = 0x6,
Quads = 0x7,
QuadStrip = 0x8,
Polygon = 0x9,
LinesAdjacency = 0xa,
LineStripAdjacency = 0xb,
TrianglesAdjacency = 0xc,
TriangleStripAdjacency = 0xd,
Patches = 0xe,
};
union {
struct {
INSERT_PADDING_WORDS(0x582);
INSERT_PADDING_WORDS(0x200);
struct {
u32 address_high;
u32 address_low;
u32 horiz;
u32 vert;
u32 format;
u32 block_dimensions;
u32 array_mode;
u32 layer_stride;
u32 base_layer;
INSERT_PADDING_WORDS(7);
GPUVAddr Address() const {
return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
address_low);
}
} rt[NumRenderTargets];
INSERT_PADDING_WORDS(0x80);
struct {
union {
BitField<0, 16, u32> x;
BitField<16, 16, u32> width;
};
union {
BitField<0, 16, u32> y;
BitField<16, 16, u32> height;
};
float depth_range_near;
float depth_range_far;
} viewport[NumViewports];
INSERT_PADDING_WORDS(0x1D);
struct {
u32 first;
u32 count;
} vertex_buffer;
INSERT_PADDING_WORDS(0x99);
struct {
u32 address_high;
u32 address_low;
u32 format;
u32 block_dimensions;
u32 layer_stride;
GPUVAddr Address() const {
return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
address_low);
}
} zeta;
INSERT_PADDING_WORDS(0x5B);
union {
BitField<0, 5, u32> buffer;
BitField<6, 1, u32> constant;
BitField<7, 14, u32> offset;
BitField<21, 6, VertexSize> size;
BitField<27, 3, VertexType> type;
BitField<31, 1, u32> bgra;
} vertex_attrib_format[NumVertexAttributes];
INSERT_PADDING_WORDS(0xF);
struct {
union {
BitField<0, 4, u32> count;
};
} rt_control;
INSERT_PADDING_WORDS(0xCF);
struct {
u32 tsc_address_high;
u32 tsc_address_low;
u32 tsc_limit;
GPUVAddr TSCAddress() const {
return static_cast<GPUVAddr>(
(static_cast<GPUVAddr>(tsc_address_high) << 32) | tsc_address_low);
}
} tsc;
INSERT_PADDING_WORDS(0x3);
struct {
u32 tic_address_high;
u32 tic_address_low;
u32 tic_limit;
GPUVAddr TICAddress() const {
return static_cast<GPUVAddr>(
(static_cast<GPUVAddr>(tic_address_high) << 32) | tic_address_low);
}
} tic;
INSERT_PADDING_WORDS(0x22);
struct {
u32 code_address_high;
u32 code_address_low;
@@ -61,10 +281,15 @@ public:
}
} code_address;
INSERT_PADDING_WORDS(1);
struct {
u32 vertex_end_gl;
u32 vertex_begin_gl;
union {
u32 vertex_begin_gl;
BitField<0, 16, PrimitiveTopology> topology;
};
} draw;
INSERT_PADDING_WORDS(0x139);
struct {
u32 query_address_high;
@@ -83,7 +308,34 @@ public:
}
} query;
INSERT_PADDING_WORDS(0x13C);
INSERT_PADDING_WORDS(0x3C);
struct {
union {
BitField<0, 12, u32> stride;
BitField<12, 1, u32> enable;
};
u32 start_high;
u32 start_low;
u32 divisor;
GPUVAddr StartAddress() const {
return static_cast<GPUVAddr>((static_cast<GPUVAddr>(start_high) << 32) |
start_low);
}
} vertex_array[NumVertexArrays];
INSERT_PADDING_WORDS(0x40);
struct {
u32 limit_high;
u32 limit_low;
GPUVAddr LimitAddress() const {
return static_cast<GPUVAddr>((static_cast<GPUVAddr>(limit_high) << 32) |
limit_low);
}
} vertex_array_limit[NumVertexArrays];
struct {
union {
@@ -93,17 +345,60 @@ public:
u32 start_id;
INSERT_PADDING_WORDS(1);
u32 gpr_alloc;
ShaderType type;
ShaderStage type;
INSERT_PADDING_WORDS(9);
} shader_config[6];
} shader_config[MaxShaderProgram];
INSERT_PADDING_WORDS(0x5D0);
INSERT_PADDING_WORDS(0x8C);
struct {
u32 shader_code_call;
u32 shader_code_args;
} shader_code;
u32 cb_size;
u32 cb_address_high;
u32 cb_address_low;
u32 cb_pos;
u32 cb_data[NumCBData];
GPUVAddr BufferAddress() const {
return static_cast<GPUVAddr>(
(static_cast<GPUVAddr>(cb_address_high) << 32) | cb_address_low);
}
} const_buffer;
INSERT_PADDING_WORDS(0x10);
struct {
union {
u32 raw_config;
BitField<0, 1, u32> valid;
BitField<4, 5, u32> index;
};
INSERT_PADDING_WORDS(7);
} cb_bind[MaxShaderStage];
INSERT_PADDING_WORDS(0x56);
u32 tex_cb_index;
INSERT_PADDING_WORDS(0x395);
struct {
/// Compressed address of a buffer that holds information about bound SSBOs.
/// This address is usually bound to c0 in the shaders.
u32 buffer_address;
GPUVAddr BufferAddress() const {
return static_cast<GPUVAddr>(buffer_address) << 8;
}
} ssbo_info;
INSERT_PADDING_WORDS(0x11);
struct {
u32 address[MaxShaderStage];
u32 size[MaxShaderStage];
} tex_info_buffers;
INSERT_PADDING_WORDS(0x102);
};
std::array<u32, NUM_REGS> reg_array;
};
@@ -111,25 +406,97 @@ public:
static_assert(sizeof(Regs) == Regs::NUM_REGS * sizeof(u32), "Maxwell3D Regs has wrong size");
struct State {
struct ConstBufferInfo {
GPUVAddr address;
u32 index;
u32 size;
bool enabled;
};
struct ShaderProgramInfo {
Regs::ShaderStage stage;
Regs::ShaderProgram program;
GPUVAddr address;
};
struct ShaderStageInfo {
std::array<ConstBufferInfo, Regs::MaxConstBuffers> const_buffers;
};
std::array<ShaderStageInfo, Regs::MaxShaderStage> shader_stages;
std::array<ShaderProgramInfo, Regs::MaxShaderProgram> shader_programs;
};
State state{};
private:
MemoryManager& memory_manager;
std::unordered_map<u32, std::vector<u32>> uploaded_macros;
/// Macro method that is currently being executed / being fed parameters.
u32 executing_macro = 0;
/// Parameters that have been submitted to the macro call so far.
std::vector<u32> macro_params;
/**
* Call a macro on this engine.
* @param method Method to call
* @param parameters Arguments to the method call
*/
void CallMacroMethod(u32 method, const std::vector<u32>& parameters);
/// Handles a write to the QUERY_GET register.
void ProcessQueryGet();
/// Handles a write to the CB_DATA[i] register.
void ProcessCBData(u32 value);
/// Handles a write to the CB_BIND register.
void ProcessCBBind(Regs::ShaderStage stage);
/// Handles a write to the VERTEX_END_GL register, triggering a draw.
void DrawArrays();
MemoryManager& memory_manager;
/// Method call handlers
void BindTextureInfoBuffer(const std::vector<u32>& parameters);
void SetShader(const std::vector<u32>& parameters);
void BindStorageBuffer(const std::vector<u32>& parameters);
struct MethodInfo {
const char* name;
u32 arguments;
void (Maxwell3D::*handler)(const std::vector<u32>& parameters);
};
static const std::unordered_map<u32, MethodInfo> method_handlers;
};
#define ASSERT_REG_POSITION(field_name, position) \
static_assert(offsetof(Maxwell3D::Regs, field_name) == position * 4, \
"Field " #field_name " has invalid position")
ASSERT_REG_POSITION(rt, 0x200);
ASSERT_REG_POSITION(viewport, 0x300);
ASSERT_REG_POSITION(vertex_buffer, 0x35D);
ASSERT_REG_POSITION(zeta, 0x3F8);
ASSERT_REG_POSITION(vertex_attrib_format[0], 0x458);
ASSERT_REG_POSITION(rt_control, 0x487);
ASSERT_REG_POSITION(tsc, 0x557);
ASSERT_REG_POSITION(tic, 0x55D);
ASSERT_REG_POSITION(code_address, 0x582);
ASSERT_REG_POSITION(draw, 0x585);
ASSERT_REG_POSITION(query, 0x6C0);
ASSERT_REG_POSITION(vertex_array[0], 0x700);
ASSERT_REG_POSITION(vertex_array_limit[0], 0x7C0);
ASSERT_REG_POSITION(shader_config[0], 0x800);
ASSERT_REG_POSITION(shader_code, 0xE24);
ASSERT_REG_POSITION(const_buffer, 0x8E0);
ASSERT_REG_POSITION(cb_bind[0], 0x904);
ASSERT_REG_POSITION(tex_cb_index, 0x982);
ASSERT_REG_POSITION(ssbo_info, 0xD18);
ASSERT_REG_POSITION(tex_info_buffers.address[0], 0xD2A);
ASSERT_REG_POSITION(tex_info_buffers.size[0], 0xD2F);
#undef ASSERT_REG_POSITION

21
src/video_core/gpu.cpp Normal file
View File

@@ -0,0 +1,21 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "video_core/engines/fermi_2d.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/engines/maxwell_compute.h"
#include "video_core/gpu.h"
namespace Tegra {
GPU::GPU() {
memory_manager = std::make_unique<MemoryManager>();
maxwell_3d = std::make_unique<Engines::Maxwell3D>(*memory_manager);
fermi_2d = std::make_unique<Engines::Fermi2D>();
maxwell_compute = std::make_unique<Engines::MaxwellCompute>();
}
GPU::~GPU() = default;
} // namespace Tegra

View File

@@ -6,14 +6,50 @@
#include <memory>
#include <unordered_map>
#include <vector>
#include "common/common_types.h"
#include "video_core/engines/fermi_2d.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/engines/maxwell_compute.h"
#include "core/hle/service/nvflinger/buffer_queue.h"
#include "video_core/memory_manager.h"
namespace Tegra {
/**
* Struct describing framebuffer configuration
*/
struct FramebufferConfig {
enum class PixelFormat : u32 {
ABGR8 = 1,
};
/**
* Returns the number of bytes per pixel.
*/
static u32 BytesPerPixel(PixelFormat format) {
switch (format) {
case PixelFormat::ABGR8:
return 4;
}
UNREACHABLE();
}
VAddr address;
u32 offset;
u32 width;
u32 height;
u32 stride;
PixelFormat pixel_format;
using TransformFlags = Service::NVFlinger::BufferQueue::BufferTransformFlags;
TransformFlags transform_flags;
};
namespace Engines {
class Fermi2D;
class Maxwell3D;
class MaxwellCompute;
} // namespace Engines
enum class EngineID {
FERMI_TWOD_A = 0x902D, // 2D Engine
MAXWELL_B = 0xB197, // 3D Engine
@@ -24,22 +60,23 @@ enum class EngineID {
class GPU final {
public:
GPU() {
memory_manager = std::make_unique<MemoryManager>();
maxwell_3d = std::make_unique<Engines::Maxwell3D>(*memory_manager);
fermi_2d = std::make_unique<Engines::Fermi2D>();
maxwell_compute = std::make_unique<Engines::MaxwellCompute>();
}
~GPU() = default;
GPU();
~GPU();
/// Processes a command list stored at the specified address in GPU memory.
void ProcessCommandList(GPUVAddr address, u32 size);
std::unique_ptr<MemoryManager> memory_manager;
Engines::Maxwell3D& Maxwell3D() {
return *maxwell_3d;
}
private:
static constexpr u32 InvalidGraphMacroEntry = 0xFFFFFFFF;
/// Writes a single register in the engine bound to the specified subchannel
void WriteReg(u32 method, u32 subchannel, u32 value);
void WriteReg(u32 method, u32 subchannel, u32 value, u32 remaining_params);
/// Mapping of command subchannels to their bound engine ids.
std::unordered_map<u32, EngineID> bound_engines;
@@ -50,6 +87,11 @@ private:
std::unique_ptr<Engines::Fermi2D> fermi_2d;
/// Compute engine
std::unique_ptr<Engines::MaxwellCompute> maxwell_compute;
/// Entry of the macro that is currently being uploaded
u32 current_macro_entry = InvalidGraphMacroEntry;
/// Code being uploaded for the current macro
std::vector<u32> current_macro_code;
};
} // namespace Tegra

View File

@@ -0,0 +1,63 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
#include "video_core/gpu.h"
struct ScreenInfo;
namespace VideoCore {
class RasterizerInterface {
public:
virtual ~RasterizerInterface() {}
/// Draw the current batch of triangles
virtual void DrawTriangles() = 0;
/// Notify rasterizer that the specified Maxwell register has been changed
virtual void NotifyMaxwellRegisterChanged(u32 id) = 0;
/// Notify rasterizer that all caches should be flushed to 3DS memory
virtual void FlushAll() = 0;
/// Notify rasterizer that any caches of the specified region should be flushed to 3DS memory
virtual void FlushRegion(VAddr addr, u64 size) = 0;
/// Notify rasterizer that any caches of the specified region should be invalidated
virtual void InvalidateRegion(VAddr addr, u64 size) = 0;
/// Notify rasterizer that any caches of the specified region should be flushed to 3DS memory
/// and invalidated
virtual void FlushAndInvalidateRegion(VAddr addr, u64 size) = 0;
/// Attempt to use a faster method to perform a display transfer with is_texture_copy = 0
virtual bool AccelerateDisplayTransfer(const void* config) {
return false;
}
/// Attempt to use a faster method to perform a display transfer with is_texture_copy = 1
virtual bool AccelerateTextureCopy(const void* config) {
return false;
}
/// Attempt to use a faster method to fill a region
virtual bool AccelerateFill(const void* config) {
return false;
}
/// Attempt to use a faster method to display the framebuffer to screen
virtual bool AccelerateDisplay(const Tegra::FramebufferConfig& framebuffer,
VAddr framebuffer_addr, u32 pixel_stride,
ScreenInfo& screen_info) {
return false;
}
virtual bool AccelerateDrawBatch(bool is_indexed) {
return false;
}
};
} // namespace VideoCore

View File

@@ -5,6 +5,11 @@
#include <atomic>
#include <memory>
#include "video_core/renderer_base.h"
#include "video_core/renderer_opengl/gl_rasterizer.h"
#include "video_core/video_core.h"
void RendererBase::RefreshRasterizerSetting() {}
void RendererBase::RefreshRasterizerSetting() {
if (rasterizer == nullptr) {
rasterizer = std::make_unique<RasterizerOpenGL>();
}
}

View File

@@ -8,6 +8,8 @@
#include <boost/optional.hpp>
#include "common/assert.h"
#include "common/common_types.h"
#include "video_core/gpu.h"
#include "video_core/rasterizer_interface.h"
class EmuWindow;
@@ -16,40 +18,10 @@ public:
/// Used to reference a framebuffer
enum kFramebuffer { kFramebuffer_VirtualXFB = 0, kFramebuffer_EFB, kFramebuffer_Texture };
/**
* Struct describing framebuffer metadata
* TODO(bunnei): This struct belongs in the GPU code, but we don't have a good place for it yet.
*/
struct FramebufferInfo {
enum class PixelFormat : u32 {
ABGR8 = 1,
};
/**
* Returns the number of bytes per pixel.
*/
static u32 BytesPerPixel(PixelFormat format) {
switch (format) {
case PixelFormat::ABGR8:
return 4;
}
UNREACHABLE();
}
VAddr address;
u32 offset;
u32 width;
u32 height;
u32 stride;
PixelFormat pixel_format;
bool flip_vertical;
};
virtual ~RendererBase() {}
/// Swap buffers (render frame)
virtual void SwapBuffers(boost::optional<const FramebufferInfo&> framebuffer_info) = 0;
virtual void SwapBuffers(boost::optional<const Tegra::FramebufferConfig&> framebuffer) = 0;
/**
* Set the emulator window to use for renderer
@@ -74,12 +46,16 @@ public:
return m_current_frame;
}
VideoCore::RasterizerInterface* Rasterizer() const {
return rasterizer.get();
}
void RefreshRasterizerSetting();
protected:
std::unique_ptr<VideoCore::RasterizerInterface> rasterizer;
f32 m_current_fps = 0.0f; ///< Current framerate, should be set by the renderer
int m_current_frame = 0; ///< Current frame, should be set by the renderer
private:
bool opengl_rasterizer_active = false;
};

View File

@@ -0,0 +1,349 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <memory>
#include <string>
#include <tuple>
#include <utility>
#include <glad/glad.h>
#include "common/alignment.h"
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/math_util.h"
#include "common/microprofile.h"
#include "common/scope_exit.h"
#include "common/vector_math.h"
#include "core/settings.h"
#include "video_core/renderer_opengl/gl_rasterizer.h"
#include "video_core/renderer_opengl/gl_shader_gen.h"
#include "video_core/renderer_opengl/renderer_opengl.h"
using PixelFormat = SurfaceParams::PixelFormat;
using SurfaceType = SurfaceParams::SurfaceType;
MICROPROFILE_DEFINE(OpenGL_VAO, "OpenGL", "Vertex Array Setup", MP_RGB(128, 128, 192));
MICROPROFILE_DEFINE(OpenGL_VS, "OpenGL", "Vertex Shader Setup", MP_RGB(128, 128, 192));
MICROPROFILE_DEFINE(OpenGL_FS, "OpenGL", "Fragment Shader Setup", MP_RGB(128, 128, 192));
MICROPROFILE_DEFINE(OpenGL_Drawing, "OpenGL", "Drawing", MP_RGB(128, 128, 192));
MICROPROFILE_DEFINE(OpenGL_Blits, "OpenGL", "Blits", MP_RGB(100, 100, 255));
MICROPROFILE_DEFINE(OpenGL_CacheManagement, "OpenGL", "Cache Mgmt", MP_RGB(100, 255, 100));
enum class UniformBindings : GLuint { Common, VS, FS };
static void SetShaderUniformBlockBinding(GLuint shader, const char* name, UniformBindings binding,
size_t expected_size) {
GLuint ub_index = glGetUniformBlockIndex(shader, name);
if (ub_index != GL_INVALID_INDEX) {
GLint ub_size = 0;
glGetActiveUniformBlockiv(shader, ub_index, GL_UNIFORM_BLOCK_DATA_SIZE, &ub_size);
ASSERT_MSG(ub_size == expected_size,
"Uniform block size did not match! Got %d, expected %zu",
static_cast<int>(ub_size), expected_size);
glUniformBlockBinding(shader, ub_index, static_cast<GLuint>(binding));
}
}
static void SetShaderUniformBlockBindings(GLuint shader) {
SetShaderUniformBlockBinding(shader, "shader_data", UniformBindings::Common,
sizeof(RasterizerOpenGL::UniformData));
SetShaderUniformBlockBinding(shader, "vs_config", UniformBindings::VS,
sizeof(RasterizerOpenGL::VSUniformData));
SetShaderUniformBlockBinding(shader, "fs_config", UniformBindings::FS,
sizeof(RasterizerOpenGL::FSUniformData));
}
RasterizerOpenGL::RasterizerOpenGL() {
shader_dirty = true;
has_ARB_buffer_storage = false;
has_ARB_direct_state_access = false;
has_ARB_separate_shader_objects = false;
has_ARB_vertex_attrib_binding = false;
GLint ext_num;
glGetIntegerv(GL_NUM_EXTENSIONS, &ext_num);
for (GLint i = 0; i < ext_num; i++) {
std::string extension{reinterpret_cast<const char*>(glGetStringi(GL_EXTENSIONS, i))};
if (extension == "GL_ARB_buffer_storage") {
has_ARB_buffer_storage = true;
} else if (extension == "GL_ARB_direct_state_access") {
has_ARB_direct_state_access = true;
} else if (extension == "GL_ARB_separate_shader_objects") {
has_ARB_separate_shader_objects = true;
} else if (extension == "GL_ARB_vertex_attrib_binding") {
has_ARB_vertex_attrib_binding = true;
}
}
// Clipping plane 0 is always enabled for PICA fixed clip plane z <= 0
state.clip_distance[0] = true;
// Generate VBO, VAO and UBO
vertex_buffer = OGLStreamBuffer::MakeBuffer(GLAD_GL_ARB_buffer_storage, GL_ARRAY_BUFFER);
vertex_buffer->Create(VERTEX_BUFFER_SIZE, VERTEX_BUFFER_SIZE / 2);
sw_vao.Create();
uniform_buffer.Create();
state.draw.vertex_array = sw_vao.handle;
state.draw.vertex_buffer = vertex_buffer->GetHandle();
state.draw.uniform_buffer = uniform_buffer.handle;
state.Apply();
glBufferData(GL_UNIFORM_BUFFER, sizeof(UniformData), nullptr, GL_STATIC_DRAW);
glBindBufferBase(GL_UNIFORM_BUFFER, 0, uniform_buffer.handle);
uniform_block_data.dirty = true;
// Create render framebuffer
framebuffer.Create();
if (has_ARB_separate_shader_objects) {
hw_vao.Create();
hw_vao_enabled_attributes.fill(false);
stream_buffer = OGLStreamBuffer::MakeBuffer(has_ARB_buffer_storage, GL_ARRAY_BUFFER);
stream_buffer->Create(STREAM_BUFFER_SIZE, STREAM_BUFFER_SIZE / 2);
state.draw.vertex_buffer = stream_buffer->GetHandle();
pipeline.Create();
state.draw.program_pipeline = pipeline.handle;
state.draw.shader_program = 0;
state.draw.vertex_array = hw_vao.handle;
state.Apply();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, stream_buffer->GetHandle());
vs_uniform_buffer.Create();
glBindBuffer(GL_UNIFORM_BUFFER, vs_uniform_buffer.handle);
glBufferData(GL_UNIFORM_BUFFER, sizeof(VSUniformData), nullptr, GL_STREAM_COPY);
glBindBufferBase(GL_UNIFORM_BUFFER, 1, vs_uniform_buffer.handle);
} else {
ASSERT_MSG(false, "Unimplemented");
}
accelerate_draw = AccelDraw::Disabled;
glEnable(GL_BLEND);
LOG_WARNING(HW_GPU, "Sync fixed function OpenGL state here when ready");
}
RasterizerOpenGL::~RasterizerOpenGL() {
if (stream_buffer != nullptr) {
state.draw.vertex_buffer = stream_buffer->GetHandle();
state.Apply();
stream_buffer->Release();
}
}
static constexpr std::array<GLenum, 4> vs_attrib_types{
GL_BYTE, // VertexAttributeFormat::BYTE
GL_UNSIGNED_BYTE, // VertexAttributeFormat::UBYTE
GL_SHORT, // VertexAttributeFormat::SHORT
GL_FLOAT // VertexAttributeFormat::FLOAT
};
void RasterizerOpenGL::AnalyzeVertexArray(bool is_indexed) {
UNIMPLEMENTED();
}
void RasterizerOpenGL::SetupVertexArray(u8* array_ptr, GLintptr buffer_offset) {
MICROPROFILE_SCOPE(OpenGL_VAO);
UNIMPLEMENTED();
}
void RasterizerOpenGL::SetupVertexShader(VSUniformData* ub_ptr, GLintptr buffer_offset) {
MICROPROFILE_SCOPE(OpenGL_VS);
UNIMPLEMENTED();
}
void RasterizerOpenGL::SetupFragmentShader(FSUniformData* ub_ptr, GLintptr buffer_offset) {
MICROPROFILE_SCOPE(OpenGL_FS);
ASSERT_MSG(false, "Unimplemented");
}
bool RasterizerOpenGL::AccelerateDrawBatch(bool is_indexed) {
if (!has_ARB_separate_shader_objects) {
ASSERT_MSG(false, "Unimplemented");
return false;
}
accelerate_draw = is_indexed ? AccelDraw::Indexed : AccelDraw::Arrays;
DrawTriangles();
return true;
}
void RasterizerOpenGL::DrawTriangles() {
MICROPROFILE_SCOPE(OpenGL_Drawing);
UNIMPLEMENTED();
}
void RasterizerOpenGL::NotifyMaxwellRegisterChanged(u32 id) {}
void RasterizerOpenGL::FlushAll() {
MICROPROFILE_SCOPE(OpenGL_CacheManagement);
res_cache.FlushAll();
}
void RasterizerOpenGL::FlushRegion(VAddr addr, u64 size) {
MICROPROFILE_SCOPE(OpenGL_CacheManagement);
res_cache.FlushRegion(addr, size);
}
void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) {
MICROPROFILE_SCOPE(OpenGL_CacheManagement);
res_cache.InvalidateRegion(addr, size, nullptr);
}
void RasterizerOpenGL::FlushAndInvalidateRegion(VAddr addr, u64 size) {
MICROPROFILE_SCOPE(OpenGL_CacheManagement);
res_cache.FlushRegion(addr, size);
res_cache.InvalidateRegion(addr, size, nullptr);
}
bool RasterizerOpenGL::AccelerateDisplayTransfer(const void* config) {
MICROPROFILE_SCOPE(OpenGL_Blits);
ASSERT_MSG(false, "Unimplemented");
return true;
}
bool RasterizerOpenGL::AccelerateTextureCopy(const void* config) {
ASSERT_MSG(false, "Unimplemented");
return true;
}
bool RasterizerOpenGL::AccelerateFill(const void* config) {
ASSERT_MSG(false, "Unimplemented");
return true;
}
bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& framebuffer,
VAddr framebuffer_addr, u32 pixel_stride,
ScreenInfo& screen_info) {
if (framebuffer_addr == 0) {
return false;
}
MICROPROFILE_SCOPE(OpenGL_CacheManagement);
SurfaceParams src_params;
src_params.addr = framebuffer_addr;
src_params.width = std::min(framebuffer.width, pixel_stride);
src_params.height = framebuffer.height;
src_params.stride = pixel_stride;
src_params.is_tiled = false;
src_params.pixel_format =
SurfaceParams::PixelFormatFromGPUPixelFormat(framebuffer.pixel_format);
src_params.UpdateParams();
MathUtil::Rectangle<u32> src_rect;
Surface src_surface;
std::tie(src_surface, src_rect) =
res_cache.GetSurfaceSubRect(src_params, ScaleMatch::Ignore, true);
if (src_surface == nullptr) {
return false;
}
u32 scaled_width = src_surface->GetScaledWidth();
u32 scaled_height = src_surface->GetScaledHeight();
screen_info.display_texcoords = MathUtil::Rectangle<float>(
(float)src_rect.bottom / (float)scaled_height, (float)src_rect.left / (float)scaled_width,
(float)src_rect.top / (float)scaled_height, (float)src_rect.right / (float)scaled_width);
screen_info.display_texture = src_surface->texture.handle;
return true;
}
void RasterizerOpenGL::SetShader() {
// TODO(bunnei): The below sets up a static test shader for passing untransformed vertices to
// OpenGL for rendering. This should be removed/replaced when we start emulating Maxwell
// shaders.
static constexpr char vertex_shader[] = R"(
#version 150 core
in vec2 vert_position;
in vec2 vert_tex_coord;
out vec2 frag_tex_coord;
void main() {
// Multiply input position by the rotscale part of the matrix and then manually translate by
// the last column. This is equivalent to using a full 3x3 matrix and expanding the vector
// to `vec3(vert_position.xy, 1.0)`
gl_Position = vec4(mat2(mat3x2(0.0015625f, 0.0, 0.0, -0.0027778, -1.0, 1.0)) * vert_position + mat3x2(0.0015625f, 0.0, 0.0, -0.0027778, -1.0, 1.0)[2], 0.0, 1.0);
frag_tex_coord = vert_tex_coord;
}
)";
static constexpr char fragment_shader[] = R"(
#version 150 core
in vec2 frag_tex_coord;
out vec4 color;
uniform sampler2D color_texture;
void main() {
color = vec4(1.0, 0.0, 1.0, 0.0);
}
)";
if (current_shader) {
return;
}
LOG_ERROR(HW_GPU, "Emulated shaders are not supported! Using a passthrough shader.");
current_shader = &test_shader;
if (has_ARB_separate_shader_objects) {
test_shader.shader.Create(vertex_shader, nullptr, fragment_shader, {}, true);
glActiveShaderProgram(pipeline.handle, test_shader.shader.handle);
} else {
ASSERT_MSG(false, "Unimplemented");
}
state.draw.shader_program = test_shader.shader.handle;
state.Apply();
if (has_ARB_separate_shader_objects) {
state.draw.shader_program = 0;
state.Apply();
}
}
void RasterizerOpenGL::SyncClipEnabled() {
ASSERT_MSG(false, "Unimplemented");
}
void RasterizerOpenGL::SyncClipCoef() {
ASSERT_MSG(false, "Unimplemented");
}
void RasterizerOpenGL::SyncCullMode() {
ASSERT_MSG(false, "Unimplemented");
}
void RasterizerOpenGL::SyncDepthScale() {
ASSERT_MSG(false, "Unimplemented");
}
void RasterizerOpenGL::SyncDepthOffset() {
ASSERT_MSG(false, "Unimplemented");
}
void RasterizerOpenGL::SyncBlendEnabled() {
ASSERT_MSG(false, "Unimplemented");
}
void RasterizerOpenGL::SyncBlendFuncs() {
ASSERT_MSG(false, "Unimplemented");
}
void RasterizerOpenGL::SyncBlendColor() {
ASSERT_MSG(false, "Unimplemented");
}

View File

@@ -0,0 +1,172 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <cstddef>
#include <cstring>
#include <memory>
#include <unordered_map>
#include <vector>
#include <glad/glad.h>
#include "common/bit_field.h"
#include "common/common_types.h"
#include "common/hash.h"
#include "common/vector_math.h"
#include "video_core/rasterizer_interface.h"
#include "video_core/renderer_opengl/gl_rasterizer_cache.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_state.h"
#include "video_core/renderer_opengl/gl_stream_buffer.h"
struct ScreenInfo;
class RasterizerOpenGL : public VideoCore::RasterizerInterface {
public:
RasterizerOpenGL();
~RasterizerOpenGL() override;
void DrawTriangles() override;
void NotifyMaxwellRegisterChanged(u32 id) override;
void FlushAll() override;
void FlushRegion(VAddr addr, u64 size) override;
void InvalidateRegion(VAddr addr, u64 size) override;
void FlushAndInvalidateRegion(VAddr addr, u64 size) override;
bool AccelerateDisplayTransfer(const void* config) override;
bool AccelerateTextureCopy(const void* config) override;
bool AccelerateFill(const void* config) override;
bool AccelerateDisplay(const Tegra::FramebufferConfig& framebuffer, VAddr framebuffer_addr,
u32 pixel_stride, ScreenInfo& screen_info) override;
bool AccelerateDrawBatch(bool is_indexed) override;
/// OpenGL shader generated for a given Maxwell register state
struct MaxwellShader {
/// OpenGL shader resource
OGLShader shader;
};
struct VertexShader {
OGLShader shader;
};
struct FragmentShader {
OGLShader shader;
};
/// Uniform structure for the Uniform Buffer Object, all vectors must be 16-byte aligned
// NOTE: Always keep a vec4 at the end. The GL spec is not clear wether the alignment at
// 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 {};
// static_assert(
// sizeof(UniformData) == 0x460,
// "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");
struct VSUniformData {};
// static_assert(
// sizeof(VSUniformData) == 1856,
// "The size of the VSUniformData structure has changed, update the structure in the
// shader");
static_assert(sizeof(VSUniformData) < 16384,
"VSUniformData structure must be less than 16kb as per the OpenGL spec");
struct FSUniformData {};
// static_assert(
// sizeof(FSUniformData) == 1856,
// "The size of the FSUniformData structure has changed, update the structure in the
// shader");
static_assert(sizeof(FSUniformData) < 16384,
"FSUniformData structure must be less than 16kb as per the OpenGL spec");
private:
struct SamplerInfo {};
/// Syncs the clip enabled status to match the guest state
void SyncClipEnabled();
/// Syncs the clip coefficients to match the guest state
void SyncClipCoef();
/// Sets the OpenGL shader in accordance with the current guest state
void SetShader();
/// Syncs the cull mode to match the guest state
void SyncCullMode();
/// Syncs the depth scale to match the guest state
void SyncDepthScale();
/// Syncs the depth offset to match the guest state
void SyncDepthOffset();
/// Syncs the blend enabled status to match the guest state
void SyncBlendEnabled();
/// Syncs the blend functions to match the guest state
void SyncBlendFuncs();
/// Syncs the blend color to match the guest state
void SyncBlendColor();
bool has_ARB_buffer_storage;
bool has_ARB_direct_state_access;
bool has_ARB_separate_shader_objects;
bool has_ARB_vertex_attrib_binding;
OpenGLState state;
RasterizerCacheOpenGL res_cache;
/// Shader used for test renderering - to be removed once we have emulated shaders
MaxwellShader test_shader{};
const MaxwellShader* current_shader{};
bool shader_dirty{};
struct {
UniformData data;
bool dirty;
} uniform_block_data = {};
OGLPipeline pipeline;
OGLVertexArray sw_vao;
OGLVertexArray hw_vao;
std::array<bool, 16> hw_vao_enabled_attributes;
std::array<SamplerInfo, 3> texture_samplers;
static constexpr size_t VERTEX_BUFFER_SIZE = 128 * 1024 * 1024;
std::unique_ptr<OGLStreamBuffer> vertex_buffer;
OGLBuffer uniform_buffer;
OGLFramebuffer framebuffer;
static constexpr size_t STREAM_BUFFER_SIZE = 4 * 1024 * 1024;
std::unique_ptr<OGLStreamBuffer> stream_buffer;
GLsizeiptr vs_input_size;
void AnalyzeVertexArray(bool is_indexed);
void SetupVertexArray(u8* array_ptr, GLintptr buffer_offset);
OGLBuffer vs_uniform_buffer;
std::unordered_map<GLShader::MaxwellVSConfig, VertexShader*> vs_shader_map;
std::unordered_map<std::string, VertexShader> vs_shader_cache;
OGLShader vs_default_shader;
void SetupVertexShader(VSUniformData* ub_ptr, GLintptr buffer_offset);
OGLBuffer fs_uniform_buffer;
std::unordered_map<GLShader::MaxwellFSConfig, FragmentShader*> fs_shader_map;
std::unordered_map<std::string, FragmentShader> fs_shader_cache;
OGLShader fs_default_shader;
void SetupFragmentShader(FSUniformData* ub_ptr, GLintptr buffer_offset);
enum class AccelDraw { Disabled, Arrays, Indexed };
AccelDraw accelerate_draw;
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,360 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <memory>
#include <set>
#include <tuple>
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#endif
#include <boost/icl/interval_map.hpp>
#include <boost/icl/interval_set.hpp>
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
#include <glad/glad.h>
#include "common/assert.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/math_util.h"
#include "video_core/gpu.h"
#include "video_core/renderer_opengl/gl_resource_manager.h"
struct CachedSurface;
using Surface = std::shared_ptr<CachedSurface>;
using SurfaceSet = std::set<Surface>;
using SurfaceRegions = boost::icl::interval_set<VAddr>;
using SurfaceMap = boost::icl::interval_map<VAddr, Surface>;
using SurfaceCache = boost::icl::interval_map<VAddr, SurfaceSet>;
using SurfaceInterval = SurfaceCache::interval_type;
static_assert(std::is_same<SurfaceRegions::interval_type, SurfaceCache::interval_type>() &&
std::is_same<SurfaceMap::interval_type, SurfaceCache::interval_type>(),
"incorrect interval types");
using SurfaceRect_Tuple = std::tuple<Surface, MathUtil::Rectangle<u32>>;
using SurfaceSurfaceRect_Tuple = std::tuple<Surface, Surface, MathUtil::Rectangle<u32>>;
using PageMap = boost::icl::interval_map<u32, int>;
enum class ScaleMatch {
Exact, // only accept same res scale
Upscale, // only allow higher scale than params
Ignore // accept every scaled res
};
struct SurfaceParams {
enum class PixelFormat {
// First 5 formats are shared between textures and color buffers
RGBA8 = 0,
RGB8 = 1,
RGB5A1 = 2,
RGB565 = 3,
RGBA4 = 4,
// Texture-only formats
IA8 = 5,
RG8 = 6,
I8 = 7,
A8 = 8,
IA4 = 9,
I4 = 10,
A4 = 11,
ETC1 = 12,
ETC1A4 = 13,
// Depth buffer-only formats
D16 = 14,
// gap
D24 = 16,
D24S8 = 17,
Invalid = 255,
};
enum class SurfaceType {
Color = 0,
Texture = 1,
Depth = 2,
DepthStencil = 3,
Fill = 4,
Invalid = 5
};
static constexpr unsigned int GetFormatBpp(PixelFormat format) {
constexpr std::array<unsigned int, 18> bpp_table = {
32, // RGBA8
24, // RGB8
16, // RGB5A1
16, // RGB565
16, // RGBA4
16, // IA8
16, // RG8
8, // I8
8, // A8
8, // IA4
4, // I4
4, // A4
4, // ETC1
8, // ETC1A4
16, // D16
0,
24, // D24
32, // D24S8
};
assert(static_cast<size_t>(format) < bpp_table.size());
return bpp_table[static_cast<size_t>(format)];
}
unsigned int GetFormatBpp() const {
return GetFormatBpp(pixel_format);
}
static PixelFormat PixelFormatFromGPUPixelFormat(Tegra::FramebufferConfig::PixelFormat format) {
switch (format) {
case Tegra::FramebufferConfig::PixelFormat::ABGR8:
return PixelFormat::RGBA8;
default:
UNREACHABLE();
}
}
static bool CheckFormatsBlittable(PixelFormat pixel_format_a, PixelFormat pixel_format_b) {
SurfaceType a_type = GetFormatType(pixel_format_a);
SurfaceType b_type = GetFormatType(pixel_format_b);
if ((a_type == SurfaceType::Color || a_type == SurfaceType::Texture) &&
(b_type == SurfaceType::Color || b_type == SurfaceType::Texture)) {
return true;
}
if (a_type == SurfaceType::Depth && b_type == SurfaceType::Depth) {
return true;
}
if (a_type == SurfaceType::DepthStencil && b_type == SurfaceType::DepthStencil) {
return true;
}
return false;
}
static constexpr SurfaceType GetFormatType(PixelFormat pixel_format) {
if ((unsigned int)pixel_format < 5) {
return SurfaceType::Color;
}
if ((unsigned int)pixel_format < 14) {
return SurfaceType::Texture;
}
if (pixel_format == PixelFormat::D16 || pixel_format == PixelFormat::D24) {
return SurfaceType::Depth;
}
if (pixel_format == PixelFormat::D24S8) {
return SurfaceType::DepthStencil;
}
return SurfaceType::Invalid;
}
/// Update the params "size", "end" and "type" from the already set "addr", "width", "height"
/// and "pixel_format"
void UpdateParams() {
if (stride == 0) {
stride = width;
}
type = GetFormatType(pixel_format);
size = !is_tiled ? BytesInPixels(stride * (height - 1) + width)
: BytesInPixels(stride * 8 * (height / 8 - 1) + width * 8);
end = addr + size;
}
SurfaceInterval GetInterval() const {
return SurfaceInterval::right_open(addr, end);
}
// Returns the outer rectangle containing "interval"
SurfaceParams FromInterval(SurfaceInterval interval) const;
SurfaceInterval GetSubRectInterval(MathUtil::Rectangle<u32> unscaled_rect) const;
// Returns the region of the biggest valid rectange within interval
SurfaceInterval GetCopyableInterval(const Surface& src_surface) const;
u32 GetScaledWidth() const {
return width * res_scale;
}
u32 GetScaledHeight() const {
return height * res_scale;
}
MathUtil::Rectangle<u32> GetRect() const {
return {0, height, width, 0};
}
MathUtil::Rectangle<u32> GetScaledRect() const {
return {0, GetScaledHeight(), GetScaledWidth(), 0};
}
u64 PixelsInBytes(u64 size) const {
return size * CHAR_BIT / GetFormatBpp(pixel_format);
}
u64 BytesInPixels(u64 pixels) const {
return pixels * GetFormatBpp(pixel_format) / CHAR_BIT;
}
bool ExactMatch(const SurfaceParams& other_surface) const;
bool CanSubRect(const SurfaceParams& sub_surface) const;
bool CanExpand(const SurfaceParams& expanded_surface) const;
bool CanTexCopy(const SurfaceParams& texcopy_params) const;
MathUtil::Rectangle<u32> GetSubRect(const SurfaceParams& sub_surface) const;
MathUtil::Rectangle<u32> GetScaledSubRect(const SurfaceParams& sub_surface) const;
VAddr addr = 0;
VAddr end = 0;
u64 size = 0;
u32 width = 0;
u32 height = 0;
u32 stride = 0;
u16 res_scale = 1;
bool is_tiled = false;
PixelFormat pixel_format = PixelFormat::Invalid;
SurfaceType type = SurfaceType::Invalid;
};
struct CachedSurface : SurfaceParams {
bool CanFill(const SurfaceParams& dest_surface, SurfaceInterval fill_interval) const;
bool CanCopy(const SurfaceParams& dest_surface, SurfaceInterval copy_interval) const;
bool IsRegionValid(SurfaceInterval interval) const {
return (invalid_regions.find(interval) == invalid_regions.end());
}
bool IsSurfaceFullyInvalid() const {
return (invalid_regions & GetInterval()) == SurfaceRegions(GetInterval());
}
bool registered = false;
SurfaceRegions invalid_regions;
u64 fill_size = 0; /// Number of bytes to read from fill_data
std::array<u8, 4> fill_data;
OGLTexture texture;
static constexpr unsigned int GetGLBytesPerPixel(PixelFormat format) {
// OpenGL needs 4 bpp alignment for D24 since using GL_UNSIGNED_INT as type
return format == PixelFormat::Invalid
? 0
: (format == PixelFormat::D24 || GetFormatType(format) == SurfaceType::Texture)
? 4
: SurfaceParams::GetFormatBpp(format) / 8;
}
std::unique_ptr<u8[]> gl_buffer;
size_t gl_buffer_size = 0;
// Read/Write data in Switch memory to/from gl_buffer
void LoadGLBuffer(VAddr load_start, VAddr load_end);
void FlushGLBuffer(VAddr flush_start, VAddr flush_end);
// Upload/Download data in gl_buffer in/to this surface's texture
void UploadGLTexture(const MathUtil::Rectangle<u32>& rect, GLuint read_fb_handle,
GLuint draw_fb_handle);
void DownloadGLTexture(const MathUtil::Rectangle<u32>& rect, GLuint read_fb_handle,
GLuint draw_fb_handle);
};
class RasterizerCacheOpenGL : NonCopyable {
public:
RasterizerCacheOpenGL();
~RasterizerCacheOpenGL();
/// Blit one surface's texture to another
bool BlitSurfaces(const Surface& src_surface, const MathUtil::Rectangle<u32>& src_rect,
const Surface& dst_surface, const MathUtil::Rectangle<u32>& dst_rect);
void ConvertD24S8toABGR(GLuint src_tex, const MathUtil::Rectangle<u32>& src_rect,
GLuint dst_tex, const MathUtil::Rectangle<u32>& dst_rect);
/// Copy one surface's region to another
void CopySurface(const Surface& src_surface, const Surface& dst_surface,
SurfaceInterval copy_interval);
/// Load a texture from 3DS memory to OpenGL and cache it (if not already cached)
Surface GetSurface(const SurfaceParams& params, ScaleMatch match_res_scale,
bool load_if_create);
/// Attempt to find a subrect (resolution scaled) of a surface, otherwise loads a texture from
/// 3DS memory to OpenGL and caches it (if not already cached)
SurfaceRect_Tuple GetSurfaceSubRect(const SurfaceParams& params, ScaleMatch match_res_scale,
bool load_if_create);
/// Get a surface based on the texture configuration
Surface GetTextureSurface(const void* config);
/// Get the color and depth surfaces based on the framebuffer configuration
SurfaceSurfaceRect_Tuple GetFramebufferSurfaces(bool using_color_fb, bool using_depth_fb,
const MathUtil::Rectangle<s32>& viewport_rect);
/// Get a surface that matches the fill config
Surface GetFillSurface(const void* config);
/// Get a surface that matches a "texture copy" display transfer config
SurfaceRect_Tuple GetTexCopySurface(const SurfaceParams& params);
/// Write any cached resources overlapping the region back to memory (if dirty)
void FlushRegion(VAddr addr, u64 size, Surface flush_surface = nullptr);
/// Mark region as being invalidated by region_owner (nullptr if 3DS memory)
void InvalidateRegion(VAddr addr, u64 size, const Surface& region_owner);
/// Flush all cached resources tracked by this cache manager
void FlushAll();
private:
void DuplicateSurface(const Surface& src_surface, const Surface& dest_surface);
/// Update surface's texture for given region when necessary
void ValidateSurface(const Surface& surface, VAddr addr, u64 size);
/// Create a new surface
Surface CreateSurface(const SurfaceParams& params);
/// Register surface into the cache
void RegisterSurface(const Surface& surface);
/// Remove surface from the cache
void UnregisterSurface(const Surface& surface);
/// Increase/decrease the number of surface in pages touching the specified region
void UpdatePagesCachedCount(VAddr addr, u64 size, int delta);
SurfaceCache surface_cache;
PageMap cached_pages;
SurfaceMap dirty_regions;
SurfaceSet remove_surfaces;
OGLFramebuffer read_framebuffer;
OGLFramebuffer draw_framebuffer;
OGLVertexArray attributeless_vao;
OGLBuffer d24s8_abgr_buffer;
GLsizeiptr d24s8_abgr_buffer_size;
OGLShader d24s8_abgr_shader;
GLint d24s8_abgr_tbo_size_u_id;
GLint d24s8_abgr_viewport_u_id;
};

View File

@@ -36,7 +36,7 @@ public:
if (handle == 0)
return;
glDeleteTextures(1, &handle);
OpenGLState::ResetTexture(handle);
OpenGLState::GetCurState().ResetTexture(handle).Apply();
handle = 0;
}
@@ -69,7 +69,7 @@ public:
if (handle == 0)
return;
glDeleteSamplers(1, &handle);
OpenGLState::ResetSampler(handle);
OpenGLState::GetCurState().ResetSampler(handle).Apply();
handle = 0;
}
@@ -91,10 +91,13 @@ public:
}
/// Creates a new internal OpenGL resource and stores the handle
void Create(const char* vert_shader, const char* frag_shader) {
void Create(const char* vert_shader, const char* geo_shader, const char* frag_shader,
const std::vector<const char*>& feedback_vars = {},
bool separable_program = false) {
if (handle != 0)
return;
handle = GLShader::LoadProgram(vert_shader, frag_shader);
handle = GLShader::LoadProgram(vert_shader, geo_shader, frag_shader, feedback_vars,
separable_program);
}
/// Deletes the internal OpenGL resource
@@ -102,7 +105,40 @@ public:
if (handle == 0)
return;
glDeleteProgram(handle);
OpenGLState::ResetProgram(handle);
OpenGLState::GetCurState().ResetProgram(handle).Apply();
handle = 0;
}
GLuint handle = 0;
};
class OGLPipeline : private NonCopyable {
public:
OGLPipeline() = default;
OGLPipeline(OGLPipeline&& o) {
handle = std::exchange<GLuint>(o.handle, 0);
}
~OGLPipeline() {
Release();
}
OGLPipeline& operator=(OGLPipeline&& o) {
handle = std::exchange<GLuint>(o.handle, 0);
return *this;
}
/// Creates a new internal OpenGL resource and stores the handle
void Create() {
if (handle != 0)
return;
glGenProgramPipelines(1, &handle);
}
/// Deletes the internal OpenGL resource
void Release() {
if (handle == 0)
return;
glDeleteProgramPipelines(1, &handle);
OpenGLState::GetCurState().ResetPipeline(handle).Apply();
handle = 0;
}
@@ -135,13 +171,46 @@ public:
if (handle == 0)
return;
glDeleteBuffers(1, &handle);
OpenGLState::ResetBuffer(handle);
OpenGLState::GetCurState().ResetBuffer(handle).Apply();
handle = 0;
}
GLuint handle = 0;
};
class OGLSync : private NonCopyable {
public:
OGLSync() = default;
OGLSync(OGLSync&& o) : handle(std::exchange(o.handle, nullptr)) {}
~OGLSync() {
Release();
}
OGLSync& operator=(OGLSync&& o) {
Release();
handle = std::exchange(o.handle, nullptr);
return *this;
}
/// Creates a new internal OpenGL resource and stores the handle
void Create() {
if (handle != 0)
return;
handle = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
}
/// Deletes the internal OpenGL resource
void Release() {
if (handle == 0)
return;
glDeleteSync(handle);
handle = 0;
}
GLsync handle = 0;
};
class OGLVertexArray : private NonCopyable {
public:
OGLVertexArray() = default;
@@ -168,7 +237,7 @@ public:
if (handle == 0)
return;
glDeleteVertexArrays(1, &handle);
OpenGLState::ResetVertexArray(handle);
OpenGLState::GetCurState().ResetVertexArray(handle).Apply();
handle = 0;
}
@@ -201,7 +270,7 @@ public:
if (handle == 0)
return;
glDeleteFramebuffers(1, &handle);
OpenGLState::ResetFramebuffer(handle);
OpenGLState::GetCurState().ResetFramebuffer(handle).Apply();
handle = 0;
}

View File

@@ -0,0 +1,58 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <string>
#include <queue>
#include "common/assert.h"
#include "common/common_types.h"
#include "video_core/renderer_opengl/gl_shader_decompiler.h"
namespace Maxwell3D {
namespace Shader {
namespace Decompiler {
constexpr u32 PROGRAM_END = MAX_PROGRAM_CODE_LENGTH;
class Impl {
public:
Impl(const std::array<u32, MAX_PROGRAM_CODE_LENGTH>& program_code,
const std::array<u32, MAX_SWIZZLE_DATA_LENGTH>& swizzle_data, u32 main_offset,
const std::function<std::string(u32)>& inputreg_getter,
const std::function<std::string(u32)>& outputreg_getter, bool sanitize_mul,
const std::string& emit_cb, const std::string& setemit_cb)
: program_code(program_code), swizzle_data(swizzle_data), main_offset(main_offset),
inputreg_getter(inputreg_getter), outputreg_getter(outputreg_getter),
sanitize_mul(sanitize_mul), emit_cb(emit_cb), setemit_cb(setemit_cb) {}
std::string Decompile() {
UNIMPLEMENTED();
return {};
}
private:
const std::array<u32, MAX_PROGRAM_CODE_LENGTH>& program_code;
const std::array<u32, MAX_SWIZZLE_DATA_LENGTH>& swizzle_data;
u32 main_offset;
const std::function<std::string(u32)>& inputreg_getter;
const std::function<std::string(u32)>& outputreg_getter;
bool sanitize_mul;
const std::string& emit_cb;
const std::string& setemit_cb;
};
std::string DecompileProgram(const std::array<u32, MAX_PROGRAM_CODE_LENGTH>& program_code,
const std::array<u32, MAX_SWIZZLE_DATA_LENGTH>& swizzle_data,
u32 main_offset,
const std::function<std::string(u32)>& inputreg_getter,
const std::function<std::string(u32)>& outputreg_getter,
bool sanitize_mul, const std::string& emit_cb,
const std::string& setemit_cb) {
Impl impl(program_code, swizzle_data, main_offset, inputreg_getter, outputreg_getter,
sanitize_mul, emit_cb, setemit_cb);
return impl.Decompile();
}
} // namespace Decompiler
} // namespace Shader
} // namespace Maxwell3D

View File

@@ -0,0 +1,27 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <array>
#include <functional>
#include <string>
#include "common/common_types.h"
namespace Maxwell3D {
namespace Shader {
namespace Decompiler {
constexpr size_t MAX_PROGRAM_CODE_LENGTH{0x100000};
constexpr size_t MAX_SWIZZLE_DATA_LENGTH{0x100000};
std::string DecompileProgram(const std::array<u32, MAX_PROGRAM_CODE_LENGTH>& program_code,
const std::array<u32, MAX_SWIZZLE_DATA_LENGTH>& swizzle_data,
u32 main_offset,
const std::function<std::string(u32)>& inputreg_getter,
const std::function<std::string(u32)>& outputreg_getter,
bool sanitize_mul, const std::string& emit_cb = "",
const std::string& setemit_cb = "");
} // namespace Decompiler
} // namespace Shader
} // namespace Maxwell3D

View File

@@ -0,0 +1,20 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/assert.h"
#include "video_core/renderer_opengl/gl_shader_gen.h"
namespace GLShader {
std::string GenerateVertexShader(const MaxwellVSConfig& config) {
UNIMPLEMENTED();
return {};
}
std::string GenerateFragmentShader(const MaxwellFSConfig& config) {
UNIMPLEMENTED();
return {};
}
} // namespace GLShader

View File

@@ -0,0 +1,66 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <cstring>
#include <string>
#include <type_traits>
#include "common/hash.h"
namespace GLShader {
enum Attributes {
ATTRIBUTE_POSITION,
ATTRIBUTE_COLOR,
ATTRIBUTE_TEXCOORD0,
ATTRIBUTE_TEXCOORD1,
ATTRIBUTE_TEXCOORD2,
ATTRIBUTE_TEXCOORD0_W,
ATTRIBUTE_NORMQUAT,
ATTRIBUTE_VIEW,
};
struct MaxwellShaderConfigCommon {
explicit MaxwellShaderConfigCommon(){};
};
struct MaxwellVSConfig : MaxwellShaderConfigCommon {
explicit MaxwellVSConfig() : MaxwellShaderConfigCommon() {}
bool operator==(const MaxwellVSConfig& o) const {
return std::memcmp(this, &o, sizeof(MaxwellVSConfig)) == 0;
};
};
struct MaxwellFSConfig : MaxwellShaderConfigCommon {
explicit MaxwellFSConfig() : MaxwellShaderConfigCommon() {}
bool operator==(const MaxwellFSConfig& o) const {
return std::memcmp(this, &o, sizeof(MaxwellFSConfig)) == 0;
};
};
std::string GenerateVertexShader(const MaxwellVSConfig& config);
std::string GenerateFragmentShader(const MaxwellFSConfig& config);
} // namespace GLShader
namespace std {
template <>
struct hash<GLShader::MaxwellVSConfig> {
size_t operator()(const GLShader::MaxwellVSConfig& k) const {
return Common::ComputeHash64(&k, sizeof(GLShader::MaxwellVSConfig));
}
};
template <>
struct hash<GLShader::MaxwellFSConfig> {
size_t operator()(const GLShader::MaxwellFSConfig& k) const {
return Common::ComputeHash64(&k, sizeof(GLShader::MaxwellFSConfig));
}
};
} // namespace std

View File

@@ -10,53 +10,85 @@
namespace GLShader {
GLuint LoadProgram(const char* vertex_shader, const char* fragment_shader) {
GLuint LoadProgram(const char* vertex_shader, const char* geometry_shader,
const char* fragment_shader, const std::vector<const char*>& feedback_vars,
bool separable_program) {
// Create the shaders
GLuint vertex_shader_id = glCreateShader(GL_VERTEX_SHADER);
GLuint fragment_shader_id = glCreateShader(GL_FRAGMENT_SHADER);
GLuint vertex_shader_id = vertex_shader ? glCreateShader(GL_VERTEX_SHADER) : 0;
GLuint geometry_shader_id = geometry_shader ? glCreateShader(GL_GEOMETRY_SHADER) : 0;
GLuint fragment_shader_id = fragment_shader ? glCreateShader(GL_FRAGMENT_SHADER) : 0;
GLint result = GL_FALSE;
int info_log_length;
// Compile Vertex Shader
LOG_DEBUG(Render_OpenGL, "Compiling vertex shader...");
if (vertex_shader) {
// Compile Vertex Shader
LOG_DEBUG(Render_OpenGL, "Compiling vertex shader...");
glShaderSource(vertex_shader_id, 1, &vertex_shader, nullptr);
glCompileShader(vertex_shader_id);
glShaderSource(vertex_shader_id, 1, &vertex_shader, nullptr);
glCompileShader(vertex_shader_id);
// Check Vertex Shader
glGetShaderiv(vertex_shader_id, GL_COMPILE_STATUS, &result);
glGetShaderiv(vertex_shader_id, GL_INFO_LOG_LENGTH, &info_log_length);
// Check Vertex Shader
glGetShaderiv(vertex_shader_id, GL_COMPILE_STATUS, &result);
glGetShaderiv(vertex_shader_id, GL_INFO_LOG_LENGTH, &info_log_length);
if (info_log_length > 1) {
std::vector<char> vertex_shader_error(info_log_length);
glGetShaderInfoLog(vertex_shader_id, info_log_length, nullptr, &vertex_shader_error[0]);
if (result == GL_TRUE) {
LOG_DEBUG(Render_OpenGL, "%s", &vertex_shader_error[0]);
} else {
LOG_ERROR(Render_OpenGL, "Error compiling vertex shader:\n%s", &vertex_shader_error[0]);
if (info_log_length > 1) {
std::vector<char> vertex_shader_error(info_log_length);
glGetShaderInfoLog(vertex_shader_id, info_log_length, nullptr, &vertex_shader_error[0]);
if (result == GL_TRUE) {
LOG_DEBUG(Render_OpenGL, "%s", &vertex_shader_error[0]);
} else {
LOG_ERROR(Render_OpenGL, "Error compiling vertex shader:\n%s",
&vertex_shader_error[0]);
}
}
}
// Compile Fragment Shader
LOG_DEBUG(Render_OpenGL, "Compiling fragment shader...");
if (geometry_shader) {
// Compile Geometry Shader
LOG_DEBUG(Render_OpenGL, "Compiling geometry shader...");
glShaderSource(fragment_shader_id, 1, &fragment_shader, nullptr);
glCompileShader(fragment_shader_id);
glShaderSource(geometry_shader_id, 1, &geometry_shader, nullptr);
glCompileShader(geometry_shader_id);
// Check Fragment Shader
glGetShaderiv(fragment_shader_id, GL_COMPILE_STATUS, &result);
glGetShaderiv(fragment_shader_id, GL_INFO_LOG_LENGTH, &info_log_length);
// Check Geometry Shader
glGetShaderiv(geometry_shader_id, GL_COMPILE_STATUS, &result);
glGetShaderiv(geometry_shader_id, GL_INFO_LOG_LENGTH, &info_log_length);
if (info_log_length > 1) {
std::vector<char> fragment_shader_error(info_log_length);
glGetShaderInfoLog(fragment_shader_id, info_log_length, nullptr, &fragment_shader_error[0]);
if (result == GL_TRUE) {
LOG_DEBUG(Render_OpenGL, "%s", &fragment_shader_error[0]);
} else {
LOG_ERROR(Render_OpenGL, "Error compiling fragment shader:\n%s",
&fragment_shader_error[0]);
if (info_log_length > 1) {
std::vector<char> geometry_shader_error(info_log_length);
glGetShaderInfoLog(geometry_shader_id, info_log_length, nullptr,
&geometry_shader_error[0]);
if (result == GL_TRUE) {
LOG_DEBUG(Render_OpenGL, "%s", &geometry_shader_error[0]);
} else {
LOG_ERROR(Render_OpenGL, "Error compiling geometry shader:\n%s",
&geometry_shader_error[0]);
}
}
}
if (fragment_shader) {
// Compile Fragment Shader
LOG_DEBUG(Render_OpenGL, "Compiling fragment shader...");
glShaderSource(fragment_shader_id, 1, &fragment_shader, nullptr);
glCompileShader(fragment_shader_id);
// Check Fragment Shader
glGetShaderiv(fragment_shader_id, GL_COMPILE_STATUS, &result);
glGetShaderiv(fragment_shader_id, GL_INFO_LOG_LENGTH, &info_log_length);
if (info_log_length > 1) {
std::vector<char> fragment_shader_error(info_log_length);
glGetShaderInfoLog(fragment_shader_id, info_log_length, nullptr,
&fragment_shader_error[0]);
if (result == GL_TRUE) {
LOG_DEBUG(Render_OpenGL, "%s", &fragment_shader_error[0]);
} else {
LOG_ERROR(Render_OpenGL, "Error compiling fragment shader:\n%s",
&fragment_shader_error[0]);
}
}
}
@@ -64,8 +96,25 @@ GLuint LoadProgram(const char* vertex_shader, const char* fragment_shader) {
LOG_DEBUG(Render_OpenGL, "Linking program...");
GLuint program_id = glCreateProgram();
glAttachShader(program_id, vertex_shader_id);
glAttachShader(program_id, fragment_shader_id);
if (vertex_shader) {
glAttachShader(program_id, vertex_shader_id);
}
if (geometry_shader) {
glAttachShader(program_id, geometry_shader_id);
}
if (fragment_shader) {
glAttachShader(program_id, fragment_shader_id);
}
if (!feedback_vars.empty()) {
auto varyings = feedback_vars;
glTransformFeedbackVaryings(program_id, static_cast<GLsizei>(feedback_vars.size()),
&varyings[0], GL_INTERLEAVED_ATTRIBS);
}
if (separable_program) {
glProgramParameteri(program_id, GL_PROGRAM_SEPARABLE, GL_TRUE);
}
glLinkProgram(program_id);
@@ -85,13 +134,30 @@ GLuint LoadProgram(const char* vertex_shader, const char* fragment_shader) {
// If the program linking failed at least one of the shaders was probably bad
if (result == GL_FALSE) {
LOG_ERROR(Render_OpenGL, "Vertex shader:\n%s", vertex_shader);
LOG_ERROR(Render_OpenGL, "Fragment shader:\n%s", fragment_shader);
if (vertex_shader) {
LOG_ERROR(Render_OpenGL, "Vertex shader:\n%s", vertex_shader);
}
if (geometry_shader) {
LOG_ERROR(Render_OpenGL, "Geometry shader:\n%s", geometry_shader);
}
if (fragment_shader) {
LOG_ERROR(Render_OpenGL, "Fragment shader:\n%s", fragment_shader);
}
}
ASSERT_MSG(result == GL_TRUE, "Shader not linked");
glDeleteShader(vertex_shader_id);
glDeleteShader(fragment_shader_id);
if (vertex_shader) {
glDetachShader(program_id, vertex_shader_id);
glDeleteShader(vertex_shader_id);
}
if (geometry_shader) {
glDetachShader(program_id, geometry_shader_id);
glDeleteShader(geometry_shader_id);
}
if (fragment_shader) {
glDetachShader(program_id, fragment_shader_id);
glDeleteShader(fragment_shader_id);
}
return program_id;
}

View File

@@ -4,6 +4,7 @@
#pragma once
#include <vector>
#include <glad/glad.h>
namespace GLShader {
@@ -11,9 +12,12 @@ namespace GLShader {
/**
* Utility function to create and compile an OpenGL GLSL shader program (vertex + fragment shader)
* @param vertex_shader String of the GLSL vertex shader program
* @param geometry_shader String of the GLSL geometry shader program
* @param fragment_shader String of the GLSL fragment shader program
* @returns Handle of the newly created OpenGL shader object
*/
GLuint LoadProgram(const char* vertex_shader, const char* fragment_shader);
GLuint LoadProgram(const char* vertex_shader, const char* geometry_shader,
const char* fragment_shader, const std::vector<const char*>& feedback_vars = {},
bool separable_program = false);
} // namespace GLShader

View File

@@ -33,7 +33,7 @@ OpenGLState::OpenGLState() {
stencil.action_depth_pass = GL_KEEP;
stencil.action_stencil_fail = GL_KEEP;
blend.enabled = false;
blend.enabled = true;
blend.rgb_equation = GL_FUNC_ADD;
blend.a_equation = GL_FUNC_ADD;
blend.src_rgb_func = GL_ONE;
@@ -68,6 +68,18 @@ OpenGLState::OpenGLState() {
draw.vertex_buffer = 0;
draw.uniform_buffer = 0;
draw.shader_program = 0;
draw.program_pipeline = 0;
scissor.enabled = false;
scissor.x = 0;
scissor.y = 0;
scissor.width = 0;
scissor.height = 0;
viewport.x = 0;
viewport.y = 0;
viewport.width = 0;
viewport.height = 0;
clip_distance = {};
}
@@ -148,9 +160,6 @@ void OpenGLState::Apply() const {
if (blend.enabled != cur_state.blend.enabled) {
if (blend.enabled) {
glEnable(GL_BLEND);
cur_state.logic_op = GL_COPY;
glLogicOp(cur_state.logic_op);
glDisable(GL_COLOR_LOGIC_OP);
} else {
glDisable(GL_BLEND);
@@ -196,7 +205,7 @@ void OpenGLState::Apply() const {
// Lighting LUTs
if (lighting_lut.texture_buffer != cur_state.lighting_lut.texture_buffer) {
glActiveTexture(TextureUnits::LightingLUT.Enum());
glBindTexture(GL_TEXTURE_BUFFER, cur_state.lighting_lut.texture_buffer);
glBindTexture(GL_TEXTURE_BUFFER, lighting_lut.texture_buffer);
}
// Fog LUT
@@ -263,6 +272,31 @@ void OpenGLState::Apply() const {
glUseProgram(draw.shader_program);
}
// Program pipeline
if (draw.program_pipeline != cur_state.draw.program_pipeline) {
glBindProgramPipeline(draw.program_pipeline);
}
// 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 ||
viewport.width != cur_state.viewport.width ||
viewport.height != cur_state.viewport.height) {
glViewport(viewport.x, viewport.y, viewport.width, viewport.height);
}
// Clip distance
for (size_t i = 0; i < clip_distance.size(); ++i) {
if (clip_distance[i] != cur_state.clip_distance[i]) {
@@ -277,62 +311,75 @@ void OpenGLState::Apply() const {
cur_state = *this;
}
void OpenGLState::ResetTexture(GLuint handle) {
for (auto& unit : cur_state.texture_units) {
OpenGLState& OpenGLState::ResetTexture(GLuint handle) {
for (auto& unit : texture_units) {
if (unit.texture_2d == handle) {
unit.texture_2d = 0;
}
}
if (cur_state.lighting_lut.texture_buffer == handle)
cur_state.lighting_lut.texture_buffer = 0;
if (cur_state.fog_lut.texture_buffer == handle)
cur_state.fog_lut.texture_buffer = 0;
if (cur_state.proctex_noise_lut.texture_buffer == handle)
cur_state.proctex_noise_lut.texture_buffer = 0;
if (cur_state.proctex_color_map.texture_buffer == handle)
cur_state.proctex_color_map.texture_buffer = 0;
if (cur_state.proctex_alpha_map.texture_buffer == handle)
cur_state.proctex_alpha_map.texture_buffer = 0;
if (cur_state.proctex_lut.texture_buffer == handle)
cur_state.proctex_lut.texture_buffer = 0;
if (cur_state.proctex_diff_lut.texture_buffer == handle)
cur_state.proctex_diff_lut.texture_buffer = 0;
if (lighting_lut.texture_buffer == handle)
lighting_lut.texture_buffer = 0;
if (fog_lut.texture_buffer == handle)
fog_lut.texture_buffer = 0;
if (proctex_noise_lut.texture_buffer == handle)
proctex_noise_lut.texture_buffer = 0;
if (proctex_color_map.texture_buffer == handle)
proctex_color_map.texture_buffer = 0;
if (proctex_alpha_map.texture_buffer == handle)
proctex_alpha_map.texture_buffer = 0;
if (proctex_lut.texture_buffer == handle)
proctex_lut.texture_buffer = 0;
if (proctex_diff_lut.texture_buffer == handle)
proctex_diff_lut.texture_buffer = 0;
return *this;
}
void OpenGLState::ResetSampler(GLuint handle) {
for (auto& unit : cur_state.texture_units) {
OpenGLState& OpenGLState::ResetSampler(GLuint handle) {
for (auto& unit : texture_units) {
if (unit.sampler == handle) {
unit.sampler = 0;
}
}
return *this;
}
void OpenGLState::ResetProgram(GLuint handle) {
if (cur_state.draw.shader_program == handle) {
cur_state.draw.shader_program = 0;
OpenGLState& OpenGLState::ResetProgram(GLuint handle) {
if (draw.shader_program == handle) {
draw.shader_program = 0;
}
return *this;
}
void OpenGLState::ResetBuffer(GLuint handle) {
if (cur_state.draw.vertex_buffer == handle) {
cur_state.draw.vertex_buffer = 0;
}
if (cur_state.draw.uniform_buffer == handle) {
cur_state.draw.uniform_buffer = 0;
OpenGLState& OpenGLState::ResetPipeline(GLuint handle) {
if (draw.program_pipeline == handle) {
draw.program_pipeline = 0;
}
return *this;
}
void OpenGLState::ResetVertexArray(GLuint handle) {
if (cur_state.draw.vertex_array == handle) {
cur_state.draw.vertex_array = 0;
OpenGLState& OpenGLState::ResetBuffer(GLuint handle) {
if (draw.vertex_buffer == handle) {
draw.vertex_buffer = 0;
}
if (draw.uniform_buffer == handle) {
draw.uniform_buffer = 0;
}
return *this;
}
void OpenGLState::ResetFramebuffer(GLuint handle) {
if (cur_state.draw.read_framebuffer == handle) {
cur_state.draw.read_framebuffer = 0;
}
if (cur_state.draw.draw_framebuffer == handle) {
cur_state.draw.draw_framebuffer = 0;
OpenGLState& OpenGLState::ResetVertexArray(GLuint handle) {
if (draw.vertex_array == handle) {
draw.vertex_array = 0;
}
return *this;
}
OpenGLState& OpenGLState::ResetFramebuffer(GLuint handle) {
if (draw.read_framebuffer == handle) {
draw.read_framebuffer = 0;
}
if (draw.draw_framebuffer == handle) {
draw.draw_framebuffer = 0;
}
return *this;
}

View File

@@ -122,27 +122,44 @@ public:
GLuint vertex_buffer; // GL_ARRAY_BUFFER_BINDING
GLuint uniform_buffer; // GL_UNIFORM_BUFFER_BINDING
GLuint shader_program; // GL_CURRENT_PROGRAM
GLuint program_pipeline; // GL_PROGRAM_PIPELINE_BINDING
} draw;
struct {
bool enabled; // GL_SCISSOR_TEST
GLint x;
GLint y;
GLsizei width;
GLsizei height;
} scissor;
struct {
GLint x;
GLint y;
GLsizei width;
GLsizei height;
} viewport;
std::array<bool, 2> clip_distance; // GL_CLIP_DISTANCE
OpenGLState();
/// Get the currently active OpenGL state
static const OpenGLState& GetCurState() {
static OpenGLState GetCurState() {
return cur_state;
}
/// Apply this state as the current OpenGL state
void Apply() const;
/// Resets and unbinds any references to the given resource in the current OpenGL state
static void ResetTexture(GLuint handle);
static void ResetSampler(GLuint handle);
static void ResetProgram(GLuint handle);
static void ResetBuffer(GLuint handle);
static void ResetVertexArray(GLuint handle);
static void ResetFramebuffer(GLuint handle);
/// Resets any references to the given resource
OpenGLState& ResetTexture(GLuint handle);
OpenGLState& ResetSampler(GLuint handle);
OpenGLState& ResetProgram(GLuint handle);
OpenGLState& ResetPipeline(GLuint handle);
OpenGLState& ResetBuffer(GLuint handle);
OpenGLState& ResetVertexArray(GLuint handle);
OpenGLState& ResetFramebuffer(GLuint handle);
private:
static OpenGLState cur_state;

View File

@@ -0,0 +1,182 @@
// Copyright 2018 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <deque>
#include <vector>
#include "common/alignment.h"
#include "common/assert.h"
#include "video_core/renderer_opengl/gl_state.h"
#include "video_core/renderer_opengl/gl_stream_buffer.h"
class OrphanBuffer : public OGLStreamBuffer {
public:
explicit OrphanBuffer(GLenum target) : OGLStreamBuffer(target) {}
~OrphanBuffer() override;
private:
void Create(size_t size, size_t sync_subdivide) override;
void Release() override;
std::pair<u8*, GLintptr> Map(size_t size, size_t alignment) override;
void Unmap() override;
std::vector<u8> data;
};
class StorageBuffer : public OGLStreamBuffer {
public:
explicit StorageBuffer(GLenum target) : OGLStreamBuffer(target) {}
~StorageBuffer() override;
private:
void Create(size_t size, size_t sync_subdivide) override;
void Release() override;
std::pair<u8*, GLintptr> Map(size_t size, size_t alignment) override;
void Unmap() override;
struct Fence {
OGLSync sync;
size_t offset;
};
std::deque<Fence> head;
std::deque<Fence> tail;
u8* mapped_ptr;
};
OGLStreamBuffer::OGLStreamBuffer(GLenum target) {
gl_target = target;
}
GLuint OGLStreamBuffer::GetHandle() const {
return gl_buffer.handle;
}
std::unique_ptr<OGLStreamBuffer> OGLStreamBuffer::MakeBuffer(bool storage_buffer, GLenum target) {
if (storage_buffer) {
return std::make_unique<StorageBuffer>(target);
}
return std::make_unique<OrphanBuffer>(target);
}
OrphanBuffer::~OrphanBuffer() {
Release();
}
void OrphanBuffer::Create(size_t size, size_t /*sync_subdivide*/) {
buffer_pos = 0;
buffer_size = size;
data.resize(buffer_size);
if (gl_buffer.handle == 0) {
gl_buffer.Create();
glBindBuffer(gl_target, gl_buffer.handle);
}
glBufferData(gl_target, static_cast<GLsizeiptr>(buffer_size), nullptr, GL_STREAM_DRAW);
}
void OrphanBuffer::Release() {
gl_buffer.Release();
}
std::pair<u8*, GLintptr> OrphanBuffer::Map(size_t size, size_t alignment) {
buffer_pos = Common::AlignUp(buffer_pos, alignment);
if (buffer_pos + size > buffer_size) {
Create(std::max(buffer_size, size), 0);
}
mapped_size = size;
return std::make_pair(&data[buffer_pos], static_cast<GLintptr>(buffer_pos));
}
void OrphanBuffer::Unmap() {
glBufferSubData(gl_target, static_cast<GLintptr>(buffer_pos),
static_cast<GLsizeiptr>(mapped_size), &data[buffer_pos]);
buffer_pos += mapped_size;
}
StorageBuffer::~StorageBuffer() {
Release();
}
void StorageBuffer::Create(size_t size, size_t sync_subdivide) {
if (gl_buffer.handle != 0)
return;
buffer_pos = 0;
buffer_size = size;
buffer_sync_subdivide = std::max<size_t>(sync_subdivide, 1);
gl_buffer.Create();
glBindBuffer(gl_target, gl_buffer.handle);
glBufferStorage(gl_target, static_cast<GLsizeiptr>(buffer_size), nullptr,
GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT);
mapped_ptr = reinterpret_cast<u8*>(
glMapBufferRange(gl_target, 0, static_cast<GLsizeiptr>(buffer_size),
GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_FLUSH_EXPLICIT_BIT));
}
void StorageBuffer::Release() {
if (gl_buffer.handle == 0)
return;
glUnmapBuffer(gl_target);
gl_buffer.Release();
head.clear();
tail.clear();
}
std::pair<u8*, GLintptr> StorageBuffer::Map(size_t size, size_t alignment) {
ASSERT(size <= buffer_size);
OGLSync sync;
buffer_pos = Common::AlignUp(buffer_pos, alignment);
size_t effective_offset = Common::AlignDown(buffer_pos, buffer_sync_subdivide);
if (!head.empty() &&
(effective_offset > head.back().offset || buffer_pos + size > buffer_size)) {
ASSERT(head.back().sync.handle == 0);
head.back().sync.Create();
}
if (buffer_pos + size > buffer_size) {
if (!tail.empty()) {
std::swap(sync, tail.back().sync);
tail.clear();
}
std::swap(tail, head);
buffer_pos = 0;
effective_offset = 0;
}
while (!tail.empty() && buffer_pos + size > tail.front().offset) {
std::swap(sync, tail.front().sync);
tail.pop_front();
}
if (sync.handle != 0) {
glClientWaitSync(sync.handle, GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED);
sync.Release();
}
if (head.empty() || effective_offset > head.back().offset) {
head.emplace_back();
head.back().offset = effective_offset;
}
mapped_size = size;
return std::make_pair(&mapped_ptr[buffer_pos], static_cast<GLintptr>(buffer_pos));
}
void StorageBuffer::Unmap() {
glFlushMappedBufferRange(gl_target, static_cast<GLintptr>(buffer_pos),
static_cast<GLsizeiptr>(mapped_size));
buffer_pos += mapped_size;
}

View File

@@ -0,0 +1,34 @@
// Copyright 2018 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <memory>
#include <glad/glad.h>
#include "common/common_types.h"
#include "video_core/renderer_opengl/gl_resource_manager.h"
class OGLStreamBuffer : private NonCopyable {
public:
explicit OGLStreamBuffer(GLenum target);
virtual ~OGLStreamBuffer() = default;
public:
static std::unique_ptr<OGLStreamBuffer> MakeBuffer(bool storage_buffer, GLenum target);
virtual void Create(size_t size, size_t sync_subdivide) = 0;
virtual void Release() {}
GLuint GetHandle() const;
virtual std::pair<u8*, GLintptr> Map(size_t size, size_t alignment) = 0;
virtual void Unmap() = 0;
protected:
OGLBuffer gl_buffer;
GLenum gl_target;
size_t buffer_pos = 0;
size_t buffer_size = 0;
size_t buffer_sync_subdivide = 0;
size_t mapped_size = 0;
};

View File

@@ -20,6 +20,7 @@
#include "core/settings.h"
#include "core/tracer/recorder.h"
#include "video_core/renderer_opengl/renderer_opengl.h"
#include "video_core/utils.h"
#include "video_core/video_core.h"
static const char vertex_shader[] = R"(
@@ -98,22 +99,22 @@ RendererOpenGL::RendererOpenGL() = default;
RendererOpenGL::~RendererOpenGL() = default;
/// Swap buffers (render frame)
void RendererOpenGL::SwapBuffers(boost::optional<const FramebufferInfo&> framebuffer_info) {
void RendererOpenGL::SwapBuffers(boost::optional<const Tegra::FramebufferConfig&> framebuffer) {
// Maintain the rasterizer's state as a priority
OpenGLState prev_state = OpenGLState::GetCurState();
state.Apply();
if (framebuffer_info != boost::none) {
// If framebuffer_info is provided, reload it from memory to a texture
if (screen_info.texture.width != (GLsizei)framebuffer_info->width ||
screen_info.texture.height != (GLsizei)framebuffer_info->height ||
screen_info.texture.pixel_format != framebuffer_info->pixel_format) {
if (framebuffer != boost::none) {
// If framebuffer is provided, reload it from memory to a texture
if (screen_info.texture.width != (GLsizei)framebuffer->width ||
screen_info.texture.height != (GLsizei)framebuffer->height ||
screen_info.texture.pixel_format != framebuffer->pixel_format) {
// Reallocate texture if the framebuffer size has changed.
// This is expected to not happen very often and hence should not be a
// performance problem.
ConfigureFramebufferTexture(screen_info.texture, *framebuffer_info);
ConfigureFramebufferTexture(screen_info.texture, *framebuffer);
}
LoadFBToScreenInfo(*framebuffer_info, screen_info);
LoadFBToScreenInfo(*framebuffer, screen_info);
}
DrawScreens();
@@ -131,164 +132,59 @@ void RendererOpenGL::SwapBuffers(boost::optional<const FramebufferInfo&> framebu
RefreshRasterizerSetting();
}
static inline u32 MortonInterleave128(u32 x, u32 y) {
// 128x128 Z-Order coordinate from 2D coordinates
static constexpr u32 xlut[] = {
0x0000, 0x0001, 0x0002, 0x0003, 0x0008, 0x0009, 0x000a, 0x000b, 0x0040, 0x0041, 0x0042,
0x0043, 0x0048, 0x0049, 0x004a, 0x004b, 0x0800, 0x0801, 0x0802, 0x0803, 0x0808, 0x0809,
0x080a, 0x080b, 0x0840, 0x0841, 0x0842, 0x0843, 0x0848, 0x0849, 0x084a, 0x084b, 0x1000,
0x1001, 0x1002, 0x1003, 0x1008, 0x1009, 0x100a, 0x100b, 0x1040, 0x1041, 0x1042, 0x1043,
0x1048, 0x1049, 0x104a, 0x104b, 0x1800, 0x1801, 0x1802, 0x1803, 0x1808, 0x1809, 0x180a,
0x180b, 0x1840, 0x1841, 0x1842, 0x1843, 0x1848, 0x1849, 0x184a, 0x184b, 0x2000, 0x2001,
0x2002, 0x2003, 0x2008, 0x2009, 0x200a, 0x200b, 0x2040, 0x2041, 0x2042, 0x2043, 0x2048,
0x2049, 0x204a, 0x204b, 0x2800, 0x2801, 0x2802, 0x2803, 0x2808, 0x2809, 0x280a, 0x280b,
0x2840, 0x2841, 0x2842, 0x2843, 0x2848, 0x2849, 0x284a, 0x284b, 0x3000, 0x3001, 0x3002,
0x3003, 0x3008, 0x3009, 0x300a, 0x300b, 0x3040, 0x3041, 0x3042, 0x3043, 0x3048, 0x3049,
0x304a, 0x304b, 0x3800, 0x3801, 0x3802, 0x3803, 0x3808, 0x3809, 0x380a, 0x380b, 0x3840,
0x3841, 0x3842, 0x3843, 0x3848, 0x3849, 0x384a, 0x384b, 0x0000, 0x0001, 0x0002, 0x0003,
0x0008, 0x0009, 0x000a, 0x000b, 0x0040, 0x0041, 0x0042, 0x0043, 0x0048, 0x0049, 0x004a,
0x004b, 0x0800, 0x0801, 0x0802, 0x0803, 0x0808, 0x0809, 0x080a, 0x080b, 0x0840, 0x0841,
0x0842, 0x0843, 0x0848, 0x0849, 0x084a, 0x084b, 0x1000, 0x1001, 0x1002, 0x1003, 0x1008,
0x1009, 0x100a, 0x100b, 0x1040, 0x1041, 0x1042, 0x1043, 0x1048, 0x1049, 0x104a, 0x104b,
0x1800, 0x1801, 0x1802, 0x1803, 0x1808, 0x1809, 0x180a, 0x180b, 0x1840, 0x1841, 0x1842,
0x1843, 0x1848, 0x1849, 0x184a, 0x184b, 0x2000, 0x2001, 0x2002, 0x2003, 0x2008, 0x2009,
0x200a, 0x200b, 0x2040, 0x2041, 0x2042, 0x2043, 0x2048, 0x2049, 0x204a, 0x204b, 0x2800,
0x2801, 0x2802, 0x2803, 0x2808, 0x2809, 0x280a, 0x280b, 0x2840, 0x2841, 0x2842, 0x2843,
0x2848, 0x2849, 0x284a, 0x284b, 0x3000, 0x3001, 0x3002, 0x3003, 0x3008, 0x3009, 0x300a,
0x300b, 0x3040, 0x3041, 0x3042, 0x3043, 0x3048, 0x3049, 0x304a, 0x304b, 0x3800, 0x3801,
0x3802, 0x3803, 0x3808, 0x3809, 0x380a, 0x380b, 0x3840, 0x3841, 0x3842, 0x3843, 0x3848,
0x3849, 0x384a, 0x384b, 0x0000, 0x0001, 0x0002, 0x0003, 0x0008, 0x0009, 0x000a, 0x000b,
0x0040, 0x0041, 0x0042, 0x0043, 0x0048, 0x0049, 0x004a, 0x004b, 0x0800, 0x0801, 0x0802,
0x0803, 0x0808, 0x0809, 0x080a, 0x080b, 0x0840, 0x0841, 0x0842, 0x0843, 0x0848, 0x0849,
0x084a, 0x084b, 0x1000, 0x1001, 0x1002, 0x1003, 0x1008, 0x1009, 0x100a, 0x100b, 0x1040,
0x1041, 0x1042, 0x1043, 0x1048, 0x1049, 0x104a, 0x104b, 0x1800, 0x1801, 0x1802, 0x1803,
0x1808, 0x1809, 0x180a, 0x180b, 0x1840, 0x1841, 0x1842, 0x1843, 0x1848, 0x1849, 0x184a,
0x184b, 0x2000, 0x2001, 0x2002, 0x2003, 0x2008, 0x2009, 0x200a, 0x200b, 0x2040, 0x2041,
0x2042, 0x2043, 0x2048, 0x2049, 0x204a, 0x204b, 0x2800, 0x2801, 0x2802, 0x2803, 0x2808,
0x2809, 0x280a, 0x280b, 0x2840, 0x2841, 0x2842, 0x2843, 0x2848, 0x2849, 0x284a, 0x284b,
0x3000, 0x3001, 0x3002, 0x3003, 0x3008, 0x3009, 0x300a, 0x300b, 0x3040, 0x3041, 0x3042,
0x3043, 0x3048, 0x3049, 0x304a, 0x304b, 0x3800, 0x3801, 0x3802, 0x3803, 0x3808, 0x3809,
0x380a, 0x380b, 0x3840, 0x3841, 0x3842, 0x3843, 0x3848, 0x3849, 0x384a, 0x384b,
};
static constexpr u32 ylut[] = {
0x0000, 0x0004, 0x0010, 0x0014, 0x0020, 0x0024, 0x0030, 0x0034, 0x0080, 0x0084, 0x0090,
0x0094, 0x00a0, 0x00a4, 0x00b0, 0x00b4, 0x0100, 0x0104, 0x0110, 0x0114, 0x0120, 0x0124,
0x0130, 0x0134, 0x0180, 0x0184, 0x0190, 0x0194, 0x01a0, 0x01a4, 0x01b0, 0x01b4, 0x0200,
0x0204, 0x0210, 0x0214, 0x0220, 0x0224, 0x0230, 0x0234, 0x0280, 0x0284, 0x0290, 0x0294,
0x02a0, 0x02a4, 0x02b0, 0x02b4, 0x0300, 0x0304, 0x0310, 0x0314, 0x0320, 0x0324, 0x0330,
0x0334, 0x0380, 0x0384, 0x0390, 0x0394, 0x03a0, 0x03a4, 0x03b0, 0x03b4, 0x0400, 0x0404,
0x0410, 0x0414, 0x0420, 0x0424, 0x0430, 0x0434, 0x0480, 0x0484, 0x0490, 0x0494, 0x04a0,
0x04a4, 0x04b0, 0x04b4, 0x0500, 0x0504, 0x0510, 0x0514, 0x0520, 0x0524, 0x0530, 0x0534,
0x0580, 0x0584, 0x0590, 0x0594, 0x05a0, 0x05a4, 0x05b0, 0x05b4, 0x0600, 0x0604, 0x0610,
0x0614, 0x0620, 0x0624, 0x0630, 0x0634, 0x0680, 0x0684, 0x0690, 0x0694, 0x06a0, 0x06a4,
0x06b0, 0x06b4, 0x0700, 0x0704, 0x0710, 0x0714, 0x0720, 0x0724, 0x0730, 0x0734, 0x0780,
0x0784, 0x0790, 0x0794, 0x07a0, 0x07a4, 0x07b0, 0x07b4, 0x0000, 0x0004, 0x0010, 0x0014,
0x0020, 0x0024, 0x0030, 0x0034, 0x0080, 0x0084, 0x0090, 0x0094, 0x00a0, 0x00a4, 0x00b0,
0x00b4, 0x0100, 0x0104, 0x0110, 0x0114, 0x0120, 0x0124, 0x0130, 0x0134, 0x0180, 0x0184,
0x0190, 0x0194, 0x01a0, 0x01a4, 0x01b0, 0x01b4, 0x0200, 0x0204, 0x0210, 0x0214, 0x0220,
0x0224, 0x0230, 0x0234, 0x0280, 0x0284, 0x0290, 0x0294, 0x02a0, 0x02a4, 0x02b0, 0x02b4,
0x0300, 0x0304, 0x0310, 0x0314, 0x0320, 0x0324, 0x0330, 0x0334, 0x0380, 0x0384, 0x0390,
0x0394, 0x03a0, 0x03a4, 0x03b0, 0x03b4, 0x0400, 0x0404, 0x0410, 0x0414, 0x0420, 0x0424,
0x0430, 0x0434, 0x0480, 0x0484, 0x0490, 0x0494, 0x04a0, 0x04a4, 0x04b0, 0x04b4, 0x0500,
0x0504, 0x0510, 0x0514, 0x0520, 0x0524, 0x0530, 0x0534, 0x0580, 0x0584, 0x0590, 0x0594,
0x05a0, 0x05a4, 0x05b0, 0x05b4, 0x0600, 0x0604, 0x0610, 0x0614, 0x0620, 0x0624, 0x0630,
0x0634, 0x0680, 0x0684, 0x0690, 0x0694, 0x06a0, 0x06a4, 0x06b0, 0x06b4, 0x0700, 0x0704,
0x0710, 0x0714, 0x0720, 0x0724, 0x0730, 0x0734, 0x0780, 0x0784, 0x0790, 0x0794, 0x07a0,
0x07a4, 0x07b0, 0x07b4, 0x0000, 0x0004, 0x0010, 0x0014, 0x0020, 0x0024, 0x0030, 0x0034,
0x0080, 0x0084, 0x0090, 0x0094, 0x00a0, 0x00a4, 0x00b0, 0x00b4, 0x0100, 0x0104, 0x0110,
0x0114, 0x0120, 0x0124, 0x0130, 0x0134, 0x0180, 0x0184, 0x0190, 0x0194, 0x01a0, 0x01a4,
0x01b0, 0x01b4, 0x0200, 0x0204, 0x0210, 0x0214, 0x0220, 0x0224, 0x0230, 0x0234, 0x0280,
0x0284, 0x0290, 0x0294, 0x02a0, 0x02a4, 0x02b0, 0x02b4, 0x0300, 0x0304, 0x0310, 0x0314,
0x0320, 0x0324, 0x0330, 0x0334, 0x0380, 0x0384, 0x0390, 0x0394, 0x03a0, 0x03a4, 0x03b0,
0x03b4, 0x0400, 0x0404, 0x0410, 0x0414, 0x0420, 0x0424, 0x0430, 0x0434, 0x0480, 0x0484,
0x0490, 0x0494, 0x04a0, 0x04a4, 0x04b0, 0x04b4, 0x0500, 0x0504, 0x0510, 0x0514, 0x0520,
0x0524, 0x0530, 0x0534, 0x0580, 0x0584, 0x0590, 0x0594, 0x05a0, 0x05a4, 0x05b0, 0x05b4,
0x0600, 0x0604, 0x0610, 0x0614, 0x0620, 0x0624, 0x0630, 0x0634, 0x0680, 0x0684, 0x0690,
0x0694, 0x06a0, 0x06a4, 0x06b0, 0x06b4, 0x0700, 0x0704, 0x0710, 0x0714, 0x0720, 0x0724,
0x0730, 0x0734, 0x0780, 0x0784, 0x0790, 0x0794, 0x07a0, 0x07a4, 0x07b0, 0x07b4,
};
return xlut[x % 128] + ylut[y % 128];
}
static inline u32 GetMortonOffset128(u32 x, u32 y, u32 bytes_per_pixel) {
// Calculates the offset of the position of the pixel in Morton order
// Framebuffer images are split into 128x128 tiles.
const unsigned int block_height = 128;
const unsigned int coarse_x = x & ~127;
u32 i = MortonInterleave128(x, y);
const unsigned int offset = coarse_x * block_height;
return (i + offset) * bytes_per_pixel;
}
static void MortonCopyPixels128(u32 width, u32 height, u32 bytes_per_pixel, u32 gl_bytes_per_pixel,
u8* morton_data, u8* gl_data, bool morton_to_gl) {
u8* data_ptrs[2];
for (unsigned y = 0; y < height; ++y) {
for (unsigned x = 0; x < width; ++x) {
const u32 coarse_y = y & ~127;
u32 morton_offset =
GetMortonOffset128(x, y, bytes_per_pixel) + coarse_y * width * bytes_per_pixel;
u32 gl_pixel_index = (x + (height - 1 - y) * width) * gl_bytes_per_pixel;
data_ptrs[morton_to_gl] = morton_data + morton_offset;
data_ptrs[!morton_to_gl] = &gl_data[gl_pixel_index];
memcpy(data_ptrs[0], data_ptrs[1], bytes_per_pixel);
}
}
}
/**
* Loads framebuffer from emulated memory into the active OpenGL texture.
*/
void RendererOpenGL::LoadFBToScreenInfo(const FramebufferInfo& framebuffer_info,
void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer,
ScreenInfo& screen_info) {
const u32 bpp{FramebufferInfo::BytesPerPixel(framebuffer_info.pixel_format)};
const u32 size_in_bytes{framebuffer_info.stride * framebuffer_info.height * bpp};
const u32 bytes_per_pixel{Tegra::FramebufferConfig::BytesPerPixel(framebuffer.pixel_format)};
const u64 size_in_bytes{framebuffer.stride * framebuffer.height * bytes_per_pixel};
const VAddr framebuffer_addr{framebuffer.address + framebuffer.offset};
MortonCopyPixels128(framebuffer_info.width, framebuffer_info.height, bpp, 4,
Memory::GetPointer(framebuffer_info.address), gl_framebuffer_data.data(),
true);
// TODO(bunnei): The framebuffer region should only be invalidated if it is written to, not
// every frame. When we find the right place for this, the below line can be removed.
Memory::RasterizerFlushVirtualRegion(framebuffer_addr, size_in_bytes,
Memory::FlushMode::Invalidate);
LOG_TRACE(Render_OpenGL, "0x%08x bytes from 0x%llx(%dx%d), fmt %x", size_in_bytes,
framebuffer_info.address, framebuffer_info.width, framebuffer_info.height,
(int)framebuffer_info.pixel_format);
// Framebuffer orientation handling
framebuffer_transform_flags = framebuffer.transform_flags;
// Ensure no bad interactions with GL_UNPACK_ALIGNMENT, which by default
// only allows rows to have a memory alignement of 4.
ASSERT(framebuffer_info.stride % 4 == 0);
ASSERT(framebuffer.stride % 4 == 0);
framebuffer_flip_vertical = framebuffer_info.flip_vertical;
if (!Rasterizer()->AccelerateDisplay(framebuffer, framebuffer_addr, framebuffer.stride,
screen_info)) {
// Reset the screen info's display texture to its own permanent texture
screen_info.display_texture = screen_info.texture.resource.handle;
screen_info.display_texcoords = MathUtil::Rectangle<float>(0.f, 0.f, 1.f, 1.f);
// Reset the screen info's display texture to its own permanent texture
screen_info.display_texture = screen_info.texture.resource.handle;
screen_info.display_texcoords = MathUtil::Rectangle<float>(0.f, 0.f, 1.f, 1.f);
Rasterizer()->FlushRegion(framebuffer_addr, size_in_bytes);
// Memory::RasterizerFlushRegion(framebuffer_info.address, size_in_bytes);
VideoCore::MortonCopyPixels128(framebuffer.width, framebuffer.height, bytes_per_pixel, 4,
Memory::GetPointer(framebuffer_addr),
gl_framebuffer_data.data(), true);
state.texture_units[0].texture_2d = screen_info.texture.resource.handle;
state.Apply();
state.texture_units[0].texture_2d = screen_info.texture.resource.handle;
state.Apply();
glActiveTexture(GL_TEXTURE0);
glPixelStorei(GL_UNPACK_ROW_LENGTH, (GLint)framebuffer_info.stride);
glActiveTexture(GL_TEXTURE0);
glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(framebuffer.stride));
// Update existing texture
// TODO: Test what happens on hardware when you change the framebuffer dimensions so that
// they differ from the LCD resolution.
// TODO: Applications could theoretically crash Citra here by specifying too large
// framebuffer sizes. We should make sure that this cannot happen.
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, framebuffer_info.width, framebuffer_info.height,
screen_info.texture.gl_format, screen_info.texture.gl_type,
gl_framebuffer_data.data());
// Update existing texture
// TODO: Test what happens on hardware when you change the framebuffer dimensions so that
// they differ from the LCD resolution.
// TODO: Applications could theoretically crash yuzu here by specifying too large
// framebuffer sizes. We should make sure that this cannot happen.
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, framebuffer.width, framebuffer.height,
screen_info.texture.gl_format, screen_info.texture.gl_type,
gl_framebuffer_data.data());
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
state.texture_units[0].texture_2d = 0;
state.Apply();
state.texture_units[0].texture_2d = 0;
state.Apply();
}
}
/**
@@ -318,7 +214,7 @@ void RendererOpenGL::InitOpenGLObjects() {
0.0f);
// Link shaders and get variable locations
shader.Create(vertex_shader, fragment_shader);
shader.Create(vertex_shader, nullptr, fragment_shader);
state.draw.shader_program = shader.handle;
state.Apply();
uniform_modelview_matrix = glGetUniformLocation(shader.handle, "modelview_matrix");
@@ -372,14 +268,14 @@ void RendererOpenGL::InitOpenGLObjects() {
}
void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
const FramebufferInfo& framebuffer_info) {
const Tegra::FramebufferConfig& framebuffer) {
texture.width = framebuffer_info.width;
texture.height = framebuffer_info.height;
texture.width = framebuffer.width;
texture.height = framebuffer.height;
GLint internal_format;
switch (framebuffer_info.pixel_format) {
case FramebufferInfo::PixelFormat::ABGR8:
switch (framebuffer.pixel_format) {
case Tegra::FramebufferConfig::PixelFormat::ABGR8:
// Use RGBA8 and swap in the fragment shader
internal_format = GL_RGBA;
texture.gl_format = GL_RGBA;
@@ -404,8 +300,19 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
void RendererOpenGL::DrawSingleScreen(const ScreenInfo& screen_info, float x, float y, float w,
float h) {
const auto& texcoords = screen_info.display_texcoords;
const auto& left = framebuffer_flip_vertical ? texcoords.right : texcoords.left;
const auto& right = framebuffer_flip_vertical ? texcoords.left : texcoords.right;
auto left = texcoords.left;
auto right = texcoords.right;
if (framebuffer_transform_flags != Tegra::FramebufferConfig::TransformFlags::Unset)
if (framebuffer_transform_flags == Tegra::FramebufferConfig::TransformFlags::FlipV) {
// Flip the framebuffer vertically
left = texcoords.right;
right = texcoords.left;
} else {
// Other transformations are unsupported
LOG_CRITICAL(HW_GPU, "unsupported framebuffer_transform_flags=%d",
framebuffer_transform_flags);
UNIMPLEMENTED();
}
std::array<ScreenRectVertex, 4> vertices = {{
ScreenRectVertex(x, y, texcoords.top, right),

View File

@@ -21,7 +21,7 @@ struct TextureInfo {
GLsizei height;
GLenum gl_format;
GLenum gl_type;
RendererBase::FramebufferInfo::PixelFormat pixel_format;
Tegra::FramebufferConfig::PixelFormat pixel_format;
};
/// Structure used for storing information about the display target for each 3DS screen
@@ -37,7 +37,7 @@ public:
~RendererOpenGL() override;
/// Swap buffers (render frame)
void SwapBuffers(boost::optional<const FramebufferInfo&> framebuffer_info) override;
void SwapBuffers(boost::optional<const Tegra::FramebufferConfig&> framebuffer) override;
/**
* Set the emulator window to use for renderer
@@ -53,13 +53,14 @@ public:
private:
void InitOpenGLObjects();
void ConfigureFramebufferTexture(TextureInfo& texture, const FramebufferInfo& framebuffer_info);
void ConfigureFramebufferTexture(TextureInfo& texture,
const Tegra::FramebufferConfig& framebuffer);
void DrawScreens();
void DrawSingleScreen(const ScreenInfo& screen_info, float x, float y, float w, float h);
void UpdateFramerate();
// Loads framebuffer from emulated memory into the display information structure
void LoadFBToScreenInfo(const FramebufferInfo& framebuffer_info, ScreenInfo& screen_info);
void LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer, ScreenInfo& screen_info);
// Fills active OpenGL texture with the given RGBA color.
void LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, u8 color_a,
const TextureInfo& texture);
@@ -87,6 +88,6 @@ private:
GLuint attrib_position;
GLuint attrib_tex_coord;
/// Flips the framebuffer vertically when true
bool framebuffer_flip_vertical;
/// Used for transforming the framebuffer orientation
Tegra::FramebufferConfig::TransformFlags framebuffer_transform_flags;
};

View File

@@ -49,4 +49,116 @@ static inline u32 GetMortonOffset(u32 x, u32 y, u32 bytes_per_pixel) {
return (i + offset) * bytes_per_pixel;
}
static inline u32 MortonInterleave128(u32 x, u32 y) {
// 128x128 Z-Order coordinate from 2D coordinates
static constexpr u32 xlut[] = {
0x0000, 0x0001, 0x0002, 0x0003, 0x0008, 0x0009, 0x000a, 0x000b, 0x0040, 0x0041, 0x0042,
0x0043, 0x0048, 0x0049, 0x004a, 0x004b, 0x0800, 0x0801, 0x0802, 0x0803, 0x0808, 0x0809,
0x080a, 0x080b, 0x0840, 0x0841, 0x0842, 0x0843, 0x0848, 0x0849, 0x084a, 0x084b, 0x1000,
0x1001, 0x1002, 0x1003, 0x1008, 0x1009, 0x100a, 0x100b, 0x1040, 0x1041, 0x1042, 0x1043,
0x1048, 0x1049, 0x104a, 0x104b, 0x1800, 0x1801, 0x1802, 0x1803, 0x1808, 0x1809, 0x180a,
0x180b, 0x1840, 0x1841, 0x1842, 0x1843, 0x1848, 0x1849, 0x184a, 0x184b, 0x2000, 0x2001,
0x2002, 0x2003, 0x2008, 0x2009, 0x200a, 0x200b, 0x2040, 0x2041, 0x2042, 0x2043, 0x2048,
0x2049, 0x204a, 0x204b, 0x2800, 0x2801, 0x2802, 0x2803, 0x2808, 0x2809, 0x280a, 0x280b,
0x2840, 0x2841, 0x2842, 0x2843, 0x2848, 0x2849, 0x284a, 0x284b, 0x3000, 0x3001, 0x3002,
0x3003, 0x3008, 0x3009, 0x300a, 0x300b, 0x3040, 0x3041, 0x3042, 0x3043, 0x3048, 0x3049,
0x304a, 0x304b, 0x3800, 0x3801, 0x3802, 0x3803, 0x3808, 0x3809, 0x380a, 0x380b, 0x3840,
0x3841, 0x3842, 0x3843, 0x3848, 0x3849, 0x384a, 0x384b, 0x0000, 0x0001, 0x0002, 0x0003,
0x0008, 0x0009, 0x000a, 0x000b, 0x0040, 0x0041, 0x0042, 0x0043, 0x0048, 0x0049, 0x004a,
0x004b, 0x0800, 0x0801, 0x0802, 0x0803, 0x0808, 0x0809, 0x080a, 0x080b, 0x0840, 0x0841,
0x0842, 0x0843, 0x0848, 0x0849, 0x084a, 0x084b, 0x1000, 0x1001, 0x1002, 0x1003, 0x1008,
0x1009, 0x100a, 0x100b, 0x1040, 0x1041, 0x1042, 0x1043, 0x1048, 0x1049, 0x104a, 0x104b,
0x1800, 0x1801, 0x1802, 0x1803, 0x1808, 0x1809, 0x180a, 0x180b, 0x1840, 0x1841, 0x1842,
0x1843, 0x1848, 0x1849, 0x184a, 0x184b, 0x2000, 0x2001, 0x2002, 0x2003, 0x2008, 0x2009,
0x200a, 0x200b, 0x2040, 0x2041, 0x2042, 0x2043, 0x2048, 0x2049, 0x204a, 0x204b, 0x2800,
0x2801, 0x2802, 0x2803, 0x2808, 0x2809, 0x280a, 0x280b, 0x2840, 0x2841, 0x2842, 0x2843,
0x2848, 0x2849, 0x284a, 0x284b, 0x3000, 0x3001, 0x3002, 0x3003, 0x3008, 0x3009, 0x300a,
0x300b, 0x3040, 0x3041, 0x3042, 0x3043, 0x3048, 0x3049, 0x304a, 0x304b, 0x3800, 0x3801,
0x3802, 0x3803, 0x3808, 0x3809, 0x380a, 0x380b, 0x3840, 0x3841, 0x3842, 0x3843, 0x3848,
0x3849, 0x384a, 0x384b, 0x0000, 0x0001, 0x0002, 0x0003, 0x0008, 0x0009, 0x000a, 0x000b,
0x0040, 0x0041, 0x0042, 0x0043, 0x0048, 0x0049, 0x004a, 0x004b, 0x0800, 0x0801, 0x0802,
0x0803, 0x0808, 0x0809, 0x080a, 0x080b, 0x0840, 0x0841, 0x0842, 0x0843, 0x0848, 0x0849,
0x084a, 0x084b, 0x1000, 0x1001, 0x1002, 0x1003, 0x1008, 0x1009, 0x100a, 0x100b, 0x1040,
0x1041, 0x1042, 0x1043, 0x1048, 0x1049, 0x104a, 0x104b, 0x1800, 0x1801, 0x1802, 0x1803,
0x1808, 0x1809, 0x180a, 0x180b, 0x1840, 0x1841, 0x1842, 0x1843, 0x1848, 0x1849, 0x184a,
0x184b, 0x2000, 0x2001, 0x2002, 0x2003, 0x2008, 0x2009, 0x200a, 0x200b, 0x2040, 0x2041,
0x2042, 0x2043, 0x2048, 0x2049, 0x204a, 0x204b, 0x2800, 0x2801, 0x2802, 0x2803, 0x2808,
0x2809, 0x280a, 0x280b, 0x2840, 0x2841, 0x2842, 0x2843, 0x2848, 0x2849, 0x284a, 0x284b,
0x3000, 0x3001, 0x3002, 0x3003, 0x3008, 0x3009, 0x300a, 0x300b, 0x3040, 0x3041, 0x3042,
0x3043, 0x3048, 0x3049, 0x304a, 0x304b, 0x3800, 0x3801, 0x3802, 0x3803, 0x3808, 0x3809,
0x380a, 0x380b, 0x3840, 0x3841, 0x3842, 0x3843, 0x3848, 0x3849, 0x384a, 0x384b,
};
static constexpr u32 ylut[] = {
0x0000, 0x0004, 0x0010, 0x0014, 0x0020, 0x0024, 0x0030, 0x0034, 0x0080, 0x0084, 0x0090,
0x0094, 0x00a0, 0x00a4, 0x00b0, 0x00b4, 0x0100, 0x0104, 0x0110, 0x0114, 0x0120, 0x0124,
0x0130, 0x0134, 0x0180, 0x0184, 0x0190, 0x0194, 0x01a0, 0x01a4, 0x01b0, 0x01b4, 0x0200,
0x0204, 0x0210, 0x0214, 0x0220, 0x0224, 0x0230, 0x0234, 0x0280, 0x0284, 0x0290, 0x0294,
0x02a0, 0x02a4, 0x02b0, 0x02b4, 0x0300, 0x0304, 0x0310, 0x0314, 0x0320, 0x0324, 0x0330,
0x0334, 0x0380, 0x0384, 0x0390, 0x0394, 0x03a0, 0x03a4, 0x03b0, 0x03b4, 0x0400, 0x0404,
0x0410, 0x0414, 0x0420, 0x0424, 0x0430, 0x0434, 0x0480, 0x0484, 0x0490, 0x0494, 0x04a0,
0x04a4, 0x04b0, 0x04b4, 0x0500, 0x0504, 0x0510, 0x0514, 0x0520, 0x0524, 0x0530, 0x0534,
0x0580, 0x0584, 0x0590, 0x0594, 0x05a0, 0x05a4, 0x05b0, 0x05b4, 0x0600, 0x0604, 0x0610,
0x0614, 0x0620, 0x0624, 0x0630, 0x0634, 0x0680, 0x0684, 0x0690, 0x0694, 0x06a0, 0x06a4,
0x06b0, 0x06b4, 0x0700, 0x0704, 0x0710, 0x0714, 0x0720, 0x0724, 0x0730, 0x0734, 0x0780,
0x0784, 0x0790, 0x0794, 0x07a0, 0x07a4, 0x07b0, 0x07b4, 0x0000, 0x0004, 0x0010, 0x0014,
0x0020, 0x0024, 0x0030, 0x0034, 0x0080, 0x0084, 0x0090, 0x0094, 0x00a0, 0x00a4, 0x00b0,
0x00b4, 0x0100, 0x0104, 0x0110, 0x0114, 0x0120, 0x0124, 0x0130, 0x0134, 0x0180, 0x0184,
0x0190, 0x0194, 0x01a0, 0x01a4, 0x01b0, 0x01b4, 0x0200, 0x0204, 0x0210, 0x0214, 0x0220,
0x0224, 0x0230, 0x0234, 0x0280, 0x0284, 0x0290, 0x0294, 0x02a0, 0x02a4, 0x02b0, 0x02b4,
0x0300, 0x0304, 0x0310, 0x0314, 0x0320, 0x0324, 0x0330, 0x0334, 0x0380, 0x0384, 0x0390,
0x0394, 0x03a0, 0x03a4, 0x03b0, 0x03b4, 0x0400, 0x0404, 0x0410, 0x0414, 0x0420, 0x0424,
0x0430, 0x0434, 0x0480, 0x0484, 0x0490, 0x0494, 0x04a0, 0x04a4, 0x04b0, 0x04b4, 0x0500,
0x0504, 0x0510, 0x0514, 0x0520, 0x0524, 0x0530, 0x0534, 0x0580, 0x0584, 0x0590, 0x0594,
0x05a0, 0x05a4, 0x05b0, 0x05b4, 0x0600, 0x0604, 0x0610, 0x0614, 0x0620, 0x0624, 0x0630,
0x0634, 0x0680, 0x0684, 0x0690, 0x0694, 0x06a0, 0x06a4, 0x06b0, 0x06b4, 0x0700, 0x0704,
0x0710, 0x0714, 0x0720, 0x0724, 0x0730, 0x0734, 0x0780, 0x0784, 0x0790, 0x0794, 0x07a0,
0x07a4, 0x07b0, 0x07b4, 0x0000, 0x0004, 0x0010, 0x0014, 0x0020, 0x0024, 0x0030, 0x0034,
0x0080, 0x0084, 0x0090, 0x0094, 0x00a0, 0x00a4, 0x00b0, 0x00b4, 0x0100, 0x0104, 0x0110,
0x0114, 0x0120, 0x0124, 0x0130, 0x0134, 0x0180, 0x0184, 0x0190, 0x0194, 0x01a0, 0x01a4,
0x01b0, 0x01b4, 0x0200, 0x0204, 0x0210, 0x0214, 0x0220, 0x0224, 0x0230, 0x0234, 0x0280,
0x0284, 0x0290, 0x0294, 0x02a0, 0x02a4, 0x02b0, 0x02b4, 0x0300, 0x0304, 0x0310, 0x0314,
0x0320, 0x0324, 0x0330, 0x0334, 0x0380, 0x0384, 0x0390, 0x0394, 0x03a0, 0x03a4, 0x03b0,
0x03b4, 0x0400, 0x0404, 0x0410, 0x0414, 0x0420, 0x0424, 0x0430, 0x0434, 0x0480, 0x0484,
0x0490, 0x0494, 0x04a0, 0x04a4, 0x04b0, 0x04b4, 0x0500, 0x0504, 0x0510, 0x0514, 0x0520,
0x0524, 0x0530, 0x0534, 0x0580, 0x0584, 0x0590, 0x0594, 0x05a0, 0x05a4, 0x05b0, 0x05b4,
0x0600, 0x0604, 0x0610, 0x0614, 0x0620, 0x0624, 0x0630, 0x0634, 0x0680, 0x0684, 0x0690,
0x0694, 0x06a0, 0x06a4, 0x06b0, 0x06b4, 0x0700, 0x0704, 0x0710, 0x0714, 0x0720, 0x0724,
0x0730, 0x0734, 0x0780, 0x0784, 0x0790, 0x0794, 0x07a0, 0x07a4, 0x07b0, 0x07b4,
};
return xlut[x % 128] + ylut[y % 128];
}
static inline u32 GetMortonOffset128(u32 x, u32 y, u32 bytes_per_pixel) {
// Calculates the offset of the position of the pixel in Morton order
// Framebuffer images are split into 128x128 tiles.
const unsigned int block_height = 128;
const unsigned int coarse_x = x & ~127;
u32 i = MortonInterleave128(x, y);
const unsigned int offset = coarse_x * block_height;
return (i + offset) * bytes_per_pixel;
}
static inline void MortonCopyPixels128(u32 width, u32 height, u32 bytes_per_pixel,
u32 gl_bytes_per_pixel, u8* morton_data, u8* gl_data,
bool morton_to_gl) {
u8* data_ptrs[2];
for (unsigned y = 0; y < height; ++y) {
for (unsigned x = 0; x < width; ++x) {
const u32 coarse_y = y & ~127;
u32 morton_offset =
GetMortonOffset128(x, y, bytes_per_pixel) + coarse_y * width * bytes_per_pixel;
u32 gl_pixel_index = (x + (height - 1 - y) * width) * gl_bytes_per_pixel;
data_ptrs[morton_to_gl] = morton_data + morton_offset;
data_ptrs[!morton_to_gl] = &gl_data[gl_pixel_index];
memcpy(data_ptrs[0], data_ptrs[1], bytes_per_pixel);
}
}
}
} // namespace VideoCore

View File

@@ -15,6 +15,8 @@ class RendererBase;
namespace VideoCore {
enum class Renderer { Software, OpenGL };
extern std::unique_ptr<RendererBase> g_renderer; ///< Renderer plugin
extern EmuWindow* g_emu_window; ///< Emu window

View File

@@ -150,8 +150,8 @@ QString WaitTreeThread::GetText() const {
case THREADSTATUS_READY:
status = tr("ready");
break;
case THREADSTATUS_WAIT_ARB:
status = tr("waiting for address 0x%1").arg(thread.wait_address, 8, 16, QLatin1Char('0'));
case THREADSTATUS_WAIT_HLE_EVENT:
status = tr("waiting for HLE return");
break;
case THREADSTATUS_WAIT_SLEEP:
status = tr("sleeping");
@@ -180,7 +180,7 @@ QColor WaitTreeThread::GetColor() const {
return QColor(Qt::GlobalColor::darkGreen);
case THREADSTATUS_READY:
return QColor(Qt::GlobalColor::darkBlue);
case THREADSTATUS_WAIT_ARB:
case THREADSTATUS_WAIT_HLE_EVENT:
return QColor(Qt::GlobalColor::darkRed);
case THREADSTATUS_WAIT_SLEEP:
return QColor(Qt::GlobalColor::darkYellow);

View File

@@ -18,7 +18,6 @@
#include "common/logging/log.h"
#include "common/logging/text_formatter.h"
#include "common/microprofile.h"
#include "common/platform.h"
#include "common/scm_rev.h"
#include "common/scope_exit.h"
#include "common/string_util.h"

Some files were not shown because too many files have changed in this diff Show More