mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-25 03:09:13 +01:00
Merge pull request #10875 from hrydgard/more-gl-shutdown-fixes
More GL shutdown fixes
This commit is contained in:
8 files changed
+234
-171
No files matched your search
@@ -109,11 +109,9 @@ void DrawEngineGLES::DeviceRestore() {
|
||||
|
||||
void DrawEngineGLES::InitDeviceObjects() {
|
||||
for (int i = 0; i < GLRenderManager::MAX_INFLIGHT_FRAMES; i++) {
|
||||
frameData_[i].pushVertex = new GLPushBuffer(render_, GL_ARRAY_BUFFER, 1024 * 1024);
|
||||
frameData_[i].pushIndex = new GLPushBuffer(render_, GL_ELEMENT_ARRAY_BUFFER, 256 * 1024);
|
||||
frameData_[i].pushVertex = render_->CreatePushBuffer(i, GL_ARRAY_BUFFER, 1024 * 1024);
|
||||
frameData_[i].pushIndex = render_->CreatePushBuffer(i, GL_ELEMENT_ARRAY_BUFFER, 256 * 1024);
|
||||
|
||||
render_->RegisterPushBuffer(i, frameData_[i].pushVertex);
|
||||
render_->RegisterPushBuffer(i, frameData_[i].pushIndex);
|
||||
}
|
||||
|
||||
int vertexSize = sizeof(TransformedVertex);
|
||||
@@ -131,12 +129,8 @@ void DrawEngineGLES::DestroyDeviceObjects() {
|
||||
if (!frameData_[i].pushVertex && !frameData_[i].pushIndex)
|
||||
continue;
|
||||
|
||||
render_->UnregisterPushBuffer(i, frameData_[i].pushVertex);
|
||||
render_->UnregisterPushBuffer(i, frameData_[i].pushIndex);
|
||||
frameData_[i].pushVertex->Destroy();
|
||||
frameData_[i].pushIndex->Destroy();
|
||||
delete frameData_[i].pushVertex;
|
||||
delete frameData_[i].pushIndex;
|
||||
render_->DeletePushBuffer(frameData_[i].pushVertex);
|
||||
render_->DeletePushBuffer(frameData_[i].pushIndex);
|
||||
frameData_[i].pushVertex = nullptr;
|
||||
frameData_[i].pushIndex = nullptr;
|
||||
}
|
||||
@@ -159,14 +153,14 @@ void DrawEngineGLES::ClearInputLayoutMap() {
|
||||
|
||||
void DrawEngineGLES::BeginFrame() {
|
||||
FrameData &frameData = frameData_[render_->GetCurFrame()];
|
||||
frameData.pushIndex->Begin();
|
||||
frameData.pushVertex->Begin();
|
||||
render_->BeginPushBuffer(frameData.pushIndex);
|
||||
render_->BeginPushBuffer(frameData.pushVertex);
|
||||
}
|
||||
|
||||
void DrawEngineGLES::EndFrame() {
|
||||
FrameData &frameData = frameData_[render_->GetCurFrame()];
|
||||
frameData.pushIndex->End();
|
||||
frameData.pushVertex->End();
|
||||
render_->EndPushBuffer(frameData.pushIndex);
|
||||
render_->EndPushBuffer(frameData.pushVertex);
|
||||
tessDataTransfer->EndFrame();
|
||||
}
|
||||
|
||||
|
||||
@@ -118,8 +118,10 @@ GPU_GLES::GPU_GLES(GraphicsContext *gfxCtx, Draw::DrawContext *draw)
|
||||
}
|
||||
|
||||
GPU_GLES::~GPU_GLES() {
|
||||
GLRenderManager *render = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
|
||||
render->Wipe();
|
||||
if (draw_) {
|
||||
GLRenderManager *render = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
|
||||
render->Wipe();
|
||||
}
|
||||
|
||||
// If we're here during app shutdown (exiting the Windows app in-game, for example)
|
||||
// everything should already be cleared since DeviceLost has been run.
|
||||
@@ -135,9 +137,6 @@ GPU_GLES::~GPU_GLES() {
|
||||
shaderManagerGL_ = nullptr;
|
||||
delete framebufferManagerGL_;
|
||||
delete textureCacheGL_;
|
||||
#ifdef _WIN32
|
||||
gfxCtx_->SwapInterval(0);
|
||||
#endif
|
||||
}
|
||||
|
||||
static constexpr int MakeIntelSimpleVer(int v1, int v2, int v3) {
|
||||
@@ -332,11 +331,13 @@ void GPU_GLES::DeviceLost() {
|
||||
// TransformDraw has registered as a GfxResourceHolder.
|
||||
drawEngine_.ClearInputLayoutMap();
|
||||
shaderManagerGL_->ClearCache(false);
|
||||
textureCacheGL_->Clear(false);
|
||||
textureCacheGL_->DeviceLost();
|
||||
fragmentTestCache_.Clear(false);
|
||||
depalShaderCache_.Clear();
|
||||
drawEngine_.DeviceLost();
|
||||
framebufferManagerGL_->DeviceLost();
|
||||
// Don't even try to access the lost device.
|
||||
draw_ = nullptr;
|
||||
}
|
||||
|
||||
void GPU_GLES::DeviceRestore() {
|
||||
|
||||
@@ -53,6 +53,7 @@ TextureCacheGLES::TextureCacheGLES(Draw::DrawContext *draw)
|
||||
SetupTextureDecoder();
|
||||
|
||||
nextTexture_ = nullptr;
|
||||
|
||||
std::vector<GLRInputLayout::Entry> entries;
|
||||
entries.push_back({ 0, 3, GL_FLOAT, GL_FALSE, 20, 0 });
|
||||
entries.push_back({ 1, 2, GL_FLOAT, GL_FALSE, 20, 12 });
|
||||
@@ -60,7 +61,9 @@ TextureCacheGLES::TextureCacheGLES(Draw::DrawContext *draw)
|
||||
}
|
||||
|
||||
TextureCacheGLES::~TextureCacheGLES() {
|
||||
render_->DeleteInputLayout(shadeInputLayout_);
|
||||
if (shadeInputLayout_) {
|
||||
render_->DeleteInputLayout(shadeInputLayout_);
|
||||
}
|
||||
Clear(true);
|
||||
}
|
||||
|
||||
@@ -839,6 +842,23 @@ bool TextureCacheGLES::GetCurrentTextureDebug(GPUDebugBuffer &buffer, int level)
|
||||
#endif
|
||||
}
|
||||
|
||||
void TextureCacheGLES::DeviceLost() {
|
||||
if (shadeInputLayout_) {
|
||||
render_->DeleteInputLayout(shadeInputLayout_);
|
||||
shadeInputLayout_ = nullptr;
|
||||
}
|
||||
Clear(false);
|
||||
draw_ = nullptr;
|
||||
render_ = nullptr;
|
||||
}
|
||||
|
||||
void TextureCacheGLES::DeviceRestore(Draw::DrawContext *draw) {
|
||||
draw_ = draw;
|
||||
render_ = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
|
||||
if (!shadeInputLayout_) {
|
||||
std::vector<GLRInputLayout::Entry> entries;
|
||||
entries.push_back({ 0, 3, GL_FLOAT, GL_FALSE, 20, 0 });
|
||||
entries.push_back({ 1, 2, GL_FLOAT, GL_FALSE, 20, 12 });
|
||||
shadeInputLayout_ = render_->CreateInputLayout(entries);
|
||||
}
|
||||
}
|
||||
@@ -66,6 +66,7 @@ public:
|
||||
void SetFramebufferSamplingParams(u16 bufferWidth, u16 bufferHeight);
|
||||
bool GetCurrentTextureDebug(GPUDebugBuffer &buffer, int level) override;
|
||||
|
||||
void DeviceLost();
|
||||
void DeviceRestore(Draw::DrawContext *draw);
|
||||
|
||||
protected:
|
||||
@@ -95,7 +96,7 @@ private:
|
||||
ShaderManagerGLES *shaderManager_;
|
||||
DrawEngineGLES *drawEngine_;
|
||||
|
||||
GLRInputLayout *shadeInputLayout_;
|
||||
GLRInputLayout *shadeInputLayout_ = nullptr;
|
||||
|
||||
enum { INVALID_TEX = -1 };
|
||||
};
|
||||
|
||||
@@ -85,8 +85,9 @@ void WindowsGLContext::Resume() {
|
||||
|
||||
void FormatDebugOutputARB(char outStr[], size_t outStrSize, GLenum source, GLenum type,
|
||||
GLuint id, GLenum severity, const char *msg) {
|
||||
|
||||
char sourceStr[32];
|
||||
const char *sourceFmt = "UNDEFINED(0x%04X)";
|
||||
const char *sourceFmt;
|
||||
switch(source) {
|
||||
case GL_DEBUG_SOURCE_API_ARB: sourceFmt = "API"; break;
|
||||
case GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB: sourceFmt = "WINDOW_SYSTEM"; break;
|
||||
@@ -94,11 +95,12 @@ void FormatDebugOutputARB(char outStr[], size_t outStrSize, GLenum source, GLenu
|
||||
case GL_DEBUG_SOURCE_THIRD_PARTY_ARB: sourceFmt = "THIRD_PARTY"; break;
|
||||
case GL_DEBUG_SOURCE_APPLICATION_ARB: sourceFmt = "APPLICATION"; break;
|
||||
case GL_DEBUG_SOURCE_OTHER_ARB: sourceFmt = "OTHER"; break;
|
||||
default: sourceFmt = "UNDEFINED(0x%04X)"; break;
|
||||
}
|
||||
snprintf(sourceStr, sizeof(sourceStr), sourceFmt, source);
|
||||
|
||||
char typeStr[32];
|
||||
const char *typeFmt = "UNDEFINED(0x%04X)";
|
||||
const char *typeFmt;
|
||||
switch(type) {
|
||||
case GL_DEBUG_TYPE_ERROR_ARB: typeFmt = "ERROR"; break;
|
||||
case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB: typeFmt = "DEPRECATED_BEHAVIOR"; break;
|
||||
@@ -106,15 +108,17 @@ void FormatDebugOutputARB(char outStr[], size_t outStrSize, GLenum source, GLenu
|
||||
case GL_DEBUG_TYPE_PORTABILITY_ARB: typeFmt = "PORTABILITY"; break;
|
||||
case GL_DEBUG_TYPE_PERFORMANCE_ARB: typeFmt = "PERFORMANCE"; break;
|
||||
case GL_DEBUG_TYPE_OTHER_ARB: typeFmt = "OTHER"; break;
|
||||
default: typeFmt = "UNDEFINED(0x%04X)"; break;
|
||||
}
|
||||
snprintf(typeStr, sizeof(typeStr), typeFmt, type);
|
||||
|
||||
char severityStr[32];
|
||||
const char *severityFmt = "UNDEFINED";
|
||||
const char *severityFmt;
|
||||
switch (severity) {
|
||||
case GL_DEBUG_SEVERITY_HIGH_ARB: severityFmt = "HIGH"; break;
|
||||
case GL_DEBUG_SEVERITY_MEDIUM_ARB: severityFmt = "MEDIUM"; break;
|
||||
case GL_DEBUG_SEVERITY_LOW_ARB: severityFmt = "LOW"; break;
|
||||
default: severityFmt = "UNDEFINED(%d)"; break;
|
||||
}
|
||||
|
||||
snprintf(severityStr, sizeof(severityStr), severityFmt, severity);
|
||||
@@ -123,6 +127,10 @@ void FormatDebugOutputARB(char outStr[], size_t outStrSize, GLenum source, GLenu
|
||||
|
||||
void DebugCallbackARB(GLenum source, GLenum type, GLuint id, GLenum severity,
|
||||
GLsizei length, const GLchar *message, GLvoid *userParam) {
|
||||
// Ignore buffer mapping messages from NVIDIA
|
||||
if (source == GL_DEBUG_SOURCE_API_ARB && type == GL_DEBUG_TYPE_OTHER_ARB && id == 131185)
|
||||
return;
|
||||
|
||||
(void)length;
|
||||
FILE *outFile = (FILE *)userParam;
|
||||
char finalMessage[1024];
|
||||
|
||||
@@ -13,7 +13,13 @@
|
||||
#define VLOG(...)
|
||||
#endif
|
||||
|
||||
void GLDeleter::Perform() {
|
||||
// Runs on the GPU thread.
|
||||
void GLDeleter::Perform(GLRenderManager *renderManager) {
|
||||
for (auto pushBuffer : pushBuffers) {
|
||||
renderManager->UnregisterPushBuffer(pushBuffer);
|
||||
delete pushBuffer;
|
||||
}
|
||||
pushBuffers.clear();
|
||||
for (auto shader : shaders) {
|
||||
delete shader;
|
||||
}
|
||||
@@ -52,7 +58,7 @@ GLRenderManager::~GLRenderManager() {
|
||||
_assert_(frameData_[i].deleter_prev.IsEmpty());
|
||||
}
|
||||
// Was anything deleted during shutdown?
|
||||
deleter_.Perform();
|
||||
deleter_.Perform(this);
|
||||
_assert_(deleter_.IsEmpty());
|
||||
}
|
||||
|
||||
@@ -98,15 +104,15 @@ void GLRenderManager::ThreadEnd() {
|
||||
|
||||
// Good point to run all the deleters to get rid of leftover objects.
|
||||
for (int i = 0; i < MAX_INFLIGHT_FRAMES; i++) {
|
||||
frameData_[i].deleter.Perform();
|
||||
frameData_[i].deleter_prev.Perform();
|
||||
frameData_[i].deleter.Perform(this);
|
||||
frameData_[i].deleter_prev.Perform(this);
|
||||
for (int j = 0; j < (int)frameData_[i].steps.size(); j++) {
|
||||
delete frameData_[i].steps[j];
|
||||
}
|
||||
frameData_[i].steps.clear();
|
||||
frameData_[i].initSteps.clear();
|
||||
}
|
||||
deleter_.Perform();
|
||||
deleter_.Perform(this);
|
||||
|
||||
for (int i = 0; i < (int)steps_.size(); i++) {
|
||||
delete steps_[i];
|
||||
@@ -140,7 +146,7 @@ bool GLRenderManager::ThreadFrame() {
|
||||
}
|
||||
VLOG("PULL: Setting frame[%d].readyForRun = false", threadFrame_);
|
||||
frameData.readyForRun = false;
|
||||
frameData.deleter_prev.Perform();
|
||||
frameData.deleter_prev.Perform(this);
|
||||
frameData.deleter_prev.Take(frameData.deleter);
|
||||
// Previously we had a quick exit here that avoided calling Run() if run_ was suddenly false,
|
||||
// but that created a race condition where frames could end up not finished properly on resize etc.
|
||||
@@ -568,7 +574,7 @@ GLPushBuffer::GLPushBuffer(GLRenderManager *render, GLuint target, size_t size)
|
||||
}
|
||||
|
||||
GLPushBuffer::~GLPushBuffer() {
|
||||
assert(buffers_.empty());
|
||||
Destroy();
|
||||
}
|
||||
|
||||
void GLPushBuffer::Map() {
|
||||
@@ -638,13 +644,17 @@ bool GLPushBuffer::AddBuffer() {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Executed on the render thread!
|
||||
void GLPushBuffer::Destroy() {
|
||||
for (BufInfo &info : buffers_) {
|
||||
// This will automatically unmap device memory, if needed.
|
||||
render_->DeleteBuffer(info.buffer);
|
||||
// NOTE: We immediately delete the buffer, don't go through the deleter, since we're on the render thread.
|
||||
// render_->DeleteBuffer(info.buffer);
|
||||
delete info.buffer;
|
||||
FreeAlignedMemory(info.localMemory);
|
||||
}
|
||||
buffers_.clear();
|
||||
buf_ = -1;
|
||||
}
|
||||
|
||||
void GLPushBuffer::NextBuffer(size_t minSize) {
|
||||
|
||||
+163
-131
@@ -177,6 +177,131 @@ private:
|
||||
bool hasStorage_ = false;
|
||||
};
|
||||
|
||||
class GLRenderManager;
|
||||
|
||||
// Similar to VulkanPushBuffer but is currently less efficient - it collects all the data in
|
||||
// RAM then does a big memcpy/buffer upload at the end of the frame. This is at least a lot
|
||||
// faster than the hundreds of buffer uploads or memory array buffers we used before.
|
||||
// On modern GL we could avoid the copy using glBufferStorage but not sure it's worth the
|
||||
// trouble.
|
||||
// We need to manage the lifetime of this together with the other resources so its destructor
|
||||
// runs on the render thread.
|
||||
class GLPushBuffer {
|
||||
public:
|
||||
friend class GLRenderManager;
|
||||
|
||||
struct BufInfo {
|
||||
GLRBuffer *buffer = nullptr;
|
||||
uint8_t *localMemory = nullptr;
|
||||
uint8_t *deviceMemory = nullptr;
|
||||
size_t flushOffset = 0;
|
||||
};
|
||||
|
||||
GLPushBuffer(GLRenderManager *render, GLuint target, size_t size);
|
||||
~GLPushBuffer();
|
||||
|
||||
void Reset() { offset_ = 0; }
|
||||
|
||||
private:
|
||||
// Needs context in case of defragment.
|
||||
void Begin() {
|
||||
buf_ = 0;
|
||||
offset_ = 0;
|
||||
// Note: we must defrag because some buffers may be smaller than size_.
|
||||
Defragment();
|
||||
Map();
|
||||
assert(writePtr_);
|
||||
}
|
||||
|
||||
void BeginNoReset() {
|
||||
Map();
|
||||
}
|
||||
|
||||
void End() {
|
||||
Unmap();
|
||||
}
|
||||
|
||||
public:
|
||||
void Map();
|
||||
void Unmap();
|
||||
|
||||
// When using the returned memory, make sure to bind the returned vkbuf.
|
||||
// This will later allow for handling overflow correctly.
|
||||
size_t Allocate(size_t numBytes, GLRBuffer **vkbuf) {
|
||||
size_t out = offset_;
|
||||
if (offset_ + ((numBytes + 3) & ~3) >= size_) {
|
||||
NextBuffer(numBytes);
|
||||
out = offset_;
|
||||
offset_ += (numBytes + 3) & ~3;
|
||||
} else {
|
||||
offset_ += (numBytes + 3) & ~3; // Round up to 4 bytes.
|
||||
}
|
||||
*vkbuf = buffers_[buf_].buffer;
|
||||
return out;
|
||||
}
|
||||
|
||||
// Returns the offset that should be used when binding this buffer to get this data.
|
||||
size_t Push(const void *data, size_t size, GLRBuffer **vkbuf) {
|
||||
assert(writePtr_);
|
||||
size_t off = Allocate(size, vkbuf);
|
||||
memcpy(writePtr_ + off, data, size);
|
||||
return off;
|
||||
}
|
||||
|
||||
uint32_t PushAligned(const void *data, size_t size, int align, GLRBuffer **vkbuf) {
|
||||
assert(writePtr_);
|
||||
offset_ = (offset_ + align - 1) & ~(align - 1);
|
||||
size_t off = Allocate(size, vkbuf);
|
||||
memcpy(writePtr_ + off, data, size);
|
||||
return (uint32_t)off;
|
||||
}
|
||||
|
||||
size_t GetOffset() const {
|
||||
return offset_;
|
||||
}
|
||||
|
||||
// "Zero-copy" variant - you can write the data directly as you compute it.
|
||||
// Recommended.
|
||||
void *Push(size_t size, uint32_t *bindOffset, GLRBuffer **vkbuf) {
|
||||
assert(writePtr_);
|
||||
size_t off = Allocate(size, vkbuf);
|
||||
*bindOffset = (uint32_t)off;
|
||||
return writePtr_ + off;
|
||||
}
|
||||
void *PushAligned(size_t size, uint32_t *bindOffset, GLRBuffer **vkbuf, int align) {
|
||||
assert(writePtr_);
|
||||
offset_ = (offset_ + align - 1) & ~(align - 1);
|
||||
size_t off = Allocate(size, vkbuf);
|
||||
*bindOffset = (uint32_t)off;
|
||||
return writePtr_ + off;
|
||||
}
|
||||
|
||||
size_t GetTotalSize() const;
|
||||
|
||||
void Flush();
|
||||
|
||||
protected:
|
||||
void MapDevice(GLBufferStrategy strategy);
|
||||
void UnmapDevice();
|
||||
|
||||
private:
|
||||
bool AddBuffer();
|
||||
void NextBuffer(size_t minSize);
|
||||
void Defragment();
|
||||
void Destroy();
|
||||
|
||||
GLRenderManager *render_;
|
||||
std::vector<BufInfo> buffers_;
|
||||
size_t buf_ = 0;
|
||||
size_t offset_ = 0;
|
||||
size_t size_ = 0;
|
||||
uint8_t *writePtr_ = nullptr;
|
||||
GLuint target_;
|
||||
GLBufferStrategy strategy_ = GLBufferStrategy::SUBDATA;
|
||||
|
||||
friend class GLRenderManager;
|
||||
};
|
||||
|
||||
enum class GLRRunType {
|
||||
END,
|
||||
SYNC,
|
||||
@@ -184,10 +309,10 @@ enum class GLRRunType {
|
||||
|
||||
class GLDeleter {
|
||||
public:
|
||||
void Perform();
|
||||
void Perform(GLRenderManager *renderManager);
|
||||
|
||||
bool IsEmpty() const {
|
||||
return shaders.empty() && programs.empty() && buffers.empty() && textures.empty() && inputLayouts.empty() && framebuffers.empty();
|
||||
return shaders.empty() && programs.empty() && buffers.empty() && textures.empty() && inputLayouts.empty() && framebuffers.empty() && pushBuffers.empty();
|
||||
}
|
||||
void Take(GLDeleter &other) {
|
||||
_assert_msg_(G3D, IsEmpty(), "Deleter already has stuff");
|
||||
@@ -197,12 +322,14 @@ public:
|
||||
textures = std::move(other.textures);
|
||||
inputLayouts = std::move(other.inputLayouts);
|
||||
framebuffers = std::move(other.framebuffers);
|
||||
pushBuffers = std::move(other.pushBuffers);
|
||||
other.shaders.clear();
|
||||
other.programs.clear();
|
||||
other.buffers.clear();
|
||||
other.textures.clear();
|
||||
other.inputLayouts.clear();
|
||||
other.framebuffers.clear();
|
||||
other.pushBuffers.clear();
|
||||
}
|
||||
|
||||
std::vector<GLRShader *> shaders;
|
||||
@@ -211,6 +338,7 @@ public:
|
||||
std::vector<GLRTexture *> textures;
|
||||
std::vector<GLRInputLayout *> inputLayouts;
|
||||
std::vector<GLRFramebuffer *> framebuffers;
|
||||
std::vector<GLPushBuffer *> pushBuffers;
|
||||
};
|
||||
|
||||
class GLRInputLayout {
|
||||
@@ -327,6 +455,12 @@ public:
|
||||
return step.create_input_layout.inputLayout;
|
||||
}
|
||||
|
||||
GLPushBuffer *CreatePushBuffer(int frame, GLuint target, size_t size) {
|
||||
GLPushBuffer *push = new GLPushBuffer(this, target, size);
|
||||
RegisterPushBuffer(frame, push);
|
||||
return push;
|
||||
}
|
||||
|
||||
void DeleteShader(GLRShader *shader) {
|
||||
deleter_.shaders.push_back(shader);
|
||||
}
|
||||
@@ -345,6 +479,17 @@ public:
|
||||
void DeleteFramebuffer(GLRFramebuffer *framebuffer) {
|
||||
deleter_.framebuffers.push_back(framebuffer);
|
||||
}
|
||||
void DeletePushBuffer(GLPushBuffer *pushbuffer) {
|
||||
deleter_.pushBuffers.push_back(pushbuffer);
|
||||
}
|
||||
|
||||
void BeginPushBuffer(GLPushBuffer *pushbuffer) {
|
||||
pushbuffer->Begin();
|
||||
}
|
||||
|
||||
void EndPushBuffer(GLPushBuffer *pushbuffer) {
|
||||
pushbuffer->End();
|
||||
}
|
||||
|
||||
void BindFramebufferAsRenderTarget(GLRFramebuffer *fb, GLRRenderPassAction color, GLRRenderPassAction depth, GLRRenderPassAction stencil, uint32_t clearColor, float clearDepth, uint8_t clearStencil);
|
||||
void BindFramebufferAsTexture(GLRFramebuffer *fb, int binding, int aspectBit, int attachment);
|
||||
@@ -679,16 +824,16 @@ public:
|
||||
queueRunner_.Resize(width, height);
|
||||
}
|
||||
|
||||
// When using legacy functionality for push buffers (glBufferData), we need to flush them
|
||||
// before actually making the glDraw* calls. It's best if the render manager handles that.
|
||||
void RegisterPushBuffer(int frame, GLPushBuffer *buffer) {
|
||||
frameData_[frame].activePushBuffers.insert(buffer);
|
||||
}
|
||||
|
||||
void UnregisterPushBuffer(int frame, GLPushBuffer *buffer) {
|
||||
auto iter = frameData_[frame].activePushBuffers.find(buffer);
|
||||
_assert_(iter != frameData_[frame].activePushBuffers.end());
|
||||
frameData_[frame].activePushBuffers.erase(iter);
|
||||
void UnregisterPushBuffer(GLPushBuffer *buffer) {
|
||||
bool found = false;
|
||||
for (int i = 0; i < MAX_INFLIGHT_FRAMES; i++) {
|
||||
auto iter = frameData_[i].activePushBuffers.find(buffer);
|
||||
if (iter != frameData_[i].activePushBuffers.end()) {
|
||||
frameData_[i].activePushBuffers.erase(iter);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
assert(found);
|
||||
}
|
||||
|
||||
void SetSwapFunction(std::function<void()> swapFunction) {
|
||||
@@ -726,6 +871,12 @@ private:
|
||||
void FlushSync();
|
||||
void EndSyncFrame(int frame);
|
||||
|
||||
// When using legacy functionality for push buffers (glBufferData), we need to flush them
|
||||
// before actually making the glDraw* calls. It's best if the render manager handles that.
|
||||
void RegisterPushBuffer(int frame, GLPushBuffer *buffer) {
|
||||
frameData_[frame].activePushBuffers.insert(buffer);
|
||||
}
|
||||
|
||||
// Per-frame data, round-robin so we can overlap submission with execution of the previous frame.
|
||||
struct FrameData {
|
||||
std::mutex push_mutex;
|
||||
@@ -788,122 +939,3 @@ private:
|
||||
int targetWidth_ = 0;
|
||||
int targetHeight_ = 0;
|
||||
};
|
||||
|
||||
// Similar to VulkanPushBuffer but is currently less efficient - it collects all the data in
|
||||
// RAM then does a big memcpy/buffer upload at the end of the frame. This is at least a lot
|
||||
// faster than the hundreds of buffer uploads or memory array buffers we used before.
|
||||
// On modern GL we could avoid the copy using glBufferStorage but not sure it's worth the
|
||||
// trouble.
|
||||
class GLPushBuffer {
|
||||
public:
|
||||
struct BufInfo {
|
||||
GLRBuffer *buffer = nullptr;
|
||||
uint8_t *localMemory = nullptr;
|
||||
uint8_t *deviceMemory = nullptr;
|
||||
size_t flushOffset = 0;
|
||||
};
|
||||
|
||||
public:
|
||||
GLPushBuffer(GLRenderManager *render, GLuint target, size_t size);
|
||||
~GLPushBuffer();
|
||||
|
||||
void Destroy();
|
||||
|
||||
void Reset() { offset_ = 0; }
|
||||
|
||||
// Needs context in case of defragment.
|
||||
void Begin() {
|
||||
buf_ = 0;
|
||||
offset_ = 0;
|
||||
// Note: we must defrag because some buffers may be smaller than size_.
|
||||
Defragment();
|
||||
Map();
|
||||
assert(writePtr_);
|
||||
}
|
||||
|
||||
void BeginNoReset() {
|
||||
Map();
|
||||
}
|
||||
|
||||
void End() {
|
||||
Unmap();
|
||||
}
|
||||
|
||||
void Map();
|
||||
void Unmap();
|
||||
|
||||
// When using the returned memory, make sure to bind the returned vkbuf.
|
||||
// This will later allow for handling overflow correctly.
|
||||
size_t Allocate(size_t numBytes, GLRBuffer **vkbuf) {
|
||||
size_t out = offset_;
|
||||
if (offset_ + ((numBytes + 3) & ~3) >= size_) {
|
||||
NextBuffer(numBytes);
|
||||
out = offset_;
|
||||
offset_ += (numBytes + 3) & ~3;
|
||||
} else {
|
||||
offset_ += (numBytes + 3) & ~3; // Round up to 4 bytes.
|
||||
}
|
||||
*vkbuf = buffers_[buf_].buffer;
|
||||
return out;
|
||||
}
|
||||
|
||||
// Returns the offset that should be used when binding this buffer to get this data.
|
||||
size_t Push(const void *data, size_t size, GLRBuffer **vkbuf) {
|
||||
assert(writePtr_);
|
||||
size_t off = Allocate(size, vkbuf);
|
||||
memcpy(writePtr_ + off, data, size);
|
||||
return off;
|
||||
}
|
||||
|
||||
uint32_t PushAligned(const void *data, size_t size, int align, GLRBuffer **vkbuf) {
|
||||
assert(writePtr_);
|
||||
offset_ = (offset_ + align - 1) & ~(align - 1);
|
||||
size_t off = Allocate(size, vkbuf);
|
||||
memcpy(writePtr_ + off, data, size);
|
||||
return (uint32_t)off;
|
||||
}
|
||||
|
||||
size_t GetOffset() const {
|
||||
return offset_;
|
||||
}
|
||||
|
||||
// "Zero-copy" variant - you can write the data directly as you compute it.
|
||||
// Recommended.
|
||||
void *Push(size_t size, uint32_t *bindOffset, GLRBuffer **vkbuf) {
|
||||
assert(writePtr_);
|
||||
size_t off = Allocate(size, vkbuf);
|
||||
*bindOffset = (uint32_t)off;
|
||||
return writePtr_ + off;
|
||||
}
|
||||
void *PushAligned(size_t size, uint32_t *bindOffset, GLRBuffer **vkbuf, int align) {
|
||||
assert(writePtr_);
|
||||
offset_ = (offset_ + align - 1) & ~(align - 1);
|
||||
size_t off = Allocate(size, vkbuf);
|
||||
*bindOffset = (uint32_t)off;
|
||||
return writePtr_ + off;
|
||||
}
|
||||
|
||||
size_t GetTotalSize() const;
|
||||
|
||||
void Flush();
|
||||
|
||||
protected:
|
||||
void MapDevice(GLBufferStrategy strategy);
|
||||
void UnmapDevice();
|
||||
|
||||
private:
|
||||
bool AddBuffer();
|
||||
void NextBuffer(size_t minSize);
|
||||
void Defragment();
|
||||
|
||||
GLRenderManager *render_;
|
||||
std::vector<BufInfo> buffers_;
|
||||
size_t buf_ = 0;
|
||||
size_t offset_ = 0;
|
||||
size_t size_ = 0;
|
||||
uint8_t *writePtr_ = nullptr;
|
||||
GLuint target_;
|
||||
GLBufferStrategy strategy_ = GLBufferStrategy::SUBDATA;
|
||||
|
||||
friend class GLRenderManager;
|
||||
};
|
||||
@@ -523,17 +523,14 @@ OpenGLContext::OpenGLContext() {
|
||||
break;
|
||||
}
|
||||
for (int i = 0; i < GLRenderManager::MAX_INFLIGHT_FRAMES; i++) {
|
||||
frameData_[i].push = new GLPushBuffer(&renderManager_, GL_ARRAY_BUFFER, 64 * 1024);
|
||||
renderManager_.RegisterPushBuffer(i, frameData_[i].push);
|
||||
frameData_[i].push = renderManager_.CreatePushBuffer(i, GL_ARRAY_BUFFER, 64 * 1024);
|
||||
}
|
||||
}
|
||||
|
||||
OpenGLContext::~OpenGLContext() {
|
||||
DestroyPresets();
|
||||
for (int i = 0; i < GLRenderManager::MAX_INFLIGHT_FRAMES; i++) {
|
||||
renderManager_.UnregisterPushBuffer(i, frameData_[i].push);
|
||||
frameData_[i].push->Destroy();
|
||||
delete frameData_[i].push;
|
||||
renderManager_.DeletePushBuffer(frameData_[i].push);
|
||||
}
|
||||
boundSamplers_.clear();
|
||||
}
|
||||
@@ -541,12 +538,12 @@ OpenGLContext::~OpenGLContext() {
|
||||
void OpenGLContext::BeginFrame() {
|
||||
renderManager_.BeginFrame();
|
||||
FrameData &frameData = frameData_[renderManager_.GetCurFrame()];
|
||||
frameData.push->Begin();
|
||||
renderManager_.BeginPushBuffer(frameData.push);
|
||||
}
|
||||
|
||||
void OpenGLContext::EndFrame() {
|
||||
FrameData &frameData = frameData_[renderManager_.GetCurFrame()];
|
||||
frameData.push->End(); // upload the data!
|
||||
renderManager_.EndPushBuffer(frameData.push); // upload the data!
|
||||
renderManager_.Finish();
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user