2011-11-16 22:17:37 +00:00
/*
* Copyright ( C ) 2011 - 2011 Gregory hainaut
* Copyright ( C ) 2007 - 2009 Gabest
*
* This Program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 , or ( at your option )
* any later version .
*
* This Program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with GNU Make ; see the file COPYING . If not , write to
2012-09-09 18:16:11 +00:00
* the Free Software Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 , USA USA .
2011-11-16 22:17:37 +00:00
* http : //www.gnu.org/copyleft/gpl.html
*
*/
2013-01-10 13:13:59 +00:00
# include "stdafx.h"
2012-08-08 17:49:23 +00:00
# include <limits.h>
2011-11-16 22:17:37 +00:00
# include "GSTextureOGL.h"
2013-08-05 20:25:25 +00:00
# include "GLState.h"
2015-05-16 10:35:45 +00:00
# include "GSPng.h"
2013-08-03 08:29:01 +00:00
2014-03-24 14:03:02 +00:00
# ifdef ENABLE_OGL_DEBUG_MEM_BW
2015-04-25 12:18:21 +00:00
extern uint64 g_real_texture_upload_byte ;
2014-03-24 14:03:02 +00:00
# endif
2013-10-24 20:54:27 +00:00
// FIXME OGL4: investigate, only 1 unpack buffer always bound
2013-08-03 08:29:01 +00:00
namespace PboPool {
2015-05-16 10:35:45 +00:00
2016-06-17 07:42:12 +00:00
const uint32 m_pbo_size = 64 * 1024 * 1024 ;
const uint32 m_seg_size = 16 * 1024 * 1024 ;
GLuint m_buffer ;
uptr m_offset ;
char * m_map ;
2013-08-17 09:05:41 +00:00
uint32 m_size ;
2016-06-17 07:42:12 +00:00
GLsync m_fence [ m_pbo_size / m_seg_size ] ;
2014-03-23 14:42:05 +00:00
// Option for buffer storage
2014-11-01 19:17:08 +00:00
// XXX: actually does I really need coherent and barrier???
// As far as I understand glTexSubImage2D is a client-server transfer so no need to make
// the value visible to the server
2015-04-23 19:10:43 +00:00
const GLbitfield common_flags = GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT ;
const GLbitfield map_flags = common_flags | GL_MAP_FLUSH_EXPLICIT_BIT ;
const GLbitfield create_flags = common_flags | GL_CLIENT_STORAGE_BIT ;
2014-03-23 14:42:05 +00:00
2013-08-03 08:29:01 +00:00
void Init ( ) {
2016-06-17 07:42:12 +00:00
glGenBuffers ( 1 , & m_buffer ) ;
2013-08-17 09:05:41 +00:00
2016-06-17 07:42:12 +00:00
BindPbo ( ) ;
2013-08-17 09:05:41 +00:00
2016-06-17 07:42:12 +00:00
glObjectLabel ( GL_BUFFER , m_buffer , - 1 , " PBO " ) ;
2016-06-07 18:30:56 +00:00
2016-06-17 07:42:12 +00:00
glBufferStorage ( GL_PIXEL_UNPACK_BUFFER , m_pbo_size , NULL , create_flags ) ;
m_map = ( char * ) glMapBufferRange ( GL_PIXEL_UNPACK_BUFFER , 0 , m_pbo_size , map_flags ) ;
m_offset = 0 ;
2013-08-17 09:05:41 +00:00
2016-06-17 07:42:12 +00:00
for ( size_t i = 0 ; i < countof ( m_fence ) ; i + + ) {
m_fence [ i ] = 0 ;
2013-08-03 08:29:01 +00:00
}
2016-06-17 07:42:12 +00:00
2013-08-03 08:29:01 +00:00
UnbindPbo ( ) ;
}
2013-08-17 09:05:41 +00:00
char * Map ( uint32 size ) {
2014-03-23 14:42:05 +00:00
char * map ;
2019-08-16 13:45:11 +00:00
// Note: keep offset aligned for SSE/AVX
m_size = ( size + 63 ) & ~ 0x3F ;
2013-08-17 09:05:41 +00:00
2014-11-01 19:17:08 +00:00
if ( m_size > m_pbo_size ) {
2016-08-13 10:40:30 +00:00
fprintf ( stderr , " BUG: PBO too small %u but need %u \n " , m_pbo_size , m_size ) ;
2013-08-17 09:05:41 +00:00
}
2016-04-07 20:17:22 +00:00
// Note: texsubimage will access currently bound buffer
// Pbo ready let's get a pointer
BindPbo ( ) ;
2013-08-17 09:05:41 +00:00
2016-06-17 07:42:12 +00:00
Sync ( ) ;
map = m_map + m_offset ;
2014-03-23 14:42:05 +00:00
return map ;
2013-08-17 09:05:41 +00:00
}
void Unmap ( ) {
2016-06-17 07:42:12 +00:00
glFlushMappedBufferRange ( GL_PIXEL_UNPACK_BUFFER , m_offset , m_size ) ;
2013-08-17 09:05:41 +00:00
}
2016-01-13 23:42:56 +00:00
uptr Offset ( ) {
2016-06-17 07:42:12 +00:00
return m_offset ;
2013-08-17 09:05:41 +00:00
}
2013-08-03 08:29:01 +00:00
void Destroy ( ) {
2016-06-17 07:42:12 +00:00
m_map = NULL ;
m_offset = 0 ;
2016-04-07 20:17:22 +00:00
2016-06-17 07:42:12 +00:00
for ( size_t i = 0 ; i < countof ( m_fence ) ; i + + ) {
glDeleteSync ( m_fence [ i ] ) ;
}
glDeleteBuffers ( 1 , & m_buffer ) ;
2013-08-17 09:05:41 +00:00
}
2016-06-17 07:42:12 +00:00
void BindPbo ( ) {
glBindBuffer ( GL_PIXEL_UNPACK_BUFFER , m_buffer ) ;
2013-08-03 08:29:01 +00:00
}
2016-06-17 07:42:12 +00:00
void Sync ( ) {
uint32 segment_current = m_offset / m_seg_size ;
uint32 segment_next = ( m_offset + m_size ) / m_seg_size ;
2015-05-15 16:32:47 +00:00
2016-06-17 07:42:12 +00:00
if ( segment_current ! = segment_next ) {
if ( segment_next > = countof ( m_fence ) ) {
segment_next = 0 ;
}
// Align current transfer on the start of the segment
m_offset = m_seg_size * segment_next ;
2019-08-16 13:52:37 +00:00
if ( m_size > m_seg_size ) {
fprintf ( stderr , " BUG: PBO Map size %u is bigger than a single segment %u. Crossing more than one fence is not supported yet, texture data may be corrupted. \n " , m_size , m_seg_size ) ;
// TODO Synchronize all crossed fences
}
2016-06-17 07:42:12 +00:00
// protect the left segment
m_fence [ segment_current ] = glFenceSync ( GL_SYNC_GPU_COMMANDS_COMPLETE , 0 ) ;
// Check next segment is free
if ( m_fence [ segment_next ] ) {
GLenum status = glClientWaitSync ( m_fence [ segment_next ] , GL_SYNC_FLUSH_COMMANDS_BIT , GL_TIMEOUT_IGNORED ) ;
// Potentially it doesn't work on AMD driver which might always return GL_CONDITION_SATISFIED
if ( status ! = GL_ALREADY_SIGNALED ) {
GL_PERF ( " GL_PIXEL_UNPACK_BUFFER: Sync Sync (%x)! Buffer too small ? " , status ) ;
}
glDeleteSync ( m_fence [ segment_next ] ) ;
m_fence [ segment_next ] = 0 ;
2015-05-15 16:32:47 +00:00
}
}
}
2013-08-03 08:29:01 +00:00
void UnbindPbo ( ) {
2015-10-17 15:05:15 +00:00
glBindBuffer ( GL_PIXEL_UNPACK_BUFFER , 0 ) ;
2013-08-03 08:29:01 +00:00
}
2013-08-17 09:05:41 +00:00
void EndTransfer ( ) {
2019-08-16 13:45:11 +00:00
m_offset + = m_size ;
2013-08-17 09:05:41 +00:00
}
2013-08-03 08:29:01 +00:00
}
2016-09-30 18:04:24 +00:00
GSTextureOGL : : GSTextureOGL ( int type , int w , int h , int format , GLuint fbo_read , bool mipmap )
2018-07-31 22:02:00 +00:00
: m_clean ( false ) , m_generate_mipmap ( true ) , m_local_buffer ( nullptr ) , m_r_x ( 0 ) , m_r_y ( 0 ) , m_r_w ( 0 ) , m_r_h ( 0 ) , m_layer ( 0 )
2011-11-16 22:17:37 +00:00
{
2015-05-01 18:04:23 +00:00
// OpenGL didn't like dimensions of size 0
2017-05-26 16:30:44 +00:00
m_size . x = std : : max ( 1 , w ) ;
m_size . y = std : : max ( 1 , h ) ;
2011-11-21 22:36:03 +00:00
m_format = format ;
m_type = type ;
2012-01-08 21:59:42 +00:00
m_fbo_read = fbo_read ;
2013-07-19 19:25:50 +00:00
m_texture_id = 0 ;
2019-02-07 11:16:32 +00:00
m_sparse = false ;
m_max_layer = 1 ;
2011-11-16 22:17:37 +00:00
2013-08-02 16:38:12 +00:00
// Bunch of constant parameter
switch ( m_format ) {
2015-07-23 07:45:47 +00:00
// 1 Channel integer
2015-05-20 06:07:40 +00:00
case GL_R32UI :
2013-08-28 08:44:16 +00:00
case GL_R32I :
2013-08-02 16:38:12 +00:00
m_int_format = GL_RED_INTEGER ;
2015-05-20 06:07:40 +00:00
m_int_type = ( m_format = = GL_R32UI ) ? GL_UNSIGNED_INT : GL_INT ;
2013-08-02 16:38:12 +00:00
m_int_shift = 2 ;
break ;
case GL_R16UI :
m_int_format = GL_RED_INTEGER ;
m_int_type = GL_UNSIGNED_SHORT ;
m_int_shift = 1 ;
break ;
2015-07-23 07:45:47 +00:00
// 1 Channel normalized
case GL_R8 :
m_int_format = GL_RED ;
m_int_type = GL_UNSIGNED_BYTE ;
m_int_shift = 0 ;
break ;
// 4 channel normalized
case GL_RGBA16 :
m_int_format = GL_RGBA ;
m_int_type = GL_UNSIGNED_SHORT ;
m_int_shift = 3 ;
break ;
2013-08-02 16:38:12 +00:00
case GL_RGBA8 :
m_int_format = GL_RGBA ;
m_int_type = GL_UNSIGNED_BYTE ;
m_int_shift = 2 ;
break ;
2015-07-23 07:45:47 +00:00
// 4 channel integer
case GL_RGBA16I :
case GL_RGBA16UI :
m_int_format = GL_RGBA_INTEGER ;
m_int_type = ( m_format = = GL_R16UI ) ? GL_UNSIGNED_SHORT : GL_SHORT ;
m_int_shift = 3 ;
2015-11-06 21:34:53 +00:00
break ;
2015-07-23 07:45:47 +00:00
// 4 channel float
case GL_RGBA32F :
m_int_format = GL_RGBA ;
m_int_type = GL_FLOAT ;
m_int_shift = 4 ;
2013-08-02 16:38:12 +00:00
break ;
2015-07-23 07:45:47 +00:00
case GL_RGBA16F :
m_int_format = GL_RGBA ;
m_int_type = GL_HALF_FLOAT ;
m_int_shift = 3 ;
break ;
2016-05-15 11:11:21 +00:00
// Depth buffer
2013-08-03 08:29:01 +00:00
case GL_DEPTH32F_STENCIL8 :
2016-05-15 11:11:21 +00:00
m_int_format = GL_DEPTH_STENCIL ;
m_int_type = GL_FLOAT_32_UNSIGNED_INT_24_8_REV ;
2016-07-07 19:33:59 +00:00
m_int_shift = 3 ; // 4 bytes for depth + 4 bytes for stencil by texels
2016-05-15 11:11:21 +00:00
break ;
// Backbuffer
case 0 :
2013-08-03 08:29:01 +00:00
m_int_format = 0 ;
m_int_type = 0 ;
2016-07-07 19:33:59 +00:00
m_int_shift = 2 ; // 4 bytes by texels
2013-08-03 08:29:01 +00:00
break ;
2016-03-28 15:39:43 +00:00
2013-08-02 16:38:12 +00:00
default :
2016-03-28 15:39:43 +00:00
m_int_format = 0 ;
m_int_type = 0 ;
m_int_shift = 0 ;
2013-08-02 16:38:12 +00:00
ASSERT ( 0 ) ;
}
2019-02-07 11:16:32 +00:00
switch ( m_type ) {
case GSTexture : : Backbuffer :
return ; // backbuffer isn't a real texture
case GSTexture : : Offscreen :
// Offscreen is only used to read color. So it only requires 4B by pixel
m_local_buffer = ( uint8 * ) _aligned_malloc ( m_size . x * m_size . y * 4 , 32 ) ;
break ;
case GSTexture : : Texture :
// Only 32 bits input texture will be supported for mipmap
m_max_layer = mipmap & & m_format = = GL_RGBA8 ? ( int ) log2 ( std : : max ( w , h ) ) : 1 ;
break ;
case SparseRenderTarget :
case SparseDepthStencil :
m_sparse = true ;
break ;
default :
break ;
}
switch ( m_format ) {
case GL_R16UI :
case GL_R8 :
m_sparse & = GLLoader : : found_compatible_GL_ARB_sparse_texture2 ;
SetGpuPageSize ( GSVector2i ( 255 , 255 ) ) ;
break ;
case GL_R32UI :
case GL_R32I :
case GL_RGBA16 :
case GL_RGBA8 :
case GL_RGBA16I :
case GL_RGBA16UI :
case GL_RGBA16F :
case 0 :
m_sparse & = GLLoader : : found_compatible_GL_ARB_sparse_texture2 ;
SetGpuPageSize ( GSVector2i ( 127 , 127 ) ) ;
break ;
case GL_RGBA32F :
m_sparse & = GLLoader : : found_compatible_GL_ARB_sparse_texture2 ;
SetGpuPageSize ( GSVector2i ( 63 , 63 ) ) ;
break ;
case GL_DEPTH32F_STENCIL8 :
m_sparse & = GLLoader : : found_compatible_sparse_depth ;
SetGpuPageSize ( GSVector2i ( 127 , 127 ) ) ;
break ;
default :
ASSERT ( 0 ) ;
}
// Create a gl object (texture isn't allocated here)
glCreateTextures ( GL_TEXTURE_2D , 1 , & m_texture_id ) ;
if ( m_format = = GL_R8 ) {
// Emulate DX behavior, beside it avoid special code in shader to differentiate
// palette texture from a GL_RGBA target or a GL_R texture.
glTextureParameteri ( m_texture_id , GL_TEXTURE_SWIZZLE_A , GL_RED ) ;
}
if ( m_sparse ) {
GSVector2i old_size = m_size ;
m_size = RoundUpPage ( m_size ) ;
if ( m_size ! = old_size ) {
fprintf ( stderr , " Sparse texture size (%dx%d) isn't a multiple of gpu page size (%dx%d) \n " ,
old_size . x , old_size . y , m_gpu_page_size . x , m_gpu_page_size . y ) ;
}
glTextureParameteri ( m_texture_id , GL_TEXTURE_SPARSE_ARB , true ) ;
} else {
m_committed_size = m_size ;
}
m_mem_usage = ( m_committed_size . x * m_committed_size . y ) < < m_int_shift ;
2016-07-09 09:43:28 +00:00
2016-07-10 08:23:23 +00:00
static int every_512 = 0 ;
2016-07-09 09:43:28 +00:00
GLState : : available_vram - = m_mem_usage ;
2016-07-10 08:23:23 +00:00
if ( ( GLState : : available_vram < 0 ) & & ( every_512 % 512 = = 0 ) ) {
2021-02-20 18:21:31 +00:00
fprintf ( stderr , " Available VRAM is very low (%lld), a crash is expected! Enable conservative buffer allocation or reduce upscaling! \n " , GLState : : available_vram ) ;
2016-07-10 08:23:23 +00:00
every_512 + + ;
// Pull emergency break
2016-10-13 18:44:59 +00:00
throw std : : bad_alloc ( ) ;
2016-07-07 19:33:59 +00:00
}
2019-02-07 11:16:32 +00:00
glTextureStorage2D ( m_texture_id , m_max_layer + GL_TEX_LEVEL_0 , m_format , m_size . x , m_size . y ) ;
2011-11-16 22:17:37 +00:00
}
GSTextureOGL : : ~ GSTextureOGL ( )
{
2013-06-01 09:29:57 +00:00
/* Unbind the texture from our local state */
2015-05-16 10:35:45 +00:00
2013-08-05 20:25:25 +00:00
if ( m_texture_id = = GLState : : rt )
GLState : : rt = 0 ;
if ( m_texture_id = = GLState : : ds )
GLState : : ds = 0 ;
2015-04-25 11:06:02 +00:00
for ( size_t i = 0 ; i < countof ( GLState : : tex_unit ) ; i + + ) {
if ( m_texture_id = = GLState : : tex_unit [ i ] )
GLState : : tex_unit [ i ] = 0 ;
}
2013-06-01 09:29:57 +00:00
2011-12-30 13:55:33 +00:00
glDeleteTextures ( 1 , & m_texture_id ) ;
2015-05-18 09:29:04 +00:00
2016-07-07 19:33:59 +00:00
GLState : : available_vram + = m_mem_usage ;
2015-05-18 09:29:04 +00:00
if ( m_local_buffer )
_aligned_free ( m_local_buffer ) ;
2011-11-16 22:17:37 +00:00
}
2016-05-15 11:11:21 +00:00
void GSTextureOGL : : Clear ( const void * data )
{
glClearTexImage ( m_texture_id , GL_TEX_LEVEL_0 , m_int_format , m_int_type , data ) ;
}
void GSTextureOGL : : Clear ( const void * data , const GSVector4i & area )
{
2016-05-15 13:54:29 +00:00
glClearTexSubImage ( m_texture_id , GL_TEX_LEVEL_0 , area . x , area . y , 0 , area . width ( ) , area . height ( ) , 1 , m_int_format , m_int_type , data ) ;
2016-05-15 11:11:21 +00:00
}
2016-09-30 19:36:52 +00:00
bool GSTextureOGL : : Update ( const GSVector4i & r , const void * data , int pitch , int layer )
2011-11-16 22:17:37 +00:00
{
2013-07-19 19:25:50 +00:00
ASSERT ( m_type ! = GSTexture : : DepthStencil & & m_type ! = GSTexture : : Offscreen ) ;
2016-03-18 23:58:26 +00:00
2016-09-30 18:04:24 +00:00
if ( layer > = m_max_layer )
return true ;
2016-03-18 23:58:26 +00:00
// Default upload path for the texture is the Map/Unmap
// This path is mostly used for palette. But also for texture that could
// overflow the pbo buffer
// Data upload is rather small typically 64B or 1024B. So don't bother with PBO
// and directly send the data to the GL synchronously
2015-05-11 09:19:00 +00:00
2015-05-12 16:03:06 +00:00
m_clean = false ;
2011-12-07 22:05:46 +00:00
2015-05-11 14:29:09 +00:00
uint32 row_byte = r . width ( ) < < m_int_shift ;
uint32 map_size = r . height ( ) * row_byte ;
2014-03-24 14:03:02 +00:00
# ifdef ENABLE_OGL_DEBUG_MEM_BW
2015-05-14 18:04:08 +00:00
g_real_texture_upload_byte + = map_size ;
2014-03-24 14:03:02 +00:00
# endif
2014-11-01 19:17:08 +00:00
2016-03-18 23:58:26 +00:00
#if 0
if ( r . height ( ) = = 1 ) {
// Palette data. Transfer is small either 64B or 1024B.
// Sometimes it is faster, sometimes slower.
glTextureSubImage2D ( m_texture_id , GL_TEX_LEVEL_0 , r . x , r . y , r . width ( ) , r . height ( ) , m_int_format , m_int_type , data ) ;
return true ;
}
# endif
GL_PUSH ( " Upload Texture %d " , m_texture_id ) ;
// The easy solution without PBO
#if 0
// Likely a bad texture
glPixelStorei ( GL_UNPACK_ROW_LENGTH , pitch > > m_int_shift ) ;
glTextureSubImage2D ( m_texture_id , GL_TEX_LEVEL_0 , r . x , r . y , r . width ( ) , r . height ( ) , m_int_format , m_int_type , data ) ;
glPixelStorei ( GL_UNPACK_ROW_LENGTH , 0 ) ; // Restore default behavior
# endif
// The complex solution with PBO
# if 1
char * src = ( char * ) data ;
char * map = PboPool : : Map ( map_size ) ;
2015-08-07 12:04:54 +00:00
// PERF: slow path of the texture upload. Dunno if we could do better maybe check if TC can keep row_byte == pitch
2015-05-11 14:29:09 +00:00
// Note: row_byte != pitch
for ( int h = 0 ; h < r . height ( ) ; h + + ) {
memcpy ( map , src , row_byte ) ;
map + = row_byte ;
src + = pitch ;
}
2013-08-03 08:29:01 +00:00
2013-08-17 09:05:41 +00:00
PboPool : : Unmap ( ) ;
2016-09-30 18:04:24 +00:00
glTextureSubImage2D ( m_texture_id , layer , r . x , r . y , r . width ( ) , r . height ( ) , m_int_format , m_int_type , ( const void * ) PboPool : : Offset ( ) ) ;
2013-08-17 09:05:41 +00:00
2013-10-24 20:54:27 +00:00
// FIXME OGL4: investigate, only 1 unpack buffer always bound
PboPool : : UnbindPbo ( ) ;
2013-08-03 08:29:01 +00:00
2013-08-17 09:05:41 +00:00
PboPool : : EndTransfer ( ) ;
2016-03-18 23:58:26 +00:00
# endif
2013-08-03 08:29:01 +00:00
2016-10-02 08:41:18 +00:00
m_generate_mipmap = true ;
2011-12-30 22:25:17 +00:00
return true ;
2011-11-16 22:17:37 +00:00
}
2016-09-30 19:36:52 +00:00
bool GSTextureOGL : : Map ( GSMap & m , const GSVector4i * _r , int layer )
2011-11-16 22:17:37 +00:00
{
2016-09-30 18:04:24 +00:00
if ( layer > = m_max_layer )
return false ;
2016-03-21 17:52:26 +00:00
GSVector4i r = _r ? * _r : GSVector4i ( 0 , 0 , m_size . x , m_size . y ) ;
2016-05-16 17:20:01 +00:00
// Will need some investigation
ASSERT ( r . width ( ) ! = 0 ) ;
ASSERT ( r . height ( ) ! = 0 ) ;
uint32 row_byte = r . width ( ) < < m_int_shift ;
m . pitch = row_byte ;
2013-07-19 19:25:50 +00:00
2016-03-18 19:20:23 +00:00
if ( m_type = = GSTexture : : Offscreen ) {
// The fastest way will be to use a PBO to read the data asynchronously. Unfortunately GSdx
// architecture is waiting the data right now.
2015-04-30 23:26:44 +00:00
#if 0
2016-03-18 19:20:23 +00:00
// Maybe it is as good as the code below. I don't know
// With openGL 4.5 you can use glGetTextureSubImage
2015-04-30 23:26:44 +00:00
2016-05-16 16:45:47 +00:00
glGetTextureSubImage ( m_texture_id , GL_TEX_LEVEL_0 , r . x , r . y , 0 , r . width ( ) , r . height ( ) , 1 , m_int_format , m_int_type , m_size . x * m_size . y * 4 , m_local_buffer ) ;
2015-04-30 23:26:44 +00:00
# else
2012-03-05 20:16:26 +00:00
2016-03-18 19:20:23 +00:00
// Bind the texture to the read framebuffer to avoid any disturbance
glBindFramebuffer ( GL_READ_FRAMEBUFFER , m_fbo_read ) ;
glFramebufferTexture2D ( GL_READ_FRAMEBUFFER , GL_COLOR_ATTACHMENT0 , GL_TEXTURE_2D , m_texture_id , 0 ) ;
2013-07-19 19:25:50 +00:00
2016-10-02 10:16:48 +00:00
// In case a target is 16 bits (GT4)
glPixelStorei ( GL_PACK_ALIGNMENT , 1u < < m_int_shift ) ;
2016-03-21 17:52:26 +00:00
glReadPixels ( r . x , r . y , r . width ( ) , r . height ( ) , m_int_format , m_int_type , m_local_buffer ) ;
2016-03-18 18:26:43 +00:00
2016-03-18 19:20:23 +00:00
glBindFramebuffer ( GL_READ_FRAMEBUFFER , 0 ) ;
2011-12-23 12:32:40 +00:00
2015-04-30 23:26:44 +00:00
# endif
2011-12-23 12:32:40 +00:00
2016-03-18 19:20:23 +00:00
m . bits = m_local_buffer ;
2015-04-30 23:26:44 +00:00
2016-03-18 19:20:23 +00:00
return true ;
} else if ( m_type = = GSTexture : : Texture | | m_type = = GSTexture : : RenderTarget ) {
2016-05-05 15:42:22 +00:00
GL_PUSH_ ( " Upload Texture %d " , m_texture_id ) ; // POP is in Unmap
2016-03-18 19:20:23 +00:00
m_clean = false ;
2016-03-21 17:52:26 +00:00
uint32 map_size = r . height ( ) * row_byte ;
2016-03-18 19:20:23 +00:00
m . bits = ( uint8 * ) PboPool : : Map ( map_size ) ;
# ifdef ENABLE_OGL_DEBUG_MEM_BW
g_real_texture_upload_byte + = map_size ;
# endif
// Save the area for the unmap
2016-03-21 17:52:26 +00:00
m_r_x = r . x ;
m_r_y = r . y ;
m_r_w = r . width ( ) ;
m_r_h = r . height ( ) ;
2016-09-30 18:04:24 +00:00
m_layer = layer ;
2016-03-18 19:20:23 +00:00
return true ;
}
return false ;
2011-11-16 22:17:37 +00:00
}
void GSTextureOGL : : Unmap ( )
{
2016-03-18 19:20:23 +00:00
if ( m_type = = GSTexture : : Texture | | m_type = = GSTexture : : RenderTarget ) {
PboPool : : Unmap ( ) ;
2016-09-30 18:04:24 +00:00
glTextureSubImage2D ( m_texture_id , m_layer , m_r_x , m_r_y , m_r_w , m_r_h , m_int_format , m_int_type , ( const void * ) PboPool : : Offset ( ) ) ;
2016-03-18 19:20:23 +00:00
// FIXME OGL4: investigate, only 1 unpack buffer always bound
PboPool : : UnbindPbo ( ) ;
PboPool : : EndTransfer ( ) ;
2016-10-02 08:41:18 +00:00
m_generate_mipmap = true ;
2016-03-18 19:20:23 +00:00
GL_POP ( ) ; // PUSH is in Map
}
2011-11-16 22:17:37 +00:00
}
2016-10-02 08:41:18 +00:00
void GSTextureOGL : : GenerateMipmap ( )
{
if ( m_generate_mipmap & & m_max_layer > 1 ) {
glGenerateTextureMipmap ( m_texture_id ) ;
m_generate_mipmap = false ;
}
}
2019-02-07 11:17:15 +00:00
void GSTextureOGL : : CommitPages ( const GSVector2i & region , bool commit )
{
GLState : : available_vram + = m_mem_usage ;
if ( commit ) {
2019-02-27 09:53:32 +00:00
if ( m_committed_size . x = = 0 ) {
// Nothing allocated so far
GL_INS ( " CommitPages initial %dx%d of %u " , region . x , region . y , m_texture_id ) ;
glTexturePageCommitmentEXT ( m_texture_id , GL_TEX_LEVEL_0 , 0 , 0 , 0 , region . x , region . y , 1 , commit ) ;
} else {
GL_INS ( " CommitPages extend %dx%d to %dx%d of %u " , m_committed_size . x , m_committed_size . y , region . x , region . y , m_texture_id ) ;
int w = region . x - m_committed_size . x ;
int h = region . y - m_committed_size . y ;
// Extend width
glTexturePageCommitmentEXT ( m_texture_id , GL_TEX_LEVEL_0 , m_committed_size . x , 0 , 0 , w , m_committed_size . y , 1 , commit ) ;
// Extend height
glTexturePageCommitmentEXT ( m_texture_id , GL_TEX_LEVEL_0 , 0 , m_committed_size . y , 0 , region . x , h , 1 , commit ) ;
}
2019-02-07 11:17:15 +00:00
m_committed_size = region ;
2019-02-27 09:53:32 +00:00
2019-02-07 11:17:15 +00:00
} else {
2019-02-27 09:53:32 +00:00
// Release everything
2019-02-07 11:17:15 +00:00
GL_INS ( " CommitPages release of %u " , m_texture_id ) ;
2019-02-27 09:53:32 +00:00
glTexturePageCommitmentEXT ( m_texture_id , GL_TEX_LEVEL_0 , 0 , 0 , 0 , m_committed_size . x , m_committed_size . y , 1 , commit ) ;
2019-02-07 11:17:15 +00:00
m_committed_size = GSVector2i ( 0 , 0 ) ;
}
m_mem_usage = ( m_committed_size . x * m_committed_size . y ) < < m_int_shift ;
GLState : : available_vram - = m_mem_usage ;
}
2018-12-25 20:16:14 +00:00
bool GSTextureOGL : : Save ( const std : : string & fn )
2011-11-16 22:17:37 +00:00
{
2011-12-07 22:05:46 +00:00
// Collect the texture data
2019-02-18 18:48:08 +00:00
uint32 pitch = 4 * m_committed_size . x ;
uint32 buf_size = pitch * m_committed_size . y * 2 ; // Note *2 for security (depth/stencil)
2016-02-21 09:59:49 +00:00
std : : unique_ptr < uint8 [ ] > image ( new uint8 [ buf_size ] ) ;
2015-05-16 10:35:45 +00:00
# ifdef ENABLE_OGL_DEBUG
GSPng : : Format fmt = GSPng : : RGB_A_PNG ;
# else
GSPng : : Format fmt = GSPng : : RGB_PNG ;
# endif
2011-12-29 14:24:26 +00:00
2011-12-23 12:32:40 +00:00
if ( IsBackbuffer ( ) ) {
2019-02-18 18:48:08 +00:00
glReadPixels ( 0 , 0 , m_committed_size . x , m_committed_size . y , GL_RGBA , GL_UNSIGNED_BYTE , image . get ( ) ) ;
2011-12-29 14:24:26 +00:00
} else if ( IsDss ( ) ) {
2015-10-17 15:05:15 +00:00
glBindFramebuffer ( GL_READ_FRAMEBUFFER , m_fbo_read ) ;
2012-08-08 17:49:23 +00:00
2015-10-17 15:05:15 +00:00
glFramebufferTexture2D ( GL_READ_FRAMEBUFFER , GL_DEPTH_ATTACHMENT , GL_TEXTURE_2D , m_texture_id , 0 ) ;
2019-02-18 18:48:08 +00:00
glReadPixels ( 0 , 0 , m_committed_size . x , m_committed_size . y , GL_DEPTH_COMPONENT , GL_UNSIGNED_INT , image . get ( ) ) ;
2012-08-08 17:49:23 +00:00
2015-10-17 15:05:15 +00:00
glBindFramebuffer ( GL_READ_FRAMEBUFFER , 0 ) ;
2015-05-16 10:35:45 +00:00
2016-02-22 19:32:57 +00:00
fmt = GSPng : : RGB_A_PNG ;
2013-08-28 08:44:16 +00:00
} else if ( m_format = = GL_R32I ) {
2019-02-18 18:48:08 +00:00
// Note: 4.5 function used for accurate DATE
// barely used outside of dev and not sparse anyway
2016-02-21 09:59:49 +00:00
glGetTextureImage ( m_texture_id , 0 , GL_RED_INTEGER , GL_INT , buf_size , image . get ( ) ) ;
2015-05-16 10:35:45 +00:00
fmt = GSPng : : R32I_PNG ;
2011-12-23 12:32:40 +00:00
} else {
2015-10-17 15:05:15 +00:00
glBindFramebuffer ( GL_READ_FRAMEBUFFER , m_fbo_read ) ;
2012-01-08 21:59:42 +00:00
2015-10-17 15:05:15 +00:00
glFramebufferTexture2D ( GL_READ_FRAMEBUFFER , GL_COLOR_ATTACHMENT0 , GL_TEXTURE_2D , m_texture_id , 0 ) ;
2012-01-08 21:59:42 +00:00
2015-05-16 10:35:45 +00:00
if ( m_format = = GL_RGBA8 ) {
2019-02-18 18:48:08 +00:00
glReadPixels ( 0 , 0 , m_committed_size . x , m_committed_size . y , GL_RGBA , GL_UNSIGNED_BYTE , image . get ( ) ) ;
2015-05-16 10:35:45 +00:00
}
2012-03-05 20:16:26 +00:00
else if ( m_format = = GL_R16UI )
{
2019-02-18 18:48:08 +00:00
glReadPixels ( 0 , 0 , m_committed_size . x , m_committed_size . y , GL_RED_INTEGER , GL_UNSIGNED_SHORT , image . get ( ) ) ;
2015-05-16 10:35:45 +00:00
fmt = GSPng : : R16I_PNG ;
2012-03-05 20:16:26 +00:00
}
else if ( m_format = = GL_R8 )
{
2015-05-16 10:35:45 +00:00
fmt = GSPng : : R8I_PNG ;
2019-02-18 18:48:08 +00:00
glReadPixels ( 0 , 0 , m_committed_size . x , m_committed_size . y , GL_RED , GL_UNSIGNED_BYTE , image . get ( ) ) ;
2012-03-05 20:16:26 +00:00
}
2012-01-08 21:59:42 +00:00
2015-10-17 15:05:15 +00:00
glBindFramebuffer ( GL_READ_FRAMEBUFFER , 0 ) ;
2011-12-07 22:05:46 +00:00
}
2016-08-20 12:05:53 +00:00
int compression = theApp . GetConfigI ( " png_compression_level " ) ;
2019-02-18 18:48:08 +00:00
return GSPng : : Save ( fmt , fn , image . get ( ) , m_committed_size . x , m_committed_size . y , pitch , compression ) ;
2011-11-16 22:17:37 +00:00
}
2015-07-10 19:11:14 +00:00
uint32 GSTextureOGL : : GetMemUsage ( )
{
2016-07-07 19:33:59 +00:00
return m_mem_usage ;
2015-07-10 19:11:14 +00:00
}