Skip to content
......@@ -54,7 +54,16 @@ bool RenderDispatcher::dispatchNoRecurse(const std::unordered_set<GUIElement*>&
instances.clear();
instancesClear.clear();
for (auto& element : elements) {
if (!element->dirty || !element->updateOverlaps) continue;
Rect bounds = element->getBounds(false);
for (auto& elementClip : elements) {
if (elementClip->dirty) continue;
if (!elementClip->getBounds(false).overlaps(bounds)) continue;
elementClip->dirty = 1;
//SDL_Log("RenderDispatcher::dispatchNoRecurse collide %i %i", (int)&element, (int)&elementClip);
}
}
for (auto& element : elements) {
if (!element->dirty) continue;
size_t appendSize = element->getInstanceCount();
......@@ -66,9 +75,12 @@ bool RenderDispatcher::dispatchNoRecurse(const std::unordered_set<GUIElement*>&
size_t appendSize = element->getInstanceCount();
Span<Instance> newInstances = instances.appendGetEnd(appendSize);
element->populateInstances(newInstances);
if (element->updateOverlaps)
for (size_t i = 0; i < appendSize; i++)
newInstances[i].depth += element->depth;
element->dirty -= 1;
instancesClear.append(InstanceDataFlat{
element->getBounds(true), clearColor
(RectU)element->getBounds(true), clearColor
});
}
if (instances.size() == 0) return false;
......@@ -99,10 +111,10 @@ void RenderDispatcher::updateUniforms(SDL_Window* window, Vec2 offset) {
0, 0, -1, 0,
-1 - offset.x * wI * 2.0f, 1 + offset.y * hI * 2.0f, 0, 1
};
commonUniforms.projection->set(ortho);
projection.set(ortho);
timeMS = SDL_GetTicks();
commonUniforms.time->value = (uint32_t)timeMS;
time.value = (uint32_t)timeMS;
}
bool RenderDispatcher::handleMouse(const std::unordered_set<GUIElement*>& elements, MouseButton button, MouseInput input, Vec2 pos, uint32_t index) {
pos = Vec2(offset.x + pos.x, offset.y + pos.y);
......@@ -156,23 +168,27 @@ bool RenderDispatcher::handleWheel(const std::unordered_set<GUIElement*>& elemen
void orderObjects(Instance* arr, size_t n) {
for (size_t i = 0; i < n; i++)
if (arr->skip) arr->depth = -1;
// Step 1: Sort by depth only (stable sort to keep original order in equal depths)
// We do stable sort so relative order inside equal depth remains for grouping later
for (size_t i = 0; i < n; i++) if (arr->skip) arr->depth = -1;
//Sort by depth
std::stable_sort(arr, arr + n, [](const Instance& a, const Instance& b) {
return a.depth < b.depth;
});
// Step 2: Within equal depth groups, group by shader (stable sort again)
// we could optimise by switching the sort order each sort if the last sorts shader doesnt equal the first to ensure blocks of shaders remain connected
size_t start = 0;
for (size_t i = 1; i <= n; i++) {
if (i == n || arr[i].depth != arr[start].depth) {
// Sort by shader inside [start, i)
std::stable_sort(arr + start, arr + i, [](const Instance& a, const Instance& b) {
//Sort by shaders
for (size_t iDepthStart = 0, iDepthEnd = 1; iDepthEnd <= n; iDepthEnd++) {
if (iDepthEnd == n || arr[iDepthEnd].depth != arr[iDepthStart].depth) {
std::stable_sort(arr + iDepthStart, arr + iDepthEnd, [](const Instance& a, const Instance& b) {
return a.shader < b.shader;
});
start = i;
//Sort by uniforms
for (size_t iUniformStart = iDepthStart, iUniformEnd = iDepthStart + 1; iUniformEnd < iDepthEnd; iUniformEnd++) {
if (iUniformEnd == iDepthEnd || arr[iUniformEnd].uniforms.data != arr[iUniformStart].uniforms.data) {
std::stable_sort(arr + iUniformStart, arr + iUniformEnd, [](const Instance& a, const Instance& b) {
return a.uniforms.data < b.uniforms.data;
});
iUniformStart = iUniformEnd;
}
}
iDepthStart = iDepthEnd;
}
}
}
......@@ -180,15 +196,21 @@ void RenderInstances(ObjectBuffer* obj, Span<Instance> data) {
orderObjects(data.data, data.size());
size_t matchStart = 0;
AShader* matchShader = data[0].shader;
Span<Uniform*> matchUniforms = data[0].uniforms;
size_t maxBatch = matchShader->getInstanceSize();
for (size_t i = 1; i < data.size(); i++) {
size_t batchSize = i - matchStart;
if (data[i].depth == -1) return;
if (data[i].shader == matchShader && batchSize != maxBatch) continue;
Instance& inst = data[i];
if (inst.depth == -1) return;
if (batchSize != maxBatch && inst.shader == matchShader &&
inst.uniforms.data == matchUniforms.data) continue;
assert(matchShader);
if (matchUniforms.size() != 0)
matchShader->setUniforms(matchUniforms);
matchShader->renderInstances(obj, Span<Instance>(data.data + matchStart, i - matchStart));
matchStart = i;
matchShader = data[i].shader;
matchShader = inst.shader;
matchUniforms = inst.uniforms;
maxBatch = matchShader->getInstanceSize();
}
if (data.size() - matchStart == 0) return;
......
......@@ -78,10 +78,10 @@ GLuint LoadShaderProgramFile(const char* vsFile, const char* fsFile) {
return LoadShaderProgramMemory(vsSrc.get(), fsSrc.get());
}
#include <cassert>
void CheckGLError(const char* function) {
GLenum err;
while ((err = glGetError()) != GL_NO_ERROR) {
std::cerr << "OpenGL Error in " << function << ": [" << err << "] " << (err) << std::endl;//glewGetErrorString
}
}
\ No newline at end of file
......@@ -6,14 +6,44 @@ void AShader::setProgram(GLuint program) {
onSetProgram();
}
ObjectBuffer::Shader ObjectBuffer::getShaderBuffers(AShader* shader) {
void ObjectBuffer::Shader::resizeBuffer(AShader* shader, size_t newBufferSize) {
size_t instanceSize = shader->getInstanceSize();
if (newBufferSize == bufferSize) return;
glBindBuffer(GL_ARRAY_BUFFER, instanceVBO);
glBufferData(GL_ARRAY_BUFFER, instanceSize * newBufferSize, NULL, GL_STREAM_DRAW);
bufferSize = newBufferSize;
}
size_t nextPow2(size_t v) {
if (v == 0) return 1;
v--;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
#if SIZE_MAX > 0xffffffff
v |= v >> 32;
#endif
v++;
return v;
}
void ObjectBuffer::Shader::upscaleBuffer(AShader* shader, size_t minBufferSize) {
size_t instanceSize = shader->getInstanceSize();
if (minBufferSize <= bufferSize) return;
size_t newBufferSize = nextPow2(instanceSize * minBufferSize);
glBindBuffer(GL_ARRAY_BUFFER, instanceVBO);
glBufferData(GL_ARRAY_BUFFER, newBufferSize, NULL, GL_STREAM_DRAW);
bufferSize = newBufferSize;
}
ObjectBuffer::Shader* ObjectBuffer::getShaderBuffers(AShader* shader) {
if (lastFound.first == shader->program)
return lastFound.second;
auto found = shaders.find(shader->program);
if (found != shaders.end()) {
lastFound = std::pair<GLuint, Shader>(shader->program, found->second);
return found->second;
lastFound = std::pair<GLuint, Shader*>(shader->program, &found->second);
return &found->second;
}
Shader newShader;
......@@ -23,11 +53,10 @@ ObjectBuffer::Shader ObjectBuffer::getShaderBuffers(AShader* shader) {
constructVBOAttribs(VBO);
size_t instanceMaxCount = shader->getInstanceMaxCount();
size_t instanceSize = shader->getInstanceSize();
glGenBuffers(1, &newShader.instanceVBO);
glBindBuffer(GL_ARRAY_BUFFER, newShader.instanceVBO);
glBufferData(GL_ARRAY_BUFFER, instanceSize * instanceMaxCount, NULL, GL_STREAM_DRAW);
glBufferData(GL_ARRAY_BUFFER, 0, NULL, GL_STREAM_DRAW);
for (auto& field : shader->fields) {
if (field.varying)
......@@ -40,9 +69,9 @@ ObjectBuffer::Shader ObjectBuffer::getShaderBuffers(AShader* shader) {
glBindVertexArray(0);
lastFound = std::pair<GLuint, Shader>(shader->program, newShader);
shaders.insert(std::pair<GLuint, Shader>(shader->program, newShader));
return newShader;
auto newInsert = shaders.insert(std::pair<GLuint, Shader>(shader->program, newShader));
lastFound = std::pair<GLuint, Shader*>(shader->program, &newInsert.first->second);
return &newInsert.first->second;
}
......
......@@ -17,9 +17,9 @@ void SliderElement::populateInstances(Span<Instance> buffer) {
Distance limitInverse = (Distance)(position.w * (limitValue / maxValue));
Distance stepPixel = (Distance)(position.w / maxValue);
background.rect = position;
background.rect = (RectU)position;
background.color = theme.background[2];
backgroundLimit.rect = position;
backgroundLimit.rect = (RectU)position;
backgroundLimit.color = theme.background[1];
backgroundLimit.rect.x += limitInverse;
backgroundLimit.rect.w -= limitInverse;
......@@ -27,19 +27,19 @@ void SliderElement::populateInstances(Span<Instance> buffer) {
if (fillValue) {
instances[instanceBackgrouncValue].skip = false;
backgroundValue.color = theme.statusArr[inputState];
backgroundValue.rect = position;
backgroundValue.rect = (RectU)position;
backgroundValue.rect.w = (Distance)(position.w * (value / maxValue));
} else
instances[instanceBackgrouncValue].skip = true;
lines.rect = position;
lines.rect = (RectU)position;
lines.color = theme.background[0];
lines.color.a = 128;
lines.distance = Vec2(stepPixel, 0);
lines.size = 1;
lines.offset = 0;
handle.rect = calculateHandleBounds();
handle.rect = (RectU)calculateHandleBounds();
handle.color = theme.interactableArr[inputState];
buffer.copyFrom(instances);
......
......@@ -20,10 +20,10 @@ void SliderPrecisionElement::populateInstances(Span<Instance> buffer) {
Distance stepPixel = (Distance)(position.w / linkedSlider->maxValue * scale);
float valueFract = linkedSlider->value - std::floorf(linkedSlider->value);
background.rect = position;
background.rect = (RectU)position;
background.color = theme.background[2];
backgroundLimit.rect = position;
backgroundLimit.rect = (RectU)position;
backgroundLimit.color = theme.background[1];
float limitRelative = linkedSlider->limitValue - linkedSlider->value;
Position limitRelativePixel = (position.w / 2) - (Position)(limitRelative * position.w * scale / linkedSlider->maxValue);
......@@ -35,19 +35,19 @@ void SliderPrecisionElement::populateInstances(Span<Instance> buffer) {
if (linkedSlider->fillValue) {
instances[instanceBackgrouncValue].skip = false;
backgroundValue.color = theme.statusArr[inputState];
backgroundValue.rect = position;
backgroundValue.rect = (RectU)position;
backgroundValue.rect.w = background.rect.w / 2;
} else
instances[instanceBackgrouncValue].skip = true;
lines.rect = position;
lines.rect = (RectU)position;
lines.color = theme.background[0];
lines.color.a = 128;
lines.distance = Vec2(stepPixel, 0);
lines.size = 1;
lines.offset = (uint16_t)(valueFract * stepPixel);
handle.rect = calculateHandleBounds();
handle.rect = (RectU)calculateHandleBounds();
handle.color = theme.interactableArr[inputState];
buffer.copyFrom(instances);
......
......@@ -23,30 +23,30 @@ void SliderRangeElement::populateInstances(Span<Instance> buffer) {
Distance limitInverse = (Distance)(position.w * (linkedSlider->limitValue / linkedSlider->maxValue));
Distance stepPixel = (Distance)(position.w / linkedSlider->maxValue);
background.rect = position;
background.rect = (RectU)position;
background.color = theme.background[2];
backgroundLimit.rect = position;
backgroundLimit.rect = (RectU)position;
backgroundLimit.color = theme.background[1];
backgroundLimit.rect.x += limitInverse;
backgroundLimit.rect.w -= limitInverse;
backgroundValue.color = theme.statusArr[inputStateMin > inputStateMax ? inputStateMin : inputStateMax];
backgroundValue.rect = position;
backgroundValue.rect = (RectU)position;
Distance valueMinPix = (Distance)(position.w * (valueMin / linkedSlider->maxValue));
Distance valueMaxPix = (Distance)(position.w * (valueMax / linkedSlider->maxValue));
backgroundValue.rect.x = position.x + valueMinPix;
backgroundValue.rect.w = valueMaxPix - valueMinPix;
lines.rect = position;
lines.rect = (RectU)position;
lines.color = theme.background[0];
lines.color.a = 128;
lines.distance = Vec2(stepPixel, 0);
lines.size = 1;
lines.offset = 0;
handleMin.rect = calculateHandleBounds(false);
handleMin.rect = (RectU)calculateHandleBounds(false);
handleMin.color = theme.interactableArr[inputStateMin];
handleMax.rect = calculateHandleBounds(true);
handleMax.rect = (RectU)calculateHandleBounds(true);
handleMax.color = theme.interactableArr[inputStateMax];
buffer.copyFrom(instances);
......@@ -130,7 +130,7 @@ bool SliderRangeElement::handleMouse(MouseButton button, MouseInput state, Vec2
//SDL_Log("Mouse x %i y %i. State %i, inputState %i", pos.x, pos.y, (int)state, (int)inputState);
if (!linkedSlider) return false;
dirty = DIRTY;
bool retMin = handleMouseMin(button, state, pos, index);
bool retMin = inputStateMax == ACTIVE ? false : handleMouseMin(button, state, pos, index);
bool retMax = handleMouseMax(button, state, pos, index);
return retMin || retMax;
}
......
......@@ -15,6 +15,7 @@ void TextElement::populateInstances(Span<Instance> buffer) {
assert(font);
size_t textSize = text.size();
if (textChange || atlas->getLastUpdateID() != atlasUpdateID) {
dirty = DIRTY;
if (atlas == nullptr || atlas->getFontSize() != fontSize)
atlas = font->getAtlas(fontSize);
......@@ -22,15 +23,6 @@ void TextElement::populateInstances(Span<Instance> buffer) {
instances.resize(textSize);
atlas->generateInstances(position, text, Span<Instance>(instances));
SDL_Log("TextElement::populateInstances [%i]:", (int)textSize);
for (Instance& instance : instances) {
InstanceDataGlyph* data = instance.data.pointerAs<InstanceDataGlyph>();
std::cout << " rect ";
data->rect.debugPrint();
//std::cout << ", \tatlas ";
//data->atlas.debugPrint();
std::cout << std::endl;
}
atlasUpdateID = atlas->getLastUpdateID();
}
assert(instances.size() == textSize);
......
......@@ -2,74 +2,8 @@
#include <cstring>
#include <cstdint>
#include <cassert>
#include <iostream>
Rect Rect::flipAxies() const {
return Rect{x, y, h, w};
}
bool Rect::inside(Vec2 point) const {
return x <= point.x && x+w >= point.x
&& y <= point.y && y+h >= point.y;
}
Rect Rect::clip(const Rect& that) const {
Rect newRect = *this;
int32_t diff;
diff = that.x - newRect.x;
if (diff > newRect.w) return Rect();
if (diff > 0) {
newRect.x += diff;
newRect.w -= diff;
}
diff = (newRect.x + newRect.w) - (that.x + that.w);
if (diff > newRect.w) return Rect();
if (diff > 0) {
newRect.w -= diff;
}
diff = that.y - newRect.y;
if (diff > newRect.h) return Rect();
if (diff > 0) {
newRect.y += diff;
newRect.h -= diff;
}
diff = (newRect.y + newRect.h) - (that.y + that.h);
if (diff > newRect.h) return Rect();
if (diff > 0) {
newRect.y -= diff;
}
return newRect;
}
Rect Rect::combine(const Rect& that) const {
Rect newRect = *this;
int32_t diff;
diff = that.x - newRect.x;
if (diff > 0) {
newRect.x -= diff;
newRect.w += diff;
}
diff = (newRect.x + newRect.w) - (that.x + that.w);
if (diff > 0) {
newRect.w += diff;
}
diff = that.y - newRect.y;
if (diff > 0) {
newRect.y -= diff;
newRect.h += diff;
}
diff = (newRect.y + newRect.h) - (that.y + that.h);
if (diff > 0) {
newRect.y += diff;
}
return newRect;
}
void Rect::debugPrint() const {
std::cout << "Rect(" << x << ", " << y << ", " << w << ", " << h << ")";
}
Vec2 Vec2::flipAxies() {
return Vec2{y, x};
......
......@@ -68,7 +68,7 @@ constexpr const GLenum textureLocs[] = {
GL_TEXTURE4, GL_TEXTURE5, GL_TEXTURE6, GL_TEXTURE7
};
void UniformTexture8::applyLoc(GLint loc) {
assert(tex != GL_NONE);
if (tex == GL_NONE) return;
glActiveTexture(textureLocs[index]);
glBindTexture(GL_TEXTURE_2D, tex);
glUniform1i(loc, index);
......@@ -77,19 +77,21 @@ void UniformTexture8::set(const std::vector<uint8_t>& data, uint32_t width, uint
assert(data.size() == (width * height));
if (tex == GL_NONE) {
glGenTextures(1, &tex);
CheckGLError("glBindTexture");
CheckGLError("glGenTextures");
glBindTexture(GL_TEXTURE_2D, tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
glBindTexture(GL_TEXTURE_2D, tex);
CheckGLError("glBindTexture");
if (width == this->width && height == this->height)
if (width == this->width && height == this->height) {
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RED, GL_UNSIGNED_BYTE, data.data());
else {
this->width = width;
this->height = height;
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width, height, 0, GL_RED, GL_UNSIGNED_BYTE, data.data());
CheckGLError("glTexSubImage2D");
} else {
glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, width, height, 0, GL_RED, GL_UNSIGNED_BYTE, data.data());
CheckGLError("glTexImage2D");
}
}