Incorporate changes from gpu-gl into gpu-gles (changes in organization and architecture of classes).

This commit is contained in:
Cristian Luis Duarte 2017-12-29 17:38:42 -03:00 committed by Gabriel Calero
parent 597114afea
commit 40a7634a53
23 changed files with 2581 additions and 1880 deletions

View file

@ -1,5 +1,5 @@
set(TARGET_NAME gpu-gles)
setup_hifi_library(OpenGL)
setup_hifi_library(Concurrent)
link_hifi_libraries(shared gl gpu)
GroupSources("src")

View file

@ -22,17 +22,18 @@
#include "nvToolsExt.h"
#endif
#include <shared/GlobalAppProperties.h>
#include <GPUIdent.h>
#include <gl/QOpenGLContextWrapper.h>
#include <QtCore/QProcessEnvironment>
#include "GLTexture.h"
#include "GLShader.h"
using namespace gpu;
using namespace gpu::gl;
static GLBackend* INSTANCE{ nullptr };
static const char* GL_BACKEND_PROPERTY_NAME = "com.highfidelity.gl.backend";
BackendPointer GLBackend::createBackend() {
// FIXME provide a mechanism to override the backend for testing
@ -45,18 +46,17 @@ BackendPointer GLBackend::createBackend() {
result->initInput();
result->initTransform();
result->initTextureManagementStage();
INSTANCE = result.get();
void* voidInstance = &(*result);
qApp->setProperty(GL_BACKEND_PROPERTY_NAME, QVariant::fromValue(voidInstance));
gl::GLTexture::initTextureTransferHelper();
qApp->setProperty(hifi::properties::gl::BACKEND, QVariant::fromValue(voidInstance));
return result;
}
GLBackend& getBackend() {
if (!INSTANCE) {
INSTANCE = static_cast<GLBackend*>(qApp->property(GL_BACKEND_PROPERTY_NAME).value<void*>());
INSTANCE = static_cast<GLBackend*>(qApp->property(hifi::properties::gl::BACKEND).value<void*>());
}
return *INSTANCE;
}
@ -65,9 +65,6 @@ bool GLBackend::makeProgram(Shader& shader, const Shader::BindingSet& slotBindin
return GLShader::makeProgram(getBackend(), shader, slotBindings);
}
std::array<QString, 45> commandNames = {
{QString("draw"),QString("drawIndexed"),QString("drawInstanced"),QString("drawIndexedInstanced"),QString("multiDrawIndirect"),QString("multiDrawIndexedIndirect"),QString("setInputFormat"),QString("setInputBuffer"),QString("setIndexBuffer"),QString("setIndirectBuffer"),QString("setModelTransform"),QString("setViewTransform"),QString("setProjectionTransform"),QString("setViewportTransform"),QString("setDepthRangeTransform"),QString("setPipeline"),QString("setStateBlendFactor"),QString("setStateScissorRect"),QString("setUniformBuffer"),QString("setResourceTexture"),QString("setFramebuffer"),QString("clearFramebuffer"),QString("blit"),QString("generateTextureMips"),QString("beginQuery"),QString("endQuery"),QString("getQuery"),QString("resetStages"),QString("runLambda"),QString("startNamedCall"),QString("stopNamedCall"),QString("glUniform1i"),QString("glUniform1f"),QString("glUniform2f"),QString("glUniform3f"),QString("glUniform4f"),QString("glUniform3fv"),QString("glUniform4fv"),QString("glUniform4iv"),QString("glUniformMatrix3fv"),QString("glUniformMatrix4fv"),QString("glColor4f"),QString("pushProfileRange"),QString("popProfileRange"),QString("NUM_COMMANDS")}
};
GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] =
{
@ -161,6 +158,12 @@ void GLBackend::init() {
qCDebug(gpugllogging, "Status: Using GLEW %s\n", glewGetString(GLEW_VERSION));
*/
#if THREADED_TEXTURE_BUFFERING
// This has to happen on the main thread in order to give the thread
// pool a reasonable parent object
GLVariableAllocationSupport::TransferJob::startBufferingThread();
#endif
});
}
@ -171,8 +174,6 @@ GLBackend::GLBackend() {
GLBackend::~GLBackend() {
resetStages();
killInput();
killTransform();
}
@ -193,7 +194,7 @@ void GLBackend::renderPassTransfer(const Batch& batch) {
}
}
{ // Sync all the buffers
{ // Sync all the transform states
ANDROID_PROFILE(render, "syncCPUTransform", 0xffaaaaff, 1)
_transform._cameras.clear();
_transform._cameraOffsets.clear();
@ -209,6 +210,14 @@ void GLBackend::renderPassTransfer(const Batch& batch) {
_transform.preUpdate(_commandIndex, _stereo);
break;
case Batch::COMMAND_disableContextStereo:
_stereo._contextDisable = true;
break;
case Batch::COMMAND_restoreContextStereo:
_stereo._contextDisable = false;
break;
case Batch::COMMAND_setViewportTransform:
case Batch::COMMAND_setViewTransform:
case Batch::COMMAND_setProjectionTransform: {
@ -263,7 +272,8 @@ void GLBackend::renderPassDraw(const Batch& batch) {
updateInput();
updateTransform(batch);
updatePipeline();
{ANDROID_PROFILE_COMMAND(render, (int)(*command), 0xff0000ff, 1)
{
ANDROID_PROFILE_COMMAND(render, (int)(*command), 0xff0000ff, 1)
CommandCall call = _commandCalls[(*command)];
(this->*(call))(batch, *offset);
}
@ -298,6 +308,11 @@ void GLBackend::render(const Batch& batch) {
renderPassTransfer(batch);
}
#ifdef GPU_STEREO_DRAWCALL_INSTANCED
if (_stereo.isStereo()) {
glEnable(GL_CLIP_DISTANCE0);
}
#endif
{
//PROFILE_RANGE(render_gpu_gl, _stereo._enable ? "Render Stereo" : "Render");
ANDROID_PROFILE(render, "RenderPassDraw", 0xff00ddff, 1)
@ -413,7 +428,7 @@ void GLBackend::do_glUniform1i(const Batch& batch, size_t paramOffset) {
}
updatePipeline();
glUniform1f(
glUniform1i(
GET_UNIFORM_LOCATION(batch._params[paramOffset + 1]._int),
batch._params[paramOffset + 0]._int);
(void)CHECK_GL_ERROR();
@ -731,9 +746,9 @@ void GLBackend::recycle() const {
}
}
#ifndef THREADED_TEXTURE_TRANSFER
gl::GLTexture::_textureTransferHelper->process();
#endif
GLVariableAllocationSupport::manageMemory();
GLVariableAllocationSupport::_frameTexturesCreated = 0;
}
void GLBackend::setCameraCorrection(const Mat4& correction) {

View file

@ -53,257 +53,259 @@
#endif
namespace gpu { namespace gl {
class GLBackend : public Backend, public std::enable_shared_from_this<GLBackend> {
// Context Backend static interface required
friend class gpu::Context;
static void init();
static BackendPointer createBackend();
class GLBackend : public Backend, public std::enable_shared_from_this<GLBackend> {
// Context Backend static interface required
friend class gpu::Context;
static void init();
static BackendPointer createBackend();
protected:
explicit GLBackend(bool syncCache);
GLBackend();
public:
static bool makeProgram(Shader& shader, const Shader::BindingSet& slotBindings = Shader::BindingSet());
protected:
explicit GLBackend(bool syncCache);
GLBackend();
public:
static bool makeProgram(Shader& shader, const Shader::BindingSet& slotBindings = Shader::BindingSet());
~GLBackend();
virtual ~GLBackend();
void setCameraCorrection(const Mat4& correction);
void render(const Batch& batch) final override;
void setCameraCorrection(const Mat4& correction);
void render(const Batch& batch) final override;
// This call synchronize the Full Backend cache with the current GLState
// THis is only intended to be used when mixing raw gl calls with the gpu api usage in order to sync
// the gpu::Backend state with the true gl state which has probably been messed up by these ugly naked gl calls
// Let's try to avoid to do that as much as possible!
void syncCache() final override;
// This call synchronize the Full Backend cache with the current GLState
// THis is only intended to be used when mixing raw gl calls with the gpu api usage in order to sync
// the gpu::Backend state with the true gl state which has probably been messed up by these ugly naked gl calls
// Let's try to avoid to do that as much as possible!
void syncCache() final override;
// This is the ugly "download the pixels to sysmem for taking a snapshot"
// Just avoid using it, it's ugly and will break performances
virtual void downloadFramebuffer(const FramebufferPointer& srcFramebuffer,
const Vec4i& region, QImage& destImage) final override;
// This is the ugly "download the pixels to sysmem for taking a snapshot"
// Just avoid using it, it's ugly and will break performances
virtual void downloadFramebuffer(const FramebufferPointer& srcFramebuffer,
const Vec4i& region, QImage& destImage) final override;
// this is the maximum numeber of available input buffers
size_t getNumInputBuffers() const { return _input._invalidBuffers.size(); }
// this is the maximum numeber of available input buffers
size_t getNumInputBuffers() const { return _input._invalidBuffers.size(); }
// this is the maximum per shader stage on the low end apple
// TODO make it platform dependant at init time
static const int MAX_NUM_UNIFORM_BUFFERS = 12;
size_t getMaxNumUniformBuffers() const { return MAX_NUM_UNIFORM_BUFFERS; }
// this is the maximum per shader stage on the low end apple
// TODO make it platform dependant at init time
static const int MAX_NUM_UNIFORM_BUFFERS = 12;
size_t getMaxNumUniformBuffers() const { return MAX_NUM_UNIFORM_BUFFERS; }
// this is the maximum per shader stage on the low end apple
// TODO make it platform dependant at init time
static const int MAX_NUM_RESOURCE_BUFFERS = 16;
size_t getMaxNumResourceBuffers() const { return MAX_NUM_RESOURCE_BUFFERS; }
static const int MAX_NUM_RESOURCE_TEXTURES = 16;
size_t getMaxNumResourceTextures() const { return MAX_NUM_RESOURCE_TEXTURES; }
// this is the maximum per shader stage on the low end apple
// TODO make it platform dependant at init time
static const int MAX_NUM_RESOURCE_BUFFERS = 16;
size_t getMaxNumResourceBuffers() const { return MAX_NUM_RESOURCE_BUFFERS; }
static const int MAX_NUM_RESOURCE_TEXTURES = 16;
size_t getMaxNumResourceTextures() const { return MAX_NUM_RESOURCE_TEXTURES; }
// Draw Stage
virtual void do_draw(const Batch& batch, size_t paramOffset) = 0;
virtual void do_drawIndexed(const Batch& batch, size_t paramOffset) = 0;
virtual void do_drawInstanced(const Batch& batch, size_t paramOffset) = 0;
virtual void do_drawIndexedInstanced(const Batch& batch, size_t paramOffset) = 0;
virtual void do_multiDrawIndirect(const Batch& batch, size_t paramOffset) = 0;
virtual void do_multiDrawIndexedIndirect(const Batch& batch, size_t paramOffset) = 0;
// Draw Stage
virtual void do_draw(const Batch& batch, size_t paramOffset) = 0;
virtual void do_drawIndexed(const Batch& batch, size_t paramOffset) = 0;
virtual void do_drawInstanced(const Batch& batch, size_t paramOffset) = 0;
virtual void do_drawIndexedInstanced(const Batch& batch, size_t paramOffset) = 0;
virtual void do_multiDrawIndirect(const Batch& batch, size_t paramOffset) = 0;
virtual void do_multiDrawIndexedIndirect(const Batch& batch, size_t paramOffset) = 0;
// Input Stage
virtual void do_setInputFormat(const Batch& batch, size_t paramOffset) final;
virtual void do_setInputBuffer(const Batch& batch, size_t paramOffset) final;
virtual void do_setIndexBuffer(const Batch& batch, size_t paramOffset) final;
virtual void do_setIndirectBuffer(const Batch& batch, size_t paramOffset) final;
virtual void do_generateTextureMips(const Batch& batch, size_t paramOffset) final;
// Input Stage
virtual void do_setInputFormat(const Batch& batch, size_t paramOffset) final;
virtual void do_setInputBuffer(const Batch& batch, size_t paramOffset) final;
virtual void do_setIndexBuffer(const Batch& batch, size_t paramOffset) final;
virtual void do_setIndirectBuffer(const Batch& batch, size_t paramOffset) final;
virtual void do_generateTextureMips(const Batch& batch, size_t paramOffset) final;
// Transform Stage
virtual void do_setModelTransform(const Batch& batch, size_t paramOffset) final;
virtual void do_setViewTransform(const Batch& batch, size_t paramOffset) final;
virtual void do_setProjectionTransform(const Batch& batch, size_t paramOffset) final;
virtual void do_setViewportTransform(const Batch& batch, size_t paramOffset) final;
virtual void do_setDepthRangeTransform(const Batch& batch, size_t paramOffset) final;
// Transform Stage
virtual void do_setModelTransform(const Batch& batch, size_t paramOffset) final;
virtual void do_setViewTransform(const Batch& batch, size_t paramOffset) final;
virtual void do_setProjectionTransform(const Batch& batch, size_t paramOffset) final;
virtual void do_setViewportTransform(const Batch& batch, size_t paramOffset) final;
virtual void do_setDepthRangeTransform(const Batch& batch, size_t paramOffset) final;
// Uniform Stage
virtual void do_setUniformBuffer(const Batch& batch, size_t paramOffset) final;
// Uniform Stage
virtual void do_setUniformBuffer(const Batch& batch, size_t paramOffset) final;
// Resource Stage
virtual void do_setResourceBuffer(const Batch& batch, size_t paramOffset) final;
virtual void do_setResourceTexture(const Batch& batch, size_t paramOffset) final;
// Resource Stage
virtual void do_setResourceBuffer(const Batch& batch, size_t paramOffset) final;
virtual void do_setResourceTexture(const Batch& batch, size_t paramOffset) final;
// Pipeline Stage
virtual void do_setPipeline(const Batch& batch, size_t paramOffset) final;
// Pipeline Stage
virtual void do_setPipeline(const Batch& batch, size_t paramOffset) final;
// Output stage
virtual void do_setFramebuffer(const Batch& batch, size_t paramOffset) final;
virtual void do_clearFramebuffer(const Batch& batch, size_t paramOffset) final;
virtual void do_blit(const Batch& batch, size_t paramOffset) = 0;
// Output stage
virtual void do_setFramebuffer(const Batch& batch, size_t paramOffset) final;
virtual void do_clearFramebuffer(const Batch& batch, size_t paramOffset) final;
virtual void do_blit(const Batch& batch, size_t paramOffset) = 0;
// Query section
virtual void do_beginQuery(const Batch& batch, size_t paramOffset) final;
virtual void do_endQuery(const Batch& batch, size_t paramOffset) final;
virtual void do_getQuery(const Batch& batch, size_t paramOffset) final;
// Query section
virtual void do_beginQuery(const Batch& batch, size_t paramOffset) final;
virtual void do_endQuery(const Batch& batch, size_t paramOffset) final;
virtual void do_getQuery(const Batch& batch, size_t paramOffset) final;
// Reset stages
virtual void do_resetStages(const Batch& batch, size_t paramOffset) final;
// Reset stages
virtual void do_resetStages(const Batch& batch, size_t paramOffset) final;
virtual void do_disableContextViewCorrection(const Batch& batch, size_t paramOffset) final;
virtual void do_restoreContextViewCorrection(const Batch& batch, size_t paramOffset) final;
virtual void do_disableContextViewCorrection(const Batch& batch, size_t paramOffset) final;
virtual void do_restoreContextViewCorrection(const Batch& batch, size_t paramOffset) final;
virtual void do_disableContextStereo(const Batch& batch, size_t paramOffset) final;
virtual void do_restoreContextStereo(const Batch& batch, size_t paramOffset) final;
virtual void do_disableContextStereo(const Batch& batch, size_t paramOffset) final;
virtual void do_restoreContextStereo(const Batch& batch, size_t paramOffset) final;
virtual void do_runLambda(const Batch& batch, size_t paramOffset) final;
virtual void do_runLambda(const Batch& batch, size_t paramOffset) final;
virtual void do_startNamedCall(const Batch& batch, size_t paramOffset) final;
virtual void do_stopNamedCall(const Batch& batch, size_t paramOffset) final;
virtual void do_startNamedCall(const Batch& batch, size_t paramOffset) final;
virtual void do_stopNamedCall(const Batch& batch, size_t paramOffset) final;
static const int MAX_NUM_ATTRIBUTES = Stream::NUM_INPUT_SLOTS;
// The drawcall Info attribute channel is reserved and is the upper bound for the number of availables Input buffers
static const int MAX_NUM_INPUT_BUFFERS = Stream::DRAW_CALL_INFO;
static const int MAX_NUM_ATTRIBUTES = Stream::NUM_INPUT_SLOTS;
// The drawcall Info attribute channel is reserved and is the upper bound for the number of availables Input buffers
static const int MAX_NUM_INPUT_BUFFERS = Stream::DRAW_CALL_INFO;
virtual void do_pushProfileRange(const Batch& batch, size_t paramOffset) final;
virtual void do_popProfileRange(const Batch& batch, size_t paramOffset) final;
virtual void do_pushProfileRange(const Batch& batch, size_t paramOffset) final;
virtual void do_popProfileRange(const Batch& batch, size_t paramOffset) final;
// TODO: As long as we have gl calls explicitely issued from interface
// code, we need to be able to record and batch these calls. THe long
// term strategy is to get rid of any GL calls in favor of the HIFI GPU API
virtual void do_glUniform1i(const Batch& batch, size_t paramOffset) final;
virtual void do_glUniform1f(const Batch& batch, size_t paramOffset) final;
virtual void do_glUniform2f(const Batch& batch, size_t paramOffset) final;
virtual void do_glUniform3f(const Batch& batch, size_t paramOffset) final;
virtual void do_glUniform4f(const Batch& batch, size_t paramOffset) final;
virtual void do_glUniform3fv(const Batch& batch, size_t paramOffset) final;
virtual void do_glUniform4fv(const Batch& batch, size_t paramOffset) final;
virtual void do_glUniform4iv(const Batch& batch, size_t paramOffset) final;
virtual void do_glUniformMatrix3fv(const Batch& batch, size_t paramOffset) final;
virtual void do_glUniformMatrix4fv(const Batch& batch, size_t paramOffset) final;
// TODO: As long as we have gl calls explicitely issued from interface
// code, we need to be able to record and batch these calls. THe long
// term strategy is to get rid of any GL calls in favor of the HIFI GPU API
virtual void do_glUniform1i(const Batch& batch, size_t paramOffset) final;
virtual void do_glUniform1f(const Batch& batch, size_t paramOffset) final;
virtual void do_glUniform2f(const Batch& batch, size_t paramOffset) final;
virtual void do_glUniform3f(const Batch& batch, size_t paramOffset) final;
virtual void do_glUniform4f(const Batch& batch, size_t paramOffset) final;
virtual void do_glUniform3fv(const Batch& batch, size_t paramOffset) final;
virtual void do_glUniform4fv(const Batch& batch, size_t paramOffset) final;
virtual void do_glUniform4iv(const Batch& batch, size_t paramOffset) final;
virtual void do_glUniformMatrix3fv(const Batch& batch, size_t paramOffset) final;
virtual void do_glUniformMatrix4fv(const Batch& batch, size_t paramOffset) final;
virtual void do_glColor4f(const Batch& batch, size_t paramOffset) final;
virtual void do_glColor4f(const Batch& batch, size_t paramOffset) final;
// The State setters called by the GLState::Commands when a new state is assigned
virtual void do_setStateFillMode(int32 mode) final;
virtual void do_setStateCullMode(int32 mode) final;
virtual void do_setStateFrontFaceClockwise(bool isClockwise) final;
virtual void do_setStateDepthClampEnable(bool enable) final;
virtual void do_setStateScissorEnable(bool enable) final;
virtual void do_setStateMultisampleEnable(bool enable) final;
virtual void do_setStateAntialiasedLineEnable(bool enable) final;
virtual void do_setStateDepthBias(Vec2 bias) final;
virtual void do_setStateDepthTest(State::DepthTest test) final;
virtual void do_setStateStencil(State::StencilActivation activation, State::StencilTest frontTest, State::StencilTest backTest) final;
virtual void do_setStateAlphaToCoverageEnable(bool enable) final;
virtual void do_setStateSampleMask(uint32 mask) final;
virtual void do_setStateBlend(State::BlendFunction blendFunction) final;
virtual void do_setStateColorWriteMask(uint32 mask) final;
virtual void do_setStateBlendFactor(const Batch& batch, size_t paramOffset) final;
virtual void do_setStateScissorRect(const Batch& batch, size_t paramOffset) final;
// The State setters called by the GLState::Commands when a new state is assigned
virtual void do_setStateFillMode(int32 mode) final;
virtual void do_setStateCullMode(int32 mode) final;
virtual void do_setStateFrontFaceClockwise(bool isClockwise) final;
virtual void do_setStateDepthClampEnable(bool enable) final;
virtual void do_setStateScissorEnable(bool enable) final;
virtual void do_setStateMultisampleEnable(bool enable) final;
virtual void do_setStateAntialiasedLineEnable(bool enable) final;
virtual void do_setStateDepthBias(Vec2 bias) final;
virtual void do_setStateDepthTest(State::DepthTest test) final;
virtual void do_setStateStencil(State::StencilActivation activation, State::StencilTest frontTest, State::StencilTest backTest) final;
virtual void do_setStateAlphaToCoverageEnable(bool enable) final;
virtual void do_setStateSampleMask(uint32 mask) final;
virtual void do_setStateBlend(State::BlendFunction blendFunction) final;
virtual void do_setStateColorWriteMask(uint32 mask) final;
virtual void do_setStateBlendFactor(const Batch& batch, size_t paramOffset) final;
virtual void do_setStateScissorRect(const Batch& batch, size_t paramOffset) final;
virtual GLuint getFramebufferID(const FramebufferPointer& framebuffer) = 0;
virtual GLuint getTextureID(const TexturePointer& texture, bool needTransfer = true) = 0;
virtual GLuint getBufferID(const Buffer& buffer) = 0;
virtual GLuint getQueryID(const QueryPointer& query) = 0;
virtual bool isTextureReady(const TexturePointer& texture);
virtual GLuint getFramebufferID(const FramebufferPointer& framebuffer) = 0;
virtual GLuint getTextureID(const TexturePointer& texture) final;
virtual GLuint getBufferID(const Buffer& buffer) = 0;
virtual GLuint getQueryID(const QueryPointer& query) = 0;
virtual void releaseBuffer(GLuint id, Size size) const;
virtual void releaseExternalTexture(GLuint id, const Texture::ExternalRecycler& recycler) const;
virtual void releaseTexture(GLuint id, Size size) const;
virtual void releaseFramebuffer(GLuint id) const;
virtual void releaseShader(GLuint id) const;
virtual void releaseProgram(GLuint id) const;
virtual void releaseQuery(GLuint id) const;
virtual void queueLambda(const std::function<void()> lambda) const;
virtual GLFramebuffer* syncGPUObject(const Framebuffer& framebuffer) = 0;
virtual GLBuffer* syncGPUObject(const Buffer& buffer) = 0;
virtual GLTexture* syncGPUObject(const TexturePointer& texture);
virtual GLQuery* syncGPUObject(const Query& query) = 0;
//virtual bool isTextureReady(const TexturePointer& texture);
bool isTextureManagementSparseEnabled() const override { return (_textureManagement._sparseCapable && Texture::getEnableSparseTextures()); }
virtual void releaseBuffer(GLuint id, Size size) const;
virtual void releaseExternalTexture(GLuint id, const Texture::ExternalRecycler& recycler) const;
virtual void releaseTexture(GLuint id, Size size) const;
virtual void releaseFramebuffer(GLuint id) const;
virtual void releaseShader(GLuint id) const;
virtual void releaseProgram(GLuint id) const;
virtual void releaseQuery(GLuint id) const;
virtual void queueLambda(const std::function<void()> lambda) const;
protected:
bool isTextureManagementSparseEnabled() const override { return (_textureManagement._sparseCapable && Texture::getEnableSparseTextures()); }
void recycle() const override;
virtual GLFramebuffer* syncGPUObject(const Framebuffer& framebuffer) = 0;
virtual GLBuffer* syncGPUObject(const Buffer& buffer) = 0;
virtual GLTexture* syncGPUObject(const TexturePointer& texture, bool sync = true) = 0;
virtual GLQuery* syncGPUObject(const Query& query) = 0;
protected:
static const size_t INVALID_OFFSET = (size_t)-1;
bool _inRenderTransferPass { false };
int32_t _uboAlignment { 0 };
int _currentDraw { -1 };
void recycle() const override;
std::list<std::string> profileRanges;
mutable Mutex _trashMutex;
mutable std::list<std::pair<GLuint, Size>> _buffersTrash;
mutable std::list<std::pair<GLuint, Size>> _texturesTrash;
mutable std::list<std::pair<GLuint, Texture::ExternalRecycler>> _externalTexturesTrash;
mutable std::list<GLuint> _framebuffersTrash;
mutable std::list<GLuint> _shadersTrash;
mutable std::list<GLuint> _programsTrash;
mutable std::list<GLuint> _queriesTrash;
mutable std::list<std::function<void()>> _lambdaQueue;
static const size_t INVALID_OFFSET = (size_t)-1;
bool _inRenderTransferPass { false };
int32_t _uboAlignment { 0 };
int _currentDraw { -1 };
void renderPassTransfer(const Batch& batch);
void renderPassDraw(const Batch& batch);
void setupStereoSide(int side);
std::list<std::string> profileRanges;
mutable Mutex _trashMutex;
mutable std::list<std::pair<GLuint, Size>> _buffersTrash;
mutable std::list<std::pair<GLuint, Size>> _texturesTrash;
mutable std::list<std::pair<GLuint, Texture::ExternalRecycler>> _externalTexturesTrash;
mutable std::list<GLuint> _framebuffersTrash;
mutable std::list<GLuint> _shadersTrash;
mutable std::list<GLuint> _programsTrash;
mutable std::list<GLuint> _queriesTrash;
mutable std::list<std::function<void()>> _lambdaQueue;
virtual void initInput() final;
virtual void killInput() final;
virtual void syncInputStateCache() final;
virtual void resetInputStage();
virtual void updateInput();
void renderPassTransfer(const Batch& batch);
void renderPassDraw(const Batch& batch);
void setupStereoSide(int side);
struct InputStageState {
bool _invalidFormat { true };
Stream::FormatPointer _format;
std::string _formatKey;
virtual void initInput() final;
virtual void killInput() final;
virtual void syncInputStateCache() final;
virtual void resetInputStage();
virtual void updateInput() = 0;
typedef std::bitset<MAX_NUM_ATTRIBUTES> ActivationCache;
ActivationCache _attributeActivation { 0 };
struct InputStageState {
bool _invalidFormat { true };
Stream::FormatPointer _format;
std::string _formatKey;
typedef std::bitset<MAX_NUM_INPUT_BUFFERS> BuffersState;
typedef std::bitset<MAX_NUM_ATTRIBUTES> ActivationCache;
ActivationCache _attributeActivation { 0 };
BuffersState _invalidBuffers{ 0 };
BuffersState _attribBindingBuffers{ 0 };
typedef std::bitset<MAX_NUM_INPUT_BUFFERS> BuffersState;
Buffers _buffers;
Offsets _bufferOffsets;
Offsets _bufferStrides;
std::vector<GLuint> _bufferVBOs;
BuffersState _invalidBuffers{ 0 };
BuffersState _attribBindingBuffers{ 0 };
glm::vec4 _colorAttribute{ 0.0f };
Buffers _buffers;
Offsets _bufferOffsets;
Offsets _bufferStrides;
std::vector<GLuint> _bufferVBOs;
BufferPointer _indexBuffer;
Offset _indexBufferOffset { 0 };
Type _indexBufferType { UINT32 };
glm::vec4 _colorAttribute{ 0.0f };
BufferPointer _indirectBuffer;
Offset _indirectBufferOffset{ 0 };
Offset _indirectBufferStride{ 0 };
BufferPointer _indexBuffer;
Offset _indexBufferOffset { 0 };
Type _indexBufferType { UINT32 };
BufferPointer _indirectBuffer;
Offset _indirectBufferOffset{ 0 };
Offset _indirectBufferStride{ 0 };
GLuint _defaultVAO { 0 };
GLuint _defaultVAO { 0 };
InputStageState() :
_invalidFormat(true),
_format(0),
_formatKey(),
_attributeActivation(0),
_buffers(_invalidBuffers.size(), BufferPointer(0)),
_bufferOffsets(_invalidBuffers.size(), 0),
_bufferStrides(_invalidBuffers.size(), 0),
_bufferVBOs(_invalidBuffers.size(), 0) {}
} _input;
InputStageState() :
_invalidFormat(true),
_format(0),
_formatKey(),
_attributeActivation(0),
_buffers(_invalidBuffers.size(), BufferPointer(0)),
_bufferOffsets(_invalidBuffers.size(), 0),
_bufferStrides(_invalidBuffers.size(), 0),
_bufferVBOs(_invalidBuffers.size(), 0) {}
} _input;
virtual void initTransform() = 0;
void killTransform();
// Synchronize the state cache of this Backend with the actual real state of the GL Context
void syncTransformStateCache();
void updateTransform(const Batch& batch);
void resetTransformStage();
virtual void initTransform() = 0;
void killTransform();
// Synchronize the state cache of this Backend with the actual real state of the GL Context
void syncTransformStateCache();
virtual void updateTransform(const Batch& batch) = 0;
virtual void resetTransformStage();
// Allows for correction of the camera pose to account for changes
// between the time when a was recorded and the time(s) when it is
// executed
struct CameraCorrection {
Mat4 correction;
Mat4 correctionInverse;
};
// Allows for correction of the camera pose to account for changes
// between the time when a was recorded and the time(s) when it is
// executed
struct CameraCorrection {
Mat4 correction;
Mat4 correctionInverse;
};
struct TransformStageState {
struct TransformStageState {
#ifdef GPU_STEREO_CAMERA_BUFFER
struct Cameras {
struct Cameras {
TransformCamera _cams[2];
Cameras() {};
@ -313,131 +315,158 @@ namespace gpu { namespace gl {
using CameraBufferElement = Cameras;
#else
using CameraBufferElement = TransformCamera;
using CameraBufferElement = TransformCamera;
#endif
using TransformCameras = std::vector<CameraBufferElement>;
using TransformCameras = std::vector<CameraBufferElement>;
TransformCamera _camera;
TransformCameras _cameras;
TransformCamera _camera;
TransformCameras _cameras;
mutable std::map<std::string, GLvoid*> _drawCallInfoOffsets;
mutable std::map<std::string, GLvoid*> _drawCallInfoOffsets;
GLuint _objectBuffer { 0 };
GLuint _cameraBuffer { 0 };
GLuint _drawCallInfoBuffer { 0 };
GLuint _objectBufferTexture { 0 };
size_t _cameraUboSize { 0 };
bool _viewIsCamera{ false };
bool _skybox { false };
Transform _view;
CameraCorrection _correction;
bool _viewCorrectionEnabled{ true };
GLuint _objectBuffer { 0 };
GLuint _cameraBuffer { 0 };
GLuint _drawCallInfoBuffer { 0 };
GLuint _objectBufferTexture { 0 };
size_t _cameraUboSize { 0 };
bool _viewIsCamera{ false };
bool _skybox { false };
Transform _view;
CameraCorrection _correction;
bool _viewCorrectionEnabled{ true };
Mat4 _projection;
Vec4i _viewport { 0, 0, 1, 1 };
Vec2 _depthRange { 0.0f, 1.0f };
bool _invalidView { false };
bool _invalidProj { false };
bool _invalidViewport { false };
bool _enabledDrawcallInfoBuffer{ false };
Mat4 _projection;
Vec4i _viewport { 0, 0, 1, 1 };
Vec2 _depthRange { 0.0f, 1.0f };
bool _invalidView { false };
bool _invalidProj { false };
bool _invalidViewport { false };
using Pair = std::pair<size_t, size_t>;
using List = std::list<Pair>;
List _cameraOffsets;
mutable List::const_iterator _camerasItr;
mutable size_t _currentCameraOffset{ INVALID_OFFSET };
bool _enabledDrawcallInfoBuffer{ false };
void preUpdate(size_t commandIndex, const StereoState& stereo);
void update(size_t commandIndex, const StereoState& stereo) const;
void bindCurrentCamera(int stereoSide) const;
} _transform;
using Pair = std::pair<size_t, size_t>;
using List = std::list<Pair>;
List _cameraOffsets;
mutable List::const_iterator _camerasItr;
mutable size_t _currentCameraOffset{ INVALID_OFFSET };
virtual void transferTransformState(const Batch& batch) const = 0;
void preUpdate(size_t commandIndex, const StereoState& stereo);
void update(size_t commandIndex, const StereoState& stereo) const;
void bindCurrentCamera(int stereoSide) const;
} _transform;
struct UniformStageState {
std::array<BufferPointer, MAX_NUM_UNIFORM_BUFFERS> _buffers;
//Buffers _buffers { };
} _uniform;
virtual void transferTransformState(const Batch& batch) const = 0;
void releaseUniformBuffer(uint32_t slot);
void resetUniformStage();
struct UniformStageState {
std::array<BufferPointer, MAX_NUM_UNIFORM_BUFFERS> _buffers;
//Buffers _buffers { };
} _uniform;
// update resource cache and do the gl bind/unbind call with the current gpu::Buffer cached at slot s
// This is using different gl object depending on the gl version
virtual bool bindResourceBuffer(uint32_t slot, BufferPointer& buffer) = 0;
virtual void releaseResourceBuffer(uint32_t slot) = 0;
void releaseUniformBuffer(uint32_t slot);
void resetUniformStage();
// update resource cache and do the gl unbind call with the current gpu::Texture cached at slot s
void releaseResourceTexture(uint32_t slot);
// update resource cache and do the gl bind/unbind call with the current gpu::Buffer cached at slot s
// This is using different gl object depending on the gl version
virtual bool bindResourceBuffer(uint32_t slot, BufferPointer& buffer) = 0;
virtual void releaseResourceBuffer(uint32_t slot) = 0;
void resetResourceStage();
// update resource cache and do the gl unbind call with the current gpu::Texture cached at slot s
void releaseResourceTexture(uint32_t slot);
struct ResourceStageState {
std::array<BufferPointer, MAX_NUM_RESOURCE_BUFFERS> _buffers;
std::array<TexturePointer, MAX_NUM_RESOURCE_TEXTURES> _textures;
//Textures _textures { { MAX_NUM_RESOURCE_TEXTURES } };
int findEmptyTextureSlot() const;
} _resource;
void resetResourceStage();
size_t _commandIndex{ 0 };
struct ResourceStageState {
std::array<BufferPointer, MAX_NUM_RESOURCE_BUFFERS> _buffers;
std::array<TexturePointer, MAX_NUM_RESOURCE_TEXTURES> _textures;
//Textures _textures { { MAX_NUM_RESOURCE_TEXTURES } };
int findEmptyTextureSlot() const;
} _resource;
// Standard update pipeline check that the current Program and current State or good to go for a
void updatePipeline();
// Force to reset all the state fields indicated by the 'toBeReset" signature
void resetPipelineState(State::Signature toBeReset);
// Synchronize the state cache of this Backend with the actual real state of the GL Context
void syncPipelineStateCache();
void resetPipelineStage();
size_t _commandIndex{ 0 };
struct PipelineStageState {
PipelinePointer _pipeline;
// Standard update pipeline check that the current Program and current State or good to go for a
void updatePipeline();
// Force to reset all the state fields indicated by the 'toBeReset" signature
void resetPipelineState(State::Signature toBeReset);
// Synchronize the state cache of this Backend with the actual real state of the GL Context
void syncPipelineStateCache();
void resetPipelineStage();
GLuint _program { 0 };
GLint _cameraCorrectionLocation { -1 };
GLShader* _programShader { nullptr };
bool _invalidProgram { false };
struct PipelineStageState {
PipelinePointer _pipeline;
BufferView _cameraCorrectionBuffer { gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(CameraCorrection), nullptr )) };
GLuint _program { 0 };
GLint _cameraCorrectionLocation { -1 };
GLShader* _programShader { nullptr };
bool _invalidProgram { false };
State::Data _stateCache{ State::DEFAULT };
State::Signature _stateSignatureCache { 0 };
BufferView _cameraCorrectionBuffer { gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(CameraCorrection), nullptr )) };
BufferView _cameraCorrectionBufferIdentity { gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(CameraCorrection), nullptr )) };
GLState* _state { nullptr };
bool _invalidState { false };
State::Data _stateCache{ State::DEFAULT };
State::Signature _stateSignatureCache { 0 };
PipelineStageState() {
_cameraCorrectionBuffer.edit<CameraCorrection>() = CameraCorrection();
}
} _pipeline;
GLState* _state { nullptr };
bool _invalidState { false };
// Synchronize the state cache of this Backend with the actual real state of the GL Context
void syncOutputStateCache();
void resetOutputStage();
PipelineStageState() {
_cameraCorrectionBuffer.edit<CameraCorrection>() = CameraCorrection();
_cameraCorrectionBufferIdentity.edit<CameraCorrection>() = CameraCorrection();
_cameraCorrectionBufferIdentity._buffer->flush();
}
} _pipeline;
struct OutputStageState {
FramebufferPointer _framebuffer { nullptr };
GLuint _drawFBO { 0 };
} _output;
// Backend dependant compilation of the shader
virtual GLShader* compileBackendProgram(const Shader& program);
virtual GLShader* compileBackendShader(const Shader& shader);
virtual std::string getBackendShaderHeader() const;
virtual void makeProgramBindings(ShaderObject& shaderObject);
class ElementResource {
public:
gpu::Element _element;
uint16 _resource;
ElementResource(Element&& elem, uint16 resource) : _element(elem), _resource(resource) {}
};
ElementResource getFormatFromGLUniform(GLenum gltype);
static const GLint UNUSED_SLOT {-1};
static bool isUnusedSlot(GLint binding) { return (binding == UNUSED_SLOT); }
virtual int makeUniformSlots(GLuint glprogram, const Shader::BindingSet& slotBindings,
Shader::SlotSet& uniforms, Shader::SlotSet& textures, Shader::SlotSet& samplers);
virtual int makeUniformBlockSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& buffers);
virtual int makeResourceBufferSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& resourceBuffers) = 0;
virtual int makeInputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& inputs);
virtual int makeOutputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& outputs);
void resetQueryStage();
struct QueryStageState {
uint32_t _rangeQueryDepth { 0 };
} _queryStage;
void resetStages();
// Synchronize the state cache of this Backend with the actual real state of the GL Context
void syncOutputStateCache();
void resetOutputStage();
struct OutputStageState {
FramebufferPointer _framebuffer { nullptr };
GLuint _drawFBO { 0 };
} _output;
struct TextureManagementStageState {
bool _sparseCapable { false };
} _textureManagement;
virtual void initTextureManagementStage() {}
void resetQueryStage();
struct QueryStageState {
uint32_t _rangeQueryDepth { 0 };
} _queryStage;
typedef void (GLBackend::*CommandCall)(const Batch&, size_t);
static CommandCall _commandCalls[Batch::NUM_COMMANDS];
friend class GLState;
friend class GLTexture;
};
void resetStages();
} }
struct TextureManagementStageState {
bool _sparseCapable { false };
} _textureManagement;
virtual void initTextureManagementStage() {}
typedef void (GLBackend::*CommandCall)(const Batch&, size_t);
static CommandCall _commandCalls[Batch::NUM_COMMANDS];
friend class GLState;
friend class GLTexture;
friend class GLShader;
};
} }
#endif

