diff --git a/externals/glad/include/glad/glad.h b/externals/glad/include/glad/glad.h index d2fc37740..6d4dc8092 100644 --- a/externals/glad/include/glad/glad.h +++ b/externals/glad/include/glad/glad.h @@ -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 } diff --git a/externals/glad/src/glad.c b/externals/glad/src/glad.c index 1630ebc19..175287a97 100644 --- a/externals/glad/src/glad.c +++ b/externals/glad/src/glad.c @@ -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 @@ -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; } diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.java b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.java index c8e8ad23f..52d838265 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.java +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.java @@ -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 sl) { diff --git a/src/android/app/src/main/jni/config.cpp b/src/android/app/src/main/jni/config.cpp index 2cd0f76a4..c5ea67319 100644 --- a/src/android/app/src/main/jni/config.cpp +++ b/src/android/app/src/main/jni/config.cpp @@ -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); diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index 272b2a9b1..e1f8cc1fc 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -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(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(); diff --git a/src/android/app/src/main/jni/native.h b/src/android/app/src/main/jni/native.h index 184b46145..a2156a4e4 100644 --- a/src/android/app/src/main/jni/native.h +++ b/src/android/app/src/main/jni/native.h @@ -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); diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml index 223714d0a..9fc74985a 100644 --- a/src/android/app/src/main/res/values/arrays.xml +++ b/src/android/app/src/main/res/values/arrays.xml @@ -179,4 +179,22 @@ 1 + + + None + Anime4K + Bicubic + Nearest Neighbor + ScaleForce + xBRZ + + + + 0 + 1 + 2 + 3 + 4 + 5 + diff --git a/src/citra/config.cpp b/src/citra/config.cpp index cb7f4b10d..53cbb107d 100644 --- a/src/citra/config.cpp +++ b/src/citra/config.cpp @@ -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); diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h index 692134590..b34b64d97 100644 --- a/src/citra/default_ini.h +++ b/src/citra/default_ini.h @@ -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. diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp index 6da3dec9b..ab5943536 100644 --- a/src/citra_qt/configuration/config.cpp +++ b/src/citra_qt/configuration/config.cpp @@ -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(), diff --git a/src/citra_qt/configuration/configuration_shared.cpp b/src/citra_qt/configuration/configuration_shared.cpp index 681a7d781..e5039256a 100644 --- a/src/citra_qt/configuration/configuration_shared.cpp +++ b/src/citra_qt/configuration/configuration_shared.cpp @@ -33,16 +33,6 @@ void ConfigurationShared::SetPerGameSetting(QCheckBox* checkbox, } } -template <> -void ConfigurationShared::SetPerGameSetting( - QComboBox* combobox, const Settings::SwitchableSetting* setting) { - const int index = - static_cast(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) }") diff --git a/src/citra_qt/configuration/configuration_shared.h b/src/citra_qt/configuration/configuration_shared.h index 7eef18076..cc7ee2fa0 100644 --- a/src/citra_qt/configuration/configuration_shared.h +++ b/src/citra_qt/configuration/configuration_shared.h @@ -78,11 +78,6 @@ void SetPerGameSetting(QComboBox* combobox, ConfigurationShared::USE_GLOBAL_OFFSET); } -/// Specialization for string settings -template <> -void SetPerGameSetting(QComboBox* combobox, - const Settings::SwitchableSetting* setting); - /// Given an index of a combobox setting extracts the setting taking into /// account per-game status template diff --git a/src/citra_qt/configuration/configure_enhancements.cpp b/src/citra_qt/configuration/configure_enhancements.cpp index 02af11dcf..996c59976 100644 --- a/src/citra_qt/configuration/configure_enhancements.cpp +++ b/src/citra_qt/configuration/configure_enhancements.cpp @@ -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->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(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(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()); diff --git a/src/citra_qt/configuration/configure_enhancements.ui b/src/citra_qt/configuration/configure_enhancements.ui index be9aca92e..5d1f8aeda 100644 --- a/src/citra_qt/configuration/configure_enhancements.ui +++ b/src/citra_qt/configuration/configure_enhancements.ui @@ -6,8 +6,8 @@ 0 0 - 400 - 657 + 440 + 748 @@ -168,7 +168,38 @@ - + + + + None + + + + + Anime4K + + + + + Bicubic + + + + + Nearest Neighbor + + + + + ScaleForce + + + + + xBRZ + + + @@ -352,7 +383,7 @@ - + 0 diff --git a/src/common/color.h b/src/common/color.h index bbcac858e..7e0ae8f4c 100644 --- a/src/common/color.h +++ b/src/common/color.h @@ -52,6 +52,11 @@ namespace Common::Color { return value >> 2; } +/// Averages the RGB components of a color +[[nodiscard]] constexpr u8 AverageRgbComponents(const Common::Vec4& color) { + return (static_cast(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 + */ +[[nodiscard]] inline Common::Vec4 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 + */ +[[nodiscard]] inline Common::Vec4 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 + */ +[[nodiscard]] inline Common::Vec4 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 + */ +[[nodiscard]] inline Common::Vec4 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& 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& 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& 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& 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& 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& 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 diff --git a/src/common/hash.h b/src/common/hash.h index 079039cfc..fde248de9 100644 --- a/src/common/hash.h +++ b/src/common/hash.h @@ -41,6 +41,13 @@ inline u64 HashCombine(std::size_t& seed, const u64 hash) { return seed ^= hash + 0x9e3779b9 + (seed << 6) + (seed >> 2); } +template +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 struct HashableStruct { diff --git a/src/common/math_util.h b/src/common/math_util.h index 382d0197d..575b27b06 100644 --- a/src/common/math_util.h +++ b/src/common/math_util.h @@ -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& rhs) const { + return (left == rhs.left) && (top == rhs.top) && (right == rhs.right) && + (bottom == rhs.bottom); + } + + [[nodiscard]] constexpr bool operator!=(const Rectangle& rhs) const { + return !operator==(rhs); + } + + [[nodiscard]] constexpr Rectangle operator*(const T value) const { + return Rectangle{left * value, top * value, right * value, bottom * value}; + } + [[nodiscard]] constexpr Rectangle operator/(const T value) const { + return Rectangle{left / value, top / value, right / value, bottom / value}; + } + [[nodiscard]] T GetWidth() const { return std::abs(static_cast>(right - left)); } diff --git a/src/common/memory_ref.h b/src/common/memory_ref.h index 70ba10123..4da70a154 100644 --- a/src/common/memory_ref.h +++ b/src/common/memory_ref.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include #include #include @@ -105,6 +106,14 @@ public: return cptr; } + std::span GetWriteBytes(std::size_t size) { + return std::span{cptr, std::min(size, csize)}; + } + + std::span GetReadBytes(std::size_t size) const { + return std::span{cptr, std::min(size, csize)}; + } + std::size_t GetSize() const { return csize; } diff --git a/src/common/settings.cpp b/src/common/settings.cpp index d10c72932..123c40b54 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp @@ -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); diff --git a/src/common/settings.h b/src/common/settings.h index 40cc5e0b7..b48264328 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -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 use_shader_jit{true, "use_shader_jit"}; SwitchableSetting resolution_factor{1, 0, 10, "resolution_factor"}; SwitchableSetting frame_limit{100, 0, 1000, "frame_limit"}; - SwitchableSetting texture_filter_name{"none", "texture_filter_name"}; + SwitchableSetting texture_filter{TextureFilter::None, "texture_filter"}; SwitchableSetting layout_option{LayoutOption::Default, "layout_option"}; SwitchableSetting swap_screen{false, "swap_screen"}; diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 197f6aa5e..463465d59 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -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) diff --git a/src/video_core/generate_shaders.cmake b/src/video_core/generate_shaders.cmake deleted file mode 100644 index e0adca65f..000000000 --- a/src/video_core/generate_shaders.cmake +++ /dev/null @@ -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() diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt new file mode 100644 index 000000000..3e3dae7c9 --- /dev/null +++ b/src/video_core/host_shaders/CMakeLists.txt @@ -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} +) diff --git a/src/video_core/host_shaders/StringShaderHeader.cmake b/src/video_core/host_shaders/StringShaderHeader.cmake new file mode 100644 index 000000000..9f7525535 --- /dev/null +++ b/src/video_core/host_shaders/StringShaderHeader.cmake @@ -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) diff --git a/src/video_core/host_shaders/format_reinterpreter/d24s8_to_rgba8.frag b/src/video_core/host_shaders/format_reinterpreter/d24s8_to_rgba8.frag new file mode 100644 index 000000000..ae0f5a36d --- /dev/null +++ b/src/video_core/host_shaders/format_reinterpreter/d24s8_to_rgba8.frag @@ -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); +} diff --git a/src/video_core/host_shaders/format_reinterpreter/fullscreen_quad.vert b/src/video_core/host_shaders/format_reinterpreter/fullscreen_quad.vert new file mode 100644 index 000000000..62928913c --- /dev/null +++ b/src/video_core/host_shaders/format_reinterpreter/fullscreen_quad.vert @@ -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); +} diff --git a/src/video_core/host_shaders/format_reinterpreter/rgba4_to_rgb5a1.frag b/src/video_core/host_shaders/format_reinterpreter/rgba4_to_rgb5a1.frag new file mode 100644 index 000000000..fb1a874f9 --- /dev/null +++ b/src/video_core/host_shaders/format_reinterpreter/rgba4_to_rgb5a1.frag @@ -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); +} diff --git a/src/video_core/host_shaders/full_screen_triangle.vert b/src/video_core/host_shaders/full_screen_triangle.vert new file mode 100644 index 000000000..7b85e4477 --- /dev/null +++ b/src/video_core/host_shaders/full_screen_triangle.vert @@ -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); +} diff --git a/src/video_core/host_shaders/opengl_present.frag b/src/video_core/host_shaders/opengl_present.frag new file mode 100644 index 000000000..3285301c8 --- /dev/null +++ b/src/video_core/host_shaders/opengl_present.frag @@ -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); +} diff --git a/src/video_core/host_shaders/opengl_present.vert b/src/video_core/host_shaders/opengl_present.vert new file mode 100644 index 000000000..f2a7a0b11 --- /dev/null +++ b/src/video_core/host_shaders/opengl_present.vert @@ -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; +} diff --git a/src/video_core/host_shaders/opengl_present_anaglyph.frag b/src/video_core/host_shaders/opengl_present_anaglyph.frag new file mode 100644 index 000000000..c477c437d --- /dev/null +++ b/src/video_core/host_shaders/opengl_present_anaglyph.frag @@ -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); +} diff --git a/src/video_core/host_shaders/opengl_present_interlaced.frag b/src/video_core/host_shaders/opengl_present_interlaced.frag new file mode 100644 index 000000000..02e507d70 --- /dev/null +++ b/src/video_core/host_shaders/opengl_present_interlaced.frag @@ -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); +} diff --git a/src/video_core/host_shaders/source_shader.h.in b/src/video_core/host_shaders/source_shader.h.in new file mode 100644 index 000000000..4d1b0702f --- /dev/null +++ b/src/video_core/host_shaders/source_shader.h.in @@ -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 + +namespace HostShaders { + +constexpr std::string_view @CONTENTS_NAME@ = { +@CONTENTS@ +}; + +} // namespace HostShaders diff --git a/src/video_core/renderer_opengl/texture_filters/bicubic/bicubic.frag b/src/video_core/host_shaders/texture_filtering/bicubic.frag similarity index 62% rename from src/video_core/renderer_opengl/texture_filters/bicubic/bicubic.frag rename to src/video_core/host_shaders/texture_filtering/bicubic.frag index f384c7864..69043472f 100644 --- a/src/video_core/renderer_opengl/texture_filters/bicubic/bicubic.frag +++ b/src/video_core/host_shaders/texture_filtering/bicubic.frag @@ -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); diff --git a/src/video_core/host_shaders/texture_filtering/nearest_neighbor.frag b/src/video_core/host_shaders/texture_filtering/nearest_neighbor.frag new file mode 100644 index 000000000..5413e7409 --- /dev/null +++ b/src/video_core/host_shaders/texture_filtering/nearest_neighbor.frag @@ -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); +} diff --git a/src/video_core/renderer_opengl/texture_filters/anime4k/refine.frag b/src/video_core/host_shaders/texture_filtering/refine.frag similarity index 68% rename from src/video_core/renderer_opengl/texture_filters/anime4k/refine.frag rename to src/video_core/host_shaders/texture_filtering/refine.frag index 569f30078..5ec6ca1b8 100644 --- a/src/video_core/renderer_opengl/texture_filters/anime4k/refine.frag +++ b/src/video_core/host_shaders/texture_filtering/refine.frag @@ -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; diff --git a/src/video_core/renderer_opengl/texture_filters/scale_force/scale_force.frag b/src/video_core/host_shaders/texture_filtering/scale_force.frag similarity index 97% rename from src/video_core/renderer_opengl/texture_filters/scale_force/scale_force.frag rename to src/video_core/host_shaders/texture_filtering/scale_force.frag index f162278a5..46a24c6df 100644 --- a/src/video_core/renderer_opengl/texture_filters/scale_force/scale_force.frag +++ b/src/video_core/host_shaders/texture_filtering/scale_force.frag @@ -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; diff --git a/src/video_core/renderer_opengl/texture_filters/tex_coord.vert b/src/video_core/host_shaders/texture_filtering/tex_coord.vert similarity index 54% rename from src/video_core/renderer_opengl/texture_filters/tex_coord.vert rename to src/video_core/host_shaders/texture_filtering/tex_coord.vert index e5e153330..b711e6906 100644 --- a/src/video_core/renderer_opengl/texture_filters/tex_coord.vert +++ b/src/video_core/host_shaders/texture_filtering/tex_coord.vert @@ -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)); diff --git a/src/video_core/host_shaders/texture_filtering/x_gradient.frag b/src/video_core/host_shaders/texture_filtering/x_gradient.frag new file mode 100644 index 000000000..8e7dfbd2b --- /dev/null +++ b/src/video_core/host_shaders/texture_filtering/x_gradient.frag @@ -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); +} diff --git a/src/video_core/renderer_opengl/texture_filters/xbrz/xbrz_freescale.frag b/src/video_core/host_shaders/texture_filtering/xbrz_freescale.frag similarity index 96% rename from src/video_core/renderer_opengl/texture_filters/xbrz/xbrz_freescale.frag rename to src/video_core/host_shaders/texture_filtering/xbrz_freescale.frag index 84f1b3503..48032ddbd 100644 --- a/src/video_core/renderer_opengl/texture_filters/xbrz/xbrz_freescale.frag +++ b/src/video_core/host_shaders/texture_filtering/xbrz_freescale.frag @@ -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; diff --git a/src/video_core/host_shaders/texture_filtering/y_gradient.frag b/src/video_core/host_shaders/texture_filtering/y_gradient.frag new file mode 100644 index 000000000..51554beee --- /dev/null +++ b/src/video_core/host_shaders/texture_filtering/y_gradient.frag @@ -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); +} diff --git a/src/video_core/rasterizer_cache/cached_surface.cpp b/src/video_core/rasterizer_cache/cached_surface.cpp deleted file mode 100644 index d09358b8f..000000000 --- a/src/video_core/rasterizer_cache/cached_surface.cpp +++ /dev/null @@ -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(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(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 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(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 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 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(rect.left); - GLint y0 = static_cast(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(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(stride)); - - glActiveTexture(GL_TEXTURE0); - glTexSubImage2D(GL_TEXTURE_2D, 0, x0, y0, static_cast(rect.GetWidth()), - static_cast(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 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& 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(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 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 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 diff --git a/src/video_core/rasterizer_cache/cached_surface.h b/src/video_core/rasterizer_cache/cached_surface.h deleted file mode 100644 index 008cad9a9..000000000 --- a/src/video_core/rasterizer_cache/cached_surface.h +++ /dev/null @@ -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 -#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&& 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 surface; - bool valid = false; -}; - -class RasterizerCacheOpenGL; - -class CachedSurface : public SurfaceParams, public std::enable_shared_from_this { -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 rect); - void DownloadGLTexture(const Common::Rectangle& 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 CreateWatcher() { - auto watcher = std::make_shared(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 gl_buffer; - - // Number of bytes to read from fill_data - u32 fill_size = 0; - std::array fill_data; - OGLTexture texture; - - // level_watchers[i] watches the (i+1)-th level mipmap source surface - std::array, 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> watchers; -}; - -struct CachedTextureCube { - OGLTexture texture; - u16 res_scale = 1; - std::shared_ptr px; - std::shared_ptr nx; - std::shared_ptr py; - std::shared_ptr ny; - std::shared_ptr pz; - std::shared_ptr nz; -}; - -} // namespace OpenGL diff --git a/src/video_core/rasterizer_cache/framebuffer_base.cpp b/src/video_core/rasterizer_cache/framebuffer_base.cpp new file mode 100644 index 000000000..7573254af --- /dev/null +++ b/src/video_core/rasterizer_cache/framebuffer_base.cpp @@ -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 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(static_cast(surfaces_rect.left) + viewport_rect.left * res_scale, + surfaces_rect.left, surfaces_rect.right); + draw_rect.top = + std::clamp(static_cast(surfaces_rect.bottom) + viewport_rect.top * res_scale, + surfaces_rect.bottom, surfaces_rect.top); + draw_rect.right = + std::clamp(static_cast(surfaces_rect.left) + viewport_rect.right * res_scale, + surfaces_rect.left, surfaces_rect.right); + draw_rect.bottom = + std::clamp(static_cast(surfaces_rect.bottom) + viewport_rect.bottom * res_scale, + surfaces_rect.bottom, surfaces_rect.top); + + // Update viewport + viewport.x = static_cast(surfaces_rect.left + viewport_rect.left * res_scale); + viewport.y = static_cast(surfaces_rect.bottom + viewport_rect.bottom * res_scale); + viewport.width = static_cast(viewport_rect.GetWidth() * res_scale); + viewport.height = static_cast(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(surfaces_rect.left + regs.rasterizer.scissor_test.x1 * res_scale); + scissor_rect.bottom = + static_cast(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(surfaces_rect.left + (regs.rasterizer.scissor_test.x2 + 1) * res_scale); + scissor_rect.top = + static_cast(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 diff --git a/src/video_core/rasterizer_cache/framebuffer_base.h b/src/video_core/rasterizer_cache/framebuffer_base.h new file mode 100644 index 000000000..23efebb8b --- /dev/null +++ b/src/video_core/rasterizer_cache/framebuffer_base.h @@ -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 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 DrawRect() const noexcept { + return draw_rect; + } + + Common::Rectangle 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 intervals{}; + Common::Rectangle scissor_rect{}; + Common::Rectangle draw_rect{}; + ViewportInfo viewport; + u32 res_scale{1}; +}; + +} // namespace VideoCore diff --git a/src/video_core/rasterizer_cache/morton_swizzle.h b/src/video_core/rasterizer_cache/morton_swizzle.h deleted file mode 100644 index e8cfc87cd..000000000 --- a/src/video_core/rasterizer_cache/morton_swizzle.h +++ /dev/null @@ -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 -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 -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 tmp_buf; - MortonCopyTile(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(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 tmp_buf; - MortonCopyTile(stride, &tmp_buf[0], gl_buffer); - std::memcpy(tile_buffer, &tmp_buf[0], end - aligned_end); - } -} - -static constexpr std::array morton_to_gl_fns = { - MortonCopy, // 0 - MortonCopy, // 1 - MortonCopy, // 2 - MortonCopy, // 3 - MortonCopy, // 4 - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, // 5 - 13 - MortonCopy, // 14 - nullptr, // 15 - MortonCopy, // 16 - MortonCopy // 17 -}; - -static constexpr std::array gl_to_morton_fns = { - MortonCopy, // 0 - MortonCopy, // 1 - MortonCopy, // 2 - MortonCopy, // 3 - MortonCopy, // 4 - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, // 5 - 13 - MortonCopy, // 14 - nullptr, // 15 - MortonCopy, // 16 - MortonCopy // 17 -}; - -} // namespace OpenGL diff --git a/src/video_core/rasterizer_cache/pixel_format.cpp b/src/video_core/rasterizer_cache/pixel_format.cpp new file mode 100644 index 000000000..0dde49120 --- /dev/null +++ b/src/video_core/rasterizer_cache/pixel_format.cpp @@ -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 diff --git a/src/video_core/rasterizer_cache/pixel_format.h b/src/video_core/rasterizer_cache/pixel_format.h index 218f4321e..34aa08ff3 100644 --- a/src/video_core/rasterizer_cache/pixel_format.h +++ b/src/video_core/rasterizer_cache/pixel_format.h @@ -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 #include #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::max(), }; +constexpr std::size_t PIXEL_FORMAT_COUNT = static_cast(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(format); - return (format_index < 14) ? static_cast(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(format); - return (format_index < 5) ? static_cast(format) : PixelFormat::Invalid; -} - -constexpr PixelFormat PixelFormatFromDepthFormat(Pica::FramebufferRegs::DepthFormat format) { - const u32 format_index = static_cast(format); - return (format_index < 4) ? static_cast(format_index + 14) : PixelFormat::Invalid; -} - -constexpr PixelFormat PixelFormatFromGPUPixelFormat(GPU::Regs::PixelFormat format) { - const u32 format_index = static_cast(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(format) : PixelFormat::Invalid; - } -} - -constexpr SurfaceType GetFormatType(PixelFormat pixel_format) { - const u32 format_index = static_cast(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 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(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(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(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 diff --git a/src/video_core/rasterizer_cache/rasterizer_cache.cpp b/src/video_core/rasterizer_cache/rasterizer_cache.cpp index ad88e2bc9..b5577f80e 100644 --- a/src/video_core/rasterizer_cache/rasterizer_cache.cpp +++ b/src/video_core/rasterizer_cache/rasterizer_cache.cpp @@ -7,156 +7,36 @@ #include "common/alignment.h" #include "common/logging/log.h" #include "common/microprofile.h" -#include "video_core/pica_state.h" +#include "core/memory.h" #include "video_core/rasterizer_cache/rasterizer_cache.h" +#include "video_core/regs.h" #include "video_core/renderer_base.h" -#include "video_core/renderer_opengl/gl_format_reinterpreter.h" -#include "video_core/renderer_opengl/texture_downloader_es.h" -#include "video_core/renderer_opengl/texture_filters/texture_filterer.h" +#include "video_core/renderer_opengl/gl_texture_runtime.h" -namespace OpenGL { +namespace VideoCore { -// TODO: Deduplicate this -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; -} - -static ClearValue ToClearValue(Aspect aspect, PixelFormat format, const u8* fill_data) { - ClearValue result{}; - switch (aspect) { - case Aspect::Color: { - Pica::Texture::TextureInfo tex_info{}; - tex_info.format = static_cast(format); - - Common::Vec4 color = Pica::Texture::LookupTexture(fill_data, 0, 0, tex_info); - result.color = color / 255.f; - break; - } - case Aspect::Depth: { - u32 depth_uint = 0; - if (format == PixelFormat::D16) { - std::memcpy(&depth_uint, fill_data, 2); - result.depth = depth_uint / 65535.0f; // 2^16 - 1 - } else if (format == PixelFormat::D24) { - std::memcpy(&depth_uint, fill_data, 3); - result.depth = depth_uint / 16777215.0f; // 2^24 - 1 - } - break; - } - case Aspect::DepthStencil: { - u32 clear_value_uint; - std::memcpy(&clear_value_uint, fill_data, sizeof(u32)); - - result.depth = (clear_value_uint & 0xFFFFFF) / 16777215.0f; // 2^24 - 1 - result.stencil = (clear_value_uint >> 24); - break; - } - } - - return result; -} - -template -static constexpr auto RangeFromInterval(Map& map, const Interval& interval) { - return boost::make_iterator_range(map.equal_range(interval)); -} - -// Allocate an uninitialized texture of appropriate size and format for the surface -OGLTexture RasterizerCacheOpenGL::AllocateSurfaceTexture(const FormatTuple& tuple, u32 width, - u32 height) { - auto recycled_tex = host_texture_recycler.find({tuple, width, height}); - if (recycled_tex != host_texture_recycler.end()) { - OGLTexture texture = std::move(recycled_tex->second); - host_texture_recycler.erase(recycled_tex); - return texture; - } - - const GLsizei levels = static_cast(std::log2(std::max(width, height))) + 1; - - OGLTexture texture; - texture.Create(); - texture.Allocate(GL_TEXTURE_2D, levels, tuple.internal_format, width, height); - - return texture; -} +namespace { MICROPROFILE_DEFINE(RasterizerCache_CopySurface, "RasterizerCache", "CopySurface", MP_RGB(128, 192, 64)); -void RasterizerCacheOpenGL::CopySurface(const Surface& src_surface, const Surface& dst_surface, - SurfaceInterval copy_interval) { - MICROPROFILE_SCOPE(RasterizerCache_CopySurface); - SurfaceParams subrect_params = dst_surface->FromInterval(copy_interval); - ASSERT(subrect_params.GetInterval() == copy_interval); - ASSERT(src_surface != dst_surface); - - // This is only called when CanCopy is true, no need to run checks here - const Aspect aspect = ToAspect(dst_surface->type); - if (src_surface->type == SurfaceType::Fill) { - // FillSurface needs a 4 bytes buffer - const u32 fill_offset = - (boost::icl::first(copy_interval) - src_surface->addr) % src_surface->fill_size; - std::array fill_buffer; - - u32 fill_buff_pos = fill_offset; - for (std::size_t i = 0; i < fill_buffer.size(); i++) { - fill_buffer[i] = src_surface->fill_data[fill_buff_pos++ % src_surface->fill_size]; - } - - const auto clear_rect = dst_surface->GetScaledSubRect(subrect_params); - const ClearValue clear_value = - ToClearValue(aspect, dst_surface->pixel_format, fill_buffer.data()); - - runtime.ClearTexture(dst_surface->texture, {aspect, clear_rect}, clear_value); - return; - } - - if (src_surface->CanSubRect(subrect_params)) { - const auto src_rect = src_surface->GetScaledSubRect(subrect_params); - const auto dst_rect = dst_surface->GetScaledSubRect(subrect_params); - - runtime.BlitTextures(src_surface->texture, {aspect, src_rect}, dst_surface->texture, - {aspect, dst_rect}); - return; - } - - UNREACHABLE(); +constexpr auto RangeFromInterval(const auto& map, const auto& interval) { + return boost::make_iterator_range(map.equal_range(interval)); } enum MatchFlags { - Invalid = 1, // Flag that can be applied to other match types, invalid matches require - // validation before they can be used - Exact = 1 << 1, // Surfaces perfectly match - SubRect = 1 << 2, // Surface encompasses params - Copy = 1 << 3, // Surface we can copy from - Expand = 1 << 4, // Surface that can expand params - TexCopy = 1 << 5 // Surface that will match a display transfer "texture copy" parameters + Exact = 1 << 0, ///< Surfaces perfectly match + SubRect = 1 << 1, ///< Surface encompasses params + Copy = 1 << 2, ///< Surface we can copy from + Expand = 1 << 3, ///< Surface that can expand params + TexCopy = 1 << 4, ///< Surface that will match a display transfer "texture copy" parameters }; -static constexpr MatchFlags operator|(MatchFlags lhs, MatchFlags rhs) { - return static_cast(static_cast(lhs) | static_cast(rhs)); -} - /// Get the best surface match (and its match type) for the given flags template -static Surface FindMatch(const SurfaceCache& surface_cache, const SurfaceParams& params, - ScaleMatch match_scale_type, - std::optional validate_interval = std::nullopt) { - Surface match_surface = nullptr; +auto FindMatch(const auto& surface_cache, const SurfaceParams& params, ScaleMatch match_scale_type, + std::optional validate_interval = std::nullopt) { + RasterizerCache::SurfaceRef match_surface = nullptr; bool match_valid = false; u32 match_scale = 0; SurfaceInterval match_interval{}; @@ -166,15 +46,12 @@ static Surface FindMatch(const SurfaceCache& surface_cache, const SurfaceParams& const bool res_scale_matched = match_scale_type == ScaleMatch::Exact ? (params.res_scale == surface->res_scale) : (params.res_scale <= surface->res_scale); - // validity will be checked in GetCopyableInterval - bool is_valid = + // Validity will be checked in GetCopyableInterval + const bool is_valid = find_flags & MatchFlags::Copy ? true : surface->IsRegionValid(validate_interval.value_or(params.GetInterval())); - if (!(find_flags & MatchFlags::Invalid) && !is_valid) - continue; - auto IsMatch_Helper = [&](auto check_type, auto match_fn) { if (!(find_flags & check_type)) return; @@ -224,7 +101,7 @@ static Surface FindMatch(const SurfaceCache& surface_cache, const SurfaceParams& IsMatch_Helper(std::integral_constant{}, [&] { ASSERT(validate_interval); auto copy_interval = - params.FromInterval(*validate_interval).GetCopyableInterval(surface); + surface->GetCopyableInterval(params.FromInterval(*validate_interval)); bool matched = boost::icl::length(copy_interval & *validate_interval) != 0 && surface->CanCopy(params, copy_interval); return std::make_pair(matched, copy_interval); @@ -240,43 +117,223 @@ static Surface FindMatch(const SurfaceCache& surface_cache, const SurfaceParams& return match_surface; } -RasterizerCacheOpenGL::RasterizerCacheOpenGL(VideoCore::RendererBase& renderer_) - : renderer{renderer_} { - resolution_scale_factor = renderer.GetResolutionScaleFactor(); - texture_filterer = std::make_unique( - Settings::values.texture_filter_name.GetValue(), resolution_scale_factor); - format_reinterpreter = std::make_unique(); - texture_downloader_es = std::make_unique(false); -} +} // Anonymous namespace -RasterizerCacheOpenGL::~RasterizerCacheOpenGL() { +RasterizerCache::RasterizerCache(Memory::MemorySystem& memory_, OpenGL::TextureRuntime& runtime_, + Pica::Regs& regs_, RendererBase& renderer_) + : memory{memory_}, runtime{runtime_}, regs{regs_}, renderer{renderer_}, + resolution_scale_factor{renderer.GetResolutionScaleFactor()}, + use_filter{Settings::values.texture_filter.GetValue() != Settings::TextureFilter::None} {} + +RasterizerCache::~RasterizerCache() { #ifndef ANDROID // This is for switching renderers, which is unsupported on Android, and costly on shutdown ClearAll(false); #endif } -MICROPROFILE_DEFINE(RasterizerCache_BlitSurface, "RasterizerCache", "BlitSurface", - MP_RGB(128, 192, 64)); -bool RasterizerCacheOpenGL::BlitSurfaces(const Surface& src_surface, - const Common::Rectangle& src_rect, - const Surface& dst_surface, - const Common::Rectangle& dst_rect) { - MICROPROFILE_SCOPE(RasterizerCache_BlitSurface); - - if (CheckFormatsBlittable(src_surface->pixel_format, dst_surface->pixel_format)) { - dst_surface->InvalidateAllWatcher(); - - const Aspect aspect = ToAspect(src_surface->type); - return runtime.BlitTextures(src_surface->texture, {aspect, src_rect}, dst_surface->texture, - {aspect, dst_rect}); +bool RasterizerCache::AccelerateTextureCopy(const GPU::Regs::DisplayTransferConfig& config) { + // Texture copy size is aligned to 16 byte units + const u32 copy_size = Common::AlignDown(config.texture_copy.size, 16); + if (copy_size == 0) { + return false; } - 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; + + const auto [src_surface, src_rect] = GetTexCopySurface(src_params); + if (!src_surface) { + return false; + } + + // If the output gap is nonzero ensure the output width matches the source rectangle width, + // otherwise we cannot use hardware accelerated texture copy. The former is in terms of bytes + // not pixels so first get the unscaled copy width and calculate the bytes this corresponds to. + // Note that tiled textures are laid out sequentially in memory, so we multiply that by eight + // to get the correct byte count. + 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; + const auto [dst_surface, dst_rect] = + GetSurfaceSubRect(dst_params, ScaleMatch::Upscale, load_gap); + + if (!dst_surface || dst_surface->type == SurfaceType::Texture || + !CheckFormatsBlittable(src_surface->pixel_format, dst_surface->pixel_format)) { + return false; + } + + ASSERT(src_rect.GetWidth() == dst_rect.GetWidth()); + + const TextureCopy texture_copy = { + .src_level = src_surface->LevelOf(src_params.addr), + .dst_level = dst_surface->LevelOf(dst_params.addr), + .src_offset = {src_rect.left, src_rect.bottom}, + .dst_offset = {dst_rect.left, dst_rect.bottom}, + .extent = {src_rect.GetWidth(), src_rect.GetHeight()}, + }; + runtime.CopyTextures(*src_surface, *dst_surface, texture_copy); + + InvalidateRegion(dst_params.addr, dst_params.size, dst_surface); + return true; } -Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, ScaleMatch match_res_scale, - bool load_if_create) { +bool RasterizerCache::AccelerateDisplayTransfer(const GPU::Regs::DisplayTransferConfig& config) { + 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(); + + auto [src_surface, src_rect] = GetSurfaceSubRect(src_params, ScaleMatch::Ignore, true); + if (!src_surface) { + return false; + } + + dst_params.res_scale = src_surface->res_scale; + + const auto [dst_surface, dst_rect] = GetSurfaceSubRect(dst_params, ScaleMatch::Upscale, false); + if (!dst_surface) { + 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 (!CheckFormatsBlittable(src_surface->pixel_format, dst_surface->pixel_format)) { + return false; + } + + const TextureBlit texture_blit = { + .src_level = src_surface->LevelOf(src_params.addr), + .dst_level = dst_surface->LevelOf(dst_params.addr), + .src_rect = src_rect, + .dst_rect = dst_rect, + }; + runtime.BlitTextures(*src_surface, *dst_surface, texture_blit); + + InvalidateRegion(dst_params.addr, dst_params.size, dst_surface); + return true; +} + +bool RasterizerCache::AccelerateFill(const GPU::Regs::MemoryFillConfig& config) { + SurfaceParams params; + params.addr = config.GetStartAddress(); + params.end = config.GetEndAddress(); + params.size = params.end - params.addr; + params.type = SurfaceType::Fill; + params.res_scale = std::numeric_limits::max(); + + SurfaceRef fill_surface = std::make_shared(runtime, params); + + std::memcpy(&fill_surface->fill_data[0], &config.value_32bit, sizeof(u32)); + if (config.fill_32bit) { + fill_surface->fill_size = 4; + } else if (config.fill_24bit) { + fill_surface->fill_size = 3; + } else { + fill_surface->fill_size = 2; + } + + RegisterSurface(fill_surface); + InvalidateRegion(fill_surface->addr, fill_surface->size, fill_surface); + return true; +} + +void RasterizerCache::CopySurface(const SurfaceRef& src_surface, const SurfaceRef& dst_surface, + SurfaceInterval copy_interval) { + MICROPROFILE_SCOPE(RasterizerCache_CopySurface); + + const PAddr copy_addr = copy_interval.lower(); + const SurfaceParams subrect_params = dst_surface->FromInterval(copy_interval); + const auto dst_rect = dst_surface->GetScaledSubRect(subrect_params); + ASSERT(subrect_params.GetInterval() == copy_interval && src_surface != dst_surface); + + if (src_surface->type == SurfaceType::Fill) { + const TextureClear clear = { + .texture_level = dst_surface->LevelOf(copy_addr), + .texture_rect = dst_rect, + .value = src_surface->MakeClearValue(copy_addr, dst_surface->pixel_format), + }; + runtime.ClearTexture(*dst_surface, clear); + return; + } + + const TextureBlit blit = { + .src_level = src_surface->LevelOf(copy_addr), + .dst_level = dst_surface->LevelOf(copy_addr), + .src_rect = src_surface->GetScaledSubRect(subrect_params), + .dst_rect = dst_rect, + }; + runtime.BlitTextures(*src_surface, *dst_surface, blit); +} + +RasterizerCache::SurfaceRef RasterizerCache::GetSurface(const SurfaceParams& params, + ScaleMatch match_res_scale, + bool load_if_create) { if (params.addr == 0 || params.height * params.width == 0) { return nullptr; } @@ -286,26 +343,25 @@ Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, ScaleMatc ASSERT(!params.is_tiled || (params.width % 8 == 0 && params.height % 8 == 0)); // Check for an exact match in existing surfaces - Surface surface = - FindMatch(surface_cache, params, match_res_scale); + SurfaceRef surface = FindMatch(surface_cache, params, match_res_scale); - if (surface == nullptr) { + if (!surface) { u16 target_res_scale = params.res_scale; if (match_res_scale != ScaleMatch::Exact) { // This surface may have a subrect of another surface with a higher res_scale, find // it to adjust our params SurfaceParams find_params = params; - Surface expandable = FindMatch( - surface_cache, find_params, match_res_scale); - if (expandable != nullptr && expandable->res_scale > target_res_scale) { + SurfaceRef expandable = + FindMatch(surface_cache, find_params, match_res_scale); + if (expandable && expandable->res_scale > target_res_scale) { target_res_scale = expandable->res_scale; } // Keep res_scale when reinterpreting d24s8 -> rgba8 if (params.pixel_format == PixelFormat::RGBA8) { find_params.pixel_format = PixelFormat::D24S8; - expandable = FindMatch( - surface_cache, find_params, match_res_scale); - if (expandable != nullptr && expandable->res_scale > target_res_scale) { + expandable = + FindMatch(surface_cache, find_params, match_res_scale); + if (expandable && expandable->res_scale > target_res_scale) { target_res_scale = expandable->res_scale; } } @@ -323,25 +379,23 @@ Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, ScaleMatc return surface; } -SurfaceRect_Tuple RasterizerCacheOpenGL::GetSurfaceSubRect(const SurfaceParams& params, - ScaleMatch match_res_scale, - bool load_if_create) { +RasterizerCache::SurfaceRect_Tuple RasterizerCache::GetSurfaceSubRect(const SurfaceParams& params, + ScaleMatch match_res_scale, + bool load_if_create) { if (params.addr == 0 || params.height * params.width == 0) { return std::make_tuple(nullptr, Common::Rectangle{}); } // Attempt to find encompassing surface - Surface surface = FindMatch(surface_cache, params, - match_res_scale); + SurfaceRef surface = FindMatch(surface_cache, params, match_res_scale); // Check if FindMatch failed because of res scaling // If that's the case create a new surface with // the dimensions of the lower res_scale surface // to suggest it should not be used again - if (surface == nullptr && match_res_scale != ScaleMatch::Ignore) { - surface = FindMatch(surface_cache, params, - ScaleMatch::Ignore); - if (surface != nullptr) { + if (!surface && match_res_scale != ScaleMatch::Ignore) { + surface = FindMatch(surface_cache, params, ScaleMatch::Ignore); + if (surface) { SurfaceParams new_params = *surface; new_params.res_scale = params.res_scale; @@ -359,10 +413,9 @@ SurfaceRect_Tuple RasterizerCacheOpenGL::GetSurfaceSubRect(const SurfaceParams& } // Check for a surface we can expand before creating a new one - if (surface == nullptr) { - surface = FindMatch(surface_cache, aligned_params, - match_res_scale); - if (surface != nullptr) { + if (!surface) { + surface = FindMatch(surface_cache, aligned_params, match_res_scale); + if (surface) { aligned_params.width = aligned_params.stride; aligned_params.UpdateParams(); @@ -372,15 +425,12 @@ SurfaceRect_Tuple RasterizerCacheOpenGL::GetSurfaceSubRect(const SurfaceParams& new_params.size = new_params.end - new_params.addr; new_params.height = new_params.size / aligned_params.BytesInPixels(aligned_params.stride); + new_params.UpdateParams(); ASSERT(new_params.size % aligned_params.BytesInPixels(aligned_params.stride) == 0); - Surface new_surface = CreateSurface(new_params); + SurfaceRef new_surface = CreateSurface(new_params); DuplicateSurface(surface, new_surface); - - // Delete the expanded surface, this can't be done safely yet - // because it may still be in use - surface->UnlinkAllWatcher(); // unlink watchers as if this surface is already deleted - remove_surfaces.emplace(surface); + UnregisterSurface(surface); surface = new_surface; RegisterSurface(new_surface); @@ -388,7 +438,7 @@ SurfaceRect_Tuple RasterizerCacheOpenGL::GetSurfaceSubRect(const SurfaceParams& } // No subrect found - create and return a new surface - if (surface == nullptr) { + if (!surface) { SurfaceParams new_params = aligned_params; // Can't have gaps in a surface new_params.width = aligned_params.stride; @@ -402,16 +452,16 @@ SurfaceRect_Tuple RasterizerCacheOpenGL::GetSurfaceSubRect(const SurfaceParams& return std::make_tuple(surface, surface->GetScaledSubRect(params)); } -Surface RasterizerCacheOpenGL::GetTextureSurface( +RasterizerCache::SurfaceRef RasterizerCache::GetTextureSurface( const Pica::TexturingRegs::FullTextureConfig& config) { - Pica::Texture::TextureInfo info = - Pica::Texture::TextureInfo::FromPicaRegister(config.config, config.format); - return GetTextureSurface(info, config.config.lod.max_level); + const auto info = Pica::Texture::TextureInfo::FromPicaRegister(config.config, config.format); + const u32 max_level = MipLevels(info.width, info.height, config.config.lod.max_level) - 1; + return GetTextureSurface(info, max_level); } -Surface RasterizerCacheOpenGL::GetTextureSurface(const Pica::Texture::TextureInfo& info, - u32 max_level) { - if (info.physical_address == 0) { +RasterizerCache::SurfaceRef RasterizerCache::GetTextureSurface( + const Pica::Texture::TextureInfo& info, u32 max_level) { + if (info.physical_address == 0) [[unlikely]] { return nullptr; } @@ -419,214 +469,133 @@ Surface RasterizerCacheOpenGL::GetTextureSurface(const Pica::Texture::TextureInf params.addr = info.physical_address; params.width = info.width; params.height = info.height; + params.levels = max_level + 1; params.is_tiled = true; params.pixel_format = PixelFormatFromTextureFormat(info.format); - params.res_scale = texture_filterer->IsNull() ? 1 : resolution_scale_factor; + params.res_scale = use_filter ? resolution_scale_factor : 1; params.UpdateParams(); u32 min_width = info.width >> max_level; u32 min_height = info.height >> max_level; if (min_width % 8 != 0 || min_height % 8 != 0) { - // This code is Temponary for 8x4 / 4x4 texture (commonly used by Health bar) support - // implementation might not be accurate and needs further testing + // This code is for 8x4 and 4x4 textures (commonly used by games for health bar) + // The implementation might not be accurate and needs further testing. if (min_width % 4 == 0 && min_height % 4 == 0 && min_width * min_height <= 32) { - Surface src_surface; - Common::Rectangle rect; - std::tie(src_surface, rect) = GetSurfaceSubRect(params, ScaleMatch::Ignore, true); - + const auto [src_surface, rect] = GetSurfaceSubRect(params, ScaleMatch::Ignore, true); params.res_scale = src_surface->res_scale; - Surface tmp_surface = CreateSurface(params); - const Aspect aspect = ToAspect(tmp_surface->type); - runtime.BlitTextures(src_surface->texture, {aspect, rect}, tmp_surface->texture, - {aspect, tmp_surface->GetScaledRect(), 0}); + SurfaceRef tmp_surface = CreateSurface(params); - remove_surfaces.emplace(tmp_surface); + const TextureBlit blit = { + .src_level = src_surface->LevelOf(params.addr), + .dst_level = 0, + .src_rect = rect, + .dst_rect = tmp_surface->GetScaledRect(), + }; + runtime.BlitTextures(*src_surface, *tmp_surface, blit); return tmp_surface; } - LOG_CRITICAL(Render_OpenGL, "Texture size ({}x{}) is not multiple of 4", min_width, - min_height); + LOG_CRITICAL(HW_GPU, "Texture size ({}x{}) is not multiple of 4", min_width, min_height); return nullptr; } if (info.width != (min_width << max_level) || info.height != (min_height << max_level)) { - LOG_CRITICAL(Render_OpenGL, - "Texture size ({}x{}) does not support required mipmap level ({})", + LOG_CRITICAL(HW_GPU, "Texture size ({}x{}) does not support required mipmap level ({})", params.width, params.height, max_level); return nullptr; } - auto surface = GetSurface(params, ScaleMatch::Ignore, true); - if (!surface) - return nullptr; - - // Update mipmap if necessary - if (max_level != 0) { - if (max_level >= 8) { - // since PICA only supports texture size between 8 and 1024, there are at most eight - // possible mipmap levels including the base. - LOG_CRITICAL(Render_OpenGL, "Unsupported mipmap level {}", max_level); - return nullptr; - } - - // Allocate more mipmap level if necessary - if (surface->max_level < max_level) { - if (surface->is_custom || !texture_filterer->IsNull()) { - // TODO: proper mipmap support for custom textures - runtime.GenerateMipmaps(surface->texture, max_level); - } - - surface->max_level = max_level; - } - - // Blit mipmaps that have been invalidated - SurfaceParams surface_params = *surface; - for (u32 level = 1; level <= max_level; ++level) { - // In PICA all mipmap levels are stored next to each other - surface_params.addr += - surface_params.width * surface_params.height * surface_params.GetFormatBpp() / 8; - surface_params.width /= 2; - surface_params.height /= 2; - surface_params.stride = 0; // reset stride and let UpdateParams re-initialize it - surface_params.UpdateParams(); - - auto& watcher = surface->level_watchers[level - 1]; - if (!watcher || !watcher->Get()) { - auto level_surface = GetSurface(surface_params, ScaleMatch::Ignore, true); - if (level_surface) { - watcher = level_surface->CreateWatcher(); - } else { - watcher = nullptr; - } - } - - if (watcher && !watcher->IsValid()) { - auto level_surface = watcher->Get(); - if (!level_surface->invalid_regions.empty()) { - ValidateSurface(level_surface, level_surface->addr, level_surface->size); - } - - if (!surface->is_custom && texture_filterer->IsNull()) { - const auto src_rect = level_surface->GetScaledRect(); - const auto dst_rect = surface_params.GetScaledRect(); - const Aspect aspect = ToAspect(surface->type); - - runtime.BlitTextures(level_surface->texture, {aspect, src_rect}, - surface->texture, {aspect, dst_rect, level}); - } - - watcher->Validate(); - } - } - } - - return surface; + return GetSurface(params, ScaleMatch::Ignore, true); } -const CachedTextureCube& RasterizerCacheOpenGL::GetTextureCube(const TextureCubeConfig& config) { - auto& cube = texture_cube_cache[config]; +RasterizerCache::SurfaceRef RasterizerCache::GetTextureCube(const TextureCubeConfig& config) { + auto [it, new_surface] = texture_cube_cache.try_emplace(config); + TextureCube& cube = it->second; - struct Face { - Face(std::shared_ptr& watcher, PAddr address) - : watcher(watcher), address(address) {} - std::shared_ptr& watcher; - PAddr address; - }; + if (new_surface) { + SurfaceParams cube_params = { + .addr = config.px, + .width = config.width, + .height = config.width, + .stride = config.width, + .levels = config.levels, + .res_scale = use_filter ? resolution_scale_factor : 1, + .texture_type = TextureType::CubeMap, + .pixel_format = PixelFormatFromTextureFormat(config.format), + .type = SurfaceType::Texture, + }; + cube_params.UpdateParams(); + cube.surface = CreateSurface(cube_params); + } - const std::array faces{{ - {cube.px, config.px}, - {cube.nx, config.nx}, - {cube.py, config.py}, - {cube.ny, config.ny}, - {cube.pz, config.pz}, - {cube.nz, config.nz}, - }}; + const u32 scaled_size = cube.surface->GetScaledWidth(); + const std::array addresses = {config.px, config.nx, config.py, config.ny, config.pz, config.nz}; - for (const Face& face : faces) { - if (!face.watcher || !face.watcher->Get()) { - Pica::Texture::TextureInfo info; - info.physical_address = face.address; - info.height = info.width = config.width; - info.format = config.format; - info.SetDefaultStride(); - auto surface = GetTextureSurface(info); - if (surface) { - face.watcher = surface->CreateWatcher(); - } else { - // Can occur when texture address is invalid. We mark the watcher with nullptr - // in this case and the content of the face wouldn't get updated. These are - // usually leftover setup in the texture unit and games are not supposed to draw - // using them. - face.watcher = nullptr; + for (u32 i = 0; i < addresses.size(); i++) { + if (!addresses[i]) { + continue; + } + + Pica::Texture::TextureInfo info = { + .physical_address = addresses[i], + .width = config.width, + .height = config.width, + .format = config.format, + }; + info.SetDefaultStride(); + + SurfaceRef& face_surface = cube.faces[i]; + if (!face_surface || !face_surface->registered) { + face_surface = GetTextureSurface(info, config.levels - 1); + ASSERT(face_surface->levels == config.levels); + } + if (cube.ticks[i] != face_surface->ModificationTick()) { + for (u32 level = 0; level < face_surface->levels; level++) { + const TextureCopy texture_copy = { + .src_level = level, + .dst_level = level, + .src_layer = 0, + .dst_layer = i, + .src_offset = {0, 0}, + .dst_offset = {0, 0}, + .extent = {scaled_size >> level, scaled_size >> level}, + }; + runtime.CopyTextures(*face_surface, *cube.surface, texture_copy); } + cube.ticks[i] = face_surface->ModificationTick(); } } - if (cube.texture.handle == 0) { - for (const Face& face : faces) { - if (face.watcher) { - auto surface = face.watcher->Get(); - cube.res_scale = std::max(cube.res_scale, surface->res_scale); - } - } - - const auto& tuple = GetFormatTuple(PixelFormatFromTextureFormat(config.format)); - const u32 width = cube.res_scale * config.width; - const GLsizei levels = static_cast(std::log2(width)) + 1; - - // Allocate the cube texture - cube.texture.Create(); - cube.texture.Allocate(GL_TEXTURE_CUBE_MAP, levels, tuple.internal_format, width, width); - } - - u32 scaled_size = cube.res_scale * config.width; - - for (std::size_t i = 0; i < faces.size(); i++) { - const Face& face = faces[i]; - if (face.watcher && !face.watcher->IsValid()) { - auto surface = face.watcher->Get(); - if (!surface->invalid_regions.empty()) { - ValidateSurface(surface, surface->addr, surface->size); - } - - const auto src_rect = surface->GetScaledRect(); - const auto dst_rect = Common::Rectangle{0, scaled_size, scaled_size, 0}; - const Aspect aspect = ToAspect(surface->type); - runtime.BlitTextures(surface->texture, {aspect, src_rect}, cube.texture, - {aspect, dst_rect, 0, static_cast(i)}, true); - - face.watcher->Validate(); - } - } - - return cube; + return cube.surface; } -SurfaceSurfaceRect_Tuple RasterizerCacheOpenGL::GetFramebufferSurfaces( - bool using_color_fb, bool using_depth_fb, const Common::Rectangle& viewport_rect) { - const auto& regs = Pica::g_state.regs; +OpenGL::Framebuffer RasterizerCache::GetFramebufferSurfaces(bool using_color_fb, + bool using_depth_fb) { const auto& config = regs.framebuffer.framebuffer; // Update resolution_scale_factor and reset cache if changed const u32 scale_factor = renderer.GetResolutionScaleFactor(); const bool resolution_scale_changed = resolution_scale_factor != scale_factor; const bool texture_filter_changed = - renderer.Settings().texture_filter_update_requested.exchange(false) && - texture_filterer->Reset(Settings::values.texture_filter_name.GetValue(), scale_factor); + renderer.Settings().texture_filter_update_requested.exchange(false); if (resolution_scale_changed || texture_filter_changed) { resolution_scale_factor = scale_factor; + use_filter = Settings::values.texture_filter.GetValue() != Settings::TextureFilter::None; FlushAll(); while (!surface_cache.empty()) UnregisterSurface(*surface_cache.begin()->second.begin()); texture_cube_cache.clear(); } - Common::Rectangle viewport_clamped{ - static_cast(std::clamp(viewport_rect.left, 0, static_cast(config.GetWidth()))), - static_cast(std::clamp(viewport_rect.top, 0, static_cast(config.GetHeight()))), - static_cast(std::clamp(viewport_rect.right, 0, static_cast(config.GetWidth()))), - static_cast( - std::clamp(viewport_rect.bottom, 0, static_cast(config.GetHeight())))}; + const s32 framebuffer_width = config.GetWidth(); + const s32 framebuffer_height = config.GetHeight(); + const auto viewport_rect = regs.rasterizer.GetViewportRect(); + const Common::Rectangle viewport_clamped = { + static_cast(std::clamp(viewport_rect.left, 0, framebuffer_width)), + static_cast(std::clamp(viewport_rect.top, 0, framebuffer_height)), + static_cast(std::clamp(viewport_rect.right, 0, framebuffer_width)), + static_cast(std::clamp(viewport_rect.bottom, 0, framebuffer_height)), + }; // get color and depth surfaces SurfaceParams color_params; @@ -650,25 +619,27 @@ SurfaceSurfaceRect_Tuple RasterizerCacheOpenGL::GetFramebufferSurfaces( // Make sure that framebuffers don't overlap if both color and depth are being used if (using_color_fb && using_depth_fb && boost::icl::length(color_vp_interval & depth_vp_interval)) { - LOG_CRITICAL(Render_OpenGL, "Color and depth framebuffer memory regions overlap; " - "overlapping framebuffers not supported!"); + LOG_CRITICAL(HW_GPU, "Color and depth framebuffer memory regions overlap; " + "overlapping framebuffers not supported!"); using_depth_fb = false; } Common::Rectangle color_rect{}; - Surface color_surface = nullptr; + SurfaceRef color_surface = nullptr; + u32 color_level{}; if (using_color_fb) std::tie(color_surface, color_rect) = GetSurfaceSubRect(color_params, ScaleMatch::Exact, false); Common::Rectangle depth_rect{}; - Surface depth_surface = nullptr; + SurfaceRef depth_surface = nullptr; + u32 depth_level{}; if (using_depth_fb) std::tie(depth_surface, depth_rect) = GetSurfaceSubRect(depth_params, ScaleMatch::Exact, false); Common::Rectangle fb_rect{}; - if (color_surface != nullptr && depth_surface != nullptr) { + if (color_surface && depth_surface) { fb_rect = color_rect; // Color and Depth surfaces must have the same dimensions and offsets if (color_rect.bottom != depth_rect.bottom || color_rect.top != depth_rect.top || @@ -677,56 +648,52 @@ SurfaceSurfaceRect_Tuple RasterizerCacheOpenGL::GetFramebufferSurfaces( depth_surface = GetSurface(depth_params, ScaleMatch::Exact, false); fb_rect = color_surface->GetScaledRect(); } - } else if (color_surface != nullptr) { + } else if (color_surface) { fb_rect = color_rect; - } else if (depth_surface != nullptr) { + } else if (depth_surface) { fb_rect = depth_rect; } - if (color_surface != nullptr) { + if (color_surface) { + color_level = color_surface->LevelOf(color_params.addr); ValidateSurface(color_surface, boost::icl::first(color_vp_interval), boost::icl::length(color_vp_interval)); - color_surface->InvalidateAllWatcher(); } - if (depth_surface != nullptr) { + if (depth_surface) { + depth_level = depth_surface->LevelOf(depth_params.addr); ValidateSurface(depth_surface, boost::icl::first(depth_vp_interval), boost::icl::length(depth_vp_interval)); - depth_surface->InvalidateAllWatcher(); } - return std::make_tuple(color_surface, depth_surface, fb_rect); + render_targets = RenderTargets{ + .color_surface = color_surface, + .depth_surface = depth_surface, + }; + + return OpenGL::Framebuffer{ + runtime, color_surface.get(), color_level, depth_surface.get(), depth_level, regs, fb_rect}; } -Surface RasterizerCacheOpenGL::GetFillSurface(const GPU::Regs::MemoryFillConfig& config) { - SurfaceParams params; - params.addr = config.GetStartAddress(); - params.end = config.GetEndAddress(); - params.size = params.end - params.addr; - params.type = SurfaceType::Fill; - params.res_scale = std::numeric_limits::max(); - - Surface new_surface = std::make_shared(params, *this, runtime); - - std::memcpy(&new_surface->fill_data[0], &config.value_32bit, 4); - if (config.fill_32bit) { - new_surface->fill_size = 4; - } else if (config.fill_24bit) { - new_surface->fill_size = 3; - } else { - new_surface->fill_size = 2; +void RasterizerCache::InvalidateFramebuffer(const OpenGL::Framebuffer& framebuffer) { + if (framebuffer.HasAttachment(SurfaceType::Color)) { + const auto interval = framebuffer.Interval(SurfaceType::Color); + InvalidateRegion(boost::icl::first(interval), boost::icl::length(interval), + render_targets.color_surface); + } + if (framebuffer.HasAttachment(SurfaceType::DepthStencil)) { + const auto interval = framebuffer.Interval(SurfaceType::DepthStencil); + InvalidateRegion(boost::icl::first(interval), boost::icl::length(interval), + render_targets.depth_surface); } - - RegisterSurface(new_surface); - return new_surface; } -SurfaceRect_Tuple RasterizerCacheOpenGL::GetTexCopySurface(const SurfaceParams& params) { +RasterizerCache::SurfaceRect_Tuple RasterizerCache::GetTexCopySurface(const SurfaceParams& params) { Common::Rectangle rect{}; - Surface match_surface = FindMatch( - surface_cache, params, ScaleMatch::Ignore); + SurfaceRef match_surface = + FindMatch(surface_cache, params, ScaleMatch::Ignore); - if (match_surface != nullptr) { + if (match_surface) { ValidateSurface(match_surface, params.addr, params.size); SurfaceParams match_subrect; @@ -747,12 +714,22 @@ SurfaceRect_Tuple RasterizerCacheOpenGL::GetTexCopySurface(const SurfaceParams& return std::make_tuple(match_surface, rect); } -void RasterizerCacheOpenGL::DuplicateSurface(const Surface& src_surface, - const Surface& dest_surface) { +void RasterizerCache::DuplicateSurface(const SurfaceRef& src_surface, + const SurfaceRef& dest_surface) { ASSERT(dest_surface->addr <= src_surface->addr && dest_surface->end >= src_surface->end); - BlitSurfaces(src_surface, src_surface->GetScaledRect(), dest_surface, - dest_surface->GetScaledSubRect(*src_surface)); + const auto src_rect = src_surface->GetScaledRect(); + const auto dst_rect = dest_surface->GetScaledSubRect(*src_surface); + ASSERT(src_rect.GetWidth() == dst_rect.GetWidth()); + + const TextureCopy copy = { + .src_level = 0, + .dst_level = 0, + .src_offset = {0, 0}, + .dst_offset = {0, 0}, + .extent = {src_rect.GetWidth(), src_rect.GetHeight()}, + }; + runtime.CopyTextures(*src_surface, *dest_surface, copy); dest_surface->invalid_regions -= src_surface->GetInterval(); dest_surface->invalid_regions += src_surface->invalid_regions; @@ -768,37 +745,44 @@ void RasterizerCacheOpenGL::DuplicateSurface(const Surface& src_surface, } } -void RasterizerCacheOpenGL::ValidateSurface(const Surface& surface, PAddr addr, u32 size) { - if (size == 0) +void RasterizerCache::ValidateSurface(const SurfaceRef& surface, PAddr addr, u32 size) { + if (size == 0) [[unlikely]] { return; + } const SurfaceInterval validate_interval(addr, addr + size); - if (surface->type == SurfaceType::Fill) { - // Sanity check, fill surfaces will always be valid when used ASSERT(surface->IsRegionValid(validate_interval)); return; } - auto validate_regions = surface->invalid_regions & validate_interval; + SurfaceRegions validate_regions = surface->invalid_regions & validate_interval; + auto notify_validated = [&](SurfaceInterval interval) { - surface->invalid_regions.erase(interval); + surface->MarkValid(interval); validate_regions.erase(interval); }; - while (true) { - const auto it = validate_regions.begin(); - if (it == validate_regions.end()) - break; + u32 level = surface->LevelOf(addr); + SurfaceInterval level_interval = surface->LevelInterval(level); + while (!validate_regions.empty()) { + // Take an invalid interval from the validation regions and clamp it + // to the current level interval since FromInterval cannot process + // intervals that span multiple levels. If the interval is empty + // then we have validated the entire level so move to the next. + const auto interval = *validate_regions.begin() & level_interval; + if (boost::icl::is_empty(interval)) { + level_interval = surface->LevelInterval(++level); + continue; + } - const auto interval = *it & validate_interval; - // Look for a valid surface to copy from - SurfaceParams params = surface->FromInterval(interval); - - Surface copy_surface = + // Look for a valid surface to copy from. + const SurfaceParams params = surface->FromInterval(interval); + const SurfaceRef copy_surface = FindMatch(surface_cache, params, ScaleMatch::Ignore, interval); - if (copy_surface != nullptr) { - SurfaceInterval copy_interval = params.GetCopyableInterval(copy_surface); + + if (copy_surface) { + const SurfaceInterval copy_interval = copy_surface->GetCopyableInterval(params); CopySurface(copy_surface, surface, copy_interval); notify_validated(copy_interval); continue; @@ -818,8 +802,8 @@ void RasterizerCacheOpenGL::ValidateSurface(const Surface& surface, PAddr addr, // If the region was created entirely on the GPU, // assume it was a developer mistake and skip flushing. if (boost::icl::contains(dirty_regions, interval)) { - LOG_DEBUG(Render_OpenGL, "Region created fully on GPU and reinterpretation is " - "invalid. Skipping validation"); + LOG_DEBUG(HW_GPU, "Region created fully on GPU and reinterpretation is " + "invalid. Skipping validation"); validate_regions.erase(interval); continue; } @@ -827,15 +811,103 @@ void RasterizerCacheOpenGL::ValidateSurface(const Surface& surface, PAddr addr, // Load data from 3DS memory FlushRegion(params.addr, params.size); - surface->LoadGLBuffer(params.addr, params.end); - surface->UploadGLTexture(surface->GetSubRect(params)); + UploadSurface(surface, interval); notify_validated(params.GetInterval()); } + + // Filtered mipmaps often look really bad. We can achieve better quality by + // generating them from the base level. + if (surface->res_scale != 1 && level != 0) { + runtime.GenerateMipmaps(*surface, surface->levels - 1); + } } -bool RasterizerCacheOpenGL::NoUnimplementedReinterpretations(const Surface& surface, - SurfaceParams& params, - const SurfaceInterval& interval) { +void RasterizerCache::UploadSurface(const SurfaceRef& surface, SurfaceInterval interval) { + const SurfaceParams load_info = surface->FromInterval(interval); + ASSERT(load_info.addr >= surface->addr && load_info.end <= surface->end); + + const auto staging = runtime.FindStaging( + load_info.width * load_info.height * surface->GetInternalBytesPerPixel(), true); + + MemoryRef source_ptr = memory.GetPhysicalRef(load_info.addr); + if (!source_ptr) [[unlikely]] { + return; + } + + const auto upload_data = source_ptr.GetWriteBytes(load_info.end - load_info.addr); + DecodeTexture(load_info, load_info.addr, load_info.end, upload_data, staging.mapped, + runtime.NeedsConversion(surface->pixel_format)); + + const BufferTextureCopy upload = { + .buffer_offset = 0, + .buffer_size = staging.size, + .texture_rect = surface->GetSubRect(load_info), + .texture_level = surface->LevelOf(load_info.addr), + }; + surface->Upload(upload, staging); +} + +void RasterizerCache::DownloadSurface(const SurfaceRef& surface, SurfaceInterval interval) { + const SurfaceParams flush_info = surface->FromInterval(interval); + const u32 flush_start = boost::icl::first(interval); + const u32 flush_end = boost::icl::last_next(interval); + ASSERT(flush_start >= surface->addr && flush_end <= surface->end); + + const auto staging = runtime.FindStaging( + flush_info.width * flush_info.height * surface->GetInternalBytesPerPixel(), false); + + const BufferTextureCopy download = { + .buffer_offset = 0, + .buffer_size = staging.size, + .texture_rect = surface->GetSubRect(flush_info), + .texture_level = surface->LevelOf(flush_start), + }; + surface->Download(download, staging); + + MemoryRef dest_ptr = memory.GetPhysicalRef(flush_start); + if (!dest_ptr) [[unlikely]] { + return; + } + + const auto download_dest = dest_ptr.GetWriteBytes(flush_end - flush_start); + EncodeTexture(flush_info, flush_start, flush_end, staging.mapped, download_dest, + runtime.NeedsConversion(surface->pixel_format)); +} + +void RasterizerCache::DownloadFillSurface(const SurfaceRef& surface, SurfaceInterval interval) { + const u32 flush_start = boost::icl::first(interval); + const u32 flush_end = boost::icl::last_next(interval); + ASSERT(flush_start >= surface->addr && flush_end <= surface->end); + + MemoryRef dest_ptr = memory.GetPhysicalRef(flush_start); + if (!dest_ptr) [[unlikely]] { + return; + } + + const u32 start_offset = flush_start - surface->addr; + const u32 download_size = + std::clamp(flush_end - flush_start, 0u, static_cast(dest_ptr.GetSize())); + const u32 coarse_start_offset = start_offset - (start_offset % surface->fill_size); + const u32 backup_bytes = start_offset % surface->fill_size; + + std::array backup_data; + if (backup_bytes) { + std::memcpy(backup_data.data(), &dest_ptr[coarse_start_offset], backup_bytes); + } + + for (u32 offset = coarse_start_offset; offset < download_size; offset += surface->fill_size) { + std::memcpy(&dest_ptr[offset], &surface->fill_data[0], + std::min(surface->fill_size, download_size - offset)); + } + + if (backup_bytes) { + std::memcpy(&dest_ptr[coarse_start_offset], &backup_data[0], backup_bytes); + } +} + +bool RasterizerCache::NoUnimplementedReinterpretations(const SurfaceRef& surface, + SurfaceParams params, + const SurfaceInterval& interval) { static constexpr std::array all_formats{ PixelFormat::RGBA8, PixelFormat::RGB8, PixelFormat::RGB5A1, PixelFormat::RGB565, PixelFormat::RGBA4, PixelFormat::IA8, PixelFormat::RG8, PixelFormat::I8, @@ -849,10 +921,10 @@ bool RasterizerCacheOpenGL::NoUnimplementedReinterpretations(const Surface& surf params.pixel_format = format; // This could potentially be expensive, // although experimentally it hasn't been too bad - Surface test_surface = + SurfaceRef test_surface = FindMatch(surface_cache, params, ScaleMatch::Ignore, interval); - if (test_surface != nullptr) { - LOG_WARNING(Render_OpenGL, "Missing pixel_format reinterpreter: {} -> {}", + if (test_surface) { + LOG_WARNING(HW_GPU, "Missing pixel_format reinterpreter: {} -> {}", PixelFormatAsString(format), PixelFormatAsString(surface->pixel_format)); implemented = false; @@ -862,63 +934,33 @@ bool RasterizerCacheOpenGL::NoUnimplementedReinterpretations(const Surface& surf return implemented; } -bool RasterizerCacheOpenGL::IntervalHasInvalidPixelFormat(SurfaceParams& params, - const SurfaceInterval& interval) { - params.pixel_format = PixelFormat::Invalid; - for (const auto& set : RangeFromInterval(surface_cache, interval)) - for (const auto& surface : set.second) +bool RasterizerCache::IntervalHasInvalidPixelFormat(const SurfaceParams& params, + const SurfaceInterval& interval) { + for (const auto& set : RangeFromInterval(surface_cache, interval)) { + for (const auto& surface : set.second) { if (surface->pixel_format == PixelFormat::Invalid) { - LOG_DEBUG(Render_OpenGL, "Surface {:#x} found with invalid pixel format", - surface->addr); + LOG_DEBUG(HW_GPU, "Surface {:#x} found with invalid pixel format", surface->addr); return true; } + } + } return false; } -bool RasterizerCacheOpenGL::ValidateByReinterpretation(const Surface& surface, - SurfaceParams& params, - const SurfaceInterval& interval) { - const PixelFormat dst_format = surface->pixel_format; - const SurfaceType type = GetFormatType(dst_format); - const FormatTuple& tuple = GetFormatTuple(dst_format); - - for (auto& reinterpreter : - format_reinterpreter->GetPossibleReinterpretations(surface->pixel_format)) { - +bool RasterizerCache::ValidateByReinterpretation(const SurfaceRef& surface, SurfaceParams params, + const SurfaceInterval& interval) { + const PixelFormat dest_format = surface->pixel_format; + for (const auto& reinterpreter : runtime.GetPossibleReinterpretations(dest_format)) { params.pixel_format = reinterpreter->GetSourceFormat(); - Surface reinterpret_surface = + SurfaceRef reinterpret_surface = FindMatch(surface_cache, params, ScaleMatch::Ignore, interval); - if (reinterpret_surface != nullptr) { - auto reinterpret_interval = params.GetCopyableInterval(reinterpret_surface); + if (reinterpret_surface) { + auto reinterpret_interval = reinterpret_surface->GetCopyableInterval(params); auto reinterpret_params = surface->FromInterval(reinterpret_interval); auto src_rect = reinterpret_surface->GetScaledSubRect(reinterpret_params); auto dest_rect = surface->GetScaledSubRect(reinterpret_params); - - if (!texture_filterer->IsNull() && reinterpret_surface->res_scale == 1 && - surface->res_scale == resolution_scale_factor) { - // The destination surface is either a framebuffer, or a filtered texture. - // Create an intermediate surface to convert to before blitting to the - // destination. - const u32 width = dest_rect.GetHeight() / resolution_scale_factor; - const u32 height = dest_rect.GetWidth() / resolution_scale_factor; - const Common::Rectangle tmp_rect{0, width, height, 0}; - - OGLTexture tmp_tex = AllocateSurfaceTexture(tuple, height, width); - reinterpreter->Reinterpret(reinterpret_surface->texture, src_rect, tmp_tex, - tmp_rect); - - if (!texture_filterer->Filter(tmp_tex, tmp_rect, surface->texture, dest_rect, - type)) { - - const Aspect aspect = ToAspect(type); - runtime.BlitTextures(tmp_tex, {aspect, tmp_rect}, surface->texture, - {aspect, dest_rect}); - } - } else { - reinterpreter->Reinterpret(reinterpret_surface->texture, src_rect, surface->texture, - dest_rect); - } + reinterpreter->Reinterpret(*reinterpret_surface, src_rect, *surface, dest_rect); return true; } @@ -927,7 +969,7 @@ bool RasterizerCacheOpenGL::ValidateByReinterpretation(const Surface& surface, return false; } -void RasterizerCacheOpenGL::ClearAll(bool flush) { +void RasterizerCache::ClearAll(bool flush) { const auto flush_interval = PageMap::interval_type::right_open(0x0, 0xFFFFFFFF); // Force flush all surfaces from the cache if (flush) { @@ -941,7 +983,7 @@ void RasterizerCacheOpenGL::ClearAll(bool flush) { const PAddr interval_end_addr = boost::icl::last_next(interval) << Memory::CITRA_PAGE_BITS; const u32 interval_size = interval_end_addr - interval_start_addr; - VideoCore::g_memory->RasterizerMarkRegionCached(interval_start_addr, interval_size, false); + memory.RasterizerMarkRegionCached(interval_start_addr, interval_size, false); } // Remove the whole cache without really looking at it. @@ -951,9 +993,7 @@ void RasterizerCacheOpenGL::ClearAll(bool flush) { remove_surfaces.clear(); } -void RasterizerCacheOpenGL::FlushRegion(PAddr addr, u32 size, Surface flush_surface) { - std::lock_guard lock{mutex}; - +void RasterizerCache::FlushRegion(PAddr addr, u32 size, SurfaceRef flush_surface) { if (size == 0) return; @@ -967,42 +1007,41 @@ void RasterizerCacheOpenGL::FlushRegion(PAddr addr, u32 size, Surface flush_surf const auto interval = size <= 8 ? pair.first : pair.first & flush_interval; auto& surface = pair.second; - if (flush_surface != nullptr && surface != flush_surface) + if (flush_surface && surface != flush_surface) continue; // Sanity check, this surface is the last one that marked this region dirty ASSERT(surface->IsRegionValid(interval)); - if (surface->type != SurfaceType::Fill) { - SurfaceParams params = surface->FromInterval(interval); - surface->DownloadGLTexture(surface->GetSubRect(params)); + if (surface->type == SurfaceType::Fill) { + DownloadFillSurface(surface, interval); + } else { + DownloadSurface(surface, interval); } - surface->FlushGLBuffer(boost::icl::first(interval), boost::icl::last_next(interval)); flushed_intervals += interval; } + // Reset dirty regions dirty_regions -= flushed_intervals; } -void RasterizerCacheOpenGL::FlushAll() { +void RasterizerCache::FlushAll() { FlushRegion(0, 0xFFFFFFFF); } -void RasterizerCacheOpenGL::InvalidateRegion(PAddr addr, u32 size, const Surface& region_owner) { - std::lock_guard lock{mutex}; - +void RasterizerCache::InvalidateRegion(PAddr addr, u32 size, const SurfaceRef& region_owner) { if (size == 0) return; const SurfaceInterval invalid_interval(addr, addr + size); - if (region_owner != nullptr) { + if (region_owner) { ASSERT(region_owner->type != SurfaceType::Texture); ASSERT(addr >= region_owner->addr && addr + size <= region_owner->end); // Surfaces can't have a gap ASSERT(region_owner->width == region_owner->stride); - region_owner->invalid_regions.erase(invalid_interval); + region_owner->MarkValid(invalid_interval); } for (const auto& pair : RangeFromInterval(surface_cache, invalid_interval)) { @@ -1012,62 +1051,42 @@ void RasterizerCacheOpenGL::InvalidateRegion(PAddr addr, u32 size, const Surface // If cpu is invalidating this region we want to remove it // to (likely) mark the memory pages as uncached - if (region_owner == nullptr && size <= 8) { + if (!region_owner && size <= 8) { FlushRegion(cached_surface->addr, cached_surface->size, cached_surface); - remove_surfaces.emplace(cached_surface); + remove_surfaces.push_back(cached_surface); continue; } const auto interval = cached_surface->GetInterval() & invalid_interval; - cached_surface->invalid_regions.insert(interval); - cached_surface->InvalidateAllWatcher(); + cached_surface->MarkInvalid(interval); // If the surface has no salvageable data it should be removed from the cache to avoid // clogging the data structure - if (cached_surface->IsSurfaceFullyInvalid()) { - remove_surfaces.emplace(cached_surface); + if (cached_surface->IsFullyInvalid()) { + remove_surfaces.push_back(cached_surface); } } } - if (region_owner != nullptr) + if (region_owner) { dirty_regions.set({invalid_interval, region_owner}); - else + } else { dirty_regions.erase(invalid_interval); + } - for (const auto& remove_surface : remove_surfaces) { - if (remove_surface == region_owner) { - Surface expanded_surface = FindMatch( - surface_cache, *region_owner, ScaleMatch::Ignore); - ASSERT(expanded_surface); - - if ((region_owner->invalid_regions - expanded_surface->invalid_regions).empty()) { - DuplicateSurface(region_owner, expanded_surface); - } else { - continue; - } - } + for (const SurfaceRef& remove_surface : remove_surfaces) { UnregisterSurface(remove_surface); } - remove_surfaces.clear(); } -Surface RasterizerCacheOpenGL::CreateSurface(const SurfaceParams& params) { - Surface surface = std::make_shared(params, *this, runtime); - surface->invalid_regions.insert(surface->GetInterval()); - - // Allocate surface texture - const FormatTuple& tuple = GetFormatTuple(surface->pixel_format); - surface->texture = - AllocateSurfaceTexture(tuple, surface->GetScaledWidth(), surface->GetScaledHeight()); - +RasterizerCache::SurfaceRef RasterizerCache::CreateSurface(const SurfaceParams& params) { + SurfaceRef surface = std::make_shared(runtime, params); + surface->MarkInvalid(surface->GetInterval()); return surface; } -void RasterizerCacheOpenGL::RegisterSurface(const Surface& surface) { - std::lock_guard lock{mutex}; - +void RasterizerCache::RegisterSurface(const SurfaceRef& surface) { if (surface->registered) { return; } @@ -1076,9 +1095,7 @@ void RasterizerCacheOpenGL::RegisterSurface(const Surface& surface) { UpdatePagesCachedCount(surface->addr, surface->size, 1); } -void RasterizerCacheOpenGL::UnregisterSurface(const Surface& surface) { - std::lock_guard lock{mutex}; - +void RasterizerCache::UnregisterSurface(const SurfaceRef& surface) { if (!surface->registered) { return; } @@ -1087,7 +1104,7 @@ void RasterizerCacheOpenGL::UnregisterSurface(const Surface& surface) { surface_cache.subtract({surface->GetInterval(), SurfaceSet{surface}}); } -void RasterizerCacheOpenGL::UpdatePagesCachedCount(PAddr addr, u32 size, int delta) { +void RasterizerCache::UpdatePagesCachedCount(PAddr addr, u32 size, int delta) { const u32 num_pages = ((addr + size - 1) >> Memory::CITRA_PAGE_BITS) - (addr >> Memory::CITRA_PAGE_BITS) + 1; const u32 page_start = addr >> Memory::CITRA_PAGE_BITS; @@ -1096,8 +1113,9 @@ void RasterizerCacheOpenGL::UpdatePagesCachedCount(PAddr addr, u32 size, int del // Interval maps will erase segments if count reaches 0, so if delta is negative we have to // subtract after iterating const auto pages_interval = PageMap::interval_type::right_open(page_start, page_end); - if (delta > 0) + if (delta > 0) { cached_pages.add({pages_interval, delta}); + } for (const auto& pair : RangeFromInterval(cached_pages, pages_interval)) { const auto interval = pair.first & pages_interval; @@ -1107,18 +1125,18 @@ void RasterizerCacheOpenGL::UpdatePagesCachedCount(PAddr addr, u32 size, int del const PAddr interval_end_addr = boost::icl::last_next(interval) << Memory::CITRA_PAGE_BITS; const u32 interval_size = interval_end_addr - interval_start_addr; - if (delta > 0 && count == delta) - VideoCore::g_memory->RasterizerMarkRegionCached(interval_start_addr, interval_size, - true); - else if (delta < 0 && count == -delta) - VideoCore::g_memory->RasterizerMarkRegionCached(interval_start_addr, interval_size, - false); - else + if (delta > 0 && count == delta) { + memory.RasterizerMarkRegionCached(interval_start_addr, interval_size, true); + } else if (delta < 0 && count == -delta) { + memory.RasterizerMarkRegionCached(interval_start_addr, interval_size, false); + } else { ASSERT(count >= 0); + } } - if (delta < 0) + if (delta < 0) { cached_pages.add({pages_interval, delta}); + } } -} // namespace OpenGL +} // namespace VideoCore diff --git a/src/video_core/rasterizer_cache/rasterizer_cache.h b/src/video_core/rasterizer_cache/rasterizer_cache.h index a3b974e7f..9607a9657 100644 --- a/src/video_core/rasterizer_cache/rasterizer_cache.h +++ b/src/video_core/rasterizer_cache/rasterizer_cache.h @@ -3,17 +3,24 @@ // Refer to the license.txt file included. #pragma once + #include -#include "video_core/rasterizer_cache/cached_surface.h" -#include "video_core/rasterizer_cache/rasterizer_cache_utils.h" +#include +#include +#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; - /// Blit one surface's texture to another - bool BlitSurfaces(const Surface& src_surface, const Common::Rectangle& src_rect, - const Surface& dst_surface, const Common::Rectangle& dst_rect); + // Declare rasterizer interval types + using SurfaceSet = std::set; + using SurfaceMap = boost::icl::interval_map; + using SurfaceCache = boost::icl::interval_map; + + static_assert(std::is_same() && + std::is_same(), + "Incorrect interval types"); + + using SurfaceRect_Tuple = std::tuple>; + using PageMap = boost::icl::interval_map; + + struct RenderTargets { + SurfaceRef color_surface; + SurfaceRef depth_surface; + }; + + struct TextureCube { + SurfaceRef surface{}; + std::array faces{}; + std::array 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& 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 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 texture_cube_cache; - - std::recursive_mutex mutex; - -public: - OGLTexture AllocateSurfaceTexture(const FormatTuple& format_tuple, u32 width, u32 height); - - std::unique_ptr texture_filterer; - std::unique_ptr format_reinterpreter; - std::unique_ptr texture_downloader_es; + std::vector remove_surfaces; + u32 resolution_scale_factor; + RenderTargets render_targets; + std::unordered_map texture_cube_cache; + bool use_filter{}; }; -} // namespace OpenGL +} // namespace VideoCore diff --git a/src/video_core/rasterizer_cache/rasterizer_cache_types.h b/src/video_core/rasterizer_cache/rasterizer_cache_types.h deleted file mode 100644 index 5bfefc983..000000000 --- a/src/video_core/rasterizer_cache/rasterizer_cache_types.h +++ /dev/null @@ -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 -#include -#include -#include -#include -#include "common/common_types.h" -#include "common/math_util.h" - -namespace OpenGL { - -class CachedSurface; -using Surface = std::shared_ptr; - -// Declare rasterizer interval types -using SurfaceInterval = boost::icl::right_open_interval; -using SurfaceSet = std::set; -using SurfaceRegions = boost::icl::interval_set; -using SurfaceMap = - boost::icl::interval_map; -using SurfaceCache = - boost::icl::interval_map; - -static_assert(std::is_same() && - std::is_same(), - "Incorrect interval types"); - -using SurfaceRect_Tuple = std::tuple>; -using SurfaceSurfaceRect_Tuple = std::tuple>; -using PageMap = boost::icl::interval_map; - -} // namespace OpenGL diff --git a/src/video_core/rasterizer_cache/rasterizer_cache_utils.cpp b/src/video_core/rasterizer_cache/rasterizer_cache_utils.cpp deleted file mode 100644 index 051aaa2d4..000000000 --- a/src/video_core/rasterizer_cache/rasterizer_cache_utils.cpp +++ /dev/null @@ -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 -#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 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 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 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(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 diff --git a/src/video_core/rasterizer_cache/rasterizer_cache_utils.h b/src/video_core/rasterizer_cache/rasterizer_cache_utils.h deleted file mode 100644 index ee4a056e4..000000000 --- a/src/video_core/rasterizer_cache/rasterizer_cache_utils.h +++ /dev/null @@ -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 -#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 { - std::size_t operator()(const OpenGL::HostTextureTag& tag) const noexcept { - return tag.Hash(); - } -}; - -template <> -struct hash { - std::size_t operator()(const OpenGL::TextureCubeConfig& config) const noexcept { - return config.Hash(); - } -}; -} // namespace std diff --git a/src/video_core/rasterizer_cache/surface_base.cpp b/src/video_core/rasterizer_cache/surface_base.cpp new file mode 100644 index 000000000..7d55ecec1 --- /dev/null +++ b/src/video_core/rasterizer_cache/surface_base.cpp @@ -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 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(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 SurfaceBase::MakeFillBuffer(PAddr copy_addr) { + const PAddr fill_offset = (copy_addr - addr) % fill_size; + std::array 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 diff --git a/src/video_core/rasterizer_cache/surface_base.h b/src/video_core/rasterizer_cache/surface_base.h new file mode 100644 index 000000000..05acff6c5 --- /dev/null +++ b/src/video_core/rasterizer_cache/surface_base.h @@ -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 +#include "video_core/rasterizer_cache/surface_params.h" + +namespace VideoCore { + +using SurfaceRegions = boost::icl::interval_set; + +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 MakeFillBuffer(PAddr copy_addr); + +public: + bool registered = false; + SurfaceRegions invalid_regions; + u32 fill_size = 0; + std::array fill_data; + u64 modification_tick = 1; +}; + +} // namespace VideoCore diff --git a/src/video_core/rasterizer_cache/surface_params.cpp b/src/video_core/rasterizer_cache/surface_params.cpp index 90f2776ea..cdd5f3a55 100644 --- a/src/video_core/rasterizer_cache/surface_params.cpp +++ b/src/video_core/rasterizer_cache/surface_params.cpp @@ -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 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 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(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(x0, y0 + sub_surface.height, x0 + sub_surface.width, y0); -} - -Common::Rectangle 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 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 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 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 diff --git a/src/video_core/rasterizer_cache/surface_params.h b/src/video_core/rasterizer_cache/surface_params.h index 23fbad4d2..2787e440a 100644 --- a/src/video_core/rasterizer_cache/surface_params.h +++ b/src/video_core/rasterizer_cache/surface_params.h @@ -4,75 +4,89 @@ #pragma once -#include -#include -#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 GetSubRect(const SurfaceParams& sub_surface) const; + + /// Returns the scaled rectangle referenced by sub_surface Common::Rectangle 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 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 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 GetRect() const { + [[nodiscard]] Common::Rectangle GetRect() const noexcept { return {0, height, width, 0}; } - Common::Rectangle GetScaledRect() const { + [[nodiscard]] Common::Rectangle 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 mipmap_offsets{}; }; -} // namespace OpenGL +} // namespace VideoCore diff --git a/src/video_core/rasterizer_cache/texture_codec.h b/src/video_core/rasterizer_cache/texture_codec.h new file mode 100644 index 000000000..3c136724d --- /dev/null +++ b/src/video_core/rasterizer_cache/texture_codec.h @@ -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 +#include +#include +#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 +inline T MakeInt(const u8* bytes) { + T integer{}; + std::memcpy(&integer, bytes, sizeof(T)); + + return integer; +} + +template +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(source), 8); + std::memcpy(dest, &d24s8, sizeof(u32)); + } else { + std::memcpy(dest, source, bytes_per_pixel); + } +} + +template +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 +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(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 +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 rgba; + std::memcpy(rgba.AsArray(), source, 4); + EncodeRGBA8(rgba, dest); + } else if constexpr (format == PixelFormat::RGB8 && converted) { + Common::Vec4 rgba; + std::memcpy(rgba.AsArray(), source, 4); + EncodeRGB8(rgba, dest); + } else if constexpr (format == PixelFormat::RGB565 && converted) { + Common::Vec4 rgba; + std::memcpy(rgba.AsArray(), source, 4); + EncodeRGB565(rgba, dest); + } else if constexpr (format == PixelFormat::RGB5A1 && converted) { + Common::Vec4 rgba; + std::memcpy(rgba.AsArray(), source, 4); + EncodeRGB5A1(rgba, dest); + } else if constexpr (format == PixelFormat::RGBA4 && converted) { + Common::Vec4 rgba; + std::memcpy(rgba.AsArray(), source, 4); + EncodeRGBA4(rgba, dest); + } else if constexpr (format == PixelFormat::IA8) { + Common::Vec4 rgba; + std::memcpy(rgba.AsArray(), source, 4); + EncodeIA8(rgba, dest); + } else if constexpr (format == PixelFormat::RG8) { + Common::Vec4 rgba; + std::memcpy(rgba.AsArray(), source, 4); + EncodeRG8(rgba, dest); + } else if constexpr (format == PixelFormat::I8) { + Common::Vec4 rgba; + std::memcpy(rgba.AsArray(), source, 4); + EncodeI8(rgba, dest); + } else if constexpr (format == PixelFormat::A8) { + Common::Vec4 rgba; + std::memcpy(rgba.AsArray(), source, 4); + EncodeA8(rgba, dest); + } else if constexpr (format == PixelFormat::IA4) { + Common::Vec4 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(source), 8); + std::memcpy(dest, &s8d24, sizeof(u32)); + } else { + std::memcpy(dest, source, bytes_per_pixel); + } +} + +template +constexpr void EncodePixel4(u32 x, u32 y, const u8* source_pixel, u8* dest_tile_buffer) { + Common::Vec4 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 +constexpr void MortonCopyTile(u32 stride, std::span tile_buffer, std::span 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(x, y, tile_buffer.data(), linear_pixel.data()); + } else if constexpr (is_4bit) { + DecodePixel4(x, y, tile_buffer.data(), linear_pixel.data()); + } else { + DecodePixel(tiled_pixel.data(), linear_pixel.data()); + } + } else { + if constexpr (is_4bit) { + EncodePixel4(x, y, linear_pixel.data(), tile_buffer.data()); + } else { + EncodePixel(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 +static constexpr void MortonCopy(u32 width, u32 height, u32 start_offset, u32 end_offset, + std::span linear_buffer, std::span 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 tmp_buf; + auto linear_data = linear_buffer.subspan(linear_offset, linear_tile_stride); + MortonCopyTile(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(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 tmp_buf; + auto linear_data = linear_buffer.subspan(linear_offset, linear_tile_stride); + MortonCopyTile(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 +static constexpr void LinearCopy(std::span src_buffer, std::span 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(src_pixel.data(), dst_pixel.data()); + } else { + EncodePixel(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, std::span); + +static constexpr std::array UNSWIZZLE_TABLE = { + MortonCopy, // 0 + MortonCopy, // 1 + MortonCopy, // 2 + MortonCopy, // 3 + MortonCopy, // 4 + MortonCopy, // 5 + MortonCopy, // 6 + MortonCopy, // 7 + MortonCopy, // 8 + MortonCopy, // 9 + MortonCopy, // 10 + MortonCopy, // 11 + MortonCopy, // 12 + MortonCopy, // 13 + MortonCopy, // 14 + nullptr, // 15 + MortonCopy, // 16 + MortonCopy, // 17 +}; + +static constexpr std::array UNSWIZZLE_TABLE_CONVERTED = { + MortonCopy, // 0 + MortonCopy, // 1 + MortonCopy, // 2 + MortonCopy, // 3 + MortonCopy, // 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, // 14 + nullptr, // 15 + MortonCopy, // 16 + // No conversion here as we need to do a special deinterleaving conversion elsewhere. + nullptr, // 17 +}; + +static constexpr std::array SWIZZLE_TABLE = { + MortonCopy, // 0 + MortonCopy, // 1 + MortonCopy, // 2 + MortonCopy, // 3 + MortonCopy, // 4 + MortonCopy, // 5 + MortonCopy, // 6 + MortonCopy, // 7 + MortonCopy, // 8 + MortonCopy, // 9 + MortonCopy, // 10 + MortonCopy, // 11 + nullptr, // 12 + nullptr, // 13 + MortonCopy, // 14 + nullptr, // 15 + MortonCopy, // 16 + MortonCopy, // 17 +}; + +static constexpr std::array SWIZZLE_TABLE_CONVERTED = { + MortonCopy, // 0 + MortonCopy, // 1 + MortonCopy, // 2 + MortonCopy, // 3 + MortonCopy, // 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, // 14 + nullptr, // 15 + MortonCopy, // 16 + // No conversion here as we need to do a special interleaving conversion elsewhere. + nullptr, // 17 +}; + +using LinearFunc = void (*)(std::span, std::span); + +static constexpr std::array LINEAR_DECODE_TABLE = { + LinearCopy, // 0 + LinearCopy, // 1 + LinearCopy, // 2 + LinearCopy, // 3 + LinearCopy, // 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, // 14 + nullptr, // 15 + LinearCopy, // 16 + LinearCopy, // 17 +}; + +static constexpr std::array LINEAR_DECODE_TABLE_CONVERTED = { + LinearCopy, // 0 + LinearCopy, // 1 + LinearCopy, // 2 + LinearCopy, // 3 + LinearCopy, // 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, // 14 + nullptr, // 15 + LinearCopy, // 16 + // No conversion here as we need to do a special deinterleaving conversion elsewhere. + nullptr, // 17 +}; + +static constexpr std::array LINEAR_ENCODE_TABLE = { + LinearCopy, // 0 + LinearCopy, // 1 + LinearCopy, // 2 + LinearCopy, // 3 + LinearCopy, // 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, // 14 + nullptr, // 15 + LinearCopy, // 16 + LinearCopy, // 17 +}; + +static constexpr std::array LINEAR_ENCODE_TABLE_CONVERTED = { + LinearCopy, // 0 + LinearCopy, // 1 + LinearCopy, // 2 + LinearCopy, // 3 + LinearCopy, // 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, // 14 + nullptr, // 15 + LinearCopy, // 16 + // No conversion here as we need to do a special interleaving conversion elsewhere. + nullptr, // 17 +}; + +} // namespace VideoCore diff --git a/src/video_core/rasterizer_cache/texture_runtime.cpp b/src/video_core/rasterizer_cache/texture_runtime.cpp deleted file mode 100644 index f7955bb25..000000000 --- a/src/video_core/rasterizer_cache/texture_runtime.cpp +++ /dev/null @@ -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 diff --git a/src/video_core/rasterizer_cache/texture_runtime.h b/src/video_core/rasterizer_cache/texture_runtime.h deleted file mode 100644 index c97076b12..000000000 --- a/src/video_core/rasterizer_cache/texture_runtime.h +++ /dev/null @@ -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 region, u32 level = 0, u32 layer = 0) - : aspect(aspect), region(region), level(level), layer(layer) {} - - Aspect aspect; - Common::Rectangle 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 diff --git a/src/video_core/rasterizer_cache/utils.cpp b/src/video_core/rasterizer_cache/utils.cpp new file mode 100644 index 000000000..33fdbb930 --- /dev/null +++ b/src/video_core/rasterizer_cache/utils.cpp @@ -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 source, std::span dest, bool convert) { + const PixelFormat format = surface_info.pixel_format; + const u32 func_index = static_cast(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 source, std::span dest, bool convert) { + const PixelFormat format = surface_info.pixel_format; + const u32 func_index = static_cast(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 diff --git a/src/video_core/rasterizer_cache/utils.h b/src/video_core/rasterizer_cache/utils.h new file mode 100644 index 000000000..c56376694 --- /dev/null +++ b/src/video_core/rasterizer_cache/utils.h @@ -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 +#include +#include +#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; + +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 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 src_rect; + Common::Rectangle dst_rect; +}; + +struct BufferTextureCopy { + u32 buffer_offset; + u32 buffer_size; + Common::Rectangle texture_rect; + u32 texture_level; +}; + +struct StagingData { + u32 size = 0; + std::span 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 source, std::span 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 source, std::span dest, bool convert = false); + +} // namespace VideoCore + +namespace std { +template <> +struct hash { + std::size_t operator()(const VideoCore::TextureCubeConfig& config) const noexcept { + return config.Hash(); + } +}; +} // namespace std diff --git a/src/video_core/regs_framebuffer.h b/src/video_core/regs_framebuffer.h index 3ee18294a..a96c363bb 100644 --- a/src/video_core/regs_framebuffer.h +++ b/src/video_core/regs_framebuffer.h @@ -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 { diff --git a/src/video_core/regs_rasterizer.h b/src/video_core/regs_rasterizer.h index 94b9f7502..c88f9d8fe 100644 --- a/src/video_core/regs_rasterizer.h +++ b/src/video_core/regs_rasterizer.h @@ -6,8 +6,8 @@ #include #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 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(float24::FromRaw(viewport_size_y).ToFloat32() * 2), + viewport_corner.x + // right + static_cast(float24::FromRaw(viewport_size_x).ToFloat32() * 2), + viewport_corner.y // bottom + }; + } + INSERT_PADDING_WORDS(0x1); BitField<0, 24, u32> viewport_depth_range; // float24 diff --git a/src/video_core/regs_texturing.h b/src/video_core/regs_texturing.h index 3954e13b4..a92d118a5 100644 --- a/src/video_core/regs_texturing.h +++ b/src/video_core/regs_texturing.h @@ -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; diff --git a/src/video_core/renderer_opengl/depth_to_color.frag b/src/video_core/renderer_opengl/depth_to_color.frag deleted file mode 100644 index e69bed890..000000000 --- a/src/video_core/renderer_opengl/depth_to_color.frag +++ /dev/null @@ -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)); -} diff --git a/src/video_core/renderer_opengl/depth_to_color.vert b/src/video_core/renderer_opengl/depth_to_color.vert deleted file mode 100644 index 866d43b46..000000000 --- a/src/video_core/renderer_opengl/depth_to_color.vert +++ /dev/null @@ -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); -} diff --git a/src/video_core/renderer_opengl/ds_to_color.frag b/src/video_core/renderer_opengl/ds_to_color.frag deleted file mode 100644 index 954217064..000000000 --- a/src/video_core/renderer_opengl/ds_to_color.frag +++ /dev/null @@ -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); -} diff --git a/src/video_core/renderer_opengl/gl_blit_helper.cpp b/src/video_core/renderer_opengl/gl_blit_helper.cpp new file mode 100644 index 000000000..2adc92c32 --- /dev/null +++ b/src/video_core/renderer_opengl/gl_blit_helper.cpp @@ -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(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 src_rect) { + glProgramUniform2f( + program.handle, 0, + static_cast(src_rect.right - src_rect.left) / static_cast(src_width), + static_cast(src_rect.top - src_rect.bottom) / static_cast(src_height)); + glProgramUniform2f(program.handle, 1, + static_cast(src_rect.left) / static_cast(src_width), + static_cast(src_rect.bottom) / static_cast(src_height)); +} + +void BlitHelper::Draw(OGLProgram& program, GLuint dst_tex, GLuint dst_fbo, u32 dst_level, + Common::Rectangle 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 diff --git a/src/video_core/renderer_opengl/gl_blit_helper.h b/src/video_core/renderer_opengl/gl_blit_helper.h new file mode 100644 index 000000000..842c5ff9b --- /dev/null +++ b/src/video_core/renderer_opengl/gl_blit_helper.h @@ -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 src_rect); + + void Draw(OGLProgram& program, GLuint dst_tex, GLuint dst_fbo, u32 dst_level, + Common::Rectangle 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 diff --git a/src/video_core/renderer_opengl/gl_driver.cpp b/src/video_core/renderer_opengl/gl_driver.cpp index 13d585270..cb2f05732 100644 --- a/src/video_core/renderer_opengl/gl_driver.cpp +++ b/src/video_core/renderer_opengl/gl_driver.cpp @@ -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(num_extensions); ++index) { + const auto name = reinterpret_cast(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(glGetString(GL_VERSION))}; diff --git a/src/video_core/renderer_opengl/gl_driver.h b/src/video_core/renderer_opengl/gl_driver.h index a7a995305..b155a890a 100644 --- a/src/video_core/renderer_opengl/gl_driver.h +++ b/src/video_core/renderer_opengl/gl_driver.h @@ -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{}; diff --git a/src/video_core/renderer_opengl/gl_format_reinterpreter.cpp b/src/video_core/renderer_opengl/gl_format_reinterpreter.cpp index 437675c46..36a1e2451 100644 --- a/src/video_core/renderer_opengl/gl_format_reinterpreter.cpp +++ b/src/video_core/renderer_opengl/gl_format_reinterpreter.cpp @@ -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 src_rect, Surface& dest, + Common::Rectangle 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(dst_rect.left), static_cast(dst_rect.bottom), + static_cast(dst_rect.GetWidth()), + static_cast(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(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 src_rect, + Surface& dest, Common::Rectangle 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 src_rect, - const OGLTexture& dst_tex, Common::Rectangle 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(dst_rect.left), static_cast(dst_rect.bottom), - static_cast(dst_rect.GetWidth()), - static_cast(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(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 src_rect, - const OGLTexture& dst_tex, Common::Rectangle 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(dst_rect.left), static_cast(dst_rect.bottom), - static_cast(dst_rect.GetWidth()), - static_cast(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 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(dst_rect.left), static_cast(dst_rect.bottom), + static_cast(dst_rect.GetWidth()), + static_cast(dst_rect.GetHeight())}; + state.Apply(); -FormatReinterpreterOpenGL::FormatReinterpreterOpenGL() { - const std::string_view vendor{reinterpret_cast(glGetString(GL_VENDOR))}; - const std::string_view version{reinterpret_cast(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&& obj) { - const u32 dst_index = static_cast(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()); - 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()); -} - -auto FormatReinterpreterOpenGL::GetPossibleReinterpretations(PixelFormat dst_format) - -> const ReinterpreterList& { - return reinterpreters[static_cast(dst_format)]; + temp_tex.Release(); } } // namespace OpenGL diff --git a/src/video_core/renderer_opengl/gl_format_reinterpreter.h b/src/video_core/renderer_opengl/gl_format_reinterpreter.h index 9e1cd06db..b576f049f 100644 --- a/src/video_core/renderer_opengl/gl_format_reinterpreter.h +++ b/src/video_core/renderer_opengl/gl_format_reinterpreter.h @@ -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 src_rect, - const OGLTexture& dst_tex, Common::Rectangle dst_rect) = 0; + virtual VideoCore::PixelFormat GetSourceFormat() const = 0; + virtual void Reinterpret(Surface& source, Common::Rectangle src_rect, Surface& dest, + Common::Rectangle dst_rect) = 0; protected: OGLFramebuffer read_fbo; @@ -33,15 +33,45 @@ protected: using ReinterpreterList = std::vector>; -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 src_rect, Surface& dest, + Common::Rectangle dst_rect) override; private: - std::array 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 src_rect, Surface& dest, + Common::Rectangle 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 temp_rect{0, 0, 0, 0}; }; } // namespace OpenGL diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 19f076dc9..bc1edf46e 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -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(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 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(Pica::float24::FromRaw(regs.rasterizer.viewport_size_y).ToFloat32() * - 2), - regs.rasterizer.viewport_corner.x + // right - static_cast(Pica::float24::FromRaw(regs.rasterizer.viewport_size_x).ToFloat32() * - 2), - regs.rasterizer.viewport_corner.y // bottom - }; - - Surface color_surface; - Surface depth_surface; - Common::Rectangle 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 draw_rect{ - static_cast(std::clamp(static_cast(surfaces_rect.left) + - viewport_rect_unscaled.left * res_scale, - surfaces_rect.left, surfaces_rect.right)), // Left - static_cast(std::clamp(static_cast(surfaces_rect.bottom) + - viewport_rect_unscaled.top * res_scale, - surfaces_rect.bottom, surfaces_rect.top)), // Top - static_cast(std::clamp(static_cast(surfaces_rect.left) + - viewport_rect_unscaled.right * res_scale, - surfaces_rect.left, surfaces_rect.right)), // Right - static_cast(std::clamp(static_cast(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(surfaces_rect.left) + viewport_rect_unscaled.left * res_scale; - state.viewport.y = - static_cast(surfaces_rect.bottom) + viewport_rect_unscaled.bottom * res_scale; - state.viewport.width = static_cast(viewport_rect_unscaled.GetWidth() * res_scale); - state.viewport.height = static_cast(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(surfaces_rect.left + regs.rasterizer.scissor_test.x1 * res_scale); - GLint scissor_y1 = - static_cast(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(surfaces_rect.left + (regs.rasterizer.scissor_test.x2 + 1) * res_scale); - GLint scissor_y2 = static_cast(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(offset / sizeof(HardwareVertex)), static_cast(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(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 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 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 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 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 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 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)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(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() { diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 575420df0..f91c68f85 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -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 shader_program_manager; OGLVertexArray sw_vao; // VAO for software shader draw @@ -146,6 +158,7 @@ private: std::array hw_vao_enabled_attributes{}; std::array texture_samplers; + GLsizeiptr texture_buffer_size; OGLStreamBuffer vertex_buffer; OGLStreamBuffer uniform_buffer; OGLStreamBuffer index_buffer; diff --git a/src/video_core/renderer_opengl/gl_resource_manager.cpp b/src/video_core/renderer_opengl/gl_resource_manager.cpp index 9585f4786..9a1e4a6dd 100644 --- a/src/video_core/renderer_opengl/gl_resource_manager.cpp +++ b/src/video_core/renderer_opengl/gl_resource_manager.cpp @@ -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& 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); diff --git a/src/video_core/renderer_opengl/gl_resource_manager.h b/src/video_core/renderer_opengl/gl_resource_manager.h index b8c8f5bc9..aa859549a 100644 --- a/src/video_core/renderer_opengl/gl_resource_manager.h +++ b/src/video_core/renderer_opengl/gl_resource_manager.h @@ -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& 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(); diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index e7e0953e7..7686980de 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -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()"; diff --git a/src/video_core/renderer_opengl/gl_shader_util.cpp b/src/video_core/renderer_opengl/gl_shader_util.cpp index 32b40c712..2e488262c 100644 --- a/src/video_core/renderer_opengl/gl_shader_util.cpp +++ b/src/video_core/renderer_opengl/gl_shader_util.cpp @@ -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 src_arr{version.data(), source}; + std::array src_arr{version.data(), source.data()}; + std::array lengths{static_cast(version.size()), + static_cast(source.size())}; GLuint shader_id = glCreateShader(type); - glShaderSource(shader_id, static_cast(src_arr.size()), src_arr.data(), nullptr); + glShaderSource(shader_id, static_cast(src_arr.size()), src_arr.data(), lengths.data()); LOG_DEBUG(Render_OpenGL, "Compiling {} shader...", debug_type); glCompileShader(shader_id); diff --git a/src/video_core/renderer_opengl/gl_shader_util.h b/src/video_core/renderer_opengl/gl_shader_util.h index fef6139d3..f4e38faf3 100644 --- a/src/video_core/renderer_opengl/gl_shader_util.h +++ b/src/video_core/renderer_opengl/gl_shader_util.h @@ -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 diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h index e3b85a297..418959749 100644 --- a/src/video_core/renderer_opengl/gl_state.h +++ b/src/video_core/renderer_opengl/gl_state.h @@ -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 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 diff --git a/src/video_core/renderer_opengl/gl_stream_buffer.cpp b/src/video_core/renderer_opengl/gl_stream_buffer.cpp index 804b462e7..b2c781f24 100644 --- a/src/video_core/renderer_opengl/gl_stream_buffer.cpp +++ b/src/video_core/renderer_opengl/gl_stream_buffer.cpp @@ -54,7 +54,7 @@ GLsizeiptr OGLStreamBuffer::GetSize() const { } std::tuple 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; diff --git a/src/video_core/renderer_opengl/gl_texture_runtime.cpp b/src/video_core/renderer_opengl/gl_texture_runtime.cpp new file mode 100644 index 000000000..cf64780a0 --- /dev/null +++ b/src/video_core/renderer_opengl/gl_texture_runtime.cpp @@ -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 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 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 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&& obj) { + const u32 dst_index = static_cast(dest); + return reinterpreters[dst_index].push_back(std::move(obj)); + }; + + Register(PixelFormat::RGBA8, std::make_unique()); + Register(PixelFormat::RGB5A1, std::make_unique()); +} + +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(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 textures{}; + std::array 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(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(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(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 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 diff --git a/src/video_core/renderer_opengl/gl_texture_runtime.h b/src/video_core/renderer_opengl/gl_texture_runtime.h new file mode 100644 index 000000000..43f91af10 --- /dev/null +++ b/src/video_core/renderer_opengl/gl_texture_runtime.h @@ -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 textures; + std::array 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 staging_buffer; + std::array reinterpreters; + std::unordered_multimap recycler; + std::unordered_map> framebuffer_cache; + std::array draw_fbos; + std::array 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(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 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(attachments[Index(type)]); + } + +private: + std::array attachments{}; + GLuint handle{}; +}; + +} // namespace OpenGL diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index a5e06e3fc..2ff11dc73 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -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 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 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 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(); + const bool has_debug_tool = driver.HasDebugTool(); + window.mailbox = std::make_unique(has_debug_tool); if (secondary_window) { - secondary_window->mailbox = std::make_unique(); + secondary_window->mailbox = std::make_unique(has_debug_tool); } frame_dumper.mailbox = std::make_unique(); 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"); diff --git a/src/video_core/renderer_opengl/texture_downloader_es.cpp b/src/video_core/renderer_opengl/texture_downloader_es.cpp deleted file mode 100644 index b7214032e..000000000 --- a/src/video_core/renderer_opengl/texture_downloader_es.cpp +++ /dev/null @@ -1,253 +0,0 @@ -// Copyright 2020 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include -#include -#include -#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(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(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{}, 4096, - [](std::size_t idx) { return static_cast((idx << 8) | (idx & 0xFF)); }); - LOG_INFO(Render_OpenGL, "GL_DEPTH_COMPONENT24 download test starting"); - test(GetFormatTuple(PixelFormat::D24), std::vector{}, 4096, - [](std::size_t idx) { return static_cast(idx << 8); }); - LOG_INFO(Render_OpenGL, "GL_DEPTH_COMPONENT16 download test starting"); - test(GetFormatTuple(PixelFormat::D16), std::vector{}, 256, - [](std::size_t idx) { return static_cast(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 diff --git a/src/video_core/renderer_opengl/texture_downloader_es.h b/src/video_core/renderer_opengl/texture_downloader_es.h deleted file mode 100644 index 66c27dde1..000000000 --- a/src/video_core/renderer_opengl/texture_downloader_es.h +++ /dev/null @@ -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 diff --git a/src/video_core/renderer_opengl/texture_filters/anime4k/anime4k_ultrafast.cpp b/src/video_core/renderer_opengl/texture_filters/anime4k/anime4k_ultrafast.cpp deleted file mode 100644 index 132e9a60f..000000000 --- a/src/video_core/renderer_opengl/texture_filters/anime4k/anime4k_ultrafast.cpp +++ /dev/null @@ -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 src_rect, - const OGLTexture& dst_tex, Common::Rectangle 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(src_rect.left * internal_scale_factor), - static_cast(src_rect.bottom * internal_scale_factor), - static_cast(src_rect.GetWidth() * internal_scale_factor), - static_cast(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(dst_rect.left), static_cast(dst_rect.bottom), - static_cast(dst_rect.GetWidth()), - static_cast(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 diff --git a/src/video_core/renderer_opengl/texture_filters/anime4k/anime4k_ultrafast.h b/src/video_core/renderer_opengl/texture_filters/anime4k/anime4k_ultrafast.h deleted file mode 100644 index 23e95ad4c..000000000 --- a/src/video_core/renderer_opengl/texture_filters/anime4k/anime4k_ultrafast.h +++ /dev/null @@ -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 src_rect, - const OGLTexture& dst_tex, Common::Rectangle dst_rect) override; - -private: - static constexpr u8 internal_scale_factor = 2; - - OpenGLState state{}; - - OGLVertexArray vao; - - struct TempTex { - OGLTexture tex; - OGLFramebuffer fbo; - }; - - std::array samplers; - - OGLProgram gradient_x_program, gradient_y_program, refine_program; -}; - -} // namespace OpenGL diff --git a/src/video_core/renderer_opengl/texture_filters/anime4k/x_gradient.frag b/src/video_core/renderer_opengl/texture_filters/anime4k/x_gradient.frag deleted file mode 100644 index 8103cb77c..000000000 --- a/src/video_core/renderer_opengl/texture_filters/anime4k/x_gradient.frag +++ /dev/null @@ -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); -} diff --git a/src/video_core/renderer_opengl/texture_filters/anime4k/y_gradient.frag b/src/video_core/renderer_opengl/texture_filters/anime4k/y_gradient.frag deleted file mode 100644 index 81e0d0f6e..000000000 --- a/src/video_core/renderer_opengl/texture_filters/anime4k/y_gradient.frag +++ /dev/null @@ -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); -} diff --git a/src/video_core/renderer_opengl/texture_filters/bicubic/bicubic.cpp b/src/video_core/renderer_opengl/texture_filters/bicubic/bicubic.cpp deleted file mode 100644 index b9981ad07..000000000 --- a/src/video_core/renderer_opengl/texture_filters/bicubic/bicubic.cpp +++ /dev/null @@ -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 src_rect, - const OGLTexture& dst_tex, Common::Rectangle 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(dst_rect.left), static_cast(dst_rect.bottom), - static_cast(dst_rect.GetWidth()), - static_cast(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 diff --git a/src/video_core/renderer_opengl/texture_filters/bicubic/bicubic.h b/src/video_core/renderer_opengl/texture_filters/bicubic/bicubic.h deleted file mode 100644 index c39b771ab..000000000 --- a/src/video_core/renderer_opengl/texture_filters/bicubic/bicubic.h +++ /dev/null @@ -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 src_rect, - const OGLTexture& dst_tex, Common::Rectangle dst_rect) override; - -private: - OpenGLState state{}; - OGLProgram program{}; - OGLVertexArray vao{}; - OGLSampler src_sampler{}; -}; - -} // namespace OpenGL diff --git a/src/video_core/renderer_opengl/texture_filters/nearest_neighbor/nearest_neighbor.cpp b/src/video_core/renderer_opengl/texture_filters/nearest_neighbor/nearest_neighbor.cpp deleted file mode 100644 index c5214c480..000000000 --- a/src/video_core/renderer_opengl/texture_filters/nearest_neighbor/nearest_neighbor.cpp +++ /dev/null @@ -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 src_rect, - const OGLTexture& dst_tex, Common::Rectangle 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(dst_rect.left), static_cast(dst_rect.bottom), - static_cast(dst_rect.GetWidth()), - static_cast(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 diff --git a/src/video_core/renderer_opengl/texture_filters/nearest_neighbor/nearest_neighbor.frag b/src/video_core/renderer_opengl/texture_filters/nearest_neighbor/nearest_neighbor.frag deleted file mode 100644 index ad5a33eca..000000000 --- a/src/video_core/renderer_opengl/texture_filters/nearest_neighbor/nearest_neighbor.frag +++ /dev/null @@ -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); -} diff --git a/src/video_core/renderer_opengl/texture_filters/nearest_neighbor/nearest_neighbor.h b/src/video_core/renderer_opengl/texture_filters/nearest_neighbor/nearest_neighbor.h deleted file mode 100644 index 3f9e9ae4c..000000000 --- a/src/video_core/renderer_opengl/texture_filters/nearest_neighbor/nearest_neighbor.h +++ /dev/null @@ -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 src_rect, - const OGLTexture& dst_tex, Common::Rectangle dst_rect) override; - -private: - OpenGLState state{}; - OGLProgram program{}; - OGLVertexArray vao{}; - OGLSampler src_sampler{}; -}; -} // namespace OpenGL diff --git a/src/video_core/renderer_opengl/texture_filters/scale_force/scale_force.cpp b/src/video_core/renderer_opengl/texture_filters/scale_force/scale_force.cpp deleted file mode 100644 index 483f3d79a..000000000 --- a/src/video_core/renderer_opengl/texture_filters/scale_force/scale_force.cpp +++ /dev/null @@ -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 src_rect, - const OGLTexture& dst_tex, Common::Rectangle 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(dst_rect.left), static_cast(dst_rect.bottom), - static_cast(dst_rect.GetWidth()), - static_cast(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 diff --git a/src/video_core/renderer_opengl/texture_filters/scale_force/scale_force.h b/src/video_core/renderer_opengl/texture_filters/scale_force/scale_force.h deleted file mode 100644 index b77f9feb6..000000000 --- a/src/video_core/renderer_opengl/texture_filters/scale_force/scale_force.h +++ /dev/null @@ -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 src_rect, - const OGLTexture& dst_tex, Common::Rectangle dst_rect) override; - -private: - OpenGLState state{}; - OGLProgram program{}; - OGLVertexArray vao{}; - OGLSampler src_sampler{}; -}; - -} // namespace OpenGL diff --git a/src/video_core/renderer_opengl/texture_filters/texture_filter_base.h b/src/video_core/renderer_opengl/texture_filters/texture_filter_base.h deleted file mode 100644 index 9957ee171..000000000 --- a/src/video_core/renderer_opengl/texture_filters/texture_filter_base.h +++ /dev/null @@ -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 -#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 src_rect, - const OGLTexture& dst_tex, Common::Rectangle dst_rect) = 0; - -protected: - OGLFramebuffer draw_fbo; - const u16 scale_factor{}; -}; - -} // namespace OpenGL diff --git a/src/video_core/renderer_opengl/texture_filters/texture_filterer.cpp b/src/video_core/renderer_opengl/texture_filters/texture_filterer.cpp deleted file mode 100644 index 264a3a767..000000000 --- a/src/video_core/renderer_opengl/texture_filters/texture_filterer.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/// Copyright 2020 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include -#include -#include -#include "common/logging/log.h" -#include "video_core/renderer_opengl/texture_filters/anime4k/anime4k_ultrafast.h" -#include "video_core/renderer_opengl/texture_filters/bicubic/bicubic.h" -#include "video_core/renderer_opengl/texture_filters/nearest_neighbor/nearest_neighbor.h" -#include "video_core/renderer_opengl/texture_filters/scale_force/scale_force.h" -#include "video_core/renderer_opengl/texture_filters/texture_filter_base.h" -#include "video_core/renderer_opengl/texture_filters/texture_filterer.h" -#include "video_core/renderer_opengl/texture_filters/xbrz/xbrz_freescale.h" - -namespace OpenGL { - -namespace { - -using TextureFilterContructor = std::function(u16)>; - -template -std::pair FilterMapPair() { - return {T::NAME, std::make_unique}; -}; - -static const std::unordered_map filter_map{ - {TextureFilterer::NONE, [](u16) { return nullptr; }}, - FilterMapPair(), - FilterMapPair(), - FilterMapPair(), - FilterMapPair(), - FilterMapPair(), -}; - -} // namespace - -TextureFilterer::TextureFilterer(std::string_view filter_name, u16 scale_factor) { - Reset(filter_name, scale_factor); -} - -bool TextureFilterer::Reset(std::string_view new_filter_name, u16 new_scale_factor) { - if (filter_name == new_filter_name && (IsNull() || filter->scale_factor == new_scale_factor)) - return false; - - auto iter = filter_map.find(new_filter_name); - if (iter == filter_map.end()) { - LOG_ERROR(Render_OpenGL, "Invalid texture filter: {}", new_filter_name); - filter = nullptr; - return true; - } - - filter_name = iter->first; - filter = iter->second(new_scale_factor); - return true; -} - -bool TextureFilterer::IsNull() const { - return !filter; -} - -bool TextureFilterer::Filter(const OGLTexture& src_tex, Common::Rectangle src_rect, - const OGLTexture& dst_tex, Common::Rectangle dst_rect, - SurfaceType type) { - - // Depth/Stencil texture filtering is not supported for now - if (IsNull() || (type != SurfaceType::Color && type != SurfaceType::Texture)) { - return false; - } - - filter->Filter(src_tex, src_rect, dst_tex, dst_rect); - return true; -} - -std::vector TextureFilterer::GetFilterNames() { - std::vector ret; - std::transform(filter_map.begin(), filter_map.end(), std::back_inserter(ret), - [](auto pair) { return pair.first; }); - std::sort(ret.begin(), ret.end(), [](std::string_view lhs, std::string_view rhs) { - // sort lexicographically with none at the top - bool lhs_is_none{lhs == NONE}; - bool rhs_is_none{rhs == NONE}; - if (lhs_is_none || rhs_is_none) - return lhs_is_none && !rhs_is_none; - return lhs < rhs; - }); - - return ret; -} - -} // namespace OpenGL diff --git a/src/video_core/renderer_opengl/texture_filters/texture_filterer.h b/src/video_core/renderer_opengl/texture_filters/texture_filterer.h deleted file mode 100644 index ca3fec4f7..000000000 --- a/src/video_core/renderer_opengl/texture_filters/texture_filterer.h +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2020 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include -#include -#include -#include "video_core/rasterizer_cache/pixel_format.h" -#include "video_core/renderer_opengl/texture_filters/texture_filter_base.h" - -namespace OpenGL { - -class TextureFilterer { -public: - static constexpr std::string_view NONE = "Linear (Default)"; - -public: - explicit TextureFilterer(std::string_view filter_name, u16 scale_factor); - - // Returns true if the filter actually changed - bool Reset(std::string_view new_filter_name, u16 new_scale_factor); - - // Returns true if there is no active filter - bool IsNull() const; - - // Returns true if the texture was able to be filtered - bool Filter(const OGLTexture& src_tex, Common::Rectangle src_rect, - const OGLTexture& dst_tex, Common::Rectangle dst_rect, SurfaceType type); - - static std::vector GetFilterNames(); - -private: - std::string_view filter_name = NONE; - std::unique_ptr filter; -}; - -} // namespace OpenGL diff --git a/src/video_core/renderer_opengl/texture_filters/xbrz/xbrz_freescale.cpp b/src/video_core/renderer_opengl/texture_filters/xbrz/xbrz_freescale.cpp deleted file mode 100644 index 3849bb2c5..000000000 --- a/src/video_core/renderer_opengl/texture_filters/xbrz/xbrz_freescale.cpp +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2019 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -// adapted from -// https://github.com/libretro/glsl-shaders/blob/d7a8b8eb2a61a5732da4cbe2e0f9ad30600c3f17/xbrz/shaders/xbrz-freescale.glsl - -// xBRZ freescale -// based on : -// 4xBRZ shader - Copyright (C) 2014-2016 DeSmuME team -// -// This file is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 2 of the License, or -// (at your option) any later version. -// -// This file is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with the this software. If not, see . - -// Hyllian's xBR-vertex code and texel mapping -// Copyright (C) 2011/2016 Hyllian - sergiogdb@gmail.com -// 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/xbrz/xbrz_freescale.h" - -#include "shaders/xbrz_freescale.frag" -#include "shaders/xbrz_freescale.vert" - -namespace OpenGL { - -XbrzFreescale::XbrzFreescale(u16 scale_factor) : TextureFilterBase(scale_factor) { - - const OpenGLState cur_state = OpenGLState::GetCurState(); - - program.Create(xbrz_freescale_vert.data(), xbrz_freescale_frag.data()); - vao.Create(); - src_sampler.Create(); - - state.draw.shader_program = program.handle; - state.Apply(); - - 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); - - const GLint scale_loc = glGetUniformLocation(program.handle, "scale"); - glUniform1f(scale_loc, static_cast(scale_factor)); - - cur_state.Apply(); - state.draw.vertex_array = vao.handle; - state.draw.shader_program = program.handle; - state.texture_units[0].sampler = src_sampler.handle; -} - -void XbrzFreescale::Filter(const OGLTexture& src_tex, Common::Rectangle src_rect, - const OGLTexture& dst_tex, Common::Rectangle 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(dst_rect.left), static_cast(dst_rect.bottom), - static_cast(dst_rect.GetWidth()), - static_cast(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 diff --git a/src/video_core/renderer_opengl/texture_filters/xbrz/xbrz_freescale.h b/src/video_core/renderer_opengl/texture_filters/xbrz/xbrz_freescale.h deleted file mode 100644 index 274fccf11..000000000 --- a/src/video_core/renderer_opengl/texture_filters/xbrz/xbrz_freescale.h +++ /dev/null @@ -1,26 +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 XbrzFreescale : public TextureFilterBase { -public: - static constexpr std::string_view NAME = "xBRZ freescale"; - - explicit XbrzFreescale(u16 scale_factor); - void Filter(const OGLTexture& src_tex, Common::Rectangle src_rect, - const OGLTexture& dst_tex, Common::Rectangle dst_rect) override; - -private: - OpenGLState state{}; - OGLProgram program{}; - OGLVertexArray vao{}; - OGLSampler src_sampler{}; -}; -} // namespace OpenGL diff --git a/src/video_core/renderer_opengl/texture_filters/xbrz/xbrz_freescale.vert b/src/video_core/renderer_opengl/texture_filters/xbrz/xbrz_freescale.vert deleted file mode 100644 index 63905075f..000000000 --- a/src/video_core/renderer_opengl/texture_filters/xbrz/xbrz_freescale.vert +++ /dev/null @@ -1,17 +0,0 @@ -//? #version 330 -out vec2 tex_coord; -out vec2 source_size; -out vec2 output_size; - -uniform sampler2D tex; -uniform lowp float scale; - -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); - tex_coord = (vertices[gl_VertexID] + 1.0) / 2.0; - source_size = vec2(textureSize(tex, 0)); - output_size = source_size * scale; -}