#include "graphics/tinygl/zrect.h" #include "graphics/tinygl/zgl.h" #include "graphics/tinygl/gl.h" namespace TinyGL { void glIssueDrawCall(Graphics::DrawCall *drawCall) { TinyGL::GLContext *c = TinyGL::gl_get_context(); c->_drawCallsQueue.push_back(drawCall); } } // end of namespace TinyGL void tglDrawRectangle(Common::Rect rect, int r, int g, int b) { TinyGL::GLContext *c = TinyGL::gl_get_context(); if (rect.left < 0) rect.left = 0; if (rect.right >= c->fb->xsize) rect.right = c->fb->xsize - 1; if (rect.top < 0) rect.top = 0; if (rect.bottom >= c->fb->ysize) rect.bottom = c->fb->ysize - 1; for(int x = rect.left; x < rect.right; x++) { c->fb->writePixel(rect.top * c->fb->xsize + x, 255, r, g, b); c->fb->writePixel(rect.bottom * c->fb->xsize + x, 255, r, g, b); } for(int y = rect.top; y < rect.bottom; y++) { c->fb->writePixel(y * c->fb->xsize + rect.left, 255, r, g, b); c->fb->writePixel(y * c->fb->xsize + rect.right, 255, r, g, b); } } struct DirtyRectangle { Common::Rect rectangle; int r, g, b; DirtyRectangle() { } DirtyRectangle(Common::Rect rect, int r, int g, int b) { this->rectangle = rect; this->r = r; this->g = g; this->b = b; } }; void tglPresentBuffer() { TinyGL::GLContext *c = TinyGL::gl_get_context(); Common::List rectangles; Common::List::const_iterator itFrame = c->_drawCallsQueue.begin(); Common::List::const_iterator endFrame = c->_drawCallsQueue.end(); Common::List::const_iterator itPrevFrame = c->_previousFrameDrawCallsQueue.begin(); Common::List::const_iterator endPrevFrame = c->_previousFrameDrawCallsQueue.end(); // Compare draw calls. bool hasPrevFrame = itPrevFrame != endPrevFrame; while (itFrame != endFrame) { const Graphics::DrawCall ¤tCall = **itFrame; const Graphics::DrawCall &previousCall = **itPrevFrame; if (hasPrevFrame && !(currentCall == previousCall) ) { rectangles.push_back(DirtyRectangle(currentCall.getDirtyRegion(), 255, 0, 0)); } itFrame ++; itPrevFrame ++; if (itPrevFrame == endPrevFrame) { hasPrevFrame = false; } } // Merge dirty rects. bool restartMerge; do { restartMerge = false; Common::List::iterator it1 = rectangles.begin(); while (it1 != rectangles.end() && restartMerge == false) { Common::List::iterator it2 = rectangles.begin(); while (it2 != rectangles.end()) { if (it1 != it2) { if ((*it1).rectangle.contains((*it2).rectangle)) { (*it1).r = (*it1).g = (*it1).b = 255; it2 = rectangles.erase(it2); restartMerge = true; break; } else if ((*it1).rectangle.intersects((*it2).rectangle)) { Common::Rect mergedArea = (*it1).rectangle; mergedArea.extend((*it2).rectangle); it1 = rectangles.erase(it1); it2 = rectangles.erase(it2); rectangles.push_back(DirtyRectangle(mergedArea, 0, 0, 255)); restartMerge = true; break; } } it2++; } it1++; } } while(restartMerge); // Execute draw calls. Common::List::const_iterator it = c->_drawCallsQueue.begin(); Common::List::const_iterator end = c->_drawCallsQueue.end(); while (it != end) { (*it)->execute(it._node->_next == end._node); it++; } // Dispose not necessary draw calls. it = c->_previousFrameDrawCallsQueue.begin(); end = c->_previousFrameDrawCallsQueue.end(); while (it != end) { delete *it; it++; } c->_previousFrameDrawCallsQueue = c->_drawCallsQueue; c->_drawCallsQueue.clear(); // Draw debug rectangles. // Note: white rectangles are rectangle that contained other rectangles // blue rectangles are rectangle merged from other rectangles // red rectangles are original dirty rects Common::List::const_iterator itRectangles = rectangles.begin(); bool blendingEnabled = c->fb->isBlendingEnabled(); bool alphaTestEnabled = c->fb->isAplhaTestEnabled(); c->fb->enableBlending(false); c->fb->enableAlphaTest(false); itRectangles = rectangles.begin(); while (itRectangles != rectangles.end()) { tglDrawRectangle((*itRectangles).rectangle, (*itRectangles).r, (*itRectangles).g, (*itRectangles).b); itRectangles++; } c->fb->enableBlending(blendingEnabled); c->fb->enableAlphaTest(alphaTestEnabled); // Dispose textures and resources. bool allDisposed; do { allDisposed = true; TinyGL::GLTexture *t = c->shared_state.texture_hash_table[0]; while (t) { if (t->disposed) { TinyGL::free_texture(c, t->handle); allDisposed = false; break; } t = t->next; } } while (allDisposed == false); Graphics::Internal::tglCleanupImages(); } namespace Graphics { DrawCall::DrawCall(DrawCallType type) : _type(type) { } bool DrawCall::operator==(const DrawCall &other) const { if (_type == other._type) { switch (_type) { case Graphics::DrawCall_Rasterization: return *(RasterizationDrawCall *)this == (const RasterizationDrawCall &)other; break; case Graphics::DrawCall_Blitting: return *(BlittingDrawCall *)this == (const BlittingDrawCall &)other; break; case Graphics::DrawCall_Clear: return *(ClearBufferDrawCall *)this == (const ClearBufferDrawCall &)other; break; default: break; } } else { return false; } } RasterizationDrawCall::RasterizationDrawCall() : DrawCall(DrawCall_Rasterization) { TinyGL::GLContext *c = TinyGL::gl_get_context(); _vertexCount = c->vertex_cnt; _vertex = new TinyGL::GLVertex[_vertexCount]; _drawTriangleFront = c->draw_triangle_front; _drawTriangleBack = c->draw_triangle_back; memcpy(_vertex, c->vertex, sizeof(TinyGL::GLVertex) * _vertexCount); _state = loadState(); _dirtyRegion = computeDirtyRegion(); } Common::Rect RasterizationDrawCall::computeDirtyRegion() { Common::Rect region; region.left = 9999; region.top = 9999; for (int i = 0; i < _vertexCount; i++) { if (_vertex[i].zp.x < region.left) { region.left = _vertex[i].zp.x; } if (_vertex[i].zp.y < region.top) { region.top = _vertex[i].zp.y; } if (_vertex[i].zp.x > region.right) { region.right = _vertex[i].zp.x; } if (_vertex[i].zp.y > region.bottom) { region.bottom = _vertex[i].zp.y; } } return region; } void RasterizationDrawCall::execute(bool restoreState) const { TinyGL::GLContext *c = TinyGL::gl_get_context(); RasterizationDrawCall::RasterizationState backupState; if (restoreState) { backupState = loadState(); } applyState(_state); TinyGL::GLVertex *prevVertex = c->vertex; int prevVertexCount = c->vertex_cnt; c->vertex = _vertex; c->vertex_cnt = _vertexCount; c->draw_triangle_front = (TinyGL::gl_draw_triangle_func)_drawTriangleFront; c->draw_triangle_back = (TinyGL::gl_draw_triangle_func)_drawTriangleBack; int n, cnt; n = c->vertex_n; cnt = c->vertex_cnt; switch (c->begin_type) { case TGL_POINTS: for(int i = 0; i < cnt; i++) { gl_draw_point(c, &c->vertex[i]); } break; case TGL_LINES: for(int i = 0; i < cnt / 2; i++) { gl_draw_line(c, &c->vertex[i * 2], &c->vertex[i * 2 + 1]); } break; case TGL_LINE_STRIP: case TGL_LINE_LOOP: for(int i = 0; i < cnt; i++) { gl_draw_line(c, &c->vertex[i], &c->vertex[i + 1]); } gl_draw_line(c, &c->vertex[0], &c->vertex[cnt - 1]); break; case TGL_TRIANGLES: for(int i = 0; i < cnt / 3; i++) { gl_draw_triangle(c, &c->vertex[i * 3], &c->vertex[i * 3 + 1], &c->vertex[i * 3 + 2]); } break; case TGL_TRIANGLE_STRIP: for(int i = 0; i < cnt; i += 2) { gl_draw_triangle(c, &c->vertex[i], &c->vertex[i + 1], &c->vertex[i + 2]); gl_draw_triangle(c, &c->vertex[i + 2], &c->vertex[i + 1], &c->vertex[i + 3]); } break; case TGL_TRIANGLE_FAN: for(int i = 1; i < cnt; i += 2) { gl_draw_triangle(c, &c->vertex[0], &c->vertex[i], &c->vertex[i + 1]); } break; case TGL_QUADS: for(int i = 0; i < cnt / 4; i++) { c->vertex[i + 2].edge_flag = 0; gl_draw_triangle(c, &c->vertex[i], &c->vertex[i + 1], &c->vertex[i + 2]); c->vertex[i + 2].edge_flag = 1; c->vertex[i + 0].edge_flag = 0; gl_draw_triangle(c, &c->vertex[i], &c->vertex[i + 2], &c->vertex[i + 3]); } break; case TGL_QUAD_STRIP: while (n >= 4) { gl_draw_triangle(c, &c->vertex[0], &c->vertex[1], &c->vertex[2]); gl_draw_triangle(c, &c->vertex[1], &c->vertex[3], &c->vertex[2]); for (int i = 0; i < 2; i++) c->vertex[i] = c->vertex[i + 2]; n -= 2; } break; case TGL_POLYGON: { int i = c->vertex_cnt; while (i >= 3) { i--; gl_draw_triangle(c, &c->vertex[i], &c->vertex[0], &c->vertex[i - 1]); } break; } default: error("glBegin: type %x not handled", c->begin_type); } c->vertex = prevVertex; c->vertex_cnt = prevVertexCount; if (restoreState) { applyState(backupState); } } RasterizationDrawCall::RasterizationState RasterizationDrawCall::loadState() const { RasterizationState state; TinyGL::GLContext *c = TinyGL::gl_get_context(); state.alphaTest = c->fb->isAplhaTestEnabled(); c->fb->getBlendingFactors(state.sfactor, state.dfactor); state.enableBlending = c->fb->isBlendingEnabled(); state.alphaFunc = c->fb->getAlphaTestFunc(); state.alphaRefValue = c->fb->getAlphaTestRefVal(); state.cullFaceEnabled = c->cull_face_enabled; state.beginType = c->begin_type; state.colorMask = c->color_mask; state.currentFrontFace = c->current_front_face; state.currentShadeModel = c->current_shade_model; state.depthTest = c->depth_test; state.polygonModeBack = c->polygon_mode_back; state.polygonModeFront = c->polygon_mode_front; state.shadowMode = c->shadow_mode; state.texture2DEnabled = c->texture_2d_enabled; state.texture = c->current_texture; state.shadowMaskBuf = c->fb->shadow_mask_buf; state.depthFunction = c->fb->getDepthFunc(); state.depthWrite = c->fb->getDepthWrite(); state.lightingEnabled = c->lighting_enabled; memcpy(state.viewportScaling, c->viewport.scale._v, sizeof(c->viewport.scale._v)); memcpy(state.viewportTranslation, c->viewport.trans._v, sizeof(c->viewport.trans._v)); memcpy(state.currentColor, c->longcurrent_color, sizeof(c->longcurrent_color)); return state; } void RasterizationDrawCall::applyState(const RasterizationDrawCall::RasterizationState &state) const { TinyGL::GLContext *c = TinyGL::gl_get_context(); c->fb->setBlendingFactors(state.sfactor, state.dfactor); c->fb->enableBlending(state.enableBlending); c->fb->enableAlphaTest(state.alphaTest); c->fb->setAlphaTestFunc(state.alphaFunc, state.alphaRefValue); c->fb->setDepthFunc(state.depthFunction); c->fb->enableDepthWrite(state.depthWrite); c->lighting_enabled = state.lightingEnabled; c->cull_face_enabled = state.cullFaceEnabled; c->begin_type = state.beginType; c->color_mask = state.colorMask; c->current_front_face = state.currentFrontFace; c->current_shade_model = state.currentShadeModel; c->depth_test = state.depthTest; c->polygon_mode_back = state.polygonModeBack; c->polygon_mode_front = state.polygonModeFront; c->shadow_mode = state.shadowMode; c->texture_2d_enabled = state.texture2DEnabled; c->current_texture = state.texture; c->fb->shadow_mask_buf = state.shadowMaskBuf; memcpy(c->viewport.scale._v, state.viewportScaling, sizeof(c->viewport.scale._v)); memcpy(c->viewport.trans._v, state.viewportTranslation, sizeof(c->viewport.trans._v)); memcpy(c->longcurrent_color, state.currentColor, sizeof(c->longcurrent_color)); } RasterizationDrawCall::~RasterizationDrawCall() { delete [] _vertex; } void RasterizationDrawCall::execute(const Common::Rect &clippingRectangle, bool restoreState) const { } const Common::Rect RasterizationDrawCall::getDirtyRegion() const { return _dirtyRegion; } bool RasterizationDrawCall::operator==(const RasterizationDrawCall &other) const { if (_vertexCount == other._vertexCount && _drawTriangleFront == other._drawTriangleFront && _drawTriangleBack == other._drawTriangleBack && _state == other._state) { for (int i = 0; i < _vertexCount; i++) { if ((_vertex[i] == other._vertex[i]) == false) { return false; } } return true; } return false; } BlittingDrawCall::BlittingDrawCall(Graphics::BlitImage *image, const BlitTransform &transform, BlittingMode blittingMode) : DrawCall(DrawCall_Blitting), _transform(transform), _mode(blittingMode), _image(image) { _blitState = loadState(); } void BlittingDrawCall::execute(bool restoreState) const { BlittingState backupState; if (restoreState) { backupState = loadState(); } applyState(_blitState); switch (_mode) { case Graphics::BlittingDrawCall::BlitMode_Regular: Graphics::Internal::tglBlit(_image, _transform); break; case Graphics::BlittingDrawCall::BlitMode_NoBlend: Graphics::Internal::tglBlitNoBlend(_image, _transform); break; case Graphics::BlittingDrawCall::BlitMode_Fast: Graphics::Internal::tglBlitFast(_image, _transform._destinationRectangle.left, _transform._destinationRectangle.top); break; case Graphics::BlittingDrawCall::BlitMode_ZBuffer: Graphics::Internal::tglBlitZBuffer(_image, _transform._destinationRectangle.left, _transform._destinationRectangle.top); break; default: break; } if (restoreState) { applyState(backupState); } } void BlittingDrawCall::execute(const Common::Rect &clippingRectangle, bool restoreState) const { } BlittingDrawCall::BlittingState BlittingDrawCall::loadState() const { BlittingState state; TinyGL::GLContext *c = TinyGL::gl_get_context(); state.alphaTest = c->fb->isAplhaTestEnabled(); c->fb->getBlendingFactors(state.sfactor, state.dfactor); state.enableBlending = c->fb->isBlendingEnabled(); state.alphaFunc = c->fb->getAlphaTestFunc(); state.alphaRefValue = c->fb->getAlphaTestRefVal(); return state; } void BlittingDrawCall::applyState(const BlittingState &state) const { TinyGL::GLContext *c = TinyGL::gl_get_context(); c->fb->setBlendingFactors(state.sfactor, state.dfactor); c->fb->enableBlending(state.enableBlending); c->fb->enableAlphaTest(state.alphaTest); c->fb->setAlphaTestFunc(state.alphaFunc, state.alphaRefValue); } const Common::Rect BlittingDrawCall::getDirtyRegion() const { int blitWidth = _transform._destinationRectangle.width(); int blitHeight = _transform._destinationRectangle.width(); if (blitWidth == 0) { if (_transform._sourceRectangle.width() != 0) { blitWidth = _transform._sourceRectangle.width(); } else { tglGetBlitImageSize(_image, blitWidth, blitHeight); } } if (blitHeight == 0) { if (_transform._sourceRectangle.height() != 0) { blitHeight = _transform._sourceRectangle.height(); } else { tglGetBlitImageSize(_image, blitWidth, blitHeight); } } return Common::Rect(_transform._destinationRectangle.left, _transform._destinationRectangle.top, _transform._destinationRectangle.left + blitWidth, _transform._destinationRectangle.top + blitHeight); } bool BlittingDrawCall::operator==(const BlittingDrawCall &other) const { return _mode == other._mode && _image == other._image && _transform == other._transform && _blitState == other._blitState; } ClearBufferDrawCall::ClearBufferDrawCall(bool clearZBuffer, int zValue, bool clearColorBuffer, int rValue, int gValue, int bValue) : clearZBuffer(clearZBuffer), clearColorBuffer(clearColorBuffer), zValue(zValue), rValue(rValue), gValue(gValue), bValue(bValue), DrawCall(DrawCall_Clear) { } void ClearBufferDrawCall::execute(bool restoreState) const { TinyGL::GLContext *c = TinyGL::gl_get_context(); c->fb->clear(clearZBuffer, zValue, clearColorBuffer, rValue, gValue, bValue); } void ClearBufferDrawCall::execute(const Common::Rect &clippingRectangle, bool restoreState) const { } const Common::Rect ClearBufferDrawCall::getDirtyRegion() const { TinyGL::GLContext *c = TinyGL::gl_get_context(); return Common::Rect(0, 0, c->fb->xsize, c->fb->ysize); } bool ClearBufferDrawCall::operator==(const ClearBufferDrawCall &other) const { return clearZBuffer == other.clearZBuffer && clearColorBuffer == other.clearColorBuffer && rValue == other.rValue && gValue == other.gValue && bValue == other.bValue && zValue == other.zValue; } } // end of namespace Graphics