View file

@ -0,0 +1,530 @@
//
// Created by Gabriel Calero & Cristian Duarte on 2017/12/28
// Copyright 2013-2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "GLBackend.h"
#include "GLShader.h"
#include <gl/GLShaders.h>
using namespace gpu;
using namespace gpu::gl;
// GLSL version
std::string GLBackend::getBackendShaderHeader() const {
return std::string("#version 310 es");
}
// Shader domain
static const size_t NUM_SHADER_DOMAINS = 2;
// GL Shader type enums
// Must match the order of type specified in gpu::Shader::Type
static const std::array<GLenum, NUM_SHADER_DOMAINS> SHADER_DOMAINS { {
GL_VERTEX_SHADER,
GL_FRAGMENT_SHADER,
// GL_GEOMETRY_SHADER,
} };
// Domain specific defines
// Must match the order of type specified in gpu::Shader::Type
static const std::array<std::string, NUM_SHADER_DOMAINS> DOMAIN_DEFINES { {
"#define GPU_VERTEX_SHADER",
"#define GPU_PIXEL_SHADER",
// "#define GPU_GEOMETRY_SHADER",
} };
// Stereo specific defines
static const std::string stereoVersion {
#ifdef GPU_STEREO_DRAWCALL_INSTANCED
"#define GPU_TRANSFORM_IS_STEREO\n#define GPU_TRANSFORM_STEREO_CAMERA\n#define GPU_TRANSFORM_STEREO_CAMERA_INSTANCED\n#define GPU_TRANSFORM_STEREO_SPLIT_SCREEN"
#endif
#ifdef GPU_STEREO_DRAWCALL_DOUBLED
#ifdef GPU_STEREO_CAMERA_BUFFER
"#define GPU_TRANSFORM_IS_STEREO\n#define GPU_TRANSFORM_STEREO_CAMERA\n#define GPU_TRANSFORM_STEREO_CAMERA_ATTRIBUTED"
#else
"#define GPU_TRANSFORM_IS_STEREO"
#endif
#endif
};
// Versions specific of the shader
static const std::array<std::string, GLShader::NumVersions> VERSION_DEFINES { {
"",
stereoVersion
} };
GLShader* GLBackend::compileBackendShader(const Shader& shader) {
// Any GLSLprogram ? normally yes...
const std::string& shaderSource = shader.getSource().getCode();
GLenum shaderDomain = SHADER_DOMAINS[shader.getType()];
GLShader::ShaderObjects shaderObjects;
for (int version = 0; version < GLShader::NumVersions; version++) {
auto& shaderObject = shaderObjects[version];
std::string shaderDefines = getBackendShaderHeader() + "\n" + DOMAIN_DEFINES[shader.getType()] + "\n" + VERSION_DEFINES[version]
+ "\n#extension GL_EXT_texture_buffer : enable"
+ "\nprecision lowp float; // check precision 2"
+ "\nprecision lowp samplerBuffer;"
+ "\nprecision lowp sampler2DShadow;";
std::string error;
#ifdef SEPARATE_PROGRAM
bool result = ::gl::compileShader(shaderDomain, shaderSource, shaderDefines, shaderObject.glshader, shaderObject.glprogram, error);
#else
bool result = ::gl::compileShader(shaderDomain, shaderSource, shaderDefines, shaderObject.glshader, error);
#endif
if (!result) {
qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - Shader didn't compile:\n" << error.c_str();
return nullptr;
}
}
// So far so good, the shader is created successfully
GLShader* object = new GLShader(this->shared_from_this());
object->_shaderObjects = shaderObjects;
return object;
}
GLShader* GLBackend::compileBackendProgram(const Shader& program) {
if (!program.isProgram()) {
return nullptr;
}
GLShader::ShaderObjects programObjects;
for (int version = 0; version < GLShader::NumVersions; version++) {
auto& programObject = programObjects[version];
// Let's go through every shaders and make sure they are ready to go
std::vector< GLuint > shaderGLObjects;
for (auto subShader : program.getShaders()) {
auto object = GLShader::sync((*this), *subShader);
if (object) {
shaderGLObjects.push_back(object->_shaderObjects[version].glshader);
} else {
qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - One of the shaders of the program is not compiled?";
return nullptr;
}
}
std::string error;
GLuint glprogram = ::gl::compileProgram(shaderGLObjects, error);
if (glprogram == 0) {
qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - Program didn't link:\n" << error.c_str();
return nullptr;
}
programObject.glprogram = glprogram;
makeProgramBindings(programObject);
}
// So far so good, the program versions have all been created successfully
GLShader* object = new GLShader(this->shared_from_this());
object->_shaderObjects = programObjects;
return object;
}
GLBackend::ElementResource GLBackend::getFormatFromGLUniform(GLenum gltype) {
switch (gltype) {
case GL_FLOAT: return ElementResource(Element(SCALAR, gpu::FLOAT, UNIFORM), Resource::BUFFER);
case GL_FLOAT_VEC2: return ElementResource(Element(VEC2, gpu::FLOAT, UNIFORM), Resource::BUFFER);
case GL_FLOAT_VEC3: return ElementResource(Element(VEC3, gpu::FLOAT, UNIFORM), Resource::BUFFER);
case GL_FLOAT_VEC4: return ElementResource(Element(VEC4, gpu::FLOAT, UNIFORM), Resource::BUFFER);
/*
case GL_DOUBLE: return ElementResource(Element(SCALAR, gpu::FLOAT, UNIFORM), Resource::BUFFER);
case GL_DOUBLE_VEC2: return ElementResource(Element(VEC2, gpu::FLOAT, UNIFORM), Resource::BUFFER);
case GL_DOUBLE_VEC3: return ElementResource(Element(VEC3, gpu::FLOAT, UNIFORM), Resource::BUFFER);
case GL_DOUBLE_VEC4: return ElementResource(Element(VEC4, gpu::FLOAT, UNIFORM), Resource::BUFFER);
*/
case GL_INT: return ElementResource(Element(SCALAR, gpu::INT32, UNIFORM), Resource::BUFFER);
case GL_INT_VEC2: return ElementResource(Element(VEC2, gpu::INT32, UNIFORM), Resource::BUFFER);
case GL_INT_VEC3: return ElementResource(Element(VEC3, gpu::INT32, UNIFORM), Resource::BUFFER);
case GL_INT_VEC4: return ElementResource(Element(VEC4, gpu::INT32, UNIFORM), Resource::BUFFER);
case GL_UNSIGNED_INT: return ElementResource(Element(SCALAR, gpu::UINT32, UNIFORM), Resource::BUFFER);
#if defined(Q_OS_WIN)
case GL_UNSIGNED_INT_VEC2: return ElementResource(Element(VEC2, gpu::UINT32, UNIFORM), Resource::BUFFER);
case GL_UNSIGNED_INT_VEC3: return ElementResource(Element(VEC3, gpu::UINT32, UNIFORM), Resource::BUFFER);
case GL_UNSIGNED_INT_VEC4: return ElementResource(Element(VEC4, gpu::UINT32, UNIFORM), Resource::BUFFER);
#endif
case GL_BOOL: return ElementResource(Element(SCALAR, gpu::BOOL, UNIFORM), Resource::BUFFER);
case GL_BOOL_VEC2: return ElementResource(Element(VEC2, gpu::BOOL, UNIFORM), Resource::BUFFER);
case GL_BOOL_VEC3: return ElementResource(Element(VEC3, gpu::BOOL, UNIFORM), Resource::BUFFER);
case GL_BOOL_VEC4: return ElementResource(Element(VEC4, gpu::BOOL, UNIFORM), Resource::BUFFER);
case GL_FLOAT_MAT2: return ElementResource(Element(gpu::MAT2, gpu::FLOAT, UNIFORM), Resource::BUFFER);
case GL_FLOAT_MAT3: return ElementResource(Element(MAT3, gpu::FLOAT, UNIFORM), Resource::BUFFER);
case GL_FLOAT_MAT4: return ElementResource(Element(MAT4, gpu::FLOAT, UNIFORM), Resource::BUFFER);
/* {GL_FLOAT_MAT2x3 mat2x3},
{GL_FLOAT_MAT2x4 mat2x4},
{GL_FLOAT_MAT3x2 mat3x2},
{GL_FLOAT_MAT3x4 mat3x4},
{GL_FLOAT_MAT4x2 mat4x2},
{GL_FLOAT_MAT4x3 mat4x3},
{GL_DOUBLE_MAT2 dmat2},
{GL_DOUBLE_MAT3 dmat3},
{GL_DOUBLE_MAT4 dmat4},
{GL_DOUBLE_MAT2x3 dmat2x3},
{GL_DOUBLE_MAT2x4 dmat2x4},
{GL_DOUBLE_MAT3x2 dmat3x2},
{GL_DOUBLE_MAT3x4 dmat3x4},
{GL_DOUBLE_MAT4x2 dmat4x2},
{GL_DOUBLE_MAT4x3 dmat4x3},
*/
//case GL_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_1D);
case GL_SAMPLER_2D: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_2D);
case GL_SAMPLER_3D: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_3D);
case GL_SAMPLER_CUBE: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_CUBE);
#if defined(Q_OS_WIN)
case GL_SAMPLER_2D_MULTISAMPLE: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D);
case GL_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_1D_ARRAY);
case GL_SAMPLER_2D_ARRAY: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_2D_ARRAY);
case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY);
#endif
case GL_SAMPLER_2D_SHADOW: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_2D);
#if defined(Q_OS_WIN)
case GL_SAMPLER_CUBE_SHADOW: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_CUBE);
case GL_SAMPLER_2D_ARRAY_SHADOW: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_2D_ARRAY);
#endif
// {GL_SAMPLER_1D_SHADOW sampler1DShadow},
// {GL_SAMPLER_1D_ARRAY_SHADOW sampler1DArrayShadow},
case GL_SAMPLER_BUFFER: return ElementResource(Element(SCALAR, gpu::FLOAT, RESOURCE_BUFFER), Resource::BUFFER);
// {GL_SAMPLER_2D_RECT sampler2DRect},
// {GL_SAMPLER_2D_RECT_SHADOW sampler2DRectShadow},
#if defined(Q_OS_WIN)
case GL_INT_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_1D);
case GL_INT_SAMPLER_2D: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_2D);
case GL_INT_SAMPLER_2D_MULTISAMPLE: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D);
case GL_INT_SAMPLER_3D: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_3D);
case GL_INT_SAMPLER_CUBE: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_CUBE);
case GL_INT_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_1D_ARRAY);
case GL_INT_SAMPLER_2D_ARRAY: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_2D_ARRAY);
case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY);
// {GL_INT_SAMPLER_BUFFER isamplerBuffer},
// {GL_INT_SAMPLER_2D_RECT isampler2DRect},
case GL_UNSIGNED_INT_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_1D);
case GL_UNSIGNED_INT_SAMPLER_2D: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_2D);
case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D);
case GL_UNSIGNED_INT_SAMPLER_3D: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_3D);
case GL_UNSIGNED_INT_SAMPLER_CUBE: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_CUBE);
case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_1D_ARRAY);
case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_2D_ARRAY);
case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY);
#endif
// {GL_UNSIGNED_INT_SAMPLER_BUFFER usamplerBuffer},
// {GL_UNSIGNED_INT_SAMPLER_2D_RECT usampler2DRect},
/*
{GL_IMAGE_1D image1D},
{GL_IMAGE_2D image2D},
{GL_IMAGE_3D image3D},
{GL_IMAGE_2D_RECT image2DRect},
{GL_IMAGE_CUBE imageCube},
{GL_IMAGE_BUFFER imageBuffer},
{GL_IMAGE_1D_ARRAY image1DArray},
{GL_IMAGE_2D_ARRAY image2DArray},
{GL_IMAGE_2D_MULTISAMPLE image2DMS},
{GL_IMAGE_2D_MULTISAMPLE_ARRAY image2DMSArray},
{GL_INT_IMAGE_1D iimage1D},
{GL_INT_IMAGE_2D iimage2D},
{GL_INT_IMAGE_3D iimage3D},
{GL_INT_IMAGE_2D_RECT iimage2DRect},
{GL_INT_IMAGE_CUBE iimageCube},
{GL_INT_IMAGE_BUFFER iimageBuffer},
{GL_INT_IMAGE_1D_ARRAY iimage1DArray},
{GL_INT_IMAGE_2D_ARRAY iimage2DArray},
{GL_INT_IMAGE_2D_MULTISAMPLE iimage2DMS},
{GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY iimage2DMSArray},
{GL_UNSIGNED_INT_IMAGE_1D uimage1D},
{GL_UNSIGNED_INT_IMAGE_2D uimage2D},
{GL_UNSIGNED_INT_IMAGE_3D uimage3D},
{GL_UNSIGNED_INT_IMAGE_2D_RECT uimage2DRect},
{GL_UNSIGNED_INT_IMAGE_CUBE uimageCube},+ [0] {_name="fInnerRadius" _location=0 _element={_semantic=15 '\xf' _dimension=0 '\0' _type=0 '\0' } } gpu::Shader::Slot
{GL_UNSIGNED_INT_IMAGE_BUFFER uimageBuffer},
{GL_UNSIGNED_INT_IMAGE_1D_ARRAY uimage1DArray},
{GL_UNSIGNED_INT_IMAGE_2D_ARRAY uimage2DArray},
{GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE uimage2DMS},
{GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY uimage2DMSArray},
{GL_UNSIGNED_INT_ATOMIC_COUNTER atomic_uint}
*/
default:
return ElementResource(Element(), Resource::BUFFER);
}
};
int GLBackend::makeUniformSlots(GLuint glprogram, const Shader::BindingSet& slotBindings,
Shader::SlotSet& uniforms, Shader::SlotSet& textures, Shader::SlotSet& samplers) {
GLint uniformsCount = 0;
glGetProgramiv(glprogram, GL_ACTIVE_UNIFORMS, &uniformsCount);
for (int i = 0; i < uniformsCount; i++) {
const GLint NAME_LENGTH = 256;
GLchar name[NAME_LENGTH];
GLint length = 0;
GLint size = 0;
GLenum type = 0;
glGetActiveUniform(glprogram, i, NAME_LENGTH, &length, &size, &type, name);
GLint location = glGetUniformLocation(glprogram, name);
const GLint INVALID_UNIFORM_LOCATION = -1;
// Try to make sense of the gltype
auto elementResource = getFormatFromGLUniform(type);
// The uniform as a standard var type
if (location != INVALID_UNIFORM_LOCATION) {
// Let's make sure the name doesn't contains an array element
std::string sname(name);
auto foundBracket = sname.find_first_of('[');
if (foundBracket != std::string::npos) {
// std::string arrayname = sname.substr(0, foundBracket);
if (sname[foundBracket + 1] == '0') {
sname = sname.substr(0, foundBracket);
} else {
// skip this uniform since it's not the first element of an array
continue;
}
}
if (elementResource._resource == Resource::BUFFER) {
uniforms.insert(Shader::Slot(sname, location, elementResource._element, elementResource._resource));
} else {
// For texture/Sampler, the location is the actual binding value
GLint binding = -1;
glGetUniformiv(glprogram, location, &binding);
auto requestedBinding = slotBindings.find(std::string(sname));
if (requestedBinding != slotBindings.end()) {
if (binding != (*requestedBinding)._location) {
binding = (*requestedBinding)._location;
for (auto i = 0; i < size; i++) {
// If we are working with an array of textures, reserve for each elemet
glProgramUniform1i(glprogram, location+i, binding+i);
}
}
}
textures.insert(Shader::Slot(name, binding, elementResource._element, elementResource._resource));
samplers.insert(Shader::Slot(name, binding, elementResource._element, elementResource._resource));
}
}
}
return uniformsCount;
}
int GLBackend::makeUniformBlockSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& buffers) {
GLint buffersCount = 0;
glGetProgramiv(glprogram, GL_ACTIVE_UNIFORM_BLOCKS, &buffersCount);
// fast exit
if (buffersCount == 0) {
return 0;
}
GLint maxNumUniformBufferSlots = 0;
glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &maxNumUniformBufferSlots);
std::vector<GLint> uniformBufferSlotMap(maxNumUniformBufferSlots, -1);
struct UniformBlockInfo {
using Vector = std::vector<UniformBlockInfo>;
const GLuint index{ 0 };
const std::string name;
GLint binding{ -1 };
GLint size{ 0 };
static std::string getName(GLuint glprogram, GLuint i) {
static const GLint NAME_LENGTH = 256;
GLint length = 0;
GLchar nameBuffer[NAME_LENGTH];
glGetActiveUniformBlockiv(glprogram, i, GL_UNIFORM_BLOCK_NAME_LENGTH, &length);
glGetActiveUniformBlockName(glprogram, i, NAME_LENGTH, &length, nameBuffer);
return std::string(nameBuffer);
}
UniformBlockInfo(GLuint glprogram, GLuint i) : index(i), name(getName(glprogram, i)) {
glGetActiveUniformBlockiv(glprogram, index, GL_UNIFORM_BLOCK_BINDING, &binding);
glGetActiveUniformBlockiv(glprogram, index, GL_UNIFORM_BLOCK_DATA_SIZE, &size);
}
};
UniformBlockInfo::Vector uniformBlocks;
uniformBlocks.reserve(buffersCount);
for (int i = 0; i < buffersCount; i++) {
uniformBlocks.push_back(UniformBlockInfo(glprogram, i));
}
for (auto& info : uniformBlocks) {
auto requestedBinding = slotBindings.find(info.name);
if (requestedBinding != slotBindings.end()) {
info.binding = (*requestedBinding)._location;
glUniformBlockBinding(glprogram, info.index, info.binding);
uniformBufferSlotMap[info.binding] = info.index;
}
}
for (auto& info : uniformBlocks) {
if (slotBindings.count(info.name)) {
continue;
}
// If the binding is 0, or the binding maps to an already used binding
if (info.binding == 0 || !isUnusedSlot(uniformBufferSlotMap[info.binding])) {
// If no binding was assigned then just do it finding a free slot
auto slotIt = std::find_if(uniformBufferSlotMap.begin(), uniformBufferSlotMap.end(), GLBackend::isUnusedSlot);
if (slotIt != uniformBufferSlotMap.end()) {
info.binding = slotIt - uniformBufferSlotMap.begin();
glUniformBlockBinding(glprogram, info.index, info.binding);
} else {
// This should neve happen, an active ubo cannot find an available slot among the max available?!
info.binding = -1;
}
}
uniformBufferSlotMap[info.binding] = info.index;
}
for (auto& info : uniformBlocks) {
static const Element element(SCALAR, gpu::UINT32, gpu::UNIFORM_BUFFER);
buffers.insert(Shader::Slot(info.name, info.binding, element, Resource::BUFFER, info.size));
}
return buffersCount;
}
int GLBackend::makeInputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& inputs) {
GLint inputsCount = 0;
glGetProgramiv(glprogram, GL_ACTIVE_ATTRIBUTES, &inputsCount);
for (int i = 0; i < inputsCount; i++) {
const GLint NAME_LENGTH = 256;
GLchar name[NAME_LENGTH];
GLint length = 0;
GLint size = 0;
GLenum type = 0;
glGetActiveAttrib(glprogram, i, NAME_LENGTH, &length, &size, &type, name);
GLint binding = glGetAttribLocation(glprogram, name);
auto elementResource = getFormatFromGLUniform(type);
inputs.insert(Shader::Slot(name, binding, elementResource._element, -1));
}
return inputsCount;
}
int GLBackend::makeOutputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& outputs) {
/* GLint outputsCount = 0;
glGetProgramiv(glprogram, GL_ACTIVE_, &outputsCount);
for (int i = 0; i < inputsCount; i++) {
const GLint NAME_LENGTH = 256;
GLchar name[NAME_LENGTH];
GLint length = 0;
GLint size = 0;
GLenum type = 0;
glGetActiveAttrib(glprogram, i, NAME_LENGTH, &length, &size, &type, name);
auto element = getFormatFromGLUniform(type);
outputs.insert(Shader::Slot(name, i, element));
}
*/
return 0; //inputsCount;
}
void GLBackend::makeProgramBindings(ShaderObject& shaderObject) {
if (!shaderObject.glprogram) {
return;
}
GLuint glprogram = shaderObject.glprogram;
GLint loc = -1;
//Check for gpu specific attribute slotBindings
loc = glGetAttribLocation(glprogram, "inPosition");
if (loc >= 0 && loc != gpu::Stream::POSITION) {
glBindAttribLocation(glprogram, gpu::Stream::POSITION, "inPosition");
}
loc = glGetAttribLocation(glprogram, "inNormal");
if (loc >= 0 && loc != gpu::Stream::NORMAL) {
glBindAttribLocation(glprogram, gpu::Stream::NORMAL, "inNormal");
}
loc = glGetAttribLocation(glprogram, "inColor");
if (loc >= 0 && loc != gpu::Stream::COLOR) {
glBindAttribLocation(glprogram, gpu::Stream::COLOR, "inColor");
}
loc = glGetAttribLocation(glprogram, "inTexCoord0");
if (loc >= 0 && loc != gpu::Stream::TEXCOORD) {
glBindAttribLocation(glprogram, gpu::Stream::TEXCOORD, "inTexCoord0");
}
loc = glGetAttribLocation(glprogram, "inTangent");
if (loc >= 0 && loc != gpu::Stream::TANGENT) {
glBindAttribLocation(glprogram, gpu::Stream::TANGENT, "inTangent");
}
char attribName[] = "inTexCoordn";
for (auto i = 0; i < 4; i++) {
auto streamId = gpu::Stream::TEXCOORD1 + i;
attribName[strlen(attribName) - 1] = '1' + i;
loc = glGetAttribLocation(glprogram, attribName);
if (loc >= 0 && loc != streamId) {
glBindAttribLocation(glprogram, streamId, attribName);
}
}
loc = glGetAttribLocation(glprogram, "inSkinClusterIndex");
if (loc >= 0 && loc != gpu::Stream::SKIN_CLUSTER_INDEX) {
glBindAttribLocation(glprogram, gpu::Stream::SKIN_CLUSTER_INDEX, "inSkinClusterIndex");
}
loc = glGetAttribLocation(glprogram, "inSkinClusterWeight");
if (loc >= 0 && loc != gpu::Stream::SKIN_CLUSTER_WEIGHT) {
glBindAttribLocation(glprogram, gpu::Stream::SKIN_CLUSTER_WEIGHT, "inSkinClusterWeight");
}
loc = glGetAttribLocation(glprogram, "_drawCallInfo");
if (loc >= 0 && loc != gpu::Stream::DRAW_CALL_INFO) {
glBindAttribLocation(glprogram, gpu::Stream::DRAW_CALL_INFO, "_drawCallInfo");
}
// Link again to take into account the assigned attrib location
glLinkProgram(glprogram);
GLint linked = 0;
glGetProgramiv(glprogram, GL_LINK_STATUS, &linked);
if (!linked) {
qCWarning(gpugllogging) << "GLShader::makeBindings - failed to link after assigning slotBindings?";
}
}

View file

@ -15,13 +15,56 @@
using namespace gpu;
using namespace gpu::gl;
bool GLBackend::isTextureReady(const TexturePointer& texture) {
// DO not transfer the texture, this call is expected for rendering texture
GLTexture* object = syncGPUObject(texture, true);
qDebug() << "GLBackendTexture isTextureReady syncGPUObject";
return object && object->isReady();
GLuint GLBackend::getTextureID(const TexturePointer& texture) {
GLTexture* object = syncGPUObject(texture);
if (!object) {
return 0;
}
return object->_id;
}
GLTexture* GLBackend::syncGPUObject(const TexturePointer& texturePointer) {
const Texture& texture = *texturePointer;
// Special case external textures
if (TextureUsageType::EXTERNAL == texture.getUsageType()) {
Texture::ExternalUpdates updates = texture.getUpdates();
if (!updates.empty()) {
Texture::ExternalRecycler recycler = texture.getExternalRecycler();
Q_ASSERT(recycler);
// Discard any superfluous updates
while (updates.size() > 1) {
const auto& update = updates.front();
// Superfluous updates will never have been read, but we want to ensure the previous
// writes to them are complete before they're written again, so return them with the
// same fences they arrived with. This can happen on any thread because no GL context
// work is involved
recycler(update.first, update.second);
updates.pop_front();
}
// The last texture remaining is the one we'll use to create the GLTexture
const auto& update = updates.front();
// Check for a fence, and if it exists, inject a wait into the command stream, then destroy the fence
if (update.second) {
GLsync fence = static_cast<GLsync>(update.second);
glWaitSync(fence, 0, GL_TIMEOUT_IGNORED);
glDeleteSync(fence);
}
// Create the new texture object (replaces any previous texture object)
new GLExternalTexture(shared_from_this(), texture, update.first);
}
// Return the texture object (if any) associated with the texture, without extensive logic
// (external textures are
return Backend::getGPUObject<GLTexture>(texture);
}
return nullptr;
}
void GLBackend::do_generateTextureMips(const Batch& batch, size_t paramOffset) {
TexturePointer resourceTexture = batch._textures.get(batch._params[paramOffset + 0]._uint);
@ -30,7 +73,7 @@ void GLBackend::do_generateTextureMips(const Batch& batch, size_t paramOffset) {
}
// DO not transfer the texture, this call is expected for rendering texture
GLTexture* object = syncGPUObject(resourceTexture, false);
GLTexture* object = syncGPUObject(resourceTexture);
qDebug() << "GLBackendTexture do_generateTextureMips syncGPUObject";
if (!object) {
return;

View file

@ -38,7 +38,7 @@ void GLBackend::do_setViewportTransform(const Batch& batch, size_t paramOffset)
glViewport(vp.x, vp.y, vp.z, vp.w);
// Where we assign the GL viewport
if (_stereo._enable) {
if (_stereo.isStereo()) {
vp.z /= 2;
if (_stereo._pass) {
vp.x += vp.z;
@ -103,7 +103,7 @@ void GLBackend::TransformStageState::preUpdate(size_t commandIndex, const Stereo
if (_invalidView) {
// Apply the correction
if (_viewIsCamera && _correction.correction != glm::mat4()) {
if (_viewIsCamera && (_viewCorrectionEnabled && _correction.correction != glm::mat4())) {
// FIXME should I switch to using the camera correction buffer in Transform.slf and leave this out?
Transform result;
_view.mult(result, _view, _correction.correction);
@ -120,7 +120,7 @@ void GLBackend::TransformStageState::preUpdate(size_t commandIndex, const Stereo
size_t offset = _cameraUboSize * _cameras.size();
_cameraOffsets.push_back(TransformStageState::Pair(commandIndex, offset));
if (stereo._enable) {
if (stereo.isStereo()) {
#ifdef GPU_STEREO_CAMERA_BUFFER
_cameras.push_back(CameraBufferElement(_camera.getEyeCamera(0, stereo, _view), _camera.getEyeCamera(1, stereo, _view)));
#else
@ -152,7 +152,7 @@ void GLBackend::TransformStageState::update(size_t commandIndex, const StereoSta
#ifdef GPU_STEREO_CAMERA_BUFFER
bindCurrentCamera(0);
#else
if (!stereo._enable) {
if (!stereo.isStereo()) {
bindCurrentCamera(0);
}
#endif
@ -162,51 +162,11 @@ void GLBackend::TransformStageState::update(size_t commandIndex, const StereoSta
void GLBackend::TransformStageState::bindCurrentCamera(int eye) const {
if (_currentCameraOffset != INVALID_OFFSET) {
//qDebug() << "GLBackend::TransformStageState::bindCurrentCamera";
glBindBufferRange(GL_UNIFORM_BUFFER, TRANSFORM_CAMERA_SLOT, _cameraBuffer, _currentCameraOffset + eye * _cameraUboSize, sizeof(CameraBufferElement));
}
}
void GLBackend::updateTransform(const Batch& batch) {
_transform.update(_commandIndex, _stereo);
auto& drawCallInfoBuffer = batch.getDrawCallInfoBuffer();
if (batch._currentNamedCall.empty()) {
(void)CHECK_GL_ERROR();
auto& drawCallInfo = drawCallInfoBuffer[_currentDraw];
glDisableVertexAttribArray(gpu::Stream::DRAW_CALL_INFO); // Make sure attrib array is disabled
(void)CHECK_GL_ERROR();
GLint current_vao, current_vbo, maxVertexAtribs;
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &current_vao);
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &current_vbo);
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxVertexAtribs);
glVertexAttribI4i(gpu::Stream::DRAW_CALL_INFO, drawCallInfo.index, drawCallInfo.unused, 0, 0);
//int values[] = {drawCallInfo.index, drawCallInfo.unused};
//glVertexAttribIPointer(gpu::Stream::DRAW_CALL_INFO, 2, GL_INT, 0, (const GLvoid *) values);
/*
//glDisableVertexAttribArray currentvao 1 current vbo 0
GL_INVALID_OPERATION is generated
a non-zero vertex array object is bound,
zero is bound to the GL_ARRAY_BUFFER buffer object binding point and
the pointer argument is not NULL. TRUE
*/
//qDebug() << "GLBackend::updateTransform glVertexAttribIPointer done";
(void)CHECK_GL_ERROR();
} else {
//qDebug() << "GLBackend::updateTransform else";
glEnableVertexAttribArray(gpu::Stream::DRAW_CALL_INFO); // Make sure attrib array is enabled
glBindBuffer(GL_ARRAY_BUFFER, _transform._drawCallInfoBuffer);
glVertexAttribIPointer(gpu::Stream::DRAW_CALL_INFO, 2, GL_UNSIGNED_SHORT, 0,
_transform._drawCallInfoOffsets[batch._currentNamedCall]);
glVertexAttribDivisor(gpu::Stream::DRAW_CALL_INFO, 1);
}
(void)CHECK_GL_ERROR();
}
void GLBackend::resetTransformStage() {
glDisableVertexAttribArray(gpu::Stream::DRAW_CALL_INFO);
_transform._enabledDrawcallInfoBuffer = false;
}

View file

@ -21,7 +21,7 @@ GLFramebuffer::~GLFramebuffer() {
}
}
bool GLFramebuffer::checkStatus(GLenum target) const {
bool GLFramebuffer::checkStatus() const {
bool result = false;
switch (_status) {
case GL_FRAMEBUFFER_COMPLETE:

View file

@ -64,7 +64,7 @@ public:
protected:
GLenum _status { GL_FRAMEBUFFER_COMPLETE };
virtual void update() = 0;
bool checkStatus(GLenum target) const;
bool checkStatus() const;
GLFramebuffer(const std::weak_ptr<GLBackend>& backend, const Framebuffer& framebuffer, GLuint id) : GLObject(backend, framebuffer, id) {}
~GLFramebuffer();

View file

@ -30,126 +30,6 @@ GLShader::~GLShader() {
}
}
// GLSL version
static const std::string glslVersion {
"#version 310 es"
};
// Shader domain
static const size_t NUM_SHADER_DOMAINS = 3;
// GL Shader type enums
// Must match the order of type specified in gpu::Shader::Type
static const std::array<GLenum, NUM_SHADER_DOMAINS> SHADER_DOMAINS { {
GL_VERTEX_SHADER,
GL_FRAGMENT_SHADER,
//GL_GEOMETRY_SHADER,
} };
// Domain specific defines
// Must match the order of type specified in gpu::Shader::Type
static const std::array<std::string, NUM_SHADER_DOMAINS> DOMAIN_DEFINES { {
"#define GPU_VERTEX_SHADER",
"#define GPU_PIXEL_SHADER",
"#define GPU_GEOMETRY_SHADER",
} };
// Stereo specific defines
static const std::string stereoVersion {
#ifdef GPU_STEREO_DRAWCALL_INSTANCED
"#define GPU_TRANSFORM_IS_STEREO\n#define GPU_TRANSFORM_STEREO_CAMERA\n#define GPU_TRANSFORM_STEREO_CAMERA_INSTANCED\n#define GPU_TRANSFORM_STEREO_SPLIT_SCREEN"
#endif
#ifdef GPU_STEREO_DRAWCALL_DOUBLED
#ifdef GPU_STEREO_CAMERA_BUFFER
"#define GPU_TRANSFORM_IS_STEREO\n#define GPU_TRANSFORM_STEREO_CAMERA\n#define GPU_TRANSFORM_STEREO_CAMERA_ATTRIBUTED"
#else
"#define GPU_TRANSFORM_IS_STEREO"
#endif
#endif
};
// Versions specific of the shader
static const std::array<std::string, GLShader::NumVersions> VERSION_DEFINES { {
"",
stereoVersion
} };
GLShader* compileBackendShader(GLBackend& backend, const Shader& shader) {
// Any GLSLprogram ? normally yes...
const std::string& shaderSource = shader.getSource().getCode();
GLenum shaderDomain = SHADER_DOMAINS[shader.getType()];
GLShader::ShaderObjects shaderObjects;
for (int version = 0; version < GLShader::NumVersions; version++) {
auto& shaderObject = shaderObjects[version];
std::string shaderDefines = glslVersion + "\n" + DOMAIN_DEFINES[shader.getType()] + "\n" + VERSION_DEFINES[version]
+ "\n" + "#extension GL_EXT_texture_buffer : enable"
+ "\nprecision lowp float; // check precision 2"
+ "\nprecision lowp samplerBuffer;"
+ "\nprecision lowp sampler2DShadow;";
// TODO Delete bool result = compileShader(shaderDomain, shaderSource, shaderDefines, shaderObject.glshader, shaderObject.glprogram);
std::string error;
#ifdef SEPARATE_PROGRAM
bool result = ::gl::compileShader(shaderDomain, shaderSource.c_str(), shaderDefines.c_str(), shaderObject.glshader, shaderObject.glprogram, error);
#else
bool result = ::gl::compileShader(shaderDomain, shaderSource, shaderDefines, shaderObject.glshader, error);
#endif
if (!result) {
qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - Shader didn't compile:\n" << error.c_str();
return nullptr;
}
}
// So far so good, the shader is created successfully
GLShader* object = new GLShader(backend.shared_from_this());
object->_shaderObjects = shaderObjects;
return object;
}
GLShader* compileBackendProgram(GLBackend& backend, const Shader& program) {
if (!program.isProgram()) {
return nullptr;
}
GLShader::ShaderObjects programObjects;
for (int version = 0; version < GLShader::NumVersions; version++) {
auto& programObject = programObjects[version];
// Let's go through every shaders and make sure they are ready to go
std::vector< GLuint > shaderGLObjects;
for (auto subShader : program.getShaders()) {
auto object = GLShader::sync(backend, *subShader);
if (object) {
shaderGLObjects.push_back(object->_shaderObjects[version].glshader);
} else {
qCDebug(gpugllogging) << "GLShader::compileBackendProgram - One of the shaders of the program is not compiled?";
return nullptr;
}
}
std::string error;
GLuint glprogram = ::gl::compileProgram(shaderGLObjects, error);
if (glprogram == 0) {
qCWarning(gpugllogging) << error.c_str();
return nullptr;
}
programObject.glprogram = glprogram;
makeProgramBindings(programObject);
}
// So far so good, the program versions have all been created successfully
GLShader* object = new GLShader(backend.shared_from_this());
object->_shaderObjects = programObjects;
return object;
}
GLShader* GLShader::sync(GLBackend& backend, const Shader& shader) {
GLShader* object = Backend::getGPUObject<GLShader>(shader);
@ -159,13 +39,13 @@ GLShader* GLShader::sync(GLBackend& backend, const Shader& shader) {
}
// need to have a gpu object?
if (shader.isProgram()) {
GLShader* tempObject = compileBackendProgram(backend, shader);
GLShader* tempObject = backend.compileBackendProgram(shader);
if (tempObject) {
object = tempObject;
Backend::setGPUObject(shader, object);
}
} else if (shader.isDomain()) {
GLShader* tempObject = compileBackendShader(backend, shader);
GLShader* tempObject = backend.compileBackendShader(shader);
if (tempObject) {
object = tempObject;
Backend::setGPUObject(shader, object);
@ -189,21 +69,21 @@ bool GLShader::makeProgram(GLBackend& backend, Shader& shader, const Shader::Bin
auto& shaderObject = object->_shaderObjects[version];
if (shaderObject.glprogram) {
Shader::SlotSet buffers;
makeUniformBlockSlots(shaderObject.glprogram, slotBindings, buffers);
backend.makeUniformBlockSlots(shaderObject.glprogram, slotBindings, buffers);
Shader::SlotSet uniforms;
Shader::SlotSet textures;
Shader::SlotSet samplers;
makeUniformSlots(shaderObject.glprogram, slotBindings, uniforms, textures, samplers);
backend.makeUniformSlots(shaderObject.glprogram, slotBindings, uniforms, textures, samplers);
Shader::SlotSet resourceBuffers;
makeResourceBufferSlots(shaderObject.glprogram, slotBindings, resourceBuffers);
backend.makeResourceBufferSlots(shaderObject.glprogram, slotBindings, resourceBuffers);
Shader::SlotSet inputs;
makeInputSlots(shaderObject.glprogram, slotBindings, inputs);
backend.makeInputSlots(shaderObject.glprogram, slotBindings, inputs);
Shader::SlotSet outputs;
makeOutputSlots(shaderObject.glprogram, slotBindings, outputs);
backend.makeOutputSlots(shaderObject.glprogram, slotBindings, outputs);
// Define the public slots only from the default version
if (version == 0) {
@ -222,3 +102,5 @@ bool GLShader::makeProgram(GLBackend& backend, Shader& shader, const Shader::Bin
return true;
}

View file

@ -12,6 +12,13 @@
namespace gpu { namespace gl {
struct ShaderObject {
GLuint glshader { 0 };
GLuint glprogram { 0 };
GLint transformCameraSlot { -1 };
GLint transformObjectSlot { -1 };
};
class GLShader : public GPUObject {
public:
static GLShader* sync(GLBackend& backend, const Shader& shader);

View file

@ -27,22 +27,22 @@ bool checkGLError(const char* name) {
} else {
switch (error) {
case GL_INVALID_ENUM:
qCDebug(gpugllogging) << "GLBackend::" << name << ": An unacceptable value is specified for an enumerated argument.The offending command is ignored and has no other side effect than to set the error flag.";
qCWarning(gpugllogging) << "GLBackend::" << name << ": An unacceptable value is specified for an enumerated argument.The offending command is ignored and has no other side effect than to set the error flag.";
break;
case GL_INVALID_VALUE:
qCDebug(gpugllogging) << "GLBackend" << name << ": A numeric argument is out of range.The offending command is ignored and has no other side effect than to set the error flag";
qCWarning(gpugllogging) << "GLBackend" << name << ": A numeric argument is out of range.The offending command is ignored and has no other side effect than to set the error flag";
break;
case GL_INVALID_OPERATION:
qCDebug(gpugllogging) << "GLBackend" << name << ": The specified operation is not allowed in the current state.The offending command is ignored and has no other side effect than to set the error flag..";
qCWarning(gpugllogging) << "GLBackend" << name << ": The specified operation is not allowed in the current state.The offending command is ignored and has no other side effect than to set the error flag..";
break;
case GL_INVALID_FRAMEBUFFER_OPERATION:
qCDebug(gpugllogging) << "GLBackend" << name << ": The framebuffer object is not complete.The offending command is ignored and has no other side effect than to set the error flag.";
qCWarning(gpugllogging) << "GLBackend" << name << ": The framebuffer object is not complete.The offending command is ignored and has no other side effect than to set the error flag.";
break;
case GL_OUT_OF_MEMORY:
qCDebug(gpugllogging) << "GLBackend" << name << ": There is not enough memory left to execute the command.The state of the GL is undefined, except for the state of the error flags, after this error is recorded.";
qCWarning(gpugllogging) << "GLBackend" << name << ": There is not enough memory left to execute the command.The state of the GL is undefined, except for the state of the error flags, after this error is recorded.";
break;
default:
qCDebug(gpugllogging) << "GLBackend" << name << ": Unknown error: " << error;
qCWarning(gpugllogging) << "GLBackend" << name << ": Unknown error: " << error;
break;
}
return true;
@ -53,7 +53,7 @@ bool checkGLErrorDebug(const char* name) {
#ifdef DEBUG
return checkGLError(name);
#else
Q_UNUSED(name);
Q_UNUSED(name);
return false;
#endif
}
@ -86,43 +86,6 @@ gpu::Size getFreeDedicatedMemory() {
return result;
}
gpu::Size getDedicatedMemory() {
static Size dedicatedMemory { 0 };
static std::once_flag once;
std::call_once(once, [&] {
if (!dedicatedMemory) {
GLint atiGpuMemory[4];
// not really total memory, but close enough if called early enough in the application lifecycle
//glGetIntegerv(GL_TEXTURE_FREE_MEMORY_ATI, atiGpuMemory);
qDebug() << "TODO: GLShared.cpp.cpp:initInput GL_TEXTURE_FREE_MEMORY_ATI";
if (GL_NO_ERROR == glGetError()) {
dedicatedMemory = KB_TO_BYTES(atiGpuMemory[0]);
}
}
if (!dedicatedMemory) {
GLint nvGpuMemory { 0 };
qDebug() << "TODO: GLShared.cpp.cpp:initInput GL_GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX";
//glGetIntegerv(GL_GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX, &nvGpuMemory);
if (GL_NO_ERROR == glGetError()) {
dedicatedMemory = KB_TO_BYTES(nvGpuMemory);
}
}
if (!dedicatedMemory) {
auto gpuIdent = GPUIdent::getInstance();
if (gpuIdent && gpuIdent->isValid()) {
dedicatedMemory = MB_TO_BYTES(gpuIdent->getMemory());
}
}
});
return dedicatedMemory;
}
ComparisonFunction comparisonFuncFromGL(GLenum func) {
if (func == GL_NEVER) {
return NEVER;
@ -354,504 +317,6 @@ void getCurrentGLState(State::Data& state) {
}
class ElementResource {
public:
gpu::Element _element;
uint16 _resource;
ElementResource(Element&& elem, uint16 resource) : _element(elem), _resource(resource) {}
};
ElementResource getFormatFromGLUniform(GLenum gltype) {
switch (gltype) {
case GL_FLOAT: return ElementResource(Element(SCALAR, gpu::FLOAT, UNIFORM), Resource::BUFFER);
case GL_FLOAT_VEC2: return ElementResource(Element(VEC2, gpu::FLOAT, UNIFORM), Resource::BUFFER);
case GL_FLOAT_VEC3: return ElementResource(Element(VEC3, gpu::FLOAT, UNIFORM), Resource::BUFFER);
case GL_FLOAT_VEC4: return ElementResource(Element(VEC4, gpu::FLOAT, UNIFORM), Resource::BUFFER);
/*
case GL_DOUBLE: return ElementResource(Element(SCALAR, gpu::FLOAT, UNIFORM), Resource::BUFFER);
case GL_DOUBLE_VEC2: return ElementResource(Element(VEC2, gpu::FLOAT, UNIFORM), Resource::BUFFER);
case GL_DOUBLE_VEC3: return ElementResource(Element(VEC3, gpu::FLOAT, UNIFORM), Resource::BUFFER);
case GL_DOUBLE_VEC4: return ElementResource(Element(VEC4, gpu::FLOAT, UNIFORM), Resource::BUFFER);
*/
case GL_INT: return ElementResource(Element(SCALAR, gpu::INT32, UNIFORM), Resource::BUFFER);
case GL_INT_VEC2: return ElementResource(Element(VEC2, gpu::INT32, UNIFORM), Resource::BUFFER);
case GL_INT_VEC3: return ElementResource(Element(VEC3, gpu::INT32, UNIFORM), Resource::BUFFER);
case GL_INT_VEC4: return ElementResource(Element(VEC4, gpu::INT32, UNIFORM), Resource::BUFFER);
case GL_UNSIGNED_INT: return ElementResource(Element(SCALAR, gpu::UINT32, UNIFORM), Resource::BUFFER);
#if defined(Q_OS_WIN)
case GL_UNSIGNED_INT_VEC2: return ElementResource(Element(VEC2, gpu::UINT32, UNIFORM), Resource::BUFFER);
case GL_UNSIGNED_INT_VEC3: return ElementResource(Element(VEC3, gpu::UINT32, UNIFORM), Resource::BUFFER);
case GL_UNSIGNED_INT_VEC4: return ElementResource(Element(VEC4, gpu::UINT32, UNIFORM), Resource::BUFFER);
#endif
case GL_BOOL: return ElementResource(Element(SCALAR, gpu::BOOL, UNIFORM), Resource::BUFFER);
case GL_BOOL_VEC2: return ElementResource(Element(VEC2, gpu::BOOL, UNIFORM), Resource::BUFFER);
case GL_BOOL_VEC3: return ElementResource(Element(VEC3, gpu::BOOL, UNIFORM), Resource::BUFFER);
case GL_BOOL_VEC4: return ElementResource(Element(VEC4, gpu::BOOL, UNIFORM), Resource::BUFFER);
case GL_FLOAT_MAT2: return ElementResource(Element(gpu::MAT2, gpu::FLOAT, UNIFORM), Resource::BUFFER);
case GL_FLOAT_MAT3: return ElementResource(Element(MAT3, gpu::FLOAT, UNIFORM), Resource::BUFFER);
case GL_FLOAT_MAT4: return ElementResource(Element(MAT4, gpu::FLOAT, UNIFORM), Resource::BUFFER);
/* {GL_FLOAT_MAT2x3 mat2x3},
{GL_FLOAT_MAT2x4 mat2x4},
{GL_FLOAT_MAT3x2 mat3x2},
{GL_FLOAT_MAT3x4 mat3x4},
{GL_FLOAT_MAT4x2 mat4x2},
{GL_FLOAT_MAT4x3 mat4x3},
{GL_DOUBLE_MAT2 dmat2},
{GL_DOUBLE_MAT3 dmat3},
{GL_DOUBLE_MAT4 dmat4},
{GL_DOUBLE_MAT2x3 dmat2x3},
{GL_DOUBLE_MAT2x4 dmat2x4},
{GL_DOUBLE_MAT3x2 dmat3x2},
{GL_DOUBLE_MAT3x4 dmat3x4},
{GL_DOUBLE_MAT4x2 dmat4x2},
{GL_DOUBLE_MAT4x3 dmat4x3},
*/
//qDebug() << "TODO: GLShared.cpp.cpp:ElementResource GL_SAMPLER_1D";
//case GL_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_1D);
case GL_SAMPLER_2D: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_2D);
case GL_SAMPLER_3D: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_3D);
case GL_SAMPLER_CUBE: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_CUBE);
#if defined(Q_OS_WIN)
case GL_SAMPLER_2D_MULTISAMPLE: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D);
case GL_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_1D_ARRAY);
case GL_SAMPLER_2D_ARRAY: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_2D_ARRAY);
case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY);
#endif
case GL_SAMPLER_2D_SHADOW: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_2D);
#if defined(Q_OS_WIN)
case GL_SAMPLER_CUBE_SHADOW: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_CUBE);
case GL_SAMPLER_2D_ARRAY_SHADOW: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_2D_ARRAY);
#endif
// {GL_SAMPLER_1D_SHADOW sampler1DShadow},
// {GL_SAMPLER_1D_ARRAY_SHADOW sampler1DArrayShadow},
// {GL_SAMPLER_BUFFER samplerBuffer},
// {GL_SAMPLER_2D_RECT sampler2DRect},
// {GL_SAMPLER_2D_RECT_SHADOW sampler2DRectShadow},
#if defined(Q_OS_WIN)
case GL_INT_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_1D);
case GL_INT_SAMPLER_2D: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_2D);
case GL_INT_SAMPLER_2D_MULTISAMPLE: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D);
case GL_INT_SAMPLER_3D: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_3D);
case GL_INT_SAMPLER_CUBE: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_CUBE);
case GL_INT_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_1D_ARRAY);
case GL_INT_SAMPLER_2D_ARRAY: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_2D_ARRAY);
case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY);
// {GL_INT_SAMPLER_BUFFER isamplerBuffer},
// {GL_INT_SAMPLER_2D_RECT isampler2DRect},
case GL_UNSIGNED_INT_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_1D);
case GL_UNSIGNED_INT_SAMPLER_2D: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_2D);
case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D);
case GL_UNSIGNED_INT_SAMPLER_3D: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_3D);
case GL_UNSIGNED_INT_SAMPLER_CUBE: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_CUBE);
case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_1D_ARRAY);
case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_2D_ARRAY);
case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY);
#endif
// {GL_UNSIGNED_INT_SAMPLER_BUFFER usamplerBuffer},
// {GL_UNSIGNED_INT_SAMPLER_2D_RECT usampler2DRect},
/*
{GL_IMAGE_1D image1D},
{GL_IMAGE_2D image2D},
{GL_IMAGE_3D image3D},
{GL_IMAGE_2D_RECT image2DRect},
{GL_IMAGE_CUBE imageCube},
{GL_IMAGE_BUFFER imageBuffer},
{GL_IMAGE_1D_ARRAY image1DArray},
{GL_IMAGE_2D_ARRAY image2DArray},
{GL_IMAGE_2D_MULTISAMPLE image2DMS},
{GL_IMAGE_2D_MULTISAMPLE_ARRAY image2DMSArray},
{GL_INT_IMAGE_1D iimage1D},
{GL_INT_IMAGE_2D iimage2D},
{GL_INT_IMAGE_3D iimage3D},
{GL_INT_IMAGE_2D_RECT iimage2DRect},
{GL_INT_IMAGE_CUBE iimageCube},
{GL_INT_IMAGE_BUFFER iimageBuffer},
{GL_INT_IMAGE_1D_ARRAY iimage1DArray},
{GL_INT_IMAGE_2D_ARRAY iimage2DArray},
{GL_INT_IMAGE_2D_MULTISAMPLE iimage2DMS},
{GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY iimage2DMSArray},
{GL_UNSIGNED_INT_IMAGE_1D uimage1D},
{GL_UNSIGNED_INT_IMAGE_2D uimage2D},
{GL_UNSIGNED_INT_IMAGE_3D uimage3D},
{GL_UNSIGNED_INT_IMAGE_2D_RECT uimage2DRect},
{GL_UNSIGNED_INT_IMAGE_CUBE uimageCube},+ [0] {_name="fInnerRadius" _location=0 _element={_semantic=15 '\xf' _dimension=0 '\0' _type=0 '\0' } } gpu::Shader::Slot
{GL_UNSIGNED_INT_IMAGE_BUFFER uimageBuffer},
{GL_UNSIGNED_INT_IMAGE_1D_ARRAY uimage1DArray},
{GL_UNSIGNED_INT_IMAGE_2D_ARRAY uimage2DArray},
{GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE uimage2DMS},
{GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY uimage2DMSArray},
{GL_UNSIGNED_INT_ATOMIC_COUNTER atomic_uint}
*/
default:
return ElementResource(Element(), Resource::BUFFER);
}
};
int makeUniformSlots(GLuint glprogram, const Shader::BindingSet& slotBindings,
Shader::SlotSet& uniforms, Shader::SlotSet& textures, Shader::SlotSet& samplers) {
GLint uniformsCount = 0;
glGetProgramiv(glprogram, GL_ACTIVE_UNIFORMS, &uniformsCount);
for (int i = 0; i < uniformsCount; i++) {
const GLint NAME_LENGTH = 256;
GLchar name[NAME_LENGTH];
GLint length = 0;
GLint size = 0;
GLenum type = 0;
glGetActiveUniform(glprogram, i, NAME_LENGTH, &length, &size, &type, name);
GLint location = glGetUniformLocation(glprogram, name);
const GLint INVALID_UNIFORM_LOCATION = -1;
// Try to make sense of the gltype
auto elementResource = getFormatFromGLUniform(type);
// The uniform as a standard var type
if (location != INVALID_UNIFORM_LOCATION) {
// Let's make sure the name doesn't contains an array element
std::string sname(name);
auto foundBracket = sname.find_first_of('[');
if (foundBracket != std::string::npos) {
// std::string arrayname = sname.substr(0, foundBracket);
if (sname[foundBracket + 1] == '0') {
sname = sname.substr(0, foundBracket);
} else {
// skip this uniform since it's not the first element of an array
continue;
}
}
if (elementResource._resource == Resource::BUFFER) {
uniforms.insert(Shader::Slot(sname, location, elementResource._element, elementResource._resource));
} else {
// For texture/Sampler, the location is the actual binding value
GLint binding = -1;
glGetUniformiv(glprogram, location, &binding);
auto requestedBinding = slotBindings.find(std::string(sname));
if (requestedBinding != slotBindings.end()) {
if (binding != (*requestedBinding)._location) {
binding = (*requestedBinding)._location;
glProgramUniform1i(glprogram, location, binding);
}
}
textures.insert(Shader::Slot(name, binding, elementResource._element, elementResource._resource));
samplers.insert(Shader::Slot(name, binding, elementResource._element, elementResource._resource));
}
}
}
return uniformsCount;
}
const GLint UNUSED_SLOT = -1;
bool isUnusedSlot(GLint binding) {
return (binding == UNUSED_SLOT);
}
int makeUniformBlockSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& buffers) {
GLint buffersCount = 0;
glGetProgramiv(glprogram, GL_ACTIVE_UNIFORM_BLOCKS, &buffersCount);
// fast exit
if (buffersCount == 0) {
return 0;
}
GLint maxNumUniformBufferSlots = 0;
glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &maxNumUniformBufferSlots);
std::vector<GLint> uniformBufferSlotMap(maxNumUniformBufferSlots, -1);
struct UniformBlockInfo {
using Vector = std::vector<UniformBlockInfo>;
const GLuint index{ 0 };
const std::string name;
GLint binding{ -1 };
GLint size{ 0 };
static std::string getName(GLuint glprogram, GLuint i) {
static const GLint NAME_LENGTH = 256;
GLint length = 0;
GLchar nameBuffer[NAME_LENGTH];
glGetActiveUniformBlockiv(glprogram, i, GL_UNIFORM_BLOCK_NAME_LENGTH, &length);
glGetActiveUniformBlockName(glprogram, i, NAME_LENGTH, &length, nameBuffer);
return std::string(nameBuffer);
}
UniformBlockInfo(GLuint glprogram, GLuint i) : index(i), name(getName(glprogram, i)) {
glGetActiveUniformBlockiv(glprogram, index, GL_UNIFORM_BLOCK_BINDING, &binding);
glGetActiveUniformBlockiv(glprogram, index, GL_UNIFORM_BLOCK_DATA_SIZE, &size);
}
};
UniformBlockInfo::Vector uniformBlocks;
uniformBlocks.reserve(buffersCount);
for (int i = 0; i < buffersCount; i++) {
uniformBlocks.push_back(UniformBlockInfo(glprogram, i));
}
for (auto& info : uniformBlocks) {
auto requestedBinding = slotBindings.find(info.name);
if (requestedBinding != slotBindings.end()) {
info.binding = (*requestedBinding)._location;
glUniformBlockBinding(glprogram, info.index, info.binding);
uniformBufferSlotMap[info.binding] = info.index;
}
}
for (auto& info : uniformBlocks) {
if (slotBindings.count(info.name)) {
continue;
}
// If the binding is 0, or the binding maps to an already used binding
if (info.binding == 0 || uniformBufferSlotMap[info.binding] != UNUSED_SLOT) {
// If no binding was assigned then just do it finding a free slot
auto slotIt = std::find_if(uniformBufferSlotMap.begin(), uniformBufferSlotMap.end(), isUnusedSlot);
if (slotIt != uniformBufferSlotMap.end()) {
info.binding = slotIt - uniformBufferSlotMap.begin();
glUniformBlockBinding(glprogram, info.index, info.binding);
} else {
// This should neve happen, an active ubo cannot find an available slot among the max available?!
info.binding = -1;
}
}
uniformBufferSlotMap[info.binding] = info.index;
}
for (auto& info : uniformBlocks) {
static const Element element(SCALAR, gpu::UINT32, gpu::UNIFORM_BUFFER);
buffers.insert(Shader::Slot(info.name, info.binding, element, Resource::BUFFER, info.size));
}
return buffersCount;
}
//CLIMAX_MERGE_START
//This has been copied over from gl45backendshader.cpp
int makeResourceBufferSlots(GLuint glprogram, const Shader::BindingSet& slotBindings,Shader::SlotSet& resourceBuffers) {
GLint buffersCount = 0;
glGetProgramInterfaceiv(glprogram, GL_SHADER_STORAGE_BLOCK, GL_ACTIVE_RESOURCES, &buffersCount);
// fast exit
if (buffersCount == 0) {
return 0;
}
GLint maxNumResourceBufferSlots = 0;
glGetIntegerv(GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS, &maxNumResourceBufferSlots);
std::vector<GLint> resourceBufferSlotMap(maxNumResourceBufferSlots, -1);
struct ResourceBlockInfo {
using Vector = std::vector<ResourceBlockInfo>;
const GLuint index{ 0 };
const std::string name;
GLint binding{ -1 };
GLint size{ 0 };
static std::string getName(GLuint glprogram, GLuint i) {
static const GLint NAME_LENGTH = 256;
GLint length = 0;
GLchar nameBuffer[NAME_LENGTH];
glGetProgramResourceName(glprogram, GL_SHADER_STORAGE_BLOCK, i, NAME_LENGTH, &length, nameBuffer);
return std::string(nameBuffer);
}
ResourceBlockInfo(GLuint glprogram, GLuint i) : index(i), name(getName(glprogram, i)) {
GLenum props[2] = { GL_BUFFER_BINDING, GL_BUFFER_DATA_SIZE};
glGetProgramResourceiv(glprogram, GL_SHADER_STORAGE_BLOCK, i, 2, props, 2, nullptr, &binding);
}
};
ResourceBlockInfo::Vector resourceBlocks;
resourceBlocks.reserve(buffersCount);
for (int i = 0; i < buffersCount; i++) {
resourceBlocks.push_back(ResourceBlockInfo(glprogram, i));
}
for (auto& info : resourceBlocks) {
auto requestedBinding = slotBindings.find(info.name);
if (requestedBinding != slotBindings.end()) {
info.binding = (*requestedBinding)._location;
glUniformBlockBinding(glprogram, info.index, info.binding);
resourceBufferSlotMap[info.binding] = info.index;
}
}
for (auto& info : resourceBlocks) {
if (slotBindings.count(info.name)) {
continue;
}
// If the binding is -1, or the binding maps to an already used binding
if (info.binding == -1 || !isUnusedSlot(resourceBufferSlotMap[info.binding])) {
// If no binding was assigned then just do it finding a free slot
auto slotIt = std::find_if(resourceBufferSlotMap.begin(), resourceBufferSlotMap.end(), isUnusedSlot);
if (slotIt != resourceBufferSlotMap.end()) {
info.binding = slotIt - resourceBufferSlotMap.begin();
glUniformBlockBinding(glprogram, info.index, info.binding);
} else {
// This should never happen, an active ssbo cannot find an available slot among the max available?!
info.binding = -1;
}
}
resourceBufferSlotMap[info.binding] = info.index;
}
for (auto& info : resourceBlocks) {
static const Element element(SCALAR, gpu::UINT32, gpu::RESOURCE_BUFFER);
resourceBuffers.insert(Shader::Slot(info.name, info.binding, element, Resource::BUFFER, info.size));
}
return buffersCount;
}
//CLIMAX_MERGE_END
int makeInputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& inputs) {
GLint inputsCount = 0;
glGetProgramiv(glprogram, GL_ACTIVE_ATTRIBUTES, &inputsCount);
for (int i = 0; i < inputsCount; i++) {
const GLint NAME_LENGTH = 256;
GLchar name[NAME_LENGTH];
GLint length = 0;
GLint size = 0;
GLenum type = 0;
glGetActiveAttrib(glprogram, i, NAME_LENGTH, &length, &size, &type, name);
GLint binding = glGetAttribLocation(glprogram, name);
auto elementResource = getFormatFromGLUniform(type);
inputs.insert(Shader::Slot(name, binding, elementResource._element, -1));
}
return inputsCount;
}
int makeOutputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& outputs) {
/* GLint outputsCount = 0;
glGetProgramiv(glprogram, GL_ACTIVE_, &outputsCount);
for (int i = 0; i < inputsCount; i++) {
const GLint NAME_LENGTH = 256;
GLchar name[NAME_LENGTH];
GLint length = 0;
GLint size = 0;
GLenum type = 0;
glGetActiveAttrib(glprogram, i, NAME_LENGTH, &length, &size, &type, name);
auto element = getFormatFromGLUniform(type);
outputs.insert(Shader::Slot(name, i, element));
}
*/
return 0; //inputsCount;
}
void makeProgramBindings(ShaderObject& shaderObject) {
if (!shaderObject.glprogram) {
return;
}
GLuint glprogram = shaderObject.glprogram;
GLint loc = -1;
//Check for gpu specific attribute slotBindings
loc = glGetAttribLocation(glprogram, "inPosition");
if (loc >= 0 && loc != gpu::Stream::POSITION) {
glBindAttribLocation(glprogram, gpu::Stream::POSITION, "inPosition");
}
loc = glGetAttribLocation(glprogram, "inNormal");
if (loc >= 0 && loc != gpu::Stream::NORMAL) {
glBindAttribLocation(glprogram, gpu::Stream::NORMAL, "inNormal");
}
loc = glGetAttribLocation(glprogram, "inColor");
if (loc >= 0 && loc != gpu::Stream::COLOR) {
glBindAttribLocation(glprogram, gpu::Stream::COLOR, "inColor");
}
loc = glGetAttribLocation(glprogram, "inTexCoord0");
if (loc >= 0 && loc != gpu::Stream::TEXCOORD) {
glBindAttribLocation(glprogram, gpu::Stream::TEXCOORD, "inTexCoord0");
}
loc = glGetAttribLocation(glprogram, "inTangent");
if (loc >= 0 && loc != gpu::Stream::TANGENT) {
glBindAttribLocation(glprogram, gpu::Stream::TANGENT, "inTangent");
}
loc = glGetAttribLocation(glprogram, "inTexCoord1");
if (loc >= 0 && loc != gpu::Stream::TEXCOORD1) {
glBindAttribLocation(glprogram, gpu::Stream::TEXCOORD1, "inTexCoord1");
}
loc = glGetAttribLocation(glprogram, "inSkinClusterIndex");
if (loc >= 0 && loc != gpu::Stream::SKIN_CLUSTER_INDEX) {
glBindAttribLocation(glprogram, gpu::Stream::SKIN_CLUSTER_INDEX, "inSkinClusterIndex");
}
loc = glGetAttribLocation(glprogram, "inSkinClusterWeight");
if (loc >= 0 && loc != gpu::Stream::SKIN_CLUSTER_WEIGHT) {
glBindAttribLocation(glprogram, gpu::Stream::SKIN_CLUSTER_WEIGHT, "inSkinClusterWeight");
}
loc = glGetAttribLocation(glprogram, "_drawCallInfo");
if (loc >= 0 && loc != gpu::Stream::DRAW_CALL_INFO) {
glBindAttribLocation(glprogram, gpu::Stream::DRAW_CALL_INFO, "_drawCallInfo");
}
// Link again to take into account the assigned attrib location
glLinkProgram(glprogram);
GLint linked = 0;
glGetProgramiv(glprogram, GL_LINK_STATUS, &linked);
if (!linked) {
qCWarning(gpugllogging) << "GLShader::makeBindings - failed to link after assigning slotBindings?";
}
// now assign the ubo binding, then DON't relink!
//Check for gpu specific uniform slotBindings
loc = glGetProgramResourceIndex(glprogram, GL_SHADER_STORAGE_BLOCK, "transformObjectBuffer");
if (loc >= 0) {
// FIXME GLES
// glShaderStorageBlockBinding(glprogram, loc, TRANSFORM_OBJECT_SLOT);
shaderObject.transformObjectSlot = TRANSFORM_OBJECT_SLOT;
}
loc = glGetUniformBlockIndex(glprogram, "transformCameraBuffer");
if (loc >= 0) {
glUniformBlockBinding(glprogram, loc, TRANSFORM_CAMERA_SLOT);
shaderObject.transformCameraSlot = TRANSFORM_CAMERA_SLOT;
}
(void)CHECK_GL_ERROR();
}
void serverWait() {
auto fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
assert(fence);

View file

@ -17,9 +17,9 @@
Q_DECLARE_LOGGING_CATEGORY(gpugllogging)
Q_DECLARE_LOGGING_CATEGORY(trace_render_gpu_gl)
namespace gpu { namespace gl {
#define BUFFER_OFFSET(bytes) ((GLubyte*) nullptr + (bytes))
static const GLint TRANSFORM_OBJECT_SLOT { 14 }; // SSBO binding slot
namespace gpu { namespace gl {
// Create a fence and inject a GPU wait on the fence
void serverWait();
@ -27,7 +27,6 @@ void serverWait();
// Create a fence and synchronously wait on the fence
void clientWait();
gpu::Size getDedicatedMemory();
gpu::Size getFreeDedicatedMemory();
ComparisonFunction comparisonFuncFromGL(GLenum func);
State::StencilOp stencilOpFromGL(GLenum stencilOp);
@ -35,25 +34,6 @@ State::BlendOp blendOpFromGL(GLenum blendOp);
State::BlendArg blendArgFromGL(GLenum blendArg);
void getCurrentGLState(State::Data& state);
struct ShaderObject {
GLuint glshader { 0 };
GLuint glprogram { 0 };
GLint transformCameraSlot { -1 };
GLint transformObjectSlot { -1 };
};
int makeUniformSlots(GLuint glprogram, const Shader::BindingSet& slotBindings,
Shader::SlotSet& uniforms, Shader::SlotSet& textures, Shader::SlotSet& samplers);
int makeUniformBlockSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& buffers);
int makeInputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& inputs);
int makeOutputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& outputs);
//CLIMAX_MERGE_START
//makeResourceBufferSlots has been added to glbacked as a virtual function and is being used in gl42 and gl45 overrides.
//Since these files dont exist in the andoid version create a stub here.
int makeResourceBufferSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& resourceBuffers);
//CLIMAX_MERGE_END
void makeProgramBindings(ShaderObject& shaderObject);
enum GLSyncState {
// The object is currently undergoing no processing, although it's content
// may be out of date, or it's storage may be invalid relative to the
@ -128,6 +108,7 @@ static const GLenum ELEMENT_TYPE_TO_GL[gpu::NUM_TYPES] = {
GL_SHORT,
GL_UNSIGNED_SHORT,
GL_BYTE,
GL_UNSIGNED_BYTE,
GL_UNSIGNED_BYTE
};
@ -156,6 +137,7 @@ class GLQuery;
class GLState;
class GLShader;
class GLTexture;
struct ShaderObject;
} } // namespace gpu::gl

View file

@ -11,6 +11,9 @@
using namespace gpu;
using namespace gpu::gl;
bool GLTexelFormat::isCompressed() const {
return false;
}
GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) {
// qDebug() << "GLTexelFormat::evalGLTexelFormatInternal " << dstFormat.getDimension() << ", " << dstFormat.getSemantic() << ", " << dstFormat.getType();
@ -18,6 +21,7 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) {
switch (dstFormat.getDimension()) {
case gpu::SCALAR: {
switch (dstFormat.getSemantic()) {
case gpu::RED:
case gpu::RGB:
case gpu::RGBA:
case gpu::SRGB:
@ -74,7 +78,10 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) {
// the type should be float
result = GL_R11F_G11F_B10F;
break;
case gpu::RGB9E5:
// the type should be float
result = GL_RGB9_E5;
break;
case gpu::DEPTH:
result = GL_DEPTH_COMPONENT16;
switch (dstFormat.getType()) {
@ -114,6 +121,7 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) {
switch (dstFormat.getSemantic()) {
case gpu::RGB:
case gpu::RGBA:
case gpu::XY:
result = GL_RG8;
break;
default:
@ -178,12 +186,12 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) {
case gpu::NINT8:
result = GL_RGBA8_SNORM;
break;
case gpu::COMPRESSED:
case gpu::NUINT2:
case gpu::NINT16:
case gpu::NUINT16:
case gpu::NUINT32:
case gpu::NINT32:
case gpu::COMPRESSED:
case gpu::NUM_TYPES: // quiet compiler
Q_UNREACHABLE();
}
@ -201,7 +209,37 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) {
}
break;
}
// TODO: implement compression formats supported by android (ASTC, ETC2)
/*
case gpu::TILE4x4: {
switch (dstFormat.getSemantic()) {
case gpu::COMPRESSED_BC4_RED:
result = GL_COMPRESSED_RED_RGTC1;
break;
case gpu::COMPRESSED_BC1_SRGB:
result = GL_COMPRESSED_SRGB_S3TC_DXT1_EXT;
break;
case gpu::COMPRESSED_BC1_SRGBA:
result = GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT;
break;
case gpu::COMPRESSED_BC3_SRGBA:
result = GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT;
break;
case gpu::COMPRESSED_BC5_XY:
result = GL_COMPRESSED_RG_RGTC2;
break;
case gpu::COMPRESSED_BC6_RGB:
result = GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT;
break;
case gpu::COMPRESSED_BC7_SRGBA:
result = GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM;
break;
default:
qCWarning(gpugllogging) << "Unknown combination of texel format";
}
break;
*/
default:
qCDebug(gpugllogging) << "Unknown combination of texel format";
}
@ -212,8 +250,6 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) {
}
GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const Element& srcFormat) {
// qDebug() << "GLTexelFormat::evalGLTexelFormat dst.getDimension=" << dstFormat.getDimension() << " dst.getType=" << dstFormat.getType() << " dst.getSemantic=" << dstFormat.getSemantic();
// qDebug() << "GLTexelFormat::evalGLTexelFormat src.getDimension=" << srcFormat.getDimension() << " src.getType=" << srcFormat.getType() << " src.getSemantic=" << srcFormat.getSemantic();
if (dstFormat != srcFormat) {
GLTexelFormat texel = { GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE };
@ -237,6 +273,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
//CLIMAX_MERGE_END
case gpu::DEPTH:
texel.format = GL_DEPTH_COMPONENT;
texel.internalFormat = GL_DEPTH_COMPONENT32_OES;
break;
case gpu::DEPTH_STENCIL:
@ -245,7 +282,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
texel.internalFormat = GL_DEPTH24_STENCIL8;
break;
default:
qCDebug(gpugllogging) << "Unknown combination of texel format";
qCWarning(gpugllogging) << "Unknown combination of texel format";
}
break;
}
@ -257,10 +294,11 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
switch (dstFormat.getSemantic()) {
case gpu::RGB:
case gpu::RGBA:
case gpu::XY:
texel.internalFormat = GL_RG8;
break;
default:
qCDebug(gpugllogging) << "Unknown combination of texel format";
qCWarning(gpugllogging) << "Unknown combination of texel format";
}
break;
@ -288,7 +326,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
// break;
//CLIMAX_MERGE_END
default:
qCDebug(gpugllogging) << "Unknown combination of texel format";
qCWarning(gpugllogging) << "Unknown combination of texel format";
}
break;
@ -356,13 +394,13 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
*/
default:
qCDebug(gpugllogging) << "Unknown combination of texel format";
qCWarning(gpugllogging) << "Unknown combination of texel format";
}
break;
}
default:
qCDebug(gpugllogging) << "Unknown combination of texel format";
qCWarning(gpugllogging) << "Unknown combination of texel format";
}
return texel;
} else {
@ -374,12 +412,8 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
texel.type = ELEMENT_TYPE_TO_GL[dstFormat.getType()];
switch (dstFormat.getSemantic()) {
//CLIMAX_MERGE_START
// case gpu::COMPRESSED_R: {
// qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_COMPRESSED_RED_RGTC1";
// //texel.internalFormat = GL_COMPRESSED_RED_RGTC1;
// break;
// }
case gpu::RED:
case gpu::RGB:
case gpu::RGBA:
case gpu::SRGB:
@ -465,6 +499,12 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
texel.internalFormat = GL_R11F_G11F_B10F;
break;
case gpu::RGB9E5:
texel.format = GL_RGB;
texel.type = GL_UNSIGNED_INT_5_9_9_9_REV;
texel.internalFormat = GL_RGB9_E5;
break;
case gpu::DEPTH:
texel.format = GL_DEPTH_COMPONENT; // It's depth component to load it
texel.internalFormat = GL_DEPTH_COMPONENT32_OES;
@ -508,7 +548,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
texel.internalFormat = GL_DEPTH24_STENCIL8;
break;
default:
qCDebug(gpugllogging) << "Unknown combination of texel format";
qCWarning(gpugllogging) << "Unknown combination of texel format";
}
break;
@ -521,10 +561,11 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
switch (dstFormat.getSemantic()) {
case gpu::RGB:
case gpu::RGBA:
case gpu::XY:
texel.internalFormat = GL_RG8;
break;
default:
qCDebug(gpugllogging) << "Unknown combination of texel format";
qCWarning(gpugllogging) << "Unknown combination of texel format";
}
break;
@ -555,7 +596,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
// qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_COMPRESSED_SRGB";
// break;
default:
qCDebug(gpugllogging) << "Unknown combination of texel format";
qCWarning(gpugllogging) << "Unknown combination of texel format";
}
break;
}
@ -645,7 +686,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
// //texel.internalFormat = GL_COMPRESSED_SRGB_ALPHA;
// break;
default:
qCDebug(gpugllogging) << "Unknown combination of texel format";
qCWarning(gpugllogging) << "Unknown combination of texel format";
}
break;
}

View file

@ -18,6 +18,11 @@ public:
GLenum format;
GLenum type;
GLTexelFormat(GLenum glinternalFormat, GLenum glformat, GLenum gltype) : internalFormat(glinternalFormat), format(glformat), type(gltype) {}
GLTexelFormat(GLenum glinternalFormat) : internalFormat(glinternalFormat) {}
bool isCompressed() const;
static GLTexelFormat evalGLTexelFormat(const Element& dstFormat) {
return evalGLTexelFormat(dstFormat, dstFormat);
}

View file

@ -8,27 +8,22 @@
#include "GLTexture.h"
#include <QtCore/QThread>
#include <NumericalConstants.h>
#include "GLTextureTransfer.h"
#include "GLBackend.h"
using namespace gpu;
using namespace gpu::gl;
std::shared_ptr<GLTextureTransferHelper> GLTexture::_textureTransferHelper;
// FIXME placeholder for texture memory over-use
#define DEFAULT_MAX_MEMORY_MB 256
#define MIN_FREE_GPU_MEMORY_PERCENTAGE 0.25f
#define OVER_MEMORY_PRESSURE 2.0f
const GLenum GLTexture::CUBE_FACE_LAYOUT[6] = {
const GLenum GLTexture::CUBE_FACE_LAYOUT[GLTexture::TEXTURE_CUBE_NUM_FACES] = {
GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
};
const GLenum GLTexture::WRAP_MODES[Sampler::NUM_WRAP_MODES] = {
GL_REPEAT, // WRAP_REPEAT,
GL_MIRRORED_REPEAT, // WRAP_MIRROR,
@ -73,6 +68,17 @@ GLenum GLTexture::getGLTextureType(const Texture& texture) {
return GL_TEXTURE_2D;
}
uint8_t GLTexture::getFaceCount(GLenum target) {
switch (target) {
case GL_TEXTURE_2D:
return TEXTURE_2D_NUM_FACES;
case GL_TEXTURE_CUBE_MAP:
return TEXTURE_CUBE_NUM_FACES;
default:
Q_UNREACHABLE();
break;
}
}
const std::vector<GLenum>& GLTexture::getFaceTargets(GLenum target) {
static std::vector<GLenum> cubeFaceTargets {
@ -96,228 +102,602 @@ const std::vector<GLenum>& GLTexture::getFaceTargets(GLenum target) {
return faceTargets;
}
// Default texture memory = GPU total memory - 2GB
#define GPU_MEMORY_RESERVE_BYTES MB_TO_BYTES(2048)
// Minimum texture memory = 1GB
#define TEXTURE_MEMORY_MIN_BYTES MB_TO_BYTES(1024)
float GLTexture::getMemoryPressure() {
// Check for an explicit memory limit
auto availableTextureMemory = Texture::getAllowedGPUMemoryUsage();
// If no memory limit has been set, use a percentage of the total dedicated memory
if (!availableTextureMemory) {
#if 0
auto totalMemory = getDedicatedMemory();
if ((GPU_MEMORY_RESERVE_BYTES + TEXTURE_MEMORY_MIN_BYTES) > totalMemory) {
availableTextureMemory = TEXTURE_MEMORY_MIN_BYTES;
} else {
availableTextureMemory = totalMemory - GPU_MEMORY_RESERVE_BYTES;
}
#else
// Hardcode texture limit for sparse textures at 1 GB for now
availableTextureMemory = TEXTURE_MEMORY_MIN_BYTES;
#endif
}
// Return the consumed texture memory divided by the available texture memory.
//CLIMAX_MERGE_START
//auto consumedGpuMemory = Context::getTextureGPUMemoryUsage() - Context::getTextureGPUFramebufferMemoryUsage();
//float memoryPressure = (float)consumedGpuMemory / (float)availableTextureMemory;
//static Context::Size lastConsumedGpuMemory = 0;
//if (memoryPressure > 1.0f && lastConsumedGpuMemory != consumedGpuMemory) {
// lastConsumedGpuMemory = consumedGpuMemory;
// qCDebug(gpugllogging) << "Exceeded max allowed texture memory: " << consumedGpuMemory << " / " << availableTextureMemory;
//}
//return memoryPressure;
return 0;
}
// Create the texture and allocate storage
GLTexture::GLTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture, GLuint id, bool transferrable) :
GLObject(backend, texture, id),
_external(false),
_source(texture.source()),
_storageStamp(texture.getStamp()),
_target(getGLTextureType(texture)),
_internalFormat(gl::GLTexelFormat::evalGLTexelFormatInternal(texture.getTexelFormat())),
_maxMip(texture.getMaxMip()),
_minMip(texture.getMinMip()),
_virtualSize(texture.evalTotalSize()),
_transferrable(transferrable)
{
//qDebug() << "GLTexture::GLTexture building GLTexture with _internalFormat" << _internalFormat;
auto strongBackend = _backend.lock();
strongBackend->recycle();
//CLIMAX_MERGE_START
//Backend::incrementTextureGPUCount();
//Backend::updateTextureGPUVirtualMemoryUsage(0, _virtualSize);
//CLIMAX_MERGE_END
Backend::setGPUObject(texture, this);
}
GLTexture::GLTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture, GLuint id) :
GLObject(backend, texture, id),
_external(true),
_source(texture.source()),
_storageStamp(0),
_target(getGLTextureType(texture)),
_internalFormat(GL_RGBA8),
// FIXME force mips to 0?
_maxMip(texture.getMaxMip()),
_minMip(texture.getMinMip()),
_virtualSize(0),
_transferrable(false)
GLObject(backend, texture, id),
_source(texture.source()),
_target(getGLTextureType(texture)),
_texelFormat(GLTexelFormat::evalGLTexelFormatInternal(texture.getTexelFormat()))
{
Backend::setGPUObject(texture, this);
// FIXME Is this necessary?
//withPreservedTexture([this] {
// syncSampler();
// if (_gpuObject.isAutogenerateMips()) {
// generateMips();
// }
//});
}
GLTexture::~GLTexture() {
auto backend = _backend.lock();
if (backend) {
if (_external) {
auto recycler = _gpuObject.getExternalRecycler();
if (recycler) {
backend->releaseExternalTexture(_id, recycler);
} else {
qWarning() << "No recycler available for texture " << _id << " possible leak";
}
} else if (_id) {
// WARNING! Sparse textures do not use this code path. See GL45BackendTexture for
// the GL45Texture destructor for doing any required work tracking GPU stats
backend->releaseTexture(_id, _size);
}
////CLIMAX_MERGE_START
//if (!_external && !_transferrable) {
// Backend::updateTextureGPUFramebufferMemoryUsage(_size, 0);
//}
if (backend && _id) {
backend->releaseTexture(_id, 0);
}
//Backend::updateTextureGPUVirtualMemoryUsage(_virtualSize, 0);
//CLIMAX_MERGE_END
}
void GLTexture::createTexture() {
withPreservedTexture([&] {
allocateStorage();
(void)CHECK_GL_ERROR();
syncSampler();
(void)CHECK_GL_ERROR();
Size GLTexture::copyMipFaceFromTexture(uint16_t sourceMip, uint16_t targetMip, uint8_t face) const {
if (!_gpuObject.isStoredMipFaceAvailable(sourceMip)) {
return 0;
}
auto dim = _gpuObject.evalMipDimensions(sourceMip);
auto mipData = _gpuObject.accessStoredMipFace(sourceMip, face);
auto mipSize = _gpuObject.getStoredMipFaceSize(sourceMip, face);
if (mipData) {
GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), _gpuObject.getStoredMipFormat());
return copyMipFaceLinesFromTexture(targetMip, face, dim, 0, texelFormat.internalFormat, texelFormat.format, texelFormat.type, mipSize, mipData->readData());
} else {
qCDebug(gpugllogging) << "Missing mipData level=" << sourceMip << " face=" << (int)face << " for texture " << _gpuObject.source().c_str();
}
return 0;
}
GLExternalTexture::GLExternalTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture, GLuint id)
: Parent(backend, texture, id) {
Backend::textureExternalCount.increment();
}
GLExternalTexture::~GLExternalTexture() {
auto backend = _backend.lock();
if (backend) {
auto recycler = _gpuObject.getExternalRecycler();
if (recycler) {
backend->releaseExternalTexture(_id, recycler);
} else {
qCWarning(gpugllogging) << "No recycler available for texture " << _id << " possible leak";
}
const_cast<GLuint&>(_id) = 0;
}
Backend::textureExternalCount.decrement();
}
// Variable sized textures
using MemoryPressureState = GLVariableAllocationSupport::MemoryPressureState;
using WorkQueue = GLVariableAllocationSupport::WorkQueue;
using TransferJobPointer = GLVariableAllocationSupport::TransferJobPointer;
std::list<TextureWeakPointer> GLVariableAllocationSupport::_memoryManagedTextures;
MemoryPressureState GLVariableAllocationSupport::_memoryPressureState { MemoryPressureState::Idle };
std::atomic<bool> GLVariableAllocationSupport::_memoryPressureStateStale { false };
const uvec3 GLVariableAllocationSupport::INITIAL_MIP_TRANSFER_DIMENSIONS { 64, 64, 1 };
WorkQueue GLVariableAllocationSupport::_transferQueue;
WorkQueue GLVariableAllocationSupport::_promoteQueue;
WorkQueue GLVariableAllocationSupport::_demoteQueue;
size_t GLVariableAllocationSupport::_frameTexturesCreated { 0 };
#define OVERSUBSCRIBED_PRESSURE_VALUE 0.95f
#define UNDERSUBSCRIBED_PRESSURE_VALUE 0.85f
#define DEFAULT_ALLOWED_TEXTURE_MEMORY_MB ((size_t)1024)
static const size_t DEFAULT_ALLOWED_TEXTURE_MEMORY = MB_TO_BYTES(DEFAULT_ALLOWED_TEXTURE_MEMORY_MB);
using TransferJob = GLVariableAllocationSupport::TransferJob;
const uvec3 GLVariableAllocationSupport::MAX_TRANSFER_DIMENSIONS { 1024, 1024, 1 };
const size_t GLVariableAllocationSupport::MAX_TRANSFER_SIZE = GLVariableAllocationSupport::MAX_TRANSFER_DIMENSIONS.x * GLVariableAllocationSupport::MAX_TRANSFER_DIMENSIONS.y * 4;
#if THREADED_TEXTURE_BUFFERING
TexturePointer GLVariableAllocationSupport::_currentTransferTexture;
TransferJobPointer GLVariableAllocationSupport::_currentTransferJob;
QThreadPool* TransferJob::_bufferThreadPool { nullptr };
void TransferJob::startBufferingThread() {
static std::once_flag once;
std::call_once(once, [&] {
_bufferThreadPool = new QThreadPool(qApp);
_bufferThreadPool->setMaxThreadCount(1);
});
}
void GLTexture::withPreservedTexture(std::function<void()> f) const {
GLint boundTex = -1;
switch (_target) {
case GL_TEXTURE_2D:
glGetIntegerv(GL_TEXTURE_BINDING_2D, &boundTex);
break;
#endif
case GL_TEXTURE_CUBE_MAP:
glGetIntegerv(GL_TEXTURE_BINDING_CUBE_MAP, &boundTex);
break;
TransferJob::TransferJob(const GLTexture& parent, uint16_t sourceMip, uint16_t targetMip, uint8_t face, uint32_t lines, uint32_t lineOffset)
: _parent(parent) {
default:
qFatal("Unsupported texture type");
auto transferDimensions = _parent._gpuObject.evalMipDimensions(sourceMip);
GLenum format;
GLenum internalFormat;
GLenum type;
GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_parent._gpuObject.getTexelFormat(), _parent._gpuObject.getStoredMipFormat());
format = texelFormat.format;
internalFormat = texelFormat.internalFormat;
type = texelFormat.type;
_transferSize = _parent._gpuObject.getStoredMipFaceSize(sourceMip, face);
// If we're copying a subsection of the mip, do additional calculations to find the size and offset of the segment
if (0 != lines) {
transferDimensions.y = lines;
auto dimensions = _parent._gpuObject.evalMipDimensions(sourceMip);
auto bytesPerLine = (uint32_t)_transferSize / dimensions.y;
_transferOffset = bytesPerLine * lineOffset;
_transferSize = bytesPerLine * lines;
}
(void)CHECK_GL_ERROR();
glBindTexture(_target, _texture);
f();
glBindTexture(_target, boundTex);
(void)CHECK_GL_ERROR();
Backend::texturePendingGPUTransferMemSize.update(0, _transferSize);
if (_transferSize > GLVariableAllocationSupport::MAX_TRANSFER_SIZE) {
qCWarning(gpugllogging) << "Transfer size of " << _transferSize << " exceeds theoretical maximum transfer size";
}
// Buffering can invoke disk IO, so it should be off of the main and render threads
_bufferingLambda = [=] {
auto mipStorage = _parent._gpuObject.accessStoredMipFace(sourceMip, face);
if (mipStorage) {
_mipData = mipStorage->createView(_transferSize, _transferOffset);
} else {
qCWarning(gpugllogging) << "Buffering failed because mip could not be retrieved from texture " << _parent._source.c_str() ;
}
};
_transferLambda = [=] {
if (_mipData) {
_parent.copyMipFaceLinesFromTexture(targetMip, face, transferDimensions, lineOffset, internalFormat, format, type, _mipData->size(), _mipData->readData());
_mipData.reset();
} else {
qCWarning(gpugllogging) << "Transfer failed because mip could not be retrieved from texture " << _parent._source.c_str();
}
};
}
void GLTexture::setSize(GLuint size) const {
////CLIMAX_MERGE_START
//if (!_external && !_transferrable) {
// Backend::updateTextureGPUFramebufferMemoryUsage(_size, 0);
//}
//Backend::updateTextureGPUMemoryUsage(_size, size);
const_cast<GLuint&>(_size) = size;
TransferJob::TransferJob(const GLTexture& parent, std::function<void()> transferLambda)
: _parent(parent), _bufferingRequired(false), _transferLambda(transferLambda) {
}
bool GLTexture::isInvalid() const {
return _storageStamp < _gpuObject.getStamp();
TransferJob::~TransferJob() {
Backend::texturePendingGPUTransferMemSize.update(_transferSize, 0);
}
bool GLTexture::isOutdated() const {
return GLSyncState::Idle == _syncState && _contentStamp < _gpuObject.getDataStamp();
}
bool GLTexture::isReady() const {
// If we have an invalid texture, we're never ready
if (isInvalid()) {
bool TransferJob::tryTransfer() {
#if THREADED_TEXTURE_BUFFERING
// Are we ready to transfer
if (!bufferingCompleted()) {
startBuffering();
return false;
}
auto syncState = _syncState.load();
if (isOutdated() || Idle != syncState) {
return false;
#else
if (_bufferingRequired) {
_bufferingLambda();
}
#endif
_transferLambda();
return true;
}
#if THREADED_TEXTURE_BUFFERING
bool TransferJob::bufferingRequired() const {
if (!_bufferingRequired) {
return false;
}
// Do any post-transfer operations that might be required on the main context / rendering thread
void GLTexture::postTransfer() {
//CLIMAX_MERGE_START
// setSyncState(GLSyncState::Idle);
// ++_transferCount;
// The default state of a QFuture is with status Canceled | Started | Finished,
// so we have to check isCancelled before we check the actual state
if (_bufferingStatus.isCanceled()) {
return true;
}
// // At this point the mip pixels have been loaded, we can notify the gpu texture to abandon it's memory
// switch (_gpuObject.getType()) {
// case Texture::TEX_2D:
// for (uint16_t i = 0; i < Sampler::MAX_MIP_LEVEL; ++i) {
// if (_gpuObject.isStoredMipFaceAvailable(i)) {
// _gpuObject.notifyMipFaceGPULoaded(i);
// }
// }
// break;
// case Texture::TEX_CUBE:
// // transfer pixels from each faces
// for (uint8_t f = 0; f < CUBE_NUM_FACES; f++) {
// for (uint16_t i = 0; i < Sampler::MAX_MIP_LEVEL; ++i) {
// if (_gpuObject.isStoredMipFaceAvailable(i, f)) {
// _gpuObject.notifyMipFaceGPULoaded(i, f);
// }
// }
// }
// break;
// default:
// qCWarning(gpugllogging) << __FUNCTION__ << " case for Texture Type " << _gpuObject.getType() << " not supported";
// break;
// }
//CLIMAX_MERGE_END
return !_bufferingStatus.isStarted();
}
void GLTexture::initTextureTransferHelper() {
_textureTransferHelper = std::make_shared<GLTextureTransferHelper>();
bool TransferJob::bufferingCompleted() const {
if (!_bufferingRequired) {
return true;
}
// The default state of a QFuture is with status Canceled | Started | Finished,
// so we have to check isCancelled before we check the actual state
if (_bufferingStatus.isCanceled()) {
return false;
}
return _bufferingStatus.isFinished();
}
void GLTexture::startTransfer() {
createTexture();
void TransferJob::startBuffering() {
if (bufferingRequired()) {
assert(_bufferingStatus.isCanceled());
_bufferingStatus = QtConcurrent::run(_bufferThreadPool, [=] {
_bufferingLambda();
});
assert(!_bufferingStatus.isCanceled());
assert(_bufferingStatus.isStarted());
}
}
#endif
GLVariableAllocationSupport::GLVariableAllocationSupport() {
_memoryPressureStateStale = true;
}
void GLTexture::finishTransfer() {
if (_gpuObject.isAutogenerateMips()) {
generateMips();
GLVariableAllocationSupport::~GLVariableAllocationSupport() {
_memoryPressureStateStale = true;
}
void GLVariableAllocationSupport::addMemoryManagedTexture(const TexturePointer& texturePointer) {
_memoryManagedTextures.push_back(texturePointer);
if (MemoryPressureState::Idle != _memoryPressureState) {
addToWorkQueue(texturePointer);
}
}
void GLVariableAllocationSupport::addToWorkQueue(const TexturePointer& texturePointer) {
GLTexture* gltexture = Backend::getGPUObject<GLTexture>(*texturePointer);
GLVariableAllocationSupport* vargltexture = dynamic_cast<GLVariableAllocationSupport*>(gltexture);
switch (_memoryPressureState) {
case MemoryPressureState::Oversubscribed:
if (vargltexture->canDemote()) {
// Demote largest first
_demoteQueue.push({ texturePointer, (float)gltexture->size() });
}
break;
case MemoryPressureState::Undersubscribed:
if (vargltexture->canPromote()) {
// Promote smallest first
_promoteQueue.push({ texturePointer, 1.0f / (float)gltexture->size() });
}
break;
case MemoryPressureState::Transfer:
if (vargltexture->hasPendingTransfers()) {
// Transfer priority given to smaller mips first
_transferQueue.push({ texturePointer, 1.0f / (float)gltexture->_gpuObject.evalMipSize(vargltexture->_populatedMip) });
}
break;
case MemoryPressureState::Idle:
Q_UNREACHABLE();
break;
}
}
WorkQueue& GLVariableAllocationSupport::getActiveWorkQueue() {
static WorkQueue empty;
switch (_memoryPressureState) {
case MemoryPressureState::Oversubscribed:
return _demoteQueue;
case MemoryPressureState::Undersubscribed:
return _promoteQueue;
case MemoryPressureState::Transfer:
return _transferQueue;
case MemoryPressureState::Idle:
Q_UNREACHABLE();
break;
}
return empty;
}
// FIXME hack for stats display
QString getTextureMemoryPressureModeString() {
switch (GLVariableAllocationSupport::_memoryPressureState) {
case MemoryPressureState::Oversubscribed:
return "Oversubscribed";
case MemoryPressureState::Undersubscribed:
return "Undersubscribed";
case MemoryPressureState::Transfer:
return "Transfer";
case MemoryPressureState::Idle:
return "Idle";
}
Q_UNREACHABLE();
return "Unknown";
}
void GLVariableAllocationSupport::updateMemoryPressure() {
static size_t lastAllowedMemoryAllocation = gpu::Texture::getAllowedGPUMemoryUsage();
size_t allowedMemoryAllocation = gpu::Texture::getAllowedGPUMemoryUsage();
if (0 == allowedMemoryAllocation) {
allowedMemoryAllocation = DEFAULT_ALLOWED_TEXTURE_MEMORY;
}
// If the user explicitly changed the allowed memory usage, we need to mark ourselves stale
// so that we react
if (allowedMemoryAllocation != lastAllowedMemoryAllocation) {
_memoryPressureStateStale = true;
lastAllowedMemoryAllocation = allowedMemoryAllocation;
}
if (!_memoryPressureStateStale.exchange(false)) {
return;
}
PROFILE_RANGE(render_gpu_gl, __FUNCTION__);
// Clear any defunct textures (weak pointers that no longer have a valid texture)
_memoryManagedTextures.remove_if([&](const TextureWeakPointer& weakPointer) {
return weakPointer.expired();
});
// Convert weak pointers to strong. This new list may still contain nulls if a texture was
// deleted on another thread between the previous line and this one
std::vector<TexturePointer> strongTextures; {
strongTextures.reserve(_memoryManagedTextures.size());
std::transform(
_memoryManagedTextures.begin(), _memoryManagedTextures.end(),
std::back_inserter(strongTextures),
[](const TextureWeakPointer& p) { return p.lock(); });
}
size_t totalVariableMemoryAllocation = 0;
size_t idealMemoryAllocation = 0;
bool canDemote = false;
bool canPromote = false;
bool hasTransfers = false;
for (const auto& texture : strongTextures) {
// Race conditions can still leave nulls in the list, so we need to check
if (!texture) {
continue;
}
GLTexture* gltexture = Backend::getGPUObject<GLTexture>(*texture);
GLVariableAllocationSupport* vartexture = dynamic_cast<GLVariableAllocationSupport*>(gltexture);
// Track how much the texture thinks it should be using
idealMemoryAllocation += texture->evalTotalSize();
// Track how much we're actually using
totalVariableMemoryAllocation += gltexture->size();
canDemote |= vartexture->canDemote();
canPromote |= vartexture->canPromote();
hasTransfers |= vartexture->hasPendingTransfers();
}
size_t unallocated = idealMemoryAllocation - totalVariableMemoryAllocation;
float pressure = (float)totalVariableMemoryAllocation / (float)allowedMemoryAllocation;
auto newState = MemoryPressureState::Idle;
if (pressure < UNDERSUBSCRIBED_PRESSURE_VALUE && (unallocated != 0 && canPromote)) {
newState = MemoryPressureState::Undersubscribed;
} else if (pressure > OVERSUBSCRIBED_PRESSURE_VALUE && canDemote) {
newState = MemoryPressureState::Oversubscribed;
} else if (hasTransfers) {
newState = MemoryPressureState::Transfer;
}
if (newState != _memoryPressureState) {
_memoryPressureState = newState;
// Clear the existing queue
_transferQueue = WorkQueue();
_promoteQueue = WorkQueue();
_demoteQueue = WorkQueue();
// Populate the existing textures into the queue
if (_memoryPressureState != MemoryPressureState::Idle) {
for (const auto& texture : strongTextures) {
// Race conditions can still leave nulls in the list, so we need to check
if (!texture) {
continue;
}
addToWorkQueue(texture);
}
}
}
}
TexturePointer GLVariableAllocationSupport::getNextWorkQueueItem(WorkQueue& workQueue) {
while (!workQueue.empty()) {
auto workTarget = workQueue.top();
auto texture = workTarget.first.lock();
if (!texture) {
workQueue.pop();
continue;
}
// Check whether the resulting texture can actually have work performed
GLTexture* gltexture = Backend::getGPUObject<GLTexture>(*texture);
GLVariableAllocationSupport* vartexture = dynamic_cast<GLVariableAllocationSupport*>(gltexture);
switch (_memoryPressureState) {
case MemoryPressureState::Oversubscribed:
if (vartexture->canDemote()) {
return texture;
}
break;
case MemoryPressureState::Undersubscribed:
if (vartexture->canPromote()) {
return texture;
}
break;
case MemoryPressureState::Transfer:
if (vartexture->hasPendingTransfers()) {
return texture;
}
break;
case MemoryPressureState::Idle:
Q_UNREACHABLE();
break;
}
// If we got here, then the texture has no work to do in the current state,
// so pop it off the queue and continue
workQueue.pop();
}
return TexturePointer();
}
void GLVariableAllocationSupport::processWorkQueue(WorkQueue& workQueue) {
if (workQueue.empty()) {
return;
}
// Get the front of the work queue to perform work
auto texture = getNextWorkQueueItem(workQueue);
if (!texture) {
return;
}
// Grab the first item off the demote queue
PROFILE_RANGE(render_gpu_gl, __FUNCTION__);
GLTexture* gltexture = Backend::getGPUObject<GLTexture>(*texture);
GLVariableAllocationSupport* vartexture = dynamic_cast<GLVariableAllocationSupport*>(gltexture);
switch (_memoryPressureState) {
case MemoryPressureState::Oversubscribed:
vartexture->demote();
workQueue.pop();
addToWorkQueue(texture);
_memoryPressureStateStale = true;
break;
case MemoryPressureState::Undersubscribed:
vartexture->promote();
workQueue.pop();
addToWorkQueue(texture);
_memoryPressureStateStale = true;
break;
case MemoryPressureState::Transfer:
if (vartexture->executeNextTransfer(texture)) {
workQueue.pop();
addToWorkQueue(texture);
#if THREADED_TEXTURE_BUFFERING
// Eagerly start the next buffering job if possible
texture = getNextWorkQueueItem(workQueue);
if (texture) {
gltexture = Backend::getGPUObject<GLTexture>(*texture);
vartexture = dynamic_cast<GLVariableAllocationSupport*>(gltexture);
vartexture->executeNextBuffer(texture);
}
#endif
}
break;
case MemoryPressureState::Idle:
Q_UNREACHABLE();
break;
}
}
void GLVariableAllocationSupport::processWorkQueues() {
if (MemoryPressureState::Idle == _memoryPressureState) {
return;
}
auto& workQueue = getActiveWorkQueue();
// Do work on the front of the queue
processWorkQueue(workQueue);
if (workQueue.empty()) {
_memoryPressureState = MemoryPressureState::Idle;
_memoryPressureStateStale = true;
}
}
void GLVariableAllocationSupport::manageMemory() {
PROFILE_RANGE(render_gpu_gl, __FUNCTION__);
updateMemoryPressure();
processWorkQueues();
}
bool GLVariableAllocationSupport::executeNextTransfer(const TexturePointer& currentTexture) {
#if THREADED_TEXTURE_BUFFERING
// If a transfer job is active on the buffering thread, but has not completed it's buffering lambda,
// then we need to exit early, since we don't want to have the transfer job leave scope while it's
// being used in another thread -- See https://highfidelity.fogbugz.com/f/cases/4626
if (_currentTransferJob && !_currentTransferJob->bufferingCompleted()) {
return false;
}
#endif
if (_populatedMip <= _allocatedMip) {
#if THREADED_TEXTURE_BUFFERING
_currentTransferJob.reset();
_currentTransferTexture.reset();
#endif
return true;
}
// If the transfer queue is empty, rebuild it
if (_pendingTransfers.empty()) {
populateTransferQueue();
}
bool result = false;
if (!_pendingTransfers.empty()) {
#if THREADED_TEXTURE_BUFFERING
// If there is a current transfer, but it's not the top of the pending transfer queue, then it's an orphan, so we want to abandon it.
if (_currentTransferJob && _currentTransferJob != _pendingTransfers.front()) {
_currentTransferJob.reset();
}
if (!_currentTransferJob) {
// Keeping hold of a strong pointer to the transfer job ensures that if the pending transfer queue is rebuilt, the transfer job
// doesn't leave scope, causing a crash in the buffering thread
_currentTransferJob = _pendingTransfers.front();
// Keeping hold of a strong pointer during the transfer ensures that the transfer thread cannot try to access a destroyed texture
_currentTransferTexture = currentTexture;
}
// transfer jobs use asynchronous buffering of the texture data because it may involve disk IO, so we execute a try here to determine if the buffering
// is complete
if (_currentTransferJob->tryTransfer()) {
_pendingTransfers.pop();
// Once a given job is finished, release the shared pointers keeping them alive
_currentTransferTexture.reset();
_currentTransferJob.reset();
result = true;
}
#else
if (_pendingTransfers.front()->tryTransfer()) {
_pendingTransfers.pop();
result = true;
}
#endif
}
return result;
}
#if THREADED_TEXTURE_BUFFERING
void GLVariableAllocationSupport::executeNextBuffer(const TexturePointer& currentTexture) {
if (_currentTransferJob && !_currentTransferJob->bufferingCompleted()) {
return;
}
// If the transfer queue is empty, rebuild it
if (_pendingTransfers.empty()) {
populateTransferQueue();
}
if (!_pendingTransfers.empty()) {
if (!_currentTransferJob) {
_currentTransferJob = _pendingTransfers.front();
_currentTransferTexture = currentTexture;
}
_currentTransferJob->startBuffering();
}
}
#endif
void GLVariableAllocationSupport::incrementPopulatedSize(Size delta) const {
_populatedSize += delta;
// Keep the 2 code paths to be able to debug
if (_size < _populatedSize) {
Backend::textureResourcePopulatedGPUMemSize.update(0, delta);
} else {
Backend::textureResourcePopulatedGPUMemSize.update(0, delta);
}
}
void GLVariableAllocationSupport::decrementPopulatedSize(Size delta) const {
_populatedSize -= delta;
// Keep the 2 code paths to be able to debug
if (_size < _populatedSize) {
Backend::textureResourcePopulatedGPUMemSize.update(delta, 0);
} else {
Backend::textureResourcePopulatedGPUMemSize.update(delta, 0);
}
}

View file

@ -8,10 +8,15 @@
#ifndef hifi_gpu_gl_GLTexture_h
#define hifi_gpu_gl_GLTexture_h
#include <QtCore/QThreadPool>
#include <QtConcurrent>
#include "GLShared.h"
#include "GLTextureTransfer.h"
#include "GLBackend.h"
#include "GLTexelFormat.h"
#include <thread>
#define THREADED_TEXTURE_BUFFERING 1
namespace gpu { namespace gl {
@ -20,214 +25,187 @@ struct GLFilterMode {
GLint magFilter;
};
class GLVariableAllocationSupport {
friend class GLBackend;
public:
GLVariableAllocationSupport();
virtual ~GLVariableAllocationSupport();
enum class MemoryPressureState {
Idle,
Transfer,
Oversubscribed,
Undersubscribed,
};
using QueuePair = std::pair<TextureWeakPointer, float>;
struct QueuePairLess {
bool operator()(const QueuePair& a, const QueuePair& b) {
return a.second < b.second;
}
};
using WorkQueue = std::priority_queue<QueuePair, std::vector<QueuePair>, QueuePairLess>;
class TransferJob {
using VoidLambda = std::function<void()>;
using VoidLambdaQueue = std::queue<VoidLambda>;
const GLTexture& _parent;
Texture::PixelsPointer _mipData;
size_t _transferOffset { 0 };
size_t _transferSize { 0 };
bool _bufferingRequired { true };
VoidLambda _transferLambda;
VoidLambda _bufferingLambda;
#if THREADED_TEXTURE_BUFFERING
// Indicates if a transfer from backing storage to interal storage has started
QFuture<void> _bufferingStatus;
static QThreadPool* _bufferThreadPool;
#endif
public:
TransferJob(const TransferJob& other) = delete;
TransferJob(const GLTexture& parent, std::function<void()> transferLambda);
TransferJob(const GLTexture& parent, uint16_t sourceMip, uint16_t targetMip, uint8_t face, uint32_t lines = 0, uint32_t lineOffset = 0);
~TransferJob();
bool tryTransfer();
#if THREADED_TEXTURE_BUFFERING
void startBuffering();
bool bufferingRequired() const;
bool bufferingCompleted() const;
static void startBufferingThread();
#endif
private:
void transfer();
};
using TransferJobPointer = std::shared_ptr<TransferJob>;
using TransferQueue = std::queue<TransferJobPointer>;
static MemoryPressureState _memoryPressureState;
public:
static void addMemoryManagedTexture(const TexturePointer& texturePointer);
protected:
static size_t _frameTexturesCreated;
static std::atomic<bool> _memoryPressureStateStale;
static std::list<TextureWeakPointer> _memoryManagedTextures;
static WorkQueue _transferQueue;
static WorkQueue _promoteQueue;
static WorkQueue _demoteQueue;
#if THREADED_TEXTURE_BUFFERING
static TexturePointer _currentTransferTexture;
static TransferJobPointer _currentTransferJob;
#endif
static const uvec3 INITIAL_MIP_TRANSFER_DIMENSIONS;
static const uvec3 MAX_TRANSFER_DIMENSIONS;
static const size_t MAX_TRANSFER_SIZE;
static void updateMemoryPressure();
static void processWorkQueues();
static void processWorkQueue(WorkQueue& workQueue);
static TexturePointer getNextWorkQueueItem(WorkQueue& workQueue);
static void addToWorkQueue(const TexturePointer& texture);
static WorkQueue& getActiveWorkQueue();
static void manageMemory();
//bool canPromoteNoAllocate() const { return _allocatedMip < _populatedMip; }
bool canPromote() const { return _allocatedMip > _minAllocatedMip; }
bool canDemote() const { return _allocatedMip < _maxAllocatedMip; }
bool hasPendingTransfers() const { return _populatedMip > _allocatedMip; }
#if THREADED_TEXTURE_BUFFERING
void executeNextBuffer(const TexturePointer& currentTexture);
#endif
bool executeNextTransfer(const TexturePointer& currentTexture);
virtual void populateTransferQueue() = 0;
virtual void promote() = 0;
virtual void demote() = 0;
// THe amount of memory currently allocated
Size _size { 0 };
// The amount of memory currnently populated
void incrementPopulatedSize(Size delta) const;
void decrementPopulatedSize(Size delta) const;
mutable Size _populatedSize { 0 };
// The allocated mip level, relative to the number of mips in the gpu::Texture object
// The relationship between a given glMip to the original gpu::Texture mip is always
// glMip + _allocatedMip
uint16 _allocatedMip { 0 };
// The populated mip level, relative to the number of mips in the gpu::Texture object
// This must always be >= the allocated mip
uint16 _populatedMip { 0 };
// The highest (lowest resolution) mip that we will support, relative to the number
// of mips in the gpu::Texture object
uint16 _maxAllocatedMip { 0 };
// The lowest (highest resolution) mip that we will support, relative to the number
// of mips in the gpu::Texture object
uint16 _minAllocatedMip { 0 };
// Contains a series of lambdas that when executed will transfer data to the GPU, modify
// the _populatedMip and update the sampler in order to fully populate the allocated texture
// until _populatedMip == _allocatedMip
TransferQueue _pendingTransfers;
};
class GLTexture : public GLObject<Texture> {
using Parent = GLObject<Texture>;
friend class GLBackend;
friend class GLVariableAllocationSupport;
public:
static const uint16_t INVALID_MIP { (uint16_t)-1 };
static const uint8_t INVALID_FACE { (uint8_t)-1 };
static void initTextureTransferHelper();
static std::shared_ptr<GLTextureTransferHelper> _textureTransferHelper;
template <typename GLTextureType>
static GLTexture* sync(GLBackend& backend, const TexturePointer& texturePointer, bool needTransfer) {
const Texture& texture = *texturePointer;
// Special case external textures
//CLIMAX_MERGE_START
//Z:/HiFi_Android/HiFi_GIT/libraries/gpu-gl-android/src/gpu/gl/../gles/../gl/GLTexture.h:37:32: error: no member named 'isExternal' in 'gpu::Texture::Usage'
// The only instance of this being used again. replace.
// if (texture.getUsage().isExternal()) {
// Texture::ExternalUpdates updates = texture.getUpdates();
// if (!updates.empty()) {
// Texture::ExternalRecycler recycler = texture.getExternalRecycler();
// Q_ASSERT(recycler);
// // Discard any superfluous updates
// while (updates.size() > 1) {
// const auto& update = updates.front();
// // Superfluous updates will never have been read, but we want to ensure the previous
// // writes to them are complete before they're written again, so return them with the
// // same fences they arrived with. This can happen on any thread because no GL context
// // work is involved
// recycler(update.first, update.second);
// updates.pop_front();
// }
// // The last texture remaining is the one we'll use to create the GLTexture
// const auto& update = updates.front();
// // Check for a fence, and if it exists, inject a wait into the command stream, then destroy the fence
// if (update.second) {
// GLsync fence = static_cast<GLsync>(update.second);
// glWaitSync(fence, 0, GL_TIMEOUT_IGNORED);
// glDeleteSync(fence);
// }
// // Create the new texture object (replaces any previous texture object)
// new GLTextureType(backend.shared_from_this(), texture, update.first);
// }
// Return the texture object (if any) associated with the texture, without extensive logic
// (external textures are
//return Backend::getGPUObject<GLTextureType>(texture);
//}
//CLIMAX_MERGE_END
if (!texture.isDefined()) {
// NO texture definition yet so let's avoid thinking
return nullptr;
}
// If the object hasn't been created, or the object definition is out of date, drop and re-create
GLTexture* object = Backend::getGPUObject<GLTextureType>(texture);
// Create the texture if need be (force re-creation if the storage stamp changes
// for easier use of immutable storage)
if (!object || object->isInvalid()) {
// This automatically any previous texture
object = new GLTextureType(backend.shared_from_this(), texture, needTransfer);
if (!object->_transferrable) {
object->createTexture();
object->_contentStamp = texture.getDataStamp();
object->updateSize();
object->postTransfer();
}
}
// Object maybe doens't neet to be tranasferred after creation
if (!object->_transferrable) {
return object;
}
// If we just did a transfer, return the object after doing post-transfer work
if (GLSyncState::Transferred == object->getSyncState()) {
object->postTransfer();
}
if (object->isOutdated()) {
// Object might be outdated, if so, start the transfer
// (outdated objects that are already in transfer will have reported 'true' for ready()
_textureTransferHelper->transferTexture(texturePointer);
return nullptr;
}
if (!object->isReady()) {
return nullptr;
}
((GLTexture*)object)->updateMips();
return object;
}
template <typename GLTextureType>
static GLuint getId(GLBackend& backend, const TexturePointer& texture, bool shouldSync) {
if (!texture) {
return 0;
}
GLTexture* object { nullptr };
if (shouldSync) {
object = sync<GLTextureType>(backend, texture, shouldSync);
} else {
object = Backend::getGPUObject<GLTextureType>(*texture);
}
if (!object) {
return 0;
}
if (!shouldSync) {
return object->_id;
}
// Don't return textures that are in transfer state
if ((object->getSyncState() != GLSyncState::Idle) ||
// Don't return transferrable textures that have never completed transfer
(!object->_transferrable || 0 != object->_transferCount)) {
return 0;
}
return object->_id;
}
~GLTexture();
// Is this texture generated outside the GPU library?
const bool _external;
const GLuint& _texture { _id };
const std::string _source;
const Stamp _storageStamp;
const GLenum _target;
const GLenum _internalFormat;
const uint16 _maxMip;
uint16 _minMip;
const GLuint _virtualSize; // theoretical size as expected
Stamp _contentStamp { 0 };
const bool _transferrable;
Size _transferCount { 0 };
GLuint size() const { return _size; }
GLSyncState getSyncState() const { return _syncState; }
GLTexelFormat _texelFormat;
// Is the storage out of date relative to the gpu texture?
bool isInvalid() const;
static const std::vector<GLenum>& getFaceTargets(GLenum textureType);
static uint8_t getFaceCount(GLenum textureType);
static GLenum getGLTextureType(const Texture& texture);
// Is the content out of date relative to the gpu texture?
bool isOutdated() const;
// Is the texture in a state where it can be rendered with no work?
bool isReady() const;
// Execute any post-move operations that must occur only on the main thread
virtual void postTransfer();
uint16 usedMipLevels() const { return (_maxMip - _minMip) + 1; }
static const size_t CUBE_NUM_FACES = 6;
static const GLenum CUBE_FACE_LAYOUT[6];
static const uint8_t TEXTURE_2D_NUM_FACES = 1;
static const uint8_t TEXTURE_CUBE_NUM_FACES = 6;
static const GLenum CUBE_FACE_LAYOUT[TEXTURE_CUBE_NUM_FACES];
static const GLFilterMode FILTER_MODES[Sampler::NUM_FILTERS];
static const GLenum WRAP_MODES[Sampler::NUM_WRAP_MODES];
// Return a floating point value indicating how much of the allowed
// texture memory we are currently consuming. A value of 0 indicates
// no texture memory usage, while a value of 1 indicates all available / allowed memory
// is consumed. A value above 1 indicates that there is a problem.
static float getMemoryPressure();
protected:
static const std::vector<GLenum>& getFaceTargets(GLenum textureType);
static GLenum getGLTextureType(const Texture& texture);
const GLuint _size { 0 }; // true size as reported by the gl api
std::atomic<GLSyncState> _syncState { GLSyncState::Idle };
GLTexture(const std::weak_ptr<gl::GLBackend>& backend, const Texture& texture, GLuint id, bool transferrable);
GLTexture(const std::weak_ptr<gl::GLBackend>& backend, const Texture& texture, GLuint id);
void setSyncState(GLSyncState syncState) { _syncState = syncState; }
void createTexture();
virtual void updateMips() {}
virtual void allocateStorage() const = 0;
virtual void updateSize() const = 0;
virtual void syncSampler() const = 0;
virtual Size size() const = 0;
virtual void generateMips() const = 0;
virtual void withPreservedTexture(std::function<void()> f) const;
virtual void syncSampler() const = 0;
protected:
void setSize(GLuint size) const;
virtual Size copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum internalFormat, GLenum format, GLenum type, Size sourceSize, const void* sourcePointer) const = 0;
virtual Size copyMipFaceFromTexture(uint16_t sourceMip, uint16_t targetMip, uint8_t face) const final;
virtual void copyTextureMipsInGPUMem(GLuint srcId, GLuint destId, uint16_t srcMipOffset, uint16_t destMipOffset, uint16_t populatedMips) {} // Only relevant for Variable Allocation textures
virtual void startTransfer();
// Returns true if this is the last block required to complete transfer
virtual bool continueTransfer() { return false; }
virtual void finishTransfer();
private:
friend class GLTextureTransferHelper;
friend class GLBackend;
GLTexture(const std::weak_ptr<gl::GLBackend>& backend, const Texture& texture, GLuint id);
};
class GLExternalTexture : public GLTexture {
using Parent = GLTexture;
friend class GLBackend;
public:
~GLExternalTexture();
protected:
GLExternalTexture(const std::weak_ptr<gl::GLBackend>& backend, const Texture& texture, GLuint id);
void generateMips() const override {}
void syncSampler() const override {}
Size copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum internalFormat, GLenum format, GLenum type, Size sourceSize, const void* sourcePointer) const override { return 0;}
Size size() const override { return 0; }
};
} }
#endif

View file

@ -1,207 +0,0 @@
//
// Created by Bradley Austin Davis on 2016/04/03
// Copyright 2013-2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "GLTextureTransfer.h"
#include <gl/GLHelpers.h>
#include <gl/Context.h>
#include "GLShared.h"
#include "GLTexture.h"
#ifdef HAVE_NSIGHT
#include "nvToolsExt.h"
std::unordered_map<TexturePointer, nvtxRangeId_t> _map;
#endif
#ifdef TEXTURE_TRANSFER_PBOS
#define TEXTURE_TRANSFER_BLOCK_SIZE (64 * 1024)
#define TEXTURE_TRANSFER_PBO_COUNT 128
#endif
using namespace gpu;
using namespace gpu::gl;
GLTextureTransferHelper::GLTextureTransferHelper() {
#ifdef THREADED_TEXTURE_TRANSFER
setObjectName("TextureTransferThread");
_context.create();
initialize(true, QThread::LowPriority);
// Clean shutdown on UNIX, otherwise _canvas is freed early
connect(qApp, &QCoreApplication::aboutToQuit, [&] { terminate(); });
#else
initialize(false, QThread::LowPriority);
#endif
}
GLTextureTransferHelper::~GLTextureTransferHelper() {
#ifdef THREADED_TEXTURE_TRANSFER
if (isStillRunning()) {
terminate();
}
#else
terminate();
#endif
}
void GLTextureTransferHelper::transferTexture(const gpu::TexturePointer& texturePointer) {
GLTexture* object = Backend::getGPUObject<GLTexture>(*texturePointer);
//CLIMAX_MERGE_START
//Backend::incrementTextureGPUTransferCount();
object->setSyncState(GLSyncState::Pending);
Lock lock(_mutex);
_pendingTextures.push_back(texturePointer);
}
void GLTextureTransferHelper::setup() {
#ifdef THREADED_TEXTURE_TRANSFER
_context.makeCurrent();
#ifdef TEXTURE_TRANSFER_FORCE_DRAW
// FIXME don't use opengl 4.5 DSA functionality without verifying it's present
glCreateRenderbuffers(1, &_drawRenderbuffer);
glNamedRenderbufferStorage(_drawRenderbuffer, GL_RGBA8, 128, 128);
glCreateFramebuffers(1, &_drawFramebuffer);
glNamedFramebufferRenderbuffer(_drawFramebuffer, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _drawRenderbuffer);
glCreateFramebuffers(1, &_readFramebuffer);
#endif
#ifdef TEXTURE_TRANSFER_PBOS
std::array<GLuint, TEXTURE_TRANSFER_PBO_COUNT> pbos;
glCreateBuffers(TEXTURE_TRANSFER_PBO_COUNT, &pbos[0]);
for (uint32_t i = 0; i < TEXTURE_TRANSFER_PBO_COUNT; ++i) {
TextureTransferBlock newBlock;
newBlock._pbo = pbos[i];
glNamedBufferStorage(newBlock._pbo, TEXTURE_TRANSFER_BLOCK_SIZE, 0, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT);
newBlock._mapped = glMapNamedBufferRange(newBlock._pbo, 0, TEXTURE_TRANSFER_BLOCK_SIZE, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT);
_readyQueue.push(newBlock);
}
#endif
#endif
}
void GLTextureTransferHelper::shutdown() {
#ifdef THREADED_TEXTURE_TRANSFER
_context.makeCurrent();
#endif
#ifdef TEXTURE_TRANSFER_FORCE_DRAW
glNamedFramebufferRenderbuffer(_drawFramebuffer, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, 0);
glDeleteFramebuffers(1, &_drawFramebuffer);
_drawFramebuffer = 0;
glDeleteFramebuffers(1, &_readFramebuffer);
_readFramebuffer = 0;
glNamedFramebufferTexture(_readFramebuffer, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0);
glDeleteRenderbuffers(1, &_drawRenderbuffer);
_drawRenderbuffer = 0;
#endif
}
void GLTextureTransferHelper::queueExecution(VoidLambda lambda) {
Lock lock(_mutex);
_pendingCommands.push_back(lambda);
}
#define MAX_TRANSFERS_PER_PASS 2
bool GLTextureTransferHelper::process() {
// Take any new textures or commands off the queue
VoidLambdaList pendingCommands;
TextureList newTransferTextures;
{
Lock lock(_mutex);
newTransferTextures.swap(_pendingTextures);
pendingCommands.swap(_pendingCommands);
}
if (!pendingCommands.empty()) {
for (auto command : pendingCommands) {
command();
}
glFlush();
}
if (!newTransferTextures.empty()) {
for (auto& texturePointer : newTransferTextures) {
#ifdef HAVE_NSIGHT
_map[texturePointer] = nvtxRangeStart("TextureTansfer");
#endif
GLTexture* object = Backend::getGPUObject<GLTexture>(*texturePointer);
object->startTransfer();
_transferringTextures.push_back(texturePointer);
_textureIterator = _transferringTextures.begin();
}
_transferringTextures.sort([](const gpu::TexturePointer& a, const gpu::TexturePointer& b)->bool {
return a->getSize() < b->getSize();
});
}
// No transfers in progress, sleep
if (_transferringTextures.empty()) {
#ifdef THREADED_TEXTURE_TRANSFER
QThread::usleep(1);
#endif
return true;
}
static auto lastReport = usecTimestampNow();
auto now = usecTimestampNow();
auto lastReportInterval = now - lastReport;
if (lastReportInterval > USECS_PER_SECOND * 4) {
lastReport = now;
qDebug() << "Texture list " << _transferringTextures.size();
}
size_t transferCount = 0;
for (_textureIterator = _transferringTextures.begin(); _textureIterator != _transferringTextures.end();) {
if (++transferCount > MAX_TRANSFERS_PER_PASS) {
break;
}
auto texture = *_textureIterator;
GLTexture* gltexture = Backend::getGPUObject<GLTexture>(*texture);
if (gltexture->continueTransfer()) {
++_textureIterator;
continue;
}
gltexture->finishTransfer();
#ifdef TEXTURE_TRANSFER_FORCE_DRAW
// FIXME force a draw on the texture transfer thread before passing the texture to the main thread for use
#endif
#ifdef THREADED_TEXTURE_TRANSFER
clientWait();
#endif
gltexture->_contentStamp = gltexture->_gpuObject.getDataStamp();
gltexture->updateSize();
gltexture->setSyncState(gpu::gl::GLSyncState::Transferred);
//CLIMAX_MERGE_START
//Backend::decrementTextureGPUTransferCount();
#ifdef HAVE_NSIGHT
// Mark the texture as transferred
nvtxRangeEnd(_map[texture]);
_map.erase(texture);
#endif
_textureIterator = _transferringTextures.erase(_textureIterator);
}
#ifdef THREADED_TEXTURE_TRANSFER
if (!_transferringTextures.empty()) {
// Don't saturate the GPU
clientWait();
} else {
// Don't saturate the CPU
QThread::msleep(1);
}
#endif
return true;
}

View file

@ -1,78 +0,0 @@
//
// Created by Bradley Austin Davis on 2016/04/03
// Copyright 2013-2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_gpu_gl_GLTextureTransfer_h
#define hifi_gpu_gl_GLTextureTransfer_h
#include <QtGlobal>
#include <QtCore/QSharedPointer>
#include <GenericQueueThread.h>
#include <gl/Context.h>
#include "GLShared.h"
#ifdef Q_OS_WIN
#define THREADED_TEXTURE_TRANSFER
#endif
#ifdef THREADED_TEXTURE_TRANSFER
// FIXME when sparse textures are enabled, it's harder to force a draw on the transfer thread
// also, the current draw code is implicitly using OpenGL 4.5 functionality
//#define TEXTURE_TRANSFER_FORCE_DRAW
// FIXME PBO's increase the complexity and don't seem to work reliably
//#define TEXTURE_TRANSFER_PBOS
#endif
namespace gpu { namespace gl {
using TextureList = std::list<TexturePointer>;
using TextureListIterator = TextureList::iterator;
class GLTextureTransferHelper : public GenericThread {
public:
using VoidLambda = std::function<void()>;
using VoidLambdaList = std::list<VoidLambda>;
using Pointer = std::shared_ptr<GLTextureTransferHelper>;
GLTextureTransferHelper();
~GLTextureTransferHelper();
void transferTexture(const gpu::TexturePointer& texturePointer);
void queueExecution(VoidLambda lambda);
void setup() override;
void shutdown() override;
bool process() override;
private:
#ifdef THREADED_TEXTURE_TRANSFER
::gl::OffscreenContext _context;
#endif
#ifdef TEXTURE_TRANSFER_FORCE_DRAW
// Framebuffers / renderbuffers for forcing access to the texture on the transfer thread
GLuint _drawRenderbuffer { 0 };
GLuint _drawFramebuffer { 0 };
GLuint _readFramebuffer { 0 };
#endif
// A mutex for protecting items access on the render and transfer threads
Mutex _mutex;
// Commands that have been submitted for execution on the texture transfer thread
VoidLambdaList _pendingCommands;
// Textures that have been submitted for transfer
TextureList _pendingTextures;
// Textures currently in the transfer process
// Only used on the transfer thread
TextureList _transferringTextures;
TextureListIterator _textureIterator;
};
} }
#endif

View file

@ -27,6 +27,12 @@ class GLESBackend : public GLBackend {
friend class Context;
public:
static const GLint TRANSFORM_OBJECT_SLOT { 31 };
static const GLint RESOURCE_TRANSFER_TEX_UNIT { 32 };
static const GLint RESOURCE_TRANSFER_EXTRA_TEX_UNIT { 33 };
static const GLint RESOURCE_BUFFER_TEXBUF_TEX_UNIT { 34 };
static const GLint RESOURCE_BUFFER_SLOT0_TEX_UNIT { 35 };
explicit GLESBackend(bool syncCache) : Parent(syncCache) {}
GLESBackend() : Parent() {}
virtual ~GLESBackend() {
@ -38,33 +44,95 @@ public:
static const std::string GLES_VERSION;
const std::string& getVersion() const override { return GLES_VERSION; }
class GLESTexture : public GLTexture {
using Parent = GLTexture;
GLuint allocate();
public:
GLESTexture(const std::weak_ptr<GLBackend>& backend, const Texture& buffer, GLuint externalId);
GLESTexture(const std::weak_ptr<GLBackend>& backend, const Texture& buffer, bool transferrable);
friend class GLESBackend;
GLuint allocate(const Texture& texture);
protected:
void transferMip(uint16_t mipLevel, uint8_t face) const;
void startTransfer() override;
void allocateStorage() const override;
void updateSize() const override;
void syncSampler() const override;
GLESTexture(const std::weak_ptr<GLBackend>& backend, const Texture& buffer);
void generateMips() const override;
Size copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum internalFormat, GLenum format, GLenum type, Size sourceSize, const void* sourcePointer) const override;
void syncSampler() const override;
void withPreservedTexture(std::function<void()> f) const;
};
//
// Textures that have fixed allocation sizes and cannot be managed at runtime
//
class GLESFixedAllocationTexture : public GLESTexture {
using Parent = GLESTexture;
friend class GLESBackend;
public:
GLESFixedAllocationTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture);
~GLESFixedAllocationTexture();
protected:
Size size() const override { return _size; }
void allocateStorage() const;
void syncSampler() const override;
const Size _size { 0 };
};
class GLESAttachmentTexture : public GLESFixedAllocationTexture {
using Parent = GLESFixedAllocationTexture;
friend class GLESBackend;
protected:
GLESAttachmentTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture);
~GLESAttachmentTexture();
};
class GLESStrictResourceTexture : public GLESFixedAllocationTexture {
using Parent = GLESFixedAllocationTexture;
friend class GLESBackend;
protected:
GLESStrictResourceTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture);
~GLESStrictResourceTexture();
};
class GLESVariableAllocationTexture : public GLESTexture, public GLVariableAllocationSupport {
using Parent = GLESTexture;
friend class GLESBackend;
using PromoteLambda = std::function<void()>;
protected:
GLESVariableAllocationTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture);
~GLESVariableAllocationTexture();
void allocateStorage(uint16 allocatedMip);
void syncSampler() const override;
void promote() override;
void demote() override;
void populateTransferQueue() override;
Size copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum internalFormat, GLenum format, GLenum type, Size sourceSize, const void* sourcePointer) const override;
Size copyMipsFromTexture();
void copyTextureMipsInGPUMem(GLuint srcId, GLuint destId, uint16_t srcMipOffset, uint16_t destMipOffset, uint16_t populatedMips) override;
Size size() const override { return _size; }
};
class GLESResourceTexture : public GLESVariableAllocationTexture {
using Parent = GLESVariableAllocationTexture;
friend class GLESBackend;
protected:
GLESResourceTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture);
~GLESResourceTexture();
};
protected:
GLuint getFramebufferID(const FramebufferPointer& framebuffer) override;
GLFramebuffer* syncGPUObject(const Framebuffer& framebuffer) override;
GLuint getBufferID(const Buffer& buffer) override;
GLuint getResourceBufferID(const Buffer& buffer);
GLBuffer* syncGPUObject(const Buffer& buffer) override;
GLuint getTextureID(const TexturePointer& texture, bool needTransfer = true) override;
GLTexture* syncGPUObject(const TexturePointer& texture, bool sync = true) override;
GLTexture* syncGPUObject(const TexturePointer& texture) override;
GLuint getQueryID(const QueryPointer& query) override;
GLQuery* syncGPUObject(const Query& query) override;
@ -78,14 +146,13 @@ protected:
void do_multiDrawIndexedIndirect(const Batch& batch, size_t paramOffset) override;
// Input Stage
void updateInput() override;
void resetInputStage() override;
void updateInput() override;
// Synchronize the state cache of this Backend with the actual real state of the GL Context
void transferTransformState(const Batch& batch) const override;
void initTransform() override;
void updateTransform(const Batch& batch);
void resetTransformStage();
void updateTransform(const Batch& batch) override;
// Resource Stage
bool bindResourceBuffer(uint32_t slot, BufferPointer& buffer) override;
@ -93,6 +160,11 @@ protected:
// Output stage
void do_blit(const Batch& batch, size_t paramOffset) override;
std::string getBackendShaderHeader() const override;
void makeProgramBindings(ShaderObject& shaderObject) override;
int makeResourceBufferSlots(GLuint glprogram, const Shader::BindingSet& slotBindings,Shader::SlotSet& resourceBuffers) override;
};
} }

View file

@ -53,10 +53,12 @@ public:
GL_COLOR_ATTACHMENT15 };
int unit = 0;
auto backend = _backend.lock();
for (auto& b : _gpuObject.getRenderBuffers()) {
surface = b._texture;
if (surface) {
gltexture = gl::GLTexture::sync<GLESBackend::GLESTexture>(*_backend.lock().get(), surface, false); // Grab the gltexture and don't transfer
Q_ASSERT(TextureUsageType::RENDERBUFFER == surface->getUsageType());
gltexture = backend->syncGPUObject(surface);
} else {
gltexture = nullptr;
}
@ -81,9 +83,11 @@ public:
}
if (_gpuObject.getDepthStamp() != _depthStamp) {
auto backend = _backend.lock();
auto surface = _gpuObject.getDepthStencilBuffer();
if (_gpuObject.hasDepthStencil() && surface) {
gltexture = gl::GLTexture::sync<GLESBackend::GLESTexture>(*_backend.lock().get(), surface, false); // Grab the gltexture and don't transfer
Q_ASSERT(TextureUsageType::RENDERBUFFER == surface->getUsageType());
gltexture = backend->syncGPUObject(surface);
}
if (gltexture) {
@ -99,8 +103,8 @@ public:
if (!_colorBuffers.empty()) {
glDrawBuffers((GLsizei)_colorBuffers.size(), _colorBuffers.data());
} else {
static const std::vector<GLenum> NO_BUFFERS{ GL_NONE };
glDrawBuffers((GLsizei)NO_BUFFERS.size(), NO_BUFFERS.data());
GLenum DrawBuffers[1] = {GL_NONE};
glDrawBuffers(1, DrawBuffers);
}
// Now check for completness
@ -111,7 +115,7 @@ public:
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, currentFBO);
}
checkStatus(GL_DRAW_FRAMEBUFFER);
checkStatus();
}
@ -120,7 +124,7 @@ public:
: Parent(backend, framebuffer, allocate()) { }
};
gl::GLFramebuffer* gpu::gles::GLESBackend::syncGPUObject(const Framebuffer& framebuffer) {
gl::GLFramebuffer* GLESBackend::syncGPUObject(const Framebuffer& framebuffer) {
return GLESFramebuffer::sync<GLESFramebuffer>(*this, framebuffer);
}

View file

@ -0,0 +1,113 @@
//
// Created by Sam Gateau on 2017/04/13
// Copyright 2013-2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "GLESBackend.h"
#include "../gl/GLShader.h"
using namespace gpu;
using namespace gpu::gl;
using namespace gpu::gles;
// GLSL version
std::string GLESBackend::getBackendShaderHeader() const {
return Parent::getBackendShaderHeader();
}
int GLESBackend::makeResourceBufferSlots(GLuint glprogram, const Shader::BindingSet& slotBindings,Shader::SlotSet& resourceBuffers) {
GLint ssboCount = 0;
GLint uniformsCount = 0;
glGetProgramiv(glprogram, GL_ACTIVE_UNIFORMS, &uniformsCount);
for (int i = 0; i < uniformsCount; i++) {
const GLint NAME_LENGTH = 256;
GLchar name[NAME_LENGTH];
GLint length = 0;
GLint size = 0;
GLenum type = 0;
glGetActiveUniform(glprogram, i, NAME_LENGTH, &length, &size, &type, name);
GLint location = glGetUniformLocation(glprogram, name);
const GLint INVALID_UNIFORM_LOCATION = -1;
// Try to make sense of the gltype
auto elementResource = getFormatFromGLUniform(type);
// The uniform as a standard var type
if (location != INVALID_UNIFORM_LOCATION) {
if (elementResource._resource == Resource::BUFFER) {
if (elementResource._element.getSemantic() == gpu::RESOURCE_BUFFER) {
// Let's make sure the name doesn't contains an array element
std::string sname(name);
auto foundBracket = sname.find_first_of('[');
if (foundBracket != std::string::npos) {
// std::string arrayname = sname.substr(0, foundBracket);
if (sname[foundBracket + 1] == '0') {
sname = sname.substr(0, foundBracket);
} else {
// skip this uniform since it's not the first element of an array
continue;
}
}
// For texture/Sampler, the location is the actual binding value
GLint binding = -1;
glGetUniformiv(glprogram, location, &binding);
if (binding == GLESBackend::TRANSFORM_OBJECT_SLOT) {
continue;
}
auto requestedBinding = slotBindings.find(std::string(sname));
if (requestedBinding != slotBindings.end()) {
GLint requestedLoc = (*requestedBinding)._location + GLESBackend::RESOURCE_BUFFER_SLOT0_TEX_UNIT;
if (binding != requestedLoc) {
binding = requestedLoc;
}
} else {
binding += GLESBackend::RESOURCE_BUFFER_SLOT0_TEX_UNIT;
}
glProgramUniform1i(glprogram, location, binding);
ssboCount++;
resourceBuffers.insert(Shader::Slot(name, binding, elementResource._element, elementResource._resource));
}
}
}
}
return ssboCount;
}
void GLESBackend::makeProgramBindings(ShaderObject& shaderObject) {
if (!shaderObject.glprogram) {
return;
}
GLuint glprogram = shaderObject.glprogram;
GLint loc = -1;
GLBackend::makeProgramBindings(shaderObject);
// now assign the ubo binding, then DON't relink!
//Check for gpu specific uniform slotBindings
loc = glGetUniformLocation(glprogram, "transformObjectBuffer");
if (loc >= 0) {
glProgramUniform1i(glprogram, loc, GLESBackend::TRANSFORM_OBJECT_SLOT);
shaderObject.transformObjectSlot = GLESBackend::TRANSFORM_OBJECT_SLOT;
}
loc = glGetUniformBlockIndex(glprogram, "transformCameraBuffer");
if (loc >= 0) {
glUniformBlockBinding(glprogram, loc, gpu::TRANSFORM_CAMERA_SLOT);
shaderObject.transformCameraSlot = gpu::TRANSFORM_CAMERA_SLOT;
}
(void)CHECK_GL_ERROR();
}

View file

@ -12,41 +12,90 @@
#include <unordered_set>
#include <unordered_map>
#include <QtCore/QThread>
// #include "../gl/GLTexelFormat.h"
#include "../gl/GLTexelFormat.h"
using namespace gpu;
using namespace gpu::gl;
using namespace gpu::gles;
//using GL41TexelFormat = GLTexelFormat;
GLTexture* GLESBackend::syncGPUObject(const TexturePointer& texturePointer) {
if (!texturePointer) {
return nullptr;
}
const Texture& texture = *texturePointer;
if (TextureUsageType::EXTERNAL == texture.getUsageType()) {
return Parent::syncGPUObject(texturePointer);
}
if (!texture.isDefined()) {
// NO texture definition yet so let's avoid thinking
return nullptr;
}
GLESTexture* object = Backend::getGPUObject<GLESTexture>(texture);
if (!object) {
switch (texture.getUsageType()) {
case TextureUsageType::RENDERBUFFER:
object = new GLESAttachmentTexture(shared_from_this(), texture);
break;
case TextureUsageType::STRICT_RESOURCE:
qCDebug(gpugllogging) << "Strict texture " << texture.source().c_str();
object = new GLESStrictResourceTexture(shared_from_this(), texture);
break;
case TextureUsageType::RESOURCE:
qCDebug(gpugllogging) << "variable / Strict texture " << texture.source().c_str();
object = new GLESResourceTexture(shared_from_this(), texture);
GLVariableAllocationSupport::addMemoryManagedTexture(texturePointer);
break;
default:
Q_UNREACHABLE();
}
} else {
if (texture.getUsageType() == TextureUsageType::RESOURCE) {
auto varTex = static_cast<GLESVariableAllocationTexture*> (object);
if (varTex->_minAllocatedMip > 0) {
auto minAvailableMip = texture.minAvailableMipLevel();
if (minAvailableMip < varTex->_minAllocatedMip) {
varTex->_minAllocatedMip = minAvailableMip;
GLESVariableAllocationTexture::_memoryPressureStateStale = true;
}
}
}
}
return object;
}
using GLESTexture = GLESBackend::GLESTexture;
GLuint GLESTexture::allocate() {
//CLIMAX_MERGE_START
//Backend::incrementTextureGPUCount();
//CLIMAX_MERGE_END
GLESTexture::GLESTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture)
: GLTexture(backend, texture, allocate(texture)) {
}
void GLESTexture::withPreservedTexture(std::function<void()> f) const {
glActiveTexture(GL_TEXTURE0 + GLESBackend::RESOURCE_TRANSFER_TEX_UNIT);
glBindTexture(_target, _texture);
(void)CHECK_GL_ERROR();
f();
glBindTexture(_target, 0);
(void)CHECK_GL_ERROR();
}
GLuint GLESTexture::allocate(const Texture& texture) {
GLuint result;
glGenTextures(1, &result);
return result;
}
GLuint GLESBackend::getTextureID(const TexturePointer& texture, bool transfer) {
return GLESTexture::getId<GLESTexture>(*this, texture, transfer);
}
GLTexture* GLESBackend::syncGPUObject(const TexturePointer& texture, bool transfer) {
return GLESTexture::sync<GLESTexture>(*this, texture, transfer);
}
GLESTexture::GLESTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture, GLuint externalId)
: GLTexture(backend, texture, externalId) {
}
GLESTexture::GLESTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture, bool transferrable)
: GLTexture(backend, texture, allocate(), transferrable) {
}
void GLESTexture::generateMips() const {
withPreservedTexture([&] {
@ -55,102 +104,62 @@ void GLESTexture::generateMips() const {
(void)CHECK_GL_ERROR();
}
void GLESTexture::allocateStorage() const {
GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat());
glTexParameteri(_target, GL_TEXTURE_BASE_LEVEL, 0);
(void)CHECK_GL_ERROR();
glTexParameteri(_target, GL_TEXTURE_MAX_LEVEL, _maxMip - _minMip);
(void)CHECK_GL_ERROR();
/* if (GLEW_VERSION_4_2 && !_gpuObject.getTexelFormat().isCompressed()) {
// Get the dimensions, accounting for the downgrade level
Vec3u dimensions = _gpuObject.evalMipDimensions(_minMip);
glTexStorage2D(_target, usedMipLevels(), texelFormat.internalFormat, dimensions.x, dimensions.y);
(void)CHECK_GL_ERROR();
} else {*/
for (uint16_t l = _minMip; l <= _maxMip; l++) {
// Get the mip level dimensions, accounting for the downgrade level
Vec3u dimensions = _gpuObject.evalMipDimensions(l);
for (GLenum target : getFaceTargets(_target)) {
glTexImage2D(target, l - _minMip, texelFormat.internalFormat, dimensions.x, dimensions.y, 0, texelFormat.format, texelFormat.type, NULL);
(void)CHECK_GL_ERROR();
}
}
//}
}
Size GLESTexture::copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum internalFormat, GLenum format, GLenum type, Size sourceSize, const void* sourcePointer) const {
Size amountCopied = sourceSize;
if (GL_TEXTURE_2D == _target) {
qDebug() << "[UNIMPLEMENTED] GL_TEXTURE_2D internalFormat: " << internalFormat;
/*switch (internalFormat) {
case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT:
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
case GL_COMPRESSED_RED_RGTC1:
case GL_COMPRESSED_RG_RGTC2:
case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM:
case GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT:
glCompressedTexSubImage2D(_target, mip, 0, yOffset, size.x, size.y, internalFormat,
static_cast<GLsizei>(sourceSize), sourcePointer);
break;
default:*/
glTexSubImage2D(_target, mip, 0, yOffset, size.x, size.y, format, type, sourcePointer);
//break;
//}
} else if (GL_TEXTURE_CUBE_MAP == _target) {
auto target = GLTexture::CUBE_FACE_LAYOUT[face];
qDebug() << "[UNIMPLEMENTED] GL_TEXTURE_CUBE_MAP internalFormat: " << internalFormat;
void GLESTexture::updateSize() const {
setSize(_virtualSize);
if (!_id) {
return;
/*switch (internalFormat) {
case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT:
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
case GL_COMPRESSED_RED_RGTC1:
case GL_COMPRESSED_RG_RGTC2:
case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM:
case GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT:
glCompressedTexSubImage2D(target, mip, 0, yOffset, size.x, size.y, internalFormat,
static_cast<GLsizei>(sourceSize), sourcePointer);
break;
default:*/
glTexSubImage2D(target, mip, 0, yOffset, size.x, size.y, format, type, sourcePointer);
// break;
//}
} else {
// TODO: implement for android
assert(false);
amountCopied = 0;
}
if (_gpuObject.getTexelFormat().isCompressed()) {
GLenum proxyType = GL_TEXTURE_2D;
GLuint numFaces = 1;
if (_gpuObject.getType() == gpu::Texture::TEX_CUBE) {
proxyType = CUBE_FACE_LAYOUT[0];
numFaces = (GLuint)CUBE_NUM_FACES;
}
GLint gpuSize{ 0 };
glGetTexLevelParameteriv(proxyType, 0, GL_TEXTURE_COMPRESSED, &gpuSize);
(void)CHECK_GL_ERROR();
if (gpuSize) {
for (GLuint level = _minMip; level < _maxMip; level++) {
GLint levelSize{ 0 };
//glGetTexLevelParameteriv(proxyType, level, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &levelSize);
//qDebug() << "TODO: GLBackendTexture.cpp:updateSize GL_TEXTURE_COMPRESSED_IMAGE_SIZE";
levelSize *= numFaces;
if (levelSize <= 0) {
break;
}
gpuSize += levelSize;
}
(void)CHECK_GL_ERROR();
setSize(gpuSize);
return;
}
}
}
// Move content bits from the CPU to the GPU for a given mip / face
void GLESTexture::transferMip(uint16_t mipLevel, uint8_t face) const {
auto mip = _gpuObject.accessStoredMipFace(mipLevel, face);
GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), _gpuObject.getStoredMipFormat());
//GLenum target = getFaceTargets()[face];
GLenum target = _target == GL_TEXTURE_2D ? GL_TEXTURE_2D : CUBE_FACE_LAYOUT[face];
auto size = _gpuObject.evalMipDimensions(mipLevel);
glTexSubImage2D(target, mipLevel, 0, 0, size.x, size.y, texelFormat.format, texelFormat.type, mip->readData());
(void)CHECK_GL_ERROR();
return amountCopied;
}
void GLESTexture::startTransfer() {
PROFILE_RANGE(render_gpu_gl, __FUNCTION__);
Parent::startTransfer();
glBindTexture(_target, _id);
(void)CHECK_GL_ERROR();
// transfer pixels from each faces
uint8_t numFaces = (Texture::TEX_CUBE == _gpuObject.getType()) ? CUBE_NUM_FACES : 1;
for (uint8_t f = 0; f < numFaces; f++) {
for (uint16_t i = 0; i < Sampler::MAX_MIP_LEVEL; ++i) {
if (_gpuObject.isStoredMipFaceAvailable(i, f)) {
transferMip(i, f);
}
}
}
}
void GLESBackend::GLESTexture::syncSampler() const {
void GLESTexture::syncSampler() const {
const Sampler& sampler = _gpuObject.getSampler();
const auto& fm = FILTER_MODES[sampler.getFilter()];
glTexParameteri(_target, GL_TEXTURE_MIN_FILTER, fm.minFilter);
glTexParameteri(_target, GL_TEXTURE_MAG_FILTER, fm.magFilter);
if (sampler.doComparison()) {
glTexParameteri(_target, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
glTexParameteri(_target, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); // GL_COMPARE_R_TO_TEXTURE
glTexParameteri(_target, GL_TEXTURE_COMPARE_FUNC, COMPARISON_TO_GL[sampler.getComparisonFunction()]);
} else {
glTexParameteri(_target, GL_TEXTURE_COMPARE_MODE, GL_NONE);
@ -161,16 +170,453 @@ void GLESBackend::GLESTexture::syncSampler() const {
glTexParameteri(_target, GL_TEXTURE_WRAP_R, WRAP_MODES[sampler.getWrapModeW()]);
glTexParameterfv(_target, GL_TEXTURE_BORDER_COLOR_EXT, (const float*)&sampler.getBorderColor());
glTexParameteri(_target, GL_TEXTURE_BASE_LEVEL, (uint16)sampler.getMipOffset());
glTexParameterf(_target, GL_TEXTURE_MIN_LOD, (float)sampler.getMinMip());
glTexParameterf(_target, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.f : sampler.getMaxMip()));
(void)CHECK_GL_ERROR();
//qDebug() << "[GPU-GL-GLBackend] syncSampler 12 " << _target << "," << sampler.getMaxAnisotropy();
//glTexParameterf(_target, GL_TEXTURE_MAX_ANISOTROPY_EXT, sampler.getMaxAnisotropy());
//(void)CHECK_GL_ERROR();
//qDebug() << "[GPU-GL-GLBackend] syncSampler end";
}
using GLESFixedAllocationTexture = GLESBackend::GLESFixedAllocationTexture;
GLESFixedAllocationTexture::GLESFixedAllocationTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture) : GLESTexture(backend, texture), _size(texture.evalTotalSize()) {
withPreservedTexture([&] {
allocateStorage();
syncSampler();
});
}
GLESFixedAllocationTexture::~GLESFixedAllocationTexture() {
}
void GLESFixedAllocationTexture::allocateStorage() const {
const GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat());
const auto numMips = _gpuObject.getNumMips();
// glTextureStorage2D(_id, mips, texelFormat.internalFormat, dimensions.x, dimensions.y);
for (GLint level = 0; level < numMips; level++) {
Vec3u dimensions = _gpuObject.evalMipDimensions(level);
for (GLenum target : getFaceTargets(_target)) {
glTexImage2D(target, level, texelFormat.internalFormat, dimensions.x, dimensions.y, 0, texelFormat.format, texelFormat.type, nullptr);
}
}
glTexParameteri(_target, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(_target, GL_TEXTURE_MAX_LEVEL, numMips - 1);
(void)CHECK_GL_ERROR();
}
void GLESFixedAllocationTexture::syncSampler() const {
Parent::syncSampler();
const Sampler& sampler = _gpuObject.getSampler();
auto baseMip = std::max<uint16_t>(sampler.getMipOffset(), sampler.getMinMip());
glTexParameteri(_target, GL_TEXTURE_BASE_LEVEL, baseMip);
glTexParameterf(_target, GL_TEXTURE_MIN_LOD, (float)sampler.getMinMip());
glTexParameterf(_target, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.0f : sampler.getMaxMip()));
}
// Renderbuffer attachment textures
using GLESAttachmentTexture = GLESBackend::GLESAttachmentTexture;
GLESAttachmentTexture::GLESAttachmentTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture) : GLESFixedAllocationTexture(backend, texture) {
Backend::textureFramebufferCount.increment();
Backend::textureFramebufferGPUMemSize.update(0, size());
}
GLESAttachmentTexture::~GLESAttachmentTexture() {
Backend::textureFramebufferCount.decrement();
Backend::textureFramebufferGPUMemSize.update(size(), 0);
}
// Strict resource textures
using GLESStrictResourceTexture = GLESBackend::GLESStrictResourceTexture;
GLESStrictResourceTexture::GLESStrictResourceTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture) : GLESFixedAllocationTexture(backend, texture) {
Backend::textureResidentCount.increment();
Backend::textureResidentGPUMemSize.update(0, size());
withPreservedTexture([&] {
auto mipLevels = _gpuObject.getNumMips();
for (uint16_t sourceMip = 0; sourceMip < mipLevels; sourceMip++) {
uint16_t targetMip = sourceMip;
size_t maxFace = GLTexture::getFaceCount(_target);
for (uint8_t face = 0; face < maxFace; face++) {
copyMipFaceFromTexture(sourceMip, targetMip, face);
}
}
});
if (texture.isAutogenerateMips()) {
generateMips();
}
}
GLESStrictResourceTexture::~GLESStrictResourceTexture() {
Backend::textureResidentCount.decrement();
Backend::textureResidentGPUMemSize.update(size(), 0);
}
using GLESVariableAllocationTexture = GLESBackend::GLESVariableAllocationTexture;
GLESVariableAllocationTexture::GLESVariableAllocationTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture) :
GLESTexture(backend, texture)
{
Backend::textureResourceCount.increment();
auto mipLevels = texture.getNumMips();
_allocatedMip = mipLevels;
_maxAllocatedMip = _populatedMip = mipLevels;
_minAllocatedMip = texture.minAvailableMipLevel();
uvec3 mipDimensions;
for (uint16_t mip = _minAllocatedMip; mip < mipLevels; ++mip) {
if (glm::all(glm::lessThanEqual(texture.evalMipDimensions(mip), INITIAL_MIP_TRANSFER_DIMENSIONS))) {
_maxAllocatedMip = _populatedMip = mip;
break;
}
}
auto targetMip = _populatedMip - std::min<uint16_t>(_populatedMip, 2);
uint16_t allocatedMip = std::max<uint16_t>(_minAllocatedMip, targetMip);
allocateStorage(allocatedMip);
_memoryPressureStateStale = true;
copyMipsFromTexture();
syncSampler();
}
GLESVariableAllocationTexture::~GLESVariableAllocationTexture() {
Backend::textureResourceCount.decrement();
Backend::textureResourceGPUMemSize.update(_size, 0);
Backend::textureResourcePopulatedGPUMemSize.update(_populatedSize, 0);
}
void GLESVariableAllocationTexture::allocateStorage(uint16 allocatedMip) {
_allocatedMip = allocatedMip;
const GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat());
const auto dimensions = _gpuObject.evalMipDimensions(_allocatedMip);
const auto totalMips = _gpuObject.getNumMips();
const auto mips = totalMips - _allocatedMip;
withPreservedTexture([&] {
// FIXME technically GL 4.2, but OSX includes the ARB_texture_storage extension
glTexStorage2D(_target, mips, texelFormat.internalFormat, dimensions.x, dimensions.y); CHECK_GL_ERROR();
});
auto mipLevels = _gpuObject.getNumMips();
_size = 0;
for (uint16_t mip = _allocatedMip; mip < mipLevels; ++mip) {
_size += _gpuObject.evalMipSize(mip);
}
Backend::textureResourceGPUMemSize.update(0, _size);
}
Size GLESVariableAllocationTexture::copyMipsFromTexture() {
auto mipLevels = _gpuObject.getNumMips();
size_t maxFace = GLTexture::getFaceCount(_target);
Size amount = 0;
for (uint16_t sourceMip = _populatedMip; sourceMip < mipLevels; ++sourceMip) {
uint16_t targetMip = sourceMip - _allocatedMip;
for (uint8_t face = 0; face < maxFace; ++face) {
amount += copyMipFaceFromTexture(sourceMip, targetMip, face);
}
}
return amount;
}
Size GLESVariableAllocationTexture::copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum internalFormat, GLenum format, GLenum type, Size sourceSize, const void* sourcePointer) const {
Size amountCopied = 0;
withPreservedTexture([&] {
amountCopied = Parent::copyMipFaceLinesFromTexture(mip, face, size, yOffset, internalFormat, format, type, sourceSize, sourcePointer);
});
incrementPopulatedSize(amountCopied);
return amountCopied;
}
void GLESVariableAllocationTexture::syncSampler() const {
withPreservedTexture([&] {
Parent::syncSampler();
glTexParameteri(_target, GL_TEXTURE_BASE_LEVEL, _populatedMip - _allocatedMip);
});
}
void copyUncompressedTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLuint srcId, GLuint destId, uint16_t numMips, uint16_t srcMipOffset, uint16_t destMipOffset, uint16_t populatedMips) {
// DestID must be bound to the GLESBackend::RESOURCE_TRANSFER_TEX_UNIT
GLuint fbo { 0 };
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
uint16_t mips = numMips;
// copy pre-existing mips
for (uint16_t mip = populatedMips; mip < mips; ++mip) {
auto mipDimensions = texture.evalMipDimensions(mip);
uint16_t targetMip = mip - destMipOffset;
uint16_t sourceMip = mip - srcMipOffset;
for (GLenum target : GLTexture::getFaceTargets(texTarget)) {
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, srcId, sourceMip);
(void)CHECK_GL_ERROR();
glCopyTexSubImage2D(target, targetMip, 0, 0, 0, 0, mipDimensions.x, mipDimensions.y);
(void)CHECK_GL_ERROR();
}
}
// destroy the transfer framebuffer
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
glDeleteFramebuffers(1, &fbo);
}
void copyCompressedTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLuint srcId, GLuint destId, uint16_t numMips, uint16_t srcMipOffset, uint16_t destMipOffset, uint16_t populatedMips) {
// DestID must be bound to the GLESBackend::RESOURCE_TRANSFER_TEX_UNIT
struct MipDesc {
GLint _faceSize;
GLint _size;
GLint _offset;
GLint _width;
GLint _height;
};
std::vector<MipDesc> sourceMips(numMips);
std::vector<GLubyte> bytes;
glActiveTexture(GL_TEXTURE0 + GLESBackend::RESOURCE_TRANSFER_EXTRA_TEX_UNIT);
glBindTexture(texTarget, srcId);
const auto& faceTargets = GLTexture::getFaceTargets(texTarget);
GLint internalFormat { 0 };
// Collect the mip description from the source texture
GLint bufferOffset { 0 };
for (uint16_t mip = populatedMips; mip < numMips; ++mip) {
auto& sourceMip = sourceMips[mip];
uint16_t sourceLevel = mip - srcMipOffset;
// Grab internal format once
if (internalFormat == 0) {
glGetTexLevelParameteriv(faceTargets[0], sourceLevel, GL_TEXTURE_INTERNAL_FORMAT, &internalFormat);
}
// Collect the size of the first face, and then compute the total size offset needed for this mip level
auto mipDimensions = texture.evalMipDimensions(mip);
sourceMip._width = mipDimensions.x;
sourceMip._height = mipDimensions.y;
#ifdef DEBUG_COPY
glGetTexLevelParameteriv(faceTargets.front(), sourceLevel, GL_TEXTURE_WIDTH, &sourceMip._width);
glGetTexLevelParameteriv(faceTargets.front(), sourceLevel, GL_TEXTURE_HEIGHT, &sourceMip._height);
#endif
// TODO: retrieve the size of a compressed image
assert(false);
//glGetTexLevelParameteriv(faceTargets.front(), sourceLevel, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &sourceMip._faceSize);
sourceMip._size = (GLint)faceTargets.size() * sourceMip._faceSize;
sourceMip._offset = bufferOffset;
bufferOffset += sourceMip._size;
gpu::gl::checkGLError();
}
(void)CHECK_GL_ERROR();
// Allocate the PBO to accomodate for all the mips to copy
GLuint pbo { 0 };
glGenBuffers(1, &pbo);
glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo);
glBufferData(GL_PIXEL_PACK_BUFFER, bufferOffset, nullptr, GL_STATIC_COPY);
(void)CHECK_GL_ERROR();
// Transfer from source texture to pbo
for (uint16_t mip = populatedMips; mip < numMips; ++mip) {
auto& sourceMip = sourceMips[mip];
uint16_t sourceLevel = mip - srcMipOffset;
for (GLint f = 0; f < (GLint)faceTargets.size(); f++) {
// TODO: implement for android
//glGetCompressedTexImage(faceTargets[f], sourceLevel, BUFFER_OFFSET(sourceMip._offset + f * sourceMip._faceSize));
}
(void)CHECK_GL_ERROR();
}
// Now populate the new texture from the pbo
glBindTexture(texTarget, 0);
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo);
glActiveTexture(GL_TEXTURE0 + GLESBackend::RESOURCE_TRANSFER_TEX_UNIT);
// Transfer from pbo to new texture
for (uint16_t mip = populatedMips; mip < numMips; ++mip) {
auto& sourceMip = sourceMips[mip];
uint16_t destLevel = mip - destMipOffset;
for (GLint f = 0; f < (GLint)faceTargets.size(); f++) {
#ifdef DEBUG_COPY
GLint destWidth, destHeight, destSize;
glGetTexLevelParameteriv(faceTargets.front(), destLevel, GL_TEXTURE_WIDTH, &destWidth);
glGetTexLevelParameteriv(faceTargets.front(), destLevel, GL_TEXTURE_HEIGHT, &destHeight);
glGetTexLevelParameteriv(faceTargets.front(), destLevel, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &destSize);
#endif
glCompressedTexSubImage2D(faceTargets[f], destLevel, 0, 0, sourceMip._width, sourceMip._height, internalFormat,
sourceMip._faceSize, BUFFER_OFFSET(sourceMip._offset + f * sourceMip._faceSize));
gpu::gl::checkGLError();
}
}
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
glDeleteBuffers(1, &pbo);
}
void GLESVariableAllocationTexture::copyTextureMipsInGPUMem(GLuint srcId, GLuint destId, uint16_t srcMipOffset, uint16_t destMipOffset, uint16_t populatedMips) {
uint16_t numMips = _gpuObject.getNumMips();
withPreservedTexture([&] {
qDebug() << "[TEXTURE] is compressed: " << _texelFormat.isCompressed();
if (_texelFormat.isCompressed()) {
copyCompressedTexGPUMem(_gpuObject, _target, srcId, destId, numMips, srcMipOffset, destMipOffset, populatedMips);
} else {
copyUncompressedTexGPUMem(_gpuObject, _target, srcId, destId, numMips, srcMipOffset, destMipOffset, populatedMips);
}
});
}
void GLESVariableAllocationTexture::promote() {
PROFILE_RANGE(render_gpu_gl, __FUNCTION__);
Q_ASSERT(_allocatedMip > 0);
uint16_t targetAllocatedMip = _allocatedMip - std::min<uint16_t>(_allocatedMip, 2);
targetAllocatedMip = std::max<uint16_t>(_minAllocatedMip, targetAllocatedMip);
GLuint oldId = _id;
auto oldSize = _size;
uint16_t oldAllocatedMip = _allocatedMip;
// create new texture
const_cast<GLuint&>(_id) = allocate(_gpuObject);
// allocate storage for new level
allocateStorage(targetAllocatedMip);
// copy pre-existing mips
copyTextureMipsInGPUMem(oldId, _id, oldAllocatedMip, _allocatedMip, _populatedMip);
// destroy the old texture
glDeleteTextures(1, &oldId);
// Update sampler
syncSampler();
// update the memory usage
Backend::textureResourceGPUMemSize.update(oldSize, 0);
// no change to Backend::textureResourcePopulatedGPUMemSize
populateTransferQueue();
}
void GLESVariableAllocationTexture::demote() {
PROFILE_RANGE(render_gpu_gl, __FUNCTION__);
Q_ASSERT(_allocatedMip < _maxAllocatedMip);
auto oldId = _id;
auto oldSize = _size;
auto oldPopulatedMip = _populatedMip;
// allocate new texture
const_cast<GLuint&>(_id) = allocate(_gpuObject);
uint16_t oldAllocatedMip = _allocatedMip;
allocateStorage(_allocatedMip + 1);
_populatedMip = std::max(_populatedMip, _allocatedMip);
// copy pre-existing mips
copyTextureMipsInGPUMem(oldId, _id, oldAllocatedMip, _allocatedMip, _populatedMip);
// destroy the old texture
glDeleteTextures(1, &oldId);
// Update sampler
syncSampler();
// update the memory usage
Backend::textureResourceGPUMemSize.update(oldSize, 0);
// Demoting unpopulate the memory delta
if (oldPopulatedMip != _populatedMip) {
auto numPopulatedDemoted = _populatedMip - oldPopulatedMip;
Size amountUnpopulated = 0;
for (int i = 0; i < numPopulatedDemoted; i++) {
amountUnpopulated += _gpuObject.evalMipSize(oldPopulatedMip + i);
}
decrementPopulatedSize(amountUnpopulated);
}
populateTransferQueue();
}
void GLESVariableAllocationTexture::populateTransferQueue() {
PROFILE_RANGE(render_gpu_gl, __FUNCTION__);
if (_populatedMip <= _allocatedMip) {
return;
}
_pendingTransfers = TransferQueue();
const uint8_t maxFace = GLTexture::getFaceCount(_target);
uint16_t sourceMip = _populatedMip;
do {
--sourceMip;
auto targetMip = sourceMip - _allocatedMip;
auto mipDimensions = _gpuObject.evalMipDimensions(sourceMip);
for (uint8_t face = 0; face < maxFace; ++face) {
if (!_gpuObject.isStoredMipFaceAvailable(sourceMip, face)) {
continue;
}
// If the mip is less than the max transfer size, then just do it in one transfer
if (glm::all(glm::lessThanEqual(mipDimensions, MAX_TRANSFER_DIMENSIONS))) {
// Can the mip be transferred in one go
_pendingTransfers.emplace(new TransferJob(*this, sourceMip, targetMip, face));
continue;
}
// break down the transfers into chunks so that no single transfer is
// consuming more than X bandwidth
// For compressed format, regions must be a multiple of the 4x4 tiles, so enforce 4 lines as the minimum block
auto mipSize = _gpuObject.getStoredMipFaceSize(sourceMip, face);
const auto lines = mipDimensions.y;
const uint32_t BLOCK_NUM_LINES { 4 };
const auto numBlocks = (lines + (BLOCK_NUM_LINES - 1)) / BLOCK_NUM_LINES;
auto bytesPerBlock = mipSize / numBlocks;
Q_ASSERT(0 == (mipSize % lines));
uint32_t linesPerTransfer = BLOCK_NUM_LINES * (uint32_t)(MAX_TRANSFER_SIZE / bytesPerBlock);
uint32_t lineOffset = 0;
while (lineOffset < lines) {
uint32_t linesToCopy = std::min<uint32_t>(lines - lineOffset, linesPerTransfer);
_pendingTransfers.emplace(new TransferJob(*this, sourceMip, targetMip, face, linesToCopy, lineOffset));
lineOffset += linesToCopy;
}
}
// queue up the sampler and populated mip change for after the transfer has completed
_pendingTransfers.emplace(new TransferJob(*this, [=] {
_populatedMip = sourceMip;
syncSampler();
}));
} while (sourceMip != _allocatedMip);
}
// resource textures
using GLESResourceTexture = GLESBackend::GLESResourceTexture;
GLESResourceTexture::GLESResourceTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture) : GLESVariableAllocationTexture(backend, texture) {
if (texture.isAutogenerateMips()) {
generateMips();
}
}
GLESResourceTexture::~GLESResourceTexture() {
}

View file

@ -38,9 +38,9 @@ void GLESBackend::transferTransformState(const Batch& batch) const {
}
if (!batch._objects.empty()) {
glBindBuffer(GL_SHADER_STORAGE_BUFFER, _transform._objectBuffer);
glBufferData(GL_SHADER_STORAGE_BUFFER, batch._objects.size() * sizeof(Batch::TransformObject), batch._objects.data(), GL_STREAM_DRAW);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
glBindBuffer(GL_TEXTURE_BUFFER, _transform._objectBuffer);
glBufferData(GL_TEXTURE_BUFFER, batch._objects.size() * sizeof(Batch::TransformObject), batch._objects.data(), GL_DYNAMIC_DRAW);
glBindBuffer(GL_TEXTURE_BUFFER, 0);
}
if (!batch._namedData.empty()) {
@ -58,10 +58,44 @@ void GLESBackend::transferTransformState(const Batch& batch) const {
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, TRANSFORM_OBJECT_SLOT, _transform._objectBuffer);
glActiveTexture(GL_TEXTURE0 + GLESBackend::TRANSFORM_OBJECT_SLOT);
glBindTexture(GL_TEXTURE_BUFFER, _transform._objectBufferTexture);
if (!batch._objects.empty()) {
glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32F, _transform._objectBuffer);
}
CHECK_GL_ERROR();
// Make sure the current Camera offset is unknown before render Draw
_transform._currentCameraOffset = INVALID_OFFSET;
}
void GLESBackend::updateTransform(const Batch& batch) {
_transform.update(_commandIndex, _stereo);
auto& drawCallInfoBuffer = batch.getDrawCallInfoBuffer();
if (batch._currentNamedCall.empty()) {
auto& drawCallInfo = drawCallInfoBuffer[_currentDraw];
if (_transform._enabledDrawcallInfoBuffer) {
glDisableVertexAttribArray(gpu::Stream::DRAW_CALL_INFO); // Make sure attrib array is disabled
_transform._enabledDrawcallInfoBuffer = false;
}
glVertexAttribI4i(gpu::Stream::DRAW_CALL_INFO, drawCallInfo.index, drawCallInfo.unused, 0, 0);
//glVertexAttribI2i(gpu::Stream::DRAW_CALL_INFO, drawCallInfo.index, drawCallInfo.unused);
} else {
if (!_transform._enabledDrawcallInfoBuffer) {
glEnableVertexAttribArray(gpu::Stream::DRAW_CALL_INFO); // Make sure attrib array is enabled
#ifdef GPU_STEREO_DRAWCALL_INSTANCED
glVertexAttribDivisor(gpu::Stream::DRAW_CALL_INFO, (isStereo() ? 2 : 1));
#else
glVertexAttribDivisor(gpu::Stream::DRAW_CALL_INFO, 1);
#endif
_transform._enabledDrawcallInfoBuffer = true;
}
glBindBuffer(GL_ARRAY_BUFFER, _transform._drawCallInfoBuffer);
glVertexAttribIPointer(gpu::Stream::DRAW_CALL_INFO, 2, GL_UNSIGNED_SHORT, 0, _transform._drawCallInfoOffsets[batch._currentNamedCall]);
}
(void)CHECK_GL_ERROR();
}