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
|
|
|
|
|
2015-05-15 16:32:47 +00:00
|
|
|
// FIXME find the optimal number of PBO
|
|
|
|
#define PBO_POOL_SIZE 8
|
|
|
|
|
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
|
|
|
|
2013-08-17 09:05:41 +00:00
|
|
|
GLuint m_pool[PBO_POOL_SIZE];
|
2016-01-13 23:42:56 +00:00
|
|
|
uptr m_offset[PBO_POOL_SIZE];
|
2013-08-17 09:05:41 +00:00
|
|
|
char* m_map[PBO_POOL_SIZE];
|
|
|
|
uint32 m_current_pbo = 0;
|
|
|
|
uint32 m_size;
|
2015-04-22 07:33:41 +00:00
|
|
|
bool m_texture_storage;
|
2015-05-15 16:32:47 +00:00
|
|
|
GLsync m_fence[PBO_POOL_SIZE];
|
2015-12-28 20:13:29 +00:00
|
|
|
const uint32 m_pbo_size = 8*1024*1024;
|
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
|
|
|
|
2015-05-14 18:05:49 +00:00
|
|
|
// Perf impact (test was only done on a gs dump):
|
|
|
|
// Normal (fast): Message:Buffer detailed info: Buffer object 9 (bound to
|
|
|
|
// GL_PIXEL_UNPACK_BUFFER_ARB, usage hint is GL_STREAM_COPY) will use VIDEO
|
|
|
|
// memory as the source for buffer object operations.
|
|
|
|
//
|
|
|
|
// Persistent (slower): Message:Buffer detailed info: Buffer object 8
|
|
|
|
// (bound to GL_PIXEL_UNPACK_BUFFER_ARB, usage hint is GL_DYNAMIC_DRAW)
|
|
|
|
// will use DMA CACHED memory as the source for buffer object operations
|
2013-08-03 08:29:01 +00:00
|
|
|
void Init() {
|
2015-10-17 15:05:15 +00:00
|
|
|
glGenBuffers(countof(m_pool), m_pool);
|
2015-05-16 13:22:20 +00:00
|
|
|
m_texture_storage = GLLoader::found_GL_ARB_buffer_storage;
|
2013-08-17 09:05:41 +00:00
|
|
|
|
|
|
|
for (size_t i = 0; i < countof(m_pool); i++) {
|
|
|
|
BindPbo();
|
|
|
|
|
2015-04-22 07:33:41 +00:00
|
|
|
if (m_texture_storage) {
|
2015-10-17 15:05:15 +00:00
|
|
|
glBufferStorage(GL_PIXEL_UNPACK_BUFFER, m_pbo_size, NULL, create_flags);
|
|
|
|
m_map[m_current_pbo] = (char*)glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, m_pbo_size, map_flags);
|
2015-05-15 16:32:47 +00:00
|
|
|
m_fence[m_current_pbo] = 0;
|
2013-08-17 09:05:41 +00:00
|
|
|
} else {
|
2015-10-17 15:05:15 +00:00
|
|
|
glBufferData(GL_PIXEL_UNPACK_BUFFER, m_pbo_size, NULL, GL_STREAM_COPY);
|
2014-03-23 14:42:05 +00:00
|
|
|
m_map[m_current_pbo] = NULL;
|
2013-08-17 09:05:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
NextPbo();
|
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;
|
2013-08-17 09:05:41 +00:00
|
|
|
m_size = size;
|
|
|
|
|
2014-11-01 19:17:08 +00:00
|
|
|
if (m_size > m_pbo_size) {
|
2013-08-17 09:05:41 +00:00
|
|
|
fprintf(stderr, "BUG: PBO too small %d but need %d\n", m_pbo_size, m_size);
|
|
|
|
}
|
|
|
|
|
2015-04-22 07:33:41 +00:00
|
|
|
if (m_texture_storage) {
|
2013-08-17 09:05:41 +00:00
|
|
|
if (m_offset[m_current_pbo] + m_size >= m_pbo_size) {
|
2015-05-15 16:32:47 +00:00
|
|
|
//NextPbo(); // For test purpose
|
|
|
|
NextPboWithSync();
|
2013-08-17 09:05:41 +00:00
|
|
|
}
|
|
|
|
|
2014-11-01 19:17:08 +00:00
|
|
|
// Note: texsubimage will access currently bound buffer
|
2013-08-17 09:05:41 +00:00
|
|
|
// Pbo ready let's get a pointer
|
|
|
|
BindPbo();
|
|
|
|
|
2014-11-01 19:17:08 +00:00
|
|
|
map = m_map[m_current_pbo] + m_offset[m_current_pbo];
|
2013-08-17 09:05:41 +00:00
|
|
|
|
2014-03-23 14:42:05 +00:00
|
|
|
} else {
|
2014-11-01 19:17:08 +00:00
|
|
|
GLbitfield flags = GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_INVALIDATE_RANGE_BIT;
|
|
|
|
|
2013-08-17 09:05:41 +00:00
|
|
|
if (m_offset[m_current_pbo] + m_size >= m_pbo_size) {
|
|
|
|
NextPbo();
|
|
|
|
|
2014-11-01 19:17:08 +00:00
|
|
|
flags &= ~GL_MAP_INVALIDATE_RANGE_BIT;
|
|
|
|
flags |= GL_MAP_INVALIDATE_BUFFER_BIT;
|
2013-08-17 09:05:41 +00:00
|
|
|
}
|
|
|
|
|
2013-10-24 20:54:27 +00:00
|
|
|
// Pbo ready let's get a pointer
|
|
|
|
BindPbo();
|
|
|
|
|
2014-11-01 19:17:08 +00:00
|
|
|
// Be sure the map is aligned
|
2015-10-17 15:05:15 +00:00
|
|
|
map = (char*)glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, m_offset[m_current_pbo], m_size, flags);
|
2013-08-17 09:05:41 +00:00
|
|
|
}
|
2014-03-23 14:42:05 +00:00
|
|
|
|
|
|
|
return map;
|
2013-08-17 09:05:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Unmap() {
|
2015-04-22 07:33:41 +00:00
|
|
|
if (m_texture_storage) {
|
2015-10-17 15:05:15 +00:00
|
|
|
glFlushMappedBufferRange(GL_PIXEL_UNPACK_BUFFER, m_offset[m_current_pbo], m_size);
|
2013-08-17 09:05:41 +00:00
|
|
|
} else {
|
2015-10-17 15:05:15 +00:00
|
|
|
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
|
2013-08-17 09:05:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-13 23:42:56 +00:00
|
|
|
uptr Offset() {
|
2013-08-17 09:05:41 +00:00
|
|
|
return m_offset[m_current_pbo];
|
|
|
|
}
|
|
|
|
|
2013-08-03 08:29:01 +00:00
|
|
|
void Destroy() {
|
2015-05-15 16:32:47 +00:00
|
|
|
if (m_texture_storage) {
|
|
|
|
for (size_t i = 0; i < countof(m_pool); i++) {
|
|
|
|
m_map[i] = NULL;
|
|
|
|
m_offset[i] = 0;
|
2015-10-17 15:05:15 +00:00
|
|
|
glDeleteSync(m_fence[i]);
|
2015-05-15 16:32:47 +00:00
|
|
|
|
|
|
|
// Don't know if we must do it
|
2015-10-17 15:05:15 +00:00
|
|
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, m_pool[i]);
|
|
|
|
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
|
2015-05-15 16:32:47 +00:00
|
|
|
}
|
2015-10-17 15:05:15 +00:00
|
|
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
2015-05-15 16:32:47 +00:00
|
|
|
}
|
2015-10-17 15:05:15 +00:00
|
|
|
glDeleteBuffers(countof(m_pool), m_pool);
|
2013-08-03 08:29:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void BindPbo() {
|
2015-10-17 15:05:15 +00:00
|
|
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, m_pool[m_current_pbo]);
|
2013-08-17 09:05:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void NextPbo() {
|
|
|
|
m_current_pbo = (m_current_pbo + 1) & (countof(m_pool)-1);
|
2014-11-01 19:17:08 +00:00
|
|
|
// Mark new PBO as free
|
|
|
|
m_offset[m_current_pbo] = 0;
|
2013-08-03 08:29:01 +00:00
|
|
|
}
|
|
|
|
|
2015-05-15 16:32:47 +00:00
|
|
|
void NextPboWithSync() {
|
2015-10-17 15:05:15 +00:00
|
|
|
m_fence[m_current_pbo] = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
2015-05-15 16:32:47 +00:00
|
|
|
NextPbo();
|
|
|
|
if (m_fence[m_current_pbo]) {
|
|
|
|
#ifdef ENABLE_OGL_DEBUG_FENCE
|
2015-10-17 15:05:15 +00:00
|
|
|
GLenum status = glClientWaitSync(m_fence[m_current_pbo], GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED);
|
2015-05-15 16:32:47 +00:00
|
|
|
#else
|
2015-10-17 15:05:15 +00:00
|
|
|
glClientWaitSync(m_fence[m_current_pbo], GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED);
|
2015-05-15 16:32:47 +00:00
|
|
|
#endif
|
2015-10-17 15:05:15 +00:00
|
|
|
glDeleteSync(m_fence[m_current_pbo]);
|
2015-05-15 16:32:47 +00:00
|
|
|
m_fence[m_current_pbo] = 0;
|
|
|
|
|
|
|
|
#ifdef ENABLE_OGL_DEBUG_FENCE
|
|
|
|
if (status != GL_ALREADY_SIGNALED) {
|
|
|
|
fprintf(stderr, "GL_PIXEL_UNPACK_BUFFER: Sync Sync! Buffer too small\n");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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() {
|
|
|
|
// Note: keep offset aligned for SSE/AVX
|
2014-03-23 14:42:05 +00:00
|
|
|
m_offset[m_current_pbo] = (m_offset[m_current_pbo] + m_size + 63) & ~0x3F;
|
2013-08-17 09:05:41 +00:00
|
|
|
}
|
2013-08-03 08:29:01 +00:00
|
|
|
}
|
|
|
|
|
2013-05-25 14:28:16 +00:00
|
|
|
// FIXME: check if it possible to always use those setup by default
|
|
|
|
// glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
|
|
|
// glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
|
|
|
|
2013-07-19 19:25:50 +00:00
|
|
|
GSTextureOGL::GSTextureOGL(int type, int w, int h, int format, GLuint fbo_read)
|
2016-03-18 19:20:23 +00:00
|
|
|
: m_pbo_size(0), m_dirty(false), m_clean(false), m_local_buffer(NULL), m_r_x(0), m_r_y(0), m_r_w(0), m_r_h(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
|
2012-03-05 20:16:26 +00:00
|
|
|
m_size.x = max(1,w);
|
|
|
|
m_size.y = 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;
|
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_alignment = 4;
|
|
|
|
m_int_shift = 2;
|
|
|
|
break;
|
|
|
|
case GL_R16UI:
|
|
|
|
m_int_format = GL_RED_INTEGER;
|
|
|
|
m_int_type = GL_UNSIGNED_SHORT;
|
|
|
|
m_int_alignment = 2;
|
|
|
|
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_alignment = 1;
|
|
|
|
m_int_shift = 0;
|
|
|
|
break;
|
|
|
|
|
|
|
|
// 4 channel normalized
|
|
|
|
case GL_RGBA16:
|
|
|
|
m_int_format = GL_RGBA;
|
|
|
|
m_int_type = GL_UNSIGNED_SHORT;
|
|
|
|
m_int_alignment = 8;
|
|
|
|
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_alignment = 4;
|
|
|
|
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_alignment = 8;
|
|
|
|
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_alignment = 16;
|
|
|
|
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_alignment = 8;
|
|
|
|
m_int_shift = 3;
|
|
|
|
break;
|
|
|
|
|
|
|
|
// Special
|
2013-08-03 08:29:01 +00:00
|
|
|
case 0:
|
|
|
|
case GL_DEPTH32F_STENCIL8:
|
2015-05-16 10:35:45 +00:00
|
|
|
// Backbuffer & dss aren't important
|
2013-08-03 08:29:01 +00:00
|
|
|
m_int_format = 0;
|
|
|
|
m_int_type = 0;
|
|
|
|
m_int_alignment = 0;
|
|
|
|
m_int_shift = 0;
|
|
|
|
break;
|
2013-08-02 16:38:12 +00:00
|
|
|
default:
|
|
|
|
ASSERT(0);
|
|
|
|
}
|
|
|
|
|
2015-05-01 18:04:23 +00:00
|
|
|
// Generate & Allocate the buffer
|
2011-11-21 22:36:03 +00:00
|
|
|
switch (m_type) {
|
|
|
|
case GSTexture::Offscreen:
|
2015-05-18 09:29:04 +00:00
|
|
|
// 8B is the worst case for depth/stencil
|
2015-07-10 19:11:14 +00:00
|
|
|
// FIXME I think it is only used for color. So you can save half of the size
|
|
|
|
m_local_buffer = (uint8*)_aligned_malloc(m_size.x * m_size.y * 4, 32);
|
2011-12-23 12:32:40 +00:00
|
|
|
case GSTexture::Texture:
|
|
|
|
case GSTexture::RenderTarget:
|
2011-12-30 13:55:33 +00:00
|
|
|
case GSTexture::DepthStencil:
|
2015-10-17 15:05:15 +00:00
|
|
|
glCreateTextures(GL_TEXTURE_2D, 1, &m_texture_id);
|
|
|
|
glTextureStorage2D(m_texture_id, 1+GL_TEX_LEVEL_0, m_format, m_size.x, m_size.y);
|
2015-06-14 08:02:30 +00:00
|
|
|
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.
|
2015-10-17 15:05:15 +00:00
|
|
|
glTextureParameteri(m_texture_id, GL_TEXTURE_SWIZZLE_A, GL_RED);
|
2015-06-14 08:02:30 +00:00
|
|
|
}
|
2011-12-23 12:32:40 +00:00
|
|
|
break;
|
2011-12-22 14:12:12 +00:00
|
|
|
case GSTexture::Backbuffer:
|
2011-12-07 22:05:46 +00:00
|
|
|
default:
|
|
|
|
break;
|
2011-11-21 22:36:03 +00:00
|
|
|
}
|
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
|
|
|
|
|
|
|
if (m_local_buffer)
|
|
|
|
_aligned_free(m_local_buffer);
|
2011-11-16 22:17:37 +00:00
|
|
|
}
|
|
|
|
|
2015-04-30 17:38:30 +00:00
|
|
|
void GSTextureOGL::Invalidate()
|
|
|
|
{
|
2015-10-17 15:05:15 +00:00
|
|
|
if (m_dirty && glInvalidateTexImage) {
|
|
|
|
glInvalidateTexImage(m_texture_id, GL_TEX_LEVEL_0);
|
2015-05-12 16:03:06 +00:00
|
|
|
m_dirty = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-11-16 22:17:37 +00:00
|
|
|
bool GSTextureOGL::Update(const GSVector4i& r, const void* data, int pitch)
|
|
|
|
{
|
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
|
|
|
|
|
|
|
// 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-04-30 17:38:30 +00:00
|
|
|
m_dirty = true;
|
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
|
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, m_int_alignment);
|
|
|
|
|
|
|
|
#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();
|
|
|
|
|
2015-10-17 15:05:15 +00:00
|
|
|
glTextureSubImage2D(m_texture_id, GL_TEX_LEVEL_0, 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
|
|
|
|
2015-05-06 16:52:51 +00:00
|
|
|
GL_POP();
|
2011-12-07 22:05:46 +00:00
|
|
|
|
2011-12-30 22:25:17 +00:00
|
|
|
return true;
|
2011-11-16 22:17:37 +00:00
|
|
|
}
|
|
|
|
|
2016-03-21 17:52:26 +00:00
|
|
|
bool GSTextureOGL::Map(GSMap& m, const GSVector4i* _r)
|
2011-11-16 22:17:37 +00:00
|
|
|
{
|
2016-03-21 17:52:26 +00:00
|
|
|
GSVector4i r = _r ? *_r : GSVector4i(0, 0, m_size.x, m_size.y);
|
2013-07-19 19:25:50 +00:00
|
|
|
|
2016-03-18 19:20:23 +00:00
|
|
|
// LOTS OF CRAP CODE!!!! PLEASE FIX ME !!!
|
|
|
|
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-03-18 19:20:23 +00:00
|
|
|
glGetTextureImage(m_texture_id, GL_TEX_LEVEL_0, m_int_format, m_int_type, 1024*1024*16, 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-03-18 19:20:23 +00:00
|
|
|
glPixelStorei(GL_PACK_ALIGNMENT, m_int_alignment);
|
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;
|
|
|
|
m.pitch = m_size.x << m_int_shift;
|
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) {
|
|
|
|
GL_PUSH("Upload Texture %d", m_texture_id); // POP is in Unmap
|
|
|
|
|
|
|
|
m_dirty = true;
|
|
|
|
m_clean = false;
|
|
|
|
|
2016-03-21 17:52:26 +00:00
|
|
|
uint32 row_byte = r.width() << m_int_shift;
|
|
|
|
uint32 map_size = r.height() * row_byte;
|
2016-03-18 19:20:23 +00:00
|
|
|
|
|
|
|
m.bits = (uint8*)PboPool::Map(map_size);
|
|
|
|
m.pitch = row_byte;
|
|
|
|
|
|
|
|
#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-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();
|
|
|
|
|
|
|
|
glTextureSubImage2D(m_texture_id, GL_TEX_LEVEL_0, m_r_x, m_r_y, m_r_w, m_r_h, m_int_format, m_int_type, (const void*)PboPool::Offset());
|
|
|
|
|
|
|
|
// FIXME OGL4: investigate, only 1 unpack buffer always bound
|
|
|
|
PboPool::UnbindPbo();
|
|
|
|
|
|
|
|
PboPool::EndTransfer();
|
|
|
|
|
|
|
|
GL_POP(); // PUSH is in Map
|
|
|
|
}
|
2011-11-16 22:17:37 +00:00
|
|
|
}
|
|
|
|
|
2016-02-24 21:52:17 +00:00
|
|
|
bool GSTextureOGL::Save(const string& fn, bool user_image, bool dds)
|
2011-11-16 22:17:37 +00:00
|
|
|
{
|
2011-12-07 22:05:46 +00:00
|
|
|
// Collect the texture data
|
2011-12-29 14:24:26 +00:00
|
|
|
uint32 pitch = 4 * m_size.x;
|
2015-04-24 17:34:17 +00:00
|
|
|
uint32 buf_size = pitch * m_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()) {
|
2016-02-21 09:59:49 +00:00
|
|
|
glReadPixels(0, 0, m_size.x, m_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);
|
2016-02-21 09:59:49 +00:00
|
|
|
glReadPixels(0, 0, m_size.x, m_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) {
|
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) {
|
2016-02-21 09:59:49 +00:00
|
|
|
glReadPixels(0, 0, m_size.x, m_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)
|
|
|
|
{
|
2016-02-21 09:59:49 +00:00
|
|
|
glReadPixels(0, 0, m_size.x, m_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;
|
2016-02-21 09:59:49 +00:00
|
|
|
glReadPixels(0, 0, m_size.x, m_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-02-24 21:52:17 +00:00
|
|
|
int compression = user_image ? Z_BEST_COMPRESSION : theApp.GetConfig("png_compression_level", Z_BEST_SPEED);
|
|
|
|
return GSPng::Save(fmt, fn, image.get(), m_size.x, m_size.y, pitch, compression);
|
2011-11-16 22:17:37 +00:00
|
|
|
}
|
|
|
|
|
2015-07-10 19:11:14 +00:00
|
|
|
uint32 GSTextureOGL::GetMemUsage()
|
|
|
|
{
|
|
|
|
switch (m_type) {
|
|
|
|
case GSTexture::Offscreen:
|
|
|
|
return m_size.x * m_size.y * (4 + m_int_alignment);
|
|
|
|
case GSTexture::Texture:
|
|
|
|
case GSTexture::RenderTarget:
|
|
|
|
return m_size.x * m_size.y * m_int_alignment;
|
|
|
|
case GSTexture::DepthStencil:
|
|
|
|
return m_size.x * m_size.y * 8;
|
|
|
|
case GSTexture::Backbuffer:
|
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|