mirror of
https://github.com/citra-emu/citra.git
synced 2024-11-22 06:30:05 +00:00
Rasterizer cache refactor (#6375)
* rasterizer_cache: Remove custom texture code * It's a hacky buggy mess, will be reimplemented later when the cache is in a better state * rasterizer_cache: Refactor surface upload/download * Switch to the texture_codec header which was written as part of the vulkan backend by steveice and me * Move most of the upload logic to the rasterizer cache and out of the surface object * Scaled uploads/downloads have been disabled for now since they require more runtime infrastructure * rasterizer_cache: Refactor runtime interface * Remove aspect enum which is the same as SurfaceType * Replace Subresource with specific structures for each operation (blit/copy/clear). This mimics moderns APIs vulkan much better * Pass the surface to the runtime instead of the texture * Implement CopyTextures with glCopyImageSubData which is available on 4.3 and gles. This function also has an overload for cubes which will be removed later. * rasterizer_cache: Move texture allocation to the runtime * renderer_opengl: Remove TextureDownloaderES * It's overly compilcated and unused at the moment. Will be replaced with a simple compute shader in a later commit * rasterizer_cache: Split CachedSurface * This commit splits CachedSurface into two classes, SurfaceBase which contains the backend agnostic functions and Surface which is the opengl specific part * For now the cache uses the opengl surface directly and there are a few ugly casts with watchers, those will be taken care of when the template convertion and watcher removal are added respectively * rasterizer_cache: Move reinterpreters to the runtime * rasterizer_cache: Move some pixel format function to the cpp file * rasterizer_cache: Common texture acceleration functions * They don't contain any backend specific code so they shouldn't be duplicated * rasterizer_cache: Remove BlitSurfaces * It's better to prefer copy/blit in the caller anyway * rasterizer_cache: Only allocate needed levels * rasterizer_cache: Move texture runtime out of common dir * Also shorten the util header filename * surface_params: Cleanup code * Add more comments, organize it a bit etc * rasterizer_cache: Move texture filtering to the runtime * rasterizer_cache: Move to VideoCore * renderer_opengl: Reimplement scaled uploads/downloads * Instead of looking up for temporary textures, each allocation now contains both a scaled and unscaled handle This allows the scale operations to be done inside the surface object itself and improves performance in general * In particular the scaled download code has been expanded to use ARB_get_texture_sub_image when possible which is faster and more convenient than glReadPixels. The latter is still relevant for OpenGLES though. * Finally allocations are now given a handy debug name that can be viewed from renderdoc. * rasterizer_cache: Remove global state * gl_rasterizer: Abstract common draw operations to Framebuffer * This also allows to cache framebuffer objects instead of always swapping the textures, something that particularly benefits mali gpus * rasterizer_cache: Implement multi-level surfaces * With this commit the cache can now directly upload and use mipmaps without needing to sync them with watchers. By using native mimaps directly this also adds support for mipmap for cube * Texture cubes have also been updated to drop the watcher requirement * host_shaders: Add CMake integration for string shaders * Improves build time shader generation making it much less prone to errors. Also moves the presentation shaders here to avoid embedding them to the cpp file. * Texture filter shaders now make explicit use of uniform bindings for better vulkan compatibility * renderer_opengl: Emulate lod bias in the shader * This way opengles can emulate it correctly * gl_rasterizer: Respect GL_MAX_TEXTURE_BUFFER_SIZE * Older Bifrost Mali GPUs only support up to 64kb texture buffers. Citra would try to allocate a much larger buffer the first 64kb of which would work fine but after that the driver starts misbehaving and showing various graphical glitches * rasterizer_cache: Cleanup CopySurface * renderer_opengl: Keep frames synchronized when using a GPU debugger * rasterizer_cache: Rename Surface to SurfaceRef * Makes it clear that surface is a shared_ptr and not an object * rasterizer_cache: Cleanup * Move constructor to the top of the file * Move FindMatch to the top as well and remove the Invalid flag which was redudant; all FindMatch calls used it expect from MatchFlags::Copy which ignores it anyway * gl_texture_runtime: Make driver const * gl_texture_runtime: Fix RGB8 format handling * The texture_codec header, being written with vulkan in mind converts RGB8 to RGBA8. The backend wasn't adjusted to account for this though and treated the data as RGB8. * Also remove D16 convertions, both opengl and vulkan are required to support this format so these are not needed * gl_texture_runtime: Reduce state switches during FBO blits * glBlitFramebuffer is only affected by the scissor rectangle so just disable scissor testing instead of resetting our entire state * surface_params: Prevent texcopy that spans multiple levels * It would have failed before as well, with multi-level surfaces it triggers the assert though * renderer_opengl: Centralize texture filters * A lot of code is shared between the filters thus is makes it sense to centralize them * Also fix an issue with partial texture uploads * Address review comments * rasterizer_cache: Use leading return types * rasterizer_cache: Cleanup null checks * renderer_opengl: Add additional logging * externals: Actually downgrade glad * For some reason I missed adding the files to git * surface_params: Do not check for levels in exact match * Some games will try to use the base level of a multi level surface. Checking for levels forces another surface to be created and a copy to be made which is both unncessary and breaks custom textures --------- Co-authored-by: bunnei <bunneidev@gmail.com>
This commit is contained in:
parent
9414db4f65
commit
26d6f9d1c6
576
externals/glad/include/glad/glad.h
vendored
576
externals/glad/include/glad/glad.h
vendored
@ -1,26 +1,28 @@
|
||||
/*
|
||||
|
||||
OpenGL, OpenGL ES loader generated by glad 0.1.36 on Sun Mar 12 10:25:27 2023.
|
||||
OpenGL, OpenGL ES loader generated by glad 0.1.36 on Sat Apr 1 20:34:42 2023.
|
||||
|
||||
Language/Generator: C/C++
|
||||
Specification: gl
|
||||
APIs: gl=4.6, gles2=3.2
|
||||
APIs: gl=4.3, gles2=3.2
|
||||
Profile: core
|
||||
Extensions:
|
||||
GL_ARB_buffer_storage,
|
||||
GL_ARB_clear_texture,
|
||||
GL_ARB_get_texture_sub_image,
|
||||
GL_ARB_texture_compression_bptc,
|
||||
GL_EXT_buffer_storage,
|
||||
GL_EXT_clip_cull_distance
|
||||
GL_EXT_clip_cull_distance,
|
||||
GL_EXT_texture_compression_s3tc
|
||||
Loader: True
|
||||
Local files: False
|
||||
Omit khrplatform: False
|
||||
Reproducible: False
|
||||
|
||||
Commandline:
|
||||
--profile="core" --api="gl=4.6,gles2=3.2" --generator="c" --spec="gl" --extensions="GL_ARB_buffer_storage,GL_ARB_clear_texture,GL_ARB_get_texture_sub_image,GL_EXT_buffer_storage,GL_EXT_clip_cull_distance"
|
||||
--profile="core" --api="gl=4.3,gles2=3.2" --generator="c" --spec="gl" --extensions="GL_ARB_buffer_storage,GL_ARB_clear_texture,GL_ARB_get_texture_sub_image,GL_ARB_texture_compression_bptc,GL_EXT_buffer_storage,GL_EXT_clip_cull_distance,GL_EXT_texture_compression_s3tc"
|
||||
Online:
|
||||
https://glad.dav1d.de/#profile=core&language=c&specification=gl&loader=on&api=gl%3D4.6&api=gles2%3D3.2&extensions=GL_ARB_buffer_storage&extensions=GL_ARB_clear_texture&extensions=GL_ARB_get_texture_sub_image&extensions=GL_EXT_buffer_storage&extensions=GL_EXT_clip_cull_distance
|
||||
https://glad.dav1d.de/#profile=core&language=c&specification=gl&loader=on&api=gl%3D4.3&api=gles2%3D3.2&extensions=GL_ARB_buffer_storage&extensions=GL_ARB_clear_texture&extensions=GL_ARB_get_texture_sub_image&extensions=GL_ARB_texture_compression_bptc&extensions=GL_EXT_buffer_storage&extensions=GL_EXT_clip_cull_distance&extensions=GL_EXT_texture_compression_s3tc
|
||||
*/
|
||||
|
||||
|
||||
@ -1454,81 +1456,6 @@ typedef void (APIENTRY *GLVULKANPROCNV)(void);
|
||||
#define GL_DISPLAY_LIST 0x82E7
|
||||
#define GL_STACK_UNDERFLOW 0x0504
|
||||
#define GL_STACK_OVERFLOW 0x0503
|
||||
#define GL_MAX_VERTEX_ATTRIB_STRIDE 0x82E5
|
||||
#define GL_PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED 0x8221
|
||||
#define GL_TEXTURE_BUFFER_BINDING 0x8C2A
|
||||
#define GL_MAP_PERSISTENT_BIT 0x0040
|
||||
#define GL_MAP_COHERENT_BIT 0x0080
|
||||
#define GL_DYNAMIC_STORAGE_BIT 0x0100
|
||||
#define GL_CLIENT_STORAGE_BIT 0x0200
|
||||
#define GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT 0x00004000
|
||||
#define GL_BUFFER_IMMUTABLE_STORAGE 0x821F
|
||||
#define GL_BUFFER_STORAGE_FLAGS 0x8220
|
||||
#define GL_CLEAR_TEXTURE 0x9365
|
||||
#define GL_LOCATION_COMPONENT 0x934A
|
||||
#define GL_TRANSFORM_FEEDBACK_BUFFER_INDEX 0x934B
|
||||
#define GL_TRANSFORM_FEEDBACK_BUFFER_STRIDE 0x934C
|
||||
#define GL_QUERY_BUFFER 0x9192
|
||||
#define GL_QUERY_BUFFER_BARRIER_BIT 0x00008000
|
||||
#define GL_QUERY_BUFFER_BINDING 0x9193
|
||||
#define GL_QUERY_RESULT_NO_WAIT 0x9194
|
||||
#define GL_MIRROR_CLAMP_TO_EDGE 0x8743
|
||||
#define GL_CONTEXT_LOST 0x0507
|
||||
#define GL_NEGATIVE_ONE_TO_ONE 0x935E
|
||||
#define GL_ZERO_TO_ONE 0x935F
|
||||
#define GL_CLIP_ORIGIN 0x935C
|
||||
#define GL_CLIP_DEPTH_MODE 0x935D
|
||||
#define GL_QUERY_WAIT_INVERTED 0x8E17
|
||||
#define GL_QUERY_NO_WAIT_INVERTED 0x8E18
|
||||
#define GL_QUERY_BY_REGION_WAIT_INVERTED 0x8E19
|
||||
#define GL_QUERY_BY_REGION_NO_WAIT_INVERTED 0x8E1A
|
||||
#define GL_MAX_CULL_DISTANCES 0x82F9
|
||||
#define GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES 0x82FA
|
||||
#define GL_TEXTURE_TARGET 0x1006
|
||||
#define GL_QUERY_TARGET 0x82EA
|
||||
#define GL_GUILTY_CONTEXT_RESET 0x8253
|
||||
#define GL_INNOCENT_CONTEXT_RESET 0x8254
|
||||
#define GL_UNKNOWN_CONTEXT_RESET 0x8255
|
||||
#define GL_RESET_NOTIFICATION_STRATEGY 0x8256
|
||||
#define GL_LOSE_CONTEXT_ON_RESET 0x8252
|
||||
#define GL_NO_RESET_NOTIFICATION 0x8261
|
||||
#define GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT 0x00000004
|
||||
#define GL_COLOR_TABLE 0x80D0
|
||||
#define GL_POST_CONVOLUTION_COLOR_TABLE 0x80D1
|
||||
#define GL_POST_COLOR_MATRIX_COLOR_TABLE 0x80D2
|
||||
#define GL_PROXY_COLOR_TABLE 0x80D3
|
||||
#define GL_PROXY_POST_CONVOLUTION_COLOR_TABLE 0x80D4
|
||||
#define GL_PROXY_POST_COLOR_MATRIX_COLOR_TABLE 0x80D5
|
||||
#define GL_CONVOLUTION_1D 0x8010
|
||||
#define GL_CONVOLUTION_2D 0x8011
|
||||
#define GL_SEPARABLE_2D 0x8012
|
||||
#define GL_HISTOGRAM 0x8024
|
||||
#define GL_PROXY_HISTOGRAM 0x8025
|
||||
#define GL_MINMAX 0x802E
|
||||
#define GL_CONTEXT_RELEASE_BEHAVIOR 0x82FB
|
||||
#define GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH 0x82FC
|
||||
#define GL_SHADER_BINARY_FORMAT_SPIR_V 0x9551
|
||||
#define GL_SPIR_V_BINARY 0x9552
|
||||
#define GL_PARAMETER_BUFFER 0x80EE
|
||||
#define GL_PARAMETER_BUFFER_BINDING 0x80EF
|
||||
#define GL_CONTEXT_FLAG_NO_ERROR_BIT 0x00000008
|
||||
#define GL_VERTICES_SUBMITTED 0x82EE
|
||||
#define GL_PRIMITIVES_SUBMITTED 0x82EF
|
||||
#define GL_VERTEX_SHADER_INVOCATIONS 0x82F0
|
||||
#define GL_TESS_CONTROL_SHADER_PATCHES 0x82F1
|
||||
#define GL_TESS_EVALUATION_SHADER_INVOCATIONS 0x82F2
|
||||
#define GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED 0x82F3
|
||||
#define GL_FRAGMENT_SHADER_INVOCATIONS 0x82F4
|
||||
#define GL_COMPUTE_SHADER_INVOCATIONS 0x82F5
|
||||
#define GL_CLIPPING_INPUT_PRIMITIVES 0x82F6
|
||||
#define GL_CLIPPING_OUTPUT_PRIMITIVES 0x82F7
|
||||
#define GL_POLYGON_OFFSET_CLAMP 0x8E1B
|
||||
#define GL_SPIR_V_EXTENSIONS 0x9553
|
||||
#define GL_NUM_SPIR_V_EXTENSIONS 0x9554
|
||||
#define GL_TEXTURE_MAX_ANISOTROPY 0x84FE
|
||||
#define GL_MAX_TEXTURE_MAX_ANISOTROPY 0x84FF
|
||||
#define GL_TRANSFORM_FEEDBACK_OVERFLOW 0x82EC
|
||||
#define GL_TRANSFORM_FEEDBACK_STREAM_OVERFLOW 0x82ED
|
||||
#define GL_ALIASED_POINT_SIZE_RANGE 0x846D
|
||||
#define GL_RED_BITS 0x0D52
|
||||
#define GL_GREEN_BITS 0x0D53
|
||||
@ -1540,6 +1467,7 @@ typedef void (APIENTRY *GLVULKANPROCNV)(void);
|
||||
#define GL_LUMINANCE 0x1909
|
||||
#define GL_LUMINANCE_ALPHA 0x190A
|
||||
#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS 0x8CD9
|
||||
#define GL_MAX_VERTEX_ATTRIB_STRIDE 0x82E5
|
||||
#define GL_MULTISAMPLE_LINE_WIDTH_RANGE 0x9381
|
||||
#define GL_MULTISAMPLE_LINE_WIDTH_GRANULARITY 0x9382
|
||||
#define GL_MULTIPLY 0x9294
|
||||
@ -1558,6 +1486,16 @@ typedef void (APIENTRY *GLVULKANPROCNV)(void);
|
||||
#define GL_HSL_COLOR 0x92AF
|
||||
#define GL_HSL_LUMINOSITY 0x92B0
|
||||
#define GL_PRIMITIVE_BOUNDING_BOX 0x92BE
|
||||
#define GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT 0x00000004
|
||||
#define GL_LOSE_CONTEXT_ON_RESET 0x8252
|
||||
#define GL_GUILTY_CONTEXT_RESET 0x8253
|
||||
#define GL_INNOCENT_CONTEXT_RESET 0x8254
|
||||
#define GL_UNKNOWN_CONTEXT_RESET 0x8255
|
||||
#define GL_RESET_NOTIFICATION_STRATEGY 0x8256
|
||||
#define GL_NO_RESET_NOTIFICATION 0x8261
|
||||
#define GL_CONTEXT_LOST 0x0507
|
||||
#define GL_PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED 0x8221
|
||||
#define GL_TEXTURE_BUFFER_BINDING 0x8C2A
|
||||
#define GL_COMPRESSED_RGBA_ASTC_4x4 0x93B0
|
||||
#define GL_COMPRESSED_RGBA_ASTC_5x4 0x93B1
|
||||
#define GL_COMPRESSED_RGBA_ASTC_5x5 0x93B2
|
||||
@ -3342,423 +3280,6 @@ typedef void (APIENTRYP PFNGLGETPOINTERVPROC)(GLenum pname, void **params);
|
||||
GLAPI PFNGLGETPOINTERVPROC glad_glGetPointerv;
|
||||
#define glGetPointerv glad_glGetPointerv
|
||||
#endif
|
||||
#ifndef GL_VERSION_4_4
|
||||
#define GL_VERSION_4_4 1
|
||||
GLAPI int GLAD_GL_VERSION_4_4;
|
||||
typedef void (APIENTRYP PFNGLBUFFERSTORAGEPROC)(GLenum target, GLsizeiptr size, const void *data, GLbitfield flags);
|
||||
GLAPI PFNGLBUFFERSTORAGEPROC glad_glBufferStorage;
|
||||
#define glBufferStorage glad_glBufferStorage
|
||||
typedef void (APIENTRYP PFNGLCLEARTEXIMAGEPROC)(GLuint texture, GLint level, GLenum format, GLenum type, const void *data);
|
||||
GLAPI PFNGLCLEARTEXIMAGEPROC glad_glClearTexImage;
|
||||
#define glClearTexImage glad_glClearTexImage
|
||||
typedef void (APIENTRYP PFNGLCLEARTEXSUBIMAGEPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *data);
|
||||
GLAPI PFNGLCLEARTEXSUBIMAGEPROC glad_glClearTexSubImage;
|
||||
#define glClearTexSubImage glad_glClearTexSubImage
|
||||
typedef void (APIENTRYP PFNGLBINDBUFFERSBASEPROC)(GLenum target, GLuint first, GLsizei count, const GLuint *buffers);
|
||||
GLAPI PFNGLBINDBUFFERSBASEPROC glad_glBindBuffersBase;
|
||||
#define glBindBuffersBase glad_glBindBuffersBase
|
||||
typedef void (APIENTRYP PFNGLBINDBUFFERSRANGEPROC)(GLenum target, GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizeiptr *sizes);
|
||||
GLAPI PFNGLBINDBUFFERSRANGEPROC glad_glBindBuffersRange;
|
||||
#define glBindBuffersRange glad_glBindBuffersRange
|
||||
typedef void (APIENTRYP PFNGLBINDTEXTURESPROC)(GLuint first, GLsizei count, const GLuint *textures);
|
||||
GLAPI PFNGLBINDTEXTURESPROC glad_glBindTextures;
|
||||
#define glBindTextures glad_glBindTextures
|
||||
typedef void (APIENTRYP PFNGLBINDSAMPLERSPROC)(GLuint first, GLsizei count, const GLuint *samplers);
|
||||
GLAPI PFNGLBINDSAMPLERSPROC glad_glBindSamplers;
|
||||
#define glBindSamplers glad_glBindSamplers
|
||||
typedef void (APIENTRYP PFNGLBINDIMAGETEXTURESPROC)(GLuint first, GLsizei count, const GLuint *textures);
|
||||
GLAPI PFNGLBINDIMAGETEXTURESPROC glad_glBindImageTextures;
|
||||
#define glBindImageTextures glad_glBindImageTextures
|
||||
typedef void (APIENTRYP PFNGLBINDVERTEXBUFFERSPROC)(GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizei *strides);
|
||||
GLAPI PFNGLBINDVERTEXBUFFERSPROC glad_glBindVertexBuffers;
|
||||
#define glBindVertexBuffers glad_glBindVertexBuffers
|
||||
#endif
|
||||
#ifndef GL_VERSION_4_5
|
||||
#define GL_VERSION_4_5 1
|
||||
GLAPI int GLAD_GL_VERSION_4_5;
|
||||
typedef void (APIENTRYP PFNGLCLIPCONTROLPROC)(GLenum origin, GLenum depth);
|
||||
GLAPI PFNGLCLIPCONTROLPROC glad_glClipControl;
|
||||
#define glClipControl glad_glClipControl
|
||||
typedef void (APIENTRYP PFNGLCREATETRANSFORMFEEDBACKSPROC)(GLsizei n, GLuint *ids);
|
||||
GLAPI PFNGLCREATETRANSFORMFEEDBACKSPROC glad_glCreateTransformFeedbacks;
|
||||
#define glCreateTransformFeedbacks glad_glCreateTransformFeedbacks
|
||||
typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKBUFFERBASEPROC)(GLuint xfb, GLuint index, GLuint buffer);
|
||||
GLAPI PFNGLTRANSFORMFEEDBACKBUFFERBASEPROC glad_glTransformFeedbackBufferBase;
|
||||
#define glTransformFeedbackBufferBase glad_glTransformFeedbackBufferBase
|
||||
typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKBUFFERRANGEPROC)(GLuint xfb, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size);
|
||||
GLAPI PFNGLTRANSFORMFEEDBACKBUFFERRANGEPROC glad_glTransformFeedbackBufferRange;
|
||||
#define glTransformFeedbackBufferRange glad_glTransformFeedbackBufferRange
|
||||
typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKIVPROC)(GLuint xfb, GLenum pname, GLint *param);
|
||||
GLAPI PFNGLGETTRANSFORMFEEDBACKIVPROC glad_glGetTransformFeedbackiv;
|
||||
#define glGetTransformFeedbackiv glad_glGetTransformFeedbackiv
|
||||
typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKI_VPROC)(GLuint xfb, GLenum pname, GLuint index, GLint *param);
|
||||
GLAPI PFNGLGETTRANSFORMFEEDBACKI_VPROC glad_glGetTransformFeedbacki_v;
|
||||
#define glGetTransformFeedbacki_v glad_glGetTransformFeedbacki_v
|
||||
typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKI64_VPROC)(GLuint xfb, GLenum pname, GLuint index, GLint64 *param);
|
||||
GLAPI PFNGLGETTRANSFORMFEEDBACKI64_VPROC glad_glGetTransformFeedbacki64_v;
|
||||
#define glGetTransformFeedbacki64_v glad_glGetTransformFeedbacki64_v
|
||||
typedef void (APIENTRYP PFNGLCREATEBUFFERSPROC)(GLsizei n, GLuint *buffers);
|
||||
GLAPI PFNGLCREATEBUFFERSPROC glad_glCreateBuffers;
|
||||
#define glCreateBuffers glad_glCreateBuffers
|
||||
typedef void (APIENTRYP PFNGLNAMEDBUFFERSTORAGEPROC)(GLuint buffer, GLsizeiptr size, const void *data, GLbitfield flags);
|
||||
GLAPI PFNGLNAMEDBUFFERSTORAGEPROC glad_glNamedBufferStorage;
|
||||
#define glNamedBufferStorage glad_glNamedBufferStorage
|
||||
typedef void (APIENTRYP PFNGLNAMEDBUFFERDATAPROC)(GLuint buffer, GLsizeiptr size, const void *data, GLenum usage);
|
||||
GLAPI PFNGLNAMEDBUFFERDATAPROC glad_glNamedBufferData;
|
||||
#define glNamedBufferData glad_glNamedBufferData
|
||||
typedef void (APIENTRYP PFNGLNAMEDBUFFERSUBDATAPROC)(GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data);
|
||||
GLAPI PFNGLNAMEDBUFFERSUBDATAPROC glad_glNamedBufferSubData;
|
||||
#define glNamedBufferSubData glad_glNamedBufferSubData
|
||||
typedef void (APIENTRYP PFNGLCOPYNAMEDBUFFERSUBDATAPROC)(GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size);
|
||||
GLAPI PFNGLCOPYNAMEDBUFFERSUBDATAPROC glad_glCopyNamedBufferSubData;
|
||||
#define glCopyNamedBufferSubData glad_glCopyNamedBufferSubData
|
||||
typedef void (APIENTRYP PFNGLCLEARNAMEDBUFFERDATAPROC)(GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void *data);
|
||||
GLAPI PFNGLCLEARNAMEDBUFFERDATAPROC glad_glClearNamedBufferData;
|
||||
#define glClearNamedBufferData glad_glClearNamedBufferData
|
||||
typedef void (APIENTRYP PFNGLCLEARNAMEDBUFFERSUBDATAPROC)(GLuint buffer, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data);
|
||||
GLAPI PFNGLCLEARNAMEDBUFFERSUBDATAPROC glad_glClearNamedBufferSubData;
|
||||
#define glClearNamedBufferSubData glad_glClearNamedBufferSubData
|
||||
typedef void * (APIENTRYP PFNGLMAPNAMEDBUFFERPROC)(GLuint buffer, GLenum access);
|
||||
GLAPI PFNGLMAPNAMEDBUFFERPROC glad_glMapNamedBuffer;
|
||||
#define glMapNamedBuffer glad_glMapNamedBuffer
|
||||
typedef void * (APIENTRYP PFNGLMAPNAMEDBUFFERRANGEPROC)(GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access);
|
||||
GLAPI PFNGLMAPNAMEDBUFFERRANGEPROC glad_glMapNamedBufferRange;
|
||||
#define glMapNamedBufferRange glad_glMapNamedBufferRange
|
||||
typedef GLboolean (APIENTRYP PFNGLUNMAPNAMEDBUFFERPROC)(GLuint buffer);
|
||||
GLAPI PFNGLUNMAPNAMEDBUFFERPROC glad_glUnmapNamedBuffer;
|
||||
#define glUnmapNamedBuffer glad_glUnmapNamedBuffer
|
||||
typedef void (APIENTRYP PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEPROC)(GLuint buffer, GLintptr offset, GLsizeiptr length);
|
||||
GLAPI PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEPROC glad_glFlushMappedNamedBufferRange;
|
||||
#define glFlushMappedNamedBufferRange glad_glFlushMappedNamedBufferRange
|
||||
typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPARAMETERIVPROC)(GLuint buffer, GLenum pname, GLint *params);
|
||||
GLAPI PFNGLGETNAMEDBUFFERPARAMETERIVPROC glad_glGetNamedBufferParameteriv;
|
||||
#define glGetNamedBufferParameteriv glad_glGetNamedBufferParameteriv
|
||||
typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPARAMETERI64VPROC)(GLuint buffer, GLenum pname, GLint64 *params);
|
||||
GLAPI PFNGLGETNAMEDBUFFERPARAMETERI64VPROC glad_glGetNamedBufferParameteri64v;
|
||||
#define glGetNamedBufferParameteri64v glad_glGetNamedBufferParameteri64v
|
||||
typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPOINTERVPROC)(GLuint buffer, GLenum pname, void **params);
|
||||
GLAPI PFNGLGETNAMEDBUFFERPOINTERVPROC glad_glGetNamedBufferPointerv;
|
||||
#define glGetNamedBufferPointerv glad_glGetNamedBufferPointerv
|
||||
typedef void (APIENTRYP PFNGLGETNAMEDBUFFERSUBDATAPROC)(GLuint buffer, GLintptr offset, GLsizeiptr size, void *data);
|
||||
GLAPI PFNGLGETNAMEDBUFFERSUBDATAPROC glad_glGetNamedBufferSubData;
|
||||
#define glGetNamedBufferSubData glad_glGetNamedBufferSubData
|
||||
typedef void (APIENTRYP PFNGLCREATEFRAMEBUFFERSPROC)(GLsizei n, GLuint *framebuffers);
|
||||
GLAPI PFNGLCREATEFRAMEBUFFERSPROC glad_glCreateFramebuffers;
|
||||
#define glCreateFramebuffers glad_glCreateFramebuffers
|
||||
typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERRENDERBUFFERPROC)(GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer);
|
||||
GLAPI PFNGLNAMEDFRAMEBUFFERRENDERBUFFERPROC glad_glNamedFramebufferRenderbuffer;
|
||||
#define glNamedFramebufferRenderbuffer glad_glNamedFramebufferRenderbuffer
|
||||
typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERPARAMETERIPROC)(GLuint framebuffer, GLenum pname, GLint param);
|
||||
GLAPI PFNGLNAMEDFRAMEBUFFERPARAMETERIPROC glad_glNamedFramebufferParameteri;
|
||||
#define glNamedFramebufferParameteri glad_glNamedFramebufferParameteri
|
||||
typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTUREPROC)(GLuint framebuffer, GLenum attachment, GLuint texture, GLint level);
|
||||
GLAPI PFNGLNAMEDFRAMEBUFFERTEXTUREPROC glad_glNamedFramebufferTexture;
|
||||
#define glNamedFramebufferTexture glad_glNamedFramebufferTexture
|
||||
typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURELAYERPROC)(GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint layer);
|
||||
GLAPI PFNGLNAMEDFRAMEBUFFERTEXTURELAYERPROC glad_glNamedFramebufferTextureLayer;
|
||||
#define glNamedFramebufferTextureLayer glad_glNamedFramebufferTextureLayer
|
||||
typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERDRAWBUFFERPROC)(GLuint framebuffer, GLenum buf);
|
||||
GLAPI PFNGLNAMEDFRAMEBUFFERDRAWBUFFERPROC glad_glNamedFramebufferDrawBuffer;
|
||||
#define glNamedFramebufferDrawBuffer glad_glNamedFramebufferDrawBuffer
|
||||
typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERDRAWBUFFERSPROC)(GLuint framebuffer, GLsizei n, const GLenum *bufs);
|
||||
GLAPI PFNGLNAMEDFRAMEBUFFERDRAWBUFFERSPROC glad_glNamedFramebufferDrawBuffers;
|
||||
#define glNamedFramebufferDrawBuffers glad_glNamedFramebufferDrawBuffers
|
||||
typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERREADBUFFERPROC)(GLuint framebuffer, GLenum src);
|
||||
GLAPI PFNGLNAMEDFRAMEBUFFERREADBUFFERPROC glad_glNamedFramebufferReadBuffer;
|
||||
#define glNamedFramebufferReadBuffer glad_glNamedFramebufferReadBuffer
|
||||
typedef void (APIENTRYP PFNGLINVALIDATENAMEDFRAMEBUFFERDATAPROC)(GLuint framebuffer, GLsizei numAttachments, const GLenum *attachments);
|
||||
GLAPI PFNGLINVALIDATENAMEDFRAMEBUFFERDATAPROC glad_glInvalidateNamedFramebufferData;
|
||||
#define glInvalidateNamedFramebufferData glad_glInvalidateNamedFramebufferData
|
||||
typedef void (APIENTRYP PFNGLINVALIDATENAMEDFRAMEBUFFERSUBDATAPROC)(GLuint framebuffer, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height);
|
||||
GLAPI PFNGLINVALIDATENAMEDFRAMEBUFFERSUBDATAPROC glad_glInvalidateNamedFramebufferSubData;
|
||||
#define glInvalidateNamedFramebufferSubData glad_glInvalidateNamedFramebufferSubData
|
||||
typedef void (APIENTRYP PFNGLCLEARNAMEDFRAMEBUFFERIVPROC)(GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLint *value);
|
||||
GLAPI PFNGLCLEARNAMEDFRAMEBUFFERIVPROC glad_glClearNamedFramebufferiv;
|
||||
#define glClearNamedFramebufferiv glad_glClearNamedFramebufferiv
|
||||
typedef void (APIENTRYP PFNGLCLEARNAMEDFRAMEBUFFERUIVPROC)(GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLuint *value);
|
||||
GLAPI PFNGLCLEARNAMEDFRAMEBUFFERUIVPROC glad_glClearNamedFramebufferuiv;
|
||||
#define glClearNamedFramebufferuiv glad_glClearNamedFramebufferuiv
|
||||
typedef void (APIENTRYP PFNGLCLEARNAMEDFRAMEBUFFERFVPROC)(GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLfloat *value);
|
||||
GLAPI PFNGLCLEARNAMEDFRAMEBUFFERFVPROC glad_glClearNamedFramebufferfv;
|
||||
#define glClearNamedFramebufferfv glad_glClearNamedFramebufferfv
|
||||
typedef void (APIENTRYP PFNGLCLEARNAMEDFRAMEBUFFERFIPROC)(GLuint framebuffer, GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil);
|
||||
GLAPI PFNGLCLEARNAMEDFRAMEBUFFERFIPROC glad_glClearNamedFramebufferfi;
|
||||
#define glClearNamedFramebufferfi glad_glClearNamedFramebufferfi
|
||||
typedef void (APIENTRYP PFNGLBLITNAMEDFRAMEBUFFERPROC)(GLuint readFramebuffer, GLuint drawFramebuffer, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter);
|
||||
GLAPI PFNGLBLITNAMEDFRAMEBUFFERPROC glad_glBlitNamedFramebuffer;
|
||||
#define glBlitNamedFramebuffer glad_glBlitNamedFramebuffer
|
||||
typedef GLenum (APIENTRYP PFNGLCHECKNAMEDFRAMEBUFFERSTATUSPROC)(GLuint framebuffer, GLenum target);
|
||||
GLAPI PFNGLCHECKNAMEDFRAMEBUFFERSTATUSPROC glad_glCheckNamedFramebufferStatus;
|
||||
#define glCheckNamedFramebufferStatus glad_glCheckNamedFramebufferStatus
|
||||
typedef void (APIENTRYP PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVPROC)(GLuint framebuffer, GLenum pname, GLint *param);
|
||||
GLAPI PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVPROC glad_glGetNamedFramebufferParameteriv;
|
||||
#define glGetNamedFramebufferParameteriv glad_glGetNamedFramebufferParameteriv
|
||||
typedef void (APIENTRYP PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVPROC)(GLuint framebuffer, GLenum attachment, GLenum pname, GLint *params);
|
||||
GLAPI PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVPROC glad_glGetNamedFramebufferAttachmentParameteriv;
|
||||
#define glGetNamedFramebufferAttachmentParameteriv glad_glGetNamedFramebufferAttachmentParameteriv
|
||||
typedef void (APIENTRYP PFNGLCREATERENDERBUFFERSPROC)(GLsizei n, GLuint *renderbuffers);
|
||||
GLAPI PFNGLCREATERENDERBUFFERSPROC glad_glCreateRenderbuffers;
|
||||
#define glCreateRenderbuffers glad_glCreateRenderbuffers
|
||||
typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEPROC)(GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height);
|
||||
GLAPI PFNGLNAMEDRENDERBUFFERSTORAGEPROC glad_glNamedRenderbufferStorage;
|
||||
#define glNamedRenderbufferStorage glad_glNamedRenderbufferStorage
|
||||
typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEPROC)(GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height);
|
||||
GLAPI PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEPROC glad_glNamedRenderbufferStorageMultisample;
|
||||
#define glNamedRenderbufferStorageMultisample glad_glNamedRenderbufferStorageMultisample
|
||||
typedef void (APIENTRYP PFNGLGETNAMEDRENDERBUFFERPARAMETERIVPROC)(GLuint renderbuffer, GLenum pname, GLint *params);
|
||||
GLAPI PFNGLGETNAMEDRENDERBUFFERPARAMETERIVPROC glad_glGetNamedRenderbufferParameteriv;
|
||||
#define glGetNamedRenderbufferParameteriv glad_glGetNamedRenderbufferParameteriv
|
||||
typedef void (APIENTRYP PFNGLCREATETEXTURESPROC)(GLenum target, GLsizei n, GLuint *textures);
|
||||
GLAPI PFNGLCREATETEXTURESPROC glad_glCreateTextures;
|
||||
#define glCreateTextures glad_glCreateTextures
|
||||
typedef void (APIENTRYP PFNGLTEXTUREBUFFERPROC)(GLuint texture, GLenum internalformat, GLuint buffer);
|
||||
GLAPI PFNGLTEXTUREBUFFERPROC glad_glTextureBuffer;
|
||||
#define glTextureBuffer glad_glTextureBuffer
|
||||
typedef void (APIENTRYP PFNGLTEXTUREBUFFERRANGEPROC)(GLuint texture, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size);
|
||||
GLAPI PFNGLTEXTUREBUFFERRANGEPROC glad_glTextureBufferRange;
|
||||
#define glTextureBufferRange glad_glTextureBufferRange
|
||||
typedef void (APIENTRYP PFNGLTEXTURESTORAGE1DPROC)(GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width);
|
||||
GLAPI PFNGLTEXTURESTORAGE1DPROC glad_glTextureStorage1D;
|
||||
#define glTextureStorage1D glad_glTextureStorage1D
|
||||
typedef void (APIENTRYP PFNGLTEXTURESTORAGE2DPROC)(GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height);
|
||||
GLAPI PFNGLTEXTURESTORAGE2DPROC glad_glTextureStorage2D;
|
||||
#define glTextureStorage2D glad_glTextureStorage2D
|
||||
typedef void (APIENTRYP PFNGLTEXTURESTORAGE3DPROC)(GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth);
|
||||
GLAPI PFNGLTEXTURESTORAGE3DPROC glad_glTextureStorage3D;
|
||||
#define glTextureStorage3D glad_glTextureStorage3D
|
||||
typedef void (APIENTRYP PFNGLTEXTURESTORAGE2DMULTISAMPLEPROC)(GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations);
|
||||
GLAPI PFNGLTEXTURESTORAGE2DMULTISAMPLEPROC glad_glTextureStorage2DMultisample;
|
||||
#define glTextureStorage2DMultisample glad_glTextureStorage2DMultisample
|
||||
typedef void (APIENTRYP PFNGLTEXTURESTORAGE3DMULTISAMPLEPROC)(GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations);
|
||||
GLAPI PFNGLTEXTURESTORAGE3DMULTISAMPLEPROC glad_glTextureStorage3DMultisample;
|
||||
#define glTextureStorage3DMultisample glad_glTextureStorage3DMultisample
|
||||
typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE1DPROC)(GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels);
|
||||
GLAPI PFNGLTEXTURESUBIMAGE1DPROC glad_glTextureSubImage1D;
|
||||
#define glTextureSubImage1D glad_glTextureSubImage1D
|
||||
typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE2DPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels);
|
||||
GLAPI PFNGLTEXTURESUBIMAGE2DPROC glad_glTextureSubImage2D;
|
||||
#define glTextureSubImage2D glad_glTextureSubImage2D
|
||||
typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE3DPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels);
|
||||
GLAPI PFNGLTEXTURESUBIMAGE3DPROC glad_glTextureSubImage3D;
|
||||
#define glTextureSubImage3D glad_glTextureSubImage3D
|
||||
typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE1DPROC)(GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data);
|
||||
GLAPI PFNGLCOMPRESSEDTEXTURESUBIMAGE1DPROC glad_glCompressedTextureSubImage1D;
|
||||
#define glCompressedTextureSubImage1D glad_glCompressedTextureSubImage1D
|
||||
typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE2DPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data);
|
||||
GLAPI PFNGLCOMPRESSEDTEXTURESUBIMAGE2DPROC glad_glCompressedTextureSubImage2D;
|
||||
#define glCompressedTextureSubImage2D glad_glCompressedTextureSubImage2D
|
||||
typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE3DPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data);
|
||||
GLAPI PFNGLCOMPRESSEDTEXTURESUBIMAGE3DPROC glad_glCompressedTextureSubImage3D;
|
||||
#define glCompressedTextureSubImage3D glad_glCompressedTextureSubImage3D
|
||||
typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE1DPROC)(GLuint texture, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width);
|
||||
GLAPI PFNGLCOPYTEXTURESUBIMAGE1DPROC glad_glCopyTextureSubImage1D;
|
||||
#define glCopyTextureSubImage1D glad_glCopyTextureSubImage1D
|
||||
typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE2DPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height);
|
||||
GLAPI PFNGLCOPYTEXTURESUBIMAGE2DPROC glad_glCopyTextureSubImage2D;
|
||||
#define glCopyTextureSubImage2D glad_glCopyTextureSubImage2D
|
||||
typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE3DPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height);
|
||||
GLAPI PFNGLCOPYTEXTURESUBIMAGE3DPROC glad_glCopyTextureSubImage3D;
|
||||
#define glCopyTextureSubImage3D glad_glCopyTextureSubImage3D
|
||||
typedef void (APIENTRYP PFNGLTEXTUREPARAMETERFPROC)(GLuint texture, GLenum pname, GLfloat param);
|
||||
GLAPI PFNGLTEXTUREPARAMETERFPROC glad_glTextureParameterf;
|
||||
#define glTextureParameterf glad_glTextureParameterf
|
||||
typedef void (APIENTRYP PFNGLTEXTUREPARAMETERFVPROC)(GLuint texture, GLenum pname, const GLfloat *param);
|
||||
GLAPI PFNGLTEXTUREPARAMETERFVPROC glad_glTextureParameterfv;
|
||||
#define glTextureParameterfv glad_glTextureParameterfv
|
||||
typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIPROC)(GLuint texture, GLenum pname, GLint param);
|
||||
GLAPI PFNGLTEXTUREPARAMETERIPROC glad_glTextureParameteri;
|
||||
#define glTextureParameteri glad_glTextureParameteri
|
||||
typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIIVPROC)(GLuint texture, GLenum pname, const GLint *params);
|
||||
GLAPI PFNGLTEXTUREPARAMETERIIVPROC glad_glTextureParameterIiv;
|
||||
#define glTextureParameterIiv glad_glTextureParameterIiv
|
||||
typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIUIVPROC)(GLuint texture, GLenum pname, const GLuint *params);
|
||||
GLAPI PFNGLTEXTUREPARAMETERIUIVPROC glad_glTextureParameterIuiv;
|
||||
#define glTextureParameterIuiv glad_glTextureParameterIuiv
|
||||
typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIVPROC)(GLuint texture, GLenum pname, const GLint *param);
|
||||
GLAPI PFNGLTEXTUREPARAMETERIVPROC glad_glTextureParameteriv;
|
||||
#define glTextureParameteriv glad_glTextureParameteriv
|
||||
typedef void (APIENTRYP PFNGLGENERATETEXTUREMIPMAPPROC)(GLuint texture);
|
||||
GLAPI PFNGLGENERATETEXTUREMIPMAPPROC glad_glGenerateTextureMipmap;
|
||||
#define glGenerateTextureMipmap glad_glGenerateTextureMipmap
|
||||
typedef void (APIENTRYP PFNGLBINDTEXTUREUNITPROC)(GLuint unit, GLuint texture);
|
||||
GLAPI PFNGLBINDTEXTUREUNITPROC glad_glBindTextureUnit;
|
||||
#define glBindTextureUnit glad_glBindTextureUnit
|
||||
typedef void (APIENTRYP PFNGLGETTEXTUREIMAGEPROC)(GLuint texture, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *pixels);
|
||||
GLAPI PFNGLGETTEXTUREIMAGEPROC glad_glGetTextureImage;
|
||||
#define glGetTextureImage glad_glGetTextureImage
|
||||
typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXTUREIMAGEPROC)(GLuint texture, GLint level, GLsizei bufSize, void *pixels);
|
||||
GLAPI PFNGLGETCOMPRESSEDTEXTUREIMAGEPROC glad_glGetCompressedTextureImage;
|
||||
#define glGetCompressedTextureImage glad_glGetCompressedTextureImage
|
||||
typedef void (APIENTRYP PFNGLGETTEXTURELEVELPARAMETERFVPROC)(GLuint texture, GLint level, GLenum pname, GLfloat *params);
|
||||
GLAPI PFNGLGETTEXTURELEVELPARAMETERFVPROC glad_glGetTextureLevelParameterfv;
|
||||
#define glGetTextureLevelParameterfv glad_glGetTextureLevelParameterfv
|
||||
typedef void (APIENTRYP PFNGLGETTEXTURELEVELPARAMETERIVPROC)(GLuint texture, GLint level, GLenum pname, GLint *params);
|
||||
GLAPI PFNGLGETTEXTURELEVELPARAMETERIVPROC glad_glGetTextureLevelParameteriv;
|
||||
#define glGetTextureLevelParameteriv glad_glGetTextureLevelParameteriv
|
||||
typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERFVPROC)(GLuint texture, GLenum pname, GLfloat *params);
|
||||
GLAPI PFNGLGETTEXTUREPARAMETERFVPROC glad_glGetTextureParameterfv;
|
||||
#define glGetTextureParameterfv glad_glGetTextureParameterfv
|
||||
typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIIVPROC)(GLuint texture, GLenum pname, GLint *params);
|
||||
GLAPI PFNGLGETTEXTUREPARAMETERIIVPROC glad_glGetTextureParameterIiv;
|
||||
#define glGetTextureParameterIiv glad_glGetTextureParameterIiv
|
||||
typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIUIVPROC)(GLuint texture, GLenum pname, GLuint *params);
|
||||
GLAPI PFNGLGETTEXTUREPARAMETERIUIVPROC glad_glGetTextureParameterIuiv;
|
||||
#define glGetTextureParameterIuiv glad_glGetTextureParameterIuiv
|
||||
typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIVPROC)(GLuint texture, GLenum pname, GLint *params);
|
||||
GLAPI PFNGLGETTEXTUREPARAMETERIVPROC glad_glGetTextureParameteriv;
|
||||
#define glGetTextureParameteriv glad_glGetTextureParameteriv
|
||||
typedef void (APIENTRYP PFNGLCREATEVERTEXARRAYSPROC)(GLsizei n, GLuint *arrays);
|
||||
GLAPI PFNGLCREATEVERTEXARRAYSPROC glad_glCreateVertexArrays;
|
||||
#define glCreateVertexArrays glad_glCreateVertexArrays
|
||||
typedef void (APIENTRYP PFNGLDISABLEVERTEXARRAYATTRIBPROC)(GLuint vaobj, GLuint index);
|
||||
GLAPI PFNGLDISABLEVERTEXARRAYATTRIBPROC glad_glDisableVertexArrayAttrib;
|
||||
#define glDisableVertexArrayAttrib glad_glDisableVertexArrayAttrib
|
||||
typedef void (APIENTRYP PFNGLENABLEVERTEXARRAYATTRIBPROC)(GLuint vaobj, GLuint index);
|
||||
GLAPI PFNGLENABLEVERTEXARRAYATTRIBPROC glad_glEnableVertexArrayAttrib;
|
||||
#define glEnableVertexArrayAttrib glad_glEnableVertexArrayAttrib
|
||||
typedef void (APIENTRYP PFNGLVERTEXARRAYELEMENTBUFFERPROC)(GLuint vaobj, GLuint buffer);
|
||||
GLAPI PFNGLVERTEXARRAYELEMENTBUFFERPROC glad_glVertexArrayElementBuffer;
|
||||
#define glVertexArrayElementBuffer glad_glVertexArrayElementBuffer
|
||||
typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXBUFFERPROC)(GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride);
|
||||
GLAPI PFNGLVERTEXARRAYVERTEXBUFFERPROC glad_glVertexArrayVertexBuffer;
|
||||
#define glVertexArrayVertexBuffer glad_glVertexArrayVertexBuffer
|
||||
typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXBUFFERSPROC)(GLuint vaobj, GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizei *strides);
|
||||
GLAPI PFNGLVERTEXARRAYVERTEXBUFFERSPROC glad_glVertexArrayVertexBuffers;
|
||||
#define glVertexArrayVertexBuffers glad_glVertexArrayVertexBuffers
|
||||
typedef void (APIENTRYP PFNGLVERTEXARRAYATTRIBBINDINGPROC)(GLuint vaobj, GLuint attribindex, GLuint bindingindex);
|
||||
GLAPI PFNGLVERTEXARRAYATTRIBBINDINGPROC glad_glVertexArrayAttribBinding;
|
||||
#define glVertexArrayAttribBinding glad_glVertexArrayAttribBinding
|
||||
typedef void (APIENTRYP PFNGLVERTEXARRAYATTRIBFORMATPROC)(GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset);
|
||||
GLAPI PFNGLVERTEXARRAYATTRIBFORMATPROC glad_glVertexArrayAttribFormat;
|
||||
#define glVertexArrayAttribFormat glad_glVertexArrayAttribFormat
|
||||
typedef void (APIENTRYP PFNGLVERTEXARRAYATTRIBIFORMATPROC)(GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset);
|
||||
GLAPI PFNGLVERTEXARRAYATTRIBIFORMATPROC glad_glVertexArrayAttribIFormat;
|
||||
#define glVertexArrayAttribIFormat glad_glVertexArrayAttribIFormat
|
||||
typedef void (APIENTRYP PFNGLVERTEXARRAYATTRIBLFORMATPROC)(GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset);
|
||||
GLAPI PFNGLVERTEXARRAYATTRIBLFORMATPROC glad_glVertexArrayAttribLFormat;
|
||||
#define glVertexArrayAttribLFormat glad_glVertexArrayAttribLFormat
|
||||
typedef void (APIENTRYP PFNGLVERTEXARRAYBINDINGDIVISORPROC)(GLuint vaobj, GLuint bindingindex, GLuint divisor);
|
||||
GLAPI PFNGLVERTEXARRAYBINDINGDIVISORPROC glad_glVertexArrayBindingDivisor;
|
||||
#define glVertexArrayBindingDivisor glad_glVertexArrayBindingDivisor
|
||||
typedef void (APIENTRYP PFNGLGETVERTEXARRAYIVPROC)(GLuint vaobj, GLenum pname, GLint *param);
|
||||
GLAPI PFNGLGETVERTEXARRAYIVPROC glad_glGetVertexArrayiv;
|
||||
#define glGetVertexArrayiv glad_glGetVertexArrayiv
|
||||
typedef void (APIENTRYP PFNGLGETVERTEXARRAYINDEXEDIVPROC)(GLuint vaobj, GLuint index, GLenum pname, GLint *param);
|
||||
GLAPI PFNGLGETVERTEXARRAYINDEXEDIVPROC glad_glGetVertexArrayIndexediv;
|
||||
#define glGetVertexArrayIndexediv glad_glGetVertexArrayIndexediv
|
||||
typedef void (APIENTRYP PFNGLGETVERTEXARRAYINDEXED64IVPROC)(GLuint vaobj, GLuint index, GLenum pname, GLint64 *param);
|
||||
GLAPI PFNGLGETVERTEXARRAYINDEXED64IVPROC glad_glGetVertexArrayIndexed64iv;
|
||||
#define glGetVertexArrayIndexed64iv glad_glGetVertexArrayIndexed64iv
|
||||
typedef void (APIENTRYP PFNGLCREATESAMPLERSPROC)(GLsizei n, GLuint *samplers);
|
||||
GLAPI PFNGLCREATESAMPLERSPROC glad_glCreateSamplers;
|
||||
#define glCreateSamplers glad_glCreateSamplers
|
||||
typedef void (APIENTRYP PFNGLCREATEPROGRAMPIPELINESPROC)(GLsizei n, GLuint *pipelines);
|
||||
GLAPI PFNGLCREATEPROGRAMPIPELINESPROC glad_glCreateProgramPipelines;
|
||||
#define glCreateProgramPipelines glad_glCreateProgramPipelines
|
||||
typedef void (APIENTRYP PFNGLCREATEQUERIESPROC)(GLenum target, GLsizei n, GLuint *ids);
|
||||
GLAPI PFNGLCREATEQUERIESPROC glad_glCreateQueries;
|
||||
#define glCreateQueries glad_glCreateQueries
|
||||
typedef void (APIENTRYP PFNGLGETQUERYBUFFEROBJECTI64VPROC)(GLuint id, GLuint buffer, GLenum pname, GLintptr offset);
|
||||
GLAPI PFNGLGETQUERYBUFFEROBJECTI64VPROC glad_glGetQueryBufferObjecti64v;
|
||||
#define glGetQueryBufferObjecti64v glad_glGetQueryBufferObjecti64v
|
||||
typedef void (APIENTRYP PFNGLGETQUERYBUFFEROBJECTIVPROC)(GLuint id, GLuint buffer, GLenum pname, GLintptr offset);
|
||||
GLAPI PFNGLGETQUERYBUFFEROBJECTIVPROC glad_glGetQueryBufferObjectiv;
|
||||
#define glGetQueryBufferObjectiv glad_glGetQueryBufferObjectiv
|
||||
typedef void (APIENTRYP PFNGLGETQUERYBUFFEROBJECTUI64VPROC)(GLuint id, GLuint buffer, GLenum pname, GLintptr offset);
|
||||
GLAPI PFNGLGETQUERYBUFFEROBJECTUI64VPROC glad_glGetQueryBufferObjectui64v;
|
||||
#define glGetQueryBufferObjectui64v glad_glGetQueryBufferObjectui64v
|
||||
typedef void (APIENTRYP PFNGLGETQUERYBUFFEROBJECTUIVPROC)(GLuint id, GLuint buffer, GLenum pname, GLintptr offset);
|
||||
GLAPI PFNGLGETQUERYBUFFEROBJECTUIVPROC glad_glGetQueryBufferObjectuiv;
|
||||
#define glGetQueryBufferObjectuiv glad_glGetQueryBufferObjectuiv
|
||||
typedef void (APIENTRYP PFNGLMEMORYBARRIERBYREGIONPROC)(GLbitfield barriers);
|
||||
GLAPI PFNGLMEMORYBARRIERBYREGIONPROC glad_glMemoryBarrierByRegion;
|
||||
#define glMemoryBarrierByRegion glad_glMemoryBarrierByRegion
|
||||
typedef void (APIENTRYP PFNGLGETTEXTURESUBIMAGEPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLsizei bufSize, void *pixels);
|
||||
GLAPI PFNGLGETTEXTURESUBIMAGEPROC glad_glGetTextureSubImage;
|
||||
#define glGetTextureSubImage glad_glGetTextureSubImage
|
||||
typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXTURESUBIMAGEPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLsizei bufSize, void *pixels);
|
||||
GLAPI PFNGLGETCOMPRESSEDTEXTURESUBIMAGEPROC glad_glGetCompressedTextureSubImage;
|
||||
#define glGetCompressedTextureSubImage glad_glGetCompressedTextureSubImage
|
||||
typedef GLenum (APIENTRYP PFNGLGETGRAPHICSRESETSTATUSPROC)(void);
|
||||
GLAPI PFNGLGETGRAPHICSRESETSTATUSPROC glad_glGetGraphicsResetStatus;
|
||||
#define glGetGraphicsResetStatus glad_glGetGraphicsResetStatus
|
||||
typedef void (APIENTRYP PFNGLGETNCOMPRESSEDTEXIMAGEPROC)(GLenum target, GLint lod, GLsizei bufSize, void *pixels);
|
||||
GLAPI PFNGLGETNCOMPRESSEDTEXIMAGEPROC glad_glGetnCompressedTexImage;
|
||||
#define glGetnCompressedTexImage glad_glGetnCompressedTexImage
|
||||
typedef void (APIENTRYP PFNGLGETNTEXIMAGEPROC)(GLenum target, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *pixels);
|
||||
GLAPI PFNGLGETNTEXIMAGEPROC glad_glGetnTexImage;
|
||||
#define glGetnTexImage glad_glGetnTexImage
|
||||
typedef void (APIENTRYP PFNGLGETNUNIFORMDVPROC)(GLuint program, GLint location, GLsizei bufSize, GLdouble *params);
|
||||
GLAPI PFNGLGETNUNIFORMDVPROC glad_glGetnUniformdv;
|
||||
#define glGetnUniformdv glad_glGetnUniformdv
|
||||
typedef void (APIENTRYP PFNGLGETNUNIFORMFVPROC)(GLuint program, GLint location, GLsizei bufSize, GLfloat *params);
|
||||
GLAPI PFNGLGETNUNIFORMFVPROC glad_glGetnUniformfv;
|
||||
#define glGetnUniformfv glad_glGetnUniformfv
|
||||
typedef void (APIENTRYP PFNGLGETNUNIFORMIVPROC)(GLuint program, GLint location, GLsizei bufSize, GLint *params);
|
||||
GLAPI PFNGLGETNUNIFORMIVPROC glad_glGetnUniformiv;
|
||||
#define glGetnUniformiv glad_glGetnUniformiv
|
||||
typedef void (APIENTRYP PFNGLGETNUNIFORMUIVPROC)(GLuint program, GLint location, GLsizei bufSize, GLuint *params);
|
||||
GLAPI PFNGLGETNUNIFORMUIVPROC glad_glGetnUniformuiv;
|
||||
#define glGetnUniformuiv glad_glGetnUniformuiv
|
||||
typedef void (APIENTRYP PFNGLREADNPIXELSPROC)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data);
|
||||
GLAPI PFNGLREADNPIXELSPROC glad_glReadnPixels;
|
||||
#define glReadnPixels glad_glReadnPixels
|
||||
typedef void (APIENTRYP PFNGLGETNMAPDVPROC)(GLenum target, GLenum query, GLsizei bufSize, GLdouble *v);
|
||||
GLAPI PFNGLGETNMAPDVPROC glad_glGetnMapdv;
|
||||
#define glGetnMapdv glad_glGetnMapdv
|
||||
typedef void (APIENTRYP PFNGLGETNMAPFVPROC)(GLenum target, GLenum query, GLsizei bufSize, GLfloat *v);
|
||||
GLAPI PFNGLGETNMAPFVPROC glad_glGetnMapfv;
|
||||
#define glGetnMapfv glad_glGetnMapfv
|
||||
typedef void (APIENTRYP PFNGLGETNMAPIVPROC)(GLenum target, GLenum query, GLsizei bufSize, GLint *v);
|
||||
GLAPI PFNGLGETNMAPIVPROC glad_glGetnMapiv;
|
||||
#define glGetnMapiv glad_glGetnMapiv
|
||||
typedef void (APIENTRYP PFNGLGETNPIXELMAPFVPROC)(GLenum map, GLsizei bufSize, GLfloat *values);
|
||||
GLAPI PFNGLGETNPIXELMAPFVPROC glad_glGetnPixelMapfv;
|
||||
#define glGetnPixelMapfv glad_glGetnPixelMapfv
|
||||
typedef void (APIENTRYP PFNGLGETNPIXELMAPUIVPROC)(GLenum map, GLsizei bufSize, GLuint *values);
|
||||
GLAPI PFNGLGETNPIXELMAPUIVPROC glad_glGetnPixelMapuiv;
|
||||
#define glGetnPixelMapuiv glad_glGetnPixelMapuiv
|
||||
typedef void (APIENTRYP PFNGLGETNPIXELMAPUSVPROC)(GLenum map, GLsizei bufSize, GLushort *values);
|
||||
GLAPI PFNGLGETNPIXELMAPUSVPROC glad_glGetnPixelMapusv;
|
||||
#define glGetnPixelMapusv glad_glGetnPixelMapusv
|
||||
typedef void (APIENTRYP PFNGLGETNPOLYGONSTIPPLEPROC)(GLsizei bufSize, GLubyte *pattern);
|
||||
GLAPI PFNGLGETNPOLYGONSTIPPLEPROC glad_glGetnPolygonStipple;
|
||||
#define glGetnPolygonStipple glad_glGetnPolygonStipple
|
||||
typedef void (APIENTRYP PFNGLGETNCOLORTABLEPROC)(GLenum target, GLenum format, GLenum type, GLsizei bufSize, void *table);
|
||||
GLAPI PFNGLGETNCOLORTABLEPROC glad_glGetnColorTable;
|
||||
#define glGetnColorTable glad_glGetnColorTable
|
||||
typedef void (APIENTRYP PFNGLGETNCONVOLUTIONFILTERPROC)(GLenum target, GLenum format, GLenum type, GLsizei bufSize, void *image);
|
||||
GLAPI PFNGLGETNCONVOLUTIONFILTERPROC glad_glGetnConvolutionFilter;
|
||||
#define glGetnConvolutionFilter glad_glGetnConvolutionFilter
|
||||
typedef void (APIENTRYP PFNGLGETNSEPARABLEFILTERPROC)(GLenum target, GLenum format, GLenum type, GLsizei rowBufSize, void *row, GLsizei columnBufSize, void *column, void *span);
|
||||
GLAPI PFNGLGETNSEPARABLEFILTERPROC glad_glGetnSeparableFilter;
|
||||
#define glGetnSeparableFilter glad_glGetnSeparableFilter
|
||||
typedef void (APIENTRYP PFNGLGETNHISTOGRAMPROC)(GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void *values);
|
||||
GLAPI PFNGLGETNHISTOGRAMPROC glad_glGetnHistogram;
|
||||
#define glGetnHistogram glad_glGetnHistogram
|
||||
typedef void (APIENTRYP PFNGLGETNMINMAXPROC)(GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void *values);
|
||||
GLAPI PFNGLGETNMINMAXPROC glad_glGetnMinmax;
|
||||
#define glGetnMinmax glad_glGetnMinmax
|
||||
typedef void (APIENTRYP PFNGLTEXTUREBARRIERPROC)(void);
|
||||
GLAPI PFNGLTEXTUREBARRIERPROC glad_glTextureBarrier;
|
||||
#define glTextureBarrier glad_glTextureBarrier
|
||||
#endif
|
||||
#ifndef GL_VERSION_4_6
|
||||
#define GL_VERSION_4_6 1
|
||||
GLAPI int GLAD_GL_VERSION_4_6;
|
||||
typedef void (APIENTRYP PFNGLSPECIALIZESHADERPROC)(GLuint shader, const GLchar *pEntryPoint, GLuint numSpecializationConstants, const GLuint *pConstantIndex, const GLuint *pConstantValue);
|
||||
GLAPI PFNGLSPECIALIZESHADERPROC glad_glSpecializeShader;
|
||||
#define glSpecializeShader glad_glSpecializeShader
|
||||
typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTCOUNTPROC)(GLenum mode, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride);
|
||||
GLAPI PFNGLMULTIDRAWARRAYSINDIRECTCOUNTPROC glad_glMultiDrawArraysIndirectCount;
|
||||
#define glMultiDrawArraysIndirectCount glad_glMultiDrawArraysIndirectCount
|
||||
typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTPROC)(GLenum mode, GLenum type, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride);
|
||||
GLAPI PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTPROC glad_glMultiDrawElementsIndirectCount;
|
||||
#define glMultiDrawElementsIndirectCount glad_glMultiDrawElementsIndirectCount
|
||||
typedef void (APIENTRYP PFNGLPOLYGONOFFSETCLAMPPROC)(GLfloat factor, GLfloat units, GLfloat clamp);
|
||||
GLAPI PFNGLPOLYGONOFFSETCLAMPPROC glad_glPolygonOffsetClamp;
|
||||
#define glPolygonOffsetClamp glad_glPolygonOffsetClamp
|
||||
#endif
|
||||
#ifndef GL_ES_VERSION_2_0
|
||||
#define GL_ES_VERSION_2_0 1
|
||||
GLAPI int GLAD_GL_ES_VERSION_2_0;
|
||||
@ -3770,6 +3291,9 @@ GLAPI int GLAD_GL_ES_VERSION_3_0;
|
||||
#ifndef GL_ES_VERSION_3_1
|
||||
#define GL_ES_VERSION_3_1 1
|
||||
GLAPI int GLAD_GL_ES_VERSION_3_1;
|
||||
typedef void (APIENTRYP PFNGLMEMORYBARRIERBYREGIONPROC)(GLbitfield barriers);
|
||||
GLAPI PFNGLMEMORYBARRIERBYREGIONPROC glad_glMemoryBarrierByRegion;
|
||||
#define glMemoryBarrierByRegion glad_glMemoryBarrierByRegion
|
||||
#endif
|
||||
#ifndef GL_ES_VERSION_3_2
|
||||
#define GL_ES_VERSION_3_2 1
|
||||
@ -3780,7 +3304,38 @@ GLAPI PFNGLBLENDBARRIERPROC glad_glBlendBarrier;
|
||||
typedef void (APIENTRYP PFNGLPRIMITIVEBOUNDINGBOXPROC)(GLfloat minX, GLfloat minY, GLfloat minZ, GLfloat minW, GLfloat maxX, GLfloat maxY, GLfloat maxZ, GLfloat maxW);
|
||||
GLAPI PFNGLPRIMITIVEBOUNDINGBOXPROC glad_glPrimitiveBoundingBox;
|
||||
#define glPrimitiveBoundingBox glad_glPrimitiveBoundingBox
|
||||
typedef GLenum (APIENTRYP PFNGLGETGRAPHICSRESETSTATUSPROC)(void);
|
||||
GLAPI PFNGLGETGRAPHICSRESETSTATUSPROC glad_glGetGraphicsResetStatus;
|
||||
#define glGetGraphicsResetStatus glad_glGetGraphicsResetStatus
|
||||
typedef void (APIENTRYP PFNGLREADNPIXELSPROC)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data);
|
||||
GLAPI PFNGLREADNPIXELSPROC glad_glReadnPixels;
|
||||
#define glReadnPixels glad_glReadnPixels
|
||||
typedef void (APIENTRYP PFNGLGETNUNIFORMFVPROC)(GLuint program, GLint location, GLsizei bufSize, GLfloat *params);
|
||||
GLAPI PFNGLGETNUNIFORMFVPROC glad_glGetnUniformfv;
|
||||
#define glGetnUniformfv glad_glGetnUniformfv
|
||||
typedef void (APIENTRYP PFNGLGETNUNIFORMIVPROC)(GLuint program, GLint location, GLsizei bufSize, GLint *params);
|
||||
GLAPI PFNGLGETNUNIFORMIVPROC glad_glGetnUniformiv;
|
||||
#define glGetnUniformiv glad_glGetnUniformiv
|
||||
typedef void (APIENTRYP PFNGLGETNUNIFORMUIVPROC)(GLuint program, GLint location, GLsizei bufSize, GLuint *params);
|
||||
GLAPI PFNGLGETNUNIFORMUIVPROC glad_glGetnUniformuiv;
|
||||
#define glGetnUniformuiv glad_glGetnUniformuiv
|
||||
#endif
|
||||
#define GL_MAP_PERSISTENT_BIT 0x0040
|
||||
#define GL_MAP_COHERENT_BIT 0x0080
|
||||
#define GL_DYNAMIC_STORAGE_BIT 0x0100
|
||||
#define GL_CLIENT_STORAGE_BIT 0x0200
|
||||
#define GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT 0x00004000
|
||||
#define GL_BUFFER_IMMUTABLE_STORAGE 0x821F
|
||||
#define GL_BUFFER_STORAGE_FLAGS 0x8220
|
||||
#define GL_CLEAR_TEXTURE 0x9365
|
||||
#define GL_COMPRESSED_RGBA_BPTC_UNORM_ARB 0x8E8C
|
||||
#define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB 0x8E8D
|
||||
#define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB 0x8E8E
|
||||
#define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB 0x8E8F
|
||||
#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0
|
||||
#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1
|
||||
#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2
|
||||
#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3
|
||||
#define GL_MAP_PERSISTENT_BIT_EXT 0x0040
|
||||
#define GL_MAP_COHERENT_BIT_EXT 0x0080
|
||||
#define GL_DYNAMIC_STORAGE_BIT_EXT 0x0100
|
||||
@ -3802,14 +3357,37 @@ GLAPI PFNGLPRIMITIVEBOUNDINGBOXPROC glad_glPrimitiveBoundingBox;
|
||||
#ifndef GL_ARB_buffer_storage
|
||||
#define GL_ARB_buffer_storage 1
|
||||
GLAPI int GLAD_GL_ARB_buffer_storage;
|
||||
typedef void (APIENTRYP PFNGLBUFFERSTORAGEPROC)(GLenum target, GLsizeiptr size, const void *data, GLbitfield flags);
|
||||
GLAPI PFNGLBUFFERSTORAGEPROC glad_glBufferStorage;
|
||||
#define glBufferStorage glad_glBufferStorage
|
||||
#endif
|
||||
#ifndef GL_ARB_clear_texture
|
||||
#define GL_ARB_clear_texture 1
|
||||
GLAPI int GLAD_GL_ARB_clear_texture;
|
||||
typedef void (APIENTRYP PFNGLCLEARTEXIMAGEPROC)(GLuint texture, GLint level, GLenum format, GLenum type, const void *data);
|
||||
GLAPI PFNGLCLEARTEXIMAGEPROC glad_glClearTexImage;
|
||||
#define glClearTexImage glad_glClearTexImage
|
||||
typedef void (APIENTRYP PFNGLCLEARTEXSUBIMAGEPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *data);
|
||||
GLAPI PFNGLCLEARTEXSUBIMAGEPROC glad_glClearTexSubImage;
|
||||
#define glClearTexSubImage glad_glClearTexSubImage
|
||||
#endif
|
||||
#ifndef GL_ARB_get_texture_sub_image
|
||||
#define GL_ARB_get_texture_sub_image 1
|
||||
GLAPI int GLAD_GL_ARB_get_texture_sub_image;
|
||||
typedef void (APIENTRYP PFNGLGETTEXTURESUBIMAGEPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLsizei bufSize, void *pixels);
|
||||
GLAPI PFNGLGETTEXTURESUBIMAGEPROC glad_glGetTextureSubImage;
|
||||
#define glGetTextureSubImage glad_glGetTextureSubImage
|
||||
typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXTURESUBIMAGEPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLsizei bufSize, void *pixels);
|
||||
GLAPI PFNGLGETCOMPRESSEDTEXTURESUBIMAGEPROC glad_glGetCompressedTextureSubImage;
|
||||
#define glGetCompressedTextureSubImage glad_glGetCompressedTextureSubImage
|
||||
#endif
|
||||
#ifndef GL_ARB_texture_compression_bptc
|
||||
#define GL_ARB_texture_compression_bptc 1
|
||||
GLAPI int GLAD_GL_ARB_texture_compression_bptc;
|
||||
#endif
|
||||
#ifndef GL_EXT_texture_compression_s3tc
|
||||
#define GL_EXT_texture_compression_s3tc 1
|
||||
GLAPI int GLAD_GL_EXT_texture_compression_s3tc;
|
||||
#endif
|
||||
#ifndef GL_EXT_buffer_storage
|
||||
#define GL_EXT_buffer_storage 1
|
||||
@ -3822,6 +3400,10 @@ GLAPI PFNGLBUFFERSTORAGEEXTPROC glad_glBufferStorageEXT;
|
||||
#define GL_EXT_clip_cull_distance 1
|
||||
GLAPI int GLAD_GL_EXT_clip_cull_distance;
|
||||
#endif
|
||||
#ifndef GL_EXT_texture_compression_s3tc
|
||||
#define GL_EXT_texture_compression_s3tc 1
|
||||
GLAPI int GLAD_GL_EXT_texture_compression_s3tc;
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
308
externals/glad/src/glad.c
vendored
308
externals/glad/src/glad.c
vendored
@ -1,26 +1,28 @@
|
||||
/*
|
||||
|
||||
OpenGL, OpenGL ES loader generated by glad 0.1.36 on Sun Mar 12 10:25:27 2023.
|
||||
OpenGL, OpenGL ES loader generated by glad 0.1.36 on Sat Apr 1 20:34:42 2023.
|
||||
|
||||
Language/Generator: C/C++
|
||||
Specification: gl
|
||||
APIs: gl=4.6, gles2=3.2
|
||||
APIs: gl=4.3, gles2=3.2
|
||||
Profile: core
|
||||
Extensions:
|
||||
GL_ARB_buffer_storage,
|
||||
GL_ARB_clear_texture,
|
||||
GL_ARB_get_texture_sub_image,
|
||||
GL_ARB_texture_compression_bptc,
|
||||
GL_EXT_buffer_storage,
|
||||
GL_EXT_clip_cull_distance
|
||||
GL_EXT_clip_cull_distance,
|
||||
GL_EXT_texture_compression_s3tc
|
||||
Loader: True
|
||||
Local files: False
|
||||
Omit khrplatform: False
|
||||
Reproducible: False
|
||||
|
||||
Commandline:
|
||||
--profile="core" --api="gl=4.6,gles2=3.2" --generator="c" --spec="gl" --extensions="GL_ARB_buffer_storage,GL_ARB_clear_texture,GL_ARB_get_texture_sub_image,GL_EXT_buffer_storage,GL_EXT_clip_cull_distance"
|
||||
--profile="core" --api="gl=4.3,gles2=3.2" --generator="c" --spec="gl" --extensions="GL_ARB_buffer_storage,GL_ARB_clear_texture,GL_ARB_get_texture_sub_image,GL_ARB_texture_compression_bptc,GL_EXT_buffer_storage,GL_EXT_clip_cull_distance,GL_EXT_texture_compression_s3tc"
|
||||
Online:
|
||||
https://glad.dav1d.de/#profile=core&language=c&specification=gl&loader=on&api=gl%3D4.6&api=gles2%3D3.2&extensions=GL_ARB_buffer_storage&extensions=GL_ARB_clear_texture&extensions=GL_ARB_get_texture_sub_image&extensions=GL_EXT_buffer_storage&extensions=GL_EXT_clip_cull_distance
|
||||
https://glad.dav1d.de/#profile=core&language=c&specification=gl&loader=on&api=gl%3D4.3&api=gles2%3D3.2&extensions=GL_ARB_buffer_storage&extensions=GL_ARB_clear_texture&extensions=GL_ARB_get_texture_sub_image&extensions=GL_ARB_texture_compression_bptc&extensions=GL_EXT_buffer_storage&extensions=GL_EXT_clip_cull_distance&extensions=GL_EXT_texture_compression_s3tc
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
@ -275,9 +277,6 @@ int GLAD_GL_VERSION_4_0 = 0;
|
||||
int GLAD_GL_VERSION_4_1 = 0;
|
||||
int GLAD_GL_VERSION_4_2 = 0;
|
||||
int GLAD_GL_VERSION_4_3 = 0;
|
||||
int GLAD_GL_VERSION_4_4 = 0;
|
||||
int GLAD_GL_VERSION_4_5 = 0;
|
||||
int GLAD_GL_VERSION_4_6 = 0;
|
||||
int GLAD_GL_ES_VERSION_2_0 = 0;
|
||||
int GLAD_GL_ES_VERSION_3_0 = 0;
|
||||
int GLAD_GL_ES_VERSION_3_1 = 0;
|
||||
@ -293,24 +292,17 @@ PFNGLBINDATTRIBLOCATIONPROC glad_glBindAttribLocation = NULL;
|
||||
PFNGLBINDBUFFERPROC glad_glBindBuffer = NULL;
|
||||
PFNGLBINDBUFFERBASEPROC glad_glBindBufferBase = NULL;
|
||||
PFNGLBINDBUFFERRANGEPROC glad_glBindBufferRange = NULL;
|
||||
PFNGLBINDBUFFERSBASEPROC glad_glBindBuffersBase = NULL;
|
||||
PFNGLBINDBUFFERSRANGEPROC glad_glBindBuffersRange = NULL;
|
||||
PFNGLBINDFRAGDATALOCATIONPROC glad_glBindFragDataLocation = NULL;
|
||||
PFNGLBINDFRAGDATALOCATIONINDEXEDPROC glad_glBindFragDataLocationIndexed = NULL;
|
||||
PFNGLBINDFRAMEBUFFERPROC glad_glBindFramebuffer = NULL;
|
||||
PFNGLBINDIMAGETEXTUREPROC glad_glBindImageTexture = NULL;
|
||||
PFNGLBINDIMAGETEXTURESPROC glad_glBindImageTextures = NULL;
|
||||
PFNGLBINDPROGRAMPIPELINEPROC glad_glBindProgramPipeline = NULL;
|
||||
PFNGLBINDRENDERBUFFERPROC glad_glBindRenderbuffer = NULL;
|
||||
PFNGLBINDSAMPLERPROC glad_glBindSampler = NULL;
|
||||
PFNGLBINDSAMPLERSPROC glad_glBindSamplers = NULL;
|
||||
PFNGLBINDTEXTUREPROC glad_glBindTexture = NULL;
|
||||
PFNGLBINDTEXTUREUNITPROC glad_glBindTextureUnit = NULL;
|
||||
PFNGLBINDTEXTURESPROC glad_glBindTextures = NULL;
|
||||
PFNGLBINDTRANSFORMFEEDBACKPROC glad_glBindTransformFeedback = NULL;
|
||||
PFNGLBINDVERTEXARRAYPROC glad_glBindVertexArray = NULL;
|
||||
PFNGLBINDVERTEXBUFFERPROC glad_glBindVertexBuffer = NULL;
|
||||
PFNGLBINDVERTEXBUFFERSPROC glad_glBindVertexBuffers = NULL;
|
||||
PFNGLBLENDBARRIERPROC glad_glBlendBarrier = NULL;
|
||||
PFNGLBLENDCOLORPROC glad_glBlendColor = NULL;
|
||||
PFNGLBLENDEQUATIONPROC glad_glBlendEquation = NULL;
|
||||
@ -322,12 +314,9 @@ PFNGLBLENDFUNCSEPARATEPROC glad_glBlendFuncSeparate = NULL;
|
||||
PFNGLBLENDFUNCSEPARATEIPROC glad_glBlendFuncSeparatei = NULL;
|
||||
PFNGLBLENDFUNCIPROC glad_glBlendFunci = NULL;
|
||||
PFNGLBLITFRAMEBUFFERPROC glad_glBlitFramebuffer = NULL;
|
||||
PFNGLBLITNAMEDFRAMEBUFFERPROC glad_glBlitNamedFramebuffer = NULL;
|
||||
PFNGLBUFFERDATAPROC glad_glBufferData = NULL;
|
||||
PFNGLBUFFERSTORAGEPROC glad_glBufferStorage = NULL;
|
||||
PFNGLBUFFERSUBDATAPROC glad_glBufferSubData = NULL;
|
||||
PFNGLCHECKFRAMEBUFFERSTATUSPROC glad_glCheckFramebufferStatus = NULL;
|
||||
PFNGLCHECKNAMEDFRAMEBUFFERSTATUSPROC glad_glCheckNamedFramebufferStatus = NULL;
|
||||
PFNGLCLAMPCOLORPROC glad_glClampColor = NULL;
|
||||
PFNGLCLEARPROC glad_glClear = NULL;
|
||||
PFNGLCLEARBUFFERDATAPROC glad_glClearBufferData = NULL;
|
||||
@ -339,17 +328,8 @@ PFNGLCLEARBUFFERUIVPROC glad_glClearBufferuiv = NULL;
|
||||
PFNGLCLEARCOLORPROC glad_glClearColor = NULL;
|
||||
PFNGLCLEARDEPTHPROC glad_glClearDepth = NULL;
|
||||
PFNGLCLEARDEPTHFPROC glad_glClearDepthf = NULL;
|
||||
PFNGLCLEARNAMEDBUFFERDATAPROC glad_glClearNamedBufferData = NULL;
|
||||
PFNGLCLEARNAMEDBUFFERSUBDATAPROC glad_glClearNamedBufferSubData = NULL;
|
||||
PFNGLCLEARNAMEDFRAMEBUFFERFIPROC glad_glClearNamedFramebufferfi = NULL;
|
||||
PFNGLCLEARNAMEDFRAMEBUFFERFVPROC glad_glClearNamedFramebufferfv = NULL;
|
||||
PFNGLCLEARNAMEDFRAMEBUFFERIVPROC glad_glClearNamedFramebufferiv = NULL;
|
||||
PFNGLCLEARNAMEDFRAMEBUFFERUIVPROC glad_glClearNamedFramebufferuiv = NULL;
|
||||
PFNGLCLEARSTENCILPROC glad_glClearStencil = NULL;
|
||||
PFNGLCLEARTEXIMAGEPROC glad_glClearTexImage = NULL;
|
||||
PFNGLCLEARTEXSUBIMAGEPROC glad_glClearTexSubImage = NULL;
|
||||
PFNGLCLIENTWAITSYNCPROC glad_glClientWaitSync = NULL;
|
||||
PFNGLCLIPCONTROLPROC glad_glClipControl = NULL;
|
||||
PFNGLCOLORMASKPROC glad_glColorMask = NULL;
|
||||
PFNGLCOLORMASKIPROC glad_glColorMaski = NULL;
|
||||
PFNGLCOLORP3UIPROC glad_glColorP3ui = NULL;
|
||||
@ -363,32 +343,16 @@ PFNGLCOMPRESSEDTEXIMAGE3DPROC glad_glCompressedTexImage3D = NULL;
|
||||
PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC glad_glCompressedTexSubImage1D = NULL;
|
||||
PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glad_glCompressedTexSubImage2D = NULL;
|
||||
PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC glad_glCompressedTexSubImage3D = NULL;
|
||||
PFNGLCOMPRESSEDTEXTURESUBIMAGE1DPROC glad_glCompressedTextureSubImage1D = NULL;
|
||||
PFNGLCOMPRESSEDTEXTURESUBIMAGE2DPROC glad_glCompressedTextureSubImage2D = NULL;
|
||||
PFNGLCOMPRESSEDTEXTURESUBIMAGE3DPROC glad_glCompressedTextureSubImage3D = NULL;
|
||||
PFNGLCOPYBUFFERSUBDATAPROC glad_glCopyBufferSubData = NULL;
|
||||
PFNGLCOPYIMAGESUBDATAPROC glad_glCopyImageSubData = NULL;
|
||||
PFNGLCOPYNAMEDBUFFERSUBDATAPROC glad_glCopyNamedBufferSubData = NULL;
|
||||
PFNGLCOPYTEXIMAGE1DPROC glad_glCopyTexImage1D = NULL;
|
||||
PFNGLCOPYTEXIMAGE2DPROC glad_glCopyTexImage2D = NULL;
|
||||
PFNGLCOPYTEXSUBIMAGE1DPROC glad_glCopyTexSubImage1D = NULL;
|
||||
PFNGLCOPYTEXSUBIMAGE2DPROC glad_glCopyTexSubImage2D = NULL;
|
||||
PFNGLCOPYTEXSUBIMAGE3DPROC glad_glCopyTexSubImage3D = NULL;
|
||||
PFNGLCOPYTEXTURESUBIMAGE1DPROC glad_glCopyTextureSubImage1D = NULL;
|
||||
PFNGLCOPYTEXTURESUBIMAGE2DPROC glad_glCopyTextureSubImage2D = NULL;
|
||||
PFNGLCOPYTEXTURESUBIMAGE3DPROC glad_glCopyTextureSubImage3D = NULL;
|
||||
PFNGLCREATEBUFFERSPROC glad_glCreateBuffers = NULL;
|
||||
PFNGLCREATEFRAMEBUFFERSPROC glad_glCreateFramebuffers = NULL;
|
||||
PFNGLCREATEPROGRAMPROC glad_glCreateProgram = NULL;
|
||||
PFNGLCREATEPROGRAMPIPELINESPROC glad_glCreateProgramPipelines = NULL;
|
||||
PFNGLCREATEQUERIESPROC glad_glCreateQueries = NULL;
|
||||
PFNGLCREATERENDERBUFFERSPROC glad_glCreateRenderbuffers = NULL;
|
||||
PFNGLCREATESAMPLERSPROC glad_glCreateSamplers = NULL;
|
||||
PFNGLCREATESHADERPROC glad_glCreateShader = NULL;
|
||||
PFNGLCREATESHADERPROGRAMVPROC glad_glCreateShaderProgramv = NULL;
|
||||
PFNGLCREATETEXTURESPROC glad_glCreateTextures = NULL;
|
||||
PFNGLCREATETRANSFORMFEEDBACKSPROC glad_glCreateTransformFeedbacks = NULL;
|
||||
PFNGLCREATEVERTEXARRAYSPROC glad_glCreateVertexArrays = NULL;
|
||||
PFNGLCULLFACEPROC glad_glCullFace = NULL;
|
||||
PFNGLDEBUGMESSAGECALLBACKPROC glad_glDebugMessageCallback = NULL;
|
||||
PFNGLDEBUGMESSAGECONTROLPROC glad_glDebugMessageControl = NULL;
|
||||
@ -413,7 +377,6 @@ PFNGLDEPTHRANGEINDEXEDPROC glad_glDepthRangeIndexed = NULL;
|
||||
PFNGLDEPTHRANGEFPROC glad_glDepthRangef = NULL;
|
||||
PFNGLDETACHSHADERPROC glad_glDetachShader = NULL;
|
||||
PFNGLDISABLEPROC glad_glDisable = NULL;
|
||||
PFNGLDISABLEVERTEXARRAYATTRIBPROC glad_glDisableVertexArrayAttrib = NULL;
|
||||
PFNGLDISABLEVERTEXATTRIBARRAYPROC glad_glDisableVertexAttribArray = NULL;
|
||||
PFNGLDISABLEIPROC glad_glDisablei = NULL;
|
||||
PFNGLDISPATCHCOMPUTEPROC glad_glDispatchCompute = NULL;
|
||||
@ -438,7 +401,6 @@ PFNGLDRAWTRANSFORMFEEDBACKINSTANCEDPROC glad_glDrawTransformFeedbackInstanced =
|
||||
PFNGLDRAWTRANSFORMFEEDBACKSTREAMPROC glad_glDrawTransformFeedbackStream = NULL;
|
||||
PFNGLDRAWTRANSFORMFEEDBACKSTREAMINSTANCEDPROC glad_glDrawTransformFeedbackStreamInstanced = NULL;
|
||||
PFNGLENABLEPROC glad_glEnable = NULL;
|
||||
PFNGLENABLEVERTEXARRAYATTRIBPROC glad_glEnableVertexArrayAttrib = NULL;
|
||||
PFNGLENABLEVERTEXATTRIBARRAYPROC glad_glEnableVertexAttribArray = NULL;
|
||||
PFNGLENABLEIPROC glad_glEnablei = NULL;
|
||||
PFNGLENDCONDITIONALRENDERPROC glad_glEndConditionalRender = NULL;
|
||||
@ -449,7 +411,6 @@ PFNGLFENCESYNCPROC glad_glFenceSync = NULL;
|
||||
PFNGLFINISHPROC glad_glFinish = NULL;
|
||||
PFNGLFLUSHPROC glad_glFlush = NULL;
|
||||
PFNGLFLUSHMAPPEDBUFFERRANGEPROC glad_glFlushMappedBufferRange = NULL;
|
||||
PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEPROC glad_glFlushMappedNamedBufferRange = NULL;
|
||||
PFNGLFRAMEBUFFERPARAMETERIPROC glad_glFramebufferParameteri = NULL;
|
||||
PFNGLFRAMEBUFFERRENDERBUFFERPROC glad_glFramebufferRenderbuffer = NULL;
|
||||
PFNGLFRAMEBUFFERTEXTUREPROC glad_glFramebufferTexture = NULL;
|
||||
@ -468,7 +429,6 @@ PFNGLGENTEXTURESPROC glad_glGenTextures = NULL;
|
||||
PFNGLGENTRANSFORMFEEDBACKSPROC glad_glGenTransformFeedbacks = NULL;
|
||||
PFNGLGENVERTEXARRAYSPROC glad_glGenVertexArrays = NULL;
|
||||
PFNGLGENERATEMIPMAPPROC glad_glGenerateMipmap = NULL;
|
||||
PFNGLGENERATETEXTUREMIPMAPPROC glad_glGenerateTextureMipmap = NULL;
|
||||
PFNGLGETACTIVEATOMICCOUNTERBUFFERIVPROC glad_glGetActiveAtomicCounterBufferiv = NULL;
|
||||
PFNGLGETACTIVEATTRIBPROC glad_glGetActiveAttrib = NULL;
|
||||
PFNGLGETACTIVESUBROUTINENAMEPROC glad_glGetActiveSubroutineName = NULL;
|
||||
@ -488,8 +448,6 @@ PFNGLGETBUFFERPARAMETERIVPROC glad_glGetBufferParameteriv = NULL;
|
||||
PFNGLGETBUFFERPOINTERVPROC glad_glGetBufferPointerv = NULL;
|
||||
PFNGLGETBUFFERSUBDATAPROC glad_glGetBufferSubData = NULL;
|
||||
PFNGLGETCOMPRESSEDTEXIMAGEPROC glad_glGetCompressedTexImage = NULL;
|
||||
PFNGLGETCOMPRESSEDTEXTUREIMAGEPROC glad_glGetCompressedTextureImage = NULL;
|
||||
PFNGLGETCOMPRESSEDTEXTURESUBIMAGEPROC glad_glGetCompressedTextureSubImage = NULL;
|
||||
PFNGLGETDEBUGMESSAGELOGPROC glad_glGetDebugMessageLog = NULL;
|
||||
PFNGLGETDOUBLEI_VPROC glad_glGetDoublei_v = NULL;
|
||||
PFNGLGETDOUBLEVPROC glad_glGetDoublev = NULL;
|
||||
@ -508,13 +466,6 @@ PFNGLGETINTEGERVPROC glad_glGetIntegerv = NULL;
|
||||
PFNGLGETINTERNALFORMATI64VPROC glad_glGetInternalformati64v = NULL;
|
||||
PFNGLGETINTERNALFORMATIVPROC glad_glGetInternalformativ = NULL;
|
||||
PFNGLGETMULTISAMPLEFVPROC glad_glGetMultisamplefv = NULL;
|
||||
PFNGLGETNAMEDBUFFERPARAMETERI64VPROC glad_glGetNamedBufferParameteri64v = NULL;
|
||||
PFNGLGETNAMEDBUFFERPARAMETERIVPROC glad_glGetNamedBufferParameteriv = NULL;
|
||||
PFNGLGETNAMEDBUFFERPOINTERVPROC glad_glGetNamedBufferPointerv = NULL;
|
||||
PFNGLGETNAMEDBUFFERSUBDATAPROC glad_glGetNamedBufferSubData = NULL;
|
||||
PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVPROC glad_glGetNamedFramebufferAttachmentParameteriv = NULL;
|
||||
PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVPROC glad_glGetNamedFramebufferParameteriv = NULL;
|
||||
PFNGLGETNAMEDRENDERBUFFERPARAMETERIVPROC glad_glGetNamedRenderbufferParameteriv = NULL;
|
||||
PFNGLGETOBJECTLABELPROC glad_glGetObjectLabel = NULL;
|
||||
PFNGLGETOBJECTPTRLABELPROC glad_glGetObjectPtrLabel = NULL;
|
||||
PFNGLGETPOINTERVPROC glad_glGetPointerv = NULL;
|
||||
@ -530,10 +481,6 @@ PFNGLGETPROGRAMRESOURCENAMEPROC glad_glGetProgramResourceName = NULL;
|
||||
PFNGLGETPROGRAMRESOURCEIVPROC glad_glGetProgramResourceiv = NULL;
|
||||
PFNGLGETPROGRAMSTAGEIVPROC glad_glGetProgramStageiv = NULL;
|
||||
PFNGLGETPROGRAMIVPROC glad_glGetProgramiv = NULL;
|
||||
PFNGLGETQUERYBUFFEROBJECTI64VPROC glad_glGetQueryBufferObjecti64v = NULL;
|
||||
PFNGLGETQUERYBUFFEROBJECTIVPROC glad_glGetQueryBufferObjectiv = NULL;
|
||||
PFNGLGETQUERYBUFFEROBJECTUI64VPROC glad_glGetQueryBufferObjectui64v = NULL;
|
||||
PFNGLGETQUERYBUFFEROBJECTUIVPROC glad_glGetQueryBufferObjectuiv = NULL;
|
||||
PFNGLGETQUERYINDEXEDIVPROC glad_glGetQueryIndexediv = NULL;
|
||||
PFNGLGETQUERYOBJECTI64VPROC glad_glGetQueryObjecti64v = NULL;
|
||||
PFNGLGETQUERYOBJECTIVPROC glad_glGetQueryObjectiv = NULL;
|
||||
@ -561,18 +508,7 @@ PFNGLGETTEXPARAMETERIIVPROC glad_glGetTexParameterIiv = NULL;
|
||||
PFNGLGETTEXPARAMETERIUIVPROC glad_glGetTexParameterIuiv = NULL;
|
||||
PFNGLGETTEXPARAMETERFVPROC glad_glGetTexParameterfv = NULL;
|
||||
PFNGLGETTEXPARAMETERIVPROC glad_glGetTexParameteriv = NULL;
|
||||
PFNGLGETTEXTUREIMAGEPROC glad_glGetTextureImage = NULL;
|
||||
PFNGLGETTEXTURELEVELPARAMETERFVPROC glad_glGetTextureLevelParameterfv = NULL;
|
||||
PFNGLGETTEXTURELEVELPARAMETERIVPROC glad_glGetTextureLevelParameteriv = NULL;
|
||||
PFNGLGETTEXTUREPARAMETERIIVPROC glad_glGetTextureParameterIiv = NULL;
|
||||
PFNGLGETTEXTUREPARAMETERIUIVPROC glad_glGetTextureParameterIuiv = NULL;
|
||||
PFNGLGETTEXTUREPARAMETERFVPROC glad_glGetTextureParameterfv = NULL;
|
||||
PFNGLGETTEXTUREPARAMETERIVPROC glad_glGetTextureParameteriv = NULL;
|
||||
PFNGLGETTEXTURESUBIMAGEPROC glad_glGetTextureSubImage = NULL;
|
||||
PFNGLGETTRANSFORMFEEDBACKVARYINGPROC glad_glGetTransformFeedbackVarying = NULL;
|
||||
PFNGLGETTRANSFORMFEEDBACKI64_VPROC glad_glGetTransformFeedbacki64_v = NULL;
|
||||
PFNGLGETTRANSFORMFEEDBACKI_VPROC glad_glGetTransformFeedbacki_v = NULL;
|
||||
PFNGLGETTRANSFORMFEEDBACKIVPROC glad_glGetTransformFeedbackiv = NULL;
|
||||
PFNGLGETUNIFORMBLOCKINDEXPROC glad_glGetUniformBlockIndex = NULL;
|
||||
PFNGLGETUNIFORMINDICESPROC glad_glGetUniformIndices = NULL;
|
||||
PFNGLGETUNIFORMLOCATIONPROC glad_glGetUniformLocation = NULL;
|
||||
@ -581,9 +517,6 @@ PFNGLGETUNIFORMDVPROC glad_glGetUniformdv = NULL;
|
||||
PFNGLGETUNIFORMFVPROC glad_glGetUniformfv = NULL;
|
||||
PFNGLGETUNIFORMIVPROC glad_glGetUniformiv = NULL;
|
||||
PFNGLGETUNIFORMUIVPROC glad_glGetUniformuiv = NULL;
|
||||
PFNGLGETVERTEXARRAYINDEXED64IVPROC glad_glGetVertexArrayIndexed64iv = NULL;
|
||||
PFNGLGETVERTEXARRAYINDEXEDIVPROC glad_glGetVertexArrayIndexediv = NULL;
|
||||
PFNGLGETVERTEXARRAYIVPROC glad_glGetVertexArrayiv = NULL;
|
||||
PFNGLGETVERTEXATTRIBIIVPROC glad_glGetVertexAttribIiv = NULL;
|
||||
PFNGLGETVERTEXATTRIBIUIVPROC glad_glGetVertexAttribIuiv = NULL;
|
||||
PFNGLGETVERTEXATTRIBLDVPROC glad_glGetVertexAttribLdv = NULL;
|
||||
@ -591,21 +524,6 @@ PFNGLGETVERTEXATTRIBPOINTERVPROC glad_glGetVertexAttribPointerv = NULL;
|
||||
PFNGLGETVERTEXATTRIBDVPROC glad_glGetVertexAttribdv = NULL;
|
||||
PFNGLGETVERTEXATTRIBFVPROC glad_glGetVertexAttribfv = NULL;
|
||||
PFNGLGETVERTEXATTRIBIVPROC glad_glGetVertexAttribiv = NULL;
|
||||
PFNGLGETNCOLORTABLEPROC glad_glGetnColorTable = NULL;
|
||||
PFNGLGETNCOMPRESSEDTEXIMAGEPROC glad_glGetnCompressedTexImage = NULL;
|
||||
PFNGLGETNCONVOLUTIONFILTERPROC glad_glGetnConvolutionFilter = NULL;
|
||||
PFNGLGETNHISTOGRAMPROC glad_glGetnHistogram = NULL;
|
||||
PFNGLGETNMAPDVPROC glad_glGetnMapdv = NULL;
|
||||
PFNGLGETNMAPFVPROC glad_glGetnMapfv = NULL;
|
||||
PFNGLGETNMAPIVPROC glad_glGetnMapiv = NULL;
|
||||
PFNGLGETNMINMAXPROC glad_glGetnMinmax = NULL;
|
||||
PFNGLGETNPIXELMAPFVPROC glad_glGetnPixelMapfv = NULL;
|
||||
PFNGLGETNPIXELMAPUIVPROC glad_glGetnPixelMapuiv = NULL;
|
||||
PFNGLGETNPIXELMAPUSVPROC glad_glGetnPixelMapusv = NULL;
|
||||
PFNGLGETNPOLYGONSTIPPLEPROC glad_glGetnPolygonStipple = NULL;
|
||||
PFNGLGETNSEPARABLEFILTERPROC glad_glGetnSeparableFilter = NULL;
|
||||
PFNGLGETNTEXIMAGEPROC glad_glGetnTexImage = NULL;
|
||||
PFNGLGETNUNIFORMDVPROC glad_glGetnUniformdv = NULL;
|
||||
PFNGLGETNUNIFORMFVPROC glad_glGetnUniformfv = NULL;
|
||||
PFNGLGETNUNIFORMIVPROC glad_glGetnUniformiv = NULL;
|
||||
PFNGLGETNUNIFORMUIVPROC glad_glGetnUniformuiv = NULL;
|
||||
@ -613,8 +531,6 @@ PFNGLHINTPROC glad_glHint = NULL;
|
||||
PFNGLINVALIDATEBUFFERDATAPROC glad_glInvalidateBufferData = NULL;
|
||||
PFNGLINVALIDATEBUFFERSUBDATAPROC glad_glInvalidateBufferSubData = NULL;
|
||||
PFNGLINVALIDATEFRAMEBUFFERPROC glad_glInvalidateFramebuffer = NULL;
|
||||
PFNGLINVALIDATENAMEDFRAMEBUFFERDATAPROC glad_glInvalidateNamedFramebufferData = NULL;
|
||||
PFNGLINVALIDATENAMEDFRAMEBUFFERSUBDATAPROC glad_glInvalidateNamedFramebufferSubData = NULL;
|
||||
PFNGLINVALIDATESUBFRAMEBUFFERPROC glad_glInvalidateSubFramebuffer = NULL;
|
||||
PFNGLINVALIDATETEXIMAGEPROC glad_glInvalidateTexImage = NULL;
|
||||
PFNGLINVALIDATETEXSUBIMAGEPROC glad_glInvalidateTexSubImage = NULL;
|
||||
@ -637,18 +553,14 @@ PFNGLLINKPROGRAMPROC glad_glLinkProgram = NULL;
|
||||
PFNGLLOGICOPPROC glad_glLogicOp = NULL;
|
||||
PFNGLMAPBUFFERPROC glad_glMapBuffer = NULL;
|
||||
PFNGLMAPBUFFERRANGEPROC glad_glMapBufferRange = NULL;
|
||||
PFNGLMAPNAMEDBUFFERPROC glad_glMapNamedBuffer = NULL;
|
||||
PFNGLMAPNAMEDBUFFERRANGEPROC glad_glMapNamedBufferRange = NULL;
|
||||
PFNGLMEMORYBARRIERPROC glad_glMemoryBarrier = NULL;
|
||||
PFNGLMEMORYBARRIERBYREGIONPROC glad_glMemoryBarrierByRegion = NULL;
|
||||
PFNGLMINSAMPLESHADINGPROC glad_glMinSampleShading = NULL;
|
||||
PFNGLMULTIDRAWARRAYSPROC glad_glMultiDrawArrays = NULL;
|
||||
PFNGLMULTIDRAWARRAYSINDIRECTPROC glad_glMultiDrawArraysIndirect = NULL;
|
||||
PFNGLMULTIDRAWARRAYSINDIRECTCOUNTPROC glad_glMultiDrawArraysIndirectCount = NULL;
|
||||
PFNGLMULTIDRAWELEMENTSPROC glad_glMultiDrawElements = NULL;
|
||||
PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC glad_glMultiDrawElementsBaseVertex = NULL;
|
||||
PFNGLMULTIDRAWELEMENTSINDIRECTPROC glad_glMultiDrawElementsIndirect = NULL;
|
||||
PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTPROC glad_glMultiDrawElementsIndirectCount = NULL;
|
||||
PFNGLMULTITEXCOORDP1UIPROC glad_glMultiTexCoordP1ui = NULL;
|
||||
PFNGLMULTITEXCOORDP1UIVPROC glad_glMultiTexCoordP1uiv = NULL;
|
||||
PFNGLMULTITEXCOORDP2UIPROC glad_glMultiTexCoordP2ui = NULL;
|
||||
@ -657,18 +569,6 @@ PFNGLMULTITEXCOORDP3UIPROC glad_glMultiTexCoordP3ui = NULL;
|
||||
PFNGLMULTITEXCOORDP3UIVPROC glad_glMultiTexCoordP3uiv = NULL;
|
||||
PFNGLMULTITEXCOORDP4UIPROC glad_glMultiTexCoordP4ui = NULL;
|
||||
PFNGLMULTITEXCOORDP4UIVPROC glad_glMultiTexCoordP4uiv = NULL;
|
||||
PFNGLNAMEDBUFFERDATAPROC glad_glNamedBufferData = NULL;
|
||||
PFNGLNAMEDBUFFERSTORAGEPROC glad_glNamedBufferStorage = NULL;
|
||||
PFNGLNAMEDBUFFERSUBDATAPROC glad_glNamedBufferSubData = NULL;
|
||||
PFNGLNAMEDFRAMEBUFFERDRAWBUFFERPROC glad_glNamedFramebufferDrawBuffer = NULL;
|
||||
PFNGLNAMEDFRAMEBUFFERDRAWBUFFERSPROC glad_glNamedFramebufferDrawBuffers = NULL;
|
||||
PFNGLNAMEDFRAMEBUFFERPARAMETERIPROC glad_glNamedFramebufferParameteri = NULL;
|
||||
PFNGLNAMEDFRAMEBUFFERREADBUFFERPROC glad_glNamedFramebufferReadBuffer = NULL;
|
||||
PFNGLNAMEDFRAMEBUFFERRENDERBUFFERPROC glad_glNamedFramebufferRenderbuffer = NULL;
|
||||
PFNGLNAMEDFRAMEBUFFERTEXTUREPROC glad_glNamedFramebufferTexture = NULL;
|
||||
PFNGLNAMEDFRAMEBUFFERTEXTURELAYERPROC glad_glNamedFramebufferTextureLayer = NULL;
|
||||
PFNGLNAMEDRENDERBUFFERSTORAGEPROC glad_glNamedRenderbufferStorage = NULL;
|
||||
PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEPROC glad_glNamedRenderbufferStorageMultisample = NULL;
|
||||
PFNGLNORMALP3UIPROC glad_glNormalP3ui = NULL;
|
||||
PFNGLNORMALP3UIVPROC glad_glNormalP3uiv = NULL;
|
||||
PFNGLOBJECTLABELPROC glad_glObjectLabel = NULL;
|
||||
@ -685,7 +585,6 @@ PFNGLPOINTPARAMETERIVPROC glad_glPointParameteriv = NULL;
|
||||
PFNGLPOINTSIZEPROC glad_glPointSize = NULL;
|
||||
PFNGLPOLYGONMODEPROC glad_glPolygonMode = NULL;
|
||||
PFNGLPOLYGONOFFSETPROC glad_glPolygonOffset = NULL;
|
||||
PFNGLPOLYGONOFFSETCLAMPPROC glad_glPolygonOffsetClamp = NULL;
|
||||
PFNGLPOPDEBUGGROUPPROC glad_glPopDebugGroup = NULL;
|
||||
PFNGLPRIMITIVEBOUNDINGBOXPROC glad_glPrimitiveBoundingBox = NULL;
|
||||
PFNGLPRIMITIVERESTARTINDEXPROC glad_glPrimitiveRestartIndex = NULL;
|
||||
@ -768,7 +667,6 @@ PFNGLSECONDARYCOLORP3UIVPROC glad_glSecondaryColorP3uiv = NULL;
|
||||
PFNGLSHADERBINARYPROC glad_glShaderBinary = NULL;
|
||||
PFNGLSHADERSOURCEPROC glad_glShaderSource = NULL;
|
||||
PFNGLSHADERSTORAGEBLOCKBINDINGPROC glad_glShaderStorageBlockBinding = NULL;
|
||||
PFNGLSPECIALIZESHADERPROC glad_glSpecializeShader = NULL;
|
||||
PFNGLSTENCILFUNCPROC glad_glStencilFunc = NULL;
|
||||
PFNGLSTENCILFUNCSEPARATEPROC glad_glStencilFuncSeparate = NULL;
|
||||
PFNGLSTENCILMASKPROC glad_glStencilMask = NULL;
|
||||
@ -804,26 +702,7 @@ PFNGLTEXSTORAGE3DMULTISAMPLEPROC glad_glTexStorage3DMultisample = NULL;
|
||||
PFNGLTEXSUBIMAGE1DPROC glad_glTexSubImage1D = NULL;
|
||||
PFNGLTEXSUBIMAGE2DPROC glad_glTexSubImage2D = NULL;
|
||||
PFNGLTEXSUBIMAGE3DPROC glad_glTexSubImage3D = NULL;
|
||||
PFNGLTEXTUREBARRIERPROC glad_glTextureBarrier = NULL;
|
||||
PFNGLTEXTUREBUFFERPROC glad_glTextureBuffer = NULL;
|
||||
PFNGLTEXTUREBUFFERRANGEPROC glad_glTextureBufferRange = NULL;
|
||||
PFNGLTEXTUREPARAMETERIIVPROC glad_glTextureParameterIiv = NULL;
|
||||
PFNGLTEXTUREPARAMETERIUIVPROC glad_glTextureParameterIuiv = NULL;
|
||||
PFNGLTEXTUREPARAMETERFPROC glad_glTextureParameterf = NULL;
|
||||
PFNGLTEXTUREPARAMETERFVPROC glad_glTextureParameterfv = NULL;
|
||||
PFNGLTEXTUREPARAMETERIPROC glad_glTextureParameteri = NULL;
|
||||
PFNGLTEXTUREPARAMETERIVPROC glad_glTextureParameteriv = NULL;
|
||||
PFNGLTEXTURESTORAGE1DPROC glad_glTextureStorage1D = NULL;
|
||||
PFNGLTEXTURESTORAGE2DPROC glad_glTextureStorage2D = NULL;
|
||||
PFNGLTEXTURESTORAGE2DMULTISAMPLEPROC glad_glTextureStorage2DMultisample = NULL;
|
||||
PFNGLTEXTURESTORAGE3DPROC glad_glTextureStorage3D = NULL;
|
||||
PFNGLTEXTURESTORAGE3DMULTISAMPLEPROC glad_glTextureStorage3DMultisample = NULL;
|
||||
PFNGLTEXTURESUBIMAGE1DPROC glad_glTextureSubImage1D = NULL;
|
||||
PFNGLTEXTURESUBIMAGE2DPROC glad_glTextureSubImage2D = NULL;
|
||||
PFNGLTEXTURESUBIMAGE3DPROC glad_glTextureSubImage3D = NULL;
|
||||
PFNGLTEXTUREVIEWPROC glad_glTextureView = NULL;
|
||||
PFNGLTRANSFORMFEEDBACKBUFFERBASEPROC glad_glTransformFeedbackBufferBase = NULL;
|
||||
PFNGLTRANSFORMFEEDBACKBUFFERRANGEPROC glad_glTransformFeedbackBufferRange = NULL;
|
||||
PFNGLTRANSFORMFEEDBACKVARYINGSPROC glad_glTransformFeedbackVaryings = NULL;
|
||||
PFNGLUNIFORM1DPROC glad_glUniform1d = NULL;
|
||||
PFNGLUNIFORM1DVPROC glad_glUniform1dv = NULL;
|
||||
@ -878,19 +757,10 @@ PFNGLUNIFORMMATRIX4X3DVPROC glad_glUniformMatrix4x3dv = NULL;
|
||||
PFNGLUNIFORMMATRIX4X3FVPROC glad_glUniformMatrix4x3fv = NULL;
|
||||
PFNGLUNIFORMSUBROUTINESUIVPROC glad_glUniformSubroutinesuiv = NULL;
|
||||
PFNGLUNMAPBUFFERPROC glad_glUnmapBuffer = NULL;
|
||||
PFNGLUNMAPNAMEDBUFFERPROC glad_glUnmapNamedBuffer = NULL;
|
||||
PFNGLUSEPROGRAMPROC glad_glUseProgram = NULL;
|
||||
PFNGLUSEPROGRAMSTAGESPROC glad_glUseProgramStages = NULL;
|
||||
PFNGLVALIDATEPROGRAMPROC glad_glValidateProgram = NULL;
|
||||
PFNGLVALIDATEPROGRAMPIPELINEPROC glad_glValidateProgramPipeline = NULL;
|
||||
PFNGLVERTEXARRAYATTRIBBINDINGPROC glad_glVertexArrayAttribBinding = NULL;
|
||||
PFNGLVERTEXARRAYATTRIBFORMATPROC glad_glVertexArrayAttribFormat = NULL;
|
||||
PFNGLVERTEXARRAYATTRIBIFORMATPROC glad_glVertexArrayAttribIFormat = NULL;
|
||||
PFNGLVERTEXARRAYATTRIBLFORMATPROC glad_glVertexArrayAttribLFormat = NULL;
|
||||
PFNGLVERTEXARRAYBINDINGDIVISORPROC glad_glVertexArrayBindingDivisor = NULL;
|
||||
PFNGLVERTEXARRAYELEMENTBUFFERPROC glad_glVertexArrayElementBuffer = NULL;
|
||||
PFNGLVERTEXARRAYVERTEXBUFFERPROC glad_glVertexArrayVertexBuffer = NULL;
|
||||
PFNGLVERTEXARRAYVERTEXBUFFERSPROC glad_glVertexArrayVertexBuffers = NULL;
|
||||
PFNGLVERTEXATTRIB1DPROC glad_glVertexAttrib1d = NULL;
|
||||
PFNGLVERTEXATTRIB1DVPROC glad_glVertexAttrib1dv = NULL;
|
||||
PFNGLVERTEXATTRIB1FPROC glad_glVertexAttrib1f = NULL;
|
||||
@ -986,8 +856,15 @@ PFNGLWAITSYNCPROC glad_glWaitSync = NULL;
|
||||
int GLAD_GL_ARB_buffer_storage = 0;
|
||||
int GLAD_GL_ARB_clear_texture = 0;
|
||||
int GLAD_GL_ARB_get_texture_sub_image = 0;
|
||||
int GLAD_GL_ARB_texture_compression_bptc = 0;
|
||||
int GLAD_GL_EXT_buffer_storage = 0;
|
||||
int GLAD_GL_EXT_clip_cull_distance = 0;
|
||||
int GLAD_GL_EXT_texture_compression_s3tc = 0;
|
||||
PFNGLBUFFERSTORAGEPROC glad_glBufferStorage = NULL;
|
||||
PFNGLCLEARTEXIMAGEPROC glad_glClearTexImage = NULL;
|
||||
PFNGLCLEARTEXSUBIMAGEPROC glad_glClearTexSubImage = NULL;
|
||||
PFNGLGETTEXTURESUBIMAGEPROC glad_glGetTextureSubImage = NULL;
|
||||
PFNGLGETCOMPRESSEDTEXTURESUBIMAGEPROC glad_glGetCompressedTextureSubImage = NULL;
|
||||
PFNGLBUFFERSTORAGEEXTPROC glad_glBufferStorageEXT = NULL;
|
||||
static void load_GL_VERSION_1_0(GLADloadproc load) {
|
||||
if(!GLAD_GL_VERSION_1_0) return;
|
||||
@ -1605,150 +1482,6 @@ static void load_GL_VERSION_4_3(GLADloadproc load) {
|
||||
glad_glGetObjectPtrLabel = (PFNGLGETOBJECTPTRLABELPROC)load("glGetObjectPtrLabel");
|
||||
glad_glGetPointerv = (PFNGLGETPOINTERVPROC)load("glGetPointerv");
|
||||
}
|
||||
static void load_GL_VERSION_4_4(GLADloadproc load) {
|
||||
if(!GLAD_GL_VERSION_4_4) return;
|
||||
glad_glBufferStorage = (PFNGLBUFFERSTORAGEPROC)load("glBufferStorage");
|
||||
glad_glClearTexImage = (PFNGLCLEARTEXIMAGEPROC)load("glClearTexImage");
|
||||
glad_glClearTexSubImage = (PFNGLCLEARTEXSUBIMAGEPROC)load("glClearTexSubImage");
|
||||
glad_glBindBuffersBase = (PFNGLBINDBUFFERSBASEPROC)load("glBindBuffersBase");
|
||||
glad_glBindBuffersRange = (PFNGLBINDBUFFERSRANGEPROC)load("glBindBuffersRange");
|
||||
glad_glBindTextures = (PFNGLBINDTEXTURESPROC)load("glBindTextures");
|
||||
glad_glBindSamplers = (PFNGLBINDSAMPLERSPROC)load("glBindSamplers");
|
||||
glad_glBindImageTextures = (PFNGLBINDIMAGETEXTURESPROC)load("glBindImageTextures");
|
||||
glad_glBindVertexBuffers = (PFNGLBINDVERTEXBUFFERSPROC)load("glBindVertexBuffers");
|
||||
}
|
||||
static void load_GL_VERSION_4_5(GLADloadproc load) {
|
||||
if(!GLAD_GL_VERSION_4_5) return;
|
||||
glad_glClipControl = (PFNGLCLIPCONTROLPROC)load("glClipControl");
|
||||
glad_glCreateTransformFeedbacks = (PFNGLCREATETRANSFORMFEEDBACKSPROC)load("glCreateTransformFeedbacks");
|
||||
glad_glTransformFeedbackBufferBase = (PFNGLTRANSFORMFEEDBACKBUFFERBASEPROC)load("glTransformFeedbackBufferBase");
|
||||
glad_glTransformFeedbackBufferRange = (PFNGLTRANSFORMFEEDBACKBUFFERRANGEPROC)load("glTransformFeedbackBufferRange");
|
||||
glad_glGetTransformFeedbackiv = (PFNGLGETTRANSFORMFEEDBACKIVPROC)load("glGetTransformFeedbackiv");
|
||||
glad_glGetTransformFeedbacki_v = (PFNGLGETTRANSFORMFEEDBACKI_VPROC)load("glGetTransformFeedbacki_v");
|
||||
glad_glGetTransformFeedbacki64_v = (PFNGLGETTRANSFORMFEEDBACKI64_VPROC)load("glGetTransformFeedbacki64_v");
|
||||
glad_glCreateBuffers = (PFNGLCREATEBUFFERSPROC)load("glCreateBuffers");
|
||||
glad_glNamedBufferStorage = (PFNGLNAMEDBUFFERSTORAGEPROC)load("glNamedBufferStorage");
|
||||
glad_glNamedBufferData = (PFNGLNAMEDBUFFERDATAPROC)load("glNamedBufferData");
|
||||
glad_glNamedBufferSubData = (PFNGLNAMEDBUFFERSUBDATAPROC)load("glNamedBufferSubData");
|
||||
glad_glCopyNamedBufferSubData = (PFNGLCOPYNAMEDBUFFERSUBDATAPROC)load("glCopyNamedBufferSubData");
|
||||
glad_glClearNamedBufferData = (PFNGLCLEARNAMEDBUFFERDATAPROC)load("glClearNamedBufferData");
|
||||
glad_glClearNamedBufferSubData = (PFNGLCLEARNAMEDBUFFERSUBDATAPROC)load("glClearNamedBufferSubData");
|
||||
glad_glMapNamedBuffer = (PFNGLMAPNAMEDBUFFERPROC)load("glMapNamedBuffer");
|
||||
glad_glMapNamedBufferRange = (PFNGLMAPNAMEDBUFFERRANGEPROC)load("glMapNamedBufferRange");
|
||||
glad_glUnmapNamedBuffer = (PFNGLUNMAPNAMEDBUFFERPROC)load("glUnmapNamedBuffer");
|
||||
glad_glFlushMappedNamedBufferRange = (PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEPROC)load("glFlushMappedNamedBufferRange");
|
||||
glad_glGetNamedBufferParameteriv = (PFNGLGETNAMEDBUFFERPARAMETERIVPROC)load("glGetNamedBufferParameteriv");
|
||||
glad_glGetNamedBufferParameteri64v = (PFNGLGETNAMEDBUFFERPARAMETERI64VPROC)load("glGetNamedBufferParameteri64v");
|
||||
glad_glGetNamedBufferPointerv = (PFNGLGETNAMEDBUFFERPOINTERVPROC)load("glGetNamedBufferPointerv");
|
||||
glad_glGetNamedBufferSubData = (PFNGLGETNAMEDBUFFERSUBDATAPROC)load("glGetNamedBufferSubData");
|
||||
glad_glCreateFramebuffers = (PFNGLCREATEFRAMEBUFFERSPROC)load("glCreateFramebuffers");
|
||||
glad_glNamedFramebufferRenderbuffer = (PFNGLNAMEDFRAMEBUFFERRENDERBUFFERPROC)load("glNamedFramebufferRenderbuffer");
|
||||
glad_glNamedFramebufferParameteri = (PFNGLNAMEDFRAMEBUFFERPARAMETERIPROC)load("glNamedFramebufferParameteri");
|
||||
glad_glNamedFramebufferTexture = (PFNGLNAMEDFRAMEBUFFERTEXTUREPROC)load("glNamedFramebufferTexture");
|
||||
glad_glNamedFramebufferTextureLayer = (PFNGLNAMEDFRAMEBUFFERTEXTURELAYERPROC)load("glNamedFramebufferTextureLayer");
|
||||
glad_glNamedFramebufferDrawBuffer = (PFNGLNAMEDFRAMEBUFFERDRAWBUFFERPROC)load("glNamedFramebufferDrawBuffer");
|
||||
glad_glNamedFramebufferDrawBuffers = (PFNGLNAMEDFRAMEBUFFERDRAWBUFFERSPROC)load("glNamedFramebufferDrawBuffers");
|
||||
glad_glNamedFramebufferReadBuffer = (PFNGLNAMEDFRAMEBUFFERREADBUFFERPROC)load("glNamedFramebufferReadBuffer");
|
||||
glad_glInvalidateNamedFramebufferData = (PFNGLINVALIDATENAMEDFRAMEBUFFERDATAPROC)load("glInvalidateNamedFramebufferData");
|
||||
glad_glInvalidateNamedFramebufferSubData = (PFNGLINVALIDATENAMEDFRAMEBUFFERSUBDATAPROC)load("glInvalidateNamedFramebufferSubData");
|
||||
glad_glClearNamedFramebufferiv = (PFNGLCLEARNAMEDFRAMEBUFFERIVPROC)load("glClearNamedFramebufferiv");
|
||||
glad_glClearNamedFramebufferuiv = (PFNGLCLEARNAMEDFRAMEBUFFERUIVPROC)load("glClearNamedFramebufferuiv");
|
||||
glad_glClearNamedFramebufferfv = (PFNGLCLEARNAMEDFRAMEBUFFERFVPROC)load("glClearNamedFramebufferfv");
|
||||
glad_glClearNamedFramebufferfi = (PFNGLCLEARNAMEDFRAMEBUFFERFIPROC)load("glClearNamedFramebufferfi");
|
||||
glad_glBlitNamedFramebuffer = (PFNGLBLITNAMEDFRAMEBUFFERPROC)load("glBlitNamedFramebuffer");
|
||||
glad_glCheckNamedFramebufferStatus = (PFNGLCHECKNAMEDFRAMEBUFFERSTATUSPROC)load("glCheckNamedFramebufferStatus");
|
||||
glad_glGetNamedFramebufferParameteriv = (PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVPROC)load("glGetNamedFramebufferParameteriv");
|
||||
glad_glGetNamedFramebufferAttachmentParameteriv = (PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVPROC)load("glGetNamedFramebufferAttachmentParameteriv");
|
||||
glad_glCreateRenderbuffers = (PFNGLCREATERENDERBUFFERSPROC)load("glCreateRenderbuffers");
|
||||
glad_glNamedRenderbufferStorage = (PFNGLNAMEDRENDERBUFFERSTORAGEPROC)load("glNamedRenderbufferStorage");
|
||||
glad_glNamedRenderbufferStorageMultisample = (PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEPROC)load("glNamedRenderbufferStorageMultisample");
|
||||
glad_glGetNamedRenderbufferParameteriv = (PFNGLGETNAMEDRENDERBUFFERPARAMETERIVPROC)load("glGetNamedRenderbufferParameteriv");
|
||||
glad_glCreateTextures = (PFNGLCREATETEXTURESPROC)load("glCreateTextures");
|
||||
glad_glTextureBuffer = (PFNGLTEXTUREBUFFERPROC)load("glTextureBuffer");
|
||||
glad_glTextureBufferRange = (PFNGLTEXTUREBUFFERRANGEPROC)load("glTextureBufferRange");
|
||||
glad_glTextureStorage1D = (PFNGLTEXTURESTORAGE1DPROC)load("glTextureStorage1D");
|
||||
glad_glTextureStorage2D = (PFNGLTEXTURESTORAGE2DPROC)load("glTextureStorage2D");
|
||||
glad_glTextureStorage3D = (PFNGLTEXTURESTORAGE3DPROC)load("glTextureStorage3D");
|
||||
glad_glTextureStorage2DMultisample = (PFNGLTEXTURESTORAGE2DMULTISAMPLEPROC)load("glTextureStorage2DMultisample");
|
||||
glad_glTextureStorage3DMultisample = (PFNGLTEXTURESTORAGE3DMULTISAMPLEPROC)load("glTextureStorage3DMultisample");
|
||||
glad_glTextureSubImage1D = (PFNGLTEXTURESUBIMAGE1DPROC)load("glTextureSubImage1D");
|
||||
glad_glTextureSubImage2D = (PFNGLTEXTURESUBIMAGE2DPROC)load("glTextureSubImage2D");
|
||||
glad_glTextureSubImage3D = (PFNGLTEXTURESUBIMAGE3DPROC)load("glTextureSubImage3D");
|
||||
glad_glCompressedTextureSubImage1D = (PFNGLCOMPRESSEDTEXTURESUBIMAGE1DPROC)load("glCompressedTextureSubImage1D");
|
||||
glad_glCompressedTextureSubImage2D = (PFNGLCOMPRESSEDTEXTURESUBIMAGE2DPROC)load("glCompressedTextureSubImage2D");
|
||||
glad_glCompressedTextureSubImage3D = (PFNGLCOMPRESSEDTEXTURESUBIMAGE3DPROC)load("glCompressedTextureSubImage3D");
|
||||
glad_glCopyTextureSubImage1D = (PFNGLCOPYTEXTURESUBIMAGE1DPROC)load("glCopyTextureSubImage1D");
|
||||
glad_glCopyTextureSubImage2D = (PFNGLCOPYTEXTURESUBIMAGE2DPROC)load("glCopyTextureSubImage2D");
|
||||
glad_glCopyTextureSubImage3D = (PFNGLCOPYTEXTURESUBIMAGE3DPROC)load("glCopyTextureSubImage3D");
|
||||
glad_glTextureParameterf = (PFNGLTEXTUREPARAMETERFPROC)load("glTextureParameterf");
|
||||
glad_glTextureParameterfv = (PFNGLTEXTUREPARAMETERFVPROC)load("glTextureParameterfv");
|
||||
glad_glTextureParameteri = (PFNGLTEXTUREPARAMETERIPROC)load("glTextureParameteri");
|
||||
glad_glTextureParameterIiv = (PFNGLTEXTUREPARAMETERIIVPROC)load("glTextureParameterIiv");
|
||||
glad_glTextureParameterIuiv = (PFNGLTEXTUREPARAMETERIUIVPROC)load("glTextureParameterIuiv");
|
||||
glad_glTextureParameteriv = (PFNGLTEXTUREPARAMETERIVPROC)load("glTextureParameteriv");
|
||||
glad_glGenerateTextureMipmap = (PFNGLGENERATETEXTUREMIPMAPPROC)load("glGenerateTextureMipmap");
|
||||
glad_glBindTextureUnit = (PFNGLBINDTEXTUREUNITPROC)load("glBindTextureUnit");
|
||||
glad_glGetTextureImage = (PFNGLGETTEXTUREIMAGEPROC)load("glGetTextureImage");
|
||||
glad_glGetCompressedTextureImage = (PFNGLGETCOMPRESSEDTEXTUREIMAGEPROC)load("glGetCompressedTextureImage");
|
||||
glad_glGetTextureLevelParameterfv = (PFNGLGETTEXTURELEVELPARAMETERFVPROC)load("glGetTextureLevelParameterfv");
|
||||
glad_glGetTextureLevelParameteriv = (PFNGLGETTEXTURELEVELPARAMETERIVPROC)load("glGetTextureLevelParameteriv");
|
||||
glad_glGetTextureParameterfv = (PFNGLGETTEXTUREPARAMETERFVPROC)load("glGetTextureParameterfv");
|
||||
glad_glGetTextureParameterIiv = (PFNGLGETTEXTUREPARAMETERIIVPROC)load("glGetTextureParameterIiv");
|
||||
glad_glGetTextureParameterIuiv = (PFNGLGETTEXTUREPARAMETERIUIVPROC)load("glGetTextureParameterIuiv");
|
||||
glad_glGetTextureParameteriv = (PFNGLGETTEXTUREPARAMETERIVPROC)load("glGetTextureParameteriv");
|
||||
glad_glCreateVertexArrays = (PFNGLCREATEVERTEXARRAYSPROC)load("glCreateVertexArrays");
|
||||
glad_glDisableVertexArrayAttrib = (PFNGLDISABLEVERTEXARRAYATTRIBPROC)load("glDisableVertexArrayAttrib");
|
||||
glad_glEnableVertexArrayAttrib = (PFNGLENABLEVERTEXARRAYATTRIBPROC)load("glEnableVertexArrayAttrib");
|
||||
glad_glVertexArrayElementBuffer = (PFNGLVERTEXARRAYELEMENTBUFFERPROC)load("glVertexArrayElementBuffer");
|
||||
glad_glVertexArrayVertexBuffer = (PFNGLVERTEXARRAYVERTEXBUFFERPROC)load("glVertexArrayVertexBuffer");
|
||||
glad_glVertexArrayVertexBuffers = (PFNGLVERTEXARRAYVERTEXBUFFERSPROC)load("glVertexArrayVertexBuffers");
|
||||
glad_glVertexArrayAttribBinding = (PFNGLVERTEXARRAYATTRIBBINDINGPROC)load("glVertexArrayAttribBinding");
|
||||
glad_glVertexArrayAttribFormat = (PFNGLVERTEXARRAYATTRIBFORMATPROC)load("glVertexArrayAttribFormat");
|
||||
glad_glVertexArrayAttribIFormat = (PFNGLVERTEXARRAYATTRIBIFORMATPROC)load("glVertexArrayAttribIFormat");
|
||||
glad_glVertexArrayAttribLFormat = (PFNGLVERTEXARRAYATTRIBLFORMATPROC)load("glVertexArrayAttribLFormat");
|
||||
glad_glVertexArrayBindingDivisor = (PFNGLVERTEXARRAYBINDINGDIVISORPROC)load("glVertexArrayBindingDivisor");
|
||||
glad_glGetVertexArrayiv = (PFNGLGETVERTEXARRAYIVPROC)load("glGetVertexArrayiv");
|
||||
glad_glGetVertexArrayIndexediv = (PFNGLGETVERTEXARRAYINDEXEDIVPROC)load("glGetVertexArrayIndexediv");
|
||||
glad_glGetVertexArrayIndexed64iv = (PFNGLGETVERTEXARRAYINDEXED64IVPROC)load("glGetVertexArrayIndexed64iv");
|
||||
glad_glCreateSamplers = (PFNGLCREATESAMPLERSPROC)load("glCreateSamplers");
|
||||
glad_glCreateProgramPipelines = (PFNGLCREATEPROGRAMPIPELINESPROC)load("glCreateProgramPipelines");
|
||||
glad_glCreateQueries = (PFNGLCREATEQUERIESPROC)load("glCreateQueries");
|
||||
glad_glGetQueryBufferObjecti64v = (PFNGLGETQUERYBUFFEROBJECTI64VPROC)load("glGetQueryBufferObjecti64v");
|
||||
glad_glGetQueryBufferObjectiv = (PFNGLGETQUERYBUFFEROBJECTIVPROC)load("glGetQueryBufferObjectiv");
|
||||
glad_glGetQueryBufferObjectui64v = (PFNGLGETQUERYBUFFEROBJECTUI64VPROC)load("glGetQueryBufferObjectui64v");
|
||||
glad_glGetQueryBufferObjectuiv = (PFNGLGETQUERYBUFFEROBJECTUIVPROC)load("glGetQueryBufferObjectuiv");
|
||||
glad_glMemoryBarrierByRegion = (PFNGLMEMORYBARRIERBYREGIONPROC)load("glMemoryBarrierByRegion");
|
||||
glad_glGetTextureSubImage = (PFNGLGETTEXTURESUBIMAGEPROC)load("glGetTextureSubImage");
|
||||
glad_glGetCompressedTextureSubImage = (PFNGLGETCOMPRESSEDTEXTURESUBIMAGEPROC)load("glGetCompressedTextureSubImage");
|
||||
glad_glGetGraphicsResetStatus = (PFNGLGETGRAPHICSRESETSTATUSPROC)load("glGetGraphicsResetStatus");
|
||||
glad_glGetnCompressedTexImage = (PFNGLGETNCOMPRESSEDTEXIMAGEPROC)load("glGetnCompressedTexImage");
|
||||
glad_glGetnTexImage = (PFNGLGETNTEXIMAGEPROC)load("glGetnTexImage");
|
||||
glad_glGetnUniformdv = (PFNGLGETNUNIFORMDVPROC)load("glGetnUniformdv");
|
||||
glad_glGetnUniformfv = (PFNGLGETNUNIFORMFVPROC)load("glGetnUniformfv");
|
||||
glad_glGetnUniformiv = (PFNGLGETNUNIFORMIVPROC)load("glGetnUniformiv");
|
||||
glad_glGetnUniformuiv = (PFNGLGETNUNIFORMUIVPROC)load("glGetnUniformuiv");
|
||||
glad_glReadnPixels = (PFNGLREADNPIXELSPROC)load("glReadnPixels");
|
||||
glad_glGetnMapdv = (PFNGLGETNMAPDVPROC)load("glGetnMapdv");
|
||||
glad_glGetnMapfv = (PFNGLGETNMAPFVPROC)load("glGetnMapfv");
|
||||
glad_glGetnMapiv = (PFNGLGETNMAPIVPROC)load("glGetnMapiv");
|
||||
glad_glGetnPixelMapfv = (PFNGLGETNPIXELMAPFVPROC)load("glGetnPixelMapfv");
|
||||
glad_glGetnPixelMapuiv = (PFNGLGETNPIXELMAPUIVPROC)load("glGetnPixelMapuiv");
|
||||
glad_glGetnPixelMapusv = (PFNGLGETNPIXELMAPUSVPROC)load("glGetnPixelMapusv");
|
||||
glad_glGetnPolygonStipple = (PFNGLGETNPOLYGONSTIPPLEPROC)load("glGetnPolygonStipple");
|
||||
glad_glGetnColorTable = (PFNGLGETNCOLORTABLEPROC)load("glGetnColorTable");
|
||||
glad_glGetnConvolutionFilter = (PFNGLGETNCONVOLUTIONFILTERPROC)load("glGetnConvolutionFilter");
|
||||
glad_glGetnSeparableFilter = (PFNGLGETNSEPARABLEFILTERPROC)load("glGetnSeparableFilter");
|
||||
glad_glGetnHistogram = (PFNGLGETNHISTOGRAMPROC)load("glGetnHistogram");
|
||||
glad_glGetnMinmax = (PFNGLGETNMINMAXPROC)load("glGetnMinmax");
|
||||
glad_glTextureBarrier = (PFNGLTEXTUREBARRIERPROC)load("glTextureBarrier");
|
||||
}
|
||||
static void load_GL_VERSION_4_6(GLADloadproc load) {
|
||||
if(!GLAD_GL_VERSION_4_6) return;
|
||||
glad_glSpecializeShader = (PFNGLSPECIALIZESHADERPROC)load("glSpecializeShader");
|
||||
glad_glMultiDrawArraysIndirectCount = (PFNGLMULTIDRAWARRAYSINDIRECTCOUNTPROC)load("glMultiDrawArraysIndirectCount");
|
||||
glad_glMultiDrawElementsIndirectCount = (PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTPROC)load("glMultiDrawElementsIndirectCount");
|
||||
glad_glPolygonOffsetClamp = (PFNGLPOLYGONOFFSETCLAMPPROC)load("glPolygonOffsetClamp");
|
||||
}
|
||||
static void load_GL_ARB_buffer_storage(GLADloadproc load) {
|
||||
if(!GLAD_GL_ARB_buffer_storage) return;
|
||||
glad_glBufferStorage = (PFNGLBUFFERSTORAGEPROC)load("glBufferStorage");
|
||||
@ -1768,6 +1501,8 @@ static int find_extensionsGL(void) {
|
||||
GLAD_GL_ARB_buffer_storage = has_ext("GL_ARB_buffer_storage");
|
||||
GLAD_GL_ARB_clear_texture = has_ext("GL_ARB_clear_texture");
|
||||
GLAD_GL_ARB_get_texture_sub_image = has_ext("GL_ARB_get_texture_sub_image");
|
||||
GLAD_GL_ARB_texture_compression_bptc = has_ext("GL_ARB_texture_compression_bptc");
|
||||
GLAD_GL_EXT_texture_compression_s3tc = has_ext("GL_EXT_texture_compression_s3tc");
|
||||
free_exts();
|
||||
return 1;
|
||||
}
|
||||
@ -1824,12 +1559,9 @@ static void find_coreGL(void) {
|
||||
GLAD_GL_VERSION_4_1 = (major == 4 && minor >= 1) || major > 4;
|
||||
GLAD_GL_VERSION_4_2 = (major == 4 && minor >= 2) || major > 4;
|
||||
GLAD_GL_VERSION_4_3 = (major == 4 && minor >= 3) || major > 4;
|
||||
GLAD_GL_VERSION_4_4 = (major == 4 && minor >= 4) || major > 4;
|
||||
GLAD_GL_VERSION_4_5 = (major == 4 && minor >= 5) || major > 4;
|
||||
GLAD_GL_VERSION_4_6 = (major == 4 && minor >= 6) || major > 4;
|
||||
if (GLVersion.major > 4 || (GLVersion.major >= 4 && GLVersion.minor >= 6)) {
|
||||
if (GLVersion.major > 4 || (GLVersion.major >= 4 && GLVersion.minor >= 3)) {
|
||||
max_loaded_major = 4;
|
||||
max_loaded_minor = 6;
|
||||
max_loaded_minor = 3;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1855,9 +1587,6 @@ int gladLoadGLLoader(GLADloadproc load) {
|
||||
load_GL_VERSION_4_1(load);
|
||||
load_GL_VERSION_4_2(load);
|
||||
load_GL_VERSION_4_3(load);
|
||||
load_GL_VERSION_4_4(load);
|
||||
load_GL_VERSION_4_5(load);
|
||||
load_GL_VERSION_4_6(load);
|
||||
|
||||
if (!find_extensionsGL()) return 0;
|
||||
load_GL_ARB_buffer_storage(load);
|
||||
@ -2244,6 +1973,7 @@ static int find_extensionsGLES2(void) {
|
||||
if (!get_exts()) return 0;
|
||||
GLAD_GL_EXT_buffer_storage = has_ext("GL_EXT_buffer_storage");
|
||||
GLAD_GL_EXT_clip_cull_distance = has_ext("GL_EXT_clip_cull_distance");
|
||||
GLAD_GL_EXT_texture_compression_s3tc = has_ext("GL_EXT_texture_compression_s3tc");
|
||||
free_exts();
|
||||
return 1;
|
||||
}
|
||||
|
@ -168,9 +168,8 @@ public final class SettingsFragmentPresenter {
|
||||
sl.add(new PremiumSingleChoiceSetting(SettingsFile.KEY_DESIGN, Settings.SECTION_PREMIUM, R.string.design, 0, R.array.designNamesOld, R.array.designValuesOld, 0, design, mView));
|
||||
}
|
||||
|
||||
String[] textureFilterNames = NativeLibrary.GetTextureFilterNames();
|
||||
Setting textureFilterName = premiumSection.getSetting(SettingsFile.KEY_TEXTURE_FILTER_NAME);
|
||||
sl.add(new StringSingleChoiceSetting(SettingsFile.KEY_TEXTURE_FILTER_NAME, Settings.SECTION_PREMIUM, R.string.texture_filter_name, R.string.texture_filter_description, textureFilterNames, textureFilterNames, "none", textureFilterName));
|
||||
sl.add(new SingleChoiceSetting(SettingsFile.KEY_TEXTURE_FILTER_NAME, Settings.SECTION_PREMIUM, R.string.texture_filter_name, 0, R.array.textureFilterNames, R.array.textureFilterValues, 0, textureFilterName));
|
||||
}
|
||||
|
||||
private void addGeneralSettings(ArrayList<SettingsItem> sl) {
|
||||
|
@ -140,7 +140,7 @@ void Config::ReadValues() {
|
||||
ReadSetting("Core", Settings::values.cpu_clock_percentage);
|
||||
|
||||
// Premium
|
||||
ReadSetting("Premium", Settings::values.texture_filter_name);
|
||||
ReadSetting("Premium", Settings::values.texture_filter);
|
||||
|
||||
// Renderer
|
||||
Settings::values.use_gles = sdl2_config->GetBoolean("Renderer", "use_gles", true);
|
||||
|
@ -44,7 +44,6 @@
|
||||
#include "jni/native.h"
|
||||
#include "jni/ndk_motion.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "video_core/renderer_opengl/texture_filters/texture_filterer.h"
|
||||
#include "video_core/video_core.h"
|
||||
|
||||
namespace {
|
||||
@ -630,17 +629,6 @@ void Java_org_citra_citra_1emu_NativeLibrary_Run__Ljava_lang_String_2(JNIEnv* en
|
||||
}
|
||||
}
|
||||
|
||||
jobjectArray Java_org_citra_citra_1emu_NativeLibrary_GetTextureFilterNames(JNIEnv* env,
|
||||
jclass clazz) {
|
||||
auto names = OpenGL::TextureFilterer::GetFilterNames();
|
||||
jobjectArray ret = (jobjectArray)env->NewObjectArray(static_cast<jsize>(names.size()),
|
||||
env->FindClass("java/lang/String"),
|
||||
env->NewStringUTF(""));
|
||||
for (jsize i = 0; i < names.size(); ++i)
|
||||
env->SetObjectArrayElement(ret, i, env->NewStringUTF(names[i].data()));
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Java_org_citra_citra_1emu_NativeLibrary_ReloadCameraDevices(JNIEnv* env, jclass clazz) {
|
||||
if (g_ndk_factory) {
|
||||
g_ndk_factory->ReloadCameraDevices();
|
||||
|
@ -138,9 +138,6 @@ JNIEXPORT jstring JNICALL Java_org_citra_citra_1emu_NativeLibrary_GetUserSetting
|
||||
JNIEXPORT jdoubleArray JNICALL Java_org_citra_citra_1emu_NativeLibrary_GetPerfStats(JNIEnv* env,
|
||||
jclass clazz);
|
||||
|
||||
JNIEXPORT jobjectArray JNICALL
|
||||
Java_org_citra_citra_1emu_NativeLibrary_GetTextureFilterNames(JNIEnv* env, jclass clazz);
|
||||
|
||||
JNIEXPORT void JNICALL Java_org_citra_citra_1emu_NativeLibrary_ReloadCameraDevices(JNIEnv* env,
|
||||
jclass clazz);
|
||||
|
||||
|
@ -179,4 +179,22 @@
|
||||
<integer-array name="graphicsApiValues">
|
||||
<item>1</item>
|
||||
</integer-array>
|
||||
|
||||
<string-array name="textureFilterNames">
|
||||
<item>None</item>
|
||||
<item>Anime4K</item>
|
||||
<item>Bicubic</item>
|
||||
<item>Nearest Neighbor</item>
|
||||
<item>ScaleForce</item>
|
||||
<item>xBRZ</item>
|
||||
</string-array>
|
||||
|
||||
<integer-array name="textureFilterValues">
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
<item>3</item>
|
||||
<item>4</item>
|
||||
<item>5</item>
|
||||
</integer-array>
|
||||
</resources>
|
||||
|
@ -148,7 +148,7 @@ void Config::ReadValues() {
|
||||
ReadSetting("Renderer", Settings::values.use_disk_shader_cache);
|
||||
ReadSetting("Renderer", Settings::values.frame_limit);
|
||||
ReadSetting("Renderer", Settings::values.use_vsync_new);
|
||||
ReadSetting("Renderer", Settings::values.texture_filter_name);
|
||||
ReadSetting("Renderer", Settings::values.texture_filter);
|
||||
|
||||
ReadSetting("Renderer", Settings::values.mono_render_option);
|
||||
ReadSetting("Renderer", Settings::values.render_3d);
|
||||
|
@ -136,8 +136,9 @@ use_disk_shader_cache =
|
||||
# factor for the 3DS resolution
|
||||
resolution_factor =
|
||||
|
||||
# Texture filter name
|
||||
texture_filter_name =
|
||||
# Texture filter
|
||||
# 0: None, 1: Anime4K, 2: Bicubic, 3: Nearest Neighbor, 4: ScaleForce, 5: xBRZ
|
||||
texture_filter =
|
||||
|
||||
# Limits the speed of the game to run no faster than this value as a percentage of target speed.
|
||||
# Will not have an effect if unthrottled is enabled.
|
||||
|
@ -644,7 +644,7 @@ void Config::ReadRendererValues() {
|
||||
ReadGlobalSetting(Settings::values.bg_green);
|
||||
ReadGlobalSetting(Settings::values.bg_blue);
|
||||
|
||||
ReadGlobalSetting(Settings::values.texture_filter_name);
|
||||
ReadGlobalSetting(Settings::values.texture_filter);
|
||||
|
||||
if (global) {
|
||||
ReadBasicSetting(Settings::values.use_shader_jit);
|
||||
@ -1122,7 +1122,7 @@ void Config::SaveRendererValues() {
|
||||
WriteGlobalSetting(Settings::values.bg_green);
|
||||
WriteGlobalSetting(Settings::values.bg_blue);
|
||||
|
||||
WriteGlobalSetting(Settings::values.texture_filter_name);
|
||||
WriteGlobalSetting(Settings::values.texture_filter);
|
||||
|
||||
if (global) {
|
||||
WriteSetting(QStringLiteral("use_shader_jit"), Settings::values.use_shader_jit.GetValue(),
|
||||
|
@ -33,16 +33,6 @@ void ConfigurationShared::SetPerGameSetting(QCheckBox* checkbox,
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void ConfigurationShared::SetPerGameSetting(
|
||||
QComboBox* combobox, const Settings::SwitchableSetting<std::string>* setting) {
|
||||
const int index =
|
||||
static_cast<int>(combobox->findText(QString::fromStdString(setting->GetValue())));
|
||||
combobox->setCurrentIndex(setting->UsingGlobal()
|
||||
? ConfigurationShared::USE_GLOBAL_INDEX
|
||||
: index + ConfigurationShared::USE_GLOBAL_OFFSET);
|
||||
}
|
||||
|
||||
void ConfigurationShared::SetHighlight(QWidget* widget, bool highlighted) {
|
||||
if (highlighted) {
|
||||
widget->setStyleSheet(QStringLiteral("QWidget#%1 { background-color:rgba(0,203,255,0.5) }")
|
||||
|
@ -78,11 +78,6 @@ void SetPerGameSetting(QComboBox* combobox,
|
||||
ConfigurationShared::USE_GLOBAL_OFFSET);
|
||||
}
|
||||
|
||||
/// Specialization for string settings
|
||||
template <>
|
||||
void SetPerGameSetting(QComboBox* combobox,
|
||||
const Settings::SwitchableSetting<std::string>* setting);
|
||||
|
||||
/// Given an index of a combobox setting extracts the setting taking into
|
||||
/// account per-game status
|
||||
template <typename Type, bool ranged>
|
||||
|
@ -8,15 +8,11 @@
|
||||
#include "common/settings.h"
|
||||
#include "ui_configure_enhancements.h"
|
||||
#include "video_core/renderer_opengl/post_processing_opengl.h"
|
||||
#include "video_core/renderer_opengl/texture_filters/texture_filterer.h"
|
||||
|
||||
ConfigureEnhancements::ConfigureEnhancements(QWidget* parent)
|
||||
: QWidget(parent), ui(std::make_unique<Ui::ConfigureEnhancements>()) {
|
||||
ui->setupUi(this);
|
||||
|
||||
for (const auto& filter : OpenGL::TextureFilterer::GetFilterNames())
|
||||
ui->texture_filter_combobox->addItem(QString::fromStdString(filter.data()));
|
||||
|
||||
SetupPerGameUI();
|
||||
SetConfiguration();
|
||||
|
||||
@ -60,9 +56,9 @@ void ConfigureEnhancements::SetConfiguration() {
|
||||
ConfigurationShared::SetPerGameSetting(ui->resolution_factor_combobox,
|
||||
&Settings::values.resolution_factor);
|
||||
ConfigurationShared::SetPerGameSetting(ui->texture_filter_combobox,
|
||||
&Settings::values.texture_filter_name);
|
||||
&Settings::values.texture_filter);
|
||||
ConfigurationShared::SetHighlight(ui->widget_texture_filter,
|
||||
!Settings::values.texture_filter_name.UsingGlobal());
|
||||
!Settings::values.texture_filter.UsingGlobal());
|
||||
ConfigurationShared::SetPerGameSetting(ui->layout_combobox,
|
||||
&Settings::values.layout_option);
|
||||
} else {
|
||||
@ -70,13 +66,8 @@ void ConfigureEnhancements::SetConfiguration() {
|
||||
Settings::values.resolution_factor.GetValue());
|
||||
ui->layout_combobox->setCurrentIndex(
|
||||
static_cast<int>(Settings::values.layout_option.GetValue()));
|
||||
int tex_filter_idx = ui->texture_filter_combobox->findText(
|
||||
QString::fromStdString(Settings::values.texture_filter_name.GetValue()));
|
||||
if (tex_filter_idx == -1) {
|
||||
ui->texture_filter_combobox->setCurrentIndex(0);
|
||||
} else {
|
||||
ui->texture_filter_combobox->setCurrentIndex(tex_filter_idx);
|
||||
}
|
||||
ui->texture_filter_combobox->setCurrentIndex(
|
||||
static_cast<int>(Settings::values.texture_filter.GetValue()));
|
||||
}
|
||||
|
||||
ui->render_3d_combobox->setCurrentIndex(
|
||||
@ -155,9 +146,8 @@ void ConfigureEnhancements::ApplyConfiguration() {
|
||||
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.filter_mode,
|
||||
ui->toggle_linear_filter, linear_filter);
|
||||
ConfigurationShared::ApplyPerGameSetting(
|
||||
&Settings::values.texture_filter_name, ui->texture_filter_combobox,
|
||||
[this](int index) { return ui->texture_filter_combobox->itemText(index).toStdString(); });
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.texture_filter,
|
||||
ui->texture_filter_combobox);
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.layout_option, ui->layout_combobox);
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.swap_screen, ui->toggle_swap_screen,
|
||||
swap_screen);
|
||||
@ -179,7 +169,7 @@ void ConfigureEnhancements::SetupPerGameUI() {
|
||||
// Block the global settings if a game is currently running that overrides them
|
||||
if (Settings::IsConfiguringGlobal()) {
|
||||
ui->widget_resolution->setEnabled(Settings::values.resolution_factor.UsingGlobal());
|
||||
ui->widget_texture_filter->setEnabled(Settings::values.texture_filter_name.UsingGlobal());
|
||||
ui->widget_texture_filter->setEnabled(Settings::values.texture_filter.UsingGlobal());
|
||||
ui->toggle_linear_filter->setEnabled(Settings::values.filter_mode.UsingGlobal());
|
||||
ui->toggle_swap_screen->setEnabled(Settings::values.swap_screen.UsingGlobal());
|
||||
ui->toggle_upright_screen->setEnabled(Settings::values.upright_screen.UsingGlobal());
|
||||
|
@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>657</height>
|
||||
<width>440</width>
|
||||
<height>748</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
@ -168,7 +168,38 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="texture_filter_combobox"/>
|
||||
<widget class="QComboBox" name="texture_filter_combobox">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>None</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Anime4K</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Bicubic</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Nearest Neighbor</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>ScaleForce</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>xBRZ</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
@ -352,7 +383,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="" native="true">
|
||||
<widget class="QWidget" name="widget" native="true">
|
||||
<layout class="QHBoxLayout" name="proportion_layout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
|
@ -52,6 +52,11 @@ namespace Common::Color {
|
||||
return value >> 2;
|
||||
}
|
||||
|
||||
/// Averages the RGB components of a color
|
||||
[[nodiscard]] constexpr u8 AverageRgbComponents(const Common::Vec4<u8>& color) {
|
||||
return (static_cast<u32>(color.r()) + color.g() + color.b()) / 3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a color stored in RGBA8 format
|
||||
* @param bytes Pointer to encoded source color
|
||||
@ -115,6 +120,44 @@ namespace Common::Color {
|
||||
Convert4To8((pixel >> 4) & 0xF), Convert4To8(pixel & 0xF)};
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a color stored in IA8 format
|
||||
* @param bytes Pointer to encoded source color
|
||||
* @return Result color decoded as Common::Vec4<u8>
|
||||
*/
|
||||
[[nodiscard]] inline Common::Vec4<u8> DecodeIA8(const u8* bytes) {
|
||||
return {bytes[1], bytes[1], bytes[1], bytes[0]};
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a color stored in I8 format
|
||||
* @param bytes Pointer to encoded source color
|
||||
* @return Result color decoded as Common::Vec4<u8>
|
||||
*/
|
||||
[[nodiscard]] inline Common::Vec4<u8> DecodeI8(const u8* bytes) {
|
||||
return {bytes[0], bytes[0], bytes[0], 255};
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a color stored in A8 format
|
||||
* @param bytes Pointer to encoded source color
|
||||
* @return Result color decoded as Common::Vec4<u8>
|
||||
*/
|
||||
[[nodiscard]] inline Common::Vec4<u8> DecodeA8(const u8* bytes) {
|
||||
return {0, 0, 0, bytes[0]};
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a color stored in IA4 format
|
||||
* @param bytes Pointer to encoded source color
|
||||
* @return Result color decoded as Common::Vec4<u8>
|
||||
*/
|
||||
[[nodiscard]] inline Common::Vec4<u8> DecodeIA4(const u8* bytes) {
|
||||
u8 i = Common::Color::Convert4To8((bytes[0] & 0xF0) >> 4);
|
||||
u8 a = Common::Color::Convert4To8(bytes[0] & 0x0F);
|
||||
return {i, i, i, a};
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a depth value stored in D16 format
|
||||
* @param bytes Pointer to encoded source value
|
||||
@ -176,6 +219,7 @@ inline void EncodeRG8(const Common::Vec4<u8>& color, u8* bytes) {
|
||||
bytes[1] = color.r();
|
||||
bytes[0] = color.g();
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a color as RGB565 format
|
||||
* @param color Source color to encode
|
||||
@ -212,6 +256,43 @@ inline void EncodeRGBA4(const Common::Vec4<u8>& color, u8* bytes) {
|
||||
std::memcpy(bytes, &data, sizeof(data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a color as IA8 format
|
||||
* @param color Source color to encode
|
||||
* @param bytes Destination pointer to store encoded color
|
||||
*/
|
||||
inline void EncodeIA8(const Common::Vec4<u8>& color, u8* bytes) {
|
||||
bytes[1] = AverageRgbComponents(color);
|
||||
bytes[0] = color.a();
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a color as I8 format
|
||||
* @param color Source color to encode
|
||||
* @param bytes Destination pointer to store encoded color
|
||||
*/
|
||||
inline void EncodeI8(const Common::Vec4<u8>& color, u8* bytes) {
|
||||
bytes[0] = AverageRgbComponents(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a color as A8 format
|
||||
* @param color Source color to encode
|
||||
* @param bytes Destination pointer to store encoded color
|
||||
*/
|
||||
inline void EncodeA8(const Common::Vec4<u8>& color, u8* bytes) {
|
||||
bytes[0] = color.a();
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a color as IA4 format
|
||||
* @param color Source color to encode
|
||||
* @param bytes Destination pointer to store encoded color
|
||||
*/
|
||||
inline void EncodeIA4(const Common::Vec4<u8>& color, u8* bytes) {
|
||||
bytes[0] = (Convert8To4(AverageRgbComponents(color)) << 4) | Convert8To4(color.a());
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a 16 bit depth value as D16 format
|
||||
* @param value 16 bit source depth value to encode
|
||||
|
@ -41,6 +41,13 @@ inline u64 HashCombine(std::size_t& seed, const u64 hash) {
|
||||
return seed ^= hash + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct IdentityHash {
|
||||
std::size_t operator()(const T& value) const {
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
/// A helper template that ensures the padding in a struct is initialized by memsetting to 0.
|
||||
template <typename T>
|
||||
struct HashableStruct {
|
||||
|
@ -23,6 +23,22 @@ struct Rectangle {
|
||||
constexpr Rectangle(T left, T top, T right, T bottom)
|
||||
: left(left), top(top), right(right), bottom(bottom) {}
|
||||
|
||||
[[nodiscard]] constexpr bool operator==(const Rectangle<T>& rhs) const {
|
||||
return (left == rhs.left) && (top == rhs.top) && (right == rhs.right) &&
|
||||
(bottom == rhs.bottom);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool operator!=(const Rectangle<T>& rhs) const {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr Rectangle<T> operator*(const T value) const {
|
||||
return Rectangle{left * value, top * value, right * value, bottom * value};
|
||||
}
|
||||
[[nodiscard]] constexpr Rectangle<T> operator/(const T value) const {
|
||||
return Rectangle{left / value, top / value, right / value, bottom / value};
|
||||
}
|
||||
|
||||
[[nodiscard]] T GetWidth() const {
|
||||
return std::abs(static_cast<std::make_signed_t<T>>(right - left));
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
#include <boost/serialization/export.hpp>
|
||||
#include <boost/serialization/shared_ptr.hpp>
|
||||
@ -105,6 +106,14 @@ public:
|
||||
return cptr;
|
||||
}
|
||||
|
||||
std::span<u8> GetWriteBytes(std::size_t size) {
|
||||
return std::span{cptr, std::min(size, csize)};
|
||||
}
|
||||
|
||||
std::span<const u8> GetReadBytes(std::size_t size) const {
|
||||
return std::span{cptr, std::min(size, csize)};
|
||||
}
|
||||
|
||||
std::size_t GetSize() const {
|
||||
return csize;
|
||||
}
|
||||
|
@ -42,6 +42,23 @@ std::string_view GetGraphicsAPIName(GraphicsAPI api) {
|
||||
}
|
||||
}
|
||||
|
||||
std::string_view GetTextureFilterName(TextureFilter filter) {
|
||||
switch (filter) {
|
||||
case TextureFilter::None:
|
||||
return "None";
|
||||
case TextureFilter::Anime4K:
|
||||
return "Anime4K";
|
||||
case TextureFilter::Bicubic:
|
||||
return "Bicubic";
|
||||
case TextureFilter::NearestNeighbor:
|
||||
return "NearestNeighbor";
|
||||
case TextureFilter::ScaleForce:
|
||||
return "ScaleForce";
|
||||
case TextureFilter::xBRZ:
|
||||
return "xBRZ";
|
||||
}
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
Values values = {};
|
||||
@ -126,7 +143,7 @@ void LogSettings() {
|
||||
log_setting("Renderer_VSyncNew", values.use_vsync_new.GetValue());
|
||||
log_setting("Renderer_PostProcessingShader", values.pp_shader_name.GetValue());
|
||||
log_setting("Renderer_FilterMode", values.filter_mode.GetValue());
|
||||
log_setting("Renderer_TextureFilterName", values.texture_filter_name.GetValue());
|
||||
log_setting("Renderer_TextureFilter", GetTextureFilterName(values.texture_filter.GetValue()));
|
||||
log_setting("Stereoscopy_Render3d", values.render_3d.GetValue());
|
||||
log_setting("Stereoscopy_Factor3d", values.factor_3d.GetValue());
|
||||
log_setting("Stereoscopy_MonoRenderOption", values.mono_render_option.GetValue());
|
||||
@ -209,7 +226,7 @@ void RestoreGlobalState(bool is_powered_on) {
|
||||
values.use_vsync_new.SetGlobal(true);
|
||||
values.resolution_factor.SetGlobal(true);
|
||||
values.frame_limit.SetGlobal(true);
|
||||
values.texture_filter_name.SetGlobal(true);
|
||||
values.texture_filter.SetGlobal(true);
|
||||
values.layout_option.SetGlobal(true);
|
||||
values.swap_screen.SetGlobal(true);
|
||||
values.upright_screen.SetGlobal(true);
|
||||
|
@ -70,6 +70,15 @@ enum class AudioEmulation : u32 {
|
||||
LLEMultithreaded = 2,
|
||||
};
|
||||
|
||||
enum class TextureFilter : u32 {
|
||||
None = 0,
|
||||
Anime4K = 1,
|
||||
Bicubic = 2,
|
||||
NearestNeighbor = 3,
|
||||
ScaleForce = 4,
|
||||
xBRZ = 5,
|
||||
};
|
||||
|
||||
namespace NativeButton {
|
||||
|
||||
enum Values {
|
||||
@ -431,7 +440,7 @@ struct Values {
|
||||
Setting<bool> use_shader_jit{true, "use_shader_jit"};
|
||||
SwitchableSetting<u32, true> resolution_factor{1, 0, 10, "resolution_factor"};
|
||||
SwitchableSetting<u16, true> frame_limit{100, 0, 1000, "frame_limit"};
|
||||
SwitchableSetting<std::string> texture_filter_name{"none", "texture_filter_name"};
|
||||
SwitchableSetting<TextureFilter> texture_filter{TextureFilter::None, "texture_filter"};
|
||||
|
||||
SwitchableSetting<LayoutOption> layout_option{LayoutOption::Default, "layout_option"};
|
||||
SwitchableSetting<bool> swap_screen{false, "swap_screen"};
|
||||
|
@ -1,3 +1,5 @@
|
||||
add_subdirectory(host_shaders)
|
||||
|
||||
add_library(video_core STATIC
|
||||
command_processor.cpp
|
||||
command_processor.h
|
||||
@ -26,23 +28,27 @@ add_library(video_core STATIC
|
||||
regs_texturing.h
|
||||
renderer_base.cpp
|
||||
renderer_base.h
|
||||
rasterizer_cache/cached_surface.cpp
|
||||
rasterizer_cache/cached_surface.h
|
||||
rasterizer_cache/morton_swizzle.h
|
||||
rasterizer_cache/framebuffer_base.cpp
|
||||
rasterizer_cache/framebuffer_base.h
|
||||
rasterizer_cache/pixel_format.cpp
|
||||
rasterizer_cache/pixel_format.h
|
||||
rasterizer_cache/rasterizer_cache.cpp
|
||||
rasterizer_cache/rasterizer_cache.h
|
||||
rasterizer_cache/rasterizer_cache_types.h
|
||||
rasterizer_cache/rasterizer_cache_utils.cpp
|
||||
rasterizer_cache/rasterizer_cache_utils.h
|
||||
rasterizer_cache/surface_base.cpp
|
||||
rasterizer_cache/surface_base.h
|
||||
rasterizer_cache/surface_params.cpp
|
||||
rasterizer_cache/surface_params.h
|
||||
rasterizer_cache/texture_runtime.cpp
|
||||
rasterizer_cache/texture_runtime.h
|
||||
rasterizer_cache/texture_codec.h
|
||||
rasterizer_cache/utils.cpp
|
||||
rasterizer_cache/utils.h
|
||||
renderer_opengl/frame_dumper_opengl.cpp
|
||||
renderer_opengl/frame_dumper_opengl.h
|
||||
renderer_opengl/gl_blit_helper.cpp
|
||||
renderer_opengl/gl_blit_helper.h
|
||||
renderer_opengl/gl_driver.cpp
|
||||
renderer_opengl/gl_driver.h
|
||||
renderer_opengl/gl_format_reinterpreter.cpp
|
||||
renderer_opengl/gl_format_reinterpreter.h
|
||||
renderer_opengl/gl_rasterizer.cpp
|
||||
renderer_opengl/gl_rasterizer.h
|
||||
renderer_opengl/gl_resource_manager.cpp
|
||||
@ -61,6 +67,8 @@ add_library(video_core STATIC
|
||||
renderer_opengl/gl_state.h
|
||||
renderer_opengl/gl_stream_buffer.cpp
|
||||
renderer_opengl/gl_stream_buffer.h
|
||||
renderer_opengl/gl_texture_runtime.cpp
|
||||
renderer_opengl/gl_texture_runtime.h
|
||||
renderer_opengl/gl_vars.cpp
|
||||
renderer_opengl/gl_vars.h
|
||||
renderer_opengl/pica_to_gl.h
|
||||
@ -68,24 +76,6 @@ add_library(video_core STATIC
|
||||
renderer_opengl/post_processing_opengl.h
|
||||
renderer_opengl/renderer_opengl.cpp
|
||||
renderer_opengl/renderer_opengl.h
|
||||
renderer_opengl/texture_downloader_es.cpp
|
||||
renderer_opengl/texture_downloader_es.h
|
||||
renderer_opengl/texture_filters/anime4k/anime4k_ultrafast.cpp
|
||||
renderer_opengl/texture_filters/anime4k/anime4k_ultrafast.h
|
||||
renderer_opengl/texture_filters/bicubic/bicubic.cpp
|
||||
renderer_opengl/texture_filters/bicubic/bicubic.h
|
||||
renderer_opengl/texture_filters/nearest_neighbor/nearest_neighbor.cpp
|
||||
renderer_opengl/texture_filters/nearest_neighbor/nearest_neighbor.h
|
||||
renderer_opengl/texture_filters/scale_force/scale_force.cpp
|
||||
renderer_opengl/texture_filters/scale_force/scale_force.h
|
||||
renderer_opengl/texture_filters/texture_filter_base.h
|
||||
renderer_opengl/texture_filters/texture_filterer.cpp
|
||||
renderer_opengl/texture_filters/texture_filterer.h
|
||||
renderer_opengl/texture_filters/xbrz/xbrz_freescale.cpp
|
||||
renderer_opengl/texture_filters/xbrz/xbrz_freescale.h
|
||||
#temporary, move these back in alphabetical order before merging
|
||||
renderer_opengl/gl_format_reinterpreter.cpp
|
||||
renderer_opengl/gl_format_reinterpreter.h
|
||||
renderer_software/rasterizer.cpp
|
||||
renderer_software/rasterizer.h
|
||||
renderer_software/renderer_software.cpp
|
||||
@ -124,37 +114,8 @@ add_library(video_core STATIC
|
||||
video_core.h
|
||||
)
|
||||
|
||||
set(SHADER_FILES
|
||||
renderer_opengl/depth_to_color.frag
|
||||
renderer_opengl/depth_to_color.vert
|
||||
renderer_opengl/ds_to_color.frag
|
||||
renderer_opengl/texture_filters/anime4k/refine.frag
|
||||
renderer_opengl/texture_filters/anime4k/x_gradient.frag
|
||||
renderer_opengl/texture_filters/anime4k/y_gradient.frag
|
||||
renderer_opengl/texture_filters/bicubic/bicubic.frag
|
||||
renderer_opengl/texture_filters/nearest_neighbor/nearest_neighbor.frag
|
||||
renderer_opengl/texture_filters/scale_force/scale_force.frag
|
||||
renderer_opengl/texture_filters/tex_coord.vert
|
||||
renderer_opengl/texture_filters/xbrz/xbrz_freescale.frag
|
||||
renderer_opengl/texture_filters/xbrz/xbrz_freescale.vert
|
||||
)
|
||||
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/generate_shaders.cmake)
|
||||
|
||||
foreach(shader_file ${SHADER_FILES})
|
||||
get_filename_component(shader_file_name ${shader_file} NAME)
|
||||
GetShaderHeaderFile(${shader_file_name})
|
||||
list(APPEND SHADER_HEADERS ${shader_header_file})
|
||||
endforeach()
|
||||
|
||||
add_custom_target(shaders
|
||||
BYPRODUCTS ${SHADER_HEADERS}
|
||||
COMMAND "${CMAKE_COMMAND}" -P ${CMAKE_CURRENT_SOURCE_DIR}/generate_shaders.cmake
|
||||
SOURCES ${SHADER_FILES}
|
||||
)
|
||||
add_dependencies(video_core shaders)
|
||||
|
||||
target_include_directories(video_core PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||
add_dependencies(video_core host_shaders)
|
||||
target_include_directories(video_core PRIVATE ${HOST_SHADERS_INCLUDE})
|
||||
|
||||
create_target_directory_groups(video_core)
|
||||
|
||||
|
@ -1,16 +0,0 @@
|
||||
function(GetShaderHeaderFile shader_file_name)
|
||||
set(shader_header_file ${CMAKE_CURRENT_BINARY_DIR}/shaders/${shader_file_name} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
foreach(shader_file ${SHADER_FILES})
|
||||
file(READ ${shader_file} shader)
|
||||
get_filename_component(shader_file_name ${shader_file} NAME)
|
||||
string(REPLACE . _ shader_name ${shader_file_name})
|
||||
GetShaderHeaderFile(${shader_file_name})
|
||||
file(WRITE ${shader_header_file}
|
||||
"#pragma once\n"
|
||||
"constexpr std::string_view ${shader_name} = R\"(\n"
|
||||
"${shader}"
|
||||
")\";\n"
|
||||
)
|
||||
endforeach()
|
60
src/video_core/host_shaders/CMakeLists.txt
Normal file
60
src/video_core/host_shaders/CMakeLists.txt
Normal file
@ -0,0 +1,60 @@
|
||||
# Copyright 2023 Citra Emulator Project
|
||||
# Licensed under GPLv2 or any later version
|
||||
# Refer to the license.txt file included.
|
||||
|
||||
set(SHADER_FILES
|
||||
format_reinterpreter/d24s8_to_rgba8.frag
|
||||
format_reinterpreter/fullscreen_quad.vert
|
||||
format_reinterpreter/rgba4_to_rgb5a1.frag
|
||||
texture_filtering/bicubic.frag
|
||||
texture_filtering/nearest_neighbor.frag
|
||||
texture_filtering/refine.frag
|
||||
texture_filtering/scale_force.frag
|
||||
texture_filtering/tex_coord.vert
|
||||
texture_filtering/xbrz_freescale.frag
|
||||
texture_filtering/x_gradient.frag
|
||||
texture_filtering/y_gradient.frag
|
||||
full_screen_triangle.vert
|
||||
opengl_present.frag
|
||||
opengl_present.vert
|
||||
opengl_present_anaglyph.frag
|
||||
opengl_present_interlaced.frag
|
||||
)
|
||||
|
||||
set(SHADER_INCLUDE ${CMAKE_CURRENT_BINARY_DIR}/include)
|
||||
set(SHADER_DIR ${SHADER_INCLUDE}/video_core/host_shaders)
|
||||
set(HOST_SHADERS_INCLUDE ${SHADER_INCLUDE} PARENT_SCOPE)
|
||||
|
||||
set(INPUT_FILE ${CMAKE_CURRENT_SOURCE_DIR}/source_shader.h.in)
|
||||
set(HEADER_GENERATOR ${CMAKE_CURRENT_SOURCE_DIR}/StringShaderHeader.cmake)
|
||||
|
||||
foreach(FILENAME IN ITEMS ${SHADER_FILES})
|
||||
string(REPLACE "." "_" SHADER_NAME ${FILENAME})
|
||||
set(SOURCE_FILE ${CMAKE_CURRENT_SOURCE_DIR}/${FILENAME})
|
||||
# Skip generating source headers on Vulkan exclusive files
|
||||
if (NOT ${FILENAME} MATCHES "vulkan.*")
|
||||
set(SOURCE_HEADER_FILE ${SHADER_DIR}/${SHADER_NAME}.h)
|
||||
add_custom_command(
|
||||
OUTPUT
|
||||
${SOURCE_HEADER_FILE}
|
||||
COMMAND
|
||||
${CMAKE_COMMAND} -P ${HEADER_GENERATOR} ${SOURCE_FILE} ${SOURCE_HEADER_FILE} ${INPUT_FILE}
|
||||
MAIN_DEPENDENCY
|
||||
${SOURCE_FILE}
|
||||
DEPENDS
|
||||
${INPUT_FILE}
|
||||
# HEADER_GENERATOR should be included here but msbuild seems to assume it's always modified
|
||||
)
|
||||
set(SHADER_HEADERS ${SHADER_HEADERS} ${SOURCE_HEADER_FILE})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
set(SHADER_SOURCES ${SHADER_FILES})
|
||||
list(APPEND SHADER_SOURCES ${GLSL_INCLUDES})
|
||||
|
||||
add_custom_target(host_shaders
|
||||
DEPENDS
|
||||
${SHADER_HEADERS}
|
||||
SOURCES
|
||||
${SHADER_SOURCES}
|
||||
)
|
36
src/video_core/host_shaders/StringShaderHeader.cmake
Normal file
36
src/video_core/host_shaders/StringShaderHeader.cmake
Normal file
@ -0,0 +1,36 @@
|
||||
# SPDX-FileCopyrightText: 2020 yuzu Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
set(SOURCE_FILE ${CMAKE_ARGV3})
|
||||
set(HEADER_FILE ${CMAKE_ARGV4})
|
||||
set(INPUT_FILE ${CMAKE_ARGV5})
|
||||
|
||||
get_filename_component(CONTENTS_NAME ${SOURCE_FILE} NAME)
|
||||
string(REPLACE "." "_" CONTENTS_NAME ${CONTENTS_NAME})
|
||||
string(TOUPPER ${CONTENTS_NAME} CONTENTS_NAME)
|
||||
|
||||
FILE(READ ${SOURCE_FILE} line_contents)
|
||||
|
||||
# Replace double quotes with single quotes,
|
||||
# as double quotes will be used to wrap the lines
|
||||
STRING(REGEX REPLACE "\"" "'" line_contents "${line_contents}")
|
||||
|
||||
# CMake separates list elements with semicolons, but semicolons
|
||||
# are used extensively in the shader code.
|
||||
# Replace with a temporary marker, to be reverted later.
|
||||
STRING(REGEX REPLACE ";" "{{SEMICOLON}}" line_contents "${line_contents}")
|
||||
|
||||
# Make every line an individual element in the CMake list.
|
||||
STRING(REGEX REPLACE "\n" ";" line_contents "${line_contents}")
|
||||
|
||||
# Build the shader string, wrapping each line in double quotes.
|
||||
foreach(line IN LISTS line_contents)
|
||||
string(CONCAT CONTENTS "${CONTENTS}" \"${line}\\n\"\n)
|
||||
endforeach()
|
||||
|
||||
# Revert the original semicolons in the source.
|
||||
STRING(REGEX REPLACE "{{SEMICOLON}}" ";" CONTENTS "${CONTENTS}")
|
||||
|
||||
get_filename_component(OUTPUT_DIR ${HEADER_FILE} DIRECTORY)
|
||||
make_directory(${OUTPUT_DIR})
|
||||
configure_file(${INPUT_FILE} ${HEADER_FILE} @ONLY)
|
@ -0,0 +1,33 @@
|
||||
// Copyright 2023 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
//? #version 430 core
|
||||
|
||||
layout(location = 0) in mediump vec2 dst_coord;
|
||||
layout(location = 0) out lowp vec4 frag_color;
|
||||
|
||||
layout(binding = 0) uniform highp sampler2D depth;
|
||||
layout(binding = 1) uniform lowp usampler2D stencil;
|
||||
uniform mediump ivec2 dst_size;
|
||||
uniform mediump ivec2 src_size;
|
||||
uniform mediump ivec2 src_offset;
|
||||
|
||||
void main() {
|
||||
mediump ivec2 tex_coord;
|
||||
if (src_size == dst_size) {
|
||||
tex_coord = ivec2(dst_coord);
|
||||
} else {
|
||||
highp int tex_index = int(dst_coord.y) * dst_size.x + int(dst_coord.x);
|
||||
mediump int y = tex_index / src_size.x;
|
||||
tex_coord = ivec2(tex_index - y * src_size.x, y);
|
||||
}
|
||||
tex_coord -= src_offset;
|
||||
|
||||
highp uint depth_val =
|
||||
uint(texelFetch(depth, tex_coord, 0).x * (exp2(32.0) - 1.0));
|
||||
lowp uint stencil_val = texelFetch(stencil, tex_coord, 0).x;
|
||||
highp uvec4 components =
|
||||
uvec4(stencil_val, (uvec3(depth_val) >> uvec3(24u, 16u, 8u)) & 0x000000FFu);
|
||||
frag_color = vec4(components) / (exp2(8.0) - 1.0);
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
// Copyright 2023 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
//? #version 430 core
|
||||
|
||||
layout(location = 0) out vec2 dst_coord;
|
||||
|
||||
uniform mediump ivec2 dst_size;
|
||||
|
||||
const vec2 vertices[4] =
|
||||
vec2[4](vec2(-1.0, -1.0), vec2(1.0, -1.0), vec2(-1.0, 1.0), vec2(1.0, 1.0));
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(vertices[gl_VertexID], 0.0, 1.0);
|
||||
dst_coord = (vertices[gl_VertexID] / 2.0 + 0.5) * vec2(dst_size);
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
// Copyright 2023 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
//? #version 430 core
|
||||
|
||||
layout(location = 0) in mediump vec2 dst_coord;
|
||||
layout(location = 0) out lowp vec4 frag_color;
|
||||
|
||||
layout(binding = 0) uniform lowp sampler2D source;
|
||||
uniform mediump ivec2 dst_size;
|
||||
uniform mediump ivec2 src_size;
|
||||
uniform mediump ivec2 src_offset;
|
||||
|
||||
void main() {
|
||||
mediump ivec2 tex_coord;
|
||||
if (src_size == dst_size) {
|
||||
tex_coord = ivec2(dst_coord);
|
||||
} else {
|
||||
highp int tex_index = int(dst_coord.y) * dst_size.x + int(dst_coord.x);
|
||||
mediump int y = tex_index / src_size.x;
|
||||
tex_coord = ivec2(tex_index - y * src_size.x, y);
|
||||
}
|
||||
tex_coord -= src_offset;
|
||||
|
||||
lowp ivec4 rgba4 = ivec4(texelFetch(source, tex_coord, 0) * (exp2(4.0) - 1.0));
|
||||
lowp ivec3 rgb5 =
|
||||
((rgba4.rgb << ivec3(1, 2, 3)) | (rgba4.gba >> ivec3(3, 2, 1))) & 0x1F;
|
||||
frag_color = vec4(vec3(rgb5) / (exp2(5.0) - 1.0), rgba4.a & 0x01);
|
||||
}
|
20
src/video_core/host_shaders/full_screen_triangle.vert
Normal file
20
src/video_core/host_shaders/full_screen_triangle.vert
Normal file
@ -0,0 +1,20 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
//? #version 450
|
||||
|
||||
out gl_PerVertex {
|
||||
vec4 gl_Position;
|
||||
};
|
||||
|
||||
layout(location = 0) out vec2 texcoord;
|
||||
|
||||
layout (location = 0) uniform vec2 tex_scale;
|
||||
layout (location = 1) uniform vec2 tex_offset;
|
||||
|
||||
void main() {
|
||||
float x = float((gl_VertexID & 1) << 2);
|
||||
float y = float((gl_VertexID & 2) << 1);
|
||||
gl_Position = vec4(x - 1.0, y - 1.0, 0.0, 1.0);
|
||||
texcoord = fma(vec2(x, y) / 2.0, tex_scale, tex_offset);
|
||||
}
|
18
src/video_core/host_shaders/opengl_present.frag
Normal file
18
src/video_core/host_shaders/opengl_present.frag
Normal file
@ -0,0 +1,18 @@
|
||||
// Copyright 2023 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
//? #version 430 core
|
||||
|
||||
layout(location = 0) in vec2 frag_tex_coord;
|
||||
layout(location = 0) out vec4 color;
|
||||
|
||||
layout(binding = 0) uniform sampler2D color_texture;
|
||||
|
||||
uniform vec4 i_resolution;
|
||||
uniform vec4 o_resolution;
|
||||
uniform int layer;
|
||||
|
||||
void main() {
|
||||
color = texture(color_texture, frag_tex_coord);
|
||||
}
|
23
src/video_core/host_shaders/opengl_present.vert
Normal file
23
src/video_core/host_shaders/opengl_present.vert
Normal file
@ -0,0 +1,23 @@
|
||||
// Copyright 2023 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
//? #version 430 core
|
||||
layout(location = 0) in vec2 vert_position;
|
||||
layout(location = 1) in vec2 vert_tex_coord;
|
||||
layout(location = 0) out vec2 frag_tex_coord;
|
||||
|
||||
// This is a truncated 3x3 matrix for 2D transformations:
|
||||
// The upper-left 2x2 submatrix performs scaling/rotation/mirroring.
|
||||
// The third column performs translation.
|
||||
// The third row could be used for projection, which we don't need in 2D. It hence is assumed to
|
||||
// implicitly be [0, 0, 1]
|
||||
uniform mat3x2 modelview_matrix;
|
||||
|
||||
void main() {
|
||||
// Multiply input position by the rotscale part of the matrix and then manually translate by
|
||||
// the last column. This is equivalent to using a full 3x3 matrix and expanding the vector
|
||||
// to `vec3(vert_position.xy, 1.0)`
|
||||
gl_Position = vec4(mat2(modelview_matrix) * vert_position + modelview_matrix[2], 0.0, 1.0);
|
||||
frag_tex_coord = vert_tex_coord;
|
||||
}
|
32
src/video_core/host_shaders/opengl_present_anaglyph.frag
Normal file
32
src/video_core/host_shaders/opengl_present_anaglyph.frag
Normal file
@ -0,0 +1,32 @@
|
||||
// Copyright 2023 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
//? #version 430 core
|
||||
|
||||
// Anaglyph Red-Cyan shader based on Dubois algorithm
|
||||
// Constants taken from the paper:
|
||||
// "Conversion of a Stereo Pair to Anaglyph with
|
||||
// the Least-Squares Projection Method"
|
||||
// Eric Dubois, March 2009
|
||||
const mat3 l = mat3( 0.437, 0.449, 0.164,
|
||||
-0.062,-0.062,-0.024,
|
||||
-0.048,-0.050,-0.017);
|
||||
const mat3 r = mat3(-0.011,-0.032,-0.007,
|
||||
0.377, 0.761, 0.009,
|
||||
-0.026,-0.093, 1.234);
|
||||
|
||||
layout(location = 0) in vec2 frag_tex_coord;
|
||||
layout(location = 0) out vec4 color;
|
||||
|
||||
layout(binding = 0) uniform sampler2D color_texture;
|
||||
layout(binding = 1) uniform sampler2D color_texture_r;
|
||||
|
||||
uniform vec4 resolution;
|
||||
uniform int layer;
|
||||
|
||||
void main() {
|
||||
vec4 color_tex_l = texture(color_texture, frag_tex_coord);
|
||||
vec4 color_tex_r = texture(color_texture_r, frag_tex_coord);
|
||||
color = vec4(color_tex_l.rgb*l+color_tex_r.rgb*r, color_tex_l.a);
|
||||
}
|
22
src/video_core/host_shaders/opengl_present_interlaced.frag
Normal file
22
src/video_core/host_shaders/opengl_present_interlaced.frag
Normal file
@ -0,0 +1,22 @@
|
||||
// Copyright 2023 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
//? #version 430 core
|
||||
|
||||
layout(location = 0) in vec2 frag_tex_coord;
|
||||
layout(location = 0) out vec4 color;
|
||||
|
||||
layout(binding = 0) uniform sampler2D color_texture;
|
||||
layout(binding = 1) uniform sampler2D color_texture_r;
|
||||
|
||||
uniform vec4 o_resolution;
|
||||
uniform int reverse_interlaced;
|
||||
|
||||
void main() {
|
||||
float screen_row = o_resolution.x * frag_tex_coord.x;
|
||||
if (int(screen_row) % 2 == reverse_interlaced)
|
||||
color = texture(color_texture, frag_tex_coord);
|
||||
else
|
||||
color = texture(color_texture_r, frag_tex_coord);
|
||||
}
|
15
src/video_core/host_shaders/source_shader.h.in
Normal file
15
src/video_core/host_shaders/source_shader.h.in
Normal file
@ -0,0 +1,15 @@
|
||||
// Copyright 2022 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
|
||||
namespace HostShaders {
|
||||
|
||||
constexpr std::string_view @CONTENTS_NAME@ = {
|
||||
@CONTENTS@
|
||||
};
|
||||
|
||||
} // namespace HostShaders
|
@ -1,11 +1,14 @@
|
||||
// Copyright 2023 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
//? #version 330
|
||||
precision mediump float;
|
||||
|
||||
in vec2 tex_coord;
|
||||
layout(location = 0) in vec2 tex_coord;
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
|
||||
out vec4 frag_color;
|
||||
|
||||
uniform sampler2D input_texture;
|
||||
layout(binding = 0) uniform sampler2D input_texture;
|
||||
|
||||
// from http://www.java-gaming.org/index.php?topic=35123.0
|
||||
vec4 cubic(float v) {
|
||||
@ -18,9 +21,8 @@ vec4 cubic(float v) {
|
||||
return vec4(x, y, z, w) * (1.0 / 6.0);
|
||||
}
|
||||
|
||||
vec4 textureBicubic(sampler2D sampler, vec2 texCoords) {
|
||||
|
||||
vec2 texSize = vec2(textureSize(sampler, 0));
|
||||
vec4 textureBicubic(sampler2D tex_sampler, vec2 texCoords) {
|
||||
vec2 texSize = vec2(textureSize(tex_sampler, 0));
|
||||
vec2 invTexSize = 1.0 / texSize;
|
||||
|
||||
texCoords = texCoords * texSize - 0.5;
|
||||
@ -38,10 +40,10 @@ vec4 textureBicubic(sampler2D sampler, vec2 texCoords) {
|
||||
|
||||
offset *= invTexSize.xxyy;
|
||||
|
||||
vec4 sample0 = texture(sampler, offset.xz);
|
||||
vec4 sample1 = texture(sampler, offset.yz);
|
||||
vec4 sample2 = texture(sampler, offset.xw);
|
||||
vec4 sample3 = texture(sampler, offset.yw);
|
||||
vec4 sample0 = texture(tex_sampler, offset.xz);
|
||||
vec4 sample1 = texture(tex_sampler, offset.yz);
|
||||
vec4 sample2 = texture(tex_sampler, offset.xw);
|
||||
vec4 sample3 = texture(tex_sampler, offset.yw);
|
||||
|
||||
float sx = s.x / (s.x + s.y);
|
||||
float sy = s.z / (s.z + s.w);
|
@ -0,0 +1,15 @@
|
||||
// Copyright 2023 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
//? #version 430 core
|
||||
precision mediump float;
|
||||
|
||||
layout(location = 0) in vec2 tex_coord;
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
|
||||
layout(binding = 0) uniform sampler2D input_texture;
|
||||
|
||||
void main() {
|
||||
frag_color = texture(input_texture, tex_coord);
|
||||
}
|
@ -1,12 +1,33 @@
|
||||
//? #version 330
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2019 bloc97
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
//? #version 430 core
|
||||
precision mediump float;
|
||||
|
||||
in vec2 tex_coord;
|
||||
layout(location = 0) in vec2 tex_coord;
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
|
||||
out vec4 frag_color;
|
||||
|
||||
uniform sampler2D HOOKED;
|
||||
uniform sampler2D LUMAD;
|
||||
layout(binding = 0) uniform sampler2D HOOKED;
|
||||
layout(binding = 1) uniform sampler2D LUMAD;
|
||||
|
||||
const float LINE_DETECT_THRESHOLD = 0.4;
|
||||
const float STRENGTH = 0.6;
|
@ -1,5 +1,3 @@
|
||||
//? #version 320 es
|
||||
|
||||
// from https://github.com/BreadFish64/ScaleFish/tree/master/scale_force
|
||||
|
||||
// MIT License
|
||||
@ -24,13 +22,14 @@
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
//? #version 320 es
|
||||
|
||||
precision mediump float;
|
||||
|
||||
in vec2 tex_coord;
|
||||
layout(location = 0) in vec2 tex_coord;
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
|
||||
out vec4 frag_color;
|
||||
|
||||
uniform sampler2D input_texture;
|
||||
layout(binding = 0) uniform sampler2D input_texture;
|
||||
|
||||
vec2 tex_size;
|
||||
vec2 inv_tex_size;
|
@ -1,5 +1,9 @@
|
||||
//? #version 330
|
||||
out vec2 tex_coord;
|
||||
// Copyright 2023 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
//? #version 430 core
|
||||
layout(location = 0) out vec2 tex_coord;
|
||||
|
||||
const vec2 vertices[4] =
|
||||
vec2[4](vec2(-1.0, -1.0), vec2(1.0, -1.0), vec2(-1.0, 1.0), vec2(1.0, 1.0));
|
@ -0,0 +1,41 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2019 bloc97
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
//? #version 430 core
|
||||
precision mediump float;
|
||||
|
||||
layout(location = 0) in vec2 tex_coord;
|
||||
layout(location = 0) out vec2 frag_color;
|
||||
|
||||
layout(binding = 0) uniform sampler2D tex_input;
|
||||
|
||||
const vec3 K = vec3(0.2627, 0.6780, 0.0593);
|
||||
// TODO: improve handling of alpha channel
|
||||
#define GetLum(xoffset) dot(K, textureLodOffset(tex_input, tex_coord, 0.0, ivec2(xoffset, 0)).rgb)
|
||||
|
||||
void main() {
|
||||
float l = GetLum(-1);
|
||||
float c = GetLum(0);
|
||||
float r = GetLum(1);
|
||||
|
||||
frag_color = vec2(r - l, l + 2.0 * c + r);
|
||||
}
|
@ -1,14 +1,15 @@
|
||||
//? #version 330
|
||||
// Copyright 2023 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
//? #version 430 core
|
||||
precision mediump float;
|
||||
|
||||
in vec2 tex_coord;
|
||||
in vec2 source_size;
|
||||
in vec2 output_size;
|
||||
layout(location = 0) in vec2 tex_coord;
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
|
||||
out vec4 frag_color;
|
||||
|
||||
uniform sampler2D tex;
|
||||
uniform lowp float scale;
|
||||
layout(binding = 0) uniform sampler2D tex;
|
||||
layout(location = 2) uniform lowp float scale;
|
||||
|
||||
const int BLEND_NONE = 0;
|
||||
const int BLEND_NORMAL = 1;
|
||||
@ -47,6 +48,7 @@ float GetLeftRatio(vec2 center, vec2 origin, vec2 direction) {
|
||||
#define P(x, y) textureOffset(tex, coord, ivec2(x, y))
|
||||
|
||||
void main() {
|
||||
vec2 source_size = vec2(textureSize(tex, 0));
|
||||
vec2 pos = fract(tex_coord * source_size) - vec2(0.5, 0.5);
|
||||
vec2 coord = tex_coord - pos / source_size;
|
||||
|
@ -0,0 +1,39 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2019 bloc97
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
//? #version 430 core
|
||||
precision mediump float;
|
||||
|
||||
layout(location = 0) in vec2 tex_coord;
|
||||
layout(location = 0) out float frag_color;
|
||||
|
||||
layout(binding = 2) uniform sampler2D tex_input;
|
||||
|
||||
void main() {
|
||||
vec2 t = textureLodOffset(tex_input, tex_coord, 0.0, ivec2(0, 1)).xy;
|
||||
vec2 c = textureLod(tex_input, tex_coord, 0.0).xy;
|
||||
vec2 b = textureLodOffset(tex_input, tex_coord, 0.0, ivec2(0, -1)).xy;
|
||||
|
||||
vec2 grad = vec2(t.x + 2.0 * c.x + b.x, b.y - t.y);
|
||||
|
||||
frag_color = 1.0 - length(grad);
|
||||
}
|
@ -1,480 +0,0 @@
|
||||
// Copyright 2022 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/microprofile.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "common/texture.h"
|
||||
#include "core/core.h"
|
||||
#include "video_core/rasterizer_cache/cached_surface.h"
|
||||
#include "video_core/rasterizer_cache/morton_swizzle.h"
|
||||
#include "video_core/rasterizer_cache/rasterizer_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_state.h"
|
||||
#include "video_core/renderer_opengl/texture_downloader_es.h"
|
||||
#include "video_core/renderer_opengl/texture_filters/texture_filterer.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
static Aspect ToAspect(SurfaceType type) {
|
||||
switch (type) {
|
||||
case SurfaceType::Color:
|
||||
case SurfaceType::Texture:
|
||||
case SurfaceType::Fill:
|
||||
return Aspect::Color;
|
||||
case SurfaceType::Depth:
|
||||
return Aspect::Depth;
|
||||
case SurfaceType::DepthStencil:
|
||||
return Aspect::DepthStencil;
|
||||
default:
|
||||
LOG_CRITICAL(Render_OpenGL, "Unknown SurfaceType {}", type);
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
return Aspect::Color;
|
||||
}
|
||||
|
||||
CachedSurface::~CachedSurface() {
|
||||
if (texture.handle) {
|
||||
auto tag = is_custom ? HostTextureTag{GetFormatTuple(PixelFormat::RGBA8),
|
||||
custom_tex_info.width, custom_tex_info.height}
|
||||
: HostTextureTag{GetFormatTuple(pixel_format), GetScaledWidth(),
|
||||
GetScaledHeight()};
|
||||
|
||||
owner.host_texture_recycler.emplace(tag, std::move(texture));
|
||||
}
|
||||
}
|
||||
|
||||
MICROPROFILE_DEFINE(RasterizerCache_SurfaceLoad, "RasterizerCache", "Surface Load",
|
||||
MP_RGB(128, 192, 64));
|
||||
void CachedSurface::LoadGLBuffer(PAddr load_start, PAddr load_end) {
|
||||
ASSERT(type != SurfaceType::Fill);
|
||||
const bool need_swap =
|
||||
GLES && (pixel_format == PixelFormat::RGBA8 || pixel_format == PixelFormat::RGB8);
|
||||
|
||||
const u8* const texture_src_data = VideoCore::g_memory->GetPhysicalPointer(addr);
|
||||
if (texture_src_data == nullptr)
|
||||
return;
|
||||
|
||||
if (gl_buffer.empty()) {
|
||||
gl_buffer.resize(width * height * GetBytesPerPixel(pixel_format));
|
||||
}
|
||||
|
||||
// TODO: Should probably be done in ::Memory:: and check for other regions too
|
||||
if (load_start < Memory::VRAM_VADDR_END && load_end > Memory::VRAM_VADDR_END)
|
||||
load_end = Memory::VRAM_VADDR_END;
|
||||
|
||||
if (load_start < Memory::VRAM_VADDR && load_end > Memory::VRAM_VADDR)
|
||||
load_start = Memory::VRAM_VADDR;
|
||||
|
||||
MICROPROFILE_SCOPE(RasterizerCache_SurfaceLoad);
|
||||
|
||||
ASSERT(load_start >= addr && load_end <= end);
|
||||
const u32 start_offset = load_start - addr;
|
||||
|
||||
if (!is_tiled) {
|
||||
ASSERT(type == SurfaceType::Color);
|
||||
if (need_swap) {
|
||||
// TODO(liushuyu): check if the byteswap here is 100% correct
|
||||
// cannot fully test this
|
||||
if (pixel_format == PixelFormat::RGBA8) {
|
||||
for (std::size_t i = start_offset; i < load_end - addr; i += 4) {
|
||||
gl_buffer[i] = texture_src_data[i + 3];
|
||||
gl_buffer[i + 1] = texture_src_data[i + 2];
|
||||
gl_buffer[i + 2] = texture_src_data[i + 1];
|
||||
gl_buffer[i + 3] = texture_src_data[i];
|
||||
}
|
||||
} else if (pixel_format == PixelFormat::RGB8) {
|
||||
for (std::size_t i = start_offset; i < load_end - addr; i += 3) {
|
||||
gl_buffer[i] = texture_src_data[i + 2];
|
||||
gl_buffer[i + 1] = texture_src_data[i + 1];
|
||||
gl_buffer[i + 2] = texture_src_data[i];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
std::memcpy(&gl_buffer[start_offset], texture_src_data + start_offset,
|
||||
load_end - load_start);
|
||||
}
|
||||
} else {
|
||||
if (type == SurfaceType::Texture) {
|
||||
Pica::Texture::TextureInfo tex_info{};
|
||||
tex_info.width = width;
|
||||
tex_info.height = height;
|
||||
tex_info.format = static_cast<Pica::TexturingRegs::TextureFormat>(pixel_format);
|
||||
tex_info.SetDefaultStride();
|
||||
tex_info.physical_address = addr;
|
||||
|
||||
const SurfaceInterval load_interval(load_start, load_end);
|
||||
const auto rect = GetSubRect(FromInterval(load_interval));
|
||||
ASSERT(FromInterval(load_interval).GetInterval() == load_interval);
|
||||
|
||||
for (unsigned y = rect.bottom; y < rect.top; ++y) {
|
||||
for (unsigned x = rect.left; x < rect.right; ++x) {
|
||||
auto vec4 =
|
||||
Pica::Texture::LookupTexture(texture_src_data, x, height - 1 - y, tex_info);
|
||||
const std::size_t offset = (x + (width * y)) * 4;
|
||||
std::memcpy(&gl_buffer[offset], vec4.AsArray(), 4);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
morton_to_gl_fns[static_cast<std::size_t>(pixel_format)](stride, height, &gl_buffer[0],
|
||||
addr, load_start, load_end);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MICROPROFILE_DEFINE(RasterizerCache_SurfaceFlush, "RasterizerCache", "Surface Flush",
|
||||
MP_RGB(128, 192, 64));
|
||||
void CachedSurface::FlushGLBuffer(PAddr flush_start, PAddr flush_end) {
|
||||
u8* const dst_buffer = VideoCore::g_memory->GetPhysicalPointer(addr);
|
||||
if (dst_buffer == nullptr)
|
||||
return;
|
||||
|
||||
ASSERT(gl_buffer.size() == width * height * GetBytesPerPixel(pixel_format));
|
||||
|
||||
// TODO: Should probably be done in ::Memory:: and check for other regions too
|
||||
// same as loadglbuffer()
|
||||
if (flush_start < Memory::VRAM_VADDR_END && flush_end > Memory::VRAM_VADDR_END)
|
||||
flush_end = Memory::VRAM_VADDR_END;
|
||||
|
||||
if (flush_start < Memory::VRAM_VADDR && flush_end > Memory::VRAM_VADDR)
|
||||
flush_start = Memory::VRAM_VADDR;
|
||||
|
||||
MICROPROFILE_SCOPE(RasterizerCache_SurfaceFlush);
|
||||
|
||||
ASSERT(flush_start >= addr && flush_end <= end);
|
||||
const u32 start_offset = flush_start - addr;
|
||||
const u32 end_offset = flush_end - addr;
|
||||
|
||||
if (type == SurfaceType::Fill) {
|
||||
const u32 coarse_start_offset = start_offset - (start_offset % fill_size);
|
||||
const u32 backup_bytes = start_offset % fill_size;
|
||||
std::array<u8, 4> backup_data;
|
||||
if (backup_bytes)
|
||||
std::memcpy(&backup_data[0], &dst_buffer[coarse_start_offset], backup_bytes);
|
||||
|
||||
for (u32 offset = coarse_start_offset; offset < end_offset; offset += fill_size) {
|
||||
std::memcpy(&dst_buffer[offset], &fill_data[0],
|
||||
std::min(fill_size, end_offset - offset));
|
||||
}
|
||||
|
||||
if (backup_bytes)
|
||||
std::memcpy(&dst_buffer[coarse_start_offset], &backup_data[0], backup_bytes);
|
||||
} else if (!is_tiled) {
|
||||
ASSERT(type == SurfaceType::Color);
|
||||
if (pixel_format == PixelFormat::RGBA8 && GLES) {
|
||||
for (std::size_t i = start_offset; i < flush_end - addr; i += 4) {
|
||||
dst_buffer[i] = gl_buffer[i + 3];
|
||||
dst_buffer[i + 1] = gl_buffer[i + 2];
|
||||
dst_buffer[i + 2] = gl_buffer[i + 1];
|
||||
dst_buffer[i + 3] = gl_buffer[i];
|
||||
}
|
||||
} else if (pixel_format == PixelFormat::RGB8 && GLES) {
|
||||
for (std::size_t i = start_offset; i < flush_end - addr; i += 3) {
|
||||
dst_buffer[i] = gl_buffer[i + 2];
|
||||
dst_buffer[i + 1] = gl_buffer[i + 1];
|
||||
dst_buffer[i + 2] = gl_buffer[i];
|
||||
}
|
||||
} else {
|
||||
std::memcpy(dst_buffer + start_offset, &gl_buffer[start_offset],
|
||||
flush_end - flush_start);
|
||||
}
|
||||
} else {
|
||||
gl_to_morton_fns[static_cast<std::size_t>(pixel_format)](stride, height, &gl_buffer[0],
|
||||
addr, flush_start, flush_end);
|
||||
}
|
||||
}
|
||||
|
||||
bool CachedSurface::LoadCustomTexture(u64 tex_hash) {
|
||||
auto& custom_tex_cache = Core::System::GetInstance().CustomTexCache();
|
||||
const auto& image_interface = Core::System::GetInstance().GetImageInterface();
|
||||
|
||||
if (custom_tex_cache.IsTextureCached(tex_hash)) {
|
||||
custom_tex_info = custom_tex_cache.LookupTexture(tex_hash);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!custom_tex_cache.CustomTextureExists(tex_hash)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& path_info = custom_tex_cache.LookupTexturePathInfo(tex_hash);
|
||||
if (!image_interface->DecodePNG(custom_tex_info.tex, custom_tex_info.width,
|
||||
custom_tex_info.height, path_info.path)) {
|
||||
LOG_ERROR(Render_OpenGL, "Failed to load custom texture {}", path_info.path);
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::bitset<32> width_bits(custom_tex_info.width);
|
||||
const std::bitset<32> height_bits(custom_tex_info.height);
|
||||
if (width_bits.count() != 1 || height_bits.count() != 1) {
|
||||
LOG_ERROR(Render_OpenGL, "Texture {} size is not a power of 2", path_info.path);
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Render_OpenGL, "Loaded custom texture from {}", path_info.path);
|
||||
Common::FlipRGBA8Texture(custom_tex_info.tex, custom_tex_info.width, custom_tex_info.height);
|
||||
custom_tex_cache.CacheTexture(tex_hash, custom_tex_info.tex, custom_tex_info.width,
|
||||
custom_tex_info.height);
|
||||
return true;
|
||||
}
|
||||
|
||||
void CachedSurface::DumpTexture(GLuint target_tex, u64 tex_hash) {
|
||||
// Make sure the texture size is a power of 2
|
||||
// If not, the surface is actually a framebuffer
|
||||
std::bitset<32> width_bits(width);
|
||||
std::bitset<32> height_bits(height);
|
||||
if (width_bits.count() != 1 || height_bits.count() != 1) {
|
||||
LOG_WARNING(Render_OpenGL, "Not dumping {:016X} because size isn't a power of 2 ({}x{})",
|
||||
tex_hash, width, height);
|
||||
return;
|
||||
}
|
||||
|
||||
// Dump texture to RGBA8 and encode as PNG
|
||||
const auto& image_interface = Core::System::GetInstance().GetImageInterface();
|
||||
auto& custom_tex_cache = Core::System::GetInstance().CustomTexCache();
|
||||
std::string dump_path =
|
||||
fmt::format("{}textures/{:016X}/", FileUtil::GetUserPath(FileUtil::UserPath::DumpDir),
|
||||
Core::System::GetInstance().Kernel().GetCurrentProcess()->codeset->program_id);
|
||||
if (!FileUtil::CreateFullPath(dump_path)) {
|
||||
LOG_ERROR(Render, "Unable to create {}", dump_path);
|
||||
return;
|
||||
}
|
||||
|
||||
dump_path += fmt::format("tex1_{}x{}_{:016X}_{}.png", width, height, tex_hash, pixel_format);
|
||||
if (!custom_tex_cache.IsTextureDumped(tex_hash) && !FileUtil::Exists(dump_path)) {
|
||||
custom_tex_cache.SetTextureDumped(tex_hash);
|
||||
|
||||
LOG_INFO(Render_OpenGL, "Dumping texture to {}", dump_path);
|
||||
std::vector<u8> decoded_texture;
|
||||
decoded_texture.resize(width * height * 4);
|
||||
OpenGLState state = OpenGLState::GetCurState();
|
||||
GLuint old_texture = state.texture_units[0].texture_2d;
|
||||
state.Apply();
|
||||
/*
|
||||
GetTexImageOES is used even if not using OpenGL ES to work around a small issue that
|
||||
happens if using custom textures with texture dumping at the same.
|
||||
Let's say there's 2 textures that are both 32x32 and one of them gets replaced with a
|
||||
higher quality 256x256 texture. If the 256x256 texture is displayed first and the
|
||||
32x32 texture gets uploaded to the same underlying OpenGL texture, the 32x32 texture
|
||||
will appear in the corner of the 256x256 texture. If texture dumping is enabled and
|
||||
the 32x32 is undumped, Citra will attempt to dump it. Since the underlying OpenGL
|
||||
texture is still 256x256, Citra crashes because it thinks the texture is only 32x32.
|
||||
GetTexImageOES conveniently only dumps the specified region, and works on both
|
||||
desktop and ES.
|
||||
*/
|
||||
owner.texture_downloader_es->GetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE,
|
||||
height, width, &decoded_texture[0]);
|
||||
state.texture_units[0].texture_2d = old_texture;
|
||||
state.Apply();
|
||||
Common::FlipRGBA8Texture(decoded_texture, width, height);
|
||||
if (!image_interface->EncodePNG(dump_path, decoded_texture, width, height))
|
||||
LOG_ERROR(Render_OpenGL, "Failed to save decoded texture");
|
||||
}
|
||||
}
|
||||
|
||||
MICROPROFILE_DEFINE(RasterizerCache_TextureUL, "RasterizerCache", "Texture Upload",
|
||||
MP_RGB(128, 192, 64));
|
||||
void CachedSurface::UploadGLTexture(Common::Rectangle<u32> rect) {
|
||||
if (type == SurfaceType::Fill) {
|
||||
return;
|
||||
}
|
||||
|
||||
MICROPROFILE_SCOPE(RasterizerCache_TextureUL);
|
||||
ASSERT(gl_buffer.size() == width * height * GetBytesPerPixel(pixel_format));
|
||||
|
||||
u64 tex_hash = 0;
|
||||
|
||||
if (Settings::values.dump_textures || Settings::values.custom_textures) {
|
||||
tex_hash = Common::ComputeHash64(gl_buffer.data(), gl_buffer.size());
|
||||
}
|
||||
|
||||
if (Settings::values.custom_textures) {
|
||||
is_custom = LoadCustomTexture(tex_hash);
|
||||
}
|
||||
|
||||
// Load data from memory to the surface
|
||||
GLint x0 = static_cast<GLint>(rect.left);
|
||||
GLint y0 = static_cast<GLint>(rect.bottom);
|
||||
std::size_t buffer_offset = (y0 * stride + x0) * GetBytesPerPixel(pixel_format);
|
||||
|
||||
const FormatTuple& tuple = GetFormatTuple(pixel_format);
|
||||
GLuint target_tex = texture.handle;
|
||||
|
||||
// If not 1x scale, create 1x texture that we will blit from to replace texture subrect in
|
||||
// surface
|
||||
OGLTexture unscaled_tex;
|
||||
if (res_scale != 1) {
|
||||
x0 = 0;
|
||||
y0 = 0;
|
||||
|
||||
if (is_custom) {
|
||||
const auto& tuple = GetFormatTuple(PixelFormat::RGBA8);
|
||||
unscaled_tex =
|
||||
owner.AllocateSurfaceTexture(tuple, custom_tex_info.width, custom_tex_info.height);
|
||||
} else {
|
||||
unscaled_tex = owner.AllocateSurfaceTexture(tuple, rect.GetWidth(), rect.GetHeight());
|
||||
}
|
||||
|
||||
target_tex = unscaled_tex.handle;
|
||||
}
|
||||
|
||||
OpenGLState cur_state = OpenGLState::GetCurState();
|
||||
|
||||
GLuint old_tex = cur_state.texture_units[0].texture_2d;
|
||||
cur_state.texture_units[0].texture_2d = target_tex;
|
||||
cur_state.Apply();
|
||||
|
||||
// Ensure no bad interactions with GL_UNPACK_ALIGNMENT
|
||||
ASSERT(stride * GetBytesPerPixel(pixel_format) % 4 == 0);
|
||||
if (is_custom) {
|
||||
if (res_scale == 1) {
|
||||
texture = owner.AllocateSurfaceTexture(GetFormatTuple(PixelFormat::RGBA8),
|
||||
custom_tex_info.width, custom_tex_info.height);
|
||||
cur_state.texture_units[0].texture_2d = texture.handle;
|
||||
cur_state.Apply();
|
||||
}
|
||||
|
||||
// Always going to be using rgba8
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(custom_tex_info.width));
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, x0, y0, custom_tex_info.width, custom_tex_info.height,
|
||||
GL_RGBA, GL_UNSIGNED_BYTE, custom_tex_info.tex.data());
|
||||
} else {
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(stride));
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, x0, y0, static_cast<GLsizei>(rect.GetWidth()),
|
||||
static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type,
|
||||
&gl_buffer[buffer_offset]);
|
||||
}
|
||||
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
if (Settings::values.dump_textures && !is_custom) {
|
||||
DumpTexture(target_tex, tex_hash);
|
||||
}
|
||||
|
||||
cur_state.texture_units[0].texture_2d = old_tex;
|
||||
cur_state.Apply();
|
||||
|
||||
if (res_scale != 1) {
|
||||
auto scaled_rect = rect;
|
||||
scaled_rect.left *= res_scale;
|
||||
scaled_rect.top *= res_scale;
|
||||
scaled_rect.right *= res_scale;
|
||||
scaled_rect.bottom *= res_scale;
|
||||
|
||||
const u32 width = is_custom ? custom_tex_info.width : rect.GetWidth();
|
||||
const u32 height = is_custom ? custom_tex_info.height : rect.GetHeight();
|
||||
const Common::Rectangle<u32> from_rect{0, height, width, 0};
|
||||
|
||||
if (is_custom ||
|
||||
!owner.texture_filterer->Filter(unscaled_tex, from_rect, texture, scaled_rect, type)) {
|
||||
const Aspect aspect = ToAspect(type);
|
||||
runtime.BlitTextures(unscaled_tex, {aspect, from_rect}, texture, {aspect, scaled_rect});
|
||||
}
|
||||
}
|
||||
|
||||
InvalidateAllWatcher();
|
||||
}
|
||||
|
||||
MICROPROFILE_DEFINE(RasterizerCache_TextureDL, "RasterizerCache", "Texture Download",
|
||||
MP_RGB(128, 192, 64));
|
||||
void CachedSurface::DownloadGLTexture(const Common::Rectangle<u32>& rect) {
|
||||
if (type == SurfaceType::Fill) {
|
||||
return;
|
||||
}
|
||||
|
||||
MICROPROFILE_SCOPE(RasterizerCache_TextureDL);
|
||||
|
||||
if (gl_buffer.empty()) {
|
||||
gl_buffer.resize(width * height * GetBytesPerPixel(pixel_format));
|
||||
}
|
||||
|
||||
OpenGLState state = OpenGLState::GetCurState();
|
||||
OpenGLState prev_state = state;
|
||||
SCOPE_EXIT({ prev_state.Apply(); });
|
||||
|
||||
const FormatTuple& tuple = GetFormatTuple(pixel_format);
|
||||
|
||||
// Ensure no bad interactions with GL_PACK_ALIGNMENT
|
||||
ASSERT(stride * GetBytesPerPixel(pixel_format) % 4 == 0);
|
||||
glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(stride));
|
||||
const std::size_t buffer_offset =
|
||||
(rect.bottom * stride + rect.left) * GetBytesPerPixel(pixel_format);
|
||||
|
||||
// If not 1x scale, blit scaled texture to a new 1x texture and use that to flush
|
||||
const Aspect aspect = ToAspect(type);
|
||||
if (res_scale != 1) {
|
||||
auto scaled_rect = rect;
|
||||
scaled_rect.left *= res_scale;
|
||||
scaled_rect.top *= res_scale;
|
||||
scaled_rect.right *= res_scale;
|
||||
scaled_rect.bottom *= res_scale;
|
||||
|
||||
const Common::Rectangle<u32> unscaled_tex_rect{0, rect.GetHeight(), rect.GetWidth(), 0};
|
||||
auto unscaled_tex = owner.AllocateSurfaceTexture(tuple, rect.GetWidth(), rect.GetHeight());
|
||||
// Blit scaled texture to the unscaled one
|
||||
runtime.BlitTextures(texture, {aspect, scaled_rect}, unscaled_tex,
|
||||
{aspect, unscaled_tex_rect});
|
||||
|
||||
state.texture_units[0].texture_2d = unscaled_tex.handle;
|
||||
state.Apply();
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
if (GLES) {
|
||||
owner.texture_downloader_es->GetTexImage(GL_TEXTURE_2D, 0, tuple.format, tuple.type,
|
||||
rect.GetHeight(), rect.GetWidth(),
|
||||
&gl_buffer[buffer_offset]);
|
||||
} else {
|
||||
glGetTexImage(GL_TEXTURE_2D, 0, tuple.format, tuple.type, &gl_buffer[buffer_offset]);
|
||||
}
|
||||
} else {
|
||||
runtime.ReadTexture(texture, {aspect, rect}, tuple, gl_buffer.data());
|
||||
}
|
||||
|
||||
glPixelStorei(GL_PACK_ROW_LENGTH, 0);
|
||||
}
|
||||
|
||||
bool CachedSurface::CanFill(const SurfaceParams& dest_surface,
|
||||
SurfaceInterval fill_interval) const {
|
||||
if (type == SurfaceType::Fill && IsRegionValid(fill_interval) &&
|
||||
boost::icl::first(fill_interval) >= addr &&
|
||||
boost::icl::last_next(fill_interval) <= end && // dest_surface is within our fill range
|
||||
dest_surface.FromInterval(fill_interval).GetInterval() ==
|
||||
fill_interval) { // make sure interval is a rectangle in dest surface
|
||||
if (fill_size * 8 != dest_surface.GetFormatBpp()) {
|
||||
// Check if bits repeat for our fill_size
|
||||
const u32 dest_bytes_per_pixel = std::max(dest_surface.GetFormatBpp() / 8, 1u);
|
||||
std::vector<u8> fill_test(fill_size * dest_bytes_per_pixel);
|
||||
|
||||
for (u32 i = 0; i < dest_bytes_per_pixel; ++i)
|
||||
std::memcpy(&fill_test[i * fill_size], &fill_data[0], fill_size);
|
||||
|
||||
for (u32 i = 0; i < fill_size; ++i)
|
||||
if (std::memcmp(&fill_test[dest_bytes_per_pixel * i], &fill_test[0],
|
||||
dest_bytes_per_pixel) != 0)
|
||||
return false;
|
||||
|
||||
if (dest_surface.GetFormatBpp() == 4 && (fill_test[0] & 0xF) != (fill_test[0] >> 4))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CachedSurface::CanCopy(const SurfaceParams& dest_surface,
|
||||
SurfaceInterval copy_interval) const {
|
||||
SurfaceParams subrect_params = dest_surface.FromInterval(copy_interval);
|
||||
ASSERT(subrect_params.GetInterval() == copy_interval);
|
||||
if (CanSubRect(subrect_params))
|
||||
return true;
|
||||
|
||||
if (CanFill(dest_surface, copy_interval))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace OpenGL
|
@ -1,137 +0,0 @@
|
||||
// Copyright 2022 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
#include <list>
|
||||
#include "common/assert.h"
|
||||
#include "core/custom_tex_cache.h"
|
||||
#include "video_core/rasterizer_cache/surface_params.h"
|
||||
#include "video_core/rasterizer_cache/texture_runtime.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
/**
|
||||
* A watcher that notifies whether a cached surface has been changed. This is useful for caching
|
||||
* surface collection objects, including texture cube and mipmap.
|
||||
*/
|
||||
class SurfaceWatcher {
|
||||
friend class CachedSurface;
|
||||
|
||||
public:
|
||||
explicit SurfaceWatcher(std::weak_ptr<CachedSurface>&& surface) : surface(std::move(surface)) {}
|
||||
|
||||
/// Checks whether the surface has been changed.
|
||||
bool IsValid() const {
|
||||
return !surface.expired() && valid;
|
||||
}
|
||||
|
||||
/// Marks that the content of the referencing surface has been updated to the watcher user.
|
||||
void Validate() {
|
||||
ASSERT(!surface.expired());
|
||||
valid = true;
|
||||
}
|
||||
|
||||
/// Gets the referencing surface. Returns null if the surface has been destroyed
|
||||
Surface Get() const {
|
||||
return surface.lock();
|
||||
}
|
||||
|
||||
private:
|
||||
std::weak_ptr<CachedSurface> surface;
|
||||
bool valid = false;
|
||||
};
|
||||
|
||||
class RasterizerCacheOpenGL;
|
||||
|
||||
class CachedSurface : public SurfaceParams, public std::enable_shared_from_this<CachedSurface> {
|
||||
public:
|
||||
CachedSurface(SurfaceParams params, RasterizerCacheOpenGL& owner, TextureRuntime& runtime)
|
||||
: SurfaceParams(params), owner(owner), runtime(runtime) {}
|
||||
~CachedSurface();
|
||||
|
||||
/// Read/Write data in 3DS memory to/from gl_buffer
|
||||
void LoadGLBuffer(PAddr load_start, PAddr load_end);
|
||||
void FlushGLBuffer(PAddr flush_start, PAddr flush_end);
|
||||
|
||||
/// Custom texture loading and dumping
|
||||
bool LoadCustomTexture(u64 tex_hash);
|
||||
void DumpTexture(GLuint target_tex, u64 tex_hash);
|
||||
|
||||
/// Upload/Download data in gl_buffer in/to this surface's texture
|
||||
void UploadGLTexture(Common::Rectangle<u32> rect);
|
||||
void DownloadGLTexture(const Common::Rectangle<u32>& rect);
|
||||
|
||||
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 {
|
||||
auto interval = GetInterval();
|
||||
return *invalid_regions.equal_range(interval).first == interval;
|
||||
}
|
||||
|
||||
std::shared_ptr<SurfaceWatcher> CreateWatcher() {
|
||||
auto watcher = std::make_shared<SurfaceWatcher>(weak_from_this());
|
||||
watchers.push_front(watcher);
|
||||
return watcher;
|
||||
}
|
||||
|
||||
void InvalidateAllWatcher() {
|
||||
for (const auto& watcher : watchers) {
|
||||
if (auto locked = watcher.lock()) {
|
||||
locked->valid = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UnlinkAllWatcher() {
|
||||
for (const auto& watcher : watchers) {
|
||||
if (auto locked = watcher.lock()) {
|
||||
locked->valid = false;
|
||||
locked->surface.reset();
|
||||
}
|
||||
}
|
||||
|
||||
watchers.clear();
|
||||
}
|
||||
|
||||
public:
|
||||
bool registered = false;
|
||||
SurfaceRegions invalid_regions;
|
||||
std::vector<u8> gl_buffer;
|
||||
|
||||
// Number of bytes to read from fill_data
|
||||
u32 fill_size = 0;
|
||||
std::array<u8, 4> fill_data;
|
||||
OGLTexture texture;
|
||||
|
||||
// level_watchers[i] watches the (i+1)-th level mipmap source surface
|
||||
std::array<std::shared_ptr<SurfaceWatcher>, 7> level_watchers;
|
||||
u32 max_level = 0;
|
||||
|
||||
// Information about custom textures
|
||||
bool is_custom = false;
|
||||
Core::CustomTexInfo custom_tex_info;
|
||||
|
||||
private:
|
||||
RasterizerCacheOpenGL& owner;
|
||||
TextureRuntime& runtime;
|
||||
std::list<std::weak_ptr<SurfaceWatcher>> watchers;
|
||||
};
|
||||
|
||||
struct CachedTextureCube {
|
||||
OGLTexture texture;
|
||||
u16 res_scale = 1;
|
||||
std::shared_ptr<SurfaceWatcher> px;
|
||||
std::shared_ptr<SurfaceWatcher> nx;
|
||||
std::shared_ptr<SurfaceWatcher> py;
|
||||
std::shared_ptr<SurfaceWatcher> ny;
|
||||
std::shared_ptr<SurfaceWatcher> pz;
|
||||
std::shared_ptr<SurfaceWatcher> nz;
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
73
src/video_core/rasterizer_cache/framebuffer_base.cpp
Normal file
73
src/video_core/rasterizer_cache/framebuffer_base.cpp
Normal file
@ -0,0 +1,73 @@
|
||||
// Copyright 2023 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "video_core/rasterizer_cache/framebuffer_base.h"
|
||||
#include "video_core/rasterizer_cache/surface_base.h"
|
||||
#include "video_core/regs.h"
|
||||
|
||||
namespace VideoCore {
|
||||
|
||||
FramebufferBase::FramebufferBase() = default;
|
||||
|
||||
FramebufferBase::FramebufferBase(const Pica::Regs& regs, const SurfaceBase* const color,
|
||||
u32 color_level, const SurfaceBase* const depth_stencil,
|
||||
u32 depth_level, Common::Rectangle<u32> surfaces_rect) {
|
||||
res_scale = color ? color->res_scale : (depth_stencil ? depth_stencil->res_scale : 1u);
|
||||
|
||||
// Determine the draw rectangle (render area + scissor)
|
||||
const Common::Rectangle viewport_rect = regs.rasterizer.GetViewportRect();
|
||||
draw_rect.left =
|
||||
std::clamp<s32>(static_cast<s32>(surfaces_rect.left) + viewport_rect.left * res_scale,
|
||||
surfaces_rect.left, surfaces_rect.right);
|
||||
draw_rect.top =
|
||||
std::clamp<s32>(static_cast<s32>(surfaces_rect.bottom) + viewport_rect.top * res_scale,
|
||||
surfaces_rect.bottom, surfaces_rect.top);
|
||||
draw_rect.right =
|
||||
std::clamp<s32>(static_cast<s32>(surfaces_rect.left) + viewport_rect.right * res_scale,
|
||||
surfaces_rect.left, surfaces_rect.right);
|
||||
draw_rect.bottom =
|
||||
std::clamp<s32>(static_cast<s32>(surfaces_rect.bottom) + viewport_rect.bottom * res_scale,
|
||||
surfaces_rect.bottom, surfaces_rect.top);
|
||||
|
||||
// Update viewport
|
||||
viewport.x = static_cast<f32>(surfaces_rect.left + viewport_rect.left * res_scale);
|
||||
viewport.y = static_cast<f32>(surfaces_rect.bottom + viewport_rect.bottom * res_scale);
|
||||
viewport.width = static_cast<f32>(viewport_rect.GetWidth() * res_scale);
|
||||
viewport.height = static_cast<f32>(viewport_rect.GetHeight() * res_scale);
|
||||
|
||||
// Scissor checks are window-, not viewport-relative, which means that if the cached texture
|
||||
// sub-rect changes, the scissor bounds also need to be updated.
|
||||
scissor_rect.left =
|
||||
static_cast<s32>(surfaces_rect.left + regs.rasterizer.scissor_test.x1 * res_scale);
|
||||
scissor_rect.bottom =
|
||||
static_cast<s32>(surfaces_rect.bottom + regs.rasterizer.scissor_test.y1 * res_scale);
|
||||
|
||||
// x2, y2 have +1 added to cover the entire pixel area, otherwise you might get cracks when
|
||||
// scaling or doing multisampling.
|
||||
scissor_rect.right =
|
||||
static_cast<s32>(surfaces_rect.left + (regs.rasterizer.scissor_test.x2 + 1) * res_scale);
|
||||
scissor_rect.top =
|
||||
static_cast<s32>(surfaces_rect.bottom + (regs.rasterizer.scissor_test.y2 + 1) * res_scale);
|
||||
|
||||
// Rendering to mipmaps is something quite rare so log it when it occurs.
|
||||
if (color_level != 0) {
|
||||
LOG_WARNING(HW_GPU, "Game is rendering to color mipmap {}", color_level);
|
||||
}
|
||||
if (depth_level != 0) {
|
||||
LOG_WARNING(HW_GPU, "Game is rendering to depth mipmap {}", depth_level);
|
||||
}
|
||||
|
||||
// Query surface invalidation intervals
|
||||
const Common::Rectangle draw_rect_unscaled{draw_rect / res_scale};
|
||||
if (color) {
|
||||
color_params = *color;
|
||||
intervals[0] = color->GetSubRectInterval(draw_rect_unscaled, color_level);
|
||||
}
|
||||
if (depth_stencil) {
|
||||
depth_params = *depth_stencil;
|
||||
intervals[1] = depth_stencil->GetSubRectInterval(draw_rect_unscaled, depth_level);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace VideoCore
|
87
src/video_core/rasterizer_cache/framebuffer_base.h
Normal file
87
src/video_core/rasterizer_cache/framebuffer_base.h
Normal file
@ -0,0 +1,87 @@
|
||||
// Copyright 2023 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/math_util.h"
|
||||
#include "video_core/rasterizer_cache/surface_params.h"
|
||||
|
||||
namespace Pica {
|
||||
struct Regs;
|
||||
}
|
||||
|
||||
namespace VideoCore {
|
||||
|
||||
class SurfaceBase;
|
||||
|
||||
struct ViewportInfo {
|
||||
f32 x;
|
||||
f32 y;
|
||||
f32 width;
|
||||
f32 height;
|
||||
};
|
||||
|
||||
/**
|
||||
* A framebuffer is a lightweight abstraction over a pair of surfaces and provides
|
||||
* metadata about them.
|
||||
*/
|
||||
class FramebufferBase {
|
||||
public:
|
||||
FramebufferBase();
|
||||
FramebufferBase(const Pica::Regs& regs, const SurfaceBase* const color, u32 color_level,
|
||||
const SurfaceBase* const depth_stencil, u32 depth_level,
|
||||
Common::Rectangle<u32> surfaces_rect);
|
||||
|
||||
SurfaceParams ColorParams() const noexcept {
|
||||
return color_params;
|
||||
}
|
||||
|
||||
SurfaceParams DepthParams() const noexcept {
|
||||
return depth_params;
|
||||
}
|
||||
|
||||
SurfaceInterval Interval(SurfaceType type) const noexcept {
|
||||
return intervals[Index(type)];
|
||||
}
|
||||
|
||||
u32 ResolutionScale() const noexcept {
|
||||
return res_scale;
|
||||
}
|
||||
|
||||
Common::Rectangle<u32> DrawRect() const noexcept {
|
||||
return draw_rect;
|
||||
}
|
||||
|
||||
Common::Rectangle<s32> Scissor() const noexcept {
|
||||
return scissor_rect;
|
||||
}
|
||||
|
||||
ViewportInfo Viewport() const noexcept {
|
||||
return viewport;
|
||||
}
|
||||
|
||||
protected:
|
||||
u32 Index(VideoCore::SurfaceType type) const noexcept {
|
||||
switch (type) {
|
||||
case VideoCore::SurfaceType::Color:
|
||||
return 0;
|
||||
case VideoCore::SurfaceType::DepthStencil:
|
||||
return 1;
|
||||
default:
|
||||
LOG_CRITICAL(HW_GPU, "Unknown surface type in framebuffer");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
SurfaceParams color_params{};
|
||||
SurfaceParams depth_params{};
|
||||
std::array<SurfaceInterval, 2> intervals{};
|
||||
Common::Rectangle<s32> scissor_rect{};
|
||||
Common::Rectangle<u32> draw_rect{};
|
||||
ViewportInfo viewport;
|
||||
u32 res_scale{1};
|
||||
};
|
||||
|
||||
} // namespace VideoCore
|
@ -1,171 +0,0 @@
|
||||
// Copyright 2022 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
#include "common/alignment.h"
|
||||
#include "core/memory.h"
|
||||
#include "video_core/rasterizer_cache/pixel_format.h"
|
||||
#include "video_core/renderer_opengl/gl_vars.h"
|
||||
#include "video_core/utils.h"
|
||||
#include "video_core/video_core.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
template <bool morton_to_gl, PixelFormat format>
|
||||
static void MortonCopyTile(u32 stride, u8* tile_buffer, u8* gl_buffer) {
|
||||
constexpr u32 bytes_per_pixel = GetFormatBpp(format) / 8;
|
||||
constexpr u32 aligned_bytes_per_pixel = GetBytesPerPixel(format);
|
||||
for (u32 y = 0; y < 8; ++y) {
|
||||
for (u32 x = 0; x < 8; ++x) {
|
||||
u8* tile_ptr = tile_buffer + VideoCore::MortonInterleave(x, y) * bytes_per_pixel;
|
||||
u8* gl_ptr = gl_buffer + ((7 - y) * stride + x) * aligned_bytes_per_pixel;
|
||||
if constexpr (morton_to_gl) {
|
||||
if constexpr (format == PixelFormat::D24S8) {
|
||||
gl_ptr[0] = tile_ptr[3];
|
||||
std::memcpy(gl_ptr + 1, tile_ptr, 3);
|
||||
} else if (format == PixelFormat::RGBA8 && GLES) {
|
||||
// because GLES does not have ABGR format
|
||||
// so we will do byteswapping here
|
||||
gl_ptr[0] = tile_ptr[3];
|
||||
gl_ptr[1] = tile_ptr[2];
|
||||
gl_ptr[2] = tile_ptr[1];
|
||||
gl_ptr[3] = tile_ptr[0];
|
||||
} else if (format == PixelFormat::RGB8 && GLES) {
|
||||
gl_ptr[0] = tile_ptr[2];
|
||||
gl_ptr[1] = tile_ptr[1];
|
||||
gl_ptr[2] = tile_ptr[0];
|
||||
} else {
|
||||
std::memcpy(gl_ptr, tile_ptr, bytes_per_pixel);
|
||||
}
|
||||
} else {
|
||||
if constexpr (format == PixelFormat::D24S8) {
|
||||
std::memcpy(tile_ptr, gl_ptr + 1, 3);
|
||||
tile_ptr[3] = gl_ptr[0];
|
||||
} else if (format == PixelFormat::RGBA8 && GLES) {
|
||||
// because GLES does not have ABGR format
|
||||
// so we will do byteswapping here
|
||||
tile_ptr[0] = gl_ptr[3];
|
||||
tile_ptr[1] = gl_ptr[2];
|
||||
tile_ptr[2] = gl_ptr[1];
|
||||
tile_ptr[3] = gl_ptr[0];
|
||||
} else if (format == PixelFormat::RGB8 && GLES) {
|
||||
tile_ptr[0] = gl_ptr[2];
|
||||
tile_ptr[1] = gl_ptr[1];
|
||||
tile_ptr[2] = gl_ptr[0];
|
||||
} else {
|
||||
std::memcpy(tile_ptr, gl_ptr, bytes_per_pixel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <bool morton_to_gl, PixelFormat format>
|
||||
static void MortonCopy(u32 stride, u32 height, u8* gl_buffer, PAddr base, PAddr start, PAddr end) {
|
||||
constexpr u32 bytes_per_pixel = GetFormatBpp(format) / 8;
|
||||
constexpr u32 tile_size = bytes_per_pixel * 64;
|
||||
|
||||
constexpr u32 aligned_bytes_per_pixel = GetBytesPerPixel(format);
|
||||
static_assert(aligned_bytes_per_pixel >= bytes_per_pixel, "");
|
||||
gl_buffer += aligned_bytes_per_pixel - bytes_per_pixel;
|
||||
|
||||
const PAddr aligned_down_start = base + Common::AlignDown(start - base, tile_size);
|
||||
const PAddr aligned_start = base + Common::AlignUp(start - base, tile_size);
|
||||
const PAddr aligned_end = base + Common::AlignDown(end - base, tile_size);
|
||||
|
||||
ASSERT(!morton_to_gl || (aligned_start == start && aligned_end == end));
|
||||
|
||||
const u32 begin_pixel_index = (aligned_down_start - base) / bytes_per_pixel;
|
||||
u32 x = (begin_pixel_index % (stride * 8)) / 8;
|
||||
u32 y = (begin_pixel_index / (stride * 8)) * 8;
|
||||
|
||||
gl_buffer += ((height - 8 - y) * stride + x) * aligned_bytes_per_pixel;
|
||||
|
||||
auto glbuf_next_tile = [&] {
|
||||
x = (x + 8) % stride;
|
||||
gl_buffer += 8 * aligned_bytes_per_pixel;
|
||||
if (!x) {
|
||||
y += 8;
|
||||
gl_buffer -= stride * 9 * aligned_bytes_per_pixel;
|
||||
}
|
||||
};
|
||||
|
||||
u8* tile_buffer = VideoCore::g_memory->GetPhysicalPointer(start);
|
||||
|
||||
if (start < aligned_start && !morton_to_gl) {
|
||||
std::array<u8, tile_size> tmp_buf;
|
||||
MortonCopyTile<morton_to_gl, format>(stride, &tmp_buf[0], gl_buffer);
|
||||
std::memcpy(tile_buffer, &tmp_buf[start - aligned_down_start],
|
||||
std::min(aligned_start, end) - start);
|
||||
|
||||
tile_buffer += aligned_start - start;
|
||||
glbuf_next_tile();
|
||||
}
|
||||
|
||||
const u8* const buffer_end = tile_buffer + aligned_end - aligned_start;
|
||||
PAddr current_paddr = aligned_start;
|
||||
while (tile_buffer < buffer_end) {
|
||||
// Pokemon Super Mystery Dungeon will try to use textures that go beyond
|
||||
// the end address of VRAM. Stop reading if reaches invalid address
|
||||
if (!VideoCore::g_memory->IsValidPhysicalAddress(current_paddr) ||
|
||||
!VideoCore::g_memory->IsValidPhysicalAddress(current_paddr + tile_size)) {
|
||||
LOG_ERROR(Render_OpenGL, "Out of bound texture");
|
||||
break;
|
||||
}
|
||||
MortonCopyTile<morton_to_gl, format>(stride, tile_buffer, gl_buffer);
|
||||
tile_buffer += tile_size;
|
||||
current_paddr += tile_size;
|
||||
glbuf_next_tile();
|
||||
}
|
||||
|
||||
if (end > std::max(aligned_start, aligned_end) && !morton_to_gl) {
|
||||
std::array<u8, tile_size> tmp_buf;
|
||||
MortonCopyTile<morton_to_gl, format>(stride, &tmp_buf[0], gl_buffer);
|
||||
std::memcpy(tile_buffer, &tmp_buf[0], end - aligned_end);
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr std::array<void (*)(u32, u32, u8*, PAddr, PAddr, PAddr), 18> morton_to_gl_fns = {
|
||||
MortonCopy<true, PixelFormat::RGBA8>, // 0
|
||||
MortonCopy<true, PixelFormat::RGB8>, // 1
|
||||
MortonCopy<true, PixelFormat::RGB5A1>, // 2
|
||||
MortonCopy<true, PixelFormat::RGB565>, // 3
|
||||
MortonCopy<true, PixelFormat::RGBA4>, // 4
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr, // 5 - 13
|
||||
MortonCopy<true, PixelFormat::D16>, // 14
|
||||
nullptr, // 15
|
||||
MortonCopy<true, PixelFormat::D24>, // 16
|
||||
MortonCopy<true, PixelFormat::D24S8> // 17
|
||||
};
|
||||
|
||||
static constexpr std::array<void (*)(u32, u32, u8*, PAddr, PAddr, PAddr), 18> gl_to_morton_fns = {
|
||||
MortonCopy<false, PixelFormat::RGBA8>, // 0
|
||||
MortonCopy<false, PixelFormat::RGB8>, // 1
|
||||
MortonCopy<false, PixelFormat::RGB5A1>, // 2
|
||||
MortonCopy<false, PixelFormat::RGB565>, // 3
|
||||
MortonCopy<false, PixelFormat::RGBA4>, // 4
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr, // 5 - 13
|
||||
MortonCopy<false, PixelFormat::D16>, // 14
|
||||
nullptr, // 15
|
||||
MortonCopy<false, PixelFormat::D24>, // 16
|
||||
MortonCopy<false, PixelFormat::D24S8> // 17
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
154
src/video_core/rasterizer_cache/pixel_format.cpp
Normal file
154
src/video_core/rasterizer_cache/pixel_format.cpp
Normal file
@ -0,0 +1,154 @@
|
||||
// Copyright 2023 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "video_core/rasterizer_cache/pixel_format.h"
|
||||
|
||||
namespace VideoCore {
|
||||
|
||||
std::string_view PixelFormatAsString(PixelFormat format) {
|
||||
switch (format) {
|
||||
case PixelFormat::RGBA8:
|
||||
return "RGBA8";
|
||||
case PixelFormat::RGB8:
|
||||
return "RGB8";
|
||||
case PixelFormat::RGB5A1:
|
||||
return "RGB5A1";
|
||||
case PixelFormat::RGB565:
|
||||
return "RGB565";
|
||||
case PixelFormat::RGBA4:
|
||||
return "RGBA4";
|
||||
case PixelFormat::IA8:
|
||||
return "IA8";
|
||||
case PixelFormat::RG8:
|
||||
return "RG8";
|
||||
case PixelFormat::I8:
|
||||
return "I8";
|
||||
case PixelFormat::A8:
|
||||
return "A8";
|
||||
case PixelFormat::IA4:
|
||||
return "IA4";
|
||||
case PixelFormat::I4:
|
||||
return "I4";
|
||||
case PixelFormat::A4:
|
||||
return "A4";
|
||||
case PixelFormat::ETC1:
|
||||
return "ETC1";
|
||||
case PixelFormat::ETC1A4:
|
||||
return "ETC1A4";
|
||||
case PixelFormat::D16:
|
||||
return "D16";
|
||||
case PixelFormat::D24:
|
||||
return "D24";
|
||||
case PixelFormat::D24S8:
|
||||
return "D24S8";
|
||||
default:
|
||||
return "NotReal";
|
||||
}
|
||||
}
|
||||
|
||||
bool CheckFormatsBlittable(PixelFormat source_format, PixelFormat dest_format) {
|
||||
SurfaceType source_type = GetFormatType(source_format);
|
||||
SurfaceType dest_type = GetFormatType(dest_format);
|
||||
|
||||
if ((source_type == SurfaceType::Color || source_type == SurfaceType::Texture) &&
|
||||
(dest_type == SurfaceType::Color || dest_type == SurfaceType::Texture)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (source_type == SurfaceType::Depth && dest_type == SurfaceType::Depth) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (source_type == SurfaceType::DepthStencil && dest_type == SurfaceType::DepthStencil) {
|
||||
return true;
|
||||
}
|
||||
|
||||
LOG_WARNING(HW_GPU, "Unblittable format pair detected {} and {}",
|
||||
PixelFormatAsString(source_format), PixelFormatAsString(dest_format));
|
||||
return false;
|
||||
}
|
||||
|
||||
PixelFormat PixelFormatFromTextureFormat(Pica::TexturingRegs::TextureFormat format) {
|
||||
switch (format) {
|
||||
case Pica::TexturingRegs::TextureFormat::RGBA8:
|
||||
return PixelFormat::RGBA8;
|
||||
case Pica::TexturingRegs::TextureFormat::RGB8:
|
||||
return PixelFormat::RGB8;
|
||||
case Pica::TexturingRegs::TextureFormat::RGB5A1:
|
||||
return PixelFormat::RGB5A1;
|
||||
case Pica::TexturingRegs::TextureFormat::RGB565:
|
||||
return PixelFormat::RGB565;
|
||||
case Pica::TexturingRegs::TextureFormat::RGBA4:
|
||||
return PixelFormat::RGBA4;
|
||||
case Pica::TexturingRegs::TextureFormat::IA8:
|
||||
return PixelFormat::IA8;
|
||||
case Pica::TexturingRegs::TextureFormat::RG8:
|
||||
return PixelFormat::RG8;
|
||||
case Pica::TexturingRegs::TextureFormat::I8:
|
||||
return PixelFormat::I8;
|
||||
case Pica::TexturingRegs::TextureFormat::A8:
|
||||
return PixelFormat::A8;
|
||||
case Pica::TexturingRegs::TextureFormat::IA4:
|
||||
return PixelFormat::IA4;
|
||||
case Pica::TexturingRegs::TextureFormat::I4:
|
||||
return PixelFormat::I4;
|
||||
case Pica::TexturingRegs::TextureFormat::A4:
|
||||
return PixelFormat::A4;
|
||||
case Pica::TexturingRegs::TextureFormat::ETC1:
|
||||
return PixelFormat::ETC1;
|
||||
case Pica::TexturingRegs::TextureFormat::ETC1A4:
|
||||
return PixelFormat::ETC1A4;
|
||||
default:
|
||||
return PixelFormat::Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
PixelFormat PixelFormatFromColorFormat(Pica::FramebufferRegs::ColorFormat format) {
|
||||
switch (format) {
|
||||
case Pica::FramebufferRegs::ColorFormat::RGBA8:
|
||||
return PixelFormat::RGBA8;
|
||||
case Pica::FramebufferRegs::ColorFormat::RGB8:
|
||||
return PixelFormat::RGB8;
|
||||
case Pica::FramebufferRegs::ColorFormat::RGB5A1:
|
||||
return PixelFormat::RGB5A1;
|
||||
case Pica::FramebufferRegs::ColorFormat::RGB565:
|
||||
return PixelFormat::RGB565;
|
||||
case Pica::FramebufferRegs::ColorFormat::RGBA4:
|
||||
return PixelFormat::RGBA4;
|
||||
default:
|
||||
return PixelFormat::Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
PixelFormat PixelFormatFromDepthFormat(Pica::FramebufferRegs::DepthFormat format) {
|
||||
switch (format) {
|
||||
case Pica::FramebufferRegs::DepthFormat::D16:
|
||||
return PixelFormat::D16;
|
||||
case Pica::FramebufferRegs::DepthFormat::D24:
|
||||
return PixelFormat::D24;
|
||||
case Pica::FramebufferRegs::DepthFormat::D24S8:
|
||||
return PixelFormat::D24S8;
|
||||
default:
|
||||
return PixelFormat::Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
PixelFormat PixelFormatFromGPUPixelFormat(GPU::Regs::PixelFormat format) {
|
||||
switch (format) {
|
||||
case GPU::Regs::PixelFormat::RGBA8:
|
||||
return PixelFormat::RGBA8;
|
||||
case GPU::Regs::PixelFormat::RGB8:
|
||||
return PixelFormat::RGB8;
|
||||
case GPU::Regs::PixelFormat::RGB565:
|
||||
return PixelFormat::RGB565;
|
||||
case GPU::Regs::PixelFormat::RGB5A1:
|
||||
return PixelFormat::RGB5A1;
|
||||
case GPU::Regs::PixelFormat::RGBA4:
|
||||
return PixelFormat::RGBA4;
|
||||
default:
|
||||
return PixelFormat::Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace VideoCore
|
@ -1,25 +1,23 @@
|
||||
// Copyright 2022 Citra Emulator Project
|
||||
// Copyright 2023 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <limits>
|
||||
#include <string_view>
|
||||
#include "core/hw/gpu.h"
|
||||
#include "video_core/regs_framebuffer.h"
|
||||
#include "video_core/regs_texturing.h"
|
||||
|
||||
namespace OpenGL {
|
||||
namespace VideoCore {
|
||||
|
||||
constexpr u32 PIXEL_FORMAT_COUNT = 18;
|
||||
|
||||
enum class PixelFormat : u8 {
|
||||
// First 5 formats are shared between textures and color buffers
|
||||
enum class PixelFormat : u32 {
|
||||
RGBA8 = 0,
|
||||
RGB8 = 1,
|
||||
RGB5A1 = 2,
|
||||
RGB565 = 3,
|
||||
RGBA4 = 4,
|
||||
// Texture-only formats
|
||||
IA8 = 5,
|
||||
RG8 = 6,
|
||||
I8 = 7,
|
||||
@ -29,168 +27,88 @@ enum class PixelFormat : u8 {
|
||||
A4 = 11,
|
||||
ETC1 = 12,
|
||||
ETC1A4 = 13,
|
||||
// Depth buffer-only formats
|
||||
D16 = 14,
|
||||
D24 = 16,
|
||||
D24S8 = 17,
|
||||
Invalid = 255,
|
||||
MaxPixelFormat = 18,
|
||||
Invalid = std::numeric_limits<u32>::max(),
|
||||
};
|
||||
constexpr std::size_t PIXEL_FORMAT_COUNT = static_cast<std::size_t>(PixelFormat::MaxPixelFormat);
|
||||
|
||||
enum class SurfaceType {
|
||||
enum class SurfaceType : u32 {
|
||||
Color = 0,
|
||||
Texture = 1,
|
||||
Depth = 2,
|
||||
DepthStencil = 3,
|
||||
Fill = 4,
|
||||
Invalid = 5
|
||||
Invalid = 5,
|
||||
};
|
||||
|
||||
constexpr std::string_view PixelFormatAsString(PixelFormat format) {
|
||||
switch (format) {
|
||||
case PixelFormat::RGBA8:
|
||||
return "RGBA8";
|
||||
case PixelFormat::RGB8:
|
||||
return "RGB8";
|
||||
case PixelFormat::RGB5A1:
|
||||
return "RGB5A1";
|
||||
case PixelFormat::RGB565:
|
||||
return "RGB565";
|
||||
case PixelFormat::RGBA4:
|
||||
return "RGBA4";
|
||||
case PixelFormat::IA8:
|
||||
return "IA8";
|
||||
case PixelFormat::RG8:
|
||||
return "RG8";
|
||||
case PixelFormat::I8:
|
||||
return "I8";
|
||||
case PixelFormat::A8:
|
||||
return "A8";
|
||||
case PixelFormat::IA4:
|
||||
return "IA4";
|
||||
case PixelFormat::I4:
|
||||
return "I4";
|
||||
case PixelFormat::A4:
|
||||
return "A4";
|
||||
case PixelFormat::ETC1:
|
||||
return "ETC1";
|
||||
case PixelFormat::ETC1A4:
|
||||
return "ETC1A4";
|
||||
case PixelFormat::D16:
|
||||
return "D16";
|
||||
case PixelFormat::D24:
|
||||
return "D24";
|
||||
case PixelFormat::D24S8:
|
||||
return "D24S8";
|
||||
default:
|
||||
return "NotReal";
|
||||
}
|
||||
}
|
||||
enum class TextureType : u32 {
|
||||
Texture2D = 0,
|
||||
CubeMap = 1,
|
||||
};
|
||||
|
||||
constexpr PixelFormat PixelFormatFromTextureFormat(Pica::TexturingRegs::TextureFormat format) {
|
||||
const u32 format_index = static_cast<u32>(format);
|
||||
return (format_index < 14) ? static_cast<PixelFormat>(format) : PixelFormat::Invalid;
|
||||
}
|
||||
struct PixelFormatInfo {
|
||||
SurfaceType type;
|
||||
u32 bits_per_block;
|
||||
u32 bytes_per_pixel;
|
||||
};
|
||||
|
||||
constexpr PixelFormat PixelFormatFromColorFormat(Pica::FramebufferRegs::ColorFormat format) {
|
||||
const u32 format_index = static_cast<u32>(format);
|
||||
return (format_index < 5) ? static_cast<PixelFormat>(format) : PixelFormat::Invalid;
|
||||
}
|
||||
|
||||
constexpr PixelFormat PixelFormatFromDepthFormat(Pica::FramebufferRegs::DepthFormat format) {
|
||||
const u32 format_index = static_cast<u32>(format);
|
||||
return (format_index < 4) ? static_cast<PixelFormat>(format_index + 14) : PixelFormat::Invalid;
|
||||
}
|
||||
|
||||
constexpr PixelFormat PixelFormatFromGPUPixelFormat(GPU::Regs::PixelFormat format) {
|
||||
const u32 format_index = static_cast<u32>(format);
|
||||
switch (format) {
|
||||
// RGB565 and RGB5A1 are switched in PixelFormat compared to ColorFormat
|
||||
case GPU::Regs::PixelFormat::RGB565:
|
||||
return PixelFormat::RGB565;
|
||||
case GPU::Regs::PixelFormat::RGB5A1:
|
||||
return PixelFormat::RGB5A1;
|
||||
default:
|
||||
return (format_index < 5) ? static_cast<PixelFormat>(format) : PixelFormat::Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr SurfaceType GetFormatType(PixelFormat pixel_format) {
|
||||
const u32 format_index = static_cast<u32>(pixel_format);
|
||||
if (format_index < 5) {
|
||||
return SurfaceType::Color;
|
||||
}
|
||||
|
||||
if (format_index < 14) {
|
||||
return SurfaceType::Texture;
|
||||
}
|
||||
|
||||
if (pixel_format == PixelFormat::D16 || pixel_format == PixelFormat::D24) {
|
||||
return SurfaceType::Depth;
|
||||
}
|
||||
|
||||
if (pixel_format == PixelFormat::D24S8) {
|
||||
return SurfaceType::DepthStencil;
|
||||
}
|
||||
|
||||
return SurfaceType::Invalid;
|
||||
}
|
||||
|
||||
constexpr bool CheckFormatsBlittable(PixelFormat source_format, PixelFormat dest_format) {
|
||||
SurfaceType source_type = GetFormatType(source_format);
|
||||
SurfaceType dest_type = GetFormatType(dest_format);
|
||||
|
||||
if ((source_type == SurfaceType::Color || source_type == SurfaceType::Texture) &&
|
||||
(dest_type == SurfaceType::Color || dest_type == SurfaceType::Texture)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (source_type == SurfaceType::Depth && dest_type == SurfaceType::Depth) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (source_type == SurfaceType::DepthStencil && dest_type == SurfaceType::DepthStencil) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Lookup table for querying pixel format properties (type, name, etc)
|
||||
* @note Modern GPUs require 4 byte alignment for D24
|
||||
* @note Texture formats are automatically converted to RGBA8
|
||||
**/
|
||||
constexpr std::array<PixelFormatInfo, PIXEL_FORMAT_COUNT> FORMAT_MAP = {{
|
||||
{SurfaceType::Color, 32, 4},
|
||||
{SurfaceType::Color, 24, 3},
|
||||
{SurfaceType::Color, 16, 2},
|
||||
{SurfaceType::Color, 16, 2},
|
||||
{SurfaceType::Color, 16, 2},
|
||||
{SurfaceType::Texture, 16, 4},
|
||||
{SurfaceType::Texture, 16, 4},
|
||||
{SurfaceType::Texture, 8, 4},
|
||||
{SurfaceType::Texture, 8, 4},
|
||||
{SurfaceType::Texture, 8, 4},
|
||||
{SurfaceType::Texture, 4, 4},
|
||||
{SurfaceType::Texture, 4, 4},
|
||||
{SurfaceType::Texture, 4, 4},
|
||||
{SurfaceType::Texture, 8, 4},
|
||||
{SurfaceType::Depth, 16, 2},
|
||||
{SurfaceType::Invalid, 0, 0},
|
||||
{SurfaceType::Depth, 24, 4},
|
||||
{SurfaceType::DepthStencil, 32, 4},
|
||||
}};
|
||||
|
||||
constexpr u32 GetFormatBpp(PixelFormat format) {
|
||||
switch (format) {
|
||||
case PixelFormat::RGBA8:
|
||||
case PixelFormat::D24S8:
|
||||
return 32;
|
||||
case PixelFormat::RGB8:
|
||||
case PixelFormat::D24:
|
||||
return 24;
|
||||
case PixelFormat::RGB5A1:
|
||||
case PixelFormat::RGB565:
|
||||
case PixelFormat::RGBA4:
|
||||
case PixelFormat::IA8:
|
||||
case PixelFormat::RG8:
|
||||
case PixelFormat::D16:
|
||||
return 16;
|
||||
case PixelFormat::I8:
|
||||
case PixelFormat::A8:
|
||||
case PixelFormat::IA4:
|
||||
case PixelFormat::ETC1A4:
|
||||
return 8;
|
||||
case PixelFormat::I4:
|
||||
case PixelFormat::A4:
|
||||
case PixelFormat::ETC1:
|
||||
return 4;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
const std::size_t index = static_cast<std::size_t>(format);
|
||||
ASSERT(index < FORMAT_MAP.size());
|
||||
return FORMAT_MAP[index].bits_per_block;
|
||||
}
|
||||
|
||||
constexpr u32 GetBytesPerPixel(PixelFormat format) {
|
||||
// OpenGL needs 4 bpp alignment for D24 since using GL_UNSIGNED_INT as type
|
||||
if (format == PixelFormat::D24 || GetFormatType(format) == SurfaceType::Texture) {
|
||||
return 4;
|
||||
}
|
||||
|
||||
return GetFormatBpp(format) / 8;
|
||||
constexpr u32 GetFormatBytesPerPixel(PixelFormat format) {
|
||||
const std::size_t index = static_cast<std::size_t>(format);
|
||||
ASSERT(index < FORMAT_MAP.size());
|
||||
return FORMAT_MAP[index].bytes_per_pixel;
|
||||
}
|
||||
|
||||
} // namespace OpenGL
|
||||
constexpr SurfaceType GetFormatType(PixelFormat format) {
|
||||
const std::size_t index = static_cast<std::size_t>(format);
|
||||
ASSERT(index < FORMAT_MAP.size());
|
||||
return FORMAT_MAP[index].type;
|
||||
}
|
||||
|
||||
std::string_view PixelFormatAsString(PixelFormat format);
|
||||
|
||||
bool CheckFormatsBlittable(PixelFormat source_format, PixelFormat dest_format);
|
||||
|
||||
PixelFormat PixelFormatFromTextureFormat(Pica::TexturingRegs::TextureFormat format);
|
||||
|
||||
PixelFormat PixelFormatFromColorFormat(Pica::FramebufferRegs::ColorFormat format);
|
||||
|
||||
PixelFormat PixelFormatFromDepthFormat(Pica::FramebufferRegs::DepthFormat format);
|
||||
|
||||
PixelFormat PixelFormatFromGPUPixelFormat(GPU::Regs::PixelFormat format);
|
||||
|
||||
} // namespace VideoCore
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -3,17 +3,24 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
#include "video_core/rasterizer_cache/cached_surface.h"
|
||||
#include "video_core/rasterizer_cache/rasterizer_cache_utils.h"
|
||||
#include <boost/icl/interval_map.hpp>
|
||||
#include <boost/icl/interval_set.hpp>
|
||||
#include "video_core/rasterizer_cache/surface_base.h"
|
||||
#include "video_core/rasterizer_cache/surface_params.h"
|
||||
#include "video_core/renderer_opengl/gl_texture_runtime.h"
|
||||
#include "video_core/texture/texture_decode.h"
|
||||
|
||||
namespace VideoCore {
|
||||
class RendererBase;
|
||||
namespace Memory {
|
||||
class MemorySystem;
|
||||
}
|
||||
|
||||
namespace OpenGL {
|
||||
namespace Pica {
|
||||
struct Regs;
|
||||
}
|
||||
|
||||
namespace VideoCore {
|
||||
|
||||
enum class ScaleMatch {
|
||||
Exact, // only accept same res scale
|
||||
@ -21,26 +28,60 @@ enum class ScaleMatch {
|
||||
Ignore // accept every scaled res
|
||||
};
|
||||
|
||||
class TextureDownloaderES;
|
||||
class TextureFilterer;
|
||||
class FormatReinterpreterOpenGL;
|
||||
class RendererBase;
|
||||
|
||||
class RasterizerCacheOpenGL : NonCopyable {
|
||||
class RasterizerCache : NonCopyable {
|
||||
public:
|
||||
RasterizerCacheOpenGL(VideoCore::RendererBase& renderer);
|
||||
~RasterizerCacheOpenGL();
|
||||
using SurfaceRef = std::shared_ptr<OpenGL::Surface>;
|
||||
|
||||
/// Blit one surface's texture to another
|
||||
bool BlitSurfaces(const Surface& src_surface, const Common::Rectangle<u32>& src_rect,
|
||||
const Surface& dst_surface, const Common::Rectangle<u32>& dst_rect);
|
||||
// Declare rasterizer interval types
|
||||
using SurfaceSet = std::set<SurfaceRef>;
|
||||
using SurfaceMap = boost::icl::interval_map<PAddr, SurfaceRef, boost::icl::partial_absorber,
|
||||
std::less, boost::icl::inplace_plus,
|
||||
boost::icl::inter_section, SurfaceInterval>;
|
||||
using SurfaceCache = boost::icl::interval_map<PAddr, SurfaceSet, boost::icl::partial_absorber,
|
||||
std::less, boost::icl::inplace_plus,
|
||||
boost::icl::inter_section, SurfaceInterval>;
|
||||
|
||||
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<SurfaceRef, Common::Rectangle<u32>>;
|
||||
using PageMap = boost::icl::interval_map<u32, int>;
|
||||
|
||||
struct RenderTargets {
|
||||
SurfaceRef color_surface;
|
||||
SurfaceRef depth_surface;
|
||||
};
|
||||
|
||||
struct TextureCube {
|
||||
SurfaceRef surface{};
|
||||
std::array<SurfaceRef, 6> faces{};
|
||||
std::array<u64, 6> ticks{};
|
||||
};
|
||||
|
||||
public:
|
||||
RasterizerCache(Memory::MemorySystem& memory, OpenGL::TextureRuntime& runtime, Pica::Regs& regs,
|
||||
RendererBase& renderer);
|
||||
~RasterizerCache();
|
||||
|
||||
/// Perform hardware accelerated texture copy according to the provided configuration
|
||||
bool AccelerateTextureCopy(const GPU::Regs::DisplayTransferConfig& config);
|
||||
|
||||
/// Perform hardware accelerated display transfer according to the provided configuration
|
||||
bool AccelerateDisplayTransfer(const GPU::Regs::DisplayTransferConfig& config);
|
||||
|
||||
/// Perform hardware accelerated memory fill according to the provided configuration
|
||||
bool AccelerateFill(const GPU::Regs::MemoryFillConfig& config);
|
||||
|
||||
/// Copy one surface's region to another
|
||||
void CopySurface(const Surface& src_surface, const Surface& dst_surface,
|
||||
void CopySurface(const SurfaceRef& src_surface, const SurfaceRef& 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);
|
||||
SurfaceRef 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)
|
||||
@ -48,27 +89,26 @@ public:
|
||||
bool load_if_create);
|
||||
|
||||
/// Get a surface based on the texture configuration
|
||||
Surface GetTextureSurface(const Pica::TexturingRegs::FullTextureConfig& config);
|
||||
Surface GetTextureSurface(const Pica::Texture::TextureInfo& info, u32 max_level = 0);
|
||||
SurfaceRef GetTextureSurface(const Pica::TexturingRegs::FullTextureConfig& config);
|
||||
SurfaceRef GetTextureSurface(const Pica::Texture::TextureInfo& info, u32 max_level = 0);
|
||||
|
||||
/// Get a texture cube based on the texture configuration
|
||||
const CachedTextureCube& GetTextureCube(const TextureCubeConfig& config);
|
||||
SurfaceRef GetTextureCube(const TextureCubeConfig& config);
|
||||
|
||||
/// Get the color and depth surfaces based on the framebuffer configuration
|
||||
SurfaceSurfaceRect_Tuple GetFramebufferSurfaces(bool using_color_fb, bool using_depth_fb,
|
||||
const Common::Rectangle<s32>& viewport_rect);
|
||||
OpenGL::Framebuffer GetFramebufferSurfaces(bool using_color_fb, bool using_depth_fb);
|
||||
|
||||
/// Get a surface that matches the fill config
|
||||
Surface GetFillSurface(const GPU::Regs::MemoryFillConfig& config);
|
||||
/// Marks the draw rectangle defined in framebuffer as invalid
|
||||
void InvalidateFramebuffer(const OpenGL::Framebuffer& framebuffer);
|
||||
|
||||
/// 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(PAddr addr, u32 size, Surface flush_surface = nullptr);
|
||||
void FlushRegion(PAddr addr, u32 size, SurfaceRef flush_surface = nullptr);
|
||||
|
||||
/// Mark region as being invalidated by region_owner (nullptr if 3DS memory)
|
||||
void InvalidateRegion(PAddr addr, u32 size, const Surface& region_owner);
|
||||
void InvalidateRegion(PAddr addr, u32 size, const SurfaceRef& region_owner);
|
||||
|
||||
/// Flush all cached resources tracked by this cache manager
|
||||
void FlushAll();
|
||||
@ -76,61 +116,59 @@ public:
|
||||
/// Clear all cached resources tracked by this cache manager
|
||||
void ClearAll(bool flush);
|
||||
|
||||
// Textures from destroyed surfaces are stored here to be recyled to reduce allocation overhead
|
||||
// in the driver
|
||||
// this must be placed above the surface_cache to ensure all cached surfaces are destroyed
|
||||
// before destroying the recycler
|
||||
std::unordered_multimap<HostTextureTag, OGLTexture> host_texture_recycler;
|
||||
|
||||
private:
|
||||
void DuplicateSurface(const Surface& src_surface, const Surface& dest_surface);
|
||||
/// Transfers ownership of a memory region from src_surface to dest_surface
|
||||
void DuplicateSurface(const SurfaceRef& src_surface, const SurfaceRef& dest_surface);
|
||||
|
||||
/// Update surface's texture for given region when necessary
|
||||
void ValidateSurface(const Surface& surface, PAddr addr, u32 size);
|
||||
void ValidateSurface(const SurfaceRef& surface, PAddr addr, u32 size);
|
||||
|
||||
// Returns false if there is a surface in the cache at the interval with the same bit-width,
|
||||
bool NoUnimplementedReinterpretations(const OpenGL::Surface& surface,
|
||||
OpenGL::SurfaceParams& params,
|
||||
const OpenGL::SurfaceInterval& interval);
|
||||
/// Copies pixel data in interval from the guest VRAM to the host GPU surface
|
||||
void UploadSurface(const SurfaceRef& surface, SurfaceInterval interval);
|
||||
|
||||
// Return true if a surface with an invalid pixel format exists at the interval
|
||||
bool IntervalHasInvalidPixelFormat(SurfaceParams& params, const SurfaceInterval& interval);
|
||||
/// Copies pixel data in interval from the host GPU surface to the guest VRAM
|
||||
void DownloadSurface(const SurfaceRef& surface, SurfaceInterval interval);
|
||||
|
||||
// Attempt to find a reinterpretable surface in the cache and use it to copy for validation
|
||||
bool ValidateByReinterpretation(const Surface& surface, SurfaceParams& params,
|
||||
/// Downloads a fill surface to guest VRAM
|
||||
void DownloadFillSurface(const SurfaceRef& surface, SurfaceInterval interval);
|
||||
|
||||
/// Returns false if there is a surface in the cache at the interval with the same bit-width,
|
||||
bool NoUnimplementedReinterpretations(const SurfaceRef& surface, SurfaceParams params,
|
||||
const SurfaceInterval& interval);
|
||||
|
||||
/// Return true if a surface with an invalid pixel format exists at the interval
|
||||
bool IntervalHasInvalidPixelFormat(const SurfaceParams& params,
|
||||
const SurfaceInterval& interval);
|
||||
|
||||
/// Attempt to find a reinterpretable surface in the cache and use it to copy for validation
|
||||
bool ValidateByReinterpretation(const SurfaceRef& surface, SurfaceParams params,
|
||||
const SurfaceInterval& interval);
|
||||
|
||||
/// Create a new surface
|
||||
Surface CreateSurface(const SurfaceParams& params);
|
||||
SurfaceRef CreateSurface(const SurfaceParams& params);
|
||||
|
||||
/// Register surface into the cache
|
||||
void RegisterSurface(const Surface& surface);
|
||||
void RegisterSurface(const SurfaceRef& surface);
|
||||
|
||||
/// Remove surface from the cache
|
||||
void UnregisterSurface(const Surface& surface);
|
||||
void UnregisterSurface(const SurfaceRef& surface);
|
||||
|
||||
/// Increase/decrease the number of surface in pages touching the specified region
|
||||
void UpdatePagesCachedCount(PAddr addr, u32 size, int delta);
|
||||
|
||||
VideoCore::RendererBase& renderer;
|
||||
TextureRuntime runtime;
|
||||
private:
|
||||
Memory::MemorySystem& memory;
|
||||
OpenGL::TextureRuntime& runtime;
|
||||
Pica::Regs& regs;
|
||||
RendererBase& renderer;
|
||||
SurfaceCache surface_cache;
|
||||
PageMap cached_pages;
|
||||
SurfaceMap dirty_regions;
|
||||
SurfaceSet remove_surfaces;
|
||||
|
||||
u16 resolution_scale_factor;
|
||||
|
||||
std::unordered_map<TextureCubeConfig, CachedTextureCube> texture_cube_cache;
|
||||
|
||||
std::recursive_mutex mutex;
|
||||
|
||||
public:
|
||||
OGLTexture AllocateSurfaceTexture(const FormatTuple& format_tuple, u32 width, u32 height);
|
||||
|
||||
std::unique_ptr<TextureFilterer> texture_filterer;
|
||||
std::unique_ptr<FormatReinterpreterOpenGL> format_reinterpreter;
|
||||
std::unique_ptr<TextureDownloaderES> texture_downloader_es;
|
||||
std::vector<SurfaceRef> remove_surfaces;
|
||||
u32 resolution_scale_factor;
|
||||
RenderTargets render_targets;
|
||||
std::unordered_map<TextureCubeConfig, TextureCube> texture_cube_cache;
|
||||
bool use_filter{};
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
||||
} // namespace VideoCore
|
||||
|
@ -1,38 +0,0 @@
|
||||
// Copyright 2022 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <tuple>
|
||||
#include <boost/icl/interval_map.hpp>
|
||||
#include <boost/icl/interval_set.hpp>
|
||||
#include "common/common_types.h"
|
||||
#include "common/math_util.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
class CachedSurface;
|
||||
using Surface = std::shared_ptr<CachedSurface>;
|
||||
|
||||
// Declare rasterizer interval types
|
||||
using SurfaceInterval = boost::icl::right_open_interval<PAddr>;
|
||||
using SurfaceSet = std::set<Surface>;
|
||||
using SurfaceRegions = boost::icl::interval_set<PAddr, std::less, SurfaceInterval>;
|
||||
using SurfaceMap =
|
||||
boost::icl::interval_map<PAddr, Surface, boost::icl::partial_absorber, std::less,
|
||||
boost::icl::inplace_plus, boost::icl::inter_section, SurfaceInterval>;
|
||||
using SurfaceCache =
|
||||
boost::icl::interval_map<PAddr, SurfaceSet, boost::icl::partial_absorber, std::less,
|
||||
boost::icl::inplace_plus, boost::icl::inter_section, SurfaceInterval>;
|
||||
|
||||
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, Common::Rectangle<u32>>;
|
||||
using SurfaceSurfaceRect_Tuple = std::tuple<Surface, Surface, Common::Rectangle<u32>>;
|
||||
using PageMap = boost::icl::interval_map<u32, int>;
|
||||
|
||||
} // namespace OpenGL
|
@ -1,56 +0,0 @@
|
||||
// Copyright 2022 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
#include <glad/glad.h>
|
||||
#include "video_core/rasterizer_cache/rasterizer_cache_utils.h"
|
||||
#include "video_core/renderer_opengl/gl_vars.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
constexpr FormatTuple tex_tuple = {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE};
|
||||
|
||||
static constexpr std::array<FormatTuple, 4> depth_format_tuples = {{
|
||||
{GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT}, // D16
|
||||
{},
|
||||
{GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT}, // D24
|
||||
{GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8}, // D24S8
|
||||
}};
|
||||
|
||||
static constexpr std::array<FormatTuple, 5> fb_format_tuples = {{
|
||||
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8}, // RGBA8
|
||||
{GL_RGB8, GL_BGR, GL_UNSIGNED_BYTE}, // RGB8
|
||||
{GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1}, // RGB5A1
|
||||
{GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5}, // RGB565
|
||||
{GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4}, // RGBA4
|
||||
}};
|
||||
|
||||
// Same as above, with minor changes for OpenGL ES. Replaced
|
||||
// GL_UNSIGNED_INT_8_8_8_8 with GL_UNSIGNED_BYTE and
|
||||
// GL_BGR with GL_RGB
|
||||
static constexpr std::array<FormatTuple, 5> fb_format_tuples_oes = {{
|
||||
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE}, // RGBA8
|
||||
{GL_RGB8, GL_RGB, GL_UNSIGNED_BYTE}, // RGB8
|
||||
{GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1}, // RGB5A1
|
||||
{GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5}, // RGB565
|
||||
{GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4}, // RGBA4
|
||||
}};
|
||||
|
||||
const FormatTuple& GetFormatTuple(PixelFormat pixel_format) {
|
||||
const SurfaceType type = GetFormatType(pixel_format);
|
||||
const std::size_t format_index = static_cast<std::size_t>(pixel_format);
|
||||
|
||||
if (type == SurfaceType::Color) {
|
||||
ASSERT(format_index < fb_format_tuples.size());
|
||||
return (GLES ? fb_format_tuples_oes : fb_format_tuples)[format_index];
|
||||
} else if (type == SurfaceType::Depth || type == SurfaceType::DepthStencil) {
|
||||
const std::size_t tuple_idx = format_index - 14;
|
||||
ASSERT(tuple_idx < depth_format_tuples.size());
|
||||
return depth_format_tuples[tuple_idx];
|
||||
}
|
||||
|
||||
return tex_tuple;
|
||||
}
|
||||
|
||||
} // namespace OpenGL
|
@ -1,73 +0,0 @@
|
||||
// Copyright 2022 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
#include <functional>
|
||||
#include "common/hash.h"
|
||||
#include "video_core/rasterizer_cache/pixel_format.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
struct FormatTuple {
|
||||
int internal_format;
|
||||
u32 format;
|
||||
u32 type;
|
||||
};
|
||||
|
||||
const FormatTuple& GetFormatTuple(PixelFormat pixel_format);
|
||||
|
||||
struct HostTextureTag {
|
||||
FormatTuple format_tuple{};
|
||||
u32 width = 0;
|
||||
u32 height = 0;
|
||||
|
||||
bool operator==(const HostTextureTag& rhs) const noexcept {
|
||||
return std::memcmp(this, &rhs, sizeof(HostTextureTag)) == 0;
|
||||
};
|
||||
|
||||
const u64 Hash() const {
|
||||
return Common::ComputeHash64(this, sizeof(HostTextureTag));
|
||||
}
|
||||
};
|
||||
|
||||
struct TextureCubeConfig {
|
||||
PAddr px;
|
||||
PAddr nx;
|
||||
PAddr py;
|
||||
PAddr ny;
|
||||
PAddr pz;
|
||||
PAddr nz;
|
||||
u32 width;
|
||||
Pica::TexturingRegs::TextureFormat format;
|
||||
|
||||
bool operator==(const TextureCubeConfig& rhs) const {
|
||||
return std::memcmp(this, &rhs, sizeof(TextureCubeConfig)) == 0;
|
||||
}
|
||||
|
||||
bool operator!=(const TextureCubeConfig& rhs) const {
|
||||
return std::memcmp(this, &rhs, sizeof(TextureCubeConfig)) != 0;
|
||||
}
|
||||
|
||||
const u64 Hash() const {
|
||||
return Common::ComputeHash64(this, sizeof(TextureCubeConfig));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
||||
|
||||
namespace std {
|
||||
template <>
|
||||
struct hash<OpenGL::HostTextureTag> {
|
||||
std::size_t operator()(const OpenGL::HostTextureTag& tag) const noexcept {
|
||||
return tag.Hash();
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<OpenGL::TextureCubeConfig> {
|
||||
std::size_t operator()(const OpenGL::TextureCubeConfig& config) const noexcept {
|
||||
return config.Hash();
|
||||
}
|
||||
};
|
||||
} // namespace std
|
156
src/video_core/rasterizer_cache/surface_base.cpp
Normal file
156
src/video_core/rasterizer_cache/surface_base.cpp
Normal file
@ -0,0 +1,156 @@
|
||||
// Copyright 2023 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "video_core/rasterizer_cache/surface_base.h"
|
||||
#include "video_core/texture/texture_decode.h"
|
||||
|
||||
namespace VideoCore {
|
||||
|
||||
SurfaceBase::SurfaceBase(const SurfaceParams& params) : SurfaceParams{params} {}
|
||||
|
||||
SurfaceBase::~SurfaceBase() = default;
|
||||
|
||||
bool SurfaceBase::CanFill(const SurfaceParams& dest_surface, SurfaceInterval fill_interval) const {
|
||||
if (type == SurfaceType::Fill && IsRegionValid(fill_interval) &&
|
||||
boost::icl::first(fill_interval) >= addr &&
|
||||
boost::icl::last_next(fill_interval) <= end && // dest_surface is within our fill range
|
||||
dest_surface.FromInterval(fill_interval).GetInterval() ==
|
||||
fill_interval) { // make sure interval is a rectangle in dest surface
|
||||
if (fill_size * 8 != dest_surface.GetFormatBpp()) {
|
||||
// Check if bits repeat for our fill_size
|
||||
const u32 dest_bytes_per_pixel = std::max(dest_surface.GetFormatBpp() / 8, 1u);
|
||||
std::vector<u8> fill_test(fill_size * dest_bytes_per_pixel);
|
||||
|
||||
for (u32 i = 0; i < dest_bytes_per_pixel; ++i) {
|
||||
std::memcpy(&fill_test[i * fill_size], &fill_data[0], fill_size);
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < fill_size; ++i) {
|
||||
if (std::memcmp(&fill_test[dest_bytes_per_pixel * i], &fill_test[0],
|
||||
dest_bytes_per_pixel) != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (dest_surface.GetFormatBpp() == 4 && (fill_test[0] & 0xF) != (fill_test[0] >> 4)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SurfaceBase::CanCopy(const SurfaceParams& dest_surface, SurfaceInterval copy_interval) const {
|
||||
SurfaceParams subrect_params = dest_surface.FromInterval(copy_interval);
|
||||
ASSERT(subrect_params.GetInterval() == copy_interval);
|
||||
if (CanSubRect(subrect_params))
|
||||
return true;
|
||||
|
||||
if (CanFill(dest_surface, copy_interval))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
SurfaceInterval SurfaceBase::GetCopyableInterval(const SurfaceParams& params) const {
|
||||
SurfaceInterval result{};
|
||||
const u32 tile_align = params.BytesInPixels(params.is_tiled ? 8 * 8 : 1);
|
||||
const auto valid_regions =
|
||||
SurfaceRegions{params.GetInterval() & GetInterval()} - invalid_regions;
|
||||
|
||||
for (auto& valid_interval : valid_regions) {
|
||||
const SurfaceInterval aligned_interval{
|
||||
params.addr +
|
||||
Common::AlignUp(boost::icl::first(valid_interval) - params.addr, tile_align),
|
||||
params.addr +
|
||||
Common::AlignDown(boost::icl::last_next(valid_interval) - params.addr, tile_align)};
|
||||
|
||||
if (tile_align > boost::icl::length(valid_interval) ||
|
||||
boost::icl::length(aligned_interval) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get the rectangle within aligned_interval
|
||||
const u32 stride_bytes = params.BytesInPixels(params.stride) * (params.is_tiled ? 8 : 1);
|
||||
SurfaceInterval rect_interval{
|
||||
params.addr +
|
||||
Common::AlignUp(boost::icl::first(aligned_interval) - params.addr, stride_bytes),
|
||||
params.addr + Common::AlignDown(boost::icl::last_next(aligned_interval) - params.addr,
|
||||
stride_bytes),
|
||||
};
|
||||
|
||||
if (boost::icl::first(rect_interval) > boost::icl::last_next(rect_interval)) {
|
||||
// 1 row
|
||||
rect_interval = aligned_interval;
|
||||
} else if (boost::icl::length(rect_interval) == 0) {
|
||||
// 2 rows that do not make a rectangle, return the larger one
|
||||
const SurfaceInterval row1{boost::icl::first(aligned_interval),
|
||||
boost::icl::first(rect_interval)};
|
||||
const SurfaceInterval row2{boost::icl::first(rect_interval),
|
||||
boost::icl::last_next(aligned_interval)};
|
||||
rect_interval = (boost::icl::length(row1) > boost::icl::length(row2)) ? row1 : row2;
|
||||
}
|
||||
|
||||
if (boost::icl::length(rect_interval) > boost::icl::length(result)) {
|
||||
result = rect_interval;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ClearValue SurfaceBase::MakeClearValue(PAddr copy_addr, PixelFormat dst_format) {
|
||||
const SurfaceType dst_type = GetFormatType(dst_format);
|
||||
const std::array fill_buffer = MakeFillBuffer(copy_addr);
|
||||
|
||||
ClearValue result{};
|
||||
switch (dst_type) {
|
||||
case SurfaceType::Color:
|
||||
case SurfaceType::Texture:
|
||||
case SurfaceType::Fill: {
|
||||
Pica::Texture::TextureInfo tex_info{};
|
||||
tex_info.format = static_cast<Pica::TexturingRegs::TextureFormat>(dst_format);
|
||||
const auto color = Pica::Texture::LookupTexture(fill_buffer.data(), 0, 0, tex_info);
|
||||
result.color = color / 255.f;
|
||||
break;
|
||||
}
|
||||
case SurfaceType::Depth: {
|
||||
u32 depth_uint = 0;
|
||||
if (dst_format == PixelFormat::D16) {
|
||||
std::memcpy(&depth_uint, fill_buffer.data(), 2);
|
||||
result.depth = depth_uint / 65535.0f; // 2^16 - 1
|
||||
} else if (dst_format == PixelFormat::D24) {
|
||||
std::memcpy(&depth_uint, fill_buffer.data(), 3);
|
||||
result.depth = depth_uint / 16777215.0f; // 2^24 - 1
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SurfaceType::DepthStencil: {
|
||||
u32 clear_value_uint;
|
||||
std::memcpy(&clear_value_uint, fill_buffer.data(), sizeof(u32));
|
||||
result.depth = (clear_value_uint & 0xFFFFFF) / 16777215.0f; // 2^24 - 1
|
||||
result.stencil = (clear_value_uint >> 24);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNREACHABLE_MSG("Invalid surface type!");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::array<u8, 4> SurfaceBase::MakeFillBuffer(PAddr copy_addr) {
|
||||
const PAddr fill_offset = (copy_addr - addr) % fill_size;
|
||||
std::array<u8, 4> fill_buffer;
|
||||
|
||||
u32 fill_buff_pos = fill_offset;
|
||||
for (std::size_t i = 0; i < fill_buffer.size(); i++) {
|
||||
fill_buffer[i] = fill_data[fill_buff_pos++ % fill_size];
|
||||
}
|
||||
|
||||
return fill_buffer;
|
||||
}
|
||||
|
||||
} // namespace VideoCore
|
66
src/video_core/rasterizer_cache/surface_base.h
Normal file
66
src/video_core/rasterizer_cache/surface_base.h
Normal file
@ -0,0 +1,66 @@
|
||||
// Copyright 2023 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <boost/icl/interval_set.hpp>
|
||||
#include "video_core/rasterizer_cache/surface_params.h"
|
||||
|
||||
namespace VideoCore {
|
||||
|
||||
using SurfaceRegions = boost::icl::interval_set<PAddr, std::less, SurfaceInterval>;
|
||||
|
||||
class SurfaceBase : public SurfaceParams {
|
||||
public:
|
||||
SurfaceBase(const SurfaceParams& params);
|
||||
~SurfaceBase();
|
||||
|
||||
/// Returns true when this surface can be used to fill the fill_interval of dest_surface
|
||||
bool CanFill(const SurfaceParams& dest_surface, SurfaceInterval fill_interval) const;
|
||||
|
||||
/// Returns true when surface can validate copy_interval of dest_surface
|
||||
bool CanCopy(const SurfaceParams& dest_surface, SurfaceInterval copy_interval) const;
|
||||
|
||||
/// Returns the region of the biggest valid rectange within interval
|
||||
SurfaceInterval GetCopyableInterval(const SurfaceParams& params) const;
|
||||
|
||||
/// Returns the clear value used to validate another surface from this fill surface
|
||||
ClearValue MakeClearValue(PAddr copy_addr, PixelFormat dst_format);
|
||||
|
||||
u64 ModificationTick() const noexcept {
|
||||
return modification_tick;
|
||||
}
|
||||
|
||||
bool IsRegionValid(SurfaceInterval interval) const {
|
||||
return (invalid_regions.find(interval) == invalid_regions.end());
|
||||
}
|
||||
|
||||
void MarkValid(SurfaceInterval interval) {
|
||||
invalid_regions.erase(interval);
|
||||
modification_tick++;
|
||||
}
|
||||
|
||||
void MarkInvalid(SurfaceInterval interval) {
|
||||
invalid_regions.insert(interval);
|
||||
modification_tick++;
|
||||
}
|
||||
|
||||
bool IsFullyInvalid() const {
|
||||
auto interval = GetInterval();
|
||||
return *invalid_regions.equal_range(interval).first == interval;
|
||||
}
|
||||
|
||||
private:
|
||||
/// Returns the fill buffer value starting from copy_addr
|
||||
std::array<u8, 4> MakeFillBuffer(PAddr copy_addr);
|
||||
|
||||
public:
|
||||
bool registered = false;
|
||||
SurfaceRegions invalid_regions;
|
||||
u32 fill_size = 0;
|
||||
std::array<u8, 4> fill_data;
|
||||
u64 modification_tick = 1;
|
||||
};
|
||||
|
||||
} // namespace VideoCore
|
@ -3,133 +3,9 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "video_core/rasterizer_cache/rasterizer_cache.h"
|
||||
#include "video_core/rasterizer_cache/surface_params.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
SurfaceParams SurfaceParams::FromInterval(SurfaceInterval interval) const {
|
||||
SurfaceParams params = *this;
|
||||
const u32 tiled_size = is_tiled ? 8 : 1;
|
||||
const u32 stride_tiled_bytes = BytesInPixels(stride * tiled_size);
|
||||
|
||||
PAddr aligned_start =
|
||||
addr + Common::AlignDown(boost::icl::first(interval) - addr, stride_tiled_bytes);
|
||||
PAddr aligned_end =
|
||||
addr + Common::AlignUp(boost::icl::last_next(interval) - addr, stride_tiled_bytes);
|
||||
|
||||
if (aligned_end - aligned_start > stride_tiled_bytes) {
|
||||
params.addr = aligned_start;
|
||||
params.height = (aligned_end - aligned_start) / BytesInPixels(stride);
|
||||
} else {
|
||||
// 1 row
|
||||
ASSERT(aligned_end - aligned_start == stride_tiled_bytes);
|
||||
const u32 tiled_alignment = BytesInPixels(is_tiled ? 8 * 8 : 1);
|
||||
|
||||
aligned_start =
|
||||
addr + Common::AlignDown(boost::icl::first(interval) - addr, tiled_alignment);
|
||||
aligned_end =
|
||||
addr + Common::AlignUp(boost::icl::last_next(interval) - addr, tiled_alignment);
|
||||
|
||||
params.addr = aligned_start;
|
||||
params.width = PixelsInBytes(aligned_end - aligned_start) / tiled_size;
|
||||
params.stride = params.width;
|
||||
params.height = tiled_size;
|
||||
}
|
||||
|
||||
params.UpdateParams();
|
||||
return params;
|
||||
}
|
||||
|
||||
SurfaceInterval SurfaceParams::GetSubRectInterval(Common::Rectangle<u32> unscaled_rect) const {
|
||||
if (unscaled_rect.GetHeight() == 0 || unscaled_rect.GetWidth() == 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (is_tiled) {
|
||||
unscaled_rect.left = Common::AlignDown(unscaled_rect.left, 8) * 8;
|
||||
unscaled_rect.bottom = Common::AlignDown(unscaled_rect.bottom, 8) / 8;
|
||||
unscaled_rect.right = Common::AlignUp(unscaled_rect.right, 8) * 8;
|
||||
unscaled_rect.top = Common::AlignUp(unscaled_rect.top, 8) / 8;
|
||||
}
|
||||
|
||||
const u32 stride_tiled = !is_tiled ? stride : stride * 8;
|
||||
|
||||
const u32 pixel_offset =
|
||||
stride_tiled * (!is_tiled ? unscaled_rect.bottom : (height / 8) - unscaled_rect.top) +
|
||||
unscaled_rect.left;
|
||||
|
||||
const u32 pixels = (unscaled_rect.GetHeight() - 1) * stride_tiled + unscaled_rect.GetWidth();
|
||||
|
||||
return {addr + BytesInPixels(pixel_offset), addr + BytesInPixels(pixel_offset + pixels)};
|
||||
}
|
||||
|
||||
SurfaceInterval SurfaceParams::GetCopyableInterval(const Surface& src_surface) const {
|
||||
SurfaceInterval result{};
|
||||
const auto valid_regions =
|
||||
SurfaceRegions(GetInterval() & src_surface->GetInterval()) - src_surface->invalid_regions;
|
||||
for (auto& valid_interval : valid_regions) {
|
||||
const SurfaceInterval aligned_interval{
|
||||
addr + Common::AlignUp(boost::icl::first(valid_interval) - addr,
|
||||
BytesInPixels(is_tiled ? 8 * 8 : 1)),
|
||||
addr + Common::AlignDown(boost::icl::last_next(valid_interval) - addr,
|
||||
BytesInPixels(is_tiled ? 8 * 8 : 1))};
|
||||
|
||||
if (BytesInPixels(is_tiled ? 8 * 8 : 1) > boost::icl::length(valid_interval) ||
|
||||
boost::icl::length(aligned_interval) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get the rectangle within aligned_interval
|
||||
const u32 stride_bytes = BytesInPixels(stride) * (is_tiled ? 8 : 1);
|
||||
SurfaceInterval rect_interval{
|
||||
addr + Common::AlignUp(boost::icl::first(aligned_interval) - addr, stride_bytes),
|
||||
addr + Common::AlignDown(boost::icl::last_next(aligned_interval) - addr, stride_bytes),
|
||||
};
|
||||
if (boost::icl::first(rect_interval) > boost::icl::last_next(rect_interval)) {
|
||||
// 1 row
|
||||
rect_interval = aligned_interval;
|
||||
} else if (boost::icl::length(rect_interval) == 0) {
|
||||
// 2 rows that do not make a rectangle, return the larger one
|
||||
const SurfaceInterval row1{boost::icl::first(aligned_interval),
|
||||
boost::icl::first(rect_interval)};
|
||||
const SurfaceInterval row2{boost::icl::first(rect_interval),
|
||||
boost::icl::last_next(aligned_interval)};
|
||||
rect_interval = (boost::icl::length(row1) > boost::icl::length(row2)) ? row1 : row2;
|
||||
}
|
||||
|
||||
if (boost::icl::length(rect_interval) > boost::icl::length(result)) {
|
||||
result = rect_interval;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Common::Rectangle<u32> SurfaceParams::GetSubRect(const SurfaceParams& sub_surface) const {
|
||||
const u32 begin_pixel_index = PixelsInBytes(sub_surface.addr - addr);
|
||||
|
||||
if (is_tiled) {
|
||||
const int x0 = (begin_pixel_index % (stride * 8)) / 8;
|
||||
const int y0 = (begin_pixel_index / (stride * 8)) * 8;
|
||||
// Top to bottom
|
||||
return Common::Rectangle<u32>(x0, height - y0, x0 + sub_surface.width,
|
||||
height - (y0 + sub_surface.height));
|
||||
}
|
||||
|
||||
const int x0 = begin_pixel_index % stride;
|
||||
const int y0 = begin_pixel_index / stride;
|
||||
// Bottom to top
|
||||
return Common::Rectangle<u32>(x0, y0 + sub_surface.height, x0 + sub_surface.width, y0);
|
||||
}
|
||||
|
||||
Common::Rectangle<u32> SurfaceParams::GetScaledSubRect(const SurfaceParams& sub_surface) const {
|
||||
auto rect = GetSubRect(sub_surface);
|
||||
rect.left = rect.left * res_scale;
|
||||
rect.right = rect.right * res_scale;
|
||||
rect.top = rect.top * res_scale;
|
||||
rect.bottom = rect.bottom * res_scale;
|
||||
return rect;
|
||||
}
|
||||
namespace VideoCore {
|
||||
|
||||
bool SurfaceParams::ExactMatch(const SurfaceParams& other_surface) const {
|
||||
return std::tie(other_surface.addr, other_surface.width, other_surface.height,
|
||||
@ -157,6 +33,7 @@ bool SurfaceParams::CanExpand(const SurfaceParams& expanded_surface) const {
|
||||
}
|
||||
|
||||
bool SurfaceParams::CanTexCopy(const SurfaceParams& texcopy_params) const {
|
||||
const SurfaceInterval copy_interval = texcopy_params.GetInterval();
|
||||
if (pixel_format == PixelFormat::Invalid || addr > texcopy_params.addr ||
|
||||
end < texcopy_params.end) {
|
||||
return false;
|
||||
@ -170,7 +47,180 @@ bool SurfaceParams::CanTexCopy(const SurfaceParams& texcopy_params) const {
|
||||
((texcopy_params.addr - addr) % tile_stride) + texcopy_params.width <= tile_stride;
|
||||
}
|
||||
|
||||
return FromInterval(texcopy_params.GetInterval()).GetInterval() == texcopy_params.GetInterval();
|
||||
const u32 target_level = LevelOf(texcopy_params.addr);
|
||||
if ((LevelInterval(target_level) & copy_interval) != copy_interval) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return FromInterval(copy_interval).GetInterval() == copy_interval;
|
||||
}
|
||||
|
||||
} // namespace OpenGL
|
||||
void SurfaceParams::UpdateParams() {
|
||||
if (stride == 0) {
|
||||
stride = width;
|
||||
}
|
||||
|
||||
type = GetFormatType(pixel_format);
|
||||
if (levels != 1) {
|
||||
ASSERT(stride == width);
|
||||
CalculateMipLevelOffsets();
|
||||
size = CalculateSurfaceSize();
|
||||
} else {
|
||||
mipmap_offsets[0] = addr;
|
||||
size = !is_tiled ? BytesInPixels(stride * (height - 1) + width)
|
||||
: BytesInPixels(stride * 8 * (height / 8 - 1) + width * 8);
|
||||
}
|
||||
|
||||
end = addr + size;
|
||||
}
|
||||
|
||||
Common::Rectangle<u32> SurfaceParams::GetSubRect(const SurfaceParams& sub_surface) const {
|
||||
const u32 level = LevelOf(sub_surface.addr);
|
||||
const u32 begin_pixel_index = PixelsInBytes(sub_surface.addr - mipmap_offsets[level]);
|
||||
ASSERT(stride == width || level == 0);
|
||||
|
||||
const u32 stride_lod = stride >> level;
|
||||
if (is_tiled) {
|
||||
const u32 x0 = (begin_pixel_index % (stride_lod * 8)) / 8;
|
||||
const u32 y0 = (begin_pixel_index / (stride_lod * 8)) * 8;
|
||||
const u32 height_lod = height >> level;
|
||||
// Top to bottom
|
||||
return {x0, height_lod - y0, x0 + sub_surface.width,
|
||||
height_lod - (y0 + sub_surface.height)};
|
||||
}
|
||||
|
||||
const u32 x0 = begin_pixel_index % stride_lod;
|
||||
const u32 y0 = begin_pixel_index / stride_lod;
|
||||
// Bottom to top
|
||||
return {x0, y0 + sub_surface.height, x0 + sub_surface.width, y0};
|
||||
}
|
||||
|
||||
Common::Rectangle<u32> SurfaceParams::GetScaledSubRect(const SurfaceParams& sub_surface) const {
|
||||
return GetSubRect(sub_surface) * res_scale;
|
||||
}
|
||||
|
||||
SurfaceParams SurfaceParams::FromInterval(SurfaceInterval interval) const {
|
||||
SurfaceParams params = *this;
|
||||
const u32 level = LevelOf(interval.lower());
|
||||
const PAddr end_addr = interval.upper();
|
||||
|
||||
// Ensure provided interval is contained in a single level
|
||||
ASSERT(level == LevelOf(end_addr) || end_addr == end || end_addr == mipmap_offsets[level + 1]);
|
||||
|
||||
params.width >>= level;
|
||||
params.stride >>= level;
|
||||
|
||||
const u32 tiled_size = is_tiled ? 8 : 1;
|
||||
const u32 stride_tiled_bytes = BytesInPixels(params.stride * tiled_size);
|
||||
ASSERT(stride == width || level == 0);
|
||||
|
||||
const PAddr start = mipmap_offsets[level];
|
||||
PAddr aligned_start =
|
||||
start + Common::AlignDown(boost::icl::first(interval) - start, stride_tiled_bytes);
|
||||
PAddr aligned_end =
|
||||
start + Common::AlignUp(boost::icl::last_next(interval) - start, stride_tiled_bytes);
|
||||
|
||||
if (aligned_end - aligned_start > stride_tiled_bytes) {
|
||||
params.addr = aligned_start;
|
||||
params.height = (aligned_end - aligned_start) / BytesInPixels(params.stride);
|
||||
} else {
|
||||
// 1 row
|
||||
ASSERT(aligned_end - aligned_start == stride_tiled_bytes);
|
||||
const u32 tiled_alignment = BytesInPixels(is_tiled ? 8 * 8 : 1);
|
||||
|
||||
aligned_start =
|
||||
start + Common::AlignDown(boost::icl::first(interval) - start, tiled_alignment);
|
||||
aligned_end =
|
||||
start + Common::AlignUp(boost::icl::last_next(interval) - start, tiled_alignment);
|
||||
|
||||
params.addr = aligned_start;
|
||||
params.width = PixelsInBytes(aligned_end - aligned_start) / tiled_size;
|
||||
params.stride = params.width;
|
||||
params.height = tiled_size;
|
||||
}
|
||||
|
||||
params.levels = 1;
|
||||
params.UpdateParams();
|
||||
return params;
|
||||
}
|
||||
|
||||
SurfaceInterval SurfaceParams::GetSubRectInterval(Common::Rectangle<u32> unscaled_rect,
|
||||
u32 level) const {
|
||||
if (unscaled_rect.GetHeight() == 0 || unscaled_rect.GetWidth() == 0) [[unlikely]] {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (is_tiled) {
|
||||
unscaled_rect.left = Common::AlignDown(unscaled_rect.left, 8) * 8;
|
||||
unscaled_rect.bottom = Common::AlignDown(unscaled_rect.bottom, 8) / 8;
|
||||
unscaled_rect.right = Common::AlignUp(unscaled_rect.right, 8) * 8;
|
||||
unscaled_rect.top = Common::AlignUp(unscaled_rect.top, 8) / 8;
|
||||
}
|
||||
|
||||
const u32 stride_tiled = (!is_tiled ? stride : stride * 8) >> level;
|
||||
const u32 pixels = (unscaled_rect.GetHeight() - 1) * stride_tiled + unscaled_rect.GetWidth();
|
||||
const u32 pixel_offset =
|
||||
stride_tiled * (!is_tiled ? unscaled_rect.bottom : (height / 8) - unscaled_rect.top) +
|
||||
unscaled_rect.left;
|
||||
|
||||
const PAddr start = mipmap_offsets[level];
|
||||
return {start + BytesInPixels(pixel_offset), start + BytesInPixels(pixel_offset + pixels)};
|
||||
}
|
||||
|
||||
void SurfaceParams::CalculateMipLevelOffsets() {
|
||||
ASSERT(levels <= MAX_PICA_LEVELS && stride == width);
|
||||
|
||||
u32 level_width = width;
|
||||
u32 level_height = height;
|
||||
u32 offset = addr;
|
||||
|
||||
for (u32 level = 0; level < levels; level++) {
|
||||
mipmap_offsets[level] = offset;
|
||||
offset += BytesInPixels(level_width * level_height);
|
||||
|
||||
level_width >>= 1;
|
||||
level_height >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
u32 SurfaceParams::CalculateSurfaceSize() const {
|
||||
ASSERT(levels <= MAX_PICA_LEVELS && stride == width);
|
||||
|
||||
u32 level_width = width;
|
||||
u32 level_height = height;
|
||||
u32 size = 0;
|
||||
|
||||
for (u32 level = 0; level < levels; level++) {
|
||||
size += BytesInPixels(level_width * level_height);
|
||||
level_width >>= 1;
|
||||
level_height >>= 1;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
SurfaceInterval SurfaceParams::LevelInterval(u32 level) const {
|
||||
ASSERT(levels > level);
|
||||
const PAddr start_addr = mipmap_offsets[level];
|
||||
const PAddr end_addr = level == (levels - 1) ? end : mipmap_offsets[level + 1];
|
||||
return {start_addr, end_addr};
|
||||
}
|
||||
|
||||
u32 SurfaceParams::LevelOf(PAddr level_addr) const {
|
||||
ASSERT(level_addr >= addr && level_addr <= end);
|
||||
|
||||
u32 level = levels - 1;
|
||||
while (mipmap_offsets[level] > level_addr) {
|
||||
level--;
|
||||
}
|
||||
return level;
|
||||
}
|
||||
|
||||
std::string SurfaceParams::DebugName(bool scaled) const noexcept {
|
||||
const u32 scaled_width = scaled ? GetScaledWidth() : width;
|
||||
const u32 scaled_height = scaled ? GetScaledHeight() : height;
|
||||
return fmt::format("Surface: {}x{} {} {} levels from {:#x} to {:#x} ({})", scaled_width,
|
||||
scaled_height, PixelFormatAsString(pixel_format), levels, addr, end,
|
||||
scaled ? "scaled" : "unscaled");
|
||||
}
|
||||
|
||||
} // namespace VideoCore
|
||||
|
@ -4,75 +4,89 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <climits>
|
||||
#include "video_core/rasterizer_cache/pixel_format.h"
|
||||
#include "video_core/rasterizer_cache/rasterizer_cache_types.h"
|
||||
#include "video_core/rasterizer_cache/utils.h"
|
||||
|
||||
namespace OpenGL {
|
||||
namespace VideoCore {
|
||||
|
||||
constexpr std::size_t MAX_PICA_LEVELS = 8;
|
||||
|
||||
class SurfaceParams {
|
||||
public:
|
||||
// Surface match traits
|
||||
/// Returns true if other_surface matches exactly params
|
||||
bool ExactMatch(const SurfaceParams& other_surface) const;
|
||||
|
||||
/// Returns true if sub_surface is a subrect of params
|
||||
bool CanSubRect(const SurfaceParams& sub_surface) const;
|
||||
|
||||
/// Returns true if params can be expanded to match expanded_surface
|
||||
bool CanExpand(const SurfaceParams& expanded_surface) const;
|
||||
|
||||
/// Returns true if params can be used for texcopy
|
||||
bool CanTexCopy(const SurfaceParams& texcopy_params) const;
|
||||
|
||||
/// Updates remaining members from the already set addr, width, height and pixel_format
|
||||
void UpdateParams();
|
||||
|
||||
/// Returns the unscaled rectangle referenced by sub_surface
|
||||
Common::Rectangle<u32> GetSubRect(const SurfaceParams& sub_surface) const;
|
||||
|
||||
/// Returns the scaled rectangle referenced by sub_surface
|
||||
Common::Rectangle<u32> GetScaledSubRect(const SurfaceParams& sub_surface) const;
|
||||
|
||||
// Returns the outer rectangle containing "interval"
|
||||
/// Returns the outer rectangle containing interval
|
||||
SurfaceParams FromInterval(SurfaceInterval interval) const;
|
||||
SurfaceInterval GetSubRectInterval(Common::Rectangle<u32> unscaled_rect) const;
|
||||
|
||||
// Returns the region of the biggest valid rectange within interval
|
||||
SurfaceInterval GetCopyableInterval(const Surface& src_surface) const;
|
||||
/// Returns the address interval referenced by unscaled_rect
|
||||
SurfaceInterval GetSubRectInterval(Common::Rectangle<u32> unscaled_rect, u32 level = 0) const;
|
||||
|
||||
/// Updates remaining members from the already set addr, width, height and pixel_format
|
||||
void UpdateParams() {
|
||||
if (stride == 0) {
|
||||
stride = width;
|
||||
}
|
||||
/// Return the address interval of the provided level
|
||||
SurfaceInterval LevelInterval(u32 level) const;
|
||||
|
||||
type = GetFormatType(pixel_format);
|
||||
size = !is_tiled ? BytesInPixels(stride * (height - 1) + width)
|
||||
: BytesInPixels(stride * 8 * (height / 8 - 1) + width * 8);
|
||||
end = addr + size;
|
||||
/// Returns the level of the provided address
|
||||
u32 LevelOf(PAddr addr) const;
|
||||
|
||||
/// Returns a string identifier of the params object
|
||||
std::string DebugName(bool scaled) const noexcept;
|
||||
|
||||
[[nodiscard]] SurfaceInterval GetInterval() const noexcept {
|
||||
return SurfaceInterval{addr, end};
|
||||
}
|
||||
|
||||
SurfaceInterval GetInterval() const {
|
||||
return SurfaceInterval(addr, end);
|
||||
[[nodiscard]] u32 GetFormatBpp() const noexcept {
|
||||
return VideoCore::GetFormatBpp(pixel_format);
|
||||
}
|
||||
|
||||
u32 GetFormatBpp() const {
|
||||
return OpenGL::GetFormatBpp(pixel_format);
|
||||
}
|
||||
|
||||
u32 GetScaledWidth() const {
|
||||
[[nodiscard]] u32 GetScaledWidth() const noexcept {
|
||||
return width * res_scale;
|
||||
}
|
||||
|
||||
u32 GetScaledHeight() const {
|
||||
[[nodiscard]] u32 GetScaledHeight() const noexcept {
|
||||
return height * res_scale;
|
||||
}
|
||||
|
||||
Common::Rectangle<u32> GetRect() const {
|
||||
[[nodiscard]] Common::Rectangle<u32> GetRect() const noexcept {
|
||||
return {0, height, width, 0};
|
||||
}
|
||||
|
||||
Common::Rectangle<u32> GetScaledRect() const {
|
||||
[[nodiscard]] Common::Rectangle<u32> GetScaledRect() const noexcept {
|
||||
return {0, GetScaledHeight(), GetScaledWidth(), 0};
|
||||
}
|
||||
|
||||
u32 PixelsInBytes(u32 size) const {
|
||||
[[nodiscard]] u32 PixelsInBytes(u32 size) const noexcept {
|
||||
return size * 8 / GetFormatBpp();
|
||||
}
|
||||
|
||||
u32 BytesInPixels(u32 pixels) const {
|
||||
[[nodiscard]] u32 BytesInPixels(u32 pixels) const noexcept {
|
||||
return pixels * GetFormatBpp() / 8;
|
||||
}
|
||||
|
||||
private:
|
||||
/// Computes the offset of each mipmap level
|
||||
void CalculateMipLevelOffsets();
|
||||
|
||||
/// Calculates total surface size taking mipmaps into account
|
||||
u32 CalculateSurfaceSize() const;
|
||||
|
||||
public:
|
||||
PAddr addr = 0;
|
||||
PAddr end = 0;
|
||||
@ -81,11 +95,15 @@ public:
|
||||
u32 width = 0;
|
||||
u32 height = 0;
|
||||
u32 stride = 0;
|
||||
u16 res_scale = 1;
|
||||
u32 levels = 1;
|
||||
u32 res_scale = 1;
|
||||
|
||||
bool is_tiled = false;
|
||||
TextureType texture_type = TextureType::Texture2D;
|
||||
PixelFormat pixel_format = PixelFormat::Invalid;
|
||||
SurfaceType type = SurfaceType::Invalid;
|
||||
|
||||
std::array<u32, MAX_PICA_LEVELS> mipmap_offsets{};
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
||||
} // namespace VideoCore
|
||||
|
548
src/video_core/rasterizer_cache/texture_codec.h
Normal file
548
src/video_core/rasterizer_cache/texture_codec.h
Normal file
@ -0,0 +1,548 @@
|
||||
// Copyright 2022 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
#include <algorithm>
|
||||
#include <bit>
|
||||
#include <span>
|
||||
#include "common/alignment.h"
|
||||
#include "common/color.h"
|
||||
#include "video_core/rasterizer_cache/pixel_format.h"
|
||||
#include "video_core/texture/etc1.h"
|
||||
#include "video_core/utils.h"
|
||||
|
||||
namespace VideoCore {
|
||||
|
||||
template <typename T>
|
||||
inline T MakeInt(const u8* bytes) {
|
||||
T integer{};
|
||||
std::memcpy(&integer, bytes, sizeof(T));
|
||||
|
||||
return integer;
|
||||
}
|
||||
|
||||
template <PixelFormat format, bool converted>
|
||||
constexpr void DecodePixel(const u8* source, u8* dest) {
|
||||
using namespace Common::Color;
|
||||
constexpr u32 bytes_per_pixel = GetFormatBpp(format) / 8;
|
||||
|
||||
if constexpr (format == PixelFormat::RGBA8 && converted) {
|
||||
const auto abgr = DecodeRGBA8(source);
|
||||
std::memcpy(dest, abgr.AsArray(), 4);
|
||||
} else if constexpr (format == PixelFormat::RGB8 && converted) {
|
||||
const auto abgr = DecodeRGB8(source);
|
||||
std::memcpy(dest, abgr.AsArray(), 4);
|
||||
} else if constexpr (format == PixelFormat::RGB565 && converted) {
|
||||
const auto abgr = DecodeRGB565(source);
|
||||
std::memcpy(dest, abgr.AsArray(), 4);
|
||||
} else if constexpr (format == PixelFormat::RGB5A1 && converted) {
|
||||
const auto abgr = DecodeRGB5A1(source);
|
||||
std::memcpy(dest, abgr.AsArray(), 4);
|
||||
} else if constexpr (format == PixelFormat::RGBA4 && converted) {
|
||||
const auto abgr = DecodeRGBA4(source);
|
||||
std::memcpy(dest, abgr.AsArray(), 4);
|
||||
} else if constexpr (format == PixelFormat::IA8) {
|
||||
const auto abgr = DecodeIA8(source);
|
||||
std::memcpy(dest, abgr.AsArray(), 4);
|
||||
} else if constexpr (format == PixelFormat::RG8) {
|
||||
const auto abgr = DecodeRG8(source);
|
||||
std::memcpy(dest, abgr.AsArray(), 4);
|
||||
} else if constexpr (format == PixelFormat::I8) {
|
||||
const auto abgr = DecodeI8(source);
|
||||
std::memcpy(dest, abgr.AsArray(), 4);
|
||||
} else if constexpr (format == PixelFormat::A8) {
|
||||
const auto abgr = DecodeA8(source);
|
||||
std::memcpy(dest, abgr.AsArray(), 4);
|
||||
} else if constexpr (format == PixelFormat::IA4) {
|
||||
const auto abgr = DecodeIA4(source);
|
||||
std::memcpy(dest, abgr.AsArray(), 4);
|
||||
} else if constexpr (format == PixelFormat::D24 && converted) {
|
||||
const auto d32 = DecodeD24(source) / 16777215.f;
|
||||
std::memcpy(dest, &d32, sizeof(d32));
|
||||
} else if constexpr (format == PixelFormat::D24S8) {
|
||||
const u32 d24s8 = std::rotl(MakeInt<u32>(source), 8);
|
||||
std::memcpy(dest, &d24s8, sizeof(u32));
|
||||
} else {
|
||||
std::memcpy(dest, source, bytes_per_pixel);
|
||||
}
|
||||
}
|
||||
|
||||
template <PixelFormat format>
|
||||
constexpr void DecodePixel4(u32 x, u32 y, const u8* source_tile, u8* dest_pixel) {
|
||||
const u32 morton_offset = VideoCore::MortonInterleave(x, y);
|
||||
const u8 value = source_tile[morton_offset >> 1];
|
||||
const u8 pixel = Common::Color::Convert4To8((morton_offset % 2) ? (value >> 4) : (value & 0xF));
|
||||
|
||||
if constexpr (format == PixelFormat::I4) {
|
||||
std::memset(dest_pixel, pixel, 3);
|
||||
dest_pixel[3] = 255;
|
||||
} else {
|
||||
std::memset(dest_pixel, 0, 3);
|
||||
dest_pixel[3] = pixel;
|
||||
}
|
||||
}
|
||||
|
||||
template <PixelFormat format>
|
||||
constexpr void DecodePixelETC1(u32 x, u32 y, const u8* source_tile, u8* dest_pixel) {
|
||||
constexpr u32 subtile_width = 4;
|
||||
constexpr u32 subtile_height = 4;
|
||||
constexpr bool has_alpha = format == PixelFormat::ETC1A4;
|
||||
constexpr std::size_t subtile_size = has_alpha ? 16 : 8;
|
||||
|
||||
const u32 subtile_index = (x / subtile_width) + 2 * (y / subtile_height);
|
||||
x %= subtile_width;
|
||||
y %= subtile_height;
|
||||
|
||||
const u8* subtile_ptr = source_tile + subtile_index * subtile_size;
|
||||
|
||||
u8 alpha = 255;
|
||||
if constexpr (has_alpha) {
|
||||
u64_le packed_alpha;
|
||||
std::memcpy(&packed_alpha, subtile_ptr, sizeof(u64));
|
||||
subtile_ptr += sizeof(u64);
|
||||
|
||||
alpha = Common::Color::Convert4To8((packed_alpha >> (4 * (x * subtile_width + y))) & 0xF);
|
||||
}
|
||||
|
||||
const u64_le subtile_data = MakeInt<u64_le>(subtile_ptr);
|
||||
const auto rgb = Pica::Texture::SampleETC1Subtile(subtile_data, x, y);
|
||||
|
||||
// Copy the uncompressed pixel to the destination
|
||||
std::memcpy(dest_pixel, rgb.AsArray(), 3);
|
||||
dest_pixel[3] = alpha;
|
||||
}
|
||||
|
||||
template <PixelFormat format, bool converted>
|
||||
constexpr void EncodePixel(const u8* source, u8* dest) {
|
||||
using namespace Common::Color;
|
||||
constexpr u32 bytes_per_pixel = GetFormatBpp(format) / 8;
|
||||
|
||||
if constexpr (format == PixelFormat::RGBA8 && converted) {
|
||||
Common::Vec4<u8> rgba;
|
||||
std::memcpy(rgba.AsArray(), source, 4);
|
||||
EncodeRGBA8(rgba, dest);
|
||||
} else if constexpr (format == PixelFormat::RGB8 && converted) {
|
||||
Common::Vec4<u8> rgba;
|
||||
std::memcpy(rgba.AsArray(), source, 4);
|
||||
EncodeRGB8(rgba, dest);
|
||||
} else if constexpr (format == PixelFormat::RGB565 && converted) {
|
||||
Common::Vec4<u8> rgba;
|
||||
std::memcpy(rgba.AsArray(), source, 4);
|
||||
EncodeRGB565(rgba, dest);
|
||||
} else if constexpr (format == PixelFormat::RGB5A1 && converted) {
|
||||
Common::Vec4<u8> rgba;
|
||||
std::memcpy(rgba.AsArray(), source, 4);
|
||||
EncodeRGB5A1(rgba, dest);
|
||||
} else if constexpr (format == PixelFormat::RGBA4 && converted) {
|
||||
Common::Vec4<u8> rgba;
|
||||
std::memcpy(rgba.AsArray(), source, 4);
|
||||
EncodeRGBA4(rgba, dest);
|
||||
} else if constexpr (format == PixelFormat::IA8) {
|
||||
Common::Vec4<u8> rgba;
|
||||
std::memcpy(rgba.AsArray(), source, 4);
|
||||
EncodeIA8(rgba, dest);
|
||||
} else if constexpr (format == PixelFormat::RG8) {
|
||||
Common::Vec4<u8> rgba;
|
||||
std::memcpy(rgba.AsArray(), source, 4);
|
||||
EncodeRG8(rgba, dest);
|
||||
} else if constexpr (format == PixelFormat::I8) {
|
||||
Common::Vec4<u8> rgba;
|
||||
std::memcpy(rgba.AsArray(), source, 4);
|
||||
EncodeI8(rgba, dest);
|
||||
} else if constexpr (format == PixelFormat::A8) {
|
||||
Common::Vec4<u8> rgba;
|
||||
std::memcpy(rgba.AsArray(), source, 4);
|
||||
EncodeA8(rgba, dest);
|
||||
} else if constexpr (format == PixelFormat::IA4) {
|
||||
Common::Vec4<u8> rgba;
|
||||
std::memcpy(rgba.AsArray(), source, 4);
|
||||
EncodeIA4(rgba, dest);
|
||||
} else if constexpr (format == PixelFormat::D24 && converted) {
|
||||
float d32;
|
||||
std::memcpy(&d32, source, sizeof(d32));
|
||||
EncodeD24(d32 * 0xFFFFFF, dest);
|
||||
} else if constexpr (format == PixelFormat::D24S8) {
|
||||
const u32 s8d24 = std::rotr(MakeInt<u32>(source), 8);
|
||||
std::memcpy(dest, &s8d24, sizeof(u32));
|
||||
} else {
|
||||
std::memcpy(dest, source, bytes_per_pixel);
|
||||
}
|
||||
}
|
||||
|
||||
template <PixelFormat format>
|
||||
constexpr void EncodePixel4(u32 x, u32 y, const u8* source_pixel, u8* dest_tile_buffer) {
|
||||
Common::Vec4<u8> rgba;
|
||||
std::memcpy(rgba.AsArray(), source_pixel, 4);
|
||||
|
||||
u8 pixel;
|
||||
if constexpr (format == PixelFormat::I4) {
|
||||
pixel = Common::Color::AverageRgbComponents(rgba);
|
||||
} else {
|
||||
pixel = rgba.a();
|
||||
}
|
||||
|
||||
const u32 morton_offset = VideoCore::MortonInterleave(x, y);
|
||||
const u32 byte_offset = morton_offset >> 1;
|
||||
|
||||
const u8 current_values = dest_tile_buffer[byte_offset];
|
||||
const u8 new_value = Common::Color::Convert8To4(pixel);
|
||||
|
||||
if (morton_offset % 2) {
|
||||
dest_tile_buffer[byte_offset] = (new_value << 4) | (current_values & 0x0F);
|
||||
} else {
|
||||
dest_tile_buffer[byte_offset] = (current_values & 0xF0) | new_value;
|
||||
}
|
||||
}
|
||||
|
||||
template <bool morton_to_linear, PixelFormat format, bool converted>
|
||||
constexpr void MortonCopyTile(u32 stride, std::span<u8> tile_buffer, std::span<u8> linear_buffer) {
|
||||
constexpr u32 bytes_per_pixel = GetFormatBpp(format) / 8;
|
||||
constexpr u32 linear_bytes_per_pixel = converted ? 4 : GetFormatBytesPerPixel(format);
|
||||
constexpr bool is_compressed = format == PixelFormat::ETC1 || format == PixelFormat::ETC1A4;
|
||||
constexpr bool is_4bit = format == PixelFormat::I4 || format == PixelFormat::A4;
|
||||
|
||||
for (u32 y = 0; y < 8; y++) {
|
||||
for (u32 x = 0; x < 8; x++) {
|
||||
const auto tiled_pixel = tile_buffer.subspan(
|
||||
VideoCore::MortonInterleave(x, y) * bytes_per_pixel, bytes_per_pixel);
|
||||
const auto linear_pixel = linear_buffer.subspan(
|
||||
((7 - y) * stride + x) * linear_bytes_per_pixel, linear_bytes_per_pixel);
|
||||
if constexpr (morton_to_linear) {
|
||||
if constexpr (is_compressed) {
|
||||
DecodePixelETC1<format>(x, y, tile_buffer.data(), linear_pixel.data());
|
||||
} else if constexpr (is_4bit) {
|
||||
DecodePixel4<format>(x, y, tile_buffer.data(), linear_pixel.data());
|
||||
} else {
|
||||
DecodePixel<format, converted>(tiled_pixel.data(), linear_pixel.data());
|
||||
}
|
||||
} else {
|
||||
if constexpr (is_4bit) {
|
||||
EncodePixel4<format>(x, y, linear_pixel.data(), tile_buffer.data());
|
||||
} else {
|
||||
EncodePixel<format, converted>(linear_pixel.data(), tiled_pixel.data());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Performs morton to/from linear convertions on the provided pixel data
|
||||
* @param converted If true performs RGBA8 to/from convertion to all color formats
|
||||
* @param width, height The dimentions of the rectangular region of pixels in linear_buffer
|
||||
* @param start_offset The number of bytes from the start of the first tile to the start of
|
||||
* tiled_buffer
|
||||
* @param end_offset The number of bytes from the start of the first tile to the end of tiled_buffer
|
||||
* @param linear_buffer The linear pixel data
|
||||
* @param tiled_buffer The tiled pixel data
|
||||
*
|
||||
* The MortonCopy is at the heart of the PICA texture implementation, as it's responsible for
|
||||
* converting between linear and morton tiled layouts. The function handles both convertions but
|
||||
* there are slightly different paths and inputs for each:
|
||||
*
|
||||
* Morton to Linear:
|
||||
* During uploads, tiled_buffer is always aligned to the tile or scanline boundary depending if the
|
||||
* linear rectangle spans multiple vertical tiles. linear_buffer does not reference the entire
|
||||
* texture area, but rather the specific rectangle affected by the upload.
|
||||
*
|
||||
* Linear to Morton:
|
||||
* This is similar to the other convertion but with some differences. In this case tiled_buffer is
|
||||
* not required to be aligned to any specific boundary which requires special care.
|
||||
* start_offset/end_offset are useful here as they tell us exactly where the data should be placed
|
||||
* in the linear_buffer.
|
||||
*/
|
||||
template <bool morton_to_linear, PixelFormat format, bool converted = false>
|
||||
static constexpr void MortonCopy(u32 width, u32 height, u32 start_offset, u32 end_offset,
|
||||
std::span<u8> linear_buffer, std::span<u8> tiled_buffer) {
|
||||
constexpr u32 bytes_per_pixel = GetFormatBpp(format) / 8;
|
||||
constexpr u32 aligned_bytes_per_pixel = converted ? 4 : GetFormatBytesPerPixel(format);
|
||||
constexpr u32 tile_size = GetFormatBpp(format) * 64 / 8;
|
||||
static_assert(aligned_bytes_per_pixel >= bytes_per_pixel, "");
|
||||
|
||||
const u32 linear_tile_stride = (7 * width + 8) * aligned_bytes_per_pixel;
|
||||
const u32 aligned_down_start_offset = Common::AlignDown(start_offset, tile_size);
|
||||
const u32 aligned_start_offset = Common::AlignUp(start_offset, tile_size);
|
||||
const u32 aligned_end_offset = Common::AlignDown(end_offset, tile_size);
|
||||
|
||||
ASSERT(!morton_to_linear ||
|
||||
(aligned_start_offset == start_offset && aligned_end_offset == end_offset));
|
||||
|
||||
// In OpenGL the texture origin is in the bottom left corner as opposed to other
|
||||
// APIs that have it at the top left. To avoid flipping texture coordinates in
|
||||
// the shader we read/write the linear buffer from the bottom up
|
||||
u32 linear_offset = ((height - 8) * width) * aligned_bytes_per_pixel;
|
||||
u32 tiled_offset = 0;
|
||||
u32 x = 0;
|
||||
u32 y = 0;
|
||||
|
||||
const auto LinearNextTile = [&] {
|
||||
x = (x + 8) % width;
|
||||
linear_offset += 8 * aligned_bytes_per_pixel;
|
||||
if (!x) {
|
||||
y = (y + 8) % height;
|
||||
if (!y) {
|
||||
return;
|
||||
}
|
||||
|
||||
linear_offset -= width * 9 * aligned_bytes_per_pixel;
|
||||
}
|
||||
};
|
||||
|
||||
// If during a texture download the start coordinate is not tile aligned, swizzle
|
||||
// the tile affected to a temporary buffer and copy the part we are interested in
|
||||
if (start_offset < aligned_start_offset && !morton_to_linear) {
|
||||
std::array<u8, tile_size> tmp_buf;
|
||||
auto linear_data = linear_buffer.subspan(linear_offset, linear_tile_stride);
|
||||
MortonCopyTile<morton_to_linear, format, converted>(width, tmp_buf, linear_data);
|
||||
|
||||
std::memcpy(tiled_buffer.data(), tmp_buf.data() + start_offset - aligned_down_start_offset,
|
||||
std::min(aligned_start_offset, end_offset) - start_offset);
|
||||
|
||||
tiled_offset += aligned_start_offset - start_offset;
|
||||
LinearNextTile();
|
||||
}
|
||||
|
||||
// If the copy spans multiple tiles, copy the fully aligned tiles in between.
|
||||
if (aligned_start_offset < aligned_end_offset) {
|
||||
const u32 buffer_end = tiled_offset + aligned_end_offset - aligned_start_offset;
|
||||
while (tiled_offset < buffer_end) {
|
||||
auto linear_data = linear_buffer.subspan(linear_offset, linear_tile_stride);
|
||||
auto tiled_data = tiled_buffer.subspan(tiled_offset, tile_size);
|
||||
MortonCopyTile<morton_to_linear, format, converted>(width, tiled_data, linear_data);
|
||||
tiled_offset += tile_size;
|
||||
LinearNextTile();
|
||||
}
|
||||
}
|
||||
|
||||
// If during a texture download the end coordinate is not tile aligned, swizzle
|
||||
// the tile affected to a temporary buffer and copy the part we are interested in
|
||||
if (end_offset > std::max(aligned_start_offset, aligned_end_offset) && !morton_to_linear) {
|
||||
std::array<u8, tile_size> tmp_buf;
|
||||
auto linear_data = linear_buffer.subspan(linear_offset, linear_tile_stride);
|
||||
MortonCopyTile<morton_to_linear, format, converted>(width, tmp_buf, linear_data);
|
||||
std::memcpy(tiled_buffer.data() + tiled_offset, tmp_buf.data(),
|
||||
end_offset - aligned_end_offset);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a linear copy, converting pixel formats if required.
|
||||
* @tparam decode If true, decodes the texture if needed. Otherwise, encodes if needed.
|
||||
* @tparam format Pixel format to copy.
|
||||
* @tparam converted If true, converts the texture to/from the appropriate format.
|
||||
* @param src_buffer The source pixel data
|
||||
* @param dst_buffer The destination pixel data
|
||||
* @return
|
||||
*/
|
||||
template <bool decode, PixelFormat format, bool converted = false>
|
||||
static constexpr void LinearCopy(std::span<u8> src_buffer, std::span<u8> dst_buffer) {
|
||||
const std::size_t src_size = src_buffer.size();
|
||||
const std::size_t dst_size = dst_buffer.size();
|
||||
|
||||
if constexpr (converted) {
|
||||
constexpr u32 encoded_bytes_per_pixel = GetFormatBpp(format) / 8;
|
||||
constexpr u32 decoded_bytes_per_pixel = 4;
|
||||
constexpr u32 src_bytes_per_pixel =
|
||||
decode ? encoded_bytes_per_pixel : decoded_bytes_per_pixel;
|
||||
constexpr u32 dst_bytes_per_pixel =
|
||||
decode ? decoded_bytes_per_pixel : encoded_bytes_per_pixel;
|
||||
|
||||
for (std::size_t src_index = 0, dst_index = 0; src_index < src_size && dst_index < dst_size;
|
||||
src_index += src_bytes_per_pixel, dst_index += dst_bytes_per_pixel) {
|
||||
const auto src_pixel = src_buffer.subspan(src_index, src_bytes_per_pixel);
|
||||
const auto dst_pixel = dst_buffer.subspan(dst_index, dst_bytes_per_pixel);
|
||||
if constexpr (decode) {
|
||||
DecodePixel<format, converted>(src_pixel.data(), dst_pixel.data());
|
||||
} else {
|
||||
EncodePixel<format, converted>(src_pixel.data(), dst_pixel.data());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
std::memcpy(dst_buffer.data(), src_buffer.data(), std::min(src_size, dst_size));
|
||||
}
|
||||
}
|
||||
|
||||
using MortonFunc = void (*)(u32, u32, u32, u32, std::span<u8>, std::span<u8>);
|
||||
|
||||
static constexpr std::array<MortonFunc, 18> UNSWIZZLE_TABLE = {
|
||||
MortonCopy<true, PixelFormat::RGBA8>, // 0
|
||||
MortonCopy<true, PixelFormat::RGB8>, // 1
|
||||
MortonCopy<true, PixelFormat::RGB5A1>, // 2
|
||||
MortonCopy<true, PixelFormat::RGB565>, // 3
|
||||
MortonCopy<true, PixelFormat::RGBA4>, // 4
|
||||
MortonCopy<true, PixelFormat::IA8>, // 5
|
||||
MortonCopy<true, PixelFormat::RG8>, // 6
|
||||
MortonCopy<true, PixelFormat::I8>, // 7
|
||||
MortonCopy<true, PixelFormat::A8>, // 8
|
||||
MortonCopy<true, PixelFormat::IA4>, // 9
|
||||
MortonCopy<true, PixelFormat::I4>, // 10
|
||||
MortonCopy<true, PixelFormat::A4>, // 11
|
||||
MortonCopy<true, PixelFormat::ETC1>, // 12
|
||||
MortonCopy<true, PixelFormat::ETC1A4>, // 13
|
||||
MortonCopy<true, PixelFormat::D16>, // 14
|
||||
nullptr, // 15
|
||||
MortonCopy<true, PixelFormat::D24>, // 16
|
||||
MortonCopy<true, PixelFormat::D24S8>, // 17
|
||||
};
|
||||
|
||||
static constexpr std::array<MortonFunc, 18> UNSWIZZLE_TABLE_CONVERTED = {
|
||||
MortonCopy<true, PixelFormat::RGBA8, true>, // 0
|
||||
MortonCopy<true, PixelFormat::RGB8, true>, // 1
|
||||
MortonCopy<true, PixelFormat::RGB5A1, true>, // 2
|
||||
MortonCopy<true, PixelFormat::RGB565, true>, // 3
|
||||
MortonCopy<true, PixelFormat::RGBA4, true>, // 4
|
||||
// The following formats are implicitly converted to RGBA regardless, so ignore them.
|
||||
nullptr, // 5
|
||||
nullptr, // 6
|
||||
nullptr, // 7
|
||||
nullptr, // 8
|
||||
nullptr, // 9
|
||||
nullptr, // 10
|
||||
nullptr, // 11
|
||||
nullptr, // 12
|
||||
nullptr, // 13
|
||||
MortonCopy<true, PixelFormat::D16, true>, // 14
|
||||
nullptr, // 15
|
||||
MortonCopy<true, PixelFormat::D24, true>, // 16
|
||||
// No conversion here as we need to do a special deinterleaving conversion elsewhere.
|
||||
nullptr, // 17
|
||||
};
|
||||
|
||||
static constexpr std::array<MortonFunc, 18> SWIZZLE_TABLE = {
|
||||
MortonCopy<false, PixelFormat::RGBA8>, // 0
|
||||
MortonCopy<false, PixelFormat::RGB8>, // 1
|
||||
MortonCopy<false, PixelFormat::RGB5A1>, // 2
|
||||
MortonCopy<false, PixelFormat::RGB565>, // 3
|
||||
MortonCopy<false, PixelFormat::RGBA4>, // 4
|
||||
MortonCopy<false, PixelFormat::IA8>, // 5
|
||||
MortonCopy<false, PixelFormat::RG8>, // 6
|
||||
MortonCopy<false, PixelFormat::I8>, // 7
|
||||
MortonCopy<false, PixelFormat::A8>, // 8
|
||||
MortonCopy<false, PixelFormat::IA4>, // 9
|
||||
MortonCopy<false, PixelFormat::I4>, // 10
|
||||
MortonCopy<false, PixelFormat::A4>, // 11
|
||||
nullptr, // 12
|
||||
nullptr, // 13
|
||||
MortonCopy<false, PixelFormat::D16>, // 14
|
||||
nullptr, // 15
|
||||
MortonCopy<false, PixelFormat::D24>, // 16
|
||||
MortonCopy<false, PixelFormat::D24S8>, // 17
|
||||
};
|
||||
|
||||
static constexpr std::array<MortonFunc, 18> SWIZZLE_TABLE_CONVERTED = {
|
||||
MortonCopy<false, PixelFormat::RGBA8, true>, // 0
|
||||
MortonCopy<false, PixelFormat::RGB8, true>, // 1
|
||||
MortonCopy<false, PixelFormat::RGB5A1, true>, // 2
|
||||
MortonCopy<false, PixelFormat::RGB565, true>, // 3
|
||||
MortonCopy<false, PixelFormat::RGBA4, true>, // 4
|
||||
// The following formats are implicitly converted from RGBA regardless, so ignore them.
|
||||
nullptr, // 5
|
||||
nullptr, // 6
|
||||
nullptr, // 7
|
||||
nullptr, // 8
|
||||
nullptr, // 9
|
||||
nullptr, // 10
|
||||
nullptr, // 11
|
||||
nullptr, // 12
|
||||
nullptr, // 13
|
||||
MortonCopy<false, PixelFormat::D16, true>, // 14
|
||||
nullptr, // 15
|
||||
MortonCopy<false, PixelFormat::D24, true>, // 16
|
||||
// No conversion here as we need to do a special interleaving conversion elsewhere.
|
||||
nullptr, // 17
|
||||
};
|
||||
|
||||
using LinearFunc = void (*)(std::span<u8>, std::span<u8>);
|
||||
|
||||
static constexpr std::array<LinearFunc, 18> LINEAR_DECODE_TABLE = {
|
||||
LinearCopy<true, PixelFormat::RGBA8>, // 0
|
||||
LinearCopy<true, PixelFormat::RGB8>, // 1
|
||||
LinearCopy<true, PixelFormat::RGB5A1>, // 2
|
||||
LinearCopy<true, PixelFormat::RGB565>, // 3
|
||||
LinearCopy<true, PixelFormat::RGBA4>, // 4
|
||||
// These formats cannot be used linearly and can be ignored.
|
||||
nullptr, // 5
|
||||
nullptr, // 6
|
||||
nullptr, // 7
|
||||
nullptr, // 8
|
||||
nullptr, // 9
|
||||
nullptr, // 10
|
||||
nullptr, // 11
|
||||
nullptr, // 12
|
||||
nullptr, // 13
|
||||
LinearCopy<true, PixelFormat::D16>, // 14
|
||||
nullptr, // 15
|
||||
LinearCopy<true, PixelFormat::D24>, // 16
|
||||
LinearCopy<true, PixelFormat::D24S8>, // 17
|
||||
};
|
||||
|
||||
static constexpr std::array<LinearFunc, 18> LINEAR_DECODE_TABLE_CONVERTED = {
|
||||
LinearCopy<true, PixelFormat::RGBA8, true>, // 0
|
||||
LinearCopy<true, PixelFormat::RGB8, true>, // 1
|
||||
LinearCopy<true, PixelFormat::RGB5A1, true>, // 2
|
||||
LinearCopy<true, PixelFormat::RGB565, true>, // 3
|
||||
LinearCopy<true, PixelFormat::RGBA4, true>, // 4
|
||||
// These formats cannot be used linearly and can be ignored.
|
||||
nullptr, // 5
|
||||
nullptr, // 6
|
||||
nullptr, // 7
|
||||
nullptr, // 8
|
||||
nullptr, // 9
|
||||
nullptr, // 10
|
||||
nullptr, // 11
|
||||
nullptr, // 12
|
||||
nullptr, // 13
|
||||
LinearCopy<true, PixelFormat::D16, true>, // 14
|
||||
nullptr, // 15
|
||||
LinearCopy<true, PixelFormat::D24, true>, // 16
|
||||
// No conversion here as we need to do a special deinterleaving conversion elsewhere.
|
||||
nullptr, // 17
|
||||
};
|
||||
|
||||
static constexpr std::array<LinearFunc, 18> LINEAR_ENCODE_TABLE = {
|
||||
LinearCopy<false, PixelFormat::RGBA8>, // 0
|
||||
LinearCopy<false, PixelFormat::RGB8>, // 1
|
||||
LinearCopy<false, PixelFormat::RGB5A1>, // 2
|
||||
LinearCopy<false, PixelFormat::RGB565>, // 3
|
||||
LinearCopy<false, PixelFormat::RGBA4>, // 4
|
||||
// These formats cannot be used linearly and can be ignored.
|
||||
nullptr, // 5
|
||||
nullptr, // 6
|
||||
nullptr, // 7
|
||||
nullptr, // 8
|
||||
nullptr, // 9
|
||||
nullptr, // 10
|
||||
nullptr, // 11
|
||||
nullptr, // 12
|
||||
nullptr, // 13
|
||||
LinearCopy<false, PixelFormat::D16>, // 14
|
||||
nullptr, // 15
|
||||
LinearCopy<false, PixelFormat::D24>, // 16
|
||||
LinearCopy<false, PixelFormat::D24S8>, // 17
|
||||
};
|
||||
|
||||
static constexpr std::array<LinearFunc, 18> LINEAR_ENCODE_TABLE_CONVERTED = {
|
||||
LinearCopy<false, PixelFormat::RGBA8, true>, // 0
|
||||
LinearCopy<false, PixelFormat::RGB8, true>, // 1
|
||||
LinearCopy<false, PixelFormat::RGB5A1, true>, // 2
|
||||
LinearCopy<false, PixelFormat::RGB565, true>, // 3
|
||||
LinearCopy<false, PixelFormat::RGBA4, true>, // 4
|
||||
// These formats cannot be used linearly and can be ignored.
|
||||
nullptr, // 5
|
||||
nullptr, // 6
|
||||
nullptr, // 7
|
||||
nullptr, // 8
|
||||
nullptr, // 9
|
||||
nullptr, // 10
|
||||
nullptr, // 11
|
||||
nullptr, // 12
|
||||
nullptr, // 13
|
||||
LinearCopy<false, PixelFormat::D16, true>, // 14
|
||||
nullptr, // 15
|
||||
LinearCopy<false, PixelFormat::D24, true>, // 16
|
||||
// No conversion here as we need to do a special interleaving conversion elsewhere.
|
||||
nullptr, // 17
|
||||
};
|
||||
|
||||
} // namespace VideoCore
|
@ -1,198 +0,0 @@
|
||||
// Copyright 2022 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/scope_exit.h"
|
||||
#include "video_core/rasterizer_cache/rasterizer_cache_utils.h"
|
||||
#include "video_core/rasterizer_cache/texture_runtime.h"
|
||||
#include "video_core/renderer_opengl/gl_state.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
GLbitfield ToBufferMask(Aspect aspect) {
|
||||
switch (aspect) {
|
||||
case Aspect::Color:
|
||||
return GL_COLOR_BUFFER_BIT;
|
||||
case Aspect::Depth:
|
||||
return GL_DEPTH_BUFFER_BIT;
|
||||
case Aspect::DepthStencil:
|
||||
return GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
|
||||
}
|
||||
}
|
||||
|
||||
TextureRuntime::TextureRuntime() {
|
||||
read_fbo.Create();
|
||||
draw_fbo.Create();
|
||||
}
|
||||
|
||||
void TextureRuntime::ReadTexture(const OGLTexture& tex, Subresource subresource,
|
||||
const FormatTuple& tuple, u8* pixels) {
|
||||
|
||||
OpenGLState prev_state = OpenGLState::GetCurState();
|
||||
SCOPE_EXIT({ prev_state.Apply(); });
|
||||
|
||||
OpenGLState state;
|
||||
state.ResetTexture(tex.handle);
|
||||
state.draw.read_framebuffer = read_fbo.handle;
|
||||
state.Apply();
|
||||
|
||||
const u32 level = subresource.level;
|
||||
switch (subresource.aspect) {
|
||||
case Aspect::Color:
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex.handle,
|
||||
level);
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0,
|
||||
0);
|
||||
break;
|
||||
case Aspect::Depth:
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, tex.handle,
|
||||
level);
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
|
||||
break;
|
||||
case Aspect::DepthStencil:
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
|
||||
tex.handle, level);
|
||||
break;
|
||||
}
|
||||
|
||||
const auto& rect = subresource.region;
|
||||
glReadPixels(rect.left, rect.bottom, rect.GetWidth(), rect.GetHeight(), tuple.format,
|
||||
tuple.type, pixels);
|
||||
}
|
||||
|
||||
bool TextureRuntime::ClearTexture(const OGLTexture& tex, Subresource subresource,
|
||||
ClearValue value) {
|
||||
OpenGLState prev_state = OpenGLState::GetCurState();
|
||||
SCOPE_EXIT({ prev_state.Apply(); });
|
||||
|
||||
// Setup scissor rectangle according to the clear rectangle
|
||||
const auto& clear_rect = subresource.region;
|
||||
OpenGLState state;
|
||||
state.scissor.enabled = true;
|
||||
state.scissor.x = clear_rect.left;
|
||||
state.scissor.y = clear_rect.bottom;
|
||||
state.scissor.width = clear_rect.GetWidth();
|
||||
state.scissor.height = clear_rect.GetHeight();
|
||||
state.draw.draw_framebuffer = draw_fbo.handle;
|
||||
state.Apply();
|
||||
|
||||
const u32 level = subresource.level;
|
||||
switch (subresource.aspect) {
|
||||
case Aspect::Color:
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex.handle,
|
||||
level);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0,
|
||||
0);
|
||||
|
||||
state.color_mask.red_enabled = true;
|
||||
state.color_mask.green_enabled = true;
|
||||
state.color_mask.blue_enabled = true;
|
||||
state.color_mask.alpha_enabled = true;
|
||||
state.Apply();
|
||||
|
||||
glClearBufferfv(GL_COLOR, 0, value.color.AsArray());
|
||||
break;
|
||||
case Aspect::Depth:
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, tex.handle,
|
||||
level);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
|
||||
|
||||
state.depth.write_mask = GL_TRUE;
|
||||
state.Apply();
|
||||
|
||||
glClearBufferfv(GL_DEPTH, 0, &value.depth);
|
||||
break;
|
||||
case Aspect::DepthStencil:
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
|
||||
tex.handle, level);
|
||||
|
||||
state.depth.write_mask = GL_TRUE;
|
||||
state.stencil.write_mask = -1;
|
||||
state.Apply();
|
||||
|
||||
glClearBufferfi(GL_DEPTH_STENCIL, 0, value.depth, value.stencil);
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TextureRuntime::CopyTextures(const OGLTexture& src_tex, Subresource src_subresource,
|
||||
const OGLTexture& dst_tex, Subresource dst_subresource) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TextureRuntime::BlitTextures(const OGLTexture& src_tex, Subresource src_subresource,
|
||||
const OGLTexture& dst_tex, Subresource dst_subresource,
|
||||
bool dst_cube) {
|
||||
OpenGLState prev_state = OpenGLState::GetCurState();
|
||||
SCOPE_EXIT({ prev_state.Apply(); });
|
||||
|
||||
OpenGLState state;
|
||||
state.draw.read_framebuffer = read_fbo.handle;
|
||||
state.draw.draw_framebuffer = draw_fbo.handle;
|
||||
state.Apply();
|
||||
|
||||
auto BindAttachment =
|
||||
[dst_cube, src_level = src_subresource.level, dst_level = dst_subresource.level,
|
||||
dst_layer = dst_subresource.layer](GLenum target, u32 src_tex, u32 dst_tex) -> void {
|
||||
GLenum dst_target = dst_cube ? GL_TEXTURE_CUBE_MAP_POSITIVE_X + dst_layer : GL_TEXTURE_2D;
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, target, GL_TEXTURE_2D, src_tex, src_level);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, target, dst_target, dst_tex, dst_level);
|
||||
};
|
||||
|
||||
// Sanity check; Can't blit a color texture to a depth buffer
|
||||
ASSERT(src_subresource.aspect == dst_subresource.aspect);
|
||||
switch (src_subresource.aspect) {
|
||||
case Aspect::Color:
|
||||
// Bind only color
|
||||
BindAttachment(GL_COLOR_ATTACHMENT0, src_tex.handle, dst_tex.handle);
|
||||
BindAttachment(GL_DEPTH_STENCIL_ATTACHMENT, 0, 0);
|
||||
break;
|
||||
case Aspect::Depth:
|
||||
// Bind only depth
|
||||
BindAttachment(GL_COLOR_ATTACHMENT0, 0, 0);
|
||||
BindAttachment(GL_DEPTH_ATTACHMENT, src_tex.handle, dst_tex.handle);
|
||||
BindAttachment(GL_STENCIL_ATTACHMENT, 0, 0);
|
||||
break;
|
||||
case Aspect::DepthStencil:
|
||||
// Bind to combined depth + stencil
|
||||
BindAttachment(GL_COLOR_ATTACHMENT0, 0, 0);
|
||||
BindAttachment(GL_DEPTH_STENCIL_ATTACHMENT, src_tex.handle, dst_tex.handle);
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO (wwylele): use GL_NEAREST for shadow map texture
|
||||
// Note: shadow map is treated as RGBA8 format in PICA, as well as in the rasterizer cache, but
|
||||
// doing linear intepolation componentwise would cause incorrect value. However, for a
|
||||
// well-programmed game this code path should be rarely executed for shadow map with
|
||||
// inconsistent scale.
|
||||
const GLenum filter = src_subresource.aspect == Aspect::Color ? GL_LINEAR : GL_NEAREST;
|
||||
const auto& src_rect = src_subresource.region;
|
||||
const auto& dst_rect = dst_subresource.region;
|
||||
glBlitFramebuffer(src_rect.left, src_rect.bottom, src_rect.right, src_rect.top, dst_rect.left,
|
||||
dst_rect.bottom, dst_rect.right, dst_rect.top,
|
||||
ToBufferMask(src_subresource.aspect), filter);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TextureRuntime::GenerateMipmaps(const OGLTexture& tex, u32 max_level) {
|
||||
OpenGLState prev_state = OpenGLState::GetCurState();
|
||||
SCOPE_EXIT({ prev_state.Apply(); });
|
||||
|
||||
OpenGLState state;
|
||||
state.texture_units[0].texture_2d = tex.handle;
|
||||
state.Apply();
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, max_level);
|
||||
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
}
|
||||
|
||||
} // namespace OpenGL
|
@ -1,70 +0,0 @@
|
||||
// Copyright 2022 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
#include "common/math_util.h"
|
||||
#include "common/vector_math.h"
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
// Describes the type of data a texture holds
|
||||
enum class Aspect { Color = 0, Depth = 1, DepthStencil = 2 };
|
||||
|
||||
// A union for both color and depth/stencil clear values
|
||||
union ClearValue {
|
||||
Common::Vec4f color;
|
||||
struct {
|
||||
float depth;
|
||||
u8 stencil;
|
||||
};
|
||||
};
|
||||
|
||||
struct Subresource {
|
||||
Subresource(Aspect aspect, Common::Rectangle<u32> region, u32 level = 0, u32 layer = 0)
|
||||
: aspect(aspect), region(region), level(level), layer(layer) {}
|
||||
|
||||
Aspect aspect;
|
||||
Common::Rectangle<u32> region;
|
||||
u32 level = 0;
|
||||
u32 layer = 0;
|
||||
};
|
||||
|
||||
struct FormatTuple;
|
||||
|
||||
/**
|
||||
* Provides texture manipulation functions to the rasterizer cache
|
||||
* Separating this into a class makes it easier to abstract graphics API code
|
||||
*/
|
||||
class TextureRuntime {
|
||||
public:
|
||||
TextureRuntime();
|
||||
~TextureRuntime() = default;
|
||||
|
||||
// Copies the GPU pixel data to the provided pixels buffer
|
||||
void ReadTexture(const OGLTexture& tex, Subresource subresource, const FormatTuple& tuple,
|
||||
u8* pixels);
|
||||
|
||||
// Fills the rectangle of the texture with the clear value provided
|
||||
bool ClearTexture(const OGLTexture& texture, Subresource subresource, ClearValue value);
|
||||
|
||||
// Copies a rectangle of src_tex to another rectange of dst_rect
|
||||
// NOTE: The width and height of the rectangles must be equal
|
||||
bool CopyTextures(const OGLTexture& src_tex, Subresource src_subresource,
|
||||
const OGLTexture& dst_tex, Subresource dst_subresource);
|
||||
|
||||
// Copies a rectangle of src_tex to another rectange of dst_rect performing
|
||||
// scaling and format conversions
|
||||
bool BlitTextures(const OGLTexture& src_tex, Subresource src_subresource,
|
||||
const OGLTexture& dst_tex, Subresource dst_subresource,
|
||||
bool dst_cube = false);
|
||||
|
||||
// Generates mipmaps for all the available levels of the texture
|
||||
void GenerateMipmaps(const OGLTexture& tex, u32 max_level);
|
||||
|
||||
private:
|
||||
OGLFramebuffer read_fbo, draw_fbo;
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
76
src/video_core/rasterizer_cache/utils.cpp
Normal file
76
src/video_core/rasterizer_cache/utils.cpp
Normal file
@ -0,0 +1,76 @@
|
||||
// Copyright 2023 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "video_core/rasterizer_cache/surface_params.h"
|
||||
#include "video_core/rasterizer_cache/texture_codec.h"
|
||||
#include "video_core/rasterizer_cache/utils.h"
|
||||
|
||||
namespace VideoCore {
|
||||
|
||||
u32 MipLevels(u32 width, u32 height, u32 max_level) {
|
||||
u32 levels = 1;
|
||||
while (width > 8 && height > 8) {
|
||||
levels++;
|
||||
width >>= 1;
|
||||
height >>= 1;
|
||||
}
|
||||
|
||||
return std::min(levels, max_level + 1);
|
||||
}
|
||||
|
||||
void EncodeTexture(const SurfaceParams& surface_info, PAddr start_addr, PAddr end_addr,
|
||||
std::span<u8> source, std::span<u8> dest, bool convert) {
|
||||
const PixelFormat format = surface_info.pixel_format;
|
||||
const u32 func_index = static_cast<u32>(format);
|
||||
|
||||
if (surface_info.is_tiled) {
|
||||
const MortonFunc SwizzleImpl =
|
||||
(convert ? SWIZZLE_TABLE_CONVERTED : SWIZZLE_TABLE)[func_index];
|
||||
if (SwizzleImpl) {
|
||||
SwizzleImpl(surface_info.width, surface_info.height, start_addr - surface_info.addr,
|
||||
end_addr - surface_info.addr, source, dest);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
const LinearFunc LinearEncodeImpl =
|
||||
(convert ? LINEAR_ENCODE_TABLE_CONVERTED : LINEAR_ENCODE_TABLE)[func_index];
|
||||
if (LinearEncodeImpl) {
|
||||
LinearEncodeImpl(source, dest);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
LOG_ERROR(HW_GPU, "Unimplemented texture encode function for pixel format = {}, tiled = {}",
|
||||
func_index, surface_info.is_tiled);
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
void DecodeTexture(const SurfaceParams& surface_info, PAddr start_addr, PAddr end_addr,
|
||||
std::span<u8> source, std::span<u8> dest, bool convert) {
|
||||
const PixelFormat format = surface_info.pixel_format;
|
||||
const u32 func_index = static_cast<u32>(format);
|
||||
|
||||
if (surface_info.is_tiled) {
|
||||
const MortonFunc UnswizzleImpl =
|
||||
(convert ? UNSWIZZLE_TABLE_CONVERTED : UNSWIZZLE_TABLE)[func_index];
|
||||
if (UnswizzleImpl) {
|
||||
UnswizzleImpl(surface_info.width, surface_info.height, start_addr - surface_info.addr,
|
||||
end_addr - surface_info.addr, dest, source);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
const LinearFunc LinearDecodeImpl =
|
||||
(convert ? LINEAR_DECODE_TABLE_CONVERTED : LINEAR_DECODE_TABLE)[func_index];
|
||||
if (LinearDecodeImpl) {
|
||||
LinearDecodeImpl(source, dest);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
LOG_ERROR(HW_GPU, "Unimplemented texture decode function for pixel format = {}, tiled = {}",
|
||||
func_index, surface_info.is_tiled);
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
} // namespace VideoCore
|
142
src/video_core/rasterizer_cache/utils.h
Normal file
142
src/video_core/rasterizer_cache/utils.h
Normal file
@ -0,0 +1,142 @@
|
||||
// Copyright 2023 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <compare>
|
||||
#include <span>
|
||||
#include <boost/icl/right_open_interval.hpp>
|
||||
#include "common/hash.h"
|
||||
#include "common/math_util.h"
|
||||
#include "common/vector_math.h"
|
||||
#include "video_core/rasterizer_cache/pixel_format.h"
|
||||
|
||||
namespace VideoCore {
|
||||
|
||||
using SurfaceInterval = boost::icl::right_open_interval<PAddr>;
|
||||
|
||||
struct Offset {
|
||||
constexpr auto operator<=>(const Offset&) const noexcept = default;
|
||||
|
||||
u32 x = 0;
|
||||
u32 y = 0;
|
||||
};
|
||||
|
||||
struct Extent {
|
||||
constexpr auto operator<=>(const Extent&) const noexcept = default;
|
||||
|
||||
u32 width = 1;
|
||||
u32 height = 1;
|
||||
};
|
||||
|
||||
union ClearValue {
|
||||
Common::Vec4f color;
|
||||
struct {
|
||||
float depth;
|
||||
u8 stencil;
|
||||
};
|
||||
};
|
||||
|
||||
struct TextureClear {
|
||||
u32 texture_level;
|
||||
Common::Rectangle<u32> texture_rect;
|
||||
ClearValue value;
|
||||
};
|
||||
|
||||
struct TextureCopy {
|
||||
u32 src_level;
|
||||
u32 dst_level;
|
||||
u32 src_layer;
|
||||
u32 dst_layer;
|
||||
Offset src_offset;
|
||||
Offset dst_offset;
|
||||
Extent extent;
|
||||
};
|
||||
|
||||
struct TextureBlit {
|
||||
u32 src_level;
|
||||
u32 dst_level;
|
||||
u32 src_layer;
|
||||
u32 dst_layer;
|
||||
Common::Rectangle<u32> src_rect;
|
||||
Common::Rectangle<u32> dst_rect;
|
||||
};
|
||||
|
||||
struct BufferTextureCopy {
|
||||
u32 buffer_offset;
|
||||
u32 buffer_size;
|
||||
Common::Rectangle<u32> texture_rect;
|
||||
u32 texture_level;
|
||||
};
|
||||
|
||||
struct StagingData {
|
||||
u32 size = 0;
|
||||
std::span<u8> mapped{};
|
||||
u64 buffer_offset = 0;
|
||||
};
|
||||
|
||||
struct TextureCubeConfig {
|
||||
PAddr px;
|
||||
PAddr nx;
|
||||
PAddr py;
|
||||
PAddr ny;
|
||||
PAddr pz;
|
||||
PAddr nz;
|
||||
u32 width;
|
||||
u32 levels;
|
||||
Pica::TexturingRegs::TextureFormat format;
|
||||
|
||||
bool operator==(const TextureCubeConfig& rhs) const {
|
||||
return std::memcmp(this, &rhs, sizeof(TextureCubeConfig)) == 0;
|
||||
}
|
||||
|
||||
bool operator!=(const TextureCubeConfig& rhs) const {
|
||||
return std::memcmp(this, &rhs, sizeof(TextureCubeConfig)) != 0;
|
||||
}
|
||||
|
||||
const u64 Hash() const {
|
||||
return Common::ComputeHash64(this, sizeof(TextureCubeConfig));
|
||||
}
|
||||
};
|
||||
|
||||
class SurfaceParams;
|
||||
|
||||
u32 MipLevels(u32 width, u32 height, u32 max_level);
|
||||
|
||||
/**
|
||||
* Encodes a linear texture to the expected linear or tiled format.
|
||||
*
|
||||
* @param surface_info Structure used to query the surface information.
|
||||
* @param start_addr The start address of the dest data. Used if tiled.
|
||||
* @param end_addr The end address of the dest data. Used if tiled.
|
||||
* @param source_tiled The source linear texture data.
|
||||
* @param dest_linear The output buffer where the encoded linear or tiled data will be written to.
|
||||
* @param convert Whether the pixel format needs to be converted.
|
||||
*/
|
||||
void EncodeTexture(const SurfaceParams& surface_info, PAddr start_addr, PAddr end_addr,
|
||||
std::span<u8> source, std::span<u8> dest, bool convert = false);
|
||||
|
||||
/**
|
||||
* Decodes a linear or tiled texture to the expected linear format.
|
||||
*
|
||||
* @param surface_info Structure used to query the surface information.
|
||||
* @param start_addr The start address of the source data. Used if tiled.
|
||||
* @param end_addr The end address of the source data. Used if tiled.
|
||||
* @param source_tiled The source linear or tiled texture data.
|
||||
* @param dest_linear The output buffer where the decoded linear data will be written to.
|
||||
* @param convert Whether the pixel format needs to be converted.
|
||||
*/
|
||||
void DecodeTexture(const SurfaceParams& surface_info, PAddr start_addr, PAddr end_addr,
|
||||
std::span<u8> source, std::span<u8> dest, bool convert = false);
|
||||
|
||||
} // namespace VideoCore
|
||||
|
||||
namespace std {
|
||||
template <>
|
||||
struct hash<VideoCore::TextureCubeConfig> {
|
||||
std::size_t operator()(const VideoCore::TextureCubeConfig& config) const noexcept {
|
||||
return config.Hash();
|
||||
}
|
||||
};
|
||||
} // namespace std
|
@ -281,6 +281,14 @@ struct FramebufferRegs {
|
||||
return 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsShadowRendering() const {
|
||||
return output_merger.fragment_operation_mode == FragmentOperationMode::Shadow;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool HasStencil() const {
|
||||
return framebuffer.depth_format == DepthFormat::D24S8;
|
||||
};
|
||||
|
||||
INSERT_PADDING_WORDS(0x10); // Gas related registers
|
||||
|
||||
union {
|
||||
|
@ -6,8 +6,8 @@
|
||||
|
||||
#include <array>
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/math_util.h"
|
||||
#include "common/vector_math.h"
|
||||
#include "video_core/pica_types.h"
|
||||
|
||||
namespace Pica {
|
||||
@ -41,6 +41,18 @@ struct RasterizerRegs {
|
||||
float24::FromRaw(clip_coef[2]), float24::FromRaw(clip_coef[3])};
|
||||
}
|
||||
|
||||
Common::Rectangle<s32> GetViewportRect() const {
|
||||
return {
|
||||
// These registers hold half-width and half-height, so must be multiplied by 2
|
||||
viewport_corner.x, // left
|
||||
viewport_corner.y + // top
|
||||
static_cast<s32>(float24::FromRaw(viewport_size_y).ToFloat32() * 2),
|
||||
viewport_corner.x + // right
|
||||
static_cast<s32>(float24::FromRaw(viewport_size_x).ToFloat32() * 2),
|
||||
viewport_corner.y // bottom
|
||||
};
|
||||
}
|
||||
|
||||
INSERT_PADDING_WORDS(0x1);
|
||||
|
||||
BitField<0, 24, u32> viewport_depth_range; // float24
|
||||
|
@ -99,29 +99,25 @@ struct TexturingRegs {
|
||||
ETC1A4 = 13, // compressed
|
||||
};
|
||||
|
||||
static unsigned NibblesPerPixel(TextureFormat format) {
|
||||
static u32 NibblesPerPixel(TextureFormat format) {
|
||||
switch (format) {
|
||||
case TextureFormat::RGBA8:
|
||||
return 8;
|
||||
|
||||
case TextureFormat::RGB8:
|
||||
return 6;
|
||||
|
||||
case TextureFormat::RGB5A1:
|
||||
case TextureFormat::RGB565:
|
||||
case TextureFormat::RGBA4:
|
||||
case TextureFormat::IA8:
|
||||
case TextureFormat::RG8:
|
||||
return 4;
|
||||
|
||||
case TextureFormat::I4:
|
||||
case TextureFormat::A4:
|
||||
return 1;
|
||||
|
||||
case TextureFormat::I8:
|
||||
case TextureFormat::A8:
|
||||
case TextureFormat::IA4:
|
||||
|
||||
return 2;
|
||||
default: // placeholder for yet unknown formats
|
||||
UNIMPLEMENTED();
|
||||
return 0;
|
||||
|
@ -1,10 +0,0 @@
|
||||
//? #version 320 es
|
||||
|
||||
out highp uint color;
|
||||
|
||||
uniform highp sampler2D depth;
|
||||
uniform int lod;
|
||||
|
||||
void main() {
|
||||
color = uint(texelFetch(depth, ivec2(gl_FragCoord.xy), lod).x * (exp2(32.0) - 1.0));
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
//? #version 320 es
|
||||
|
||||
const vec2 vertices[4] =
|
||||
vec2[4](vec2(-1.0, -1.0), vec2(1.0, -1.0), vec2(-1.0, 1.0), vec2(1.0, 1.0));
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(vertices[gl_VertexID], 0.0, 1.0);
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
//? #version 320 es
|
||||
#extension GL_ARM_shader_framebuffer_fetch_depth_stencil : enable
|
||||
|
||||
out highp uint color;
|
||||
|
||||
void main() {
|
||||
color = uint(gl_LastFragDepthARM * (exp2(24.0) - 1.0)) << 8;
|
||||
color |= uint(gl_LastFragStencilARM);
|
||||
}
|
218
src/video_core/renderer_opengl/gl_blit_helper.cpp
Normal file
218
src/video_core/renderer_opengl/gl_blit_helper.cpp
Normal file
@ -0,0 +1,218 @@
|
||||
// Copyright 2023 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/settings.h"
|
||||
#include "video_core/rasterizer_cache/pixel_format.h"
|
||||
#include "video_core/renderer_opengl/gl_blit_helper.h"
|
||||
#include "video_core/renderer_opengl/gl_state.h"
|
||||
#include "video_core/renderer_opengl/gl_texture_runtime.h"
|
||||
|
||||
#include "video_core/host_shaders/full_screen_triangle_vert.h"
|
||||
#include "video_core/host_shaders/texture_filtering/bicubic_frag.h"
|
||||
#include "video_core/host_shaders/texture_filtering/nearest_neighbor_frag.h"
|
||||
#include "video_core/host_shaders/texture_filtering/refine_frag.h"
|
||||
#include "video_core/host_shaders/texture_filtering/scale_force_frag.h"
|
||||
#include "video_core/host_shaders/texture_filtering/tex_coord_vert.h"
|
||||
#include "video_core/host_shaders/texture_filtering/x_gradient_frag.h"
|
||||
#include "video_core/host_shaders/texture_filtering/xbrz_freescale_frag.h"
|
||||
#include "video_core/host_shaders/texture_filtering/y_gradient_frag.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
using Settings::TextureFilter;
|
||||
using VideoCore::SurfaceType;
|
||||
|
||||
namespace {
|
||||
|
||||
struct TempTexture {
|
||||
OGLTexture tex;
|
||||
OGLFramebuffer fbo;
|
||||
};
|
||||
|
||||
OGLSampler CreateSampler(GLenum filter) {
|
||||
OGLSampler sampler;
|
||||
sampler.Create();
|
||||
glSamplerParameteri(sampler.handle, GL_TEXTURE_MIN_FILTER, filter);
|
||||
glSamplerParameteri(sampler.handle, GL_TEXTURE_MAG_FILTER, filter);
|
||||
glSamplerParameteri(sampler.handle, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glSamplerParameteri(sampler.handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
return sampler;
|
||||
}
|
||||
|
||||
OGLProgram CreateProgram(std::string_view frag) {
|
||||
OGLProgram program;
|
||||
program.Create(HostShaders::FULL_SCREEN_TRIANGLE_VERT, frag);
|
||||
glProgramUniform2f(program.handle, 0, 1.f, 1.f);
|
||||
glProgramUniform2f(program.handle, 1, 0.f, 0.f);
|
||||
return program;
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
BlitHelper::BlitHelper(TextureRuntime& runtime_)
|
||||
: runtime{runtime_}, linear_sampler{CreateSampler(GL_LINEAR)},
|
||||
nearest_sampler{CreateSampler(GL_NEAREST)}, bicubic_program{CreateProgram(
|
||||
HostShaders::BICUBIC_FRAG)},
|
||||
nearest_program{CreateProgram(HostShaders::NEAREST_NEIGHBOR_FRAG)},
|
||||
scale_force_program{CreateProgram(HostShaders::SCALE_FORCE_FRAG)},
|
||||
xbrz_program{CreateProgram(HostShaders::XBRZ_FREESCALE_FRAG)},
|
||||
gradient_x_program{CreateProgram(HostShaders::X_GRADIENT_FRAG)},
|
||||
gradient_y_program{CreateProgram(HostShaders::Y_GRADIENT_FRAG)},
|
||||
refine_program{CreateProgram(HostShaders::REFINE_FRAG)} {
|
||||
vao.Create();
|
||||
filter_fbo.Create();
|
||||
state.draw.vertex_array = vao.handle;
|
||||
for (u32 i = 0; i < 3; i++) {
|
||||
state.texture_units[i].sampler = i == 2 ? nearest_sampler.handle : linear_sampler.handle;
|
||||
}
|
||||
}
|
||||
|
||||
BlitHelper::~BlitHelper() = default;
|
||||
|
||||
bool BlitHelper::Filter(Surface& surface, const VideoCore::TextureBlit& blit) {
|
||||
// Filtering to depth stencil surfaces isn't supported.
|
||||
if (surface.type == SurfaceType::Depth || surface.type == SurfaceType::DepthStencil) {
|
||||
return false;
|
||||
}
|
||||
// Avoid filtering for mipmaps as the result often looks terrible.
|
||||
if (blit.src_level != 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const OpenGLState prev_state = OpenGLState::GetCurState();
|
||||
state.texture_units[0].texture_2d = surface.Handle(false);
|
||||
|
||||
const auto filter{Settings::values.texture_filter.GetValue()};
|
||||
switch (filter) {
|
||||
case TextureFilter::None:
|
||||
break;
|
||||
case TextureFilter::Anime4K:
|
||||
FilterAnime4K(surface, blit);
|
||||
break;
|
||||
case TextureFilter::Bicubic:
|
||||
FilterBicubic(surface, blit);
|
||||
break;
|
||||
case TextureFilter::NearestNeighbor:
|
||||
FilterNearest(surface, blit);
|
||||
break;
|
||||
case TextureFilter::ScaleForce:
|
||||
FilterScaleForce(surface, blit);
|
||||
break;
|
||||
case TextureFilter::xBRZ:
|
||||
FilterXbrz(surface, blit);
|
||||
break;
|
||||
}
|
||||
|
||||
prev_state.Apply();
|
||||
return true;
|
||||
}
|
||||
|
||||
void BlitHelper::FilterAnime4K(Surface& surface, const VideoCore::TextureBlit& blit) {
|
||||
static constexpr u8 internal_scale_factor = 2;
|
||||
|
||||
const auto& tuple = surface.Tuple();
|
||||
const u32 src_width = blit.src_rect.GetWidth();
|
||||
const u32 src_height = blit.src_rect.GetHeight();
|
||||
const auto temp_rect{blit.src_rect * internal_scale_factor};
|
||||
|
||||
const auto setup_temp_tex = [&](GLint internal_format, GLint format, u32 width, u32 height) {
|
||||
TempTexture texture;
|
||||
texture.fbo.Create();
|
||||
texture.tex.Create();
|
||||
state.texture_units[1].texture_2d = texture.tex.handle;
|
||||
state.draw.draw_framebuffer = texture.fbo.handle;
|
||||
state.Apply();
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glBindTexture(GL_TEXTURE_2D, texture.tex.handle);
|
||||
glTexStorage2D(GL_TEXTURE_2D, 1, internal_format, width, height);
|
||||
return texture;
|
||||
};
|
||||
|
||||
// Create intermediate textures
|
||||
auto SRC = setup_temp_tex(tuple.internal_format, tuple.format, src_width, src_height);
|
||||
auto XY = setup_temp_tex(GL_RG16F, GL_RG, temp_rect.GetWidth(), temp_rect.GetHeight());
|
||||
auto LUMAD = setup_temp_tex(GL_R16F, GL_RED, temp_rect.GetWidth(), temp_rect.GetHeight());
|
||||
|
||||
// Copy to SRC
|
||||
glCopyImageSubData(surface.Handle(false), GL_TEXTURE_2D, 0, blit.src_rect.left,
|
||||
blit.src_rect.bottom, 0, SRC.tex.handle, GL_TEXTURE_2D, 0, 0, 0, 0,
|
||||
src_width, src_height, 1);
|
||||
|
||||
state.texture_units[0].texture_2d = SRC.tex.handle;
|
||||
state.texture_units[1].texture_2d = LUMAD.tex.handle;
|
||||
state.texture_units[2].texture_2d = XY.tex.handle;
|
||||
|
||||
// gradient x pass
|
||||
Draw(gradient_x_program, XY.tex.handle, XY.fbo.handle, 0, temp_rect);
|
||||
|
||||
// gradient y pass
|
||||
Draw(gradient_y_program, LUMAD.tex.handle, LUMAD.fbo.handle, 0, temp_rect);
|
||||
|
||||
// refine pass
|
||||
Draw(refine_program, surface.Handle(), filter_fbo.handle, blit.dst_level, blit.dst_rect);
|
||||
|
||||
// These will have handles from the previous texture that was filtered, reset them to avoid
|
||||
// binding invalid textures.
|
||||
state.texture_units[0].texture_2d = 0;
|
||||
state.texture_units[1].texture_2d = 0;
|
||||
state.texture_units[2].texture_2d = 0;
|
||||
state.Apply();
|
||||
}
|
||||
|
||||
void BlitHelper::FilterBicubic(Surface& surface, const VideoCore::TextureBlit& blit) {
|
||||
SetParams(bicubic_program, surface.width, surface.height, blit.src_rect);
|
||||
Draw(bicubic_program, surface.Handle(), filter_fbo.handle, blit.dst_level, blit.dst_rect);
|
||||
}
|
||||
|
||||
void BlitHelper::FilterNearest(Surface& surface, const VideoCore::TextureBlit& blit) {
|
||||
SetParams(nearest_program, surface.width, surface.height, blit.src_rect);
|
||||
Draw(nearest_program, surface.Handle(), filter_fbo.handle, blit.dst_level, blit.dst_rect);
|
||||
}
|
||||
|
||||
void BlitHelper::FilterScaleForce(Surface& surface, const VideoCore::TextureBlit& blit) {
|
||||
SetParams(scale_force_program, surface.width, surface.height, blit.src_rect);
|
||||
Draw(scale_force_program, surface.Handle(), filter_fbo.handle, blit.dst_level, blit.dst_rect);
|
||||
}
|
||||
|
||||
void BlitHelper::FilterXbrz(Surface& surface, const VideoCore::TextureBlit& blit) {
|
||||
glProgramUniform1f(xbrz_program.handle, 2, static_cast<GLfloat>(surface.res_scale));
|
||||
SetParams(xbrz_program, surface.width, surface.height, blit.src_rect);
|
||||
Draw(xbrz_program, surface.Handle(), filter_fbo.handle, blit.dst_level, blit.dst_rect);
|
||||
}
|
||||
|
||||
void BlitHelper::SetParams(OGLProgram& program, u32 src_width, u32 src_height,
|
||||
Common::Rectangle<u32> src_rect) {
|
||||
glProgramUniform2f(
|
||||
program.handle, 0,
|
||||
static_cast<float>(src_rect.right - src_rect.left) / static_cast<float>(src_width),
|
||||
static_cast<float>(src_rect.top - src_rect.bottom) / static_cast<float>(src_height));
|
||||
glProgramUniform2f(program.handle, 1,
|
||||
static_cast<float>(src_rect.left) / static_cast<float>(src_width),
|
||||
static_cast<float>(src_rect.bottom) / static_cast<float>(src_height));
|
||||
}
|
||||
|
||||
void BlitHelper::Draw(OGLProgram& program, GLuint dst_tex, GLuint dst_fbo, u32 dst_level,
|
||||
Common::Rectangle<u32> dst_rect) {
|
||||
state.draw.draw_framebuffer = dst_fbo;
|
||||
state.draw.shader_program = program.handle;
|
||||
state.scissor.enabled = true;
|
||||
state.scissor.x = dst_rect.left;
|
||||
state.scissor.y = dst_rect.bottom;
|
||||
state.scissor.width = dst_rect.GetWidth();
|
||||
state.scissor.height = dst_rect.GetHeight();
|
||||
state.viewport.x = dst_rect.left;
|
||||
state.viewport.y = dst_rect.bottom;
|
||||
state.viewport.width = dst_rect.GetWidth();
|
||||
state.viewport.height = dst_rect.GetHeight();
|
||||
state.Apply();
|
||||
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst_tex,
|
||||
dst_level);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
|
||||
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
}
|
||||
|
||||
} // namespace OpenGL
|
61
src/video_core/renderer_opengl/gl_blit_helper.h
Normal file
61
src/video_core/renderer_opengl/gl_blit_helper.h
Normal file
@ -0,0 +1,61 @@
|
||||
// Copyright 2023 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/math_util.h"
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
#include "video_core/renderer_opengl/gl_state.h"
|
||||
|
||||
namespace VideoCore {
|
||||
struct TextureBlit;
|
||||
}
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
class TextureRuntime;
|
||||
class Surface;
|
||||
|
||||
class BlitHelper {
|
||||
public:
|
||||
BlitHelper(TextureRuntime& runtime);
|
||||
~BlitHelper();
|
||||
|
||||
bool Filter(Surface& surface, const VideoCore::TextureBlit& blit);
|
||||
|
||||
private:
|
||||
void FilterAnime4K(Surface& surface, const VideoCore::TextureBlit& blit);
|
||||
|
||||
void FilterBicubic(Surface& surface, const VideoCore::TextureBlit& blit);
|
||||
|
||||
void FilterNearest(Surface& surface, const VideoCore::TextureBlit& blit);
|
||||
|
||||
void FilterScaleForce(Surface& surface, const VideoCore::TextureBlit& blit);
|
||||
|
||||
void FilterXbrz(Surface& surface, const VideoCore::TextureBlit& blit);
|
||||
|
||||
void SetParams(OGLProgram& program, u32 src_width, u32 src_height,
|
||||
Common::Rectangle<u32> src_rect);
|
||||
|
||||
void Draw(OGLProgram& program, GLuint dst_tex, GLuint dst_fbo, u32 dst_level,
|
||||
Common::Rectangle<u32> dst_rect);
|
||||
|
||||
private:
|
||||
TextureRuntime& runtime;
|
||||
OGLVertexArray vao;
|
||||
OpenGLState state;
|
||||
OGLFramebuffer filter_fbo;
|
||||
OGLSampler linear_sampler;
|
||||
OGLSampler nearest_sampler;
|
||||
|
||||
OGLProgram bicubic_program;
|
||||
OGLProgram nearest_program;
|
||||
OGLProgram scale_force_program;
|
||||
OGLProgram xbrz_program;
|
||||
OGLProgram gradient_x_program;
|
||||
OGLProgram gradient_y_program;
|
||||
OGLProgram refine_program;
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
@ -71,10 +71,11 @@ static void APIENTRY DebugHandler(GLenum source, GLenum type, GLuint id, GLenum
|
||||
id, message);
|
||||
}
|
||||
|
||||
Driver::Driver(Core::TelemetrySession& telemetry_session_) : telemetry_session{telemetry_session_} {
|
||||
Driver::Driver(Core::TelemetrySession& telemetry_session_)
|
||||
: telemetry_session{telemetry_session_}, is_gles{Settings::values.use_gles.GetValue()} {
|
||||
const bool enable_debug = Settings::values.renderer_debug.GetValue();
|
||||
if (enable_debug) {
|
||||
glEnable(GL_DEBUG_OUTPUT);
|
||||
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
|
||||
glDebugMessageCallback(DebugHandler, nullptr);
|
||||
}
|
||||
|
||||
@ -90,6 +91,18 @@ bool Driver::HasBug(DriverBug bug) const {
|
||||
return True(bugs & bug);
|
||||
}
|
||||
|
||||
bool Driver::HasDebugTool() {
|
||||
GLint num_extensions;
|
||||
glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions);
|
||||
for (GLuint index = 0; index < static_cast<GLuint>(num_extensions); ++index) {
|
||||
const auto name = reinterpret_cast<const char*>(glGetStringi(GL_EXTENSIONS, index));
|
||||
if (!std::strcmp(name, "GL_EXT_debug_tool")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Driver::ReportDriverInfo() {
|
||||
// Report the context version and the vendor string
|
||||
gl_version = std::string_view{reinterpret_cast<const char*>(glGetString(GL_VERSION))};
|
||||
|
@ -48,6 +48,9 @@ public:
|
||||
/// Returns true of the driver has a particular bug stated in the DriverBug enum
|
||||
bool HasBug(DriverBug bug) const;
|
||||
|
||||
/// Returns true if any debug tool is attached
|
||||
bool HasDebugTool();
|
||||
|
||||
/// Returns the vendor of the currently selected physical device
|
||||
Vendor GetVendor() const {
|
||||
return vendor;
|
||||
@ -58,6 +61,11 @@ public:
|
||||
return gpu_vendor;
|
||||
}
|
||||
|
||||
/// Returns true if the an OpenGLES context is used
|
||||
bool IsOpenGLES() const noexcept {
|
||||
return is_gles;
|
||||
}
|
||||
|
||||
/// Returns true if the implementation is suitable for emulation
|
||||
bool IsSuitable() const {
|
||||
return is_suitable;
|
||||
@ -99,6 +107,7 @@ private:
|
||||
Vendor vendor = Vendor::Unknown;
|
||||
DriverBug bugs{};
|
||||
bool is_suitable{};
|
||||
bool is_gles{};
|
||||
|
||||
bool ext_buffer_storage{};
|
||||
bool arb_buffer_storage{};
|
||||
|
@ -5,261 +5,128 @@
|
||||
#include "common/scope_exit.h"
|
||||
#include "video_core/renderer_opengl/gl_format_reinterpreter.h"
|
||||
#include "video_core/renderer_opengl/gl_state.h"
|
||||
#include "video_core/renderer_opengl/gl_texture_runtime.h"
|
||||
|
||||
#include "video_core/host_shaders/format_reinterpreter/d24s8_to_rgba8_frag.h"
|
||||
#include "video_core/host_shaders/format_reinterpreter/fullscreen_quad_vert.h"
|
||||
#include "video_core/host_shaders/format_reinterpreter/rgba4_to_rgb5a1_frag.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
class RGBA4toRGB5A1 final : public FormatReinterpreterBase {
|
||||
public:
|
||||
RGBA4toRGB5A1() {
|
||||
constexpr std::string_view vs_source = R"(
|
||||
out vec2 dst_coord;
|
||||
|
||||
uniform mediump ivec2 dst_size;
|
||||
|
||||
const vec2 vertices[4] =
|
||||
vec2[4](vec2(-1.0, -1.0), vec2(1.0, -1.0), vec2(-1.0, 1.0), vec2(1.0, 1.0));
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(vertices[gl_VertexID], 0.0, 1.0);
|
||||
dst_coord = (vertices[gl_VertexID] / 2.0 + 0.5) * vec2(dst_size);
|
||||
RGBA4toRGB5A1::RGBA4toRGB5A1() {
|
||||
program.Create(HostShaders::FULLSCREEN_QUAD_VERT, HostShaders::RGBA4_TO_RGB5A1_FRAG);
|
||||
dst_size_loc = glGetUniformLocation(program.handle, "dst_size");
|
||||
src_size_loc = glGetUniformLocation(program.handle, "src_size");
|
||||
src_offset_loc = glGetUniformLocation(program.handle, "src_offset");
|
||||
vao.Create();
|
||||
}
|
||||
)";
|
||||
|
||||
constexpr std::string_view fs_source = R"(
|
||||
in mediump vec2 dst_coord;
|
||||
void RGBA4toRGB5A1::Reinterpret(Surface& source, Common::Rectangle<u32> src_rect, Surface& dest,
|
||||
Common::Rectangle<u32> dst_rect) {
|
||||
OpenGLState prev_state = OpenGLState::GetCurState();
|
||||
SCOPE_EXIT({ prev_state.Apply(); });
|
||||
|
||||
out lowp vec4 frag_color;
|
||||
OpenGLState state;
|
||||
state.texture_units[0].texture_2d = source.Handle();
|
||||
state.draw.draw_framebuffer = draw_fbo.handle;
|
||||
state.draw.shader_program = program.handle;
|
||||
state.draw.vertex_array = vao.handle;
|
||||
state.viewport = {static_cast<GLint>(dst_rect.left), static_cast<GLint>(dst_rect.bottom),
|
||||
static_cast<GLsizei>(dst_rect.GetWidth()),
|
||||
static_cast<GLsizei>(dst_rect.GetHeight())};
|
||||
state.Apply();
|
||||
|
||||
uniform lowp sampler2D source;
|
||||
uniform mediump ivec2 dst_size;
|
||||
uniform mediump ivec2 src_size;
|
||||
uniform mediump ivec2 src_offset;
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dest.Handle(),
|
||||
0);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
|
||||
|
||||
void main() {
|
||||
mediump ivec2 tex_coord;
|
||||
if (src_size == dst_size) {
|
||||
tex_coord = ivec2(dst_coord);
|
||||
glUniform2i(dst_size_loc, dst_rect.GetWidth(), dst_rect.GetHeight());
|
||||
glUniform2i(src_size_loc, src_rect.GetWidth(), src_rect.GetHeight());
|
||||
glUniform2i(src_offset_loc, src_rect.left, src_rect.bottom);
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
}
|
||||
|
||||
ShaderD24S8toRGBA8::ShaderD24S8toRGBA8() {
|
||||
program.Create(HostShaders::FULLSCREEN_QUAD_VERT, HostShaders::D24S8_TO_RGBA8_FRAG);
|
||||
dst_size_loc = glGetUniformLocation(program.handle, "dst_size");
|
||||
src_size_loc = glGetUniformLocation(program.handle, "src_size");
|
||||
src_offset_loc = glGetUniformLocation(program.handle, "src_offset");
|
||||
vao.Create();
|
||||
|
||||
auto state = OpenGLState::GetCurState();
|
||||
auto cur_program = state.draw.shader_program;
|
||||
state.draw.shader_program = program.handle;
|
||||
state.Apply();
|
||||
glUniform1i(glGetUniformLocation(program.handle, "stencil"), 1);
|
||||
state.draw.shader_program = cur_program;
|
||||
state.Apply();
|
||||
|
||||
// Nvidia seem to be the only one to support D24S8 views, at least on windows
|
||||
// so for everyone else it will do an intermediate copy before running through the shader
|
||||
std::string_view vendor{reinterpret_cast<const char*>(glGetString(GL_VENDOR))};
|
||||
if (vendor.find("NVIDIA") != vendor.npos) {
|
||||
use_texture_view = true;
|
||||
} else {
|
||||
highp int tex_index = int(dst_coord.y) * dst_size.x + int(dst_coord.x);
|
||||
mediump int y = tex_index / src_size.x;
|
||||
tex_coord = ivec2(tex_index - y * src_size.x, y);
|
||||
LOG_INFO(Render_OpenGL,
|
||||
"Texture views are unsupported, reinterpretation will do intermediate copy");
|
||||
temp_tex.Create();
|
||||
}
|
||||
tex_coord -= src_offset;
|
||||
|
||||
lowp ivec4 rgba4 = ivec4(texelFetch(source, tex_coord, 0) * (exp2(4.0) - 1.0));
|
||||
lowp ivec3 rgb5 =
|
||||
((rgba4.rgb << ivec3(1, 2, 3)) | (rgba4.gba >> ivec3(3, 2, 1))) & 0x1F;
|
||||
frag_color = vec4(vec3(rgb5) / (exp2(5.0) - 1.0), rgba4.a & 0x01);
|
||||
}
|
||||
)";
|
||||
|
||||
program.Create(vs_source.data(), fs_source.data());
|
||||
dst_size_loc = glGetUniformLocation(program.handle, "dst_size");
|
||||
src_size_loc = glGetUniformLocation(program.handle, "src_size");
|
||||
src_offset_loc = glGetUniformLocation(program.handle, "src_offset");
|
||||
vao.Create();
|
||||
}
|
||||
void ShaderD24S8toRGBA8::Reinterpret(Surface& source, Common::Rectangle<u32> src_rect,
|
||||
Surface& dest, Common::Rectangle<u32> dst_rect) {
|
||||
OpenGLState prev_state = OpenGLState::GetCurState();
|
||||
SCOPE_EXIT({ prev_state.Apply(); });
|
||||
|
||||
PixelFormat GetSourceFormat() const override {
|
||||
return PixelFormat::RGBA4;
|
||||
}
|
||||
|
||||
void Reinterpret(const OGLTexture& src_tex, Common::Rectangle<u32> src_rect,
|
||||
const OGLTexture& dst_tex, Common::Rectangle<u32> dst_rect) override {
|
||||
OpenGLState prev_state = OpenGLState::GetCurState();
|
||||
SCOPE_EXIT({ prev_state.Apply(); });
|
||||
|
||||
OpenGLState state;
|
||||
state.texture_units[0].texture_2d = src_tex.handle;
|
||||
state.draw.draw_framebuffer = draw_fbo.handle;
|
||||
state.draw.shader_program = program.handle;
|
||||
state.draw.vertex_array = vao.handle;
|
||||
state.viewport = {static_cast<GLint>(dst_rect.left), static_cast<GLint>(dst_rect.bottom),
|
||||
static_cast<GLsizei>(dst_rect.GetWidth()),
|
||||
static_cast<GLsizei>(dst_rect.GetHeight())};
|
||||
state.Apply();
|
||||
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
||||
dst_tex.handle, 0);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0,
|
||||
0);
|
||||
|
||||
glUniform2i(dst_size_loc, dst_rect.GetWidth(), dst_rect.GetHeight());
|
||||
glUniform2i(src_size_loc, src_rect.GetWidth(), src_rect.GetHeight());
|
||||
glUniform2i(src_offset_loc, src_rect.left, src_rect.bottom);
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
}
|
||||
|
||||
private:
|
||||
OGLProgram program;
|
||||
GLint dst_size_loc{-1}, src_size_loc{-1}, src_offset_loc{-1};
|
||||
OGLVertexArray vao;
|
||||
};
|
||||
|
||||
class ShaderD24S8toRGBA8 final : public FormatReinterpreterBase {
|
||||
public:
|
||||
ShaderD24S8toRGBA8() {
|
||||
constexpr std::string_view vs_source = R"(
|
||||
out vec2 dst_coord;
|
||||
|
||||
uniform mediump ivec2 dst_size;
|
||||
|
||||
const vec2 vertices[4] =
|
||||
vec2[4](vec2(-1.0, -1.0), vec2(1.0, -1.0), vec2(-1.0, 1.0), vec2(1.0, 1.0));
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(vertices[gl_VertexID], 0.0, 1.0);
|
||||
dst_coord = (vertices[gl_VertexID] / 2.0 + 0.5) * vec2(dst_size);
|
||||
}
|
||||
)";
|
||||
|
||||
constexpr std::string_view fs_source = R"(
|
||||
in mediump vec2 dst_coord;
|
||||
|
||||
out lowp vec4 frag_color;
|
||||
|
||||
uniform highp sampler2D depth;
|
||||
uniform lowp usampler2D stencil;
|
||||
uniform mediump ivec2 dst_size;
|
||||
uniform mediump ivec2 src_size;
|
||||
uniform mediump ivec2 src_offset;
|
||||
|
||||
void main() {
|
||||
mediump ivec2 tex_coord;
|
||||
if (src_size == dst_size) {
|
||||
tex_coord = ivec2(dst_coord);
|
||||
} else {
|
||||
highp int tex_index = int(dst_coord.y) * dst_size.x + int(dst_coord.x);
|
||||
mediump int y = tex_index / src_size.x;
|
||||
tex_coord = ivec2(tex_index - y * src_size.x, y);
|
||||
}
|
||||
tex_coord -= src_offset;
|
||||
|
||||
highp uint depth_val =
|
||||
uint(texelFetch(depth, tex_coord, 0).x * (exp2(32.0) - 1.0));
|
||||
lowp uint stencil_val = texelFetch(stencil, tex_coord, 0).x;
|
||||
highp uvec4 components =
|
||||
uvec4(stencil_val, (uvec3(depth_val) >> uvec3(24u, 16u, 8u)) & 0x000000FFu);
|
||||
frag_color = vec4(components) / (exp2(8.0) - 1.0);
|
||||
}
|
||||
)";
|
||||
|
||||
program.Create(vs_source.data(), fs_source.data());
|
||||
dst_size_loc = glGetUniformLocation(program.handle, "dst_size");
|
||||
src_size_loc = glGetUniformLocation(program.handle, "src_size");
|
||||
src_offset_loc = glGetUniformLocation(program.handle, "src_offset");
|
||||
vao.Create();
|
||||
|
||||
auto state = OpenGLState::GetCurState();
|
||||
auto cur_program = state.draw.shader_program;
|
||||
state.draw.shader_program = program.handle;
|
||||
state.Apply();
|
||||
glUniform1i(glGetUniformLocation(program.handle, "stencil"), 1);
|
||||
state.draw.shader_program = cur_program;
|
||||
state.Apply();
|
||||
|
||||
// Nvidia seem to be the only one to support D24S8 views, at least on windows
|
||||
// so for everyone else it will do an intermediate copy before running through the shader
|
||||
std::string_view vendor{reinterpret_cast<const char*>(glGetString(GL_VENDOR))};
|
||||
if (vendor.find("NVIDIA") != vendor.npos) {
|
||||
use_texture_view = true;
|
||||
} else {
|
||||
LOG_INFO(Render_OpenGL,
|
||||
"Texture views are unsupported, reinterpretation will do intermediate copy");
|
||||
temp_tex.Create();
|
||||
}
|
||||
}
|
||||
|
||||
PixelFormat GetSourceFormat() const override {
|
||||
return PixelFormat::D24S8;
|
||||
}
|
||||
|
||||
void Reinterpret(const OGLTexture& src_tex, Common::Rectangle<u32> src_rect,
|
||||
const OGLTexture& dst_tex, Common::Rectangle<u32> dst_rect) override {
|
||||
OpenGLState prev_state = OpenGLState::GetCurState();
|
||||
SCOPE_EXIT({ prev_state.Apply(); });
|
||||
|
||||
OpenGLState state;
|
||||
state.texture_units[0].texture_2d = src_tex.handle;
|
||||
|
||||
if (use_texture_view) {
|
||||
temp_tex.Create();
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glTextureView(temp_tex.handle, GL_TEXTURE_2D, src_tex.handle, GL_DEPTH24_STENCIL8, 0, 1,
|
||||
0, 1);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
} else if (src_rect.top > temp_rect.top || src_rect.right > temp_rect.right) {
|
||||
temp_tex.Release();
|
||||
temp_tex.Create();
|
||||
state.texture_units[1].texture_2d = temp_tex.handle;
|
||||
state.Apply();
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glTexStorage2D(GL_TEXTURE_2D, 1, GL_DEPTH24_STENCIL8, src_rect.right, src_rect.top);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
temp_rect = src_rect;
|
||||
}
|
||||
|
||||
state.texture_units[1].texture_2d = temp_tex.handle;
|
||||
state.draw.draw_framebuffer = draw_fbo.handle;
|
||||
state.draw.shader_program = program.handle;
|
||||
state.draw.vertex_array = vao.handle;
|
||||
state.viewport = {static_cast<GLint>(dst_rect.left), static_cast<GLint>(dst_rect.bottom),
|
||||
static_cast<GLsizei>(dst_rect.GetWidth()),
|
||||
static_cast<GLsizei>(dst_rect.GetHeight())};
|
||||
state.Apply();
|
||||
OpenGLState state;
|
||||
state.texture_units[0].texture_2d = source.Handle();
|
||||
|
||||
if (use_texture_view) {
|
||||
temp_tex.Create();
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
if (!use_texture_view) {
|
||||
glCopyImageSubData(src_tex.handle, GL_TEXTURE_2D, 0, src_rect.left, src_rect.bottom, 0,
|
||||
temp_tex.handle, GL_TEXTURE_2D, 0, src_rect.left, src_rect.bottom, 0,
|
||||
src_rect.GetWidth(), src_rect.GetHeight(), 1);
|
||||
}
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_STENCIL_INDEX);
|
||||
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
||||
dst_tex.handle, 0);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0,
|
||||
0);
|
||||
|
||||
glUniform2i(dst_size_loc, dst_rect.GetWidth(), dst_rect.GetHeight());
|
||||
glUniform2i(src_size_loc, src_rect.GetWidth(), src_rect.GetHeight());
|
||||
glUniform2i(src_offset_loc, src_rect.left, src_rect.bottom);
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
if (use_texture_view) {
|
||||
temp_tex.Release();
|
||||
}
|
||||
glTextureView(temp_tex.handle, GL_TEXTURE_2D, source.Handle(), GL_DEPTH24_STENCIL8, 0, 1, 0,
|
||||
1);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
} else if (src_rect.top > temp_rect.top || src_rect.right > temp_rect.right) {
|
||||
temp_tex.Release();
|
||||
temp_tex.Create();
|
||||
state.texture_units[1].texture_2d = temp_tex.handle;
|
||||
state.Apply();
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glTexStorage2D(GL_TEXTURE_2D, 1, GL_DEPTH24_STENCIL8, src_rect.right, src_rect.top);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
temp_rect = src_rect;
|
||||
}
|
||||
|
||||
private:
|
||||
bool use_texture_view{};
|
||||
OGLProgram program{};
|
||||
GLint dst_size_loc{-1}, src_size_loc{-1}, src_offset_loc{-1};
|
||||
OGLVertexArray vao{};
|
||||
OGLTexture temp_tex{};
|
||||
Common::Rectangle<u32> temp_rect{0, 0, 0, 0};
|
||||
};
|
||||
state.texture_units[1].texture_2d = temp_tex.handle;
|
||||
state.draw.draw_framebuffer = draw_fbo.handle;
|
||||
state.draw.shader_program = program.handle;
|
||||
state.draw.vertex_array = vao.handle;
|
||||
state.viewport = {static_cast<GLint>(dst_rect.left), static_cast<GLint>(dst_rect.bottom),
|
||||
static_cast<GLsizei>(dst_rect.GetWidth()),
|
||||
static_cast<GLsizei>(dst_rect.GetHeight())};
|
||||
state.Apply();
|
||||
|
||||
FormatReinterpreterOpenGL::FormatReinterpreterOpenGL() {
|
||||
const std::string_view vendor{reinterpret_cast<const char*>(glGetString(GL_VENDOR))};
|
||||
const std::string_view version{reinterpret_cast<const char*>(glGetString(GL_VERSION))};
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
if (!use_texture_view) {
|
||||
glCopyImageSubData(source.Handle(), GL_TEXTURE_2D, 0, src_rect.left, src_rect.bottom, 0,
|
||||
temp_tex.handle, GL_TEXTURE_2D, 0, src_rect.left, src_rect.bottom, 0,
|
||||
src_rect.GetWidth(), src_rect.GetHeight(), 1);
|
||||
}
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_STENCIL_INDEX);
|
||||
|
||||
auto Register = [this](PixelFormat dest, std::unique_ptr<FormatReinterpreterBase>&& obj) {
|
||||
const u32 dst_index = static_cast<u32>(dest);
|
||||
return reinterpreters[dst_index].push_back(std::move(obj));
|
||||
};
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dest.Handle(),
|
||||
0);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
|
||||
|
||||
Register(PixelFormat::RGBA8, std::make_unique<ShaderD24S8toRGBA8>());
|
||||
LOG_INFO(Render_OpenGL, "Using shader for D24S8 to RGBA8 reinterpretation");
|
||||
glUniform2i(dst_size_loc, dst_rect.GetWidth(), dst_rect.GetHeight());
|
||||
glUniform2i(src_size_loc, src_rect.GetWidth(), src_rect.GetHeight());
|
||||
glUniform2i(src_offset_loc, src_rect.left, src_rect.bottom);
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
Register(PixelFormat::RGB5A1, std::make_unique<RGBA4toRGB5A1>());
|
||||
}
|
||||
|
||||
auto FormatReinterpreterOpenGL::GetPossibleReinterpretations(PixelFormat dst_format)
|
||||
-> const ReinterpreterList& {
|
||||
return reinterpreters[static_cast<u32>(dst_format)];
|
||||
temp_tex.Release();
|
||||
}
|
||||
|
||||
} // namespace OpenGL
|
||||
|
@ -11,7 +11,7 @@
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
class RasterizerCacheOpenGL;
|
||||
class Surface;
|
||||
|
||||
class FormatReinterpreterBase {
|
||||
public:
|
||||
@ -22,9 +22,9 @@ public:
|
||||
|
||||
virtual ~FormatReinterpreterBase() = default;
|
||||
|
||||
virtual PixelFormat GetSourceFormat() const = 0;
|
||||
virtual void Reinterpret(const OGLTexture& src_tex, Common::Rectangle<u32> src_rect,
|
||||
const OGLTexture& dst_tex, Common::Rectangle<u32> dst_rect) = 0;
|
||||
virtual VideoCore::PixelFormat GetSourceFormat() const = 0;
|
||||
virtual void Reinterpret(Surface& source, Common::Rectangle<u32> src_rect, Surface& dest,
|
||||
Common::Rectangle<u32> dst_rect) = 0;
|
||||
|
||||
protected:
|
||||
OGLFramebuffer read_fbo;
|
||||
@ -33,15 +33,45 @@ protected:
|
||||
|
||||
using ReinterpreterList = std::vector<std::unique_ptr<FormatReinterpreterBase>>;
|
||||
|
||||
class FormatReinterpreterOpenGL : NonCopyable {
|
||||
class RGBA4toRGB5A1 final : public FormatReinterpreterBase {
|
||||
public:
|
||||
FormatReinterpreterOpenGL();
|
||||
~FormatReinterpreterOpenGL() = default;
|
||||
RGBA4toRGB5A1();
|
||||
|
||||
const ReinterpreterList& GetPossibleReinterpretations(PixelFormat dst_format);
|
||||
VideoCore::PixelFormat GetSourceFormat() const override {
|
||||
return VideoCore::PixelFormat::RGBA4;
|
||||
}
|
||||
|
||||
void Reinterpret(Surface& source, Common::Rectangle<u32> src_rect, Surface& dest,
|
||||
Common::Rectangle<u32> dst_rect) override;
|
||||
|
||||
private:
|
||||
std::array<ReinterpreterList, PIXEL_FORMAT_COUNT> reinterpreters;
|
||||
OGLProgram program;
|
||||
GLint dst_size_loc{-1};
|
||||
GLint src_size_loc{-1};
|
||||
GLint src_offset_loc{-1};
|
||||
OGLVertexArray vao;
|
||||
};
|
||||
|
||||
class ShaderD24S8toRGBA8 final : public FormatReinterpreterBase {
|
||||
public:
|
||||
ShaderD24S8toRGBA8();
|
||||
|
||||
VideoCore::PixelFormat GetSourceFormat() const override {
|
||||
return VideoCore::PixelFormat::D24S8;
|
||||
}
|
||||
|
||||
void Reinterpret(Surface& source, Common::Rectangle<u32> src_rect, Surface& dest,
|
||||
Common::Rectangle<u32> dst_rect) override;
|
||||
|
||||
private:
|
||||
bool use_texture_view{};
|
||||
OGLProgram program{};
|
||||
GLint dst_size_loc{-1};
|
||||
GLint src_size_loc{-1};
|
||||
GLint src_offset_loc{-1};
|
||||
OGLVertexArray vao{};
|
||||
OGLTexture temp_tex{};
|
||||
Common::Rectangle<u32> temp_rect{0, 0, 0, 0};
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
||||
|
@ -25,9 +25,10 @@ MICROPROFILE_DEFINE(OpenGL_VAO, "OpenGL", "Vertex Array Setup", MP_RGB(255, 128,
|
||||
MICROPROFILE_DEFINE(OpenGL_VS, "OpenGL", "Vertex Shader Setup", MP_RGB(192, 128, 128));
|
||||
MICROPROFILE_DEFINE(OpenGL_GS, "OpenGL", "Geometry Shader Setup", MP_RGB(128, 192, 128));
|
||||
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));
|
||||
|
||||
using VideoCore::SurfaceType;
|
||||
|
||||
constexpr std::size_t VERTEX_BUFFER_SIZE = 16 * 1024 * 1024;
|
||||
constexpr std::size_t INDEX_BUFFER_SIZE = 2 * 1024 * 1024;
|
||||
constexpr std::size_t UNIFORM_BUFFER_SIZE = 2 * 1024 * 1024;
|
||||
@ -62,17 +63,27 @@ GLenum MakeAttributeType(Pica::PipelineRegs::VertexAttributeFormat format) {
|
||||
return GL_UNSIGNED_BYTE;
|
||||
}
|
||||
|
||||
[[nodiscard]] GLsizeiptr TextureBufferSize() {
|
||||
// Use the smallest texel size from the texel views
|
||||
// which corresponds to GL_RG32F
|
||||
GLint max_texel_buffer_size;
|
||||
glGetIntegerv(GL_MAX_TEXTURE_BUFFER_SIZE, &max_texel_buffer_size);
|
||||
LOG_INFO(Render_OpenGL, "Max texture buffer size: {}", max_texel_buffer_size);
|
||||
return std::min<GLsizeiptr>(max_texel_buffer_size * 8, TEXTURE_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
RasterizerOpenGL::RasterizerOpenGL(Memory::MemorySystem& memory, VideoCore::RendererBase& renderer,
|
||||
Driver& driver_)
|
||||
: VideoCore::RasterizerAccelerated{memory}, driver{driver_}, res_cache{renderer},
|
||||
: VideoCore::RasterizerAccelerated{memory}, driver{driver_}, runtime{driver, renderer},
|
||||
res_cache{memory, runtime, regs, renderer}, texture_buffer_size{TextureBufferSize()},
|
||||
vertex_buffer{driver, GL_ARRAY_BUFFER, VERTEX_BUFFER_SIZE},
|
||||
uniform_buffer{driver, GL_UNIFORM_BUFFER, UNIFORM_BUFFER_SIZE},
|
||||
index_buffer{driver, GL_ELEMENT_ARRAY_BUFFER, INDEX_BUFFER_SIZE},
|
||||
texture_buffer{driver, GL_TEXTURE_BUFFER, TEXTURE_BUFFER_SIZE}, texture_lf_buffer{
|
||||
texture_buffer{driver, GL_TEXTURE_BUFFER, texture_buffer_size}, texture_lf_buffer{
|
||||
driver, GL_TEXTURE_BUFFER,
|
||||
TEXTURE_BUFFER_SIZE} {
|
||||
texture_buffer_size} {
|
||||
|
||||
// Clipping plane 0 is always enabled for PICA fixed clip plane z <= 0
|
||||
state.clip_distance[0] = true;
|
||||
@ -372,11 +383,8 @@ void RasterizerOpenGL::DrawTriangles() {
|
||||
bool RasterizerOpenGL::Draw(bool accelerate, bool is_indexed) {
|
||||
MICROPROFILE_SCOPE(OpenGL_Drawing);
|
||||
|
||||
bool shadow_rendering = regs.framebuffer.output_merger.fragment_operation_mode ==
|
||||
Pica::FramebufferRegs::FragmentOperationMode::Shadow;
|
||||
|
||||
const bool has_stencil =
|
||||
regs.framebuffer.framebuffer.depth_format == Pica::FramebufferRegs::DepthFormat::D24S8;
|
||||
const bool shadow_rendering = regs.framebuffer.IsShadowRendering();
|
||||
const bool has_stencil = regs.framebuffer.HasStencil();
|
||||
|
||||
const bool write_color_fb = shadow_rendering || state.color_mask.red_enabled == GL_TRUE ||
|
||||
state.color_mask.green_enabled == GL_TRUE ||
|
||||
@ -394,108 +402,45 @@ bool RasterizerOpenGL::Draw(bool accelerate, bool is_indexed) {
|
||||
(write_depth_fb || regs.framebuffer.output_merger.depth_test_enable != 0 ||
|
||||
(has_stencil && state.stencil.test_enabled));
|
||||
|
||||
Common::Rectangle<s32> viewport_rect_unscaled{
|
||||
// These registers hold half-width and half-height, so must be multiplied by 2
|
||||
regs.rasterizer.viewport_corner.x, // left
|
||||
regs.rasterizer.viewport_corner.y + // top
|
||||
static_cast<s32>(Pica::float24::FromRaw(regs.rasterizer.viewport_size_y).ToFloat32() *
|
||||
2),
|
||||
regs.rasterizer.viewport_corner.x + // right
|
||||
static_cast<s32>(Pica::float24::FromRaw(regs.rasterizer.viewport_size_x).ToFloat32() *
|
||||
2),
|
||||
regs.rasterizer.viewport_corner.y // bottom
|
||||
};
|
||||
|
||||
Surface color_surface;
|
||||
Surface depth_surface;
|
||||
Common::Rectangle<u32> surfaces_rect;
|
||||
std::tie(color_surface, depth_surface, surfaces_rect) =
|
||||
res_cache.GetFramebufferSurfaces(using_color_fb, using_depth_fb, viewport_rect_unscaled);
|
||||
|
||||
const u16 res_scale = color_surface != nullptr
|
||||
? color_surface->res_scale
|
||||
: (depth_surface == nullptr ? 1u : depth_surface->res_scale);
|
||||
|
||||
Common::Rectangle<u32> draw_rect{
|
||||
static_cast<u32>(std::clamp<s32>(static_cast<s32>(surfaces_rect.left) +
|
||||
viewport_rect_unscaled.left * res_scale,
|
||||
surfaces_rect.left, surfaces_rect.right)), // Left
|
||||
static_cast<u32>(std::clamp<s32>(static_cast<s32>(surfaces_rect.bottom) +
|
||||
viewport_rect_unscaled.top * res_scale,
|
||||
surfaces_rect.bottom, surfaces_rect.top)), // Top
|
||||
static_cast<u32>(std::clamp<s32>(static_cast<s32>(surfaces_rect.left) +
|
||||
viewport_rect_unscaled.right * res_scale,
|
||||
surfaces_rect.left, surfaces_rect.right)), // Right
|
||||
static_cast<u32>(std::clamp<s32>(static_cast<s32>(surfaces_rect.bottom) +
|
||||
viewport_rect_unscaled.bottom * res_scale,
|
||||
surfaces_rect.bottom, surfaces_rect.top))}; // Bottom
|
||||
|
||||
// Bind the framebuffer surfaces
|
||||
state.draw.draw_framebuffer = framebuffer.handle;
|
||||
state.Apply();
|
||||
|
||||
if (shadow_rendering) {
|
||||
if (color_surface == nullptr) {
|
||||
return true;
|
||||
}
|
||||
|
||||
glFramebufferParameteri(GL_DRAW_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_WIDTH,
|
||||
color_surface->width * color_surface->res_scale);
|
||||
glFramebufferParameteri(GL_DRAW_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_HEIGHT,
|
||||
color_surface->height * color_surface->res_scale);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0,
|
||||
0);
|
||||
state.image_shadow_buffer = color_surface->texture.handle;
|
||||
} else {
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
||||
color_surface != nullptr ? color_surface->texture.handle : 0, 0);
|
||||
if (depth_surface != nullptr) {
|
||||
if (has_stencil) {
|
||||
// attach both depth and stencil
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
|
||||
GL_TEXTURE_2D, depth_surface->texture.handle, 0);
|
||||
} else {
|
||||
// attach depth
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D,
|
||||
depth_surface->texture.handle, 0);
|
||||
// clear stencil attachment
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0,
|
||||
0);
|
||||
}
|
||||
} else {
|
||||
// clear both depth and stencil attachment
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
|
||||
0, 0);
|
||||
}
|
||||
const Framebuffer framebuffer =
|
||||
res_cache.GetFramebufferSurfaces(using_color_fb, using_depth_fb);
|
||||
const bool has_color = framebuffer.HasAttachment(SurfaceType::Color);
|
||||
const bool has_depth_stencil = framebuffer.HasAttachment(SurfaceType::DepthStencil);
|
||||
if (!has_color && (shadow_rendering || !has_depth_stencil)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Sync the viewport
|
||||
state.viewport.x =
|
||||
static_cast<GLint>(surfaces_rect.left) + viewport_rect_unscaled.left * res_scale;
|
||||
state.viewport.y =
|
||||
static_cast<GLint>(surfaces_rect.bottom) + viewport_rect_unscaled.bottom * res_scale;
|
||||
state.viewport.width = static_cast<GLsizei>(viewport_rect_unscaled.GetWidth() * res_scale);
|
||||
state.viewport.height = static_cast<GLsizei>(viewport_rect_unscaled.GetHeight() * res_scale);
|
||||
// Bind the framebuffer surfaces
|
||||
if (shadow_rendering) {
|
||||
state.image_shadow_buffer = framebuffer.Attachment(SurfaceType::Color);
|
||||
}
|
||||
state.draw.draw_framebuffer = framebuffer.Handle();
|
||||
|
||||
// Sync the viewport
|
||||
const auto viewport = framebuffer.Viewport();
|
||||
state.viewport.x = viewport.x;
|
||||
state.viewport.y = viewport.y;
|
||||
state.viewport.width = viewport.width;
|
||||
state.viewport.height = viewport.height;
|
||||
|
||||
// Viewport can have negative offsets or larger dimensions than our framebuffer sub-rect.
|
||||
// Enable scissor test to prevent drawing outside of the framebuffer region
|
||||
const auto draw_rect = framebuffer.DrawRect();
|
||||
state.scissor.enabled = true;
|
||||
state.scissor.x = draw_rect.left;
|
||||
state.scissor.y = draw_rect.bottom;
|
||||
state.scissor.width = draw_rect.GetWidth();
|
||||
state.scissor.height = draw_rect.GetHeight();
|
||||
state.Apply();
|
||||
|
||||
const u32 res_scale = framebuffer.ResolutionScale();
|
||||
if (uniform_block_data.data.framebuffer_scale != res_scale) {
|
||||
uniform_block_data.data.framebuffer_scale = res_scale;
|
||||
uniform_block_data.dirty = true;
|
||||
}
|
||||
|
||||
// Scissor checks are window-, not viewport-relative, which means that if the cached texture
|
||||
// sub-rect changes, the scissor bounds also need to be updated.
|
||||
GLint scissor_x1 =
|
||||
static_cast<GLint>(surfaces_rect.left + regs.rasterizer.scissor_test.x1 * res_scale);
|
||||
GLint scissor_y1 =
|
||||
static_cast<GLint>(surfaces_rect.bottom + regs.rasterizer.scissor_test.y1 * res_scale);
|
||||
// x2, y2 have +1 added to cover the entire pixel area, otherwise you might get cracks when
|
||||
// scaling or doing multisampling.
|
||||
GLint scissor_x2 =
|
||||
static_cast<GLint>(surfaces_rect.left + (regs.rasterizer.scissor_test.x2 + 1) * res_scale);
|
||||
GLint scissor_y2 = static_cast<GLint>(surfaces_rect.bottom +
|
||||
(regs.rasterizer.scissor_test.y2 + 1) * res_scale);
|
||||
|
||||
// Update scissor uniforms
|
||||
const auto [scissor_x1, scissor_y2, scissor_x2, scissor_y1] = framebuffer.Scissor();
|
||||
if (uniform_block_data.data.scissor_x1 != scissor_x1 ||
|
||||
uniform_block_data.data.scissor_x2 != scissor_x2 ||
|
||||
uniform_block_data.data.scissor_y1 != scissor_y1 ||
|
||||
@ -508,127 +453,8 @@ bool RasterizerOpenGL::Draw(bool accelerate, bool is_indexed) {
|
||||
uniform_block_data.dirty = true;
|
||||
}
|
||||
|
||||
bool need_duplicate_texture = false;
|
||||
auto CheckBarrier = [&need_duplicate_texture, &color_surface](GLuint handle) {
|
||||
if (color_surface && color_surface->texture.handle == handle) {
|
||||
need_duplicate_texture = true;
|
||||
}
|
||||
};
|
||||
|
||||
const auto BindCubeFace = [&](GLuint& target, Pica::TexturingRegs::CubeFace face,
|
||||
Pica::Texture::TextureInfo& info) {
|
||||
info.physical_address = regs.texturing.GetCubePhysicalAddress(face);
|
||||
Surface surface = res_cache.GetTextureSurface(info);
|
||||
|
||||
if (surface != nullptr) {
|
||||
CheckBarrier(target = surface->texture.handle);
|
||||
} else {
|
||||
target = 0;
|
||||
}
|
||||
};
|
||||
|
||||
// Sync and bind the texture surfaces
|
||||
const auto pica_textures = regs.texturing.GetTextures();
|
||||
for (unsigned texture_index = 0; texture_index < pica_textures.size(); ++texture_index) {
|
||||
const auto& texture = pica_textures[texture_index];
|
||||
|
||||
if (texture.enabled) {
|
||||
if (texture_index == 0) {
|
||||
using TextureType = Pica::TexturingRegs::TextureConfig::TextureType;
|
||||
switch (texture.config.type.Value()) {
|
||||
case TextureType::Shadow2D: {
|
||||
Surface surface = res_cache.GetTextureSurface(texture);
|
||||
if (surface != nullptr) {
|
||||
CheckBarrier(state.image_shadow_texture_px = surface->texture.handle);
|
||||
} else {
|
||||
state.image_shadow_texture_px = 0;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
case TextureType::ShadowCube: {
|
||||
using CubeFace = Pica::TexturingRegs::CubeFace;
|
||||
auto info = Pica::Texture::TextureInfo::FromPicaRegister(texture.config,
|
||||
texture.format);
|
||||
BindCubeFace(state.image_shadow_texture_px, CubeFace::PositiveX, info);
|
||||
BindCubeFace(state.image_shadow_texture_nx, CubeFace::NegativeX, info);
|
||||
BindCubeFace(state.image_shadow_texture_py, CubeFace::PositiveY, info);
|
||||
BindCubeFace(state.image_shadow_texture_ny, CubeFace::NegativeY, info);
|
||||
BindCubeFace(state.image_shadow_texture_pz, CubeFace::PositiveZ, info);
|
||||
BindCubeFace(state.image_shadow_texture_nz, CubeFace::NegativeZ, info);
|
||||
continue;
|
||||
}
|
||||
case TextureType::TextureCube:
|
||||
using CubeFace = Pica::TexturingRegs::CubeFace;
|
||||
TextureCubeConfig config;
|
||||
config.px = regs.texturing.GetCubePhysicalAddress(CubeFace::PositiveX);
|
||||
config.nx = regs.texturing.GetCubePhysicalAddress(CubeFace::NegativeX);
|
||||
config.py = regs.texturing.GetCubePhysicalAddress(CubeFace::PositiveY);
|
||||
config.ny = regs.texturing.GetCubePhysicalAddress(CubeFace::NegativeY);
|
||||
config.pz = regs.texturing.GetCubePhysicalAddress(CubeFace::PositiveZ);
|
||||
config.nz = regs.texturing.GetCubePhysicalAddress(CubeFace::NegativeZ);
|
||||
config.width = texture.config.width;
|
||||
config.format = texture.format;
|
||||
state.texture_cube_unit.texture_cube =
|
||||
res_cache.GetTextureCube(config).texture.handle;
|
||||
|
||||
texture_cube_sampler.SyncWithConfig(texture.config);
|
||||
state.texture_units[texture_index].texture_2d = 0;
|
||||
continue; // Texture unit 0 setup finished. Continue to next unit
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
state.texture_cube_unit.texture_cube = 0;
|
||||
}
|
||||
|
||||
texture_samplers[texture_index].SyncWithConfig(texture.config);
|
||||
Surface surface = res_cache.GetTextureSurface(texture);
|
||||
if (surface != nullptr) {
|
||||
CheckBarrier(state.texture_units[texture_index].texture_2d =
|
||||
surface->texture.handle);
|
||||
} else {
|
||||
// Can occur when texture addr is null or its memory is unmapped/invalid
|
||||
// HACK: In this case, the correct behaviour for the PICA is to use the last
|
||||
// rendered colour. But because this would be impractical to implement, the
|
||||
// next best alternative is to use a clear texture, essentially skipping
|
||||
// the geometry in question.
|
||||
// For example: a bug in Pokemon X/Y causes NULL-texture squares to be drawn
|
||||
// on the male character's face, which in the OpenGL default appear black.
|
||||
state.texture_units[texture_index].texture_2d = default_texture;
|
||||
}
|
||||
} else {
|
||||
state.texture_units[texture_index].texture_2d = 0;
|
||||
}
|
||||
}
|
||||
|
||||
OGLTexture temp_tex;
|
||||
if (need_duplicate_texture) {
|
||||
const auto& tuple = GetFormatTuple(color_surface->pixel_format);
|
||||
const GLsizei levels = color_surface->max_level + 1;
|
||||
|
||||
// The game is trying to use a surface as a texture and framebuffer at the same time
|
||||
// which causes unpredictable behavior on the host.
|
||||
// Making a copy to sample from eliminates this issue and seems to be fairly cheap.
|
||||
temp_tex.Create();
|
||||
temp_tex.Allocate(GL_TEXTURE_2D, levels, tuple.internal_format,
|
||||
color_surface->GetScaledWidth(), color_surface->GetScaledHeight());
|
||||
|
||||
temp_tex.CopyFrom(color_surface->texture, GL_TEXTURE_2D, levels,
|
||||
color_surface->GetScaledWidth(), color_surface->GetScaledHeight());
|
||||
|
||||
for (auto& unit : state.texture_units) {
|
||||
if (unit.texture_2d == color_surface->texture.handle) {
|
||||
unit.texture_2d = temp_tex.handle;
|
||||
}
|
||||
}
|
||||
for (auto shadow_unit : {&state.image_shadow_texture_nx, &state.image_shadow_texture_ny,
|
||||
&state.image_shadow_texture_nz, &state.image_shadow_texture_px,
|
||||
&state.image_shadow_texture_py, &state.image_shadow_texture_pz}) {
|
||||
if (*shadow_unit == color_surface->texture.handle) {
|
||||
*shadow_unit = temp_tex.handle;
|
||||
}
|
||||
}
|
||||
}
|
||||
SyncTextureUnits(framebuffer);
|
||||
|
||||
// Sync and bind the shader
|
||||
if (shader_dirty) {
|
||||
@ -643,17 +469,6 @@ bool RasterizerOpenGL::Draw(bool accelerate, bool is_indexed) {
|
||||
// Sync the uniform data
|
||||
UploadUniforms(accelerate);
|
||||
|
||||
// Viewport can have negative offsets or larger
|
||||
// dimensions than our framebuffer sub-rect.
|
||||
// Enable scissor test to prevent drawing
|
||||
// outside of the framebuffer region
|
||||
state.scissor.enabled = true;
|
||||
state.scissor.x = draw_rect.left;
|
||||
state.scissor.y = draw_rect.bottom;
|
||||
state.scissor.width = draw_rect.GetWidth();
|
||||
state.scissor.height = draw_rect.GetHeight();
|
||||
state.Apply();
|
||||
|
||||
// Draw the vertex batch
|
||||
bool succeeded = true;
|
||||
if (accelerate) {
|
||||
@ -671,12 +486,11 @@ bool RasterizerOpenGL::Draw(bool accelerate, bool is_indexed) {
|
||||
base_vertex += max_vertices) {
|
||||
const std::size_t vertices = std::min(max_vertices, vertex_batch.size() - base_vertex);
|
||||
const std::size_t vertex_size = vertices * sizeof(HardwareVertex);
|
||||
u8* vbo;
|
||||
GLintptr offset;
|
||||
std::tie(vbo, offset, std::ignore) =
|
||||
vertex_buffer.Map(vertex_size, sizeof(HardwareVertex));
|
||||
|
||||
const auto [vbo, offset, _] = vertex_buffer.Map(vertex_size, sizeof(HardwareVertex));
|
||||
std::memcpy(vbo, vertex_batch.data() + base_vertex, vertex_size);
|
||||
vertex_buffer.Unmap(vertex_size);
|
||||
|
||||
glDrawArrays(GL_TRIANGLES, static_cast<GLint>(offset / sizeof(HardwareVertex)),
|
||||
static_cast<GLsizei>(vertices));
|
||||
}
|
||||
@ -684,10 +498,133 @@ bool RasterizerOpenGL::Draw(bool accelerate, bool is_indexed) {
|
||||
|
||||
vertex_batch.clear();
|
||||
|
||||
// Reset textures in rasterizer state context because the rasterizer cache might delete them
|
||||
for (unsigned texture_index = 0; texture_index < pica_textures.size(); ++texture_index) {
|
||||
state.texture_units[texture_index].texture_2d = 0;
|
||||
if (shadow_rendering) {
|
||||
glMemoryBarrier(GL_TEXTURE_FETCH_BARRIER_BIT | GL_SHADER_IMAGE_ACCESS_BARRIER_BIT |
|
||||
GL_TEXTURE_UPDATE_BARRIER_BIT | GL_FRAMEBUFFER_BARRIER_BIT);
|
||||
}
|
||||
|
||||
res_cache.InvalidateFramebuffer(framebuffer);
|
||||
|
||||
return succeeded;
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SyncTextureUnits(const Framebuffer& framebuffer) {
|
||||
using TextureType = Pica::TexturingRegs::TextureConfig::TextureType;
|
||||
|
||||
const auto pica_textures = regs.texturing.GetTextures();
|
||||
for (u32 texture_index = 0; texture_index < pica_textures.size(); ++texture_index) {
|
||||
const auto& texture = pica_textures[texture_index];
|
||||
|
||||
// If the texture unit is disabled unbind the corresponding gl unit
|
||||
if (!texture.enabled) {
|
||||
state.texture_units[texture_index].texture_2d = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle special tex0 configurations
|
||||
if (texture_index == 0) {
|
||||
switch (texture.config.type.Value()) {
|
||||
case TextureType::Shadow2D: {
|
||||
auto surface = res_cache.GetTextureSurface(texture);
|
||||
state.image_shadow_texture_px = surface->Handle();
|
||||
continue;
|
||||
}
|
||||
case TextureType::ShadowCube: {
|
||||
BindShadowCube(texture);
|
||||
continue;
|
||||
}
|
||||
case TextureType::TextureCube: {
|
||||
BindTextureCube(texture);
|
||||
continue;
|
||||
}
|
||||
default:
|
||||
UnbindSpecial();
|
||||
}
|
||||
}
|
||||
|
||||
// Sync texture unit sampler
|
||||
texture_samplers[texture_index].SyncWithConfig(texture.config);
|
||||
|
||||
// Bind the texture provided by the rasterizer cache
|
||||
auto surface = res_cache.GetTextureSurface(texture);
|
||||
if (!surface) {
|
||||
// Can occur when texture addr is null or its memory is unmapped/invalid
|
||||
// HACK: In this case, the correct behaviour for the PICA is to use the last
|
||||
// rendered colour. But because this would be impractical to implement, the
|
||||
// next best alternative is to use a clear texture, essentially skipping
|
||||
// the geometry in question.
|
||||
// For example: a bug in Pokemon X/Y causes NULL-texture squares to be drawn
|
||||
// on the male character's face, which in the OpenGL default appear black.
|
||||
state.texture_units[texture_index].texture_2d = default_texture;
|
||||
} else if (!IsFeedbackLoop(texture_index, framebuffer, *surface)) {
|
||||
state.texture_units[texture_index].texture_2d = surface->Handle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::BindShadowCube(const Pica::TexturingRegs::FullTextureConfig& texture) {
|
||||
using CubeFace = Pica::TexturingRegs::CubeFace;
|
||||
auto info = Pica::Texture::TextureInfo::FromPicaRegister(texture.config, texture.format);
|
||||
constexpr std::array faces = {
|
||||
CubeFace::PositiveX, CubeFace::NegativeX, CubeFace::PositiveY,
|
||||
CubeFace::NegativeY, CubeFace::PositiveZ, CubeFace::NegativeZ,
|
||||
};
|
||||
|
||||
for (CubeFace face : faces) {
|
||||
const u32 binding = static_cast<u32>(face);
|
||||
info.physical_address = regs.texturing.GetCubePhysicalAddress(face);
|
||||
|
||||
auto surface = res_cache.GetTextureSurface(info);
|
||||
state.image_shadow_texture[binding] = surface->Handle();
|
||||
}
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::BindTextureCube(const Pica::TexturingRegs::FullTextureConfig& texture) {
|
||||
using CubeFace = Pica::TexturingRegs::CubeFace;
|
||||
const VideoCore::TextureCubeConfig config = {
|
||||
.px = regs.texturing.GetCubePhysicalAddress(CubeFace::PositiveX),
|
||||
.nx = regs.texturing.GetCubePhysicalAddress(CubeFace::NegativeX),
|
||||
.py = regs.texturing.GetCubePhysicalAddress(CubeFace::PositiveY),
|
||||
.ny = regs.texturing.GetCubePhysicalAddress(CubeFace::NegativeY),
|
||||
.pz = regs.texturing.GetCubePhysicalAddress(CubeFace::PositiveZ),
|
||||
.nz = regs.texturing.GetCubePhysicalAddress(CubeFace::NegativeZ),
|
||||
.width = texture.config.width,
|
||||
.levels = texture.config.lod.max_level + 1,
|
||||
.format = texture.format,
|
||||
};
|
||||
|
||||
auto surface = res_cache.GetTextureCube(config);
|
||||
texture_cube_sampler.SyncWithConfig(texture.config);
|
||||
|
||||
state.texture_cube_unit.texture_cube = surface->Handle();
|
||||
state.texture_units[0].texture_2d = 0;
|
||||
}
|
||||
|
||||
bool RasterizerOpenGL::IsFeedbackLoop(u32 texture_index, const Framebuffer& framebuffer,
|
||||
Surface& surface) {
|
||||
const GLuint color_attachment = framebuffer.Attachment(SurfaceType::Color);
|
||||
const bool is_feedback_loop = color_attachment == surface.Handle();
|
||||
if (!is_feedback_loop) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make a temporary copy of the framebuffer to sample from
|
||||
Surface temp_surface{runtime, framebuffer.ColorParams()};
|
||||
const VideoCore::TextureCopy copy = {
|
||||
.src_level = 0,
|
||||
.dst_level = 0,
|
||||
.src_layer = 0,
|
||||
.dst_layer = 0,
|
||||
.src_offset = {0, 0},
|
||||
.dst_offset = {0, 0},
|
||||
.extent = {temp_surface.GetScaledWidth(), temp_surface.GetScaledHeight()},
|
||||
};
|
||||
runtime.CopyTextures(surface, temp_surface, copy);
|
||||
state.texture_units[texture_index].texture_2d = temp_surface.Handle();
|
||||
return true;
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::UnbindSpecial() {
|
||||
state.texture_cube_unit.texture_cube = 0;
|
||||
state.image_shadow_texture_px = 0;
|
||||
state.image_shadow_texture_nx = 0;
|
||||
@ -697,29 +634,6 @@ bool RasterizerOpenGL::Draw(bool accelerate, bool is_indexed) {
|
||||
state.image_shadow_texture_nz = 0;
|
||||
state.image_shadow_buffer = 0;
|
||||
state.Apply();
|
||||
|
||||
if (shadow_rendering) {
|
||||
glMemoryBarrier(GL_TEXTURE_FETCH_BARRIER_BIT | GL_SHADER_IMAGE_ACCESS_BARRIER_BIT |
|
||||
GL_TEXTURE_UPDATE_BARRIER_BIT | GL_FRAMEBUFFER_BARRIER_BIT);
|
||||
}
|
||||
|
||||
// Mark framebuffer surfaces as dirty
|
||||
Common::Rectangle<u32> draw_rect_unscaled{draw_rect.left / res_scale, draw_rect.top / res_scale,
|
||||
draw_rect.right / res_scale,
|
||||
draw_rect.bottom / res_scale};
|
||||
|
||||
if (color_surface != nullptr && write_color_fb) {
|
||||
auto interval = color_surface->GetSubRectInterval(draw_rect_unscaled);
|
||||
res_cache.InvalidateRegion(boost::icl::first(interval), boost::icl::length(interval),
|
||||
color_surface);
|
||||
}
|
||||
if (depth_surface != nullptr && write_depth_fb) {
|
||||
auto interval = depth_surface->GetSubRectInterval(draw_rect_unscaled);
|
||||
res_cache.InvalidateRegion(boost::icl::first(interval), boost::icl::length(interval),
|
||||
depth_surface);
|
||||
}
|
||||
|
||||
return succeeded;
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::NotifyFixedFunctionPicaRegisterChanged(u32 id) {
|
||||
@ -815,148 +729,15 @@ void RasterizerOpenGL::ClearAll(bool flush) {
|
||||
}
|
||||
|
||||
bool RasterizerOpenGL::AccelerateDisplayTransfer(const GPU::Regs::DisplayTransferConfig& config) {
|
||||
MICROPROFILE_SCOPE(OpenGL_Blits);
|
||||
|
||||
SurfaceParams src_params;
|
||||
src_params.addr = config.GetPhysicalInputAddress();
|
||||
src_params.width = config.output_width;
|
||||
src_params.stride = config.input_width;
|
||||
src_params.height = config.output_height;
|
||||
src_params.is_tiled = !config.input_linear;
|
||||
src_params.pixel_format = PixelFormatFromGPUPixelFormat(config.input_format);
|
||||
src_params.UpdateParams();
|
||||
|
||||
SurfaceParams dst_params;
|
||||
dst_params.addr = config.GetPhysicalOutputAddress();
|
||||
dst_params.width = config.scaling != config.NoScale ? config.output_width.Value() / 2
|
||||
: config.output_width.Value();
|
||||
dst_params.height = config.scaling == config.ScaleXY ? config.output_height.Value() / 2
|
||||
: config.output_height.Value();
|
||||
dst_params.is_tiled = config.input_linear != config.dont_swizzle;
|
||||
dst_params.pixel_format = PixelFormatFromGPUPixelFormat(config.output_format);
|
||||
dst_params.UpdateParams();
|
||||
|
||||
Common::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;
|
||||
|
||||
dst_params.res_scale = src_surface->res_scale;
|
||||
|
||||
Common::Rectangle<u32> dst_rect;
|
||||
Surface dst_surface;
|
||||
std::tie(dst_surface, dst_rect) =
|
||||
res_cache.GetSurfaceSubRect(dst_params, ScaleMatch::Upscale, false);
|
||||
if (dst_surface == nullptr)
|
||||
return false;
|
||||
|
||||
if (src_surface->is_tiled != dst_surface->is_tiled)
|
||||
std::swap(src_rect.top, src_rect.bottom);
|
||||
|
||||
if (config.flip_vertically)
|
||||
std::swap(src_rect.top, src_rect.bottom);
|
||||
|
||||
if (!res_cache.BlitSurfaces(src_surface, src_rect, dst_surface, dst_rect))
|
||||
return false;
|
||||
|
||||
res_cache.InvalidateRegion(dst_params.addr, dst_params.size, dst_surface);
|
||||
return true;
|
||||
return res_cache.AccelerateDisplayTransfer(config);
|
||||
}
|
||||
|
||||
bool RasterizerOpenGL::AccelerateTextureCopy(const GPU::Regs::DisplayTransferConfig& config) {
|
||||
u32 copy_size = Common::AlignDown(config.texture_copy.size, 16);
|
||||
if (copy_size == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
u32 input_gap = config.texture_copy.input_gap * 16;
|
||||
u32 input_width = config.texture_copy.input_width * 16;
|
||||
if (input_width == 0 && input_gap != 0) {
|
||||
return false;
|
||||
}
|
||||
if (input_gap == 0 || input_width >= copy_size) {
|
||||
input_width = copy_size;
|
||||
input_gap = 0;
|
||||
}
|
||||
if (copy_size % input_width != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
u32 output_gap = config.texture_copy.output_gap * 16;
|
||||
u32 output_width = config.texture_copy.output_width * 16;
|
||||
if (output_width == 0 && output_gap != 0) {
|
||||
return false;
|
||||
}
|
||||
if (output_gap == 0 || output_width >= copy_size) {
|
||||
output_width = copy_size;
|
||||
output_gap = 0;
|
||||
}
|
||||
if (copy_size % output_width != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SurfaceParams src_params;
|
||||
src_params.addr = config.GetPhysicalInputAddress();
|
||||
src_params.stride = input_width + input_gap; // stride in bytes
|
||||
src_params.width = input_width; // width in bytes
|
||||
src_params.height = copy_size / input_width;
|
||||
src_params.size = ((src_params.height - 1) * src_params.stride) + src_params.width;
|
||||
src_params.end = src_params.addr + src_params.size;
|
||||
|
||||
Common::Rectangle<u32> src_rect;
|
||||
Surface src_surface;
|
||||
std::tie(src_surface, src_rect) = res_cache.GetTexCopySurface(src_params);
|
||||
if (src_surface == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (output_gap != 0 &&
|
||||
(output_width != src_surface->BytesInPixels(src_rect.GetWidth() / src_surface->res_scale) *
|
||||
(src_surface->is_tiled ? 8 : 1) ||
|
||||
output_gap % src_surface->BytesInPixels(src_surface->is_tiled ? 64 : 1) != 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SurfaceParams dst_params = *src_surface;
|
||||
dst_params.addr = config.GetPhysicalOutputAddress();
|
||||
dst_params.width = src_rect.GetWidth() / src_surface->res_scale;
|
||||
dst_params.stride = dst_params.width + src_surface->PixelsInBytes(
|
||||
src_surface->is_tiled ? output_gap / 8 : output_gap);
|
||||
dst_params.height = src_rect.GetHeight() / src_surface->res_scale;
|
||||
dst_params.res_scale = src_surface->res_scale;
|
||||
dst_params.UpdateParams();
|
||||
|
||||
// Since we are going to invalidate the gap if there is one, we will have to load it first
|
||||
const bool load_gap = output_gap != 0;
|
||||
Common::Rectangle<u32> dst_rect;
|
||||
Surface dst_surface;
|
||||
std::tie(dst_surface, dst_rect) =
|
||||
res_cache.GetSurfaceSubRect(dst_params, ScaleMatch::Upscale, load_gap);
|
||||
if (dst_surface == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dst_surface->type == SurfaceType::Texture) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!res_cache.BlitSurfaces(src_surface, src_rect, dst_surface, dst_rect)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
res_cache.InvalidateRegion(dst_params.addr, dst_params.size, dst_surface);
|
||||
return true;
|
||||
return res_cache.AccelerateTextureCopy(config);
|
||||
}
|
||||
|
||||
bool RasterizerOpenGL::AccelerateFill(const GPU::Regs::MemoryFillConfig& config) {
|
||||
Surface dst_surface = res_cache.GetFillSurface(config);
|
||||
if (dst_surface == nullptr)
|
||||
return false;
|
||||
|
||||
res_cache.InvalidateRegion(dst_surface->addr, dst_surface->size, dst_surface);
|
||||
return true;
|
||||
return res_cache.AccelerateFill(config);
|
||||
}
|
||||
|
||||
bool RasterizerOpenGL::AccelerateDisplay(const GPU::Regs::FramebufferConfig& config,
|
||||
@ -967,32 +748,30 @@ bool RasterizerOpenGL::AccelerateDisplay(const GPU::Regs::FramebufferConfig& con
|
||||
}
|
||||
MICROPROFILE_SCOPE(OpenGL_CacheManagement);
|
||||
|
||||
SurfaceParams src_params;
|
||||
VideoCore::SurfaceParams src_params;
|
||||
src_params.addr = framebuffer_addr;
|
||||
src_params.width = std::min(config.width.Value(), pixel_stride);
|
||||
src_params.height = config.height;
|
||||
src_params.stride = pixel_stride;
|
||||
src_params.is_tiled = false;
|
||||
src_params.pixel_format = PixelFormatFromGPUPixelFormat(config.color_format);
|
||||
src_params.pixel_format = VideoCore::PixelFormatFromGPUPixelFormat(config.color_format);
|
||||
src_params.UpdateParams();
|
||||
|
||||
Common::Rectangle<u32> src_rect;
|
||||
Surface src_surface;
|
||||
std::tie(src_surface, src_rect) =
|
||||
res_cache.GetSurfaceSubRect(src_params, ScaleMatch::Ignore, true);
|
||||
auto [src_surface, src_rect] =
|
||||
res_cache.GetSurfaceSubRect(src_params, VideoCore::ScaleMatch::Ignore, true);
|
||||
|
||||
if (src_surface == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
u32 scaled_width = src_surface->GetScaledWidth();
|
||||
u32 scaled_height = src_surface->GetScaledHeight();
|
||||
const u32 scaled_width = src_surface->GetScaledWidth();
|
||||
const u32 scaled_height = src_surface->GetScaledHeight();
|
||||
|
||||
screen_info.display_texcoords = Common::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;
|
||||
screen_info.display_texture = src_surface->Handle();
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -1003,7 +782,6 @@ void RasterizerOpenGL::SamplerInfo::Create() {
|
||||
wrap_s = wrap_t = TextureConfig::Repeat;
|
||||
border_color = 0;
|
||||
lod_min = lod_max = 0;
|
||||
lod_bias = 0;
|
||||
|
||||
// default is 1000 and -1000
|
||||
// Other attributes have correct defaults
|
||||
@ -1021,22 +799,11 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(
|
||||
glSamplerParameteri(s, GL_TEXTURE_MAG_FILTER, PicaToGL::TextureMagFilterMode(mag_filter));
|
||||
}
|
||||
|
||||
// TODO(wwylele): remove new_supress_mipmap_for_cube logic once mipmap for cube is implemented
|
||||
bool new_supress_mipmap_for_cube =
|
||||
config.type == Pica::TexturingRegs::TextureConfig::TextureCube;
|
||||
if (min_filter != config.min_filter || mip_filter != config.mip_filter ||
|
||||
supress_mipmap_for_cube != new_supress_mipmap_for_cube) {
|
||||
if (min_filter != config.min_filter || mip_filter != config.mip_filter) {
|
||||
min_filter = config.min_filter;
|
||||
mip_filter = config.mip_filter;
|
||||
supress_mipmap_for_cube = new_supress_mipmap_for_cube;
|
||||
if (new_supress_mipmap_for_cube) {
|
||||
// HACK: use mag filter converter for min filter because they are the same anyway
|
||||
glSamplerParameteri(s, GL_TEXTURE_MIN_FILTER,
|
||||
PicaToGL::TextureMagFilterMode(min_filter));
|
||||
} else {
|
||||
glSamplerParameteri(s, GL_TEXTURE_MIN_FILTER,
|
||||
PicaToGL::TextureMinFilterMode(min_filter, mip_filter));
|
||||
}
|
||||
glSamplerParameteri(s, GL_TEXTURE_MIN_FILTER,
|
||||
PicaToGL::TextureMinFilterMode(min_filter, mip_filter));
|
||||
}
|
||||
|
||||
if (wrap_s != config.wrap_s) {
|
||||
@ -1065,11 +832,6 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(
|
||||
lod_max = config.lod.max_level;
|
||||
glSamplerParameterf(s, GL_TEXTURE_MAX_LOD, static_cast<float>(lod_max));
|
||||
}
|
||||
|
||||
if (!GLES && lod_bias != config.lod.bias) {
|
||||
lod_bias = config.lod.bias;
|
||||
glSamplerParameterf(s, GL_TEXTURE_LOD_BIAS, lod_bias / 256.0f);
|
||||
}
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SetShader() {
|
||||
|
@ -12,7 +12,7 @@
|
||||
#include "video_core/renderer_opengl/gl_shader_manager.h"
|
||||
#include "video_core/renderer_opengl/gl_state.h"
|
||||
#include "video_core/renderer_opengl/gl_stream_buffer.h"
|
||||
#include "video_core/shader/shader.h"
|
||||
#include "video_core/renderer_opengl/gl_texture_runtime.h"
|
||||
|
||||
namespace VideoCore {
|
||||
class RendererBase;
|
||||
@ -69,10 +69,6 @@ private:
|
||||
u32 border_color;
|
||||
u32 lod_min;
|
||||
u32 lod_max;
|
||||
s32 lod_bias;
|
||||
|
||||
// TODO(wwylele): remove this once mipmap for cube is implemented
|
||||
bool supress_mipmap_for_cube = false;
|
||||
};
|
||||
|
||||
/// Syncs the clip enabled status to match the PICA register
|
||||
@ -115,6 +111,21 @@ private:
|
||||
void SyncAndUploadLUTs();
|
||||
void SyncAndUploadLUTsLF();
|
||||
|
||||
/// Syncs all enabled PICA texture units
|
||||
void SyncTextureUnits(const Framebuffer& framebuffer);
|
||||
|
||||
/// Binds the PICA shadow cube required for shadow mapping
|
||||
void BindShadowCube(const Pica::TexturingRegs::FullTextureConfig& texture);
|
||||
|
||||
/// Binds a texture cube to texture unit 0
|
||||
void BindTextureCube(const Pica::TexturingRegs::FullTextureConfig& texture);
|
||||
|
||||
/// Makes a temporary copy of the framebuffer if a feedback loop is detected
|
||||
bool IsFeedbackLoop(u32 texture_index, const Framebuffer& framebuffer, Surface& surface);
|
||||
|
||||
/// Unbinds all special texture unit 0 texture configurations
|
||||
void UnbindSpecial();
|
||||
|
||||
/// Upload the uniform blocks to the uniform buffer object
|
||||
void UploadUniforms(bool accelerate_draw);
|
||||
|
||||
@ -138,7 +149,8 @@ private:
|
||||
Driver& driver;
|
||||
OpenGLState state;
|
||||
GLuint default_texture;
|
||||
RasterizerCacheOpenGL res_cache;
|
||||
TextureRuntime runtime;
|
||||
VideoCore::RasterizerCache res_cache;
|
||||
std::unique_ptr<ShaderProgramManager> shader_program_manager;
|
||||
|
||||
OGLVertexArray sw_vao; // VAO for software shader draw
|
||||
@ -146,6 +158,7 @@ private:
|
||||
std::array<bool, 16> hw_vao_enabled_attributes{};
|
||||
|
||||
std::array<SamplerInfo, 3> texture_samplers;
|
||||
GLsizeiptr texture_buffer_size;
|
||||
OGLStreamBuffer vertex_buffer;
|
||||
OGLStreamBuffer uniform_buffer;
|
||||
OGLStreamBuffer index_buffer;
|
||||
|
@ -83,20 +83,6 @@ void OGLTexture::Allocate(GLenum target, GLsizei levels, GLenum internalformat,
|
||||
glBindTexture(GL_TEXTURE_2D, old_tex);
|
||||
}
|
||||
|
||||
void OGLTexture::CopyFrom(const OGLTexture& other, GLenum target, GLsizei levels, GLsizei width,
|
||||
GLsizei height) {
|
||||
GLuint old_tex = OpenGLState::GetCurState().texture_units[0].texture_2d;
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, handle);
|
||||
|
||||
for (GLsizei level = 0; level < levels; level++) {
|
||||
glCopyImageSubData(other.handle, target, level, 0, 0, 0, handle, target, level, 0, 0, 0,
|
||||
width >> level, height >> level, 1);
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, old_tex);
|
||||
}
|
||||
|
||||
void OGLSampler::Create() {
|
||||
if (handle != 0) {
|
||||
return;
|
||||
@ -117,10 +103,10 @@ void OGLSampler::Release() {
|
||||
handle = 0;
|
||||
}
|
||||
|
||||
void OGLShader::Create(const char* source, GLenum type) {
|
||||
void OGLShader::Create(std::string_view source, GLenum type) {
|
||||
if (handle != 0)
|
||||
return;
|
||||
if (source == nullptr)
|
||||
if (source.empty())
|
||||
return;
|
||||
|
||||
MICROPROFILE_SCOPE(OpenGL_ResourceCreation);
|
||||
@ -144,7 +130,7 @@ void OGLProgram::Create(bool separable_program, const std::vector<GLuint>& shade
|
||||
handle = LoadProgram(separable_program, shaders);
|
||||
}
|
||||
|
||||
void OGLProgram::Create(const char* vert_shader, const char* frag_shader) {
|
||||
void OGLProgram::Create(std::string_view vert_shader, std::string_view frag_shader) {
|
||||
OGLShader vert, frag;
|
||||
vert.Create(vert_shader, GL_VERTEX_SHADER);
|
||||
frag.Create(frag_shader, GL_FRAGMENT_SHADER);
|
||||
|
@ -61,9 +61,6 @@ public:
|
||||
void Allocate(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width,
|
||||
GLsizei height = 1, GLsizei depth = 1);
|
||||
|
||||
void CopyFrom(const OGLTexture& other, GLenum target, GLsizei levels, GLsizei width,
|
||||
GLsizei height);
|
||||
|
||||
GLuint handle = 0;
|
||||
};
|
||||
|
||||
@ -108,7 +105,7 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Create(const char* source, GLenum type);
|
||||
void Create(std::string_view source, GLenum type);
|
||||
|
||||
void Release();
|
||||
|
||||
@ -135,7 +132,7 @@ public:
|
||||
void Create(bool separable_program, const std::vector<GLuint>& shaders);
|
||||
|
||||
/// Creates a new program from given shader soruce code
|
||||
void Create(const char* vert_shader, const char* frag_shader);
|
||||
void Create(std::string_view vert_shader, std::string_view frag_shader);
|
||||
|
||||
/// Deletes the internal OpenGL resource
|
||||
void Release();
|
||||
|
@ -262,7 +262,8 @@ static std::string SampleTexture(const PicaFSConfig& config, unsigned texture_un
|
||||
// Only unit 0 respects the texturing type
|
||||
switch (state.texture0_type) {
|
||||
case TexturingRegs::TextureConfig::Texture2D:
|
||||
return "textureLod(tex0, texcoord0, getLod(texcoord0 * vec2(textureSize(tex0, 0))))";
|
||||
return "textureLod(tex0, texcoord0, getLod(texcoord0 * vec2(textureSize(tex0, 0))) + "
|
||||
"tex_lod_bias[0])";
|
||||
case TexturingRegs::TextureConfig::Projection2D:
|
||||
// TODO (wwylele): find the exact LOD formula for projection texture
|
||||
return "textureProj(tex0, vec3(texcoord0, texcoord0_w))";
|
||||
@ -280,12 +281,15 @@ static std::string SampleTexture(const PicaFSConfig& config, unsigned texture_un
|
||||
return "texture(tex0, texcoord0)";
|
||||
}
|
||||
case 1:
|
||||
return "textureLod(tex1, texcoord1, getLod(texcoord1 * vec2(textureSize(tex1, 0))))";
|
||||
return "textureLod(tex1, texcoord1, getLod(texcoord1 * vec2(textureSize(tex1, 0))) + "
|
||||
"tex_lod_bias[1])";
|
||||
case 2:
|
||||
if (state.texture2_use_coord1)
|
||||
return "textureLod(tex2, texcoord1, getLod(texcoord1 * vec2(textureSize(tex2, 0))))";
|
||||
return "textureLod(tex2, texcoord1, getLod(texcoord1 * vec2(textureSize(tex2, 0))) + "
|
||||
"tex_lod_bias[2])";
|
||||
else
|
||||
return "textureLod(tex2, texcoord2, getLod(texcoord2 * vec2(textureSize(tex2, 0))))";
|
||||
return "textureLod(tex2, texcoord2, getLod(texcoord2 * vec2(textureSize(tex2, 0))) + "
|
||||
"tex_lod_bias[2])";
|
||||
case 3:
|
||||
if (state.proctex.enable) {
|
||||
return "ProcTex()";
|
||||
|
@ -13,7 +13,7 @@
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
GLuint LoadShader(const char* source, GLenum type) {
|
||||
GLuint LoadShader(std::string_view source, GLenum type) {
|
||||
const std::string version = GLES ? R"(#version 320 es
|
||||
|
||||
#define CITRA_GLES
|
||||
@ -28,7 +28,7 @@ GLuint LoadShader(const char* source, GLenum type) {
|
||||
)"
|
||||
: "#version 430 core\n";
|
||||
|
||||
const char* debug_type;
|
||||
std::string_view debug_type;
|
||||
switch (type) {
|
||||
case GL_VERTEX_SHADER:
|
||||
debug_type = "vertex";
|
||||
@ -43,9 +43,11 @@ GLuint LoadShader(const char* source, GLenum type) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
std::array<const char*, 2> src_arr{version.data(), source};
|
||||
std::array<const GLchar*, 2> src_arr{version.data(), source.data()};
|
||||
std::array<GLint, 2> lengths{static_cast<GLint>(version.size()),
|
||||
static_cast<GLint>(source.size())};
|
||||
GLuint shader_id = glCreateShader(type);
|
||||
glShaderSource(shader_id, static_cast<GLsizei>(src_arr.size()), src_arr.data(), nullptr);
|
||||
glShaderSource(shader_id, static_cast<GLsizei>(src_arr.size()), src_arr.data(), lengths.data());
|
||||
LOG_DEBUG(Render_OpenGL, "Compiling {} shader...", debug_type);
|
||||
glCompileShader(shader_id);
|
||||
|
||||
|
@ -29,7 +29,7 @@ precision mediump uimage2D;
|
||||
* @param source String of the GLSL shader program
|
||||
* @param type Type of the shader (GL_VERTEX_SHADER, GL_GEOMETRY_SHADER or GL_FRAGMENT_SHADER)
|
||||
*/
|
||||
GLuint LoadShader(const char* source, GLenum type);
|
||||
GLuint LoadShader(std::string_view source, GLenum type);
|
||||
|
||||
/**
|
||||
* Utility function to create and link an OpenGL GLSL shader program
|
||||
|
@ -116,12 +116,17 @@ public:
|
||||
|
||||
// GL_IMAGE_BINDING_NAME
|
||||
GLuint image_shadow_buffer;
|
||||
GLuint image_shadow_texture_px;
|
||||
GLuint image_shadow_texture_nx;
|
||||
GLuint image_shadow_texture_py;
|
||||
GLuint image_shadow_texture_ny;
|
||||
GLuint image_shadow_texture_pz;
|
||||
GLuint image_shadow_texture_nz;
|
||||
union {
|
||||
std::array<GLuint, 6> image_shadow_texture;
|
||||
struct {
|
||||
GLuint image_shadow_texture_px;
|
||||
GLuint image_shadow_texture_nx;
|
||||
GLuint image_shadow_texture_py;
|
||||
GLuint image_shadow_texture_ny;
|
||||
GLuint image_shadow_texture_pz;
|
||||
GLuint image_shadow_texture_nz;
|
||||
};
|
||||
};
|
||||
|
||||
struct {
|
||||
GLuint read_framebuffer; // GL_READ_FRAMEBUFFER_BINDING
|
||||
|
@ -54,7 +54,7 @@ GLsizeiptr OGLStreamBuffer::GetSize() const {
|
||||
}
|
||||
|
||||
std::tuple<u8*, GLintptr, bool> OGLStreamBuffer::Map(GLsizeiptr size, GLintptr alignment) {
|
||||
ASSERT(size <= buffer_size);
|
||||
ASSERT_MSG(size <= buffer_size, "Requested size {} exceeds buffer size {}", size, buffer_size);
|
||||
ASSERT(alignment <= buffer_size);
|
||||
mapped_size = size;
|
||||
|
||||
|
598
src/video_core/renderer_opengl/gl_texture_runtime.cpp
Normal file
598
src/video_core/renderer_opengl/gl_texture_runtime.cpp
Normal file
@ -0,0 +1,598 @@
|
||||
// Copyright 2023 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/scope_exit.h"
|
||||
#include "common/settings.h"
|
||||
#include "video_core/regs.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "video_core/renderer_opengl/gl_driver.h"
|
||||
#include "video_core/renderer_opengl/gl_state.h"
|
||||
#include "video_core/renderer_opengl/gl_texture_runtime.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
namespace {
|
||||
|
||||
using VideoCore::PixelFormat;
|
||||
using VideoCore::SurfaceType;
|
||||
|
||||
constexpr FormatTuple DEFAULT_TUPLE = {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE};
|
||||
|
||||
static constexpr std::array<FormatTuple, 4> DEPTH_TUPLES = {{
|
||||
{GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT}, // D16
|
||||
{},
|
||||
{GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT}, // D24
|
||||
{GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8}, // D24S8
|
||||
}};
|
||||
|
||||
static constexpr std::array<FormatTuple, 5> COLOR_TUPLES = {{
|
||||
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8}, // RGBA8
|
||||
{GL_RGB8, GL_BGR, GL_UNSIGNED_BYTE}, // RGB8
|
||||
{GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1}, // RGB5A1
|
||||
{GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5}, // RGB565
|
||||
{GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4}, // RGBA4
|
||||
}};
|
||||
|
||||
static constexpr std::array<FormatTuple, 5> COLOR_TUPLES_OES = {{
|
||||
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE}, // RGBA8
|
||||
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE}, // RGB8
|
||||
{GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1}, // RGB5A1
|
||||
{GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5}, // RGB565
|
||||
{GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4}, // RGBA4
|
||||
}};
|
||||
|
||||
struct FramebufferInfo {
|
||||
GLuint color;
|
||||
GLuint depth;
|
||||
u32 color_level;
|
||||
u32 depth_level;
|
||||
};
|
||||
|
||||
[[nodiscard]] GLbitfield MakeBufferMask(SurfaceType type) {
|
||||
switch (type) {
|
||||
case SurfaceType::Color:
|
||||
case SurfaceType::Texture:
|
||||
case SurfaceType::Fill:
|
||||
return GL_COLOR_BUFFER_BIT;
|
||||
case SurfaceType::Depth:
|
||||
return GL_DEPTH_BUFFER_BIT;
|
||||
case SurfaceType::DepthStencil:
|
||||
return GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
|
||||
default:
|
||||
UNREACHABLE_MSG("Invalid surface type!");
|
||||
}
|
||||
return GL_COLOR_BUFFER_BIT;
|
||||
}
|
||||
|
||||
[[nodiscard]] u32 FboIndex(VideoCore::SurfaceType type) {
|
||||
switch (type) {
|
||||
case VideoCore::SurfaceType::Color:
|
||||
case VideoCore::SurfaceType::Texture:
|
||||
case VideoCore::SurfaceType::Fill:
|
||||
return 0;
|
||||
case VideoCore::SurfaceType::Depth:
|
||||
return 1;
|
||||
case VideoCore::SurfaceType::DepthStencil:
|
||||
return 2;
|
||||
default:
|
||||
UNREACHABLE_MSG("Invalid surface type!");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] OGLTexture MakeHandle(GLenum target, u32 width, u32 height, u32 levels,
|
||||
const FormatTuple& tuple, std::string_view debug_name = "") {
|
||||
OGLTexture texture{};
|
||||
texture.Create();
|
||||
|
||||
glBindTexture(target, texture.handle);
|
||||
glTexStorage2D(target, levels, tuple.internal_format, width, height);
|
||||
|
||||
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
if (!debug_name.empty()) {
|
||||
glObjectLabel(GL_TEXTURE, texture.handle, -1, debug_name.data());
|
||||
}
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
TextureRuntime::TextureRuntime(const Driver& driver_, VideoCore::RendererBase& renderer)
|
||||
: driver{driver_}, blit_helper{*this} {
|
||||
for (std::size_t i = 0; i < draw_fbos.size(); ++i) {
|
||||
draw_fbos[i].Create();
|
||||
read_fbos[i].Create();
|
||||
}
|
||||
|
||||
auto Register = [this](PixelFormat dest, std::unique_ptr<FormatReinterpreterBase>&& obj) {
|
||||
const u32 dst_index = static_cast<u32>(dest);
|
||||
return reinterpreters[dst_index].push_back(std::move(obj));
|
||||
};
|
||||
|
||||
Register(PixelFormat::RGBA8, std::make_unique<ShaderD24S8toRGBA8>());
|
||||
Register(PixelFormat::RGB5A1, std::make_unique<RGBA4toRGB5A1>());
|
||||
}
|
||||
|
||||
TextureRuntime::~TextureRuntime() = default;
|
||||
|
||||
bool TextureRuntime::NeedsConversion(VideoCore::PixelFormat pixel_format) const {
|
||||
const bool should_convert = pixel_format == PixelFormat::RGBA8 || // Needs byteswap
|
||||
pixel_format == PixelFormat::RGB8; // Is converted to RGBA8
|
||||
return driver.IsOpenGLES() && should_convert;
|
||||
}
|
||||
|
||||
VideoCore::StagingData TextureRuntime::FindStaging(u32 size, bool upload) {
|
||||
if (size > staging_buffer.size()) {
|
||||
staging_buffer.resize(size);
|
||||
}
|
||||
return VideoCore::StagingData{
|
||||
.size = size,
|
||||
.mapped = std::span{staging_buffer.data(), size},
|
||||
.buffer_offset = 0,
|
||||
};
|
||||
}
|
||||
|
||||
const FormatTuple& TextureRuntime::GetFormatTuple(PixelFormat pixel_format) const {
|
||||
const auto type = GetFormatType(pixel_format);
|
||||
const std::size_t format_index = static_cast<std::size_t>(pixel_format);
|
||||
|
||||
if (type == SurfaceType::Color) {
|
||||
ASSERT(format_index < COLOR_TUPLES.size());
|
||||
return (driver.IsOpenGLES() ? COLOR_TUPLES_OES : COLOR_TUPLES)[format_index];
|
||||
} else if (type == SurfaceType::Depth || type == SurfaceType::DepthStencil) {
|
||||
const std::size_t tuple_idx = format_index - 14;
|
||||
ASSERT(tuple_idx < DEPTH_TUPLES.size());
|
||||
return DEPTH_TUPLES[tuple_idx];
|
||||
}
|
||||
|
||||
return DEFAULT_TUPLE;
|
||||
}
|
||||
|
||||
void TextureRuntime::Recycle(const HostTextureTag tag, Allocation&& alloc) {
|
||||
recycler.emplace(tag, std::move(alloc));
|
||||
}
|
||||
|
||||
Allocation TextureRuntime::Allocate(const VideoCore::SurfaceParams& params) {
|
||||
const u32 width = params.width;
|
||||
const u32 height = params.height;
|
||||
const u32 levels = params.levels;
|
||||
const GLenum target = params.texture_type == VideoCore::TextureType::CubeMap
|
||||
? GL_TEXTURE_CUBE_MAP
|
||||
: GL_TEXTURE_2D;
|
||||
const auto& tuple = GetFormatTuple(params.pixel_format);
|
||||
|
||||
const HostTextureTag key = {
|
||||
.tuple = tuple,
|
||||
.type = params.texture_type,
|
||||
.width = width,
|
||||
.height = height,
|
||||
.levels = levels,
|
||||
.res_scale = params.res_scale,
|
||||
};
|
||||
|
||||
if (auto it = recycler.find(key); it != recycler.end()) {
|
||||
Allocation alloc = std::move(it->second);
|
||||
ASSERT(alloc.res_scale == params.res_scale);
|
||||
recycler.erase(it);
|
||||
return alloc;
|
||||
}
|
||||
|
||||
const GLuint old_tex = OpenGLState::GetCurState().texture_units[0].texture_2d;
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
|
||||
std::array<OGLTexture, 2> textures{};
|
||||
std::array<GLuint, 2> handles{};
|
||||
|
||||
textures[0] = MakeHandle(target, width, height, levels, tuple, params.DebugName(false));
|
||||
handles.fill(textures[0].handle);
|
||||
|
||||
if (params.res_scale != 1) {
|
||||
const u32 scaled_width = params.GetScaledWidth();
|
||||
const u32 scaled_height = params.GetScaledHeight();
|
||||
textures[1] =
|
||||
MakeHandle(target, scaled_width, scaled_height, levels, tuple, params.DebugName(true));
|
||||
handles[1] = textures[1].handle;
|
||||
}
|
||||
|
||||
glBindTexture(target, old_tex);
|
||||
|
||||
return Allocation{
|
||||
.textures = std::move(textures),
|
||||
.handles = std::move(handles),
|
||||
.tuple = tuple,
|
||||
.width = params.width,
|
||||
.height = params.height,
|
||||
.levels = params.levels,
|
||||
.res_scale = params.res_scale,
|
||||
};
|
||||
}
|
||||
|
||||
bool TextureRuntime::ClearTexture(Surface& surface, const VideoCore::TextureClear& clear) {
|
||||
const auto prev_state = OpenGLState::GetCurState();
|
||||
|
||||
// Setup scissor rectangle according to the clear rectangle
|
||||
OpenGLState state;
|
||||
state.scissor.enabled = true;
|
||||
state.scissor.x = clear.texture_rect.left;
|
||||
state.scissor.y = clear.texture_rect.bottom;
|
||||
state.scissor.width = clear.texture_rect.GetWidth();
|
||||
state.scissor.height = clear.texture_rect.GetHeight();
|
||||
state.draw.draw_framebuffer = draw_fbos[FboIndex(surface.type)].handle;
|
||||
state.Apply();
|
||||
|
||||
switch (surface.type) {
|
||||
case SurfaceType::Color:
|
||||
case SurfaceType::Texture:
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
||||
surface.Handle(), clear.texture_level);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0,
|
||||
0);
|
||||
|
||||
state.color_mask.red_enabled = true;
|
||||
state.color_mask.green_enabled = true;
|
||||
state.color_mask.blue_enabled = true;
|
||||
state.color_mask.alpha_enabled = true;
|
||||
state.Apply();
|
||||
|
||||
glClearBufferfv(GL_COLOR, 0, clear.value.color.AsArray());
|
||||
break;
|
||||
case SurfaceType::Depth:
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D,
|
||||
surface.Handle(), clear.texture_level);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
|
||||
|
||||
state.depth.write_mask = GL_TRUE;
|
||||
state.Apply();
|
||||
|
||||
glClearBufferfv(GL_DEPTH, 0, &clear.value.depth);
|
||||
break;
|
||||
case SurfaceType::DepthStencil:
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
|
||||
surface.Handle(), clear.texture_level);
|
||||
|
||||
state.depth.write_mask = GL_TRUE;
|
||||
state.stencil.write_mask = -1;
|
||||
state.Apply();
|
||||
|
||||
glClearBufferfi(GL_DEPTH_STENCIL, 0, clear.value.depth, clear.value.stencil);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE_MSG("Unknown surface type {}", surface.type);
|
||||
return false;
|
||||
}
|
||||
|
||||
prev_state.Apply();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TextureRuntime::CopyTextures(Surface& source, Surface& dest,
|
||||
const VideoCore::TextureCopy& copy) {
|
||||
const GLenum src_textarget = source.texture_type == VideoCore::TextureType::CubeMap
|
||||
? GL_TEXTURE_CUBE_MAP
|
||||
: GL_TEXTURE_2D;
|
||||
const GLenum dest_textarget =
|
||||
dest.texture_type == VideoCore::TextureType::CubeMap ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D;
|
||||
glCopyImageSubData(source.Handle(), src_textarget, copy.src_level, copy.src_offset.x,
|
||||
copy.src_offset.y, copy.src_layer, dest.Handle(), dest_textarget,
|
||||
copy.dst_level, copy.dst_offset.x, copy.dst_offset.y, copy.dst_layer,
|
||||
copy.extent.width, copy.extent.height, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TextureRuntime::BlitTextures(Surface& source, Surface& dest,
|
||||
const VideoCore::TextureBlit& blit) {
|
||||
OpenGLState state = OpenGLState::GetCurState();
|
||||
state.scissor.enabled = false;
|
||||
state.draw.read_framebuffer = read_fbos[FboIndex(source.type)].handle;
|
||||
state.draw.draw_framebuffer = draw_fbos[FboIndex(dest.type)].handle;
|
||||
state.Apply();
|
||||
|
||||
source.Attach(GL_READ_FRAMEBUFFER, blit.src_level, blit.src_layer);
|
||||
dest.Attach(GL_DRAW_FRAMEBUFFER, blit.dst_level, blit.dst_layer);
|
||||
|
||||
// TODO (wwylele): use GL_NEAREST for shadow map texture
|
||||
// Note: shadow map is treated as RGBA8 format in PICA, as well as in the rasterizer cache, but
|
||||
// doing linear intepolation componentwise would cause incorrect value. However, for a
|
||||
// well-programmed game this code path should be rarely executed for shadow map with
|
||||
// inconsistent scale.
|
||||
const GLbitfield buffer_mask = MakeBufferMask(source.type);
|
||||
const GLenum filter = buffer_mask == GL_COLOR_BUFFER_BIT ? GL_LINEAR : GL_NEAREST;
|
||||
glBlitFramebuffer(blit.src_rect.left, blit.src_rect.bottom, blit.src_rect.right,
|
||||
blit.src_rect.top, blit.dst_rect.left, blit.dst_rect.bottom,
|
||||
blit.dst_rect.right, blit.dst_rect.top, buffer_mask, filter);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TextureRuntime::GenerateMipmaps(Surface& surface, u32 max_level) {
|
||||
OpenGLState state = OpenGLState::GetCurState();
|
||||
state.texture_units[0].texture_2d = surface.Handle();
|
||||
state.Apply();
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, max_level);
|
||||
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
}
|
||||
|
||||
const ReinterpreterList& TextureRuntime::GetPossibleReinterpretations(
|
||||
PixelFormat dest_format) const {
|
||||
return reinterpreters[static_cast<u32>(dest_format)];
|
||||
}
|
||||
|
||||
Surface::Surface(TextureRuntime& runtime_, const VideoCore::SurfaceParams& params)
|
||||
: SurfaceBase{params}, driver{&runtime_.GetDriver()}, runtime{&runtime_} {
|
||||
if (pixel_format == PixelFormat::Invalid) {
|
||||
return;
|
||||
}
|
||||
|
||||
alloc = runtime->Allocate(params);
|
||||
}
|
||||
|
||||
Surface::~Surface() {
|
||||
if (pixel_format == PixelFormat::Invalid || !alloc) {
|
||||
return;
|
||||
}
|
||||
|
||||
const HostTextureTag tag = {
|
||||
.tuple = alloc.tuple,
|
||||
.type = texture_type,
|
||||
.width = alloc.width,
|
||||
.height = alloc.height,
|
||||
.levels = alloc.levels,
|
||||
.res_scale = alloc.res_scale,
|
||||
};
|
||||
runtime->Recycle(tag, std::move(alloc));
|
||||
}
|
||||
|
||||
void Surface::Upload(const VideoCore::BufferTextureCopy& upload,
|
||||
const VideoCore::StagingData& staging) {
|
||||
// Ensure no bad interactions with GL_UNPACK_ALIGNMENT
|
||||
ASSERT(stride * GetFormatBytesPerPixel(pixel_format) % 4 == 0);
|
||||
|
||||
const GLuint old_tex = OpenGLState::GetCurState().texture_units[0].texture_2d;
|
||||
const u32 unscaled_width = upload.texture_rect.GetWidth();
|
||||
const u32 unscaled_height = upload.texture_rect.GetHeight();
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, unscaled_width);
|
||||
|
||||
// Bind the unscaled texture
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, Handle(false));
|
||||
|
||||
// Upload the requested rectangle of pixels
|
||||
const auto& tuple = runtime->GetFormatTuple(pixel_format);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, upload.texture_level, upload.texture_rect.left,
|
||||
upload.texture_rect.bottom, unscaled_width, unscaled_height, tuple.format,
|
||||
tuple.type, staging.mapped.data());
|
||||
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
glBindTexture(GL_TEXTURE_2D, old_tex);
|
||||
|
||||
const VideoCore::TextureBlit blit = {
|
||||
.src_level = upload.texture_level,
|
||||
.dst_level = upload.texture_level,
|
||||
.src_rect = upload.texture_rect,
|
||||
.dst_rect = upload.texture_rect * res_scale,
|
||||
};
|
||||
|
||||
// If texture filtering is enabled attempt to upscale with that, otherwise fallback
|
||||
// to normal blit.
|
||||
if (res_scale != 1 && !runtime->blit_helper.Filter(*this, blit)) {
|
||||
BlitScale(blit, true);
|
||||
}
|
||||
}
|
||||
|
||||
void Surface::Download(const VideoCore::BufferTextureCopy& download,
|
||||
const VideoCore::StagingData& staging) {
|
||||
// Ensure no bad interactions with GL_PACK_ALIGNMENT
|
||||
ASSERT(stride * GetFormatBytesPerPixel(pixel_format) % 4 == 0);
|
||||
|
||||
const u32 unscaled_width = download.texture_rect.GetWidth();
|
||||
const u32 unscaled_height = download.texture_rect.GetHeight();
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, unscaled_width);
|
||||
|
||||
// Scale down upscaled data before downloading it
|
||||
if (res_scale != 1) {
|
||||
const VideoCore::TextureBlit blit = {
|
||||
.src_level = download.texture_level,
|
||||
.dst_level = download.texture_level,
|
||||
.src_rect = download.texture_rect * res_scale,
|
||||
.dst_rect = download.texture_rect,
|
||||
};
|
||||
BlitScale(blit, false);
|
||||
}
|
||||
|
||||
// Try to download without using an fbo. This should succeed on recent desktop drivers
|
||||
if (DownloadWithoutFbo(download, staging)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const u32 fbo_index = FboIndex(type);
|
||||
|
||||
OpenGLState state = OpenGLState::GetCurState();
|
||||
state.scissor.enabled = false;
|
||||
state.draw.read_framebuffer = runtime->read_fbos[fbo_index].handle;
|
||||
state.Apply();
|
||||
|
||||
Attach(GL_READ_FRAMEBUFFER, download.texture_level, 0, false);
|
||||
|
||||
// Read the pixel data to the staging buffer
|
||||
const auto& tuple = runtime->GetFormatTuple(pixel_format);
|
||||
glReadPixels(download.texture_rect.left, download.texture_rect.bottom, unscaled_width,
|
||||
unscaled_height, tuple.format, tuple.type, staging.mapped.data());
|
||||
|
||||
// Restore previous state
|
||||
glPixelStorei(GL_PACK_ROW_LENGTH, 0);
|
||||
}
|
||||
|
||||
bool Surface::DownloadWithoutFbo(const VideoCore::BufferTextureCopy& download,
|
||||
const VideoCore::StagingData& staging) {
|
||||
// If a partial download is requested and ARB_get_texture_sub_image (core in 4.5)
|
||||
// is not available we cannot proceed further.
|
||||
const bool is_full_download = download.texture_rect == GetRect();
|
||||
const bool has_sub_image = driver->HasArbGetTextureSubImage();
|
||||
if (driver->IsOpenGLES() || (!is_full_download && !has_sub_image)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const GLuint old_tex = OpenGLState::GetCurState().texture_units[0].texture_2d;
|
||||
const auto& tuple = runtime->GetFormatTuple(pixel_format);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(stride));
|
||||
SCOPE_EXIT({ glPixelStorei(GL_PACK_ROW_LENGTH, 0); });
|
||||
|
||||
// Prefer glGetTextureSubImage in most cases since it's the fastest and most convenient option
|
||||
if (has_sub_image) {
|
||||
const GLsizei buf_size = static_cast<GLsizei>(staging.mapped.size());
|
||||
glGetTextureSubImage(Handle(false), download.texture_level, download.texture_rect.left,
|
||||
download.texture_rect.bottom, 0, download.texture_rect.GetWidth(),
|
||||
download.texture_rect.GetHeight(), 1, tuple.format, tuple.type,
|
||||
buf_size, staging.mapped.data());
|
||||
return true;
|
||||
}
|
||||
|
||||
// This should only trigger for full texture downloads in oldish intel drivers
|
||||
// that only support up to 4.3
|
||||
glBindTexture(GL_TEXTURE_2D, Handle(false));
|
||||
glGetTexImage(GL_TEXTURE_2D, download.texture_level, tuple.format, tuple.type,
|
||||
staging.mapped.data());
|
||||
glBindTexture(GL_TEXTURE_2D, old_tex);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Surface::Attach(GLenum target, u32 level, u32 layer, bool scaled) {
|
||||
const GLuint handle = Handle(scaled);
|
||||
const GLenum textarget = texture_type == VideoCore::TextureType::CubeMap
|
||||
? GL_TEXTURE_CUBE_MAP_POSITIVE_X + layer
|
||||
: GL_TEXTURE_2D;
|
||||
|
||||
switch (type) {
|
||||
case VideoCore::SurfaceType::Color:
|
||||
case VideoCore::SurfaceType::Texture:
|
||||
glFramebufferTexture2D(target, GL_COLOR_ATTACHMENT0, textarget, handle, level);
|
||||
break;
|
||||
case VideoCore::SurfaceType::Depth:
|
||||
glFramebufferTexture2D(target, GL_DEPTH_ATTACHMENT, textarget, handle, level);
|
||||
break;
|
||||
case VideoCore::SurfaceType::DepthStencil:
|
||||
glFramebufferTexture2D(target, GL_DEPTH_STENCIL_ATTACHMENT, textarget, handle, level);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE_MSG("Invalid surface type!");
|
||||
}
|
||||
}
|
||||
|
||||
u32 Surface::GetInternalBytesPerPixel() const {
|
||||
// RGB8 is converted to RGBA8 on OpenGL ES since it doesn't support BGR8
|
||||
if (driver->IsOpenGLES() && pixel_format == VideoCore::PixelFormat::RGB8) {
|
||||
return 4;
|
||||
}
|
||||
return GetFormatBytesPerPixel(pixel_format);
|
||||
}
|
||||
|
||||
void Surface::BlitScale(const VideoCore::TextureBlit& blit, bool up_scale) {
|
||||
const u32 fbo_index = FboIndex(type);
|
||||
|
||||
OpenGLState state = OpenGLState::GetCurState();
|
||||
state.scissor.enabled = false;
|
||||
state.draw.read_framebuffer = runtime->read_fbos[fbo_index].handle;
|
||||
state.draw.draw_framebuffer = runtime->draw_fbos[fbo_index].handle;
|
||||
state.Apply();
|
||||
|
||||
Attach(GL_READ_FRAMEBUFFER, blit.src_level, blit.src_layer, !up_scale);
|
||||
Attach(GL_DRAW_FRAMEBUFFER, blit.dst_level, blit.dst_layer, up_scale);
|
||||
|
||||
const GLenum buffer_mask = MakeBufferMask(type);
|
||||
const GLenum filter = buffer_mask == GL_COLOR_BUFFER_BIT ? GL_LINEAR : GL_NEAREST;
|
||||
glBlitFramebuffer(blit.src_rect.left, blit.src_rect.bottom, blit.src_rect.right,
|
||||
blit.src_rect.top, blit.dst_rect.left, blit.dst_rect.bottom,
|
||||
blit.dst_rect.right, blit.dst_rect.top, buffer_mask, filter);
|
||||
}
|
||||
|
||||
Framebuffer::Framebuffer(TextureRuntime& runtime, Surface* const color, u32 color_level,
|
||||
Surface* const depth_stencil, u32 depth_level, const Pica::Regs& regs,
|
||||
Common::Rectangle<u32> surfaces_rect)
|
||||
: VideoCore::FramebufferBase{regs, color, color_level,
|
||||
depth_stencil, depth_level, surfaces_rect} {
|
||||
|
||||
const bool shadow_rendering = regs.framebuffer.IsShadowRendering();
|
||||
const bool has_stencil = regs.framebuffer.HasStencil();
|
||||
if (shadow_rendering && !color) {
|
||||
return; // Framebuffer won't get used
|
||||
}
|
||||
|
||||
if (color) {
|
||||
attachments[0] = color->Handle();
|
||||
}
|
||||
if (depth_stencil) {
|
||||
attachments[1] = depth_stencil->Handle();
|
||||
}
|
||||
|
||||
const FramebufferInfo info = {
|
||||
.color = attachments[0],
|
||||
.depth = attachments[1],
|
||||
.color_level = color_level,
|
||||
.depth_level = depth_level,
|
||||
};
|
||||
|
||||
const u64 hash = Common::ComputeHash64(&info, sizeof(FramebufferInfo));
|
||||
auto [it, new_framebuffer] = runtime.framebuffer_cache.try_emplace(hash);
|
||||
|
||||
if (!new_framebuffer) {
|
||||
handle = it->second.handle;
|
||||
return;
|
||||
}
|
||||
|
||||
const GLuint old_fbo = OpenGLState::GetCurState().draw.draw_framebuffer;
|
||||
|
||||
OGLFramebuffer& framebuffer = it->second;
|
||||
framebuffer.Create();
|
||||
handle = it->second.handle;
|
||||
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer.handle);
|
||||
SCOPE_EXIT({ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_fbo); });
|
||||
|
||||
if (shadow_rendering) {
|
||||
glFramebufferParameteri(GL_DRAW_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_WIDTH,
|
||||
color->width * res_scale);
|
||||
glFramebufferParameteri(GL_DRAW_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_HEIGHT,
|
||||
color->height * res_scale);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0,
|
||||
0);
|
||||
} else {
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
||||
color ? color->Handle() : 0, color_level);
|
||||
if (depth_stencil) {
|
||||
if (has_stencil) {
|
||||
// Attach both depth and stencil
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
|
||||
GL_TEXTURE_2D, depth_stencil->Handle(), depth_level);
|
||||
} else {
|
||||
// Attach depth
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D,
|
||||
depth_stencil->Handle(), depth_level);
|
||||
// Clear stencil attachment
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0,
|
||||
0);
|
||||
}
|
||||
} else {
|
||||
// Clear both depth and stencil attachment
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
|
||||
0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Framebuffer::~Framebuffer() = default;
|
||||
|
||||
} // namespace OpenGL
|
204
src/video_core/renderer_opengl/gl_texture_runtime.h
Normal file
204
src/video_core/renderer_opengl/gl_texture_runtime.h
Normal file
@ -0,0 +1,204 @@
|
||||
// Copyright 2023 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "video_core/rasterizer_cache/framebuffer_base.h"
|
||||
#include "video_core/rasterizer_cache/surface_base.h"
|
||||
#include "video_core/renderer_opengl/gl_blit_helper.h"
|
||||
#include "video_core/renderer_opengl/gl_format_reinterpreter.h"
|
||||
|
||||
namespace VideoCore {
|
||||
class RendererBase;
|
||||
}
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
struct FormatTuple {
|
||||
GLint internal_format;
|
||||
GLenum format;
|
||||
GLenum type;
|
||||
|
||||
bool operator==(const FormatTuple& other) const noexcept {
|
||||
return std::tie(internal_format, format, type) ==
|
||||
std::tie(other.internal_format, other.format, other.type);
|
||||
}
|
||||
};
|
||||
|
||||
struct HostTextureTag {
|
||||
FormatTuple tuple{};
|
||||
VideoCore::TextureType type{};
|
||||
u32 width = 0;
|
||||
u32 height = 0;
|
||||
u32 levels = 1;
|
||||
u32 res_scale = 1;
|
||||
|
||||
bool operator==(const HostTextureTag& other) const noexcept {
|
||||
return std::tie(tuple, type, width, height, levels, res_scale) ==
|
||||
std::tie(other.tuple, other.type, other.width, other.height, other.levels,
|
||||
other.res_scale);
|
||||
}
|
||||
|
||||
struct Hash {
|
||||
const u64 operator()(const HostTextureTag& tag) const {
|
||||
return Common::ComputeHash64(&tag, sizeof(HostTextureTag));
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
struct Allocation {
|
||||
std::array<OGLTexture, 2> textures;
|
||||
std::array<GLuint, 2> handles;
|
||||
FormatTuple tuple;
|
||||
u32 width;
|
||||
u32 height;
|
||||
u32 levels;
|
||||
u32 res_scale;
|
||||
|
||||
operator bool() const noexcept {
|
||||
return textures[0].handle;
|
||||
}
|
||||
|
||||
bool Matches(u32 width_, u32 height_, u32 levels_, const FormatTuple& tuple_) const {
|
||||
return std::tie(width, height, levels, tuple) == std::tie(width_, height_, levels_, tuple_);
|
||||
}
|
||||
};
|
||||
|
||||
class Surface;
|
||||
class Driver;
|
||||
struct CachedTextureCube;
|
||||
|
||||
/**
|
||||
* Provides texture manipulation functions to the rasterizer cache
|
||||
* Separating this into a class makes it easier to abstract graphics API code
|
||||
*/
|
||||
class TextureRuntime {
|
||||
friend class Surface;
|
||||
friend class Framebuffer;
|
||||
friend class BlitHelper;
|
||||
|
||||
public:
|
||||
explicit TextureRuntime(const Driver& driver, VideoCore::RendererBase& renderer);
|
||||
~TextureRuntime();
|
||||
|
||||
/// Returns true if the provided pixel format cannot be used natively by the runtime.
|
||||
bool NeedsConversion(VideoCore::PixelFormat pixel_format) const;
|
||||
|
||||
/// Maps an internal staging buffer of the provided size of pixel uploads/downloads
|
||||
VideoCore::StagingData FindStaging(u32 size, bool upload);
|
||||
|
||||
/// Returns the OpenGL format tuple associated with the provided pixel format
|
||||
const FormatTuple& GetFormatTuple(VideoCore::PixelFormat pixel_format) const;
|
||||
|
||||
/// Takes back ownership of the allocation for recycling
|
||||
void Recycle(const HostTextureTag tag, Allocation&& alloc);
|
||||
|
||||
/// Allocates an OpenGL texture with the specified dimentions and format
|
||||
Allocation Allocate(const VideoCore::SurfaceParams& params);
|
||||
|
||||
/// Fills the rectangle of the texture with the clear value provided
|
||||
bool ClearTexture(Surface& surface, const VideoCore::TextureClear& clear);
|
||||
|
||||
/// Copies a rectangle of source to another rectange of dest
|
||||
bool CopyTextures(Surface& source, Surface& dest, const VideoCore::TextureCopy& copy);
|
||||
|
||||
/// Blits a rectangle of source to another rectange of dest
|
||||
bool BlitTextures(Surface& source, Surface& dest, const VideoCore::TextureBlit& blit);
|
||||
|
||||
/// Generates mipmaps for all the available levels of the texture
|
||||
void GenerateMipmaps(Surface& surface, u32 max_level);
|
||||
|
||||
/// Returns all source formats that support reinterpretation to the dest format
|
||||
const ReinterpreterList& GetPossibleReinterpretations(VideoCore::PixelFormat dest_format) const;
|
||||
|
||||
private:
|
||||
/// Returns the OpenGL driver class
|
||||
const Driver& GetDriver() const {
|
||||
return driver;
|
||||
}
|
||||
|
||||
private:
|
||||
const Driver& driver;
|
||||
BlitHelper blit_helper;
|
||||
std::vector<u8> staging_buffer;
|
||||
std::array<ReinterpreterList, VideoCore::PIXEL_FORMAT_COUNT> reinterpreters;
|
||||
std::unordered_multimap<HostTextureTag, Allocation, HostTextureTag::Hash> recycler;
|
||||
std::unordered_map<u64, OGLFramebuffer, Common::IdentityHash<u64>> framebuffer_cache;
|
||||
std::array<OGLFramebuffer, 3> draw_fbos;
|
||||
std::array<OGLFramebuffer, 3> read_fbos;
|
||||
};
|
||||
|
||||
class Surface : public VideoCore::SurfaceBase {
|
||||
public:
|
||||
explicit Surface(TextureRuntime& runtime, const VideoCore::SurfaceParams& params);
|
||||
~Surface();
|
||||
|
||||
Surface(const Surface&) = delete;
|
||||
Surface& operator=(const Surface&) = delete;
|
||||
|
||||
Surface(Surface&& o) noexcept = default;
|
||||
Surface& operator=(Surface&& o) noexcept = default;
|
||||
|
||||
/// Returns the surface image handle
|
||||
GLuint Handle(bool scaled = true) const noexcept {
|
||||
return alloc.handles[static_cast<u32>(scaled)];
|
||||
}
|
||||
|
||||
/// Returns the tuple of the surface allocation.
|
||||
const FormatTuple& Tuple() const noexcept {
|
||||
return alloc.tuple;
|
||||
}
|
||||
|
||||
/// Uploads pixel data in staging to a rectangle region of the surface texture
|
||||
void Upload(const VideoCore::BufferTextureCopy& upload, const VideoCore::StagingData& staging);
|
||||
|
||||
/// Downloads pixel data to staging from a rectangle region of the surface texture
|
||||
void Download(const VideoCore::BufferTextureCopy& download,
|
||||
const VideoCore::StagingData& staging);
|
||||
|
||||
/// Attaches a handle of surface to the specified framebuffer target
|
||||
void Attach(GLenum target, u32 level, u32 layer, bool scaled = true);
|
||||
|
||||
/// Returns the bpp of the internal surface format
|
||||
u32 GetInternalBytesPerPixel() const;
|
||||
|
||||
private:
|
||||
/// Performs blit between the scaled/unscaled images
|
||||
void BlitScale(const VideoCore::TextureBlit& blit, bool up_scale);
|
||||
|
||||
/// Attempts to download without using an fbo
|
||||
bool DownloadWithoutFbo(const VideoCore::BufferTextureCopy& download,
|
||||
const VideoCore::StagingData& staging);
|
||||
|
||||
private:
|
||||
const Driver* driver;
|
||||
TextureRuntime* runtime;
|
||||
Allocation alloc{};
|
||||
};
|
||||
|
||||
class Framebuffer : public VideoCore::FramebufferBase {
|
||||
public:
|
||||
explicit Framebuffer(TextureRuntime& runtime, Surface* const color, u32 color_level,
|
||||
Surface* const depth_stencil, u32 depth_level, const Pica::Regs& regs,
|
||||
Common::Rectangle<u32> surfaces_rect);
|
||||
~Framebuffer();
|
||||
|
||||
[[nodiscard]] GLuint Handle() const noexcept {
|
||||
return handle;
|
||||
}
|
||||
|
||||
[[nodiscard]] GLuint Attachment(VideoCore::SurfaceType type) const noexcept {
|
||||
return attachments[Index(type)];
|
||||
}
|
||||
|
||||
[[nodiscard]] bool HasAttachment(VideoCore::SurfaceType type) const noexcept {
|
||||
return static_cast<bool>(attachments[Index(type)]);
|
||||
}
|
||||
|
||||
private:
|
||||
std::array<GLuint, 2> attachments{};
|
||||
GLuint handle{};
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
@ -21,8 +21,16 @@
|
||||
#include "video_core/renderer_opengl/renderer_opengl.h"
|
||||
#include "video_core/video_core.h"
|
||||
|
||||
#include "video_core/host_shaders/opengl_present_anaglyph_frag.h"
|
||||
#include "video_core/host_shaders/opengl_present_frag.h"
|
||||
#include "video_core/host_shaders/opengl_present_interlaced_frag.h"
|
||||
#include "video_core/host_shaders/opengl_present_vert.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
MICROPROFILE_DEFINE(OpenGL_RenderFrame, "OpenGL", "Render Frame", MP_RGB(128, 128, 64));
|
||||
MICROPROFILE_DEFINE(OpenGL_WaitPresent, "OpenGL", "Wait For Present", MP_RGB(128, 128, 128));
|
||||
|
||||
// If the size of this is too small, it ends up creating a soft cap on FPS as the renderer will have
|
||||
// to wait on available presentation frames. There doesn't seem to be much of a downside to a larger
|
||||
// number but 9 swap textures at 60FPS presentation allows for 800% speed so thats probably fine
|
||||
@ -48,7 +56,7 @@ public:
|
||||
std::deque<Frontend::Frame*> present_queue{};
|
||||
Frontend::Frame* previous_frame = nullptr;
|
||||
|
||||
OGLTextureMailbox() {
|
||||
OGLTextureMailbox(bool has_debug_tool_ = false) : has_debug_tool{has_debug_tool_} {
|
||||
for (auto& frame : swap_chain) {
|
||||
free_queue.push(&frame);
|
||||
}
|
||||
@ -126,6 +134,8 @@ public:
|
||||
std::unique_lock<std::mutex> lock(swap_chain_lock);
|
||||
present_queue.push_front(frame);
|
||||
present_cv.notify_one();
|
||||
|
||||
DebugNotifyNextFrame();
|
||||
}
|
||||
|
||||
// This is virtual as it is to be overriden in OGLVideoDumpingMailbox below.
|
||||
@ -148,6 +158,8 @@ public:
|
||||
}
|
||||
|
||||
Frontend::Frame* TryGetPresentFrame(int timeout_ms) override {
|
||||
DebugWaitForNextFrame();
|
||||
|
||||
std::unique_lock<std::mutex> lock(swap_chain_lock);
|
||||
// wait for new entries in the present_queue
|
||||
present_cv.wait_for(lock, std::chrono::milliseconds(timeout_ms),
|
||||
@ -160,6 +172,33 @@ public:
|
||||
LoadPresentFrame();
|
||||
return previous_frame;
|
||||
}
|
||||
|
||||
private:
|
||||
std::mutex debug_synch_mutex;
|
||||
std::condition_variable debug_synch_condition;
|
||||
std::atomic_int frame_for_debug{};
|
||||
const bool has_debug_tool; // When true, using a GPU debugger, so keep frames in lock-step
|
||||
|
||||
/// Signal that a new frame is available (called from GPU thread)
|
||||
void DebugNotifyNextFrame() {
|
||||
if (!has_debug_tool) {
|
||||
return;
|
||||
}
|
||||
frame_for_debug++;
|
||||
std::lock_guard lock{debug_synch_mutex};
|
||||
debug_synch_condition.notify_one();
|
||||
}
|
||||
|
||||
/// Wait for a new frame to be available (called from presentation thread)
|
||||
void DebugWaitForNextFrame() {
|
||||
if (!has_debug_tool) {
|
||||
return;
|
||||
}
|
||||
const int last_frame = frame_for_debug;
|
||||
std::unique_lock lock{debug_synch_mutex};
|
||||
debug_synch_condition.wait(lock,
|
||||
[this, last_frame] { return frame_for_debug > last_frame; });
|
||||
}
|
||||
};
|
||||
|
||||
/// This mailbox is different in that it will never discard rendered frames
|
||||
@ -218,93 +257,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
static const char vertex_shader[] = R"(
|
||||
in vec2 vert_position;
|
||||
in vec2 vert_tex_coord;
|
||||
out vec2 frag_tex_coord;
|
||||
|
||||
// This is a truncated 3x3 matrix for 2D transformations:
|
||||
// The upper-left 2x2 submatrix performs scaling/rotation/mirroring.
|
||||
// The third column performs translation.
|
||||
// The third row could be used for projection, which we don't need in 2D. It hence is assumed to
|
||||
// implicitly be [0, 0, 1]
|
||||
uniform mat3x2 modelview_matrix;
|
||||
|
||||
void main() {
|
||||
// Multiply input position by the rotscale part of the matrix and then manually translate by
|
||||
// the last column. This is equivalent to using a full 3x3 matrix and expanding the vector
|
||||
// to `vec3(vert_position.xy, 1.0)`
|
||||
gl_Position = vec4(mat2(modelview_matrix) * vert_position + modelview_matrix[2], 0.0, 1.0);
|
||||
frag_tex_coord = vert_tex_coord;
|
||||
}
|
||||
)";
|
||||
|
||||
static const char fragment_shader[] = R"(
|
||||
in vec2 frag_tex_coord;
|
||||
layout(location = 0) out vec4 color;
|
||||
|
||||
uniform vec4 i_resolution;
|
||||
uniform vec4 o_resolution;
|
||||
uniform int layer;
|
||||
|
||||
uniform sampler2D color_texture;
|
||||
|
||||
void main() {
|
||||
color = texture(color_texture, frag_tex_coord);
|
||||
}
|
||||
)";
|
||||
|
||||
static const char fragment_shader_anaglyph[] = R"(
|
||||
|
||||
// Anaglyph Red-Cyan shader based on Dubois algorithm
|
||||
// Constants taken from the paper:
|
||||
// "Conversion of a Stereo Pair to Anaglyph with
|
||||
// the Least-Squares Projection Method"
|
||||
// Eric Dubois, March 2009
|
||||
const mat3 l = mat3( 0.437, 0.449, 0.164,
|
||||
-0.062,-0.062,-0.024,
|
||||
-0.048,-0.050,-0.017);
|
||||
const mat3 r = mat3(-0.011,-0.032,-0.007,
|
||||
0.377, 0.761, 0.009,
|
||||
-0.026,-0.093, 1.234);
|
||||
|
||||
in vec2 frag_tex_coord;
|
||||
out vec4 color;
|
||||
|
||||
uniform vec4 resolution;
|
||||
uniform int layer;
|
||||
|
||||
uniform sampler2D color_texture;
|
||||
uniform sampler2D color_texture_r;
|
||||
|
||||
void main() {
|
||||
vec4 color_tex_l = texture(color_texture, frag_tex_coord);
|
||||
vec4 color_tex_r = texture(color_texture_r, frag_tex_coord);
|
||||
color = vec4(color_tex_l.rgb*l+color_tex_r.rgb*r, color_tex_l.a);
|
||||
}
|
||||
)";
|
||||
|
||||
static const char fragment_shader_interlaced[] = R"(
|
||||
|
||||
in vec2 frag_tex_coord;
|
||||
out vec4 color;
|
||||
|
||||
uniform vec4 o_resolution;
|
||||
|
||||
uniform sampler2D color_texture;
|
||||
uniform sampler2D color_texture_r;
|
||||
|
||||
uniform int reverse_interlaced;
|
||||
|
||||
void main() {
|
||||
float screen_row = o_resolution.x * frag_tex_coord.x;
|
||||
if (int(screen_row) % 2 == reverse_interlaced)
|
||||
color = texture(color_texture, frag_tex_coord);
|
||||
else
|
||||
color = texture(color_texture_r, frag_tex_coord);
|
||||
}
|
||||
)";
|
||||
|
||||
/**
|
||||
* Vertex structure that the drawn screen rectangles are composed of.
|
||||
*/
|
||||
@ -354,9 +306,10 @@ RendererOpenGL::RendererOpenGL(Core::System& system, Frontend::EmuWindow& window
|
||||
Frontend::EmuWindow* secondary_window)
|
||||
: VideoCore::RendererBase{system, window, secondary_window}, driver{system.TelemetrySession()},
|
||||
frame_dumper{system.VideoDumper(), window} {
|
||||
window.mailbox = std::make_unique<OGLTextureMailbox>();
|
||||
const bool has_debug_tool = driver.HasDebugTool();
|
||||
window.mailbox = std::make_unique<OGLTextureMailbox>(has_debug_tool);
|
||||
if (secondary_window) {
|
||||
secondary_window->mailbox = std::make_unique<OGLTextureMailbox>();
|
||||
secondary_window->mailbox = std::make_unique<OGLTextureMailbox>(has_debug_tool);
|
||||
}
|
||||
frame_dumper.mailbox = std::make_unique<OGLVideoDumpingMailbox>();
|
||||
InitOpenGLObjects();
|
||||
@ -365,10 +318,6 @@ RendererOpenGL::RendererOpenGL(Core::System& system, Frontend::EmuWindow& window
|
||||
|
||||
RendererOpenGL::~RendererOpenGL() = default;
|
||||
|
||||
MICROPROFILE_DEFINE(OpenGL_RenderFrame, "OpenGL", "Render Frame", MP_RGB(128, 128, 64));
|
||||
MICROPROFILE_DEFINE(OpenGL_WaitPresent, "OpenGL", "Wait For Present", MP_RGB(128, 128, 128));
|
||||
|
||||
/// Swap buffers (render frame)
|
||||
void RendererOpenGL::SwapBuffers() {
|
||||
// Maintain the rasterizer's state as a priority
|
||||
OpenGLState prev_state = OpenGLState::GetCurState();
|
||||
@ -673,13 +622,13 @@ void RendererOpenGL::ReloadShader() {
|
||||
|
||||
if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::Anaglyph) {
|
||||
if (Settings::values.anaglyph_shader_name.GetValue() == "dubois (builtin)") {
|
||||
shader_data += fragment_shader_anaglyph;
|
||||
shader_data += HostShaders::OPENGL_PRESENT_ANAGLYPH_FRAG;
|
||||
} else {
|
||||
std::string shader_text = OpenGL::GetPostProcessingShaderCode(
|
||||
true, Settings::values.anaglyph_shader_name.GetValue());
|
||||
if (shader_text.empty()) {
|
||||
// Should probably provide some information that the shader couldn't load
|
||||
shader_data += fragment_shader_anaglyph;
|
||||
shader_data += HostShaders::OPENGL_PRESENT_ANAGLYPH_FRAG;
|
||||
} else {
|
||||
shader_data += shader_text;
|
||||
}
|
||||
@ -687,22 +636,22 @@ void RendererOpenGL::ReloadShader() {
|
||||
} else if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::Interlaced ||
|
||||
Settings::values.render_3d.GetValue() ==
|
||||
Settings::StereoRenderOption::ReverseInterlaced) {
|
||||
shader_data += fragment_shader_interlaced;
|
||||
shader_data += HostShaders::OPENGL_PRESENT_INTERLACED_FRAG;
|
||||
} else {
|
||||
if (Settings::values.pp_shader_name.GetValue() == "none (builtin)") {
|
||||
shader_data += fragment_shader;
|
||||
shader_data += HostShaders::OPENGL_PRESENT_FRAG;
|
||||
} else {
|
||||
std::string shader_text = OpenGL::GetPostProcessingShaderCode(
|
||||
false, Settings::values.pp_shader_name.GetValue());
|
||||
if (shader_text.empty()) {
|
||||
// Should probably provide some information that the shader couldn't load
|
||||
shader_data += fragment_shader;
|
||||
shader_data += HostShaders::OPENGL_PRESENT_FRAG;
|
||||
} else {
|
||||
shader_data += shader_text;
|
||||
}
|
||||
}
|
||||
}
|
||||
shader.Create(vertex_shader, shader_data.c_str());
|
||||
shader.Create(HostShaders::OPENGL_PRESENT_VERT, shader_data);
|
||||
state.draw.shader_program = shader.handle;
|
||||
state.Apply();
|
||||
uniform_modelview_matrix = glGetUniformLocation(shader.handle, "modelview_matrix");
|
||||
|
@ -1,253 +0,0 @@
|
||||
// Copyright 2020 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <chrono>
|
||||
#include <vector>
|
||||
#include <fmt/chrono.h>
|
||||
#include "common/logging/log.h"
|
||||
#include "video_core/rasterizer_cache/rasterizer_cache_utils.h"
|
||||
#include "video_core/renderer_opengl/gl_state.h"
|
||||
#include "video_core/renderer_opengl/texture_downloader_es.h"
|
||||
|
||||
#include "shaders/depth_to_color.frag"
|
||||
#include "shaders/depth_to_color.vert"
|
||||
#include "shaders/ds_to_color.frag"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
/**
|
||||
* Self tests for the texture downloader
|
||||
*/
|
||||
void TextureDownloaderES::Test() {
|
||||
auto cur_state = OpenGLState::GetCurState();
|
||||
OpenGLState state;
|
||||
|
||||
{
|
||||
GLint range[2];
|
||||
GLint precision;
|
||||
#define PRECISION_TEST(type) \
|
||||
glGetShaderPrecisionFormat(GL_FRAGMENT_SHADER, type, range, &precision); \
|
||||
LOG_INFO(Render_OpenGL, #type " range: [{}, {}], precision: {}", range[0], range[1], precision);
|
||||
PRECISION_TEST(GL_LOW_INT);
|
||||
PRECISION_TEST(GL_MEDIUM_INT);
|
||||
PRECISION_TEST(GL_HIGH_INT);
|
||||
PRECISION_TEST(GL_LOW_FLOAT);
|
||||
PRECISION_TEST(GL_MEDIUM_FLOAT);
|
||||
PRECISION_TEST(GL_HIGH_FLOAT);
|
||||
#undef PRECISION_TEST
|
||||
}
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
|
||||
const auto test = [this, &state](FormatTuple tuple, auto original_data, std::size_t tex_size,
|
||||
auto data_generator) {
|
||||
OGLTexture texture;
|
||||
texture.Create();
|
||||
state.texture_units[0].texture_2d = texture.handle;
|
||||
state.Apply();
|
||||
|
||||
original_data.resize(tex_size * tex_size);
|
||||
for (std::size_t idx = 0; idx < original_data.size(); ++idx) {
|
||||
original_data[idx] = data_generator(idx);
|
||||
}
|
||||
GLsizei tex_sizei = static_cast<GLsizei>(tex_size);
|
||||
glTexStorage2D(GL_TEXTURE_2D, 1, tuple.internal_format, tex_sizei, tex_sizei);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, tex_sizei, tex_sizei, tuple.format, tuple.type,
|
||||
original_data.data());
|
||||
|
||||
decltype(original_data) new_data(original_data.size());
|
||||
glFinish();
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
GetTexImage(GL_TEXTURE_2D, 0, tuple.format, tuple.type, tex_sizei, tex_sizei,
|
||||
new_data.data());
|
||||
glFinish();
|
||||
auto time = std::chrono::high_resolution_clock::now() - start;
|
||||
LOG_INFO(Render_OpenGL, "test took {}", std::chrono::duration<double, std::milli>(time));
|
||||
|
||||
int diff = 0;
|
||||
for (std::size_t idx = 0; idx < original_data.size(); ++idx)
|
||||
if (new_data[idx] - original_data[idx] != diff) {
|
||||
diff = new_data[idx] - original_data[idx];
|
||||
// every time the error between the real and expected value changes, log it
|
||||
// some error is expected in D24 due to floating point precision
|
||||
LOG_WARNING(Render_OpenGL, "difference changed at {:#X}: {:#X} -> {:#X}", idx,
|
||||
original_data[idx], new_data[idx]);
|
||||
}
|
||||
};
|
||||
LOG_INFO(Render_OpenGL, "GL_DEPTH24_STENCIL8 download test starting");
|
||||
test(GetFormatTuple(PixelFormat::D24S8), std::vector<u32>{}, 4096,
|
||||
[](std::size_t idx) { return static_cast<u32>((idx << 8) | (idx & 0xFF)); });
|
||||
LOG_INFO(Render_OpenGL, "GL_DEPTH_COMPONENT24 download test starting");
|
||||
test(GetFormatTuple(PixelFormat::D24), std::vector<u32>{}, 4096,
|
||||
[](std::size_t idx) { return static_cast<u32>(idx << 8); });
|
||||
LOG_INFO(Render_OpenGL, "GL_DEPTH_COMPONENT16 download test starting");
|
||||
test(GetFormatTuple(PixelFormat::D16), std::vector<u16>{}, 256,
|
||||
[](std::size_t idx) { return static_cast<u16>(idx); });
|
||||
|
||||
cur_state.Apply();
|
||||
}
|
||||
|
||||
TextureDownloaderES::TextureDownloaderES(bool enable_depth_stencil) {
|
||||
vao.Create();
|
||||
read_fbo_generic.Create();
|
||||
|
||||
depth32_fbo.Create();
|
||||
r32ui_renderbuffer.Create();
|
||||
depth16_fbo.Create();
|
||||
r16_renderbuffer.Create();
|
||||
|
||||
const auto init_program = [](ConversionShader& converter, std::string_view frag) {
|
||||
converter.program.Create(depth_to_color_vert.data(), frag.data());
|
||||
converter.lod_location = glGetUniformLocation(converter.program.handle, "lod");
|
||||
};
|
||||
|
||||
// xperia64: The depth stencil shader currently uses a GLES extension that is not supported
|
||||
// across all devices Reportedly broken on Tegra devices and the Nexus 6P, so enabling it can be
|
||||
// toggled
|
||||
if (enable_depth_stencil) {
|
||||
init_program(d24s8_r32ui_conversion_shader, ds_to_color_frag);
|
||||
}
|
||||
|
||||
init_program(d24_r32ui_conversion_shader, depth_to_color_frag);
|
||||
init_program(d16_r16_conversion_shader, R"(
|
||||
out highp float color;
|
||||
|
||||
uniform highp sampler2D depth;
|
||||
uniform int lod;
|
||||
|
||||
void main(){
|
||||
color = texelFetch(depth, ivec2(gl_FragCoord.xy), lod).x;
|
||||
}
|
||||
)");
|
||||
|
||||
sampler.Create();
|
||||
glSamplerParameteri(sampler.handle, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glSamplerParameteri(sampler.handle, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
|
||||
auto cur_state = OpenGLState::GetCurState();
|
||||
auto state = cur_state;
|
||||
|
||||
state.draw.shader_program = d24s8_r32ui_conversion_shader.program.handle;
|
||||
state.draw.draw_framebuffer = depth32_fbo.handle;
|
||||
state.renderbuffer = r32ui_renderbuffer.handle;
|
||||
state.Apply();
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_R32UI, max_size, max_size);
|
||||
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
|
||||
r32ui_renderbuffer.handle);
|
||||
glUniform1i(glGetUniformLocation(d24s8_r32ui_conversion_shader.program.handle, "depth"), 1);
|
||||
|
||||
state.draw.draw_framebuffer = depth16_fbo.handle;
|
||||
state.renderbuffer = r16_renderbuffer.handle;
|
||||
state.Apply();
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_R16, max_size, max_size);
|
||||
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
|
||||
r16_renderbuffer.handle);
|
||||
|
||||
cur_state.Apply();
|
||||
}
|
||||
|
||||
/**
|
||||
* OpenGL ES does not support glReadBuffer for depth/stencil formats
|
||||
* This gets around it by converting to a Red surface before downloading
|
||||
*/
|
||||
GLuint TextureDownloaderES::ConvertDepthToColor(GLuint level, GLenum& format, GLenum& type,
|
||||
GLint height, GLint width) {
|
||||
ASSERT(width <= max_size && height <= max_size);
|
||||
const OpenGLState cur_state = OpenGLState::GetCurState();
|
||||
OpenGLState state;
|
||||
state.texture_units[0] = {cur_state.texture_units[0].texture_2d, sampler.handle};
|
||||
state.draw.vertex_array = vao.handle;
|
||||
|
||||
OGLTexture texture_view;
|
||||
const ConversionShader* converter;
|
||||
switch (type) {
|
||||
case GL_UNSIGNED_SHORT:
|
||||
state.draw.draw_framebuffer = depth16_fbo.handle;
|
||||
converter = &d16_r16_conversion_shader;
|
||||
format = GL_RED;
|
||||
break;
|
||||
case GL_UNSIGNED_INT:
|
||||
state.draw.draw_framebuffer = depth32_fbo.handle;
|
||||
converter = &d24_r32ui_conversion_shader;
|
||||
format = GL_RED_INTEGER;
|
||||
break;
|
||||
case GL_UNSIGNED_INT_24_8:
|
||||
state.draw.draw_framebuffer = depth32_fbo.handle;
|
||||
converter = &d24s8_r32ui_conversion_shader;
|
||||
format = GL_RED_INTEGER;
|
||||
type = GL_UNSIGNED_INT;
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE_MSG("Destination type not recognized");
|
||||
}
|
||||
state.draw.shader_program = converter->program.handle;
|
||||
state.viewport = {0, 0, width, height};
|
||||
state.Apply();
|
||||
if (converter->program.handle == d24s8_r32ui_conversion_shader.program.handle) {
|
||||
// TODO BreadFish64: the ARM framebuffer reading extension is probably not the most optimal
|
||||
// way to do this, search for another solution
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
|
||||
state.texture_units[0].texture_2d, level);
|
||||
}
|
||||
|
||||
glUniform1i(converter->lod_location, level);
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
if (texture_view.handle) {
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_DEPTH_COMPONENT);
|
||||
}
|
||||
return state.draw.draw_framebuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* OpenGL ES does not support glGetTexImage. Obtain the pixels by attaching the
|
||||
* texture to a framebuffer.
|
||||
* Originally from https://github.com/apitrace/apitrace/blob/master/retrace/glstate_images.cpp
|
||||
* Depth texture download assumes that the texture's format tuple matches what is found
|
||||
* OpenGL::depth_format_tuples
|
||||
*/
|
||||
void TextureDownloaderES::GetTexImage(GLenum target, GLuint level, GLenum format, GLenum type,
|
||||
GLint height, GLint width, void* pixels) {
|
||||
OpenGLState state = OpenGLState::GetCurState();
|
||||
GLuint texture{};
|
||||
const GLuint old_read_buffer = state.draw.read_framebuffer;
|
||||
switch (target) {
|
||||
case GL_TEXTURE_2D:
|
||||
texture = state.texture_units[0].texture_2d;
|
||||
break;
|
||||
case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
|
||||
case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
|
||||
case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
|
||||
case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
|
||||
case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
|
||||
case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
|
||||
texture = state.texture_cube_unit.texture_cube;
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unexpected target {:x}", target);
|
||||
}
|
||||
|
||||
switch (format) {
|
||||
case GL_DEPTH_COMPONENT:
|
||||
case GL_DEPTH_STENCIL:
|
||||
// unfortunately, the accurate way is too slow for release
|
||||
return;
|
||||
state.draw.read_framebuffer = ConvertDepthToColor(level, format, type, height, width);
|
||||
state.Apply();
|
||||
break;
|
||||
default:
|
||||
state.draw.read_framebuffer = read_fbo_generic.handle;
|
||||
state.Apply();
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture,
|
||||
level);
|
||||
}
|
||||
GLenum status = glCheckFramebufferStatus(GL_READ_FRAMEBUFFER);
|
||||
if (status != GL_FRAMEBUFFER_COMPLETE) {
|
||||
LOG_DEBUG(Render_OpenGL, "Framebuffer is incomplete, status: {:X}", status);
|
||||
}
|
||||
glReadPixels(0, 0, width, height, format, type, pixels);
|
||||
|
||||
state.draw.read_framebuffer = old_read_buffer;
|
||||
state.Apply();
|
||||
}
|
||||
|
||||
} // namespace OpenGL
|
@ -1,36 +0,0 @@
|
||||
// Copyright 2020 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
|
||||
namespace OpenGL {
|
||||
class OpenGLState;
|
||||
|
||||
class TextureDownloaderES {
|
||||
static constexpr u16 max_size = 1024;
|
||||
|
||||
OGLVertexArray vao;
|
||||
OGLFramebuffer read_fbo_generic;
|
||||
OGLFramebuffer depth32_fbo, depth16_fbo;
|
||||
OGLRenderbuffer r32ui_renderbuffer, r16_renderbuffer;
|
||||
struct ConversionShader {
|
||||
OGLProgram program;
|
||||
GLint lod_location{-1};
|
||||
} d24_r32ui_conversion_shader, d16_r16_conversion_shader, d24s8_r32ui_conversion_shader;
|
||||
OGLSampler sampler;
|
||||
|
||||
void Test();
|
||||
GLuint ConvertDepthToColor(GLuint level, GLenum& format, GLenum& type, GLint height,
|
||||
GLint width);
|
||||
|
||||
public:
|
||||
TextureDownloaderES(bool enable_depth_stencil);
|
||||
|
||||
void GetTexImage(GLenum target, GLuint level, GLenum format, const GLenum type, GLint height,
|
||||
GLint width, void* pixels);
|
||||
};
|
||||
} // namespace OpenGL
|
@ -1,138 +0,0 @@
|
||||
// Copyright 2019 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
// modified from
|
||||
// https://github.com/bloc97/Anime4K/blob/533cee5f7018d0e57ad2a26d76d43f13b9d8782a/glsl/Anime4K_Adaptive_v1.0RC2_UltraFast.glsl
|
||||
|
||||
// MIT License
|
||||
//
|
||||
// Copyright(c) 2019 bloc97
|
||||
//
|
||||
// Permission is hereby granted,
|
||||
// free of charge,
|
||||
// to any person obtaining a copy of this software and associated documentation
|
||||
// files(the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation the rights to use,
|
||||
// copy, modify, merge, publish, distribute, sublicense, and / or sell copies of the Software,
|
||||
// and to permit persons to whom the Software is furnished to do so,
|
||||
// subject to the following conditions :
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies
|
||||
// or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS",
|
||||
// WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
#include "video_core/renderer_opengl/texture_filters/anime4k/anime4k_ultrafast.h"
|
||||
|
||||
#include "shaders/refine.frag"
|
||||
#include "shaders/tex_coord.vert"
|
||||
#include "shaders/x_gradient.frag"
|
||||
#include "shaders/y_gradient.frag"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
Anime4kUltrafast::Anime4kUltrafast(u16 scale_factor) : TextureFilterBase(scale_factor) {
|
||||
const OpenGLState cur_state = OpenGLState::GetCurState();
|
||||
|
||||
vao.Create();
|
||||
|
||||
for (std::size_t idx = 0; idx < samplers.size(); ++idx) {
|
||||
samplers[idx].Create();
|
||||
state.texture_units[idx].sampler = samplers[idx].handle;
|
||||
glSamplerParameteri(samplers[idx].handle, GL_TEXTURE_MIN_FILTER,
|
||||
idx != 2 ? GL_LINEAR : GL_NEAREST);
|
||||
glSamplerParameteri(samplers[idx].handle, GL_TEXTURE_MAG_FILTER,
|
||||
idx != 2 ? GL_LINEAR : GL_NEAREST);
|
||||
glSamplerParameteri(samplers[idx].handle, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glSamplerParameteri(samplers[idx].handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
}
|
||||
state.draw.vertex_array = vao.handle;
|
||||
|
||||
gradient_x_program.Create(tex_coord_vert.data(), x_gradient_frag.data());
|
||||
gradient_y_program.Create(tex_coord_vert.data(), y_gradient_frag.data());
|
||||
refine_program.Create(tex_coord_vert.data(), refine_frag.data());
|
||||
|
||||
state.draw.shader_program = gradient_y_program.handle;
|
||||
state.Apply();
|
||||
glUniform1i(glGetUniformLocation(gradient_y_program.handle, "tex_input"), 2);
|
||||
|
||||
state.draw.shader_program = refine_program.handle;
|
||||
state.Apply();
|
||||
glUniform1i(glGetUniformLocation(refine_program.handle, "LUMAD"), 1);
|
||||
|
||||
cur_state.Apply();
|
||||
}
|
||||
|
||||
void Anime4kUltrafast::Filter(const OGLTexture& src_tex, Common::Rectangle<u32> src_rect,
|
||||
const OGLTexture& dst_tex, Common::Rectangle<u32> dst_rect) {
|
||||
const OpenGLState cur_state = OpenGLState::GetCurState();
|
||||
|
||||
// These will have handles from the previous texture that was filtered, reset them to avoid
|
||||
// binding invalid textures.
|
||||
state.texture_units[0].texture_2d = 0;
|
||||
state.texture_units[1].texture_2d = 0;
|
||||
state.texture_units[2].texture_2d = 0;
|
||||
|
||||
const auto setup_temp_tex = [this, &src_rect](GLint internal_format, GLint format) {
|
||||
TempTex texture;
|
||||
texture.fbo.Create();
|
||||
texture.tex.Create();
|
||||
state.texture_units[0].texture_2d = texture.tex.handle;
|
||||
state.draw.draw_framebuffer = texture.fbo.handle;
|
||||
state.Apply();
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, texture.tex.handle);
|
||||
glTexStorage2D(GL_TEXTURE_2D, 1, internal_format,
|
||||
src_rect.GetWidth() * internal_scale_factor,
|
||||
src_rect.GetHeight() * internal_scale_factor);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
||||
texture.tex.handle, 0);
|
||||
return texture;
|
||||
};
|
||||
auto XY = setup_temp_tex(GL_RG16F, GL_RG);
|
||||
auto LUMAD = setup_temp_tex(GL_R16F, GL_RED);
|
||||
|
||||
state.viewport = {static_cast<GLint>(src_rect.left * internal_scale_factor),
|
||||
static_cast<GLint>(src_rect.bottom * internal_scale_factor),
|
||||
static_cast<GLsizei>(src_rect.GetWidth() * internal_scale_factor),
|
||||
static_cast<GLsizei>(src_rect.GetHeight() * internal_scale_factor)};
|
||||
state.texture_units[0].texture_2d = src_tex.handle;
|
||||
state.texture_units[1].texture_2d = LUMAD.tex.handle;
|
||||
state.texture_units[2].texture_2d = XY.tex.handle;
|
||||
state.draw.draw_framebuffer = XY.fbo.handle;
|
||||
state.draw.shader_program = gradient_x_program.handle;
|
||||
state.Apply();
|
||||
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
// gradient y pass
|
||||
state.draw.draw_framebuffer = LUMAD.fbo.handle;
|
||||
state.draw.shader_program = gradient_y_program.handle;
|
||||
state.Apply();
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
// refine pass
|
||||
state.viewport = {static_cast<GLint>(dst_rect.left), static_cast<GLint>(dst_rect.bottom),
|
||||
static_cast<GLsizei>(dst_rect.GetWidth()),
|
||||
static_cast<GLsizei>(dst_rect.GetHeight())};
|
||||
state.draw.draw_framebuffer = draw_fbo.handle;
|
||||
state.draw.shader_program = refine_program.handle;
|
||||
state.Apply();
|
||||
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst_tex.handle,
|
||||
0);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
cur_state.Apply();
|
||||
}
|
||||
|
||||
} // namespace OpenGL
|
@ -1,38 +0,0 @@
|
||||
// Copyright 2019 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
#include "video_core/renderer_opengl/gl_state.h"
|
||||
#include "video_core/renderer_opengl/texture_filters/texture_filter_base.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
class Anime4kUltrafast : public TextureFilterBase {
|
||||
public:
|
||||
static constexpr std::string_view NAME = "Anime4K Ultrafast";
|
||||
|
||||
explicit Anime4kUltrafast(u16 scale_factor);
|
||||
void Filter(const OGLTexture& src_tex, Common::Rectangle<u32> src_rect,
|
||||
const OGLTexture& dst_tex, Common::Rectangle<u32> dst_rect) override;
|
||||
|
||||
private:
|
||||
static constexpr u8 internal_scale_factor = 2;
|
||||
|
||||
OpenGLState state{};
|
||||
|
||||
OGLVertexArray vao;
|
||||
|
||||
struct TempTex {
|
||||
OGLTexture tex;
|
||||
OGLFramebuffer fbo;
|
||||
};
|
||||
|
||||
std::array<OGLSampler, 3> samplers;
|
||||
|
||||
OGLProgram gradient_x_program, gradient_y_program, refine_program;
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
@ -1,20 +0,0 @@
|
||||
//? #version 330
|
||||
precision mediump float;
|
||||
|
||||
in vec2 tex_coord;
|
||||
|
||||
out vec2 frag_color;
|
||||
|
||||
uniform sampler2D tex_input;
|
||||
|
||||
const vec3 K = vec3(0.2627, 0.6780, 0.0593);
|
||||
// TODO: improve handling of alpha channel
|
||||
#define GetLum(xoffset) dot(K, textureLodOffset(tex_input, tex_coord, 0.0, ivec2(xoffset, 0)).rgb)
|
||||
|
||||
void main() {
|
||||
float l = GetLum(-1);
|
||||
float c = GetLum(0);
|
||||
float r = GetLum(1);
|
||||
|
||||
frag_color = vec2(r - l, l + 2.0 * c + r);
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
//? #version 330
|
||||
precision mediump float;
|
||||
|
||||
in vec2 tex_coord;
|
||||
|
||||
out float frag_color;
|
||||
|
||||
uniform sampler2D tex_input;
|
||||
|
||||
void main() {
|
||||
vec2 t = textureLodOffset(tex_input, tex_coord, 0.0, ivec2(0, 1)).xy;
|
||||
vec2 c = textureLod(tex_input, tex_coord, 0.0).xy;
|
||||
vec2 b = textureLodOffset(tex_input, tex_coord, 0.0, ivec2(0, -1)).xy;
|
||||
|
||||
vec2 grad = vec2(t.x + 2.0 * c.x + b.x, b.y - t.y);
|
||||
|
||||
frag_color = 1.0 - length(grad);
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
// Copyright 2020 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "video_core/renderer_opengl/texture_filters/bicubic/bicubic.h"
|
||||
|
||||
#include "shaders/bicubic.frag"
|
||||
#include "shaders/tex_coord.vert"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
Bicubic::Bicubic(u16 scale_factor) : TextureFilterBase(scale_factor) {
|
||||
program.Create(tex_coord_vert.data(), bicubic_frag.data());
|
||||
vao.Create();
|
||||
src_sampler.Create();
|
||||
|
||||
state.draw.shader_program = program.handle;
|
||||
state.draw.vertex_array = vao.handle;
|
||||
state.draw.shader_program = program.handle;
|
||||
state.texture_units[0].sampler = src_sampler.handle;
|
||||
|
||||
glSamplerParameteri(src_sampler.handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glSamplerParameteri(src_sampler.handle, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glSamplerParameteri(src_sampler.handle, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glSamplerParameteri(src_sampler.handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
} // namespace OpenGL
|
||||
|
||||
void Bicubic::Filter(const OGLTexture& src_tex, Common::Rectangle<u32> src_rect,
|
||||
const OGLTexture& dst_tex, Common::Rectangle<u32> dst_rect) {
|
||||
const OpenGLState cur_state = OpenGLState::GetCurState();
|
||||
state.texture_units[0].texture_2d = src_tex.handle;
|
||||
state.draw.draw_framebuffer = draw_fbo.handle;
|
||||
state.viewport = {static_cast<GLint>(dst_rect.left), static_cast<GLint>(dst_rect.bottom),
|
||||
static_cast<GLsizei>(dst_rect.GetWidth()),
|
||||
static_cast<GLsizei>(dst_rect.GetHeight())};
|
||||
state.Apply();
|
||||
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst_tex.handle,
|
||||
0);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
cur_state.Apply();
|
||||
}
|
||||
|
||||
} // namespace OpenGL
|
@ -1,28 +0,0 @@
|
||||
// Copyright 2020 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
#include "video_core/renderer_opengl/gl_state.h"
|
||||
#include "video_core/renderer_opengl/texture_filters/texture_filter_base.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
class Bicubic : public TextureFilterBase {
|
||||
public:
|
||||
static constexpr std::string_view NAME = "Bicubic";
|
||||
|
||||
explicit Bicubic(u16 scale_factor);
|
||||
void Filter(const OGLTexture& src_tex, Common::Rectangle<u32> src_rect,
|
||||
const OGLTexture& dst_tex, Common::Rectangle<u32> dst_rect) override;
|
||||
|
||||
private:
|
||||
OpenGLState state{};
|
||||
OGLProgram program{};
|
||||
OGLVertexArray vao{};
|
||||
OGLSampler src_sampler{};
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
@ -1,47 +0,0 @@
|
||||
// Copyright 2022 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "video_core/renderer_opengl/texture_filters/nearest_neighbor/nearest_neighbor.h"
|
||||
|
||||
#include "shaders/nearest_neighbor.frag"
|
||||
#include "shaders/tex_coord.vert"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
NearestNeighbor::NearestNeighbor(u16 scale_factor) : TextureFilterBase(scale_factor) {
|
||||
program.Create(tex_coord_vert.data(), nearest_neighbor_frag.data());
|
||||
vao.Create();
|
||||
src_sampler.Create();
|
||||
|
||||
state.draw.shader_program = program.handle;
|
||||
state.draw.vertex_array = vao.handle;
|
||||
state.draw.shader_program = program.handle;
|
||||
state.texture_units[0].sampler = src_sampler.handle;
|
||||
|
||||
// linear minification still makes sense
|
||||
glSamplerParameteri(src_sampler.handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glSamplerParameteri(src_sampler.handle, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glSamplerParameteri(src_sampler.handle, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glSamplerParameteri(src_sampler.handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
}
|
||||
|
||||
void NearestNeighbor::Filter(const OGLTexture& src_tex, Common::Rectangle<u32> src_rect,
|
||||
const OGLTexture& dst_tex, Common::Rectangle<u32> dst_rect) {
|
||||
const OpenGLState cur_state = OpenGLState::GetCurState();
|
||||
state.texture_units[0].texture_2d = src_tex.handle;
|
||||
state.draw.draw_framebuffer = draw_fbo.handle;
|
||||
state.viewport = {static_cast<GLint>(dst_rect.left), static_cast<GLint>(dst_rect.bottom),
|
||||
static_cast<GLsizei>(dst_rect.GetWidth()),
|
||||
static_cast<GLsizei>(dst_rect.GetHeight())};
|
||||
state.Apply();
|
||||
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst_tex.handle,
|
||||
0);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
cur_state.Apply();
|
||||
}
|
||||
|
||||
} // namespace OpenGL
|
@ -1,12 +0,0 @@
|
||||
//? #version 330
|
||||
precision mediump float;
|
||||
|
||||
in vec2 tex_coord;
|
||||
|
||||
out vec4 frag_color;
|
||||
|
||||
uniform sampler2D input_texture;
|
||||
|
||||
void main() {
|
||||
frag_color = texture(input_texture, tex_coord);
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
// Copyright 2022 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
#include "video_core/renderer_opengl/gl_state.h"
|
||||
#include "video_core/renderer_opengl/texture_filters/texture_filter_base.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
class NearestNeighbor : public TextureFilterBase {
|
||||
public:
|
||||
static constexpr std::string_view NAME = "Nearest Neighbor";
|
||||
|
||||
explicit NearestNeighbor(u16 scale_factor);
|
||||
void Filter(const OGLTexture& src_tex, Common::Rectangle<u32> src_rect,
|
||||
const OGLTexture& dst_tex, Common::Rectangle<u32> dst_rect) override;
|
||||
|
||||
private:
|
||||
OpenGLState state{};
|
||||
OGLProgram program{};
|
||||
OGLVertexArray vao{};
|
||||
OGLSampler src_sampler{};
|
||||
};
|
||||
} // namespace OpenGL
|
@ -1,46 +0,0 @@
|
||||
// Copyright 2020 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "video_core/renderer_opengl/texture_filters/scale_force/scale_force.h"
|
||||
|
||||
#include "shaders/scale_force.frag"
|
||||
#include "shaders/tex_coord.vert"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
ScaleForce::ScaleForce(u16 scale_factor) : TextureFilterBase(scale_factor) {
|
||||
program.Create(tex_coord_vert.data(), scale_force_frag.data());
|
||||
vao.Create();
|
||||
src_sampler.Create();
|
||||
|
||||
state.draw.shader_program = program.handle;
|
||||
state.draw.vertex_array = vao.handle;
|
||||
state.draw.shader_program = program.handle;
|
||||
state.texture_units[0].sampler = src_sampler.handle;
|
||||
|
||||
glSamplerParameteri(src_sampler.handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glSamplerParameteri(src_sampler.handle, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glSamplerParameteri(src_sampler.handle, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glSamplerParameteri(src_sampler.handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
}
|
||||
|
||||
void ScaleForce::Filter(const OGLTexture& src_tex, Common::Rectangle<u32> src_rect,
|
||||
const OGLTexture& dst_tex, Common::Rectangle<u32> dst_rect) {
|
||||
const OpenGLState cur_state = OpenGLState::GetCurState();
|
||||
state.texture_units[0].texture_2d = src_tex.handle;
|
||||
state.draw.draw_framebuffer = draw_fbo.handle;
|
||||
state.viewport = {static_cast<GLint>(dst_rect.left), static_cast<GLint>(dst_rect.bottom),
|
||||
static_cast<GLsizei>(dst_rect.GetWidth()),
|
||||
static_cast<GLsizei>(dst_rect.GetHeight())};
|
||||
state.Apply();
|
||||
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst_tex.handle,
|
||||
0);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
cur_state.Apply();
|
||||
}
|
||||
|
||||
} // namespace OpenGL
|
@ -1,28 +0,0 @@
|
||||
// Copyright 2020 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
#include "video_core/renderer_opengl/gl_state.h"
|
||||
#include "video_core/renderer_opengl/texture_filters/texture_filter_base.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
class ScaleForce : public TextureFilterBase {
|
||||
public:
|
||||
static constexpr std::string_view NAME = "ScaleForce";
|
||||
|
||||
explicit ScaleForce(u16 scale_factor);
|
||||
void Filter(const OGLTexture& src_tex, Common::Rectangle<u32> src_rect,
|
||||
const OGLTexture& dst_tex, Common::Rectangle<u32> dst_rect) override;
|
||||
|
||||
private:
|
||||
OpenGLState state{};
|
||||
OGLProgram program{};
|
||||
OGLVertexArray vao{};
|
||||
OGLSampler src_sampler{};
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
@ -1,35 +0,0 @@
|
||||
// Copyright 2020 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
#include <string_view>
|
||||
#include "common/common_types.h"
|
||||
#include "common/math_util.h"
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
class TextureRuntime;
|
||||
class OGLTexture;
|
||||
|
||||
class TextureFilterBase {
|
||||
friend class TextureFilterer;
|
||||
|
||||
public:
|
||||
explicit TextureFilterBase(u16 scale_factor) : scale_factor(scale_factor) {
|
||||
draw_fbo.Create();
|
||||
};
|
||||
|
||||
virtual ~TextureFilterBase() = default;
|
||||
|
||||
private:
|
||||
virtual void Filter(const OGLTexture& src_tex, Common::Rectangle<u32> src_rect,
|
||||
const OGLTexture& dst_tex, Common::Rectangle<u32> dst_rect) = 0;
|
||||
|
||||
protected:
|
||||
OGLFramebuffer draw_fbo;
|
||||
const u16 scale_factor{};
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user