CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/Common/GPU/OpenGL/GLRenderManager.h
Views: 1401
#pragma once12#include <thread>3#include <unordered_map>4#include <vector>5#include <functional>6#include <set>7#include <string>8#include <mutex>9#include <queue>10#include <condition_variable>1112#include "Common/GPU/MiscTypes.h"13#include "Common/Data/Convert/SmallDataConvert.h"14#include "Common/Log.h"15#include "Common/GPU/OpenGL/GLQueueRunner.h"16#include "Common/GPU/OpenGL/GLFrameData.h"17#include "Common/GPU/OpenGL/GLCommon.h"18#include "Common/GPU/OpenGL/GLMemory.h"1920class GLRInputLayout;21class GLPushBuffer;2223namespace Draw {24class DrawContext;25}2627constexpr int MAX_GL_TEXTURE_SLOTS = 8;2829class GLRTexture {30public:31GLRTexture(const Draw::DeviceCaps &caps, int width, int height, int depth, int numMips);32~GLRTexture();3334GLuint texture = 0;35uint16_t w;36uint16_t h;37uint16_t d;3839// We don't trust OpenGL defaults - setting wildly off values ensures that we'll end up overwriting these parameters.40GLenum target = 0xFFFF;41GLenum wrapS = 0xFFFF;42GLenum wrapT = 0xFFFF;43GLenum magFilter = 0xFFFF;44GLenum minFilter = 0xFFFF;45uint8_t numMips = 0;46bool canWrap = true;47float anisotropy = -100000.0f;48float minLod = -1000.0f;49float maxLod = 1000.0f;50float lodBias = 0.0f;51};5253class GLRFramebuffer {54public:55GLRFramebuffer(const Draw::DeviceCaps &caps, int _width, int _height, bool z_stencil, const char *tag)56: color_texture(caps, _width, _height, 1, 1), z_stencil_texture(caps, _width, _height, 1, 1),57width(_width), height(_height), z_stencil_(z_stencil) {58}59~GLRFramebuffer();6061const char *Tag() const { return tag_.c_str(); }6263GLuint handle = 0;64GLRTexture color_texture;65// Either z_stencil_texture, z_stencil_buffer, or (z_buffer and stencil_buffer) are set.66GLuint z_stencil_buffer = 0;67GLRTexture z_stencil_texture;68GLuint z_buffer = 0;69GLuint stencil_buffer = 0;7071int width;72int height;73GLuint colorDepth = 0;74bool z_stencil_;7576private:77std::string tag_;78};7980// We need to create some custom heap-allocated types so we can forward things that need to be created on the GL thread, before81// they've actually been created.8283class GLRShader {84public:85~GLRShader() {86if (shader) {87glDeleteShader(shader);88}89}9091GLuint shader = 0;92bool valid = false;93// Warning: Won't know until a future frame.94bool failed = false;95std::string desc;96std::string code;97std::string error;98};99100struct GLRProgramFlags {101bool supportDualSource : 1;102bool useClipDistance0 : 1;103bool useClipDistance1 : 1;104bool useClipDistance2 : 1;105};106107// Unless you manage lifetimes in some smart way,108// your loc data for uniforms and samplers need to be in a struct109// derived from this, and passed into CreateProgram.110class GLRProgramLocData {111public:112virtual ~GLRProgramLocData() {}113};114115class GLRProgram {116public:117~GLRProgram() {118if (deleteCallback_) {119deleteCallback_(deleteParam_);120}121if (program) {122glDeleteProgram(program);123}124delete locData_;125}126struct Semantic {127int location;128const char *attrib;129};130131struct UniformLocQuery {132GLint *dest;133const char *name;134bool required;135};136137struct Initializer {138GLint *uniform;139int type;140int value;141};142143GLuint program = 0;144std::vector<Semantic> semantics_;145std::vector<UniformLocQuery> queries_;146std::vector<Initializer> initialize_;147148GLRProgramLocData *locData_;149bool use_clip_distance[8]{};150151struct UniformInfo {152int loc_;153};154155// Must ONLY be called from GLQueueRunner!156// Also it's pretty slow...157int GetUniformLoc(const char *name) {158auto iter = uniformCache_.find(std::string(name));159int loc = -1;160if (iter != uniformCache_.end()) {161loc = iter->second.loc_;162} else {163loc = glGetUniformLocation(program, name);164UniformInfo info;165info.loc_ = loc;166uniformCache_[name] = info;167}168return loc;169}170171void SetDeleteCallback(void(*cb)(void *), void *p) {172deleteCallback_ = cb;173deleteParam_ = p;174}175176private:177void(*deleteCallback_)(void *) = nullptr;178void *deleteParam_ = nullptr;179180std::unordered_map<std::string, UniformInfo> uniformCache_;181};182183class GLRInputLayout {184public:185struct Entry {186int location;187int count;188GLenum type;189GLboolean normalized;190intptr_t offset;191};192std::vector<Entry> entries;193int stride;194int semanticsMask_ = 0;195};196197enum class GLRRunType {198SUBMIT,199PRESENT,200SYNC,201EXIT,202};203204class GLRenderManager;205class GLPushBuffer;206207// These are enqueued from the main thread, and the render thread pops them off208struct GLRRenderThreadTask {209GLRRenderThreadTask(GLRRunType _runType) : runType(_runType) {}210211std::vector<GLRStep *> steps;212FastVec<GLRInitStep> initSteps;213214int frame = -1;215GLRRunType runType;216217// Avoid copying these by accident.218GLRRenderThreadTask(GLRRenderThreadTask &) = delete;219GLRRenderThreadTask& operator =(GLRRenderThreadTask &) = delete;220};221222// Note: The GLRenderManager is created and destroyed on the render thread, and the latter223// happens after the emu thread has been destroyed. Therefore, it's safe to run wild deleting stuff224// directly in the destructor.225class GLRenderManager {226public:227GLRenderManager(HistoryBuffer<FrameTimeData, FRAME_TIME_HISTORY_LENGTH> &frameTimeHistory);228~GLRenderManager();229230GLRenderManager(GLRenderManager &) = delete;231GLRenderManager &operator=(GLRenderManager &) = delete;232233void SetInvalidationCallback(InvalidationCallback callback) {234invalidationCallback_ = callback;235}236237void ThreadStart(Draw::DrawContext *draw);238void ThreadEnd();239bool ThreadFrame(); // Returns true if it did anything. False means the queue was empty.240241void SetErrorCallback(ErrorCallbackFn callback, void *userdata) {242queueRunner_.SetErrorCallback(callback, userdata);243}244void SetDeviceCaps(const Draw::DeviceCaps &caps) {245queueRunner_.SetDeviceCaps(caps);246caps_ = caps;247}248249std::string GetGpuProfileString() const;250251// Makes sure that the GPU has caught up enough that we can start writing buffers of this frame again.252void BeginFrame(bool enableProfiling);253// Can run on a different thread!254void Finish();255void Present();256257// Creation commands. These were not needed in Vulkan since there we can do that on the main thread.258// We pass in width/height here even though it's not strictly needed until we support glTextureStorage259// and then we'll also need formats and stuff.260GLRTexture *CreateTexture(GLenum target, int width, int height, int depth, int numMips) {261_dbg_assert_(target != 0);262GLRInitStep &step = initSteps_.push_uninitialized();263step.stepType = GLRInitStepType::CREATE_TEXTURE;264step.create_texture.texture = new GLRTexture(caps_, width, height, depth, numMips);265step.create_texture.texture->target = target;266return step.create_texture.texture;267}268269GLRBuffer *CreateBuffer(GLuint target, size_t size, GLuint usage) {270GLRInitStep &step = initSteps_.push_uninitialized();271step.stepType = GLRInitStepType::CREATE_BUFFER;272step.create_buffer.buffer = new GLRBuffer(target, size);273step.create_buffer.size = (int)size;274step.create_buffer.usage = usage;275return step.create_buffer.buffer;276}277278GLRShader *CreateShader(GLuint stage, const std::string &code, const std::string &desc) {279GLRInitStep &step = initSteps_.push_uninitialized();280step.stepType = GLRInitStepType::CREATE_SHADER;281step.create_shader.shader = new GLRShader();282step.create_shader.shader->desc = desc;283step.create_shader.stage = stage;284step.create_shader.code = new char[code.size() + 1];285memcpy(step.create_shader.code, code.data(), code.size() + 1);286return step.create_shader.shader;287}288289GLRFramebuffer *CreateFramebuffer(int width, int height, bool z_stencil, const char *tag) {290_dbg_assert_(width > 0 && height > 0 && tag != nullptr);291292GLRInitStep &step = initSteps_.push_uninitialized();293step.stepType = GLRInitStepType::CREATE_FRAMEBUFFER;294step.create_framebuffer.framebuffer = new GLRFramebuffer(caps_, width, height, z_stencil, tag);295return step.create_framebuffer.framebuffer;296}297298// Can't replace uniform initializers with direct calls to SetUniform() etc because there might299// not be an active render pass.300GLRProgram *CreateProgram(301std::vector<GLRShader *> shaders, std::vector<GLRProgram::Semantic> semantics, std::vector<GLRProgram::UniformLocQuery> queries,302std::vector<GLRProgram::Initializer> initializers, GLRProgramLocData *locData, const GLRProgramFlags &flags) {303GLRInitStep &step = initSteps_.push_uninitialized();304step.stepType = GLRInitStepType::CREATE_PROGRAM;305_assert_(shaders.size() <= ARRAY_SIZE(step.create_program.shaders));306step.create_program.program = new GLRProgram();307step.create_program.program->semantics_ = semantics;308step.create_program.program->queries_ = queries;309step.create_program.program->initialize_ = initializers;310step.create_program.program->locData_ = locData;311step.create_program.program->use_clip_distance[0] = flags.useClipDistance0;312step.create_program.program->use_clip_distance[1] = flags.useClipDistance1;313step.create_program.program->use_clip_distance[2] = flags.useClipDistance2;314step.create_program.support_dual_source = flags.supportDualSource;315_assert_msg_(shaders.size() > 0, "Can't create a program with zero shaders");316for (size_t i = 0; i < shaders.size(); i++) {317step.create_program.shaders[i] = shaders[i];318}319#ifdef _DEBUG320for (auto &iter : queries) {321_dbg_assert_(iter.name);322}323for (auto &sem : semantics) {324_dbg_assert_(sem.attrib);325}326#endif327step.create_program.num_shaders = (int)shaders.size();328return step.create_program.program;329}330331GLRInputLayout *CreateInputLayout(const std::vector<GLRInputLayout::Entry> &entries, int stride) {332GLRInitStep &step = initSteps_.push_uninitialized();333step.stepType = GLRInitStepType::CREATE_INPUT_LAYOUT;334step.create_input_layout.inputLayout = new GLRInputLayout();335step.create_input_layout.inputLayout->entries = entries;336step.create_input_layout.inputLayout->stride = stride;337for (auto &iter : step.create_input_layout.inputLayout->entries) {338step.create_input_layout.inputLayout->semanticsMask_ |= 1 << iter.location;339}340return step.create_input_layout.inputLayout;341}342343GLPushBuffer *CreatePushBuffer(int frame, GLuint target, size_t size, const char *tag) {344GLPushBuffer *push = new GLPushBuffer(this, target, size, tag);345RegisterPushBuffer(frame, push);346return push;347}348349void DeleteShader(GLRShader *shader) {350_dbg_assert_(shader != nullptr);351deleter_.shaders.push_back(shader);352}353void DeleteProgram(GLRProgram *program) {354_dbg_assert_(program != nullptr);355deleter_.programs.push_back(program);356}357void DeleteBuffer(GLRBuffer *buffer) {358_dbg_assert_(buffer != nullptr);359deleter_.buffers.push_back(buffer);360}361void DeleteTexture(GLRTexture *texture) {362_dbg_assert_(texture != nullptr);363deleter_.textures.push_back(texture);364}365void DeleteInputLayout(GLRInputLayout *inputLayout) {366_dbg_assert_(inputLayout != nullptr);367deleter_.inputLayouts.push_back(inputLayout);368}369void DeleteFramebuffer(GLRFramebuffer *framebuffer) {370_dbg_assert_(framebuffer != nullptr);371deleter_.framebuffers.push_back(framebuffer);372}373void DeletePushBuffer(GLPushBuffer *pushbuffer) {374_dbg_assert_(pushbuffer != nullptr);375deleter_.pushBuffers.push_back(pushbuffer);376}377378bool IsInRenderPass() const {379return curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER;380}381382// This starts a new step (like a "render pass" in Vulkan).383//384// After a "CopyFramebuffer" or the other functions that start "steps", you need to call this before385// making any new render state changes or draw calls.386//387// The following state needs to be reset by the caller after calling this (and will thus not safely carry over from388// the previous one):389// * Viewport/Scissor390// * Depth/stencil391// * Blend392// * Raster state like primitive, culling, etc.393//394// It can be useful to use GetCurrentStepId() to figure out when you need to send all this state again, if you're395// not keeping track of your calls to this function on your own.396void BindFramebufferAsRenderTarget(GLRFramebuffer *fb, GLRRenderPassAction color, GLRRenderPassAction depth, GLRRenderPassAction stencil, uint32_t clearColor, float clearDepth, uint8_t clearStencil, const char *tag);397398// Binds a framebuffer as a texture, for the following draws.399void BindFramebufferAsTexture(GLRFramebuffer *fb, int binding, int aspectBit);400401bool CopyFramebufferToMemory(GLRFramebuffer *src, int aspectBits, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride, Draw::ReadbackMode mode, const char *tag);402void CopyImageToMemorySync(GLRTexture *texture, int mipLevel, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride, const char *tag);403404void CopyFramebuffer(GLRFramebuffer *src, GLRect2D srcRect, GLRFramebuffer *dst, GLOffset2D dstPos, int aspectMask, const char *tag);405void BlitFramebuffer(GLRFramebuffer *src, GLRect2D srcRect, GLRFramebuffer *dst, GLRect2D dstRect, int aspectMask, bool filter, const char *tag);406407// Takes ownership of data if deleteData = true.408void BufferSubdata(GLRBuffer *buffer, size_t offset, size_t size, uint8_t *data, bool deleteData = true) {409// TODO: Maybe should be a render command instead of an init command? When possible it's better as410// an init command, that's for sure.411GLRInitStep &step = initSteps_.push_uninitialized();412step.stepType = GLRInitStepType::BUFFER_SUBDATA;413_dbg_assert_(offset <= buffer->size_ - size);414step.buffer_subdata.buffer = buffer;415step.buffer_subdata.offset = (int)offset;416step.buffer_subdata.size = (int)size;417step.buffer_subdata.data = data;418step.buffer_subdata.deleteData = deleteData;419}420421// Takes ownership over the data pointer and delete[]-s it.422void TextureImage(GLRTexture *texture, int level, int width, int height, int depth, Draw::DataFormat format, uint8_t *data, GLRAllocType allocType = GLRAllocType::NEW, bool linearFilter = false) {423GLRInitStep &step = initSteps_.push_uninitialized();424step.stepType = GLRInitStepType::TEXTURE_IMAGE;425step.texture_image.texture = texture;426step.texture_image.data = data;427step.texture_image.format = format;428step.texture_image.level = level;429step.texture_image.width = width;430step.texture_image.height = height;431step.texture_image.depth = depth;432step.texture_image.allocType = allocType;433step.texture_image.linearFilter = linearFilter;434}435436void TextureSubImage(int slot, GLRTexture *texture, int level, int x, int y, int width, int height, Draw::DataFormat format, uint8_t *data, GLRAllocType allocType = GLRAllocType::NEW) {437_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);438GLRRenderData _data(GLRRenderCommand::TEXTURE_SUBIMAGE);439_data.texture_subimage.texture = texture;440_data.texture_subimage.data = data;441_data.texture_subimage.format = format;442_data.texture_subimage.level = level;443_data.texture_subimage.x = x;444_data.texture_subimage.y = y;445_data.texture_subimage.width = width;446_data.texture_subimage.height = height;447_data.texture_subimage.allocType = allocType;448_data.texture_subimage.slot = slot;449curRenderStep_->commands.push_back(_data);450}451452void FinalizeTexture(GLRTexture *texture, int loadedLevels, bool genMips) {453GLRInitStep &step = initSteps_.push_uninitialized();454step.stepType = GLRInitStepType::TEXTURE_FINALIZE;455step.texture_finalize.texture = texture;456step.texture_finalize.loadedLevels = loadedLevels;457step.texture_finalize.genMips = genMips;458}459460void BindTexture(int slot, GLRTexture *tex) {461if (!curRenderStep_ && !tex) {462// Likely a pre-emptive bindtexture for D3D11 to avoid hazards. Not necessary.463// This can happen in BlitUsingRaster.464return;465}466_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);467_dbg_assert_(slot < MAX_GL_TEXTURE_SLOTS);468GLRRenderData &data = curRenderStep_->commands.push_uninitialized();469data.cmd = GLRRenderCommand::BINDTEXTURE;470data.texture.slot = slot;471data.texture.texture = tex;472}473474void BindProgram(GLRProgram *program) {475_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);476GLRRenderData &data = curRenderStep_->commands.push_uninitialized();477data.cmd = GLRRenderCommand::BINDPROGRAM;478_dbg_assert_(program != nullptr);479data.program.program = program;480#ifdef _DEBUG481curProgram_ = program;482#endif483}484485void SetDepth(bool enabled, bool write, GLenum func) {486_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);487GLRRenderData &data = curRenderStep_->commands.push_uninitialized();488data.cmd = GLRRenderCommand::DEPTH;489data.depth.enabled = enabled;490data.depth.write = write;491data.depth.func = func;492}493494void SetViewport(const GLRViewport &vp) {495_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);496GLRRenderData &data = curRenderStep_->commands.push_uninitialized();497data.cmd = GLRRenderCommand::VIEWPORT;498data.viewport.vp = vp;499}500501void SetScissor(const GLRect2D &rc) {502_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);503GLRRenderData &data = curRenderStep_->commands.push_uninitialized();504data.cmd = GLRRenderCommand::SCISSOR;505data.scissor.rc = rc;506}507508void SetUniformI(const GLint *loc, int count, const int *udata) {509_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);510#ifdef _DEBUG511_dbg_assert_(curProgram_);512#endif513GLRRenderData &data = curRenderStep_->commands.push_uninitialized();514data.cmd = GLRRenderCommand::UNIFORM4I;515data.uniform4.name = nullptr;516data.uniform4.loc = loc;517data.uniform4.count = count;518memcpy(data.uniform4.v, udata, sizeof(int) * count);519}520521void SetUniformI1(const GLint *loc, int udata) {522_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);523#ifdef _DEBUG524_dbg_assert_(curProgram_);525#endif526GLRRenderData &data = curRenderStep_->commands.push_uninitialized();527data.cmd = GLRRenderCommand::UNIFORM4I;528data.uniform4.name = nullptr;529data.uniform4.loc = loc;530data.uniform4.count = 1;531memcpy(data.uniform4.v, &udata, sizeof(udata));532}533534void SetUniformUI(const GLint *loc, int count, const uint32_t *udata) {535_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);536#ifdef _DEBUG537_dbg_assert_(curProgram_);538#endif539GLRRenderData &data = curRenderStep_->commands.push_uninitialized();540data.cmd = GLRRenderCommand::UNIFORM4UI;541data.uniform4.name = nullptr;542data.uniform4.loc = loc;543data.uniform4.count = count;544memcpy(data.uniform4.v, udata, sizeof(uint32_t) * count);545}546547void SetUniformUI1(const GLint *loc, uint32_t udata) {548_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);549#ifdef _DEBUG550_dbg_assert_(curProgram_);551#endif552GLRRenderData &data = curRenderStep_->commands.push_uninitialized();553data.cmd = GLRRenderCommand::UNIFORM4UI;554data.uniform4.name = nullptr;555data.uniform4.loc = loc;556data.uniform4.count = 1;557memcpy(data.uniform4.v, &udata, sizeof(udata));558}559560void SetUniformF(const GLint *loc, int count, const float *udata) {561_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);562#ifdef _DEBUG563_dbg_assert_(curProgram_);564#endif565GLRRenderData &data = curRenderStep_->commands.push_uninitialized();566data.cmd = GLRRenderCommand::UNIFORM4F;567data.uniform4.name = nullptr;568data.uniform4.loc = loc;569data.uniform4.count = count;570memcpy(data.uniform4.v, udata, sizeof(float) * count);571}572573void SetUniformF1(const GLint *loc, const float udata) {574_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);575#ifdef _DEBUG576_dbg_assert_(curProgram_);577#endif578GLRRenderData &data = curRenderStep_->commands.push_uninitialized();579data.cmd = GLRRenderCommand::UNIFORM4F;580data.uniform4.name = nullptr;581data.uniform4.loc = loc;582data.uniform4.count = 1;583memcpy(data.uniform4.v, &udata, sizeof(float));584}585586void SetUniformF(const char *name, int count, const float *udata) {587_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);588#ifdef _DEBUG589_dbg_assert_(curProgram_);590#endif591GLRRenderData &data = curRenderStep_->commands.push_uninitialized();592data.cmd = GLRRenderCommand::UNIFORM4F;593data.uniform4.name = name;594data.uniform4.loc = nullptr;595data.uniform4.count = count;596memcpy(data.uniform4.v, udata, sizeof(float) * count);597}598599void SetUniformM4x4(const GLint *loc, const float *udata) {600_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);601#ifdef _DEBUG602_dbg_assert_(curProgram_);603#endif604GLRRenderData &data = curRenderStep_->commands.push_uninitialized();605data.cmd = GLRRenderCommand::UNIFORMMATRIX;606data.uniformMatrix4.name = nullptr;607data.uniformMatrix4.loc = loc;608memcpy(data.uniformMatrix4.m, udata, sizeof(float) * 16);609}610611void SetUniformM4x4Stereo(const char *name, const GLint *loc, const float *left, const float *right) {612_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);613#ifdef _DEBUG614_dbg_assert_(curProgram_);615#endif616GLRRenderData &data = curRenderStep_->commands.push_uninitialized();617data.cmd = GLRRenderCommand::UNIFORMSTEREOMATRIX;618data.uniformStereoMatrix4.name = name;619data.uniformStereoMatrix4.loc = loc;620data.uniformStereoMatrix4.mData = new float[32];621memcpy(&data.uniformStereoMatrix4.mData[0], left, sizeof(float) * 16);622memcpy(&data.uniformStereoMatrix4.mData[16], right, sizeof(float) * 16);623}624625void SetUniformM4x4(const char *name, const float *udata) {626_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);627#ifdef _DEBUG628_dbg_assert_(curProgram_);629#endif630GLRRenderData &data = curRenderStep_->commands.push_uninitialized();631data.cmd = GLRRenderCommand::UNIFORMMATRIX;632data.uniformMatrix4.name = name;633data.uniformMatrix4.loc = nullptr;634memcpy(data.uniformMatrix4.m, udata, sizeof(float) * 16);635}636637void SetBlendAndMask(int colorMask, bool blendEnabled, GLenum srcColor, GLenum dstColor, GLenum srcAlpha, GLenum dstAlpha, GLenum funcColor, GLenum funcAlpha) {638// Make this one only a non-debug _assert_, since it often comes first.639// Lets us collect info about this potential crash through assert extra data.640_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);641GLRRenderData &data = curRenderStep_->commands.push_uninitialized();642data.cmd = GLRRenderCommand::BLEND;643data.blend.mask = colorMask;644data.blend.enabled = blendEnabled;645data.blend.srcColor = srcColor;646data.blend.dstColor = dstColor;647data.blend.srcAlpha = srcAlpha;648data.blend.dstAlpha = dstAlpha;649data.blend.funcColor = funcColor;650data.blend.funcAlpha = funcAlpha;651}652653void SetNoBlendAndMask(int colorMask) {654_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);655GLRRenderData &data = curRenderStep_->commands.push_uninitialized();656data.cmd = GLRRenderCommand::BLEND;657data.blend.mask = colorMask;658data.blend.enabled = false;659}660661#ifndef USING_GLES2662void SetLogicOp(bool enabled, GLenum logicOp) {663_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);664GLRRenderData &data = curRenderStep_->commands.push_uninitialized();665data.cmd = GLRRenderCommand::LOGICOP;666data.logic.enabled = enabled;667data.logic.logicOp = logicOp;668}669#endif670671void SetStencil(bool enabled, GLenum func, uint8_t refValue, uint8_t compareMask, uint8_t writeMask, GLenum sFail, GLenum zFail, GLenum pass) {672_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);673GLRRenderData &data = curRenderStep_->commands.push_uninitialized();674data.cmd = GLRRenderCommand::STENCIL;675data.stencil.enabled = enabled;676data.stencil.func = func;677data.stencil.ref = refValue;678data.stencil.compareMask = compareMask;679data.stencil.writeMask = writeMask;680data.stencil.sFail = sFail;681data.stencil.zFail = zFail;682data.stencil.pass = pass;683}684685void SetStencilDisabled() {686_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);687GLRRenderData &data = curRenderStep_->commands.push_uninitialized();688data.cmd = GLRRenderCommand::STENCIL;689data.stencil.enabled = false;690}691692void SetBlendFactor(const float color[4]) {693_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);694GLRRenderData &data = curRenderStep_->commands.push_uninitialized();695data.cmd = GLRRenderCommand::BLENDCOLOR;696CopyFloat4(data.blendColor.color, color);697}698699void SetRaster(GLboolean cullEnable, GLenum frontFace, GLenum cullFace, GLboolean ditherEnable, GLboolean depthClamp) {700_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);701GLRRenderData &data = curRenderStep_->commands.push_uninitialized();702data.cmd = GLRRenderCommand::RASTER;703data.raster.cullEnable = cullEnable;704data.raster.frontFace = frontFace;705data.raster.cullFace = cullFace;706data.raster.ditherEnable = ditherEnable;707data.raster.depthClampEnable = depthClamp;708}709710// Modifies the current texture as per GL specs, not global state.711void SetTextureSampler(int slot, GLenum wrapS, GLenum wrapT, GLenum magFilter, GLenum minFilter, float anisotropy) {712_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);713_dbg_assert_(slot < MAX_GL_TEXTURE_SLOTS);714GLRRenderData &data = curRenderStep_->commands.push_uninitialized();715data.cmd = GLRRenderCommand::TEXTURESAMPLER;716data.textureSampler.slot = slot;717data.textureSampler.wrapS = wrapS;718data.textureSampler.wrapT = wrapT;719data.textureSampler.magFilter = magFilter;720data.textureSampler.minFilter = minFilter;721data.textureSampler.anisotropy = anisotropy;722}723724void SetTextureLod(int slot, float minLod, float maxLod, float lodBias) {725_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);726_dbg_assert_(slot < MAX_GL_TEXTURE_SLOTS);727GLRRenderData &data = curRenderStep_->commands.push_uninitialized();728data.cmd = GLRRenderCommand::TEXTURELOD;729data.textureLod.slot = slot;730data.textureLod.minLod = minLod;731data.textureLod.maxLod = maxLod;732data.textureLod.lodBias = lodBias;733}734735// If scissorW == 0, no scissor is applied (the whole render target is cleared).736void Clear(uint32_t clearColor, float clearZ, int clearStencil, int clearMask, int colorMask, int scissorX, int scissorY, int scissorW, int scissorH) {737_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);738if (!clearMask)739return;740GLRRenderData &data = curRenderStep_->commands.push_uninitialized();741data.cmd = GLRRenderCommand::CLEAR;742data.clear.clearMask = clearMask;743data.clear.clearColor = clearColor;744data.clear.clearZ = clearZ;745data.clear.clearStencil = clearStencil;746data.clear.colorMask = colorMask;747data.clear.scissorX = scissorX;748data.clear.scissorY = scissorY;749data.clear.scissorW = scissorW;750data.clear.scissorH = scissorH;751}752753void Draw(GLRInputLayout *inputLayout, GLRBuffer *vertexBuffer, uint32_t vertexOffset, GLenum mode, int first, int count) {754_dbg_assert_(vertexBuffer && curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);755GLRRenderData &data = curRenderStep_->commands.push_uninitialized();756data.cmd = GLRRenderCommand::DRAW;757data.draw.inputLayout = inputLayout;758data.draw.vertexOffset = vertexOffset;759data.draw.vertexBuffer = vertexBuffer;760data.draw.indexBuffer = nullptr;761data.draw.mode = mode;762data.draw.first = first;763data.draw.count = count;764data.draw.indexType = 0;765}766767// Would really love to have a basevertex parameter, but impossible in unextended GLES, without glDrawElementsBaseVertex, unfortunately.768void DrawIndexed(GLRInputLayout *inputLayout, GLRBuffer *vertexBuffer, uint32_t vertexOffset, GLRBuffer *indexBuffer, uint32_t indexOffset, GLenum mode, int count, GLenum indexType, int instances = 1) {769_dbg_assert_(vertexBuffer && indexBuffer && curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);770GLRRenderData &data = curRenderStep_->commands.push_uninitialized();771data.cmd = GLRRenderCommand::DRAW;772data.draw.inputLayout = inputLayout;773data.draw.vertexOffset = vertexOffset;774data.draw.vertexBuffer = vertexBuffer;775data.draw.indexBuffer = indexBuffer;776data.draw.indexOffset = indexOffset;777data.draw.mode = mode;778data.draw.count = count;779data.draw.indexType = indexType;780data.draw.instances = instances;781}782783enum { MAX_INFLIGHT_FRAMES = 3 };784785void SetInflightFrames(int f) {786newInflightFrames_ = f < 1 || f > MAX_INFLIGHT_FRAMES ? MAX_INFLIGHT_FRAMES : f;787}788789int GetCurFrame() const {790return curFrame_;791}792793void Resize(int width, int height) {794targetWidth_ = width;795targetHeight_ = height;796queueRunner_.Resize(width, height);797}798799void UnregisterPushBuffer(GLPushBuffer *buffer) {800int foundCount = 0;801for (int i = 0; i < MAX_INFLIGHT_FRAMES; i++) {802auto iter = frameData_[i].activePushBuffers.find(buffer);803if (iter != frameData_[i].activePushBuffers.end()) {804frameData_[i].activePushBuffers.erase(iter);805foundCount++;806}807}808_dbg_assert_(foundCount == 1);809}810811void SetSwapFunction(std::function<void()> swapFunction) {812swapFunction_ = swapFunction;813}814815void SetSwapIntervalFunction(std::function<void(int)> swapIntervalFunction) {816swapIntervalFunction_ = swapIntervalFunction;817}818819void SwapInterval(int interval) {820if (interval != swapInterval_) {821swapInterval_ = interval;822swapIntervalChanged_ = true;823}824}825826void StartThread(); // Currently only used on iOS, since we fully recreate the context on Android827void StopThread();828829bool SawOutOfMemory() {830return queueRunner_.SawOutOfMemory();831}832833// Only supports a common subset.834std::string GetGLString(int name) const {835return queueRunner_.GetGLString(name);836}837838// Used during Android-style ugly shutdown. No need to have a way to set it back because we'll be839// destroyed.840void SetSkipGLCalls() {841skipGLCalls_ = true;842}843844private:845bool Run(GLRRenderThreadTask &task);846847// Bad for performance but sometimes necessary for synchronous CPU readbacks (screenshots and whatnot).848void FlushSync();849850// When using legacy functionality for push buffers (glBufferData), we need to flush them851// before actually making the glDraw* calls. It's best if the render manager handles that.852void RegisterPushBuffer(int frame, GLPushBuffer *buffer) {853frameData_[frame].activePushBuffers.insert(buffer);854}855856GLFrameData frameData_[MAX_INFLIGHT_FRAMES];857858// Submission time state859bool insideFrame_ = false;860861GLRStep *curRenderStep_ = nullptr;862std::vector<GLRStep *> steps_;863FastVec<GLRInitStep> initSteps_;864865// Execution time state866// TODO: Rename this, as we don't actually use a compile thread on OpenGL.867bool runCompileThread_ = true;868869// Thread is managed elsewhere, and should call ThreadFrame.870GLQueueRunner queueRunner_;871872// For pushing data on the queue.873std::mutex pushMutex_;874std::condition_variable pushCondVar_;875876std::queue<GLRRenderThreadTask *> renderThreadQueue_;877878// For readbacks and other reasons we need to sync with the render thread.879std::mutex syncMutex_;880std::condition_variable syncCondVar_;881882bool firstFrame_ = true;883bool vrRenderStarted_ = false;884bool syncDone_ = false;885886GLDeleter deleter_;887bool skipGLCalls_ = false;888889int curFrame_ = 0;890891std::function<void()> swapFunction_;892std::function<void(int)> swapIntervalFunction_;893GLBufferStrategy bufferStrategy_ = GLBufferStrategy::SUBDATA;894895int inflightFrames_ = MAX_INFLIGHT_FRAMES;896int newInflightFrames_ = -1;897898int swapInterval_ = 0;899bool swapIntervalChanged_ = true;900901int targetWidth_ = 0;902int targetHeight_ = 0;903904#ifdef _DEBUG905GLRProgram *curProgram_ = nullptr;906#endif907Draw::DeviceCaps caps_{};908909std::string profilePassesString_;910InvalidationCallback invalidationCallback_;911912uint64_t frameIdGen_ = FRAME_TIME_HISTORY_LENGTH;913HistoryBuffer<FrameTimeData, FRAME_TIME_HISTORY_LENGTH> &frameTimeHistory_;914};915916917