2017-11-18 15:42:39 +01:00
# pragma once
# include <thread>
2018-01-10 10:18:18 +01:00
# include <unordered_map>
2017-11-18 20:17:17 +01:00
# include <vector>
2018-01-16 14:16:56 +01:00
# include <functional>
2017-12-27 12:44:46 +01:00
# include <set>
2017-12-14 17:50:40 +01:00
# include <string>
2017-11-18 15:42:39 +01:00
# include <mutex>
2017-12-14 17:50:40 +01:00
# include <condition_variable>
2017-11-18 16:50:49 +01:00
# include <cassert>
2017-11-18 15:42:39 +01:00
# include "gfx/gl_common.h"
# include "math/dataconv.h"
# include "Common/Log.h"
# include "GLQueueRunner.h"
2017-11-19 00:23:52 +01:00
class GLRInputLayout ;
2017-12-27 12:44:46 +01:00
class GLPushBuffer ;
2017-11-19 00:23:52 +01:00
2017-11-18 15:42:39 +01:00
class GLRFramebuffer {
public :
2017-11-19 17:37:56 +01:00
GLRFramebuffer ( int _width , int _height , bool z_stencil )
: width ( _width ) , height ( _height ) , z_stencil_ ( z_stencil ) {
2017-11-18 15:42:39 +01:00
}
2017-12-13 01:05:33 +01:00
~ GLRFramebuffer ( ) ;
2017-11-18 15:42:39 +01:00
int numShadows = 1 ; // TODO: Support this.
2017-12-13 01:05:33 +01:00
GLuint handle = 0 ;
GLuint color_texture = 0 ;
GLuint z_stencil_buffer = 0 ; // Either this is set, or the two below.
GLuint z_buffer = 0 ;
GLuint stencil_buffer = 0 ;
int width ;
int height ;
GLuint colorDepth ;
2017-11-19 17:37:56 +01:00
bool z_stencil_ ;
2017-11-18 15:42:39 +01:00
} ;
// We need to create some custom heap-allocated types so we can forward things that need to be created on the GL thread, before
// they've actually been created.
class GLRShader {
public :
~ GLRShader ( ) {
if ( shader ) {
glDeleteShader ( shader ) ;
}
}
GLuint shader = 0 ;
2017-12-12 15:32:15 +01:00
bool valid = false ;
2017-11-18 15:42:39 +01:00
} ;
class GLRProgram {
public :
~ GLRProgram ( ) {
if ( program ) {
glDeleteProgram ( program ) ;
}
}
2017-11-18 16:50:49 +01:00
struct Semantic {
int location ;
const char * attrib ;
} ;
2017-11-19 00:43:58 +01:00
struct UniformLocQuery {
GLint * dest ;
const char * name ;
} ;
2017-11-19 12:25:57 +01:00
struct Initializer {
GLint * uniform ;
int type ;
int value ;
} ;
2017-11-18 15:42:39 +01:00
GLuint program = 0 ;
2017-11-18 16:50:49 +01:00
std : : vector < Semantic > semantics_ ;
2017-11-19 00:43:58 +01:00
std : : vector < UniformLocQuery > queries_ ;
2017-11-19 12:25:57 +01:00
std : : vector < Initializer > initialize_ ;
2017-11-18 20:17:17 +01:00
struct UniformInfo {
int loc_ ;
} ;
// Must ONLY be called from GLQueueRunner!
2017-12-14 17:50:40 +01:00
// Also it's pretty slow...
2017-11-18 20:17:17 +01:00
int GetUniformLoc ( const char * name ) {
2017-12-14 17:50:40 +01:00
auto iter = uniformCache_ . find ( std : : string ( name ) ) ;
2017-11-18 20:17:17 +01:00
int loc = - 1 ;
if ( iter ! = uniformCache_ . end ( ) ) {
loc = iter - > second . loc_ ;
} else {
loc = glGetUniformLocation ( program , name ) ;
UniformInfo info ;
info . loc_ = loc ;
uniformCache_ [ name ] = info ;
}
return loc ;
}
2018-01-10 10:18:18 +01:00
std : : unordered_map < std : : string , UniformInfo > uniformCache_ ;
2017-11-18 15:42:39 +01:00
} ;
class GLRTexture {
public :
~ GLRTexture ( ) {
if ( texture ) {
glDeleteTextures ( 1 , & texture ) ;
}
2018-01-16 17:32:50 +01:00
canary = 0xd31373d ; // deleted
2017-11-18 15:42:39 +01:00
}
2018-01-16 17:32:50 +01:00
enum {
CanaryValue = 0x12345678 ,
} ;
uint32_t canary = CanaryValue ;
2017-12-27 14:33:18 +01:00
GLuint texture = 0 ;
// Could also trust OpenGL defaults I guess..
GLenum target = 0xFFFF ;
GLenum wrapS = 0xFFFF ;
GLenum wrapT = 0xFFFF ;
GLenum magFilter = 0xFFFF ;
GLenum minFilter = 0xFFFF ;
float anisotropy = - 100000.0f ;
float minLod = - 100000.0f ;
float maxLod = 100000.0f ;
float lodBias = 0.0f ;
2017-11-18 15:42:39 +01:00
} ;
class GLRBuffer {
public :
2017-12-12 15:32:15 +01:00
GLRBuffer ( GLuint target , size_t size ) : target_ ( target ) , size_ ( ( int ) size ) { }
2017-11-18 15:42:39 +01:00
~ GLRBuffer ( ) {
2017-11-18 16:50:49 +01:00
if ( buffer ) {
glDeleteBuffers ( 1 , & buffer ) ;
2017-11-18 15:42:39 +01:00
}
}
2017-12-12 14:07:52 +01:00
GLuint buffer = 0 ;
2017-11-18 20:17:17 +01:00
GLuint target_ ;
2017-12-12 15:32:15 +01:00
int size_ ;
2017-11-18 20:17:17 +01:00
private :
2017-11-18 15:42:39 +01:00
} ;
enum class GLRRunType {
END ,
SYNC ,
} ;
class GLDeleter {
public :
void Perform ( ) ;
void Take ( GLDeleter & other ) {
2018-01-16 17:32:50 +01:00
deleterMutex_ . lock ( ) ;
_assert_msg_ ( G3D , shaders . empty ( ) & & programs . empty ( ) & & buffers . empty ( ) & & textures . empty ( ) & & inputLayouts . empty ( ) & & framebuffers . empty ( ) , " Deleter already has stuff " ) ;
2017-11-18 15:42:39 +01:00
shaders = std : : move ( other . shaders ) ;
programs = std : : move ( other . programs ) ;
2017-11-18 21:12:11 +01:00
buffers = std : : move ( other . buffers ) ;
textures = std : : move ( other . textures ) ;
2017-11-19 00:23:52 +01:00
inputLayouts = std : : move ( other . inputLayouts ) ;
2017-12-13 01:05:33 +01:00
framebuffers = std : : move ( other . framebuffers ) ;
other . shaders . clear ( ) ;
other . programs . clear ( ) ;
other . buffers . clear ( ) ;
other . textures . clear ( ) ;
other . inputLayouts . clear ( ) ;
other . framebuffers . clear ( ) ;
2018-01-16 17:32:50 +01:00
deleterMutex_ . unlock ( ) ;
2017-11-18 15:42:39 +01:00
}
std : : vector < GLRShader * > shaders ;
std : : vector < GLRProgram * > programs ;
2017-11-18 20:17:17 +01:00
std : : vector < GLRBuffer * > buffers ;
std : : vector < GLRTexture * > textures ;
2017-11-19 00:23:52 +01:00
std : : vector < GLRInputLayout * > inputLayouts ;
2017-12-13 01:05:33 +01:00
std : : vector < GLRFramebuffer * > framebuffers ;
2018-01-16 17:32:50 +01:00
std : : mutex deleterMutex_ ;
2017-11-18 15:42:39 +01:00
} ;
2017-11-18 21:12:11 +01:00
class GLRInputLayout {
public :
struct Entry {
int location ;
int count ;
GLenum type ;
GLboolean normalized ;
int stride ;
intptr_t offset ;
} ;
std : : vector < Entry > entries ;
int semanticsMask_ = 0 ;
} ;
2017-11-18 15:42:39 +01:00
class GLRenderManager {
public :
GLRenderManager ( ) ;
~ GLRenderManager ( ) ;
2018-01-16 18:13:31 +01:00
void ThreadStart ( ) ;
void ThreadEnd ( ) ;
bool ThreadFrame ( ) ; // Returns false to request exiting the loop.
2017-11-18 15:42:39 +01:00
// Makes sure that the GPU has caught up enough that we can start writing buffers of this frame again.
void BeginFrame ( ) ;
// Can run on a different thread!
void Finish ( ) ;
void Run ( int frame ) ;
// Zaps queued up commands. Use if you know there's a risk you've queued up stuff that has already been deleted. Can happen during in-game shutdown.
void Wipe ( ) ;
// Creation commands. These were not needed in Vulkan since there we can do that on the main thread.
2017-11-19 17:37:56 +01:00
GLRTexture * CreateTexture ( GLenum target ) {
2017-11-18 15:42:39 +01:00
GLRInitStep step { GLRInitStepType : : CREATE_TEXTURE } ;
step . create_texture . texture = new GLRTexture ( ) ;
2017-11-18 20:17:17 +01:00
step . create_texture . texture - > target = target ;
2017-11-18 15:42:39 +01:00
initSteps_ . push_back ( step ) ;
return step . create_texture . texture ;
}
2017-11-19 00:43:58 +01:00
GLRBuffer * CreateBuffer ( GLuint target , size_t size , GLuint usage ) {
2017-11-18 20:17:17 +01:00
GLRInitStep step { GLRInitStepType : : CREATE_BUFFER } ;
2017-12-12 15:32:15 +01:00
step . create_buffer . buffer = new GLRBuffer ( target , size ) ;
2017-11-19 00:43:58 +01:00
step . create_buffer . size = ( int ) size ;
2017-11-19 00:23:52 +01:00
step . create_buffer . usage = usage ;
2017-11-18 20:17:17 +01:00
initSteps_ . push_back ( step ) ;
return step . create_buffer . buffer ;
}
2018-01-10 10:48:46 +01:00
GLRShader * CreateShader ( GLuint stage , std : : string code , std : : string desc ) {
2017-11-18 15:42:39 +01:00
GLRInitStep step { GLRInitStepType : : CREATE_SHADER } ;
step . create_shader . shader = new GLRShader ( ) ;
2017-11-18 16:50:49 +01:00
step . create_shader . stage = stage ;
step . create_shader . code = new char [ code . size ( ) + 1 ] ;
memcpy ( step . create_shader . code , code . data ( ) , code . size ( ) + 1 ) ;
2018-01-10 10:48:46 +01:00
if ( ! desc . empty ( ) ) {
step . create_shader . desc = desc . size ( ) ? new char [ desc . size ( ) + 1 ] : nullptr ;
memcpy ( step . create_shader . desc , desc . data ( ) , desc . size ( ) + 1 ) ;
}
2017-11-18 15:42:39 +01:00
initSteps_ . push_back ( step ) ;
return step . create_shader . shader ;
}
2017-11-19 17:37:56 +01:00
GLRFramebuffer * CreateFramebuffer ( int width , int height , bool z_stencil ) {
GLRInitStep step { GLRInitStepType : : CREATE_FRAMEBUFFER } ;
step . create_framebuffer . framebuffer = new GLRFramebuffer ( width , height , z_stencil ) ;
initSteps_ . push_back ( step ) ;
return step . create_framebuffer . framebuffer ;
}
2017-12-12 14:07:52 +01:00
// Can't replace uniform initializers with direct calls to SetUniform() etc because there might
// not be an active render pass.
2017-11-19 12:25:57 +01:00
GLRProgram * CreateProgram (
std : : vector < GLRShader * > shaders , std : : vector < GLRProgram : : Semantic > semantics , std : : vector < GLRProgram : : UniformLocQuery > queries ,
std : : vector < GLRProgram : : Initializer > initalizers , bool supportDualSource ) {
2017-11-18 15:42:39 +01:00
GLRInitStep step { GLRInitStepType : : CREATE_PROGRAM } ;
2017-11-18 16:50:49 +01:00
assert ( shaders . size ( ) < = ARRAY_SIZE ( step . create_program . shaders ) ) ;
2017-11-18 15:42:39 +01:00
step . create_program . program = new GLRProgram ( ) ;
2017-11-18 16:50:49 +01:00
step . create_program . program - > semantics_ = semantics ;
2017-11-19 00:43:58 +01:00
step . create_program . program - > queries_ = queries ;
2017-11-19 12:25:57 +01:00
step . create_program . program - > initialize_ = initalizers ;
2017-12-14 17:50:40 +01:00
step . create_program . support_dual_source = supportDualSource ;
2017-12-12 15:32:15 +01:00
_assert_msg_ ( G3D , shaders . size ( ) > 0 , " Can't create a program with zero shaders " ) ;
2017-11-18 16:50:49 +01:00
for ( int i = 0 ; i < shaders . size ( ) ; i + + ) {
step . create_program . shaders [ i ] = shaders [ i ] ;
}
2017-11-19 00:43:58 +01:00
# ifdef _DEBUG
for ( auto & iter : queries ) {
assert ( iter . name ) ;
}
for ( auto & sem : semantics ) {
assert ( sem . attrib ) ;
}
# endif
2017-11-18 21:51:17 +01:00
step . create_program . num_shaders = ( int ) shaders . size ( ) ;
2017-11-18 15:42:39 +01:00
initSteps_ . push_back ( step ) ;
return step . create_program . program ;
}
2017-11-18 21:12:11 +01:00
GLRInputLayout * CreateInputLayout ( std : : vector < GLRInputLayout : : Entry > & entries ) {
2017-11-18 21:51:17 +01:00
GLRInitStep step { GLRInitStepType : : CREATE_INPUT_LAYOUT } ;
2017-11-18 21:12:11 +01:00
step . create_input_layout . inputLayout = new GLRInputLayout ( ) ;
2017-11-18 21:51:17 +01:00
step . create_input_layout . inputLayout - > entries = entries ;
for ( auto & iter : step . create_input_layout . inputLayout - > entries ) {
2017-11-18 21:12:11 +01:00
step . create_input_layout . inputLayout - > semanticsMask_ | = 1 < < iter . location ;
}
initSteps_ . push_back ( step ) ;
return step . create_input_layout . inputLayout ;
}
2017-11-18 16:50:49 +01:00
void DeleteShader ( GLRShader * shader ) {
deleter_ . shaders . push_back ( shader ) ;
}
void DeleteProgram ( GLRProgram * program ) {
deleter_ . programs . push_back ( program ) ;
}
2017-11-18 20:17:17 +01:00
void DeleteBuffer ( GLRBuffer * buffer ) {
deleter_ . buffers . push_back ( buffer ) ;
}
void DeleteTexture ( GLRTexture * texture ) {
deleter_ . textures . push_back ( texture ) ;
}
2017-11-18 21:12:11 +01:00
void DeleteInputLayout ( GLRInputLayout * inputLayout ) {
2017-11-19 00:23:52 +01:00
deleter_ . inputLayouts . push_back ( inputLayout ) ;
2017-11-18 21:12:11 +01:00
}
2017-12-13 01:05:33 +01:00
void DeleteFramebuffer ( GLRFramebuffer * framebuffer ) {
deleter_ . framebuffers . push_back ( framebuffer ) ;
}
2017-11-18 16:50:49 +01:00
2018-01-18 21:34:52 -08:00
void BindFramebufferAsRenderTarget ( GLRFramebuffer * fb , GLRRenderPassAction color , GLRRenderPassAction depth , GLRRenderPassAction stencil , uint32_t clearColor , float clearDepth , uint8_t clearStencil ) ;
2017-11-18 22:15:12 +01:00
void BindFramebufferAsTexture ( GLRFramebuffer * fb , int binding , int aspectBit , int attachment ) ;
2017-11-18 15:42:39 +01:00
bool CopyFramebufferToMemorySync ( GLRFramebuffer * src , int aspectBits , int x , int y , int w , int h , Draw : : DataFormat destFormat , uint8_t * pixels , int pixelStride ) ;
void CopyImageToMemorySync ( GLuint texture , int mipLevel , int x , int y , int w , int h , Draw : : DataFormat destFormat , uint8_t * pixels , int pixelStride ) ;
void CopyFramebuffer ( GLRFramebuffer * src , GLRect2D srcRect , GLRFramebuffer * dst , GLOffset2D dstPos , int aspectMask ) ;
void BlitFramebuffer ( GLRFramebuffer * src , GLRect2D srcRect , GLRFramebuffer * dst , GLRect2D dstRect , int aspectMask , bool filter ) ;
2017-11-19 00:23:52 +01:00
// Takes ownership of data if deleteData = true.
2017-12-14 17:50:40 +01:00
void BufferSubdata ( GLRBuffer * buffer , size_t offset , size_t size , uint8_t * data , bool deleteData = true ) {
2017-11-18 20:17:17 +01:00
// TODO: Maybe should be a render command instead of an init command? When possible it's better as
// an init command, that's for sure.
GLRInitStep step { GLRInitStepType : : BUFFER_SUBDATA } ;
2017-12-12 15:32:15 +01:00
_dbg_assert_ ( G3D , offset > = 0 ) ;
_dbg_assert_ ( G3D , offset < = buffer - > size_ - size ) ;
2017-11-18 20:17:17 +01:00
step . buffer_subdata . buffer = buffer ;
2017-12-14 17:50:40 +01:00
step . buffer_subdata . offset = ( int ) offset ;
step . buffer_subdata . size = ( int ) size ;
2017-11-18 20:17:17 +01:00
step . buffer_subdata . data = data ;
2017-11-19 00:23:52 +01:00
step . buffer_subdata . deleteData = deleteData ;
2017-11-18 20:17:17 +01:00
initSteps_ . push_back ( step ) ;
}
2017-11-19 18:22:46 +01:00
// Takes ownership over the data pointer and delete[]-s it.
2017-11-19 17:37:56 +01:00
void TextureImage ( GLRTexture * texture , int level , int width , int height , GLenum internalFormat , GLenum format , GLenum type , uint8_t * data , bool linearFilter = false ) {
2017-11-18 20:17:17 +01:00
GLRInitStep step { GLRInitStepType : : TEXTURE_IMAGE } ;
2017-11-18 21:51:17 +01:00
step . texture_image . texture = texture ;
2017-11-18 20:17:17 +01:00
step . texture_image . data = data ;
step . texture_image . internalFormat = internalFormat ;
step . texture_image . format = format ;
step . texture_image . type = type ;
step . texture_image . level = level ;
step . texture_image . width = width ;
step . texture_image . height = height ;
2017-11-19 17:37:56 +01:00
step . texture_image . linearFilter = linearFilter ;
2017-11-18 20:17:17 +01:00
initSteps_ . push_back ( step ) ;
}
2017-12-14 17:08:45 +01:00
void FinalizeTexture ( GLRTexture * texture , int maxLevels , bool genMips ) {
2017-12-14 16:54:21 +01:00
GLRInitStep step { GLRInitStepType : : TEXTURE_FINALIZE } ;
step . texture_finalize . texture = texture ;
step . texture_finalize . maxLevel = maxLevels ;
2017-12-14 17:08:45 +01:00
step . texture_finalize . genMips = genMips ;
2017-12-14 16:54:21 +01:00
initSteps_ . push_back ( step ) ;
}
2017-11-18 20:17:17 +01:00
void BindTexture ( int slot , GLRTexture * tex ) {
_dbg_assert_ ( G3D , curRenderStep_ & & curRenderStep_ - > stepType = = GLRStepType : : RENDER ) ;
GLRRenderData data { GLRRenderCommand : : BINDTEXTURE } ;
2017-12-12 15:32:15 +01:00
_dbg_assert_ ( G3D , slot < 16 ) ;
2017-11-18 20:17:17 +01:00
data . texture . slot = slot ;
data . texture . texture = tex ;
curRenderStep_ - > commands . push_back ( data ) ;
}
2017-11-18 16:50:49 +01:00
void BindProgram ( GLRProgram * program ) {
_dbg_assert_ ( G3D , curRenderStep_ & & curRenderStep_ - > stepType = = GLRStepType : : RENDER ) ;
GLRRenderData data { GLRRenderCommand : : BINDPROGRAM } ;
2017-12-14 14:53:27 +01:00
_dbg_assert_ ( G3D , program ! = nullptr ) ;
2017-11-18 16:50:49 +01:00
data . program . program = program ;
curRenderStep_ - > commands . push_back ( data ) ;
}
2017-12-14 14:53:27 +01:00
void BindPixelPackBuffer ( GLRBuffer * buffer ) { // Want to support an offset but can't in ES 2.0. We supply an offset when binding the buffers instead.
_dbg_assert_ ( G3D , curRenderStep_ & & curRenderStep_ - > stepType = = GLRStepType : : RENDER ) ;
GLRRenderData data { GLRRenderCommand : : BIND_BUFFER } ;
data . bind_buffer . buffer = buffer ;
data . bind_buffer . target = GL_PIXEL_PACK_BUFFER ;
2017-11-19 00:23:52 +01:00
curRenderStep_ - > commands . push_back ( data ) ;
}
2017-11-19 12:25:57 +01:00
void BindIndexBuffer ( GLRBuffer * buffer ) { // Want to support an offset but can't in ES 2.0. We supply an offset when binding the buffers instead.
_dbg_assert_ ( G3D , curRenderStep_ & & curRenderStep_ - > stepType = = GLRStepType : : RENDER ) ;
2017-12-14 14:53:27 +01:00
GLRRenderData data { GLRRenderCommand : : BIND_BUFFER } ;
2017-11-19 12:25:57 +01:00
data . bind_buffer . buffer = buffer ;
2017-12-14 14:53:27 +01:00
data . bind_buffer . target = GL_ELEMENT_ARRAY_BUFFER ;
2017-11-19 12:25:57 +01:00
curRenderStep_ - > commands . push_back ( data ) ;
}
2017-12-19 12:25:13 +01:00
void BindVertexBuffer ( GLRInputLayout * inputLayout , GLRBuffer * buffer , size_t offset ) {
2017-11-18 21:12:11 +01:00
_dbg_assert_ ( G3D , curRenderStep_ & & curRenderStep_ - > stepType = = GLRStepType : : RENDER ) ;
2017-11-19 00:23:52 +01:00
assert ( inputLayout ) ;
2017-12-19 12:25:13 +01:00
GLRRenderData data { GLRRenderCommand : : BIND_VERTEX_BUFFER } ;
data . bindVertexBuffer . inputLayout = inputLayout ;
data . bindVertexBuffer . offset = offset ;
data . bindVertexBuffer . buffer = buffer ;
2017-11-18 21:12:11 +01:00
curRenderStep_ - > commands . push_back ( data ) ;
}
2017-11-18 16:50:49 +01:00
void SetDepth ( bool enabled , bool write , GLenum func ) {
_dbg_assert_ ( G3D , curRenderStep_ & & curRenderStep_ - > stepType = = GLRStepType : : RENDER ) ;
GLRRenderData data { GLRRenderCommand : : DEPTH } ;
data . depth . enabled = enabled ;
data . depth . write = write ;
data . depth . func = func ;
curRenderStep_ - > commands . push_back ( data ) ;
}
2017-11-18 15:42:39 +01:00
void SetViewport ( const GLRViewport & vp ) {
_dbg_assert_ ( G3D , curRenderStep_ & & curRenderStep_ - > stepType = = GLRStepType : : RENDER ) ;
GLRRenderData data { GLRRenderCommand : : VIEWPORT } ;
data . viewport . vp = vp ;
curRenderStep_ - > commands . push_back ( data ) ;
}
void SetScissor ( const GLRect2D & rc ) {
_dbg_assert_ ( G3D , curRenderStep_ & & curRenderStep_ - > stepType = = GLRStepType : : RENDER ) ;
GLRRenderData data { GLRRenderCommand : : SCISSOR } ;
data . scissor . rc = rc ;
curRenderStep_ - > commands . push_back ( data ) ;
}
2017-11-19 00:43:58 +01:00
void SetUniformI ( GLint * loc , int count , const int * udata ) {
_dbg_assert_ ( G3D , curRenderStep_ & & curRenderStep_ - > stepType = = GLRStepType : : RENDER ) ;
GLRRenderData data { GLRRenderCommand : : UNIFORM4I } ;
data . uniform4 . loc = loc ;
data . uniform4 . count = count ;
memcpy ( data . uniform4 . v , udata , sizeof ( int ) * count ) ;
curRenderStep_ - > commands . push_back ( data ) ;
}
void SetUniformI1 ( GLint * loc , int udata ) {
_dbg_assert_ ( G3D , curRenderStep_ & & curRenderStep_ - > stepType = = GLRStepType : : RENDER ) ;
GLRRenderData data { GLRRenderCommand : : UNIFORM4I } ;
data . uniform4 . loc = loc ;
data . uniform4 . count = 1 ;
memcpy ( data . uniform4 . v , & udata , sizeof ( udata ) ) ;
curRenderStep_ - > commands . push_back ( data ) ;
}
void SetUniformF ( GLint * loc , int count , const float * udata ) {
2017-11-18 20:17:17 +01:00
_dbg_assert_ ( G3D , curRenderStep_ & & curRenderStep_ - > stepType = = GLRStepType : : RENDER ) ;
GLRRenderData data { GLRRenderCommand : : UNIFORM4F } ;
data . uniform4 . loc = loc ;
data . uniform4 . count = count ;
memcpy ( data . uniform4 . v , udata , sizeof ( float ) * count ) ;
curRenderStep_ - > commands . push_back ( data ) ;
}
2017-12-12 14:07:52 +01:00
void SetUniformF1 ( GLint * loc , const float udata ) {
_dbg_assert_ ( G3D , curRenderStep_ & & curRenderStep_ - > stepType = = GLRStepType : : RENDER ) ;
GLRRenderData data { GLRRenderCommand : : UNIFORM4F } ;
data . uniform4 . loc = loc ;
data . uniform4 . count = 1 ;
memcpy ( data . uniform4 . v , & udata , sizeof ( float ) ) ;
curRenderStep_ - > commands . push_back ( data ) ;
}
2017-11-18 20:17:17 +01:00
void SetUniformF ( const char * name , int count , const float * udata ) {
_dbg_assert_ ( G3D , curRenderStep_ & & curRenderStep_ - > stepType = = GLRStepType : : RENDER ) ;
GLRRenderData data { GLRRenderCommand : : UNIFORM4F } ;
data . uniform4 . name = name ;
data . uniform4 . count = count ;
memcpy ( data . uniform4 . v , udata , sizeof ( float ) * count ) ;
curRenderStep_ - > commands . push_back ( data ) ;
}
2017-11-19 00:43:58 +01:00
void SetUniformM4x4 ( GLint * loc , const float * udata ) {
2017-11-18 20:17:17 +01:00
_dbg_assert_ ( G3D , curRenderStep_ & & curRenderStep_ - > stepType = = GLRStepType : : RENDER ) ;
2017-11-19 12:25:57 +01:00
GLRRenderData data { GLRRenderCommand : : UNIFORMMATRIX } ;
2017-11-18 20:17:17 +01:00
data . uniformMatrix4 . loc = loc ;
memcpy ( data . uniformMatrix4 . m , udata , sizeof ( float ) * 16 ) ;
curRenderStep_ - > commands . push_back ( data ) ;
}
void SetUniformM4x4 ( const char * name , const float * udata ) {
_dbg_assert_ ( G3D , curRenderStep_ & & curRenderStep_ - > stepType = = GLRStepType : : RENDER ) ;
2017-11-18 21:51:17 +01:00
GLRRenderData data { GLRRenderCommand : : UNIFORMMATRIX } ;
2017-11-18 20:17:17 +01:00
data . uniformMatrix4 . name = name ;
memcpy ( data . uniformMatrix4 . m , udata , sizeof ( float ) * 16 ) ;
curRenderStep_ - > commands . push_back ( data ) ;
}
2017-11-19 00:23:52 +01:00
void SetBlendAndMask ( int colorMask , bool blendEnabled , GLenum srcColor , GLenum dstColor , GLenum srcAlpha , GLenum dstAlpha , GLenum funcColor , GLenum funcAlpha ) {
_dbg_assert_ ( G3D , curRenderStep_ & & curRenderStep_ - > stepType = = GLRStepType : : RENDER ) ;
GLRRenderData data { GLRRenderCommand : : BLEND } ;
data . blend . mask = colorMask ;
data . blend . enabled = blendEnabled ;
data . blend . srcColor = srcColor ;
data . blend . dstColor = dstColor ;
data . blend . srcAlpha = srcAlpha ;
data . blend . dstAlpha = dstAlpha ;
data . blend . funcColor = funcColor ;
data . blend . funcAlpha = funcAlpha ;
curRenderStep_ - > commands . push_back ( data ) ;
}
2017-11-19 00:43:58 +01:00
void SetNoBlendAndMask ( int colorMask ) {
_dbg_assert_ ( G3D , curRenderStep_ & & curRenderStep_ - > stepType = = GLRStepType : : RENDER ) ;
GLRRenderData data { GLRRenderCommand : : BLEND } ;
data . blend . mask = colorMask ;
data . blend . enabled = false ;
curRenderStep_ - > commands . push_back ( data ) ;
}
2018-01-19 20:24:09 +01:00
# ifndef USING_GLES2
void SetLogicOp ( bool enabled , GLenum logicOp ) {
_dbg_assert_ ( G3D , curRenderStep_ & & curRenderStep_ - > stepType = = GLRStepType : : RENDER ) ;
GLRRenderData data { GLRRenderCommand : : LOGICOP } ;
data . logic . enabled = enabled ;
data . logic . logicOp = logicOp ;
curRenderStep_ - > commands . push_back ( data ) ;
}
# endif
2017-12-12 15:04:46 +01:00
void SetStencilFunc ( bool enabled , GLenum func , uint8_t refValue , uint8_t compareMask ) {
2017-11-18 15:42:39 +01:00
_dbg_assert_ ( G3D , curRenderStep_ & & curRenderStep_ - > stepType = = GLRStepType : : RENDER ) ;
2017-12-12 15:04:46 +01:00
GLRRenderData data { GLRRenderCommand : : STENCILFUNC } ;
data . stencilFunc . enabled = enabled ;
data . stencilFunc . func = func ;
data . stencilFunc . ref = refValue ;
data . stencilFunc . compareMask = compareMask ;
curRenderStep_ - > commands . push_back ( data ) ;
}
void SetStencilOp ( uint8_t writeMask , GLenum sFail , GLenum zFail , GLenum pass ) {
_dbg_assert_ ( G3D , curRenderStep_ & & curRenderStep_ - > stepType = = GLRStepType : : RENDER ) ;
GLRRenderData data { GLRRenderCommand : : STENCILOP } ;
data . stencilOp . writeMask = writeMask ;
data . stencilOp . sFail = sFail ;
data . stencilOp . zFail = zFail ;
data . stencilOp . pass = pass ;
2017-11-18 15:42:39 +01:00
curRenderStep_ - > commands . push_back ( data ) ;
}
2017-11-19 12:25:57 +01:00
void SetStencilDisabled ( ) {
_dbg_assert_ ( G3D , curRenderStep_ & & curRenderStep_ - > stepType = = GLRStepType : : RENDER ) ;
GLRRenderData data ;
2017-12-12 15:04:46 +01:00
data . cmd = GLRRenderCommand : : STENCILFUNC ;
data . stencilFunc . enabled = false ;
2017-11-19 12:25:57 +01:00
curRenderStep_ - > commands . push_back ( data ) ;
}
2017-11-19 00:43:58 +01:00
void SetBlendFactor ( const float color [ 4 ] ) {
2017-11-18 15:42:39 +01:00
_dbg_assert_ ( G3D , curRenderStep_ & & curRenderStep_ - > stepType = = GLRStepType : : RENDER ) ;
GLRRenderData data { GLRRenderCommand : : BLENDCOLOR } ;
CopyFloat4 ( data . blendColor . color , color ) ;
curRenderStep_ - > commands . push_back ( data ) ;
}
2017-11-19 00:43:58 +01:00
void SetRaster ( GLboolean cullEnable , GLenum frontFace , GLenum cullFace , GLboolean ditherEnable ) {
2017-11-18 16:50:49 +01:00
_dbg_assert_ ( G3D , curRenderStep_ & & curRenderStep_ - > stepType = = GLRStepType : : RENDER ) ;
GLRRenderData data { GLRRenderCommand : : RASTER } ;
data . raster . cullEnable = cullEnable ;
data . raster . frontFace = frontFace ;
data . raster . cullFace = cullFace ;
2017-11-19 00:43:58 +01:00
data . raster . ditherEnable = ditherEnable ;
2017-11-18 16:50:49 +01:00
curRenderStep_ - > commands . push_back ( data ) ;
}
2017-11-18 20:17:17 +01:00
// Modifies the current texture as per GL specs, not global state.
2017-11-19 17:37:56 +01:00
void SetTextureSampler ( GLenum wrapS , GLenum wrapT , GLenum magFilter , GLenum minFilter , float anisotropy ) {
2017-11-18 20:17:17 +01:00
_dbg_assert_ ( G3D , curRenderStep_ & & curRenderStep_ - > stepType = = GLRStepType : : RENDER ) ;
GLRRenderData data { GLRRenderCommand : : TEXTURESAMPLER } ;
data . textureSampler . wrapS = wrapS ;
data . textureSampler . wrapT = wrapT ;
data . textureSampler . magFilter = magFilter ;
data . textureSampler . minFilter = minFilter ;
2017-11-19 17:37:56 +01:00
data . textureSampler . anisotropy = anisotropy ;
2017-11-18 20:17:17 +01:00
curRenderStep_ - > commands . push_back ( data ) ;
}
2017-11-18 16:50:49 +01:00
2017-12-13 01:05:33 +01:00
void SetTextureLod ( float minLod , float maxLod , float lodBias ) {
_dbg_assert_ ( G3D , curRenderStep_ & & curRenderStep_ - > stepType = = GLRStepType : : RENDER ) ;
GLRRenderData data { GLRRenderCommand : : TEXTURELOD } ;
data . textureLod . minLod = minLod ;
data . textureLod . maxLod = maxLod ;
data . textureLod . lodBias = lodBias ;
curRenderStep_ - > commands . push_back ( data ) ;
}
2018-01-18 22:17:29 -08:00
void Clear ( uint32_t clearColor , float clearZ , int clearStencil , int clearMask , int colorMask = 0xF ) {
2017-11-18 15:42:39 +01:00
_dbg_assert_ ( G3D , curRenderStep_ & & curRenderStep_ - > stepType = = GLRStepType : : RENDER ) ;
GLRRenderData data { GLRRenderCommand : : CLEAR } ;
data . clear . clearMask = clearMask ;
data . clear . clearColor = clearColor ;
data . clear . clearZ = clearZ ;
data . clear . clearStencil = clearStencil ;
2018-01-18 22:17:29 -08:00
data . clear . colorMask = colorMask ;
2017-11-18 15:42:39 +01:00
curRenderStep_ - > commands . push_back ( data ) ;
}
2017-12-14 14:53:27 +01:00
void Invalidate ( int invalidateMask ) {
_dbg_assert_ ( G3D , curRenderStep_ & & curRenderStep_ - > stepType = = GLRStepType : : RENDER ) ;
GLRRenderData data { GLRRenderCommand : : INVALIDATE } ;
data . clear . clearMask = invalidateMask ;
curRenderStep_ - > commands . push_back ( data ) ;
}
2017-11-18 15:42:39 +01:00
void Draw ( GLenum mode , int first , int count ) {
_dbg_assert_ ( G3D , curRenderStep_ & & curRenderStep_ - > stepType = = GLRStepType : : RENDER ) ;
GLRRenderData data { GLRRenderCommand : : DRAW } ;
data . draw . mode = mode ;
data . draw . first = first ;
data . draw . count = count ;
data . draw . buffer = 0 ;
curRenderStep_ - > commands . push_back ( data ) ;
curRenderStep_ - > render . numDraws + + ;
}
2017-12-19 12:25:13 +01:00
void DrawIndexed ( GLenum mode , int count , GLenum indexType , void * indices , int instances = 1 ) {
2017-11-18 15:42:39 +01:00
_dbg_assert_ ( G3D , curRenderStep_ & & curRenderStep_ - > stepType = = GLRStepType : : RENDER ) ;
GLRRenderData data { GLRRenderCommand : : DRAW_INDEXED } ;
data . drawIndexed . mode = mode ;
data . drawIndexed . count = count ;
data . drawIndexed . indexType = indexType ;
2017-12-19 12:25:13 +01:00
data . drawIndexed . instances = instances ;
2017-11-18 15:42:39 +01:00
data . drawIndexed . indices = indices ;
curRenderStep_ - > commands . push_back ( data ) ;
curRenderStep_ - > render . numDraws + + ;
}
enum { MAX_INFLIGHT_FRAMES = 3 } ;
2017-11-19 00:23:52 +01:00
int GetCurFrame ( ) const {
return curFrame_ ;
}
2017-12-12 15:04:46 +01:00
void Resize ( int width , int height ) {
targetWidth_ = width ;
targetHeight_ = height ;
queueRunner_ . Resize ( width , height ) ;
}
2017-12-27 12:44:46 +01:00
// When using legacy functionality for push buffers (glBufferData), we need to flush them
// before actually making the glDraw* calls. It's best if the render manager handles that.
void RegisterPushBuffer ( int frame , GLPushBuffer * buffer ) {
frameData_ [ frame ] . activePushBuffers . insert ( buffer ) ;
}
void UnregisterPushBuffer ( int frame , GLPushBuffer * buffer ) {
auto iter = frameData_ [ frame ] . activePushBuffers . find ( buffer ) ;
_assert_ ( iter ! = frameData_ [ frame ] . activePushBuffers . end ( ) ) ;
frameData_ [ frame ] . activePushBuffers . erase ( iter ) ;
}
2018-01-16 14:16:56 +01:00
void SetSwapFunction ( std : : function < void ( ) > swapFunction ) {
swapFunction_ = swapFunction ;
}
void Swap ( ) {
if ( ! useThread_ & & swapFunction_ ) {
swapFunction_ ( ) ;
}
}
2018-01-16 18:13:31 +01:00
void StopThread ( ) ;
2018-01-16 14:16:56 +01:00
2018-01-16 18:13:31 +01:00
private :
2017-11-18 15:42:39 +01:00
void BeginSubmitFrame ( int frame ) ;
void EndSubmitFrame ( int frame ) ;
void Submit ( int frame , bool triggerFence ) ;
// Bad for performance but sometimes necessary for synchronous CPU readbacks (screenshots and whatnot).
void FlushSync ( ) ;
void EndSyncFrame ( int frame ) ;
// Per-frame data, round-robin so we can overlap submission with execution of the previous frame.
struct FrameData {
std : : mutex push_mutex ;
std : : condition_variable push_condVar ;
std : : mutex pull_mutex ;
std : : condition_variable pull_condVar ;
bool readyForFence = true ;
bool readyForRun = false ;
bool skipSwap = false ;
GLRRunType type = GLRRunType : : END ;
// GLuint fence; For future AZDO stuff?
std : : vector < GLRStep * > steps ;
std : : vector < GLRInitStep > initSteps ;
// Swapchain.
bool hasBegun = false ;
uint32_t curSwapchainImage = - 1 ;
GLDeleter deleter ;
2017-12-27 12:44:46 +01:00
std : : set < GLPushBuffer * > activePushBuffers ;
2017-11-18 15:42:39 +01:00
} ;
FrameData frameData_ [ MAX_INFLIGHT_FRAMES ] ;
// Submission time state
bool insideFrame_ = false ;
GLRStep * curRenderStep_ = nullptr ;
std : : vector < GLRStep * > steps_ ;
std : : vector < GLRInitStep > initSteps_ ;
// Execution time state
bool run_ = true ;
2018-01-16 14:16:56 +01:00
// Thread is managed elsewhere, and should call ThreadFrame.
2017-11-18 15:42:39 +01:00
std : : mutex mutex_ ;
int threadInitFrame_ = 0 ;
GLQueueRunner queueRunner_ ;
2018-01-16 17:32:50 +01:00
// Thread state
int threadFrame_ ;
2018-01-16 14:16:56 +01:00
bool nextFrame = false ;
bool firstFrame = true ;
2017-11-18 15:42:39 +01:00
GLDeleter deleter_ ;
2018-01-16 14:16:56 +01:00
bool useThread_ = true ;
2017-11-18 15:42:39 +01:00
int curFrame_ = 0 ;
2017-12-12 15:04:46 +01:00
2018-01-16 14:16:56 +01:00
std : : function < void ( ) > swapFunction_ ;
2017-12-12 15:04:46 +01:00
int targetWidth_ = 0 ;
int targetHeight_ = 0 ;
2017-11-18 15:42:39 +01:00
} ;
2017-11-19 00:23:52 +01:00
// Similar to VulkanPushBuffer but uses really stupid tactics - collect all the data in RAM then do a big
// memcpy/buffer upload at the end. This can however be optimized with glBufferStorage on chips that support that
// for massive boosts.
class GLPushBuffer {
public :
struct BufInfo {
GLRBuffer * buffer ;
uint8_t * deviceMemory ;
} ;
public :
2017-11-19 18:22:46 +01:00
GLPushBuffer ( GLRenderManager * render , GLuint target , size_t size ) ;
2017-11-19 00:23:52 +01:00
~ GLPushBuffer ( ) ;
void Destroy ( ) ;
void Reset ( ) { offset_ = 0 ; }
// Needs context in case of defragment.
void Begin ( ) {
buf_ = 0 ;
offset_ = 0 ;
// Note: we must defrag because some buffers may be smaller than size_.
Defragment ( ) ;
Map ( ) ;
assert ( writePtr_ ) ;
}
void BeginNoReset ( ) {
Map ( ) ;
}
void End ( ) {
Unmap ( ) ;
}
2017-11-19 12:25:57 +01:00
void Map ( ) ;
2017-11-19 00:23:52 +01:00
void Unmap ( ) ;
// When using the returned memory, make sure to bind the returned vkbuf.
// This will later allow for handling overflow correctly.
size_t Allocate ( size_t numBytes , GLRBuffer * * vkbuf ) {
size_t out = offset_ ;
2017-12-12 15:32:15 +01:00
if ( offset_ + ( ( numBytes + 3 ) & ~ 3 ) > = size_ ) {
2017-11-19 00:23:52 +01:00
NextBuffer ( numBytes ) ;
out = offset_ ;
offset_ + = ( numBytes + 3 ) & ~ 3 ;
2017-12-12 15:32:15 +01:00
} else {
offset_ + = ( numBytes + 3 ) & ~ 3 ; // Round up to 4 bytes.
2017-11-19 00:23:52 +01:00
}
* vkbuf = buffers_ [ buf_ ] . buffer ;
return out ;
}
// Returns the offset that should be used when binding this buffer to get this data.
size_t Push ( const void * data , size_t size , GLRBuffer * * vkbuf ) {
assert ( writePtr_ ) ;
size_t off = Allocate ( size , vkbuf ) ;
memcpy ( writePtr_ + off , data , size ) ;
return off ;
}
uint32_t PushAligned ( const void * data , size_t size , int align , GLRBuffer * * vkbuf ) {
assert ( writePtr_ ) ;
offset_ = ( offset_ + align - 1 ) & ~ ( align - 1 ) ;
size_t off = Allocate ( size , vkbuf ) ;
memcpy ( writePtr_ + off , data , size ) ;
return ( uint32_t ) off ;
}
size_t GetOffset ( ) const {
return offset_ ;
}
// "Zero-copy" variant - you can write the data directly as you compute it.
// Recommended.
void * Push ( size_t size , uint32_t * bindOffset , GLRBuffer * * vkbuf ) {
assert ( writePtr_ ) ;
size_t off = Allocate ( size , vkbuf ) ;
* bindOffset = ( uint32_t ) off ;
return writePtr_ + off ;
}
void * PushAligned ( size_t size , uint32_t * bindOffset , GLRBuffer * * vkbuf , int align ) {
assert ( writePtr_ ) ;
offset_ = ( offset_ + align - 1 ) & ~ ( align - 1 ) ;
size_t off = Allocate ( size , vkbuf ) ;
* bindOffset = ( uint32_t ) off ;
return writePtr_ + off ;
}
size_t GetTotalSize ( ) const ;
2017-12-27 12:44:46 +01:00
void Flush ( ) ;
2017-11-19 00:23:52 +01:00
private :
bool AddBuffer ( ) ;
void NextBuffer ( size_t minSize ) ;
void Defragment ( ) ;
GLRenderManager * render_ ;
std : : vector < BufInfo > buffers_ ;
2017-11-19 18:22:46 +01:00
size_t buf_ = 0 ;
size_t offset_ = 0 ;
size_t size_ = 0 ;
uint8_t * writePtr_ = nullptr ;
GLuint target_ ;
2017-11-19 00:23:52 +01:00
} ;