1 //
2 // Copyright (c) 2008-2017 the Urho3D project.
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a copy
5 // of this software and associated documentation files (the "Software"), to deal
6 // in the Software without restriction, including without limitation the rights
7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 // copies of the Software, and to permit persons to whom the Software is
9 // furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 // THE SOFTWARE.
21 //
22 
23 #include "../../Precompiled.h"
24 
25 #include "../../Core/Context.h"
26 #include "../../Core/Mutex.h"
27 #include "../../Core/ProcessUtils.h"
28 #include "../../Core/Profiler.h"
29 #include "../../Graphics/ConstantBuffer.h"
30 #include "../../Graphics/Graphics.h"
31 #include "../../Graphics/GraphicsEvents.h"
32 #include "../../Graphics/GraphicsImpl.h"
33 #include "../../Graphics/IndexBuffer.h"
34 #include "../../Graphics/RenderSurface.h"
35 #include "../../Graphics/Shader.h"
36 #include "../../Graphics/ShaderPrecache.h"
37 #include "../../Graphics/ShaderProgram.h"
38 #include "../../Graphics/ShaderVariation.h"
39 #include "../../Graphics/Texture2D.h"
40 #include "../../Graphics/TextureCube.h"
41 #include "../../Graphics/VertexBuffer.h"
42 #include "../../IO/File.h"
43 #include "../../IO/Log.h"
44 #include "../../Resource/ResourceCache.h"
45 
46 #include <SDL/SDL.h>
47 
48 #include "../../DebugNew.h"
49 
50 #ifdef GL_ES_VERSION_2_0
51 #define GL_DEPTH_COMPONENT24 GL_DEPTH_COMPONENT24_OES
52 #define glClearDepth glClearDepthf
53 #endif
54 
55 #ifdef __EMSCRIPTEN__
56 // Emscripten provides even all GL extension functions via static linking. However there is
57 // no GLES2-specific extension header at the moment to include instanced rendering declarations,
58 // so declare them manually from GLES3 gl2ext.h. Emscripten will provide these when linking final output.
59 extern "C"
60 {
61     GL_APICALL void GL_APIENTRY glDrawArraysInstancedANGLE (GLenum mode, GLint first, GLsizei count, GLsizei primcount);
62     GL_APICALL void GL_APIENTRY glDrawElementsInstancedANGLE (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount);
63     GL_APICALL void GL_APIENTRY glVertexAttribDivisorANGLE (GLuint index, GLuint divisor);
64 }
65 #endif
66 
67 #ifdef _WIN32
68 // Prefer the high-performance GPU on switchable GPU systems
69 #include <windows.h>
70 extern "C"
71 {
72     __declspec(dllexport) DWORD NvOptimusEnablement = 1;
73     __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
74 }
75 #endif
76 
77 namespace Urho3D
78 {
79 
80 static const unsigned glCmpFunc[] =
81 {
82     GL_ALWAYS,
83     GL_EQUAL,
84     GL_NOTEQUAL,
85     GL_LESS,
86     GL_LEQUAL,
87     GL_GREATER,
88     GL_GEQUAL
89 };
90 
91 static const unsigned glSrcBlend[] =
92 {
93     GL_ONE,
94     GL_ONE,
95     GL_DST_COLOR,
96     GL_SRC_ALPHA,
97     GL_SRC_ALPHA,
98     GL_ONE,
99     GL_ONE_MINUS_DST_ALPHA,
100     GL_ONE,
101     GL_SRC_ALPHA
102 };
103 
104 static const unsigned glDestBlend[] =
105 {
106     GL_ZERO,
107     GL_ONE,
108     GL_ZERO,
109     GL_ONE_MINUS_SRC_ALPHA,
110     GL_ONE,
111     GL_ONE_MINUS_SRC_ALPHA,
112     GL_DST_ALPHA,
113     GL_ONE,
114     GL_ONE
115 };
116 
117 static const unsigned glBlendOp[] =
118 {
119     GL_FUNC_ADD,
120     GL_FUNC_ADD,
121     GL_FUNC_ADD,
122     GL_FUNC_ADD,
123     GL_FUNC_ADD,
124     GL_FUNC_ADD,
125     GL_FUNC_ADD,
126     GL_FUNC_REVERSE_SUBTRACT,
127     GL_FUNC_REVERSE_SUBTRACT
128 };
129 
130 #ifndef GL_ES_VERSION_2_0
131 static const unsigned glFillMode[] =
132 {
133     GL_FILL,
134     GL_LINE,
135     GL_POINT
136 };
137 
138 static const unsigned glStencilOps[] =
139 {
140     GL_KEEP,
141     GL_ZERO,
142     GL_REPLACE,
143     GL_INCR_WRAP,
144     GL_DECR_WRAP
145 };
146 #endif
147 
148 static const unsigned glElementTypes[] =
149 {
150     GL_INT,
151     GL_FLOAT,
152     GL_FLOAT,
153     GL_FLOAT,
154     GL_FLOAT,
155     GL_UNSIGNED_BYTE,
156     GL_UNSIGNED_BYTE
157 };
158 
159 static const unsigned glElementComponents[] =
160 {
161     1,
162     1,
163     2,
164     3,
165     4,
166     4,
167     4
168 };
169 
170 #ifdef GL_ES_VERSION_2_0
171 static unsigned glesDepthStencilFormat = GL_DEPTH_COMPONENT16;
172 static unsigned glesReadableDepthFormat = GL_DEPTH_COMPONENT;
173 #endif
174 
175 static String extensions;
176 
CheckExtension(const String & name)177 bool CheckExtension(const String& name)
178 {
179     if (extensions.Empty())
180         extensions = (const char*)glGetString(GL_EXTENSIONS);
181     return extensions.Contains(name);
182 }
183 
GetGLPrimitiveType(unsigned elementCount,PrimitiveType type,unsigned & primitiveCount,GLenum & glPrimitiveType)184 static void GetGLPrimitiveType(unsigned elementCount, PrimitiveType type, unsigned& primitiveCount, GLenum& glPrimitiveType)
185 {
186     switch (type)
187     {
188     case TRIANGLE_LIST:
189         primitiveCount = elementCount / 3;
190         glPrimitiveType = GL_TRIANGLES;
191         break;
192 
193     case LINE_LIST:
194         primitiveCount = elementCount / 2;
195         glPrimitiveType = GL_LINES;
196         break;
197 
198     case POINT_LIST:
199         primitiveCount = elementCount;
200         glPrimitiveType = GL_POINTS;
201         break;
202 
203     case TRIANGLE_STRIP:
204         primitiveCount = elementCount - 2;
205         glPrimitiveType = GL_TRIANGLE_STRIP;
206         break;
207 
208     case LINE_STRIP:
209         primitiveCount = elementCount - 1;
210         glPrimitiveType = GL_LINE_STRIP;
211         break;
212 
213     case TRIANGLE_FAN:
214         primitiveCount = elementCount - 2;
215         glPrimitiveType = GL_TRIANGLE_FAN;
216         break;
217     }
218 }
219 
220 const Vector2 Graphics::pixelUVOffset(0.0f, 0.0f);
221 bool Graphics::gl3Support = false;
222 
Graphics(Context * context_)223 Graphics::Graphics(Context* context_) :
224     Object(context_),
225     impl_(new GraphicsImpl()),
226     window_(0),
227     externalWindow_(0),
228     width_(0),
229     height_(0),
230     position_(SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED),
231     multiSample_(1),
232     fullscreen_(false),
233     borderless_(false),
234     resizable_(false),
235     highDPI_(false),
236     vsync_(false),
237     monitor_(0),
238     refreshRate_(0),
239     tripleBuffer_(false),
240     sRGB_(false),
241     forceGL2_(false),
242     instancingSupport_(false),
243     lightPrepassSupport_(false),
244     deferredSupport_(false),
245     anisotropySupport_(false),
246     dxtTextureSupport_(false),
247     etcTextureSupport_(false),
248     pvrtcTextureSupport_(false),
249     hardwareShadowSupport_(false),
250     sRGBSupport_(false),
251     sRGBWriteSupport_(false),
252     numPrimitives_(0),
253     numBatches_(0),
254     maxScratchBufferRequest_(0),
255     dummyColorFormat_(0),
256     shadowMapFormat_(GL_DEPTH_COMPONENT16),
257     hiresShadowMapFormat_(GL_DEPTH_COMPONENT24),
258     defaultTextureFilterMode_(FILTER_TRILINEAR),
259     defaultTextureAnisotropy_(4),
260     shaderPath_("Shaders/GLSL/"),
261     shaderExtension_(".glsl"),
262     orientations_("LandscapeLeft LandscapeRight"),
263 #ifndef GL_ES_VERSION_2_0
264     apiName_("GL2")
265 #else
266     apiName_("GLES2")
267 #endif
268 {
269     SetTextureUnitMappings();
270     ResetCachedState();
271 
272     context_->RequireSDL(SDL_INIT_VIDEO);
273 
274     // Register Graphics library object factories
275     RegisterGraphicsLibrary(context_);
276 }
277 
~Graphics()278 Graphics::~Graphics()
279 {
280     Close();
281 
282     delete impl_;
283     impl_ = 0;
284 
285     context_->ReleaseSDL();
286 }
287 
SetMode(int width,int height,bool fullscreen,bool borderless,bool resizable,bool highDPI,bool vsync,bool tripleBuffer,int multiSample,int monitor,int refreshRate)288 bool Graphics::SetMode(int width, int height, bool fullscreen, bool borderless, bool resizable, bool highDPI, bool vsync,
289     bool tripleBuffer, int multiSample, int monitor, int refreshRate)
290 {
291     URHO3D_PROFILE(SetScreenMode);
292 
293     bool maximize = false;
294 
295 #if defined(IOS) || defined(TVOS)
296     // iOS and tvOS app always take the fullscreen (and with status bar hidden)
297     fullscreen = true;
298 #endif
299 
300     // Make sure monitor index is not bigger than the currently detected monitors
301     int monitors = SDL_GetNumVideoDisplays();
302     if (monitor >= monitors || monitor < 0)
303         monitor = 0; // this monitor is not present, use first monitor
304 
305     // Fullscreen or Borderless can not be resizable
306     if (fullscreen || borderless)
307         resizable = false;
308 
309     // Borderless cannot be fullscreen, they are mutually exclusive
310     if (borderless)
311         fullscreen = false;
312 
313     multiSample = Clamp(multiSample, 1, 16);
314 
315     if (IsInitialized() && width == width_ && height == height_ && fullscreen == fullscreen_ && borderless == borderless_ &&
316         resizable == resizable_ && vsync == vsync_ && tripleBuffer == tripleBuffer_ && multiSample == multiSample_)
317         return true;
318 
319     // If only vsync changes, do not destroy/recreate the context
320     if (IsInitialized() && width == width_ && height == height_ && fullscreen == fullscreen_ && borderless == borderless_ &&
321         resizable == resizable_ && tripleBuffer == tripleBuffer_ && multiSample == multiSample_ && vsync != vsync_)
322     {
323         SDL_GL_SetSwapInterval(vsync ? 1 : 0);
324         vsync_ = vsync;
325         return true;
326     }
327 
328     // If zero dimensions in windowed mode, set windowed mode to maximize and set a predefined default restored window size.
329     // If zero in fullscreen, use desktop mode
330     if (!width || !height)
331     {
332         if (fullscreen || borderless)
333         {
334             SDL_DisplayMode mode;
335             SDL_GetDesktopDisplayMode(monitor, &mode);
336             width = mode.w;
337             height = mode.h;
338         }
339         else
340         {
341             maximize = resizable;
342             width = 1024;
343             height = 768;
344         }
345     }
346 
347     // Check fullscreen mode validity (desktop only). Use a closest match if not found
348 #ifdef DESKTOP_GRAPHICS
349     if (fullscreen)
350     {
351         PODVector<IntVector3> resolutions = GetResolutions(monitor);
352         if (resolutions.Size())
353         {
354             unsigned best = 0;
355             unsigned bestError = M_MAX_UNSIGNED;
356 
357             for (unsigned i = 0; i < resolutions.Size(); ++i)
358             {
359                 unsigned error = Abs(resolutions[i].x_ - width) + Abs(resolutions[i].y_ - height);
360                 if (error < bestError)
361                 {
362                     best = i;
363                     bestError = error;
364                 }
365             }
366 
367             width = resolutions[best].x_;
368             height = resolutions[best].y_;
369             refreshRate = resolutions[best].z_;
370         }
371     }
372 #endif
373 
374     // With an external window, only the size can change after initial setup, so do not recreate context
375     if (!externalWindow_ || !impl_->context_)
376     {
377         // Close the existing window and OpenGL context, mark GPU objects as lost
378         Release(false, true);
379 
380 #ifdef IOS
381         // On iOS window needs to be resizable to handle orientation changes properly
382         resizable = true;
383 #endif
384 
385         SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
386 
387 #ifndef GL_ES_VERSION_2_0
388         SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
389         SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
390         SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
391         SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
392 
393         if (externalWindow_)
394             SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
395         else
396             SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0);
397 
398         SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
399 
400         if (!forceGL2_)
401         {
402             SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
403             SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
404             SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
405         }
406         else
407         {
408             SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
409             SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
410             SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, 0);
411         }
412 #else
413         SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
414         SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
415 #endif
416 
417         if (multiSample > 1)
418         {
419             SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
420             SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, multiSample);
421         }
422         else
423         {
424             SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0);
425             SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0);
426         }
427 
428         // Reposition the window on the specified monitor
429         SDL_Rect display_rect;
430         SDL_GetDisplayBounds(monitor, &display_rect);
431         SDL_SetWindowPosition(window_, display_rect.x, display_rect.y);
432         bool reposition = fullscreen || (borderless && width >= display_rect.w && height >= display_rect.h);
433 
434         int x = reposition ? display_rect.x : position_.x_;
435         int y = reposition ? display_rect.y : position_.y_;
436 
437         unsigned flags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN;
438         if (fullscreen)
439             flags |= SDL_WINDOW_FULLSCREEN;
440         if (borderless)
441             flags |= SDL_WINDOW_BORDERLESS;
442         if (resizable)
443             flags |= SDL_WINDOW_RESIZABLE;
444         if (highDPI)
445             flags |= SDL_WINDOW_ALLOW_HIGHDPI;
446 
447         SDL_SetHint(SDL_HINT_ORIENTATIONS, orientations_.CString());
448 
449         for (;;)
450         {
451             if (!externalWindow_)
452                 window_ = SDL_CreateWindow(windowTitle_.CString(), x, y, width, height, flags);
453             else
454             {
455 #ifndef __EMSCRIPTEN__
456                 if (!window_)
457                     window_ = SDL_CreateWindowFrom(externalWindow_, SDL_WINDOW_OPENGL);
458                 fullscreen = false;
459 #endif
460             }
461 
462             if (window_)
463                 break;
464             else
465             {
466                 if (multiSample > 1)
467                 {
468                     // If failed with multisampling, retry first without
469                     multiSample = 1;
470                     SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0);
471                     SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0);
472                 }
473                 else
474                 {
475                     URHO3D_LOGERRORF("Could not create window, root cause: '%s'", SDL_GetError());
476                     return false;
477                 }
478             }
479         }
480 
481         CreateWindowIcon();
482 
483         if (maximize)
484         {
485             Maximize();
486             SDL_GL_GetDrawableSize(window_, &width, &height);
487         }
488 
489         // Create/restore context and GPU objects and set initial renderstate
490         Restore();
491 
492         // Specific error message is already logged by Restore() when context creation or OpenGL extensions check fails
493         if (!impl_->context_)
494             return false;
495     }
496 
497     // Set vsync
498     SDL_GL_SetSwapInterval(vsync ? 1 : 0);
499 
500     // Store the system FBO on iOS/tvOS now
501 #if defined(IOS) || defined(TVOS)
502     glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&impl_->systemFBO_);
503 #endif
504 
505     fullscreen_ = fullscreen;
506     borderless_ = borderless;
507     resizable_ = resizable;
508     vsync_ = vsync;
509     tripleBuffer_ = tripleBuffer;
510     multiSample_ = multiSample;
511     monitor_ = monitor;
512     refreshRate_ = refreshRate;
513 
514     SDL_GL_GetDrawableSize(window_, &width_, &height_);
515     if (!fullscreen)
516         SDL_GetWindowPosition(window_, &position_.x_, &position_.y_);
517 
518     int logicalWidth, logicalHeight;
519     SDL_GetWindowSize(window_, &logicalWidth, &logicalHeight);
520     highDPI_ = (width_ != logicalWidth) || (height_ != logicalHeight);
521 
522     // Reset rendertargets and viewport for the new screen mode
523     ResetRenderTargets();
524 
525     // Clear the initial window contents to black
526     Clear(CLEAR_COLOR);
527     SDL_GL_SwapWindow(window_);
528 
529     CheckFeatureSupport();
530 
531 #ifdef URHO3D_LOGGING
532     String msg;
533     msg.AppendWithFormat("Set screen mode %dx%d %s monitor %d", width_, height_, (fullscreen_ ? "fullscreen" : "windowed"), monitor_);
534     if (borderless_)
535         msg.Append(" borderless");
536     if (resizable_)
537         msg.Append(" resizable");
538     if (multiSample > 1)
539         msg.AppendWithFormat(" multisample %d", multiSample);
540     URHO3D_LOGINFO(msg);
541 #endif
542 
543     using namespace ScreenMode;
544 
545     VariantMap& eventData = GetEventDataMap();
546     eventData[P_WIDTH] = width_;
547     eventData[P_HEIGHT] = height_;
548     eventData[P_FULLSCREEN] = fullscreen_;
549     eventData[P_BORDERLESS] = borderless_;
550     eventData[P_RESIZABLE] = resizable_;
551     eventData[P_HIGHDPI] = highDPI_;
552     eventData[P_MONITOR] = monitor_;
553     eventData[P_REFRESHRATE] = refreshRate_;
554     SendEvent(E_SCREENMODE, eventData);
555 
556     return true;
557 }
558 
SetMode(int width,int height)559 bool Graphics::SetMode(int width, int height)
560 {
561     return SetMode(width, height, fullscreen_, borderless_, resizable_, highDPI_, vsync_, tripleBuffer_, multiSample_, monitor_, refreshRate_);
562 }
563 
SetSRGB(bool enable)564 void Graphics::SetSRGB(bool enable)
565 {
566     enable &= sRGBWriteSupport_;
567 
568     if (enable != sRGB_)
569     {
570         sRGB_ = enable;
571         impl_->fboDirty_ = true;
572     }
573 }
574 
SetDither(bool enable)575 void Graphics::SetDither(bool enable)
576 {
577     if (enable)
578         glEnable(GL_DITHER);
579     else
580         glDisable(GL_DITHER);
581 }
582 
SetFlushGPU(bool enable)583 void Graphics::SetFlushGPU(bool enable)
584 {
585     // Currently unimplemented on OpenGL
586 }
587 
SetForceGL2(bool enable)588 void Graphics::SetForceGL2(bool enable)
589 {
590     if (IsInitialized())
591     {
592         URHO3D_LOGERROR("OpenGL 2 can only be forced before setting the initial screen mode");
593         return;
594     }
595 
596     forceGL2_ = enable;
597 }
598 
Close()599 void Graphics::Close()
600 {
601     if (!IsInitialized())
602         return;
603 
604     // Actually close the window
605     Release(true, true);
606 }
607 
TakeScreenShot(Image & destImage)608 bool Graphics::TakeScreenShot(Image& destImage)
609 {
610     URHO3D_PROFILE(TakeScreenShot);
611 
612     if (!IsInitialized())
613         return false;
614 
615     if (IsDeviceLost())
616     {
617         URHO3D_LOGERROR("Can not take screenshot while device is lost");
618         return false;
619     }
620 
621     ResetRenderTargets();
622 
623 #ifndef GL_ES_VERSION_2_0
624     destImage.SetSize(width_, height_, 3);
625     glReadPixels(0, 0, width_, height_, GL_RGB, GL_UNSIGNED_BYTE, destImage.GetData());
626 #else
627     // Use RGBA format on OpenGL ES, as otherwise (at least on Android) the produced image is all black
628     destImage.SetSize(width_, height_, 4);
629     glReadPixels(0, 0, width_, height_, GL_RGBA, GL_UNSIGNED_BYTE, destImage.GetData());
630 #endif
631 
632     // On OpenGL we need to flip the image vertically after reading
633     destImage.FlipVertical();
634 
635     return true;
636 }
637 
BeginFrame()638 bool Graphics::BeginFrame()
639 {
640     if (!IsInitialized() || IsDeviceLost())
641         return false;
642 
643     // If using an external window, check it for size changes, and reset screen mode if necessary
644     if (externalWindow_)
645     {
646         int width, height;
647 
648         SDL_GL_GetDrawableSize(window_, &width, &height);
649         if (width != width_ || height != height_)
650             SetMode(width, height);
651     }
652 
653     // Re-enable depth test and depth func in case a third party program has modified it
654     glEnable(GL_DEPTH_TEST);
655     glDepthFunc(glCmpFunc[depthTestMode_]);
656 
657     // Set default rendertarget and depth buffer
658     ResetRenderTargets();
659 
660     // Cleanup textures from previous frame
661     for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
662         SetTexture(i, 0);
663 
664     // Enable color and depth write
665     SetColorWrite(true);
666     SetDepthWrite(true);
667 
668     numPrimitives_ = 0;
669     numBatches_ = 0;
670 
671     SendEvent(E_BEGINRENDERING);
672 
673     return true;
674 }
675 
EndFrame()676 void Graphics::EndFrame()
677 {
678     if (!IsInitialized())
679         return;
680 
681     URHO3D_PROFILE(Present);
682 
683     SendEvent(E_ENDRENDERING);
684 
685     SDL_GL_SwapWindow(window_);
686 
687     // Clean up too large scratch buffers
688     CleanupScratchBuffers();
689 }
690 
Clear(unsigned flags,const Color & color,float depth,unsigned stencil)691 void Graphics::Clear(unsigned flags, const Color& color, float depth, unsigned stencil)
692 {
693     PrepareDraw();
694 
695 #ifdef GL_ES_VERSION_2_0
696     flags &= ~CLEAR_STENCIL;
697 #endif
698 
699     bool oldColorWrite = colorWrite_;
700     bool oldDepthWrite = depthWrite_;
701 
702     if (flags & CLEAR_COLOR && !oldColorWrite)
703         SetColorWrite(true);
704     if (flags & CLEAR_DEPTH && !oldDepthWrite)
705         SetDepthWrite(true);
706     if (flags & CLEAR_STENCIL && stencilWriteMask_ != M_MAX_UNSIGNED)
707         glStencilMask(M_MAX_UNSIGNED);
708 
709     unsigned glFlags = 0;
710     if (flags & CLEAR_COLOR)
711     {
712         glFlags |= GL_COLOR_BUFFER_BIT;
713         glClearColor(color.r_, color.g_, color.b_, color.a_);
714     }
715     if (flags & CLEAR_DEPTH)
716     {
717         glFlags |= GL_DEPTH_BUFFER_BIT;
718         glClearDepth(depth);
719     }
720     if (flags & CLEAR_STENCIL)
721     {
722         glFlags |= GL_STENCIL_BUFFER_BIT;
723         glClearStencil(stencil);
724     }
725 
726     // If viewport is less than full screen, set a scissor to limit the clear
727     /// \todo Any user-set scissor test will be lost
728     IntVector2 viewSize = GetRenderTargetDimensions();
729     if (viewport_.left_ != 0 || viewport_.top_ != 0 || viewport_.right_ != viewSize.x_ || viewport_.bottom_ != viewSize.y_)
730         SetScissorTest(true, IntRect(0, 0, viewport_.Width(), viewport_.Height()));
731     else
732         SetScissorTest(false);
733 
734     glClear(glFlags);
735 
736     SetScissorTest(false);
737     SetColorWrite(oldColorWrite);
738     SetDepthWrite(oldDepthWrite);
739     if (flags & CLEAR_STENCIL && stencilWriteMask_ != M_MAX_UNSIGNED)
740         glStencilMask(stencilWriteMask_);
741 }
742 
ResolveToTexture(Texture2D * destination,const IntRect & viewport)743 bool Graphics::ResolveToTexture(Texture2D* destination, const IntRect& viewport)
744 {
745     if (!destination || !destination->GetRenderSurface())
746         return false;
747 
748     URHO3D_PROFILE(ResolveToTexture);
749 
750     IntRect vpCopy = viewport;
751     if (vpCopy.right_ <= vpCopy.left_)
752         vpCopy.right_ = vpCopy.left_ + 1;
753     if (vpCopy.bottom_ <= vpCopy.top_)
754         vpCopy.bottom_ = vpCopy.top_ + 1;
755     vpCopy.left_ = Clamp(vpCopy.left_, 0, width_);
756     vpCopy.top_ = Clamp(vpCopy.top_, 0, height_);
757     vpCopy.right_ = Clamp(vpCopy.right_, 0, width_);
758     vpCopy.bottom_ = Clamp(vpCopy.bottom_, 0, height_);
759 
760     // Make sure the FBO is not in use
761     ResetRenderTargets();
762 
763     // Use Direct3D convention with the vertical coordinates ie. 0 is top
764     SetTextureForUpdate(destination);
765     glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, vpCopy.left_, height_ - vpCopy.bottom_, vpCopy.Width(), vpCopy.Height());
766     SetTexture(0, 0);
767 
768     return true;
769 }
770 
ResolveToTexture(Texture2D * texture)771 bool Graphics::ResolveToTexture(Texture2D* texture)
772 {
773 #ifndef GL_ES_VERSION_2_0
774     if (!texture)
775         return false;
776     RenderSurface* surface = texture->GetRenderSurface();
777     if (!surface || !surface->GetRenderBuffer())
778         return false;
779 
780     URHO3D_PROFILE(ResolveToTexture);
781 
782     texture->SetResolveDirty(false);
783     surface->SetResolveDirty(false);
784 
785     // Use separate FBOs for resolve to not disturb the currently set rendertarget(s)
786     if (!impl_->resolveSrcFBO_)
787         impl_->resolveSrcFBO_ = CreateFramebuffer();
788     if (!impl_->resolveDestFBO_)
789         impl_->resolveDestFBO_ = CreateFramebuffer();
790 
791     if (!gl3Support)
792     {
793         glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, impl_->resolveSrcFBO_);
794         glFramebufferRenderbufferEXT(GL_READ_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT,
795             surface->GetRenderBuffer());
796         glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, impl_->resolveDestFBO_);
797         glFramebufferTexture2DEXT(GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, texture->GetGPUObjectName(),
798             0);
799         glBlitFramebufferEXT(0, 0, texture->GetWidth(), texture->GetHeight(), 0, 0, texture->GetWidth(), texture->GetHeight(),
800             GL_COLOR_BUFFER_BIT, GL_NEAREST);
801         glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, 0);
802         glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, 0);
803     }
804     else
805     {
806         glBindFramebuffer(GL_READ_FRAMEBUFFER, impl_->resolveSrcFBO_);
807         glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, surface->GetRenderBuffer());
808         glBindFramebuffer(GL_DRAW_FRAMEBUFFER, impl_->resolveDestFBO_);
809         glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture->GetGPUObjectName(), 0);
810         glBlitFramebuffer(0, 0, texture->GetWidth(), texture->GetHeight(), 0, 0, texture->GetWidth(), texture->GetHeight(),
811             GL_COLOR_BUFFER_BIT, GL_NEAREST);
812         glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
813         glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
814     }
815 
816     // Restore previously bound FBO
817     BindFramebuffer(impl_->boundFBO_);
818     return true;
819 #else
820     // Not supported on GLES
821     return false;
822 #endif
823 }
824 
ResolveToTexture(TextureCube * texture)825 bool Graphics::ResolveToTexture(TextureCube* texture)
826 {
827 #ifndef GL_ES_VERSION_2_0
828     if (!texture)
829         return false;
830 
831     URHO3D_PROFILE(ResolveToTexture);
832 
833     texture->SetResolveDirty(false);
834 
835     // Use separate FBOs for resolve to not disturb the currently set rendertarget(s)
836     if (!impl_->resolveSrcFBO_)
837         impl_->resolveSrcFBO_ = CreateFramebuffer();
838     if (!impl_->resolveDestFBO_)
839         impl_->resolveDestFBO_ = CreateFramebuffer();
840 
841     if (!gl3Support)
842     {
843         for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
844         {
845             // Resolve only the surface(s) that were actually rendered to
846             RenderSurface* surface = texture->GetRenderSurface((CubeMapFace)i);
847             if (!surface->IsResolveDirty())
848                 continue;
849 
850             surface->SetResolveDirty(false);
851             glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, impl_->resolveSrcFBO_);
852             glFramebufferRenderbufferEXT(GL_READ_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT,
853                 surface->GetRenderBuffer());
854             glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, impl_->resolveDestFBO_);
855             glFramebufferTexture2DEXT(GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i,
856                 texture->GetGPUObjectName(), 0);
857             glBlitFramebufferEXT(0, 0, texture->GetWidth(), texture->GetHeight(), 0, 0, texture->GetWidth(), texture->GetHeight(),
858                 GL_COLOR_BUFFER_BIT, GL_NEAREST);
859         }
860 
861         glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, 0);
862         glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, 0);
863     }
864     else
865     {
866         for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
867         {
868             RenderSurface* surface = texture->GetRenderSurface((CubeMapFace)i);
869             if (!surface->IsResolveDirty())
870                 continue;
871 
872             surface->SetResolveDirty(false);
873             glBindFramebuffer(GL_READ_FRAMEBUFFER, impl_->resolveSrcFBO_);
874             glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, surface->GetRenderBuffer());
875             glBindFramebuffer(GL_DRAW_FRAMEBUFFER, impl_->resolveDestFBO_);
876             glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i,
877                 texture->GetGPUObjectName(), 0);
878             glBlitFramebuffer(0, 0, texture->GetWidth(), texture->GetHeight(), 0, 0, texture->GetWidth(), texture->GetHeight(),
879                 GL_COLOR_BUFFER_BIT, GL_NEAREST);
880         }
881 
882         glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
883         glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
884     }
885 
886     // Restore previously bound FBO
887     BindFramebuffer(impl_->boundFBO_);
888     return true;
889 #else
890     // Not supported on GLES
891     return false;
892 #endif
893 }
894 
Draw(PrimitiveType type,unsigned vertexStart,unsigned vertexCount)895 void Graphics::Draw(PrimitiveType type, unsigned vertexStart, unsigned vertexCount)
896 {
897     if (!vertexCount)
898         return;
899 
900     PrepareDraw();
901 
902     unsigned primitiveCount;
903     GLenum glPrimitiveType;
904 
905     GetGLPrimitiveType(vertexCount, type, primitiveCount, glPrimitiveType);
906     glDrawArrays(glPrimitiveType, vertexStart, vertexCount);
907 
908     numPrimitives_ += primitiveCount;
909     ++numBatches_;
910 }
911 
Draw(PrimitiveType type,unsigned indexStart,unsigned indexCount,unsigned minVertex,unsigned vertexCount)912 void Graphics::Draw(PrimitiveType type, unsigned indexStart, unsigned indexCount, unsigned minVertex, unsigned vertexCount)
913 {
914     if (!indexCount || !indexBuffer_ || !indexBuffer_->GetGPUObjectName())
915         return;
916 
917     PrepareDraw();
918 
919     unsigned indexSize = indexBuffer_->GetIndexSize();
920     unsigned primitiveCount;
921     GLenum glPrimitiveType;
922 
923     GetGLPrimitiveType(indexCount, type, primitiveCount, glPrimitiveType);
924     GLenum indexType = indexSize == sizeof(unsigned short) ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT;
925     glDrawElements(glPrimitiveType, indexCount, indexType, reinterpret_cast<const GLvoid*>(indexStart * indexSize));
926 
927     numPrimitives_ += primitiveCount;
928     ++numBatches_;
929 }
930 
Draw(PrimitiveType type,unsigned indexStart,unsigned indexCount,unsigned baseVertexIndex,unsigned minVertex,unsigned vertexCount)931 void Graphics::Draw(PrimitiveType type, unsigned indexStart, unsigned indexCount, unsigned baseVertexIndex, unsigned minVertex, unsigned vertexCount)
932 {
933 #ifndef GL_ES_VERSION_2_0
934     if (!gl3Support || !indexCount || !indexBuffer_ || !indexBuffer_->GetGPUObjectName())
935         return;
936 
937     PrepareDraw();
938 
939     unsigned indexSize = indexBuffer_->GetIndexSize();
940     unsigned primitiveCount;
941     GLenum glPrimitiveType;
942 
943     GetGLPrimitiveType(indexCount, type, primitiveCount, glPrimitiveType);
944     GLenum indexType = indexSize == sizeof(unsigned short) ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT;
945     glDrawElementsBaseVertex(glPrimitiveType, indexCount, indexType, reinterpret_cast<GLvoid*>(indexStart * indexSize), baseVertexIndex);
946 
947     numPrimitives_ += primitiveCount;
948     ++numBatches_;
949 #endif
950 }
951 
DrawInstanced(PrimitiveType type,unsigned indexStart,unsigned indexCount,unsigned minVertex,unsigned vertexCount,unsigned instanceCount)952 void Graphics::DrawInstanced(PrimitiveType type, unsigned indexStart, unsigned indexCount, unsigned minVertex, unsigned vertexCount,
953     unsigned instanceCount)
954 {
955 #if !defined(GL_ES_VERSION_2_0) || defined(__EMSCRIPTEN__)
956     if (!indexCount || !indexBuffer_ || !indexBuffer_->GetGPUObjectName() || !instancingSupport_)
957         return;
958 
959     PrepareDraw();
960 
961     unsigned indexSize = indexBuffer_->GetIndexSize();
962     unsigned primitiveCount;
963     GLenum glPrimitiveType;
964 
965     GetGLPrimitiveType(indexCount, type, primitiveCount, glPrimitiveType);
966     GLenum indexType = indexSize == sizeof(unsigned short) ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT;
967 #ifdef __EMSCRIPTEN__
968     glDrawElementsInstancedANGLE(glPrimitiveType, indexCount, indexType, reinterpret_cast<const GLvoid*>(indexStart * indexSize),
969         instanceCount);
970 #else
971     if (gl3Support)
972     {
973         glDrawElementsInstanced(glPrimitiveType, indexCount, indexType, reinterpret_cast<const GLvoid*>(indexStart * indexSize),
974             instanceCount);
975     }
976     else
977     {
978         glDrawElementsInstancedARB(glPrimitiveType, indexCount, indexType, reinterpret_cast<const GLvoid*>(indexStart * indexSize),
979             instanceCount);
980     }
981 #endif
982 
983     numPrimitives_ += instanceCount * primitiveCount;
984     ++numBatches_;
985 #endif
986 }
987 
DrawInstanced(PrimitiveType type,unsigned indexStart,unsigned indexCount,unsigned baseVertexIndex,unsigned minVertex,unsigned vertexCount,unsigned instanceCount)988 void Graphics::DrawInstanced(PrimitiveType type, unsigned indexStart, unsigned indexCount, unsigned baseVertexIndex, unsigned minVertex,
989         unsigned vertexCount, unsigned instanceCount)
990 {
991 #ifndef GL_ES_VERSION_2_0
992     if (!gl3Support || !indexCount || !indexBuffer_ || !indexBuffer_->GetGPUObjectName() || !instancingSupport_)
993         return;
994 
995     PrepareDraw();
996 
997     unsigned indexSize = indexBuffer_->GetIndexSize();
998     unsigned primitiveCount;
999     GLenum glPrimitiveType;
1000 
1001     GetGLPrimitiveType(indexCount, type, primitiveCount, glPrimitiveType);
1002     GLenum indexType = indexSize == sizeof(unsigned short) ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT;
1003 
1004     glDrawElementsInstancedBaseVertex(glPrimitiveType, indexCount, indexType, reinterpret_cast<const GLvoid*>(indexStart * indexSize),
1005         instanceCount, baseVertexIndex);
1006 
1007     numPrimitives_ += instanceCount * primitiveCount;
1008     ++numBatches_;
1009 #endif
1010 }
1011 
SetVertexBuffer(VertexBuffer * buffer)1012 void Graphics::SetVertexBuffer(VertexBuffer* buffer)
1013 {
1014     // Note: this is not multi-instance safe
1015     static PODVector<VertexBuffer*> vertexBuffers(1);
1016     vertexBuffers[0] = buffer;
1017     SetVertexBuffers(vertexBuffers);
1018 }
1019 
SetVertexBuffers(const PODVector<VertexBuffer * > & buffers,unsigned instanceOffset)1020 bool Graphics::SetVertexBuffers(const PODVector<VertexBuffer*>& buffers, unsigned instanceOffset)
1021 {
1022     if (buffers.Size() > MAX_VERTEX_STREAMS)
1023     {
1024         URHO3D_LOGERROR("Too many vertex buffers");
1025         return false;
1026     }
1027 
1028     if (instanceOffset != impl_->lastInstanceOffset_)
1029     {
1030         impl_->lastInstanceOffset_ = instanceOffset;
1031         impl_->vertexBuffersDirty_ = true;
1032     }
1033 
1034     for (unsigned i = 0; i < MAX_VERTEX_STREAMS; ++i)
1035     {
1036         VertexBuffer* buffer = 0;
1037         if (i < buffers.Size())
1038             buffer = buffers[i];
1039         if (buffer != vertexBuffers_[i])
1040         {
1041             vertexBuffers_[i] = buffer;
1042             impl_->vertexBuffersDirty_ = true;
1043         }
1044     }
1045 
1046     return true;
1047 }
1048 
SetVertexBuffers(const Vector<SharedPtr<VertexBuffer>> & buffers,unsigned instanceOffset)1049 bool Graphics::SetVertexBuffers(const Vector<SharedPtr<VertexBuffer> >& buffers, unsigned instanceOffset)
1050 {
1051     return SetVertexBuffers(reinterpret_cast<const PODVector<VertexBuffer*>&>(buffers), instanceOffset);
1052 }
1053 
SetIndexBuffer(IndexBuffer * buffer)1054 void Graphics::SetIndexBuffer(IndexBuffer* buffer)
1055 {
1056     if (indexBuffer_ == buffer)
1057         return;
1058 
1059     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer ? buffer->GetGPUObjectName() : 0);
1060     indexBuffer_ = buffer;
1061 }
1062 
SetShaders(ShaderVariation * vs,ShaderVariation * ps)1063 void Graphics::SetShaders(ShaderVariation* vs, ShaderVariation* ps)
1064 {
1065     if (vs == vertexShader_ && ps == pixelShader_)
1066         return;
1067 
1068     // Compile the shaders now if not yet compiled. If already attempted, do not retry
1069     if (vs && !vs->GetGPUObjectName())
1070     {
1071         if (vs->GetCompilerOutput().Empty())
1072         {
1073             URHO3D_PROFILE(CompileVertexShader);
1074 
1075             bool success = vs->Create();
1076             if (success)
1077                 URHO3D_LOGDEBUG("Compiled vertex shader " + vs->GetFullName());
1078             else
1079             {
1080                 URHO3D_LOGERROR("Failed to compile vertex shader " + vs->GetFullName() + ":\n" + vs->GetCompilerOutput());
1081                 vs = 0;
1082             }
1083         }
1084         else
1085             vs = 0;
1086     }
1087 
1088     if (ps && !ps->GetGPUObjectName())
1089     {
1090         if (ps->GetCompilerOutput().Empty())
1091         {
1092             URHO3D_PROFILE(CompilePixelShader);
1093 
1094             bool success = ps->Create();
1095             if (success)
1096                 URHO3D_LOGDEBUG("Compiled pixel shader " + ps->GetFullName());
1097             else
1098             {
1099                 URHO3D_LOGERROR("Failed to compile pixel shader " + ps->GetFullName() + ":\n" + ps->GetCompilerOutput());
1100                 ps = 0;
1101             }
1102         }
1103         else
1104             ps = 0;
1105     }
1106 
1107     if (!vs || !ps)
1108     {
1109         glUseProgram(0);
1110         vertexShader_ = 0;
1111         pixelShader_ = 0;
1112         impl_->shaderProgram_ = 0;
1113     }
1114     else
1115     {
1116         vertexShader_ = vs;
1117         pixelShader_ = ps;
1118 
1119         Pair<ShaderVariation*, ShaderVariation*> combination(vs, ps);
1120         ShaderProgramMap::Iterator i = impl_->shaderPrograms_.Find(combination);
1121 
1122         if (i != impl_->shaderPrograms_.End())
1123         {
1124             // Use the existing linked program
1125             if (i->second_->GetGPUObjectName())
1126             {
1127                 glUseProgram(i->second_->GetGPUObjectName());
1128                 impl_->shaderProgram_ = i->second_;
1129             }
1130             else
1131             {
1132                 glUseProgram(0);
1133                 impl_->shaderProgram_ = 0;
1134             }
1135         }
1136         else
1137         {
1138             // Link a new combination
1139             URHO3D_PROFILE(LinkShaders);
1140 
1141             SharedPtr<ShaderProgram> newProgram(new ShaderProgram(this, vs, ps));
1142             if (newProgram->Link())
1143             {
1144                 URHO3D_LOGDEBUG("Linked vertex shader " + vs->GetFullName() + " and pixel shader " + ps->GetFullName());
1145                 // Note: Link() calls glUseProgram() to set the texture sampler uniforms,
1146                 // so it is not necessary to call it again
1147                 impl_->shaderProgram_ = newProgram;
1148             }
1149             else
1150             {
1151                 URHO3D_LOGERROR("Failed to link vertex shader " + vs->GetFullName() + " and pixel shader " + ps->GetFullName() + ":\n" +
1152                          newProgram->GetLinkerOutput());
1153                 glUseProgram(0);
1154                 impl_->shaderProgram_ = 0;
1155             }
1156 
1157             impl_->shaderPrograms_[combination] = newProgram;
1158         }
1159     }
1160 
1161     // Update the clip plane uniform on GL3, and set constant buffers
1162 #ifndef GL_ES_VERSION_2_0
1163     if (gl3Support && impl_->shaderProgram_)
1164     {
1165         const SharedPtr<ConstantBuffer>* constantBuffers = impl_->shaderProgram_->GetConstantBuffers();
1166         for (unsigned i = 0; i < MAX_SHADER_PARAMETER_GROUPS * 2; ++i)
1167         {
1168             ConstantBuffer* buffer = constantBuffers[i].Get();
1169             if (buffer != impl_->constantBuffers_[i])
1170             {
1171                 unsigned object = buffer ? buffer->GetGPUObjectName() : 0;
1172                 glBindBufferBase(GL_UNIFORM_BUFFER, i, object);
1173                 // Calling glBindBufferBase also affects the generic buffer binding point
1174                 impl_->boundUBO_ = object;
1175                 impl_->constantBuffers_[i] = buffer;
1176                 ShaderProgram::ClearGlobalParameterSource((ShaderParameterGroup)(i % MAX_SHADER_PARAMETER_GROUPS));
1177             }
1178         }
1179 
1180         SetShaderParameter(VSP_CLIPPLANE, useClipPlane_ ? clipPlane_ : Vector4(0.0f, 0.0f, 0.0f, 1.0f));
1181     }
1182 #endif
1183 
1184     // Store shader combination if shader dumping in progress
1185     if (shaderPrecache_)
1186         shaderPrecache_->StoreShaders(vertexShader_, pixelShader_);
1187 
1188     if (impl_->shaderProgram_)
1189     {
1190         impl_->usedVertexAttributes_ = impl_->shaderProgram_->GetUsedVertexAttributes();
1191         impl_->vertexAttributes_ = &impl_->shaderProgram_->GetVertexAttributes();
1192     }
1193     else
1194     {
1195         impl_->usedVertexAttributes_ = 0;
1196         impl_->vertexAttributes_ = 0;
1197     }
1198 
1199     impl_->vertexBuffersDirty_ = true;
1200 }
1201 
SetShaderParameter(StringHash param,const float * data,unsigned count)1202 void Graphics::SetShaderParameter(StringHash param, const float* data, unsigned count)
1203 {
1204     if (impl_->shaderProgram_)
1205     {
1206         const ShaderParameter* info = impl_->shaderProgram_->GetParameter(param);
1207         if (info)
1208         {
1209             if (info->bufferPtr_)
1210             {
1211                 ConstantBuffer* buffer = info->bufferPtr_;
1212                 if (!buffer->IsDirty())
1213                     impl_->dirtyConstantBuffers_.Push(buffer);
1214                 buffer->SetParameter(info->offset_, (unsigned)(count * sizeof(float)), data);
1215                 return;
1216             }
1217 
1218             switch (info->glType_)
1219             {
1220             case GL_FLOAT:
1221                 glUniform1fv(info->location_, count, data);
1222                 break;
1223 
1224             case GL_FLOAT_VEC2:
1225                 glUniform2fv(info->location_, count / 2, data);
1226                 break;
1227 
1228             case GL_FLOAT_VEC3:
1229                 glUniform3fv(info->location_, count / 3, data);
1230                 break;
1231 
1232             case GL_FLOAT_VEC4:
1233                 glUniform4fv(info->location_, count / 4, data);
1234                 break;
1235 
1236             case GL_FLOAT_MAT3:
1237                 glUniformMatrix3fv(info->location_, count / 9, GL_FALSE, data);
1238                 break;
1239 
1240             case GL_FLOAT_MAT4:
1241                 glUniformMatrix4fv(info->location_, count / 16, GL_FALSE, data);
1242                 break;
1243 
1244             default: break;
1245             }
1246         }
1247     }
1248 }
1249 
SetShaderParameter(StringHash param,float value)1250 void Graphics::SetShaderParameter(StringHash param, float value)
1251 {
1252     if (impl_->shaderProgram_)
1253     {
1254         const ShaderParameter* info = impl_->shaderProgram_->GetParameter(param);
1255         if (info)
1256         {
1257             if (info->bufferPtr_)
1258             {
1259                 ConstantBuffer* buffer = info->bufferPtr_;
1260                 if (!buffer->IsDirty())
1261                     impl_->dirtyConstantBuffers_.Push(buffer);
1262                 buffer->SetParameter(info->offset_, sizeof(float), &value);
1263                 return;
1264             }
1265 
1266             glUniform1fv(info->location_, 1, &value);
1267         }
1268     }
1269 }
1270 
SetShaderParameter(StringHash param,int value)1271 void Graphics::SetShaderParameter(StringHash param, int value)
1272 {
1273     if (impl_->shaderProgram_)
1274     {
1275         const ShaderParameter* info = impl_->shaderProgram_->GetParameter(param);
1276         if (info)
1277         {
1278             if (info->bufferPtr_)
1279             {
1280                 ConstantBuffer* buffer = info->bufferPtr_;
1281                 if (!buffer->IsDirty())
1282                     impl_->dirtyConstantBuffers_.Push(buffer);
1283                 buffer->SetParameter(info->offset_, sizeof(int), &value);
1284                 return;
1285             }
1286 
1287             glUniform1i(info->location_, value);
1288         }
1289     }
1290 }
1291 
SetShaderParameter(StringHash param,bool value)1292 void Graphics::SetShaderParameter(StringHash param, bool value)
1293 {
1294     // \todo Not tested
1295     if (impl_->shaderProgram_)
1296     {
1297         const ShaderParameter* info = impl_->shaderProgram_->GetParameter(param);
1298         if (info)
1299         {
1300             if (info->bufferPtr_)
1301             {
1302                 ConstantBuffer* buffer = info->bufferPtr_;
1303                 if (!buffer->IsDirty())
1304                     impl_->dirtyConstantBuffers_.Push(buffer);
1305                 buffer->SetParameter(info->offset_, sizeof(bool), &value);
1306                 return;
1307             }
1308 
1309             glUniform1i(info->location_, (int)value);
1310         }
1311     }
1312 }
1313 
SetShaderParameter(StringHash param,const Color & color)1314 void Graphics::SetShaderParameter(StringHash param, const Color& color)
1315 {
1316     SetShaderParameter(param, color.Data(), 4);
1317 }
1318 
SetShaderParameter(StringHash param,const Vector2 & vector)1319 void Graphics::SetShaderParameter(StringHash param, const Vector2& vector)
1320 {
1321     if (impl_->shaderProgram_)
1322     {
1323         const ShaderParameter* info = impl_->shaderProgram_->GetParameter(param);
1324         if (info)
1325         {
1326             if (info->bufferPtr_)
1327             {
1328                 ConstantBuffer* buffer = info->bufferPtr_;
1329                 if (!buffer->IsDirty())
1330                     impl_->dirtyConstantBuffers_.Push(buffer);
1331                 buffer->SetParameter(info->offset_, sizeof(Vector2), &vector);
1332                 return;
1333             }
1334 
1335             // Check the uniform type to avoid mismatch
1336             switch (info->glType_)
1337             {
1338             case GL_FLOAT:
1339                 glUniform1fv(info->location_, 1, vector.Data());
1340                 break;
1341 
1342             case GL_FLOAT_VEC2:
1343                 glUniform2fv(info->location_, 1, vector.Data());
1344                 break;
1345 
1346             default: break;
1347             }
1348         }
1349     }
1350 }
1351 
SetShaderParameter(StringHash param,const Matrix3 & matrix)1352 void Graphics::SetShaderParameter(StringHash param, const Matrix3& matrix)
1353 {
1354     if (impl_->shaderProgram_)
1355     {
1356         const ShaderParameter* info = impl_->shaderProgram_->GetParameter(param);
1357         if (info)
1358         {
1359             if (info->bufferPtr_)
1360             {
1361                 ConstantBuffer* buffer = info->bufferPtr_;
1362                 if (!buffer->IsDirty())
1363                     impl_->dirtyConstantBuffers_.Push(buffer);
1364                 buffer->SetVector3ArrayParameter(info->offset_, 3, &matrix);
1365                 return;
1366             }
1367 
1368             glUniformMatrix3fv(info->location_, 1, GL_FALSE, matrix.Data());
1369         }
1370     }
1371 }
1372 
SetShaderParameter(StringHash param,const Vector3 & vector)1373 void Graphics::SetShaderParameter(StringHash param, const Vector3& vector)
1374 {
1375     if (impl_->shaderProgram_)
1376     {
1377         const ShaderParameter* info = impl_->shaderProgram_->GetParameter(param);
1378         if (info)
1379         {
1380             if (info->bufferPtr_)
1381             {
1382                 ConstantBuffer* buffer = info->bufferPtr_;
1383                 if (!buffer->IsDirty())
1384                     impl_->dirtyConstantBuffers_.Push(buffer);
1385                 buffer->SetParameter(info->offset_, sizeof(Vector3), &vector);
1386                 return;
1387             }
1388 
1389             // Check the uniform type to avoid mismatch
1390             switch (info->glType_)
1391             {
1392             case GL_FLOAT:
1393                 glUniform1fv(info->location_, 1, vector.Data());
1394                 break;
1395 
1396             case GL_FLOAT_VEC2:
1397                 glUniform2fv(info->location_, 1, vector.Data());
1398                 break;
1399 
1400             case GL_FLOAT_VEC3:
1401                 glUniform3fv(info->location_, 1, vector.Data());
1402                 break;
1403 
1404             default: break;
1405             }
1406         }
1407     }
1408 }
1409 
SetShaderParameter(StringHash param,const Matrix4 & matrix)1410 void Graphics::SetShaderParameter(StringHash param, const Matrix4& matrix)
1411 {
1412     if (impl_->shaderProgram_)
1413     {
1414         const ShaderParameter* info = impl_->shaderProgram_->GetParameter(param);
1415         if (info)
1416         {
1417             if (info->bufferPtr_)
1418             {
1419                 ConstantBuffer* buffer = info->bufferPtr_;
1420                 if (!buffer->IsDirty())
1421                     impl_->dirtyConstantBuffers_.Push(buffer);
1422                 buffer->SetParameter(info->offset_, sizeof(Matrix4), &matrix);
1423                 return;
1424             }
1425 
1426             glUniformMatrix4fv(info->location_, 1, GL_FALSE, matrix.Data());
1427         }
1428     }
1429 }
1430 
SetShaderParameter(StringHash param,const Vector4 & vector)1431 void Graphics::SetShaderParameter(StringHash param, const Vector4& vector)
1432 {
1433     if (impl_->shaderProgram_)
1434     {
1435         const ShaderParameter* info = impl_->shaderProgram_->GetParameter(param);
1436         if (info)
1437         {
1438             if (info->bufferPtr_)
1439             {
1440                 ConstantBuffer* buffer = info->bufferPtr_;
1441                 if (!buffer->IsDirty())
1442                     impl_->dirtyConstantBuffers_.Push(buffer);
1443                 buffer->SetParameter(info->offset_, sizeof(Vector4), &vector);
1444                 return;
1445             }
1446 
1447             // Check the uniform type to avoid mismatch
1448             switch (info->glType_)
1449             {
1450             case GL_FLOAT:
1451                 glUniform1fv(info->location_, 1, vector.Data());
1452                 break;
1453 
1454             case GL_FLOAT_VEC2:
1455                 glUniform2fv(info->location_, 1, vector.Data());
1456                 break;
1457 
1458             case GL_FLOAT_VEC3:
1459                 glUniform3fv(info->location_, 1, vector.Data());
1460                 break;
1461 
1462             case GL_FLOAT_VEC4:
1463                 glUniform4fv(info->location_, 1, vector.Data());
1464                 break;
1465 
1466             default: break;
1467             }
1468         }
1469     }
1470 }
1471 
SetShaderParameter(StringHash param,const Matrix3x4 & matrix)1472 void Graphics::SetShaderParameter(StringHash param, const Matrix3x4& matrix)
1473 {
1474     if (impl_->shaderProgram_)
1475     {
1476         const ShaderParameter* info = impl_->shaderProgram_->GetParameter(param);
1477         if (info)
1478         {
1479             // Expand to a full Matrix4
1480             static Matrix4 fullMatrix;
1481             fullMatrix.m00_ = matrix.m00_;
1482             fullMatrix.m01_ = matrix.m01_;
1483             fullMatrix.m02_ = matrix.m02_;
1484             fullMatrix.m03_ = matrix.m03_;
1485             fullMatrix.m10_ = matrix.m10_;
1486             fullMatrix.m11_ = matrix.m11_;
1487             fullMatrix.m12_ = matrix.m12_;
1488             fullMatrix.m13_ = matrix.m13_;
1489             fullMatrix.m20_ = matrix.m20_;
1490             fullMatrix.m21_ = matrix.m21_;
1491             fullMatrix.m22_ = matrix.m22_;
1492             fullMatrix.m23_ = matrix.m23_;
1493 
1494             if (info->bufferPtr_)
1495             {
1496                 ConstantBuffer* buffer = info->bufferPtr_;
1497                 if (!buffer->IsDirty())
1498                     impl_->dirtyConstantBuffers_.Push(buffer);
1499                 buffer->SetParameter(info->offset_, sizeof(Matrix4), &fullMatrix);
1500                 return;
1501             }
1502 
1503             glUniformMatrix4fv(info->location_, 1, GL_FALSE, fullMatrix.Data());
1504         }
1505     }
1506 }
1507 
NeedParameterUpdate(ShaderParameterGroup group,const void * source)1508 bool Graphics::NeedParameterUpdate(ShaderParameterGroup group, const void* source)
1509 {
1510     return impl_->shaderProgram_ ? impl_->shaderProgram_->NeedParameterUpdate(group, source) : false;
1511 }
1512 
HasShaderParameter(StringHash param)1513 bool Graphics::HasShaderParameter(StringHash param)
1514 {
1515     return impl_->shaderProgram_ && impl_->shaderProgram_->HasParameter(param);
1516 }
1517 
HasTextureUnit(TextureUnit unit)1518 bool Graphics::HasTextureUnit(TextureUnit unit)
1519 {
1520     return impl_->shaderProgram_ && impl_->shaderProgram_->HasTextureUnit(unit);
1521 }
1522 
ClearParameterSource(ShaderParameterGroup group)1523 void Graphics::ClearParameterSource(ShaderParameterGroup group)
1524 {
1525     if (impl_->shaderProgram_)
1526         impl_->shaderProgram_->ClearParameterSource(group);
1527 }
1528 
ClearParameterSources()1529 void Graphics::ClearParameterSources()
1530 {
1531     ShaderProgram::ClearParameterSources();
1532 }
1533 
ClearTransformSources()1534 void Graphics::ClearTransformSources()
1535 {
1536     if (impl_->shaderProgram_)
1537     {
1538         impl_->shaderProgram_->ClearParameterSource(SP_CAMERA);
1539         impl_->shaderProgram_->ClearParameterSource(SP_OBJECT);
1540     }
1541 }
1542 
SetTexture(unsigned index,Texture * texture)1543 void Graphics::SetTexture(unsigned index, Texture* texture)
1544 {
1545     if (index >= MAX_TEXTURE_UNITS)
1546         return;
1547 
1548     // Check if texture is currently bound as a rendertarget. In that case, use its backup texture, or blank if not defined
1549     if (texture)
1550     {
1551         if (renderTargets_[0] && renderTargets_[0]->GetParentTexture() == texture)
1552             texture = texture->GetBackupTexture();
1553         else
1554         {
1555             // Resolve multisampled texture now as necessary
1556             if (texture->GetMultiSample() > 1 && texture->GetAutoResolve() && texture->IsResolveDirty())
1557             {
1558                 if (texture->GetType() == Texture2D::GetTypeStatic())
1559                     ResolveToTexture(static_cast<Texture2D*>(texture));
1560                 if (texture->GetType() == TextureCube::GetTypeStatic())
1561                     ResolveToTexture(static_cast<TextureCube*>(texture));
1562             }
1563         }
1564     }
1565 
1566     if (textures_[index] != texture)
1567     {
1568         if (impl_->activeTexture_ != index)
1569         {
1570             glActiveTexture(GL_TEXTURE0 + index);
1571             impl_->activeTexture_ = index;
1572         }
1573 
1574         if (texture)
1575         {
1576             unsigned glType = texture->GetTarget();
1577             // Unbind old texture type if necessary
1578             if (impl_->textureTypes_[index] && impl_->textureTypes_[index] != glType)
1579                 glBindTexture(impl_->textureTypes_[index], 0);
1580             glBindTexture(glType, texture->GetGPUObjectName());
1581             impl_->textureTypes_[index] = glType;
1582 
1583             if (texture->GetParametersDirty())
1584                 texture->UpdateParameters();
1585             if (texture->GetLevelsDirty())
1586                 texture->RegenerateLevels();
1587         }
1588         else if (impl_->textureTypes_[index])
1589         {
1590             glBindTexture(impl_->textureTypes_[index], 0);
1591             impl_->textureTypes_[index] = 0;
1592         }
1593 
1594         textures_[index] = texture;
1595     }
1596     else
1597     {
1598         if (texture && (texture->GetParametersDirty() || texture->GetLevelsDirty()))
1599         {
1600             if (impl_->activeTexture_ != index)
1601             {
1602                 glActiveTexture(GL_TEXTURE0 + index);
1603                 impl_->activeTexture_ = index;
1604             }
1605 
1606             glBindTexture(texture->GetTarget(), texture->GetGPUObjectName());
1607             if (texture->GetParametersDirty())
1608                 texture->UpdateParameters();
1609             if (texture->GetLevelsDirty())
1610                 texture->RegenerateLevels();
1611         }
1612     }
1613 }
1614 
SetTextureForUpdate(Texture * texture)1615 void Graphics::SetTextureForUpdate(Texture* texture)
1616 {
1617     if (impl_->activeTexture_ != 0)
1618     {
1619         glActiveTexture(GL_TEXTURE0);
1620         impl_->activeTexture_ = 0;
1621     }
1622 
1623     unsigned glType = texture->GetTarget();
1624     // Unbind old texture type if necessary
1625     if (impl_->textureTypes_[0] && impl_->textureTypes_[0] != glType)
1626         glBindTexture(impl_->textureTypes_[0], 0);
1627     glBindTexture(glType, texture->GetGPUObjectName());
1628     impl_->textureTypes_[0] = glType;
1629     textures_[0] = texture;
1630 }
1631 
SetDefaultTextureFilterMode(TextureFilterMode mode)1632 void Graphics::SetDefaultTextureFilterMode(TextureFilterMode mode)
1633 {
1634     if (mode != defaultTextureFilterMode_)
1635     {
1636         defaultTextureFilterMode_ = mode;
1637         SetTextureParametersDirty();
1638     }
1639 }
1640 
SetDefaultTextureAnisotropy(unsigned level)1641 void Graphics::SetDefaultTextureAnisotropy(unsigned level)
1642 {
1643     level = Max(level, 1U);
1644 
1645     if (level != defaultTextureAnisotropy_)
1646     {
1647         defaultTextureAnisotropy_ = level;
1648         SetTextureParametersDirty();
1649     }
1650 }
1651 
SetTextureParametersDirty()1652 void Graphics::SetTextureParametersDirty()
1653 {
1654     MutexLock lock(gpuObjectMutex_);
1655 
1656     for (PODVector<GPUObject*>::Iterator i = gpuObjects_.Begin(); i != gpuObjects_.End(); ++i)
1657     {
1658         Texture* texture = dynamic_cast<Texture*>(*i);
1659         if (texture)
1660             texture->SetParametersDirty();
1661     }
1662 }
1663 
ResetRenderTargets()1664 void Graphics::ResetRenderTargets()
1665 {
1666     for (unsigned i = 0; i < MAX_RENDERTARGETS; ++i)
1667         SetRenderTarget(i, (RenderSurface*)0);
1668     SetDepthStencil((RenderSurface*)0);
1669     SetViewport(IntRect(0, 0, width_, height_));
1670 }
1671 
ResetRenderTarget(unsigned index)1672 void Graphics::ResetRenderTarget(unsigned index)
1673 {
1674     SetRenderTarget(index, (RenderSurface*)0);
1675 }
1676 
ResetDepthStencil()1677 void Graphics::ResetDepthStencil()
1678 {
1679     SetDepthStencil((RenderSurface*)0);
1680 }
1681 
SetRenderTarget(unsigned index,RenderSurface * renderTarget)1682 void Graphics::SetRenderTarget(unsigned index, RenderSurface* renderTarget)
1683 {
1684     if (index >= MAX_RENDERTARGETS)
1685         return;
1686 
1687     if (renderTarget != renderTargets_[index])
1688     {
1689         renderTargets_[index] = renderTarget;
1690 
1691         // If the rendertarget is also bound as a texture, replace with backup texture or null
1692         if (renderTarget)
1693         {
1694             Texture* parentTexture = renderTarget->GetParentTexture();
1695 
1696             for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
1697             {
1698                 if (textures_[i] == parentTexture)
1699                     SetTexture(i, textures_[i]->GetBackupTexture());
1700             }
1701 
1702             // If multisampled, mark the texture & surface needing resolve
1703             if (parentTexture->GetMultiSample() > 1 && parentTexture->GetAutoResolve())
1704             {
1705                 parentTexture->SetResolveDirty(true);
1706                 renderTarget->SetResolveDirty(true);
1707             }
1708 
1709             // If mipmapped, mark the levels needing regeneration
1710             if (parentTexture->GetLevels() > 1)
1711                 parentTexture->SetLevelsDirty();
1712         }
1713 
1714         impl_->fboDirty_ = true;
1715     }
1716 }
1717 
SetRenderTarget(unsigned index,Texture2D * texture)1718 void Graphics::SetRenderTarget(unsigned index, Texture2D* texture)
1719 {
1720     RenderSurface* renderTarget = 0;
1721     if (texture)
1722         renderTarget = texture->GetRenderSurface();
1723 
1724     SetRenderTarget(index, renderTarget);
1725 }
1726 
SetDepthStencil(RenderSurface * depthStencil)1727 void Graphics::SetDepthStencil(RenderSurface* depthStencil)
1728 {
1729     // If we are using a rendertarget texture, it is required in OpenGL to also have an own depth-stencil
1730     // Create a new depth-stencil texture as necessary to be able to provide similar behaviour as Direct3D9
1731     // Only do this for non-multisampled rendertargets; when using multisampled target a similarly multisampled
1732     // depth-stencil should also be provided (backbuffer depth isn't compatible)
1733     if (renderTargets_[0] && renderTargets_[0]->GetMultiSample() == 1 && !depthStencil)
1734     {
1735         int width = renderTargets_[0]->GetWidth();
1736         int height = renderTargets_[0]->GetHeight();
1737 
1738         // Direct3D9 default depth-stencil can not be used when rendertarget is larger than the window.
1739         // Check size similarly
1740         if (width <= width_ && height <= height_)
1741         {
1742             int searchKey = (width << 16) | height;
1743             HashMap<int, SharedPtr<Texture2D> >::Iterator i = impl_->depthTextures_.Find(searchKey);
1744             if (i != impl_->depthTextures_.End())
1745                 depthStencil = i->second_->GetRenderSurface();
1746             else
1747             {
1748                 SharedPtr<Texture2D> newDepthTexture(new Texture2D(context_));
1749                 newDepthTexture->SetSize(width, height, GetDepthStencilFormat(), TEXTURE_DEPTHSTENCIL);
1750                 impl_->depthTextures_[searchKey] = newDepthTexture;
1751                 depthStencil = newDepthTexture->GetRenderSurface();
1752             }
1753         }
1754     }
1755 
1756     if (depthStencil != depthStencil_)
1757     {
1758         depthStencil_ = depthStencil;
1759         impl_->fboDirty_ = true;
1760     }
1761 }
1762 
SetDepthStencil(Texture2D * texture)1763 void Graphics::SetDepthStencil(Texture2D* texture)
1764 {
1765     RenderSurface* depthStencil = 0;
1766     if (texture)
1767         depthStencil = texture->GetRenderSurface();
1768 
1769     SetDepthStencil(depthStencil);
1770 }
1771 
SetViewport(const IntRect & rect)1772 void Graphics::SetViewport(const IntRect& rect)
1773 {
1774     PrepareDraw();
1775 
1776     IntVector2 rtSize = GetRenderTargetDimensions();
1777 
1778     IntRect rectCopy = rect;
1779 
1780     if (rectCopy.right_ <= rectCopy.left_)
1781         rectCopy.right_ = rectCopy.left_ + 1;
1782     if (rectCopy.bottom_ <= rectCopy.top_)
1783         rectCopy.bottom_ = rectCopy.top_ + 1;
1784     rectCopy.left_ = Clamp(rectCopy.left_, 0, rtSize.x_);
1785     rectCopy.top_ = Clamp(rectCopy.top_, 0, rtSize.y_);
1786     rectCopy.right_ = Clamp(rectCopy.right_, 0, rtSize.x_);
1787     rectCopy.bottom_ = Clamp(rectCopy.bottom_, 0, rtSize.y_);
1788 
1789     // Use Direct3D convention with the vertical coordinates ie. 0 is top
1790     glViewport(rectCopy.left_, rtSize.y_ - rectCopy.bottom_, rectCopy.Width(), rectCopy.Height());
1791     viewport_ = rectCopy;
1792 
1793     // Disable scissor test, needs to be re-enabled by the user
1794     SetScissorTest(false);
1795 }
1796 
SetBlendMode(BlendMode mode,bool alphaToCoverage)1797 void Graphics::SetBlendMode(BlendMode mode, bool alphaToCoverage)
1798 {
1799     if (mode != blendMode_)
1800     {
1801         if (mode == BLEND_REPLACE)
1802             glDisable(GL_BLEND);
1803         else
1804         {
1805             glEnable(GL_BLEND);
1806             glBlendFunc(glSrcBlend[mode], glDestBlend[mode]);
1807             glBlendEquation(glBlendOp[mode]);
1808         }
1809 
1810         blendMode_ = mode;
1811     }
1812 
1813     if (alphaToCoverage != alphaToCoverage_)
1814     {
1815         if (alphaToCoverage)
1816             glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE);
1817         else
1818             glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE);
1819 
1820         alphaToCoverage_ = alphaToCoverage;
1821     }
1822 }
1823 
SetColorWrite(bool enable)1824 void Graphics::SetColorWrite(bool enable)
1825 {
1826     if (enable != colorWrite_)
1827     {
1828         if (enable)
1829             glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
1830         else
1831             glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
1832 
1833         colorWrite_ = enable;
1834     }
1835 }
1836 
SetCullMode(CullMode mode)1837 void Graphics::SetCullMode(CullMode mode)
1838 {
1839     if (mode != cullMode_)
1840     {
1841         if (mode == CULL_NONE)
1842             glDisable(GL_CULL_FACE);
1843         else
1844         {
1845             // Use Direct3D convention, ie. clockwise vertices define a front face
1846             glEnable(GL_CULL_FACE);
1847             glCullFace(mode == CULL_CCW ? GL_FRONT : GL_BACK);
1848         }
1849 
1850         cullMode_ = mode;
1851     }
1852 }
1853 
SetDepthBias(float constantBias,float slopeScaledBias)1854 void Graphics::SetDepthBias(float constantBias, float slopeScaledBias)
1855 {
1856     if (constantBias != constantDepthBias_ || slopeScaledBias != slopeScaledDepthBias_)
1857     {
1858 #ifndef GL_ES_VERSION_2_0
1859         if (slopeScaledBias != 0.0f)
1860         {
1861             // OpenGL constant bias is unreliable and dependent on depth buffer bitdepth, apply in the projection matrix instead
1862             glEnable(GL_POLYGON_OFFSET_FILL);
1863             glPolygonOffset(slopeScaledBias, 0.0f);
1864         }
1865         else
1866             glDisable(GL_POLYGON_OFFSET_FILL);
1867 #endif
1868 
1869         constantDepthBias_ = constantBias;
1870         slopeScaledDepthBias_ = slopeScaledBias;
1871         // Force update of the projection matrix shader parameter
1872         ClearParameterSource(SP_CAMERA);
1873     }
1874 }
1875 
SetDepthTest(CompareMode mode)1876 void Graphics::SetDepthTest(CompareMode mode)
1877 {
1878     if (mode != depthTestMode_)
1879     {
1880         glDepthFunc(glCmpFunc[mode]);
1881         depthTestMode_ = mode;
1882     }
1883 }
1884 
SetDepthWrite(bool enable)1885 void Graphics::SetDepthWrite(bool enable)
1886 {
1887     if (enable != depthWrite_)
1888     {
1889         glDepthMask(enable ? GL_TRUE : GL_FALSE);
1890         depthWrite_ = enable;
1891     }
1892 }
1893 
SetFillMode(FillMode mode)1894 void Graphics::SetFillMode(FillMode mode)
1895 {
1896 #ifndef GL_ES_VERSION_2_0
1897     if (mode != fillMode_)
1898     {
1899         glPolygonMode(GL_FRONT_AND_BACK, glFillMode[mode]);
1900         fillMode_ = mode;
1901     }
1902 #endif
1903 }
1904 
SetLineAntiAlias(bool enable)1905 void Graphics::SetLineAntiAlias(bool enable)
1906 {
1907 #ifndef GL_ES_VERSION_2_0
1908     if (enable != lineAntiAlias_)
1909     {
1910         if (enable)
1911             glEnable(GL_LINE_SMOOTH);
1912         else
1913             glDisable(GL_LINE_SMOOTH);
1914         lineAntiAlias_ = enable;
1915     }
1916 #endif
1917 }
1918 
SetScissorTest(bool enable,const Rect & rect,bool borderInclusive)1919 void Graphics::SetScissorTest(bool enable, const Rect& rect, bool borderInclusive)
1920 {
1921     // During some light rendering loops, a full rect is toggled on/off repeatedly.
1922     // Disable scissor in that case to reduce state changes
1923     if (rect.min_.x_ <= 0.0f && rect.min_.y_ <= 0.0f && rect.max_.x_ >= 1.0f && rect.max_.y_ >= 1.0f)
1924         enable = false;
1925 
1926     if (enable)
1927     {
1928         IntVector2 rtSize(GetRenderTargetDimensions());
1929         IntVector2 viewSize(viewport_.Size());
1930         IntVector2 viewPos(viewport_.left_, viewport_.top_);
1931         IntRect intRect;
1932         int expand = borderInclusive ? 1 : 0;
1933 
1934         intRect.left_ = Clamp((int)((rect.min_.x_ + 1.0f) * 0.5f * viewSize.x_) + viewPos.x_, 0, rtSize.x_ - 1);
1935         intRect.top_ = Clamp((int)((-rect.max_.y_ + 1.0f) * 0.5f * viewSize.y_) + viewPos.y_, 0, rtSize.y_ - 1);
1936         intRect.right_ = Clamp((int)((rect.max_.x_ + 1.0f) * 0.5f * viewSize.x_) + viewPos.x_ + expand, 0, rtSize.x_);
1937         intRect.bottom_ = Clamp((int)((-rect.min_.y_ + 1.0f) * 0.5f * viewSize.y_) + viewPos.y_ + expand, 0, rtSize.y_);
1938 
1939         if (intRect.right_ == intRect.left_)
1940             intRect.right_++;
1941         if (intRect.bottom_ == intRect.top_)
1942             intRect.bottom_++;
1943 
1944         if (intRect.right_ < intRect.left_ || intRect.bottom_ < intRect.top_)
1945             enable = false;
1946 
1947         if (enable && scissorRect_ != intRect)
1948         {
1949             // Use Direct3D convention with the vertical coordinates ie. 0 is top
1950             glScissor(intRect.left_, rtSize.y_ - intRect.bottom_, intRect.Width(), intRect.Height());
1951             scissorRect_ = intRect;
1952         }
1953     }
1954     else
1955         scissorRect_ = IntRect::ZERO;
1956 
1957     if (enable != scissorTest_)
1958     {
1959         if (enable)
1960             glEnable(GL_SCISSOR_TEST);
1961         else
1962             glDisable(GL_SCISSOR_TEST);
1963         scissorTest_ = enable;
1964     }
1965 }
1966 
SetScissorTest(bool enable,const IntRect & rect)1967 void Graphics::SetScissorTest(bool enable, const IntRect& rect)
1968 {
1969     IntVector2 rtSize(GetRenderTargetDimensions());
1970     IntVector2 viewPos(viewport_.left_, viewport_.top_);
1971 
1972     if (enable)
1973     {
1974         IntRect intRect;
1975         intRect.left_ = Clamp(rect.left_ + viewPos.x_, 0, rtSize.x_ - 1);
1976         intRect.top_ = Clamp(rect.top_ + viewPos.y_, 0, rtSize.y_ - 1);
1977         intRect.right_ = Clamp(rect.right_ + viewPos.x_, 0, rtSize.x_);
1978         intRect.bottom_ = Clamp(rect.bottom_ + viewPos.y_, 0, rtSize.y_);
1979 
1980         if (intRect.right_ == intRect.left_)
1981             intRect.right_++;
1982         if (intRect.bottom_ == intRect.top_)
1983             intRect.bottom_++;
1984 
1985         if (intRect.right_ < intRect.left_ || intRect.bottom_ < intRect.top_)
1986             enable = false;
1987 
1988         if (enable && scissorRect_ != intRect)
1989         {
1990             // Use Direct3D convention with the vertical coordinates ie. 0 is top
1991             glScissor(intRect.left_, rtSize.y_ - intRect.bottom_, intRect.Width(), intRect.Height());
1992             scissorRect_ = intRect;
1993         }
1994     }
1995     else
1996         scissorRect_ = IntRect::ZERO;
1997 
1998     if (enable != scissorTest_)
1999     {
2000         if (enable)
2001             glEnable(GL_SCISSOR_TEST);
2002         else
2003             glDisable(GL_SCISSOR_TEST);
2004         scissorTest_ = enable;
2005     }
2006 }
2007 
SetClipPlane(bool enable,const Plane & clipPlane,const Matrix3x4 & view,const Matrix4 & projection)2008 void Graphics::SetClipPlane(bool enable, const Plane& clipPlane, const Matrix3x4& view, const Matrix4& projection)
2009 {
2010 #ifndef GL_ES_VERSION_2_0
2011     if (enable != useClipPlane_)
2012     {
2013         if (enable)
2014             glEnable(GL_CLIP_PLANE0);
2015         else
2016             glDisable(GL_CLIP_PLANE0);
2017 
2018         useClipPlane_ = enable;
2019     }
2020 
2021     if (enable)
2022     {
2023         Matrix4 viewProj = projection * view;
2024         clipPlane_ = clipPlane.Transformed(viewProj).ToVector4();
2025 
2026         if (!gl3Support)
2027         {
2028             GLdouble planeData[4];
2029             planeData[0] = clipPlane_.x_;
2030             planeData[1] = clipPlane_.y_;
2031             planeData[2] = clipPlane_.z_;
2032             planeData[3] = clipPlane_.w_;
2033 
2034             glClipPlane(GL_CLIP_PLANE0, &planeData[0]);
2035         }
2036     }
2037 #endif
2038 }
2039 
SetStencilTest(bool enable,CompareMode mode,StencilOp pass,StencilOp fail,StencilOp zFail,unsigned stencilRef,unsigned compareMask,unsigned writeMask)2040 void Graphics::SetStencilTest(bool enable, CompareMode mode, StencilOp pass, StencilOp fail, StencilOp zFail, unsigned stencilRef,
2041     unsigned compareMask, unsigned writeMask)
2042 {
2043 #ifndef GL_ES_VERSION_2_0
2044     if (enable != stencilTest_)
2045     {
2046         if (enable)
2047             glEnable(GL_STENCIL_TEST);
2048         else
2049             glDisable(GL_STENCIL_TEST);
2050         stencilTest_ = enable;
2051     }
2052 
2053     if (enable)
2054     {
2055         if (mode != stencilTestMode_ || stencilRef != stencilRef_ || compareMask != stencilCompareMask_)
2056         {
2057             glStencilFunc(glCmpFunc[mode], stencilRef, compareMask);
2058             stencilTestMode_ = mode;
2059             stencilRef_ = stencilRef;
2060             stencilCompareMask_ = compareMask;
2061         }
2062         if (writeMask != stencilWriteMask_)
2063         {
2064             glStencilMask(writeMask);
2065             stencilWriteMask_ = writeMask;
2066         }
2067         if (pass != stencilPass_ || fail != stencilFail_ || zFail != stencilZFail_)
2068         {
2069             glStencilOp(glStencilOps[fail], glStencilOps[zFail], glStencilOps[pass]);
2070             stencilPass_ = pass;
2071             stencilFail_ = fail;
2072             stencilZFail_ = zFail;
2073         }
2074     }
2075 #endif
2076 }
2077 
IsInitialized() const2078 bool Graphics::IsInitialized() const
2079 {
2080     return window_ != 0;
2081 }
2082 
GetDither() const2083 bool Graphics::GetDither() const
2084 {
2085     return glIsEnabled(GL_DITHER) ? true : false;
2086 }
2087 
IsDeviceLost() const2088 bool Graphics::IsDeviceLost() const
2089 {
2090     // On iOS and tvOS treat window minimization as device loss, as it is forbidden to access OpenGL when minimized
2091 #if defined(IOS) || defined(TVOS)
2092     if (window_ && (SDL_GetWindowFlags(window_) & SDL_WINDOW_MINIMIZED) != 0)
2093         return true;
2094 #endif
2095 
2096     return impl_->context_ == 0;
2097 }
2098 
GetMultiSampleLevels() const2099 PODVector<int> Graphics::GetMultiSampleLevels() const
2100 {
2101     PODVector<int> ret;
2102     // No multisampling always supported
2103     ret.Push(1);
2104 
2105 #ifndef GL_ES_VERSION_2_0
2106     int maxSamples = 0;
2107     glGetIntegerv(GL_MAX_SAMPLES, &maxSamples);
2108     for (int i = 2; i <= maxSamples && i <= 16; i *= 2)
2109         ret.Push(i);
2110 #endif
2111 
2112     return ret;
2113 }
2114 
GetFormat(CompressedFormat format) const2115 unsigned Graphics::GetFormat(CompressedFormat format) const
2116 {
2117     switch (format)
2118     {
2119     case CF_RGBA:
2120         return GL_RGBA;
2121 
2122     case CF_DXT1:
2123         return dxtTextureSupport_ ? GL_COMPRESSED_RGBA_S3TC_DXT1_EXT : 0;
2124 
2125 #if !defined(GL_ES_VERSION_2_0) || defined(__EMSCRIPTEN__)
2126     case CF_DXT3:
2127         return dxtTextureSupport_ ? GL_COMPRESSED_RGBA_S3TC_DXT3_EXT : 0;
2128 
2129     case CF_DXT5:
2130         return dxtTextureSupport_ ? GL_COMPRESSED_RGBA_S3TC_DXT5_EXT : 0;
2131 #endif
2132 #ifdef GL_ES_VERSION_2_0
2133     case CF_ETC1:
2134         return etcTextureSupport_ ? GL_ETC1_RGB8_OES : 0;
2135 
2136     case CF_PVRTC_RGB_2BPP:
2137         return pvrtcTextureSupport_ ? COMPRESSED_RGB_PVRTC_2BPPV1_IMG : 0;
2138 
2139     case CF_PVRTC_RGB_4BPP:
2140         return pvrtcTextureSupport_ ? COMPRESSED_RGB_PVRTC_4BPPV1_IMG : 0;
2141 
2142     case CF_PVRTC_RGBA_2BPP:
2143         return pvrtcTextureSupport_ ? COMPRESSED_RGBA_PVRTC_2BPPV1_IMG : 0;
2144 
2145     case CF_PVRTC_RGBA_4BPP:
2146         return pvrtcTextureSupport_ ? COMPRESSED_RGBA_PVRTC_4BPPV1_IMG : 0;
2147 #endif
2148 
2149     default:
2150         return 0;
2151     }
2152 }
2153 
GetMaxBones()2154 unsigned Graphics::GetMaxBones()
2155 {
2156 #ifdef RPI
2157     // At the moment all RPI GPUs are low powered and only have limited number of uniforms
2158     return 32;
2159 #else
2160     return gl3Support ? 128 : 64;
2161 #endif
2162 }
2163 
GetGL3Support()2164 bool Graphics::GetGL3Support()
2165 {
2166     return gl3Support;
2167 }
2168 
GetShader(ShaderType type,const String & name,const String & defines) const2169 ShaderVariation* Graphics::GetShader(ShaderType type, const String& name, const String& defines) const
2170 {
2171     return GetShader(type, name.CString(), defines.CString());
2172 }
2173 
GetShader(ShaderType type,const char * name,const char * defines) const2174 ShaderVariation* Graphics::GetShader(ShaderType type, const char* name, const char* defines) const
2175 {
2176     if (lastShaderName_ != name || !lastShader_)
2177     {
2178         ResourceCache* cache = GetSubsystem<ResourceCache>();
2179 
2180         String fullShaderName = shaderPath_ + name + shaderExtension_;
2181         // Try to reduce repeated error log prints because of missing shaders
2182         if (lastShaderName_ == name && !cache->Exists(fullShaderName))
2183             return 0;
2184 
2185         lastShader_ = cache->GetResource<Shader>(fullShaderName);
2186         lastShaderName_ = name;
2187     }
2188 
2189     return lastShader_ ? lastShader_->GetVariation(type, defines) : (ShaderVariation*)0;
2190 }
2191 
GetVertexBuffer(unsigned index) const2192 VertexBuffer* Graphics::GetVertexBuffer(unsigned index) const
2193 {
2194     return index < MAX_VERTEX_STREAMS ? vertexBuffers_[index] : 0;
2195 }
2196 
GetShaderProgram() const2197 ShaderProgram* Graphics::GetShaderProgram() const
2198 {
2199     return impl_->shaderProgram_;
2200 }
2201 
GetTextureUnit(const String & name)2202 TextureUnit Graphics::GetTextureUnit(const String& name)
2203 {
2204     HashMap<String, TextureUnit>::Iterator i = textureUnits_.Find(name);
2205     if (i != textureUnits_.End())
2206         return i->second_;
2207     else
2208         return MAX_TEXTURE_UNITS;
2209 }
2210 
GetTextureUnitName(TextureUnit unit)2211 const String& Graphics::GetTextureUnitName(TextureUnit unit)
2212 {
2213     for (HashMap<String, TextureUnit>::Iterator i = textureUnits_.Begin(); i != textureUnits_.End(); ++i)
2214     {
2215         if (i->second_ == unit)
2216             return i->first_;
2217     }
2218     return String::EMPTY;
2219 }
2220 
GetTexture(unsigned index) const2221 Texture* Graphics::GetTexture(unsigned index) const
2222 {
2223     return index < MAX_TEXTURE_UNITS ? textures_[index] : 0;
2224 }
2225 
GetRenderTarget(unsigned index) const2226 RenderSurface* Graphics::GetRenderTarget(unsigned index) const
2227 {
2228     return index < MAX_RENDERTARGETS ? renderTargets_[index] : 0;
2229 }
2230 
GetRenderTargetDimensions() const2231 IntVector2 Graphics::GetRenderTargetDimensions() const
2232 {
2233     int width, height;
2234 
2235     if (renderTargets_[0])
2236     {
2237         width = renderTargets_[0]->GetWidth();
2238         height = renderTargets_[0]->GetHeight();
2239     }
2240     else if (depthStencil_)
2241     {
2242         width = depthStencil_->GetWidth();
2243         height = depthStencil_->GetHeight();
2244     }
2245     else
2246     {
2247         width = width_;
2248         height = height_;
2249     }
2250 
2251     return IntVector2(width, height);
2252 }
2253 
OnWindowResized()2254 void Graphics::OnWindowResized()
2255 {
2256     if (!window_)
2257         return;
2258 
2259     int newWidth, newHeight;
2260 
2261     SDL_GL_GetDrawableSize(window_, &newWidth, &newHeight);
2262     if (newWidth == width_ && newHeight == height_)
2263         return;
2264 
2265     width_ = newWidth;
2266     height_ = newHeight;
2267 
2268     int logicalWidth, logicalHeight;
2269     SDL_GetWindowSize(window_, &logicalWidth, &logicalHeight);
2270     highDPI_ = (width_ != logicalWidth) || (height_ != logicalHeight);
2271 
2272     // Reset rendertargets and viewport for the new screen size. Also clean up any FBO's, as they may be screen size dependent
2273     CleanupFramebuffers();
2274     ResetRenderTargets();
2275 
2276     URHO3D_LOGDEBUGF("Window was resized to %dx%d", width_, height_);
2277 
2278     using namespace ScreenMode;
2279 
2280     VariantMap& eventData = GetEventDataMap();
2281     eventData[P_WIDTH] = width_;
2282     eventData[P_HEIGHT] = height_;
2283     eventData[P_FULLSCREEN] = fullscreen_;
2284     eventData[P_RESIZABLE] = resizable_;
2285     eventData[P_BORDERLESS] = borderless_;
2286     eventData[P_HIGHDPI] = highDPI_;
2287     SendEvent(E_SCREENMODE, eventData);
2288 }
2289 
OnWindowMoved()2290 void Graphics::OnWindowMoved()
2291 {
2292     if (!window_ || fullscreen_)
2293         return;
2294 
2295     int newX, newY;
2296 
2297     SDL_GetWindowPosition(window_, &newX, &newY);
2298     if (newX == position_.x_ && newY == position_.y_)
2299         return;
2300 
2301     position_.x_ = newX;
2302     position_.y_ = newY;
2303 
2304     URHO3D_LOGDEBUGF("Window was moved to %d,%d", position_.x_, position_.y_);
2305 
2306     using namespace WindowPos;
2307 
2308     VariantMap& eventData = GetEventDataMap();
2309     eventData[P_X] = position_.x_;
2310     eventData[P_Y] = position_.y_;
2311     SendEvent(E_WINDOWPOS, eventData);
2312 }
2313 
CleanupRenderSurface(RenderSurface * surface)2314 void Graphics::CleanupRenderSurface(RenderSurface* surface)
2315 {
2316     if (!surface)
2317         return;
2318 
2319     // Flush pending FBO changes first if any
2320     PrepareDraw();
2321 
2322     unsigned currentFBO = impl_->boundFBO_;
2323 
2324     // Go through all FBOs and clean up the surface from them
2325     for (HashMap<unsigned long long, FrameBufferObject>::Iterator i = impl_->frameBuffers_.Begin();
2326          i != impl_->frameBuffers_.End(); ++i)
2327     {
2328         for (unsigned j = 0; j < MAX_RENDERTARGETS; ++j)
2329         {
2330             if (i->second_.colorAttachments_[j] == surface)
2331             {
2332                 if (currentFBO != i->second_.fbo_)
2333                 {
2334                     BindFramebuffer(i->second_.fbo_);
2335                     currentFBO = i->second_.fbo_;
2336                 }
2337                 BindColorAttachment(j, GL_TEXTURE_2D, 0, false);
2338                 i->second_.colorAttachments_[j] = 0;
2339                 // Mark drawbuffer bits to need recalculation
2340                 i->second_.drawBuffers_ = M_MAX_UNSIGNED;
2341             }
2342         }
2343         if (i->second_.depthAttachment_ == surface)
2344         {
2345             if (currentFBO != i->second_.fbo_)
2346             {
2347                 BindFramebuffer(i->second_.fbo_);
2348                 currentFBO = i->second_.fbo_;
2349             }
2350             BindDepthAttachment(0, false);
2351             BindStencilAttachment(0, false);
2352             i->second_.depthAttachment_ = 0;
2353         }
2354     }
2355 
2356     // Restore previously bound FBO now if needed
2357     if (currentFBO != impl_->boundFBO_)
2358         BindFramebuffer(impl_->boundFBO_);
2359 }
2360 
CleanupShaderPrograms(ShaderVariation * variation)2361 void Graphics::CleanupShaderPrograms(ShaderVariation* variation)
2362 {
2363     for (ShaderProgramMap::Iterator i = impl_->shaderPrograms_.Begin(); i != impl_->shaderPrograms_.End();)
2364     {
2365         if (i->second_->GetVertexShader() == variation || i->second_->GetPixelShader() == variation)
2366             i = impl_->shaderPrograms_.Erase(i);
2367         else
2368             ++i;
2369     }
2370 
2371     if (vertexShader_ == variation || pixelShader_ == variation)
2372         impl_->shaderProgram_ = 0;
2373 }
2374 
GetOrCreateConstantBuffer(ShaderType,unsigned bindingIndex,unsigned size)2375 ConstantBuffer* Graphics::GetOrCreateConstantBuffer(ShaderType /*type*/,  unsigned bindingIndex, unsigned size)
2376 {
2377     // Note: shaderType parameter is not used on OpenGL, instead binding index should already use the PS range
2378     // for PS constant buffers
2379 
2380     unsigned key = (bindingIndex << 16) | size;
2381     HashMap<unsigned, SharedPtr<ConstantBuffer> >::Iterator i = impl_->allConstantBuffers_.Find(key);
2382     if (i == impl_->allConstantBuffers_.End())
2383     {
2384         i = impl_->allConstantBuffers_.Insert(MakePair(key, SharedPtr<ConstantBuffer>(new ConstantBuffer(context_))));
2385         i->second_->SetSize(size);
2386     }
2387     return i->second_.Get();
2388 }
2389 
Release(bool clearGPUObjects,bool closeWindow)2390 void Graphics::Release(bool clearGPUObjects, bool closeWindow)
2391 {
2392     if (!window_)
2393         return;
2394 
2395     {
2396         MutexLock lock(gpuObjectMutex_);
2397 
2398         if (clearGPUObjects)
2399         {
2400             // Shutting down: release all GPU objects that still exist
2401             // Shader programs are also GPU objects; clear them first to avoid list modification during iteration
2402             impl_->shaderPrograms_.Clear();
2403 
2404             for (PODVector<GPUObject*>::Iterator i = gpuObjects_.Begin(); i != gpuObjects_.End(); ++i)
2405                 (*i)->Release();
2406             gpuObjects_.Clear();
2407         }
2408         else
2409         {
2410             // We are not shutting down, but recreating the context: mark GPU objects lost
2411             for (PODVector<GPUObject*>::Iterator i = gpuObjects_.Begin(); i != gpuObjects_.End(); ++i)
2412                 (*i)->OnDeviceLost();
2413 
2414             // In this case clear shader programs last so that they do not attempt to delete their OpenGL program
2415             // from a context that may no longer exist
2416             impl_->shaderPrograms_.Clear();
2417 
2418             SendEvent(E_DEVICELOST);
2419         }
2420     }
2421 
2422     CleanupFramebuffers();
2423     impl_->depthTextures_.Clear();
2424 
2425     // End fullscreen mode first to counteract transition and getting stuck problems on OS X
2426 #if defined(__APPLE__) && !defined(IOS) && !defined(TVOS)
2427     if (closeWindow && fullscreen_ && !externalWindow_)
2428         SDL_SetWindowFullscreen(window_, 0);
2429 #endif
2430 
2431     if (impl_->context_)
2432     {
2433         // Do not log this message if we are exiting
2434         if (!clearGPUObjects)
2435             URHO3D_LOGINFO("OpenGL context lost");
2436 
2437         SDL_GL_DeleteContext(impl_->context_);
2438         impl_->context_ = 0;
2439     }
2440 
2441     if (closeWindow)
2442     {
2443         SDL_ShowCursor(SDL_TRUE);
2444 
2445         // Do not destroy external window except when shutting down
2446         if (!externalWindow_ || clearGPUObjects)
2447         {
2448             SDL_DestroyWindow(window_);
2449             window_ = 0;
2450         }
2451     }
2452 }
2453 
Restore()2454 void Graphics::Restore()
2455 {
2456     if (!window_)
2457         return;
2458 
2459 #ifdef __ANDROID__
2460     // On Android the context may be lost behind the scenes as the application is minimized
2461     if (impl_->context_ && !SDL_GL_GetCurrentContext())
2462     {
2463         impl_->context_ = 0;
2464         // Mark GPU objects lost without a current context. In this case they just mark their internal state lost
2465         // but do not perform OpenGL commands to delete the GL objects
2466         Release(false, false);
2467     }
2468 #endif
2469 
2470     // Ensure first that the context exists
2471     if (!impl_->context_)
2472     {
2473         impl_->context_ = SDL_GL_CreateContext(window_);
2474 
2475 #ifndef GL_ES_VERSION_2_0
2476         // If we're trying to use OpenGL 3, but context creation fails, retry with 2
2477         if (!forceGL2_ && !impl_->context_)
2478         {
2479             forceGL2_ = true;
2480             SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
2481             SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
2482             SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, 0);
2483             impl_->context_ = SDL_GL_CreateContext(window_);
2484         }
2485 #endif
2486 
2487 #if defined(IOS) || defined(TVOS)
2488         glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&impl_->systemFBO_);
2489 #endif
2490 
2491         if (!impl_->context_)
2492         {
2493             URHO3D_LOGERRORF("Could not create OpenGL context, root cause '%s'", SDL_GetError());
2494             return;
2495         }
2496 
2497         // Clear cached extensions string from the previous context
2498         extensions.Clear();
2499 
2500         // Initialize OpenGL extensions library (desktop only)
2501 #ifndef GL_ES_VERSION_2_0
2502         GLenum err = glewInit();
2503         if (GLEW_OK != err)
2504         {
2505             URHO3D_LOGERRORF("Could not initialize OpenGL extensions, root cause: '%s'", glewGetErrorString(err));
2506             return;
2507         }
2508 
2509         if (!forceGL2_ && GLEW_VERSION_3_2)
2510         {
2511             gl3Support = true;
2512             apiName_ = "GL3";
2513 
2514             // Create and bind a vertex array object that will stay in use throughout
2515             unsigned vertexArrayObject;
2516             glGenVertexArrays(1, &vertexArrayObject);
2517             glBindVertexArray(vertexArrayObject);
2518         }
2519         else if (GLEW_VERSION_2_0)
2520         {
2521             if (!GLEW_EXT_framebuffer_object || !GLEW_EXT_packed_depth_stencil)
2522             {
2523                 URHO3D_LOGERROR("EXT_framebuffer_object and EXT_packed_depth_stencil OpenGL extensions are required");
2524                 return;
2525             }
2526 
2527             gl3Support = false;
2528             apiName_ = "GL2";
2529         }
2530         else
2531         {
2532             URHO3D_LOGERROR("OpenGL 2.0 is required");
2533             return;
2534         }
2535 
2536         // Enable seamless cubemap if possible
2537         // Note: even though we check the extension, this can lead to software fallback on some old GPU's
2538         // See https://github.com/urho3d/Urho3D/issues/1380 or
2539         // http://distrustsimplicity.net/articles/gl_texture_cube_map_seamless-on-os-x/
2540         // In case of trouble or for wanting maximum compatibility, simply remove the glEnable below.
2541         if (gl3Support || GLEW_ARB_seamless_cube_map)
2542             glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
2543 #endif
2544 
2545         // Set up texture data read/write alignment. It is important that this is done before uploading any texture data
2546         glPixelStorei(GL_PACK_ALIGNMENT, 1);
2547         glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
2548         ResetCachedState();
2549     }
2550 
2551     {
2552         MutexLock lock(gpuObjectMutex_);
2553 
2554         for (PODVector<GPUObject*>::Iterator i = gpuObjects_.Begin(); i != gpuObjects_.End(); ++i)
2555             (*i)->OnDeviceReset();
2556     }
2557 
2558     SendEvent(E_DEVICERESET);
2559 }
2560 
MarkFBODirty()2561 void Graphics::MarkFBODirty()
2562 {
2563     impl_->fboDirty_ = true;
2564 }
2565 
SetVBO(unsigned object)2566 void Graphics::SetVBO(unsigned object)
2567 {
2568     if (impl_->boundVBO_ != object)
2569     {
2570         if (object)
2571             glBindBuffer(GL_ARRAY_BUFFER, object);
2572         impl_->boundVBO_ = object;
2573     }
2574 }
2575 
SetUBO(unsigned object)2576 void Graphics::SetUBO(unsigned object)
2577 {
2578 #ifndef GL_ES_VERSION_2_0
2579     if (impl_->boundUBO_ != object)
2580     {
2581         if (object)
2582             glBindBuffer(GL_UNIFORM_BUFFER, object);
2583         impl_->boundUBO_ = object;
2584     }
2585 #endif
2586 }
2587 
GetAlphaFormat()2588 unsigned Graphics::GetAlphaFormat()
2589 {
2590 #ifndef GL_ES_VERSION_2_0
2591     // Alpha format is deprecated on OpenGL 3+
2592     if (gl3Support)
2593         return GL_R8;
2594 #endif
2595     return GL_ALPHA;
2596 }
2597 
GetLuminanceFormat()2598 unsigned Graphics::GetLuminanceFormat()
2599 {
2600 #ifndef GL_ES_VERSION_2_0
2601     // Luminance format is deprecated on OpenGL 3+
2602     if (gl3Support)
2603         return GL_R8;
2604 #endif
2605     return GL_LUMINANCE;
2606 }
2607 
GetLuminanceAlphaFormat()2608 unsigned Graphics::GetLuminanceAlphaFormat()
2609 {
2610 #ifndef GL_ES_VERSION_2_0
2611     // Luminance alpha format is deprecated on OpenGL 3+
2612     if (gl3Support)
2613         return GL_RG8;
2614 #endif
2615     return GL_LUMINANCE_ALPHA;
2616 }
2617 
GetRGBFormat()2618 unsigned Graphics::GetRGBFormat()
2619 {
2620     return GL_RGB;
2621 }
2622 
GetRGBAFormat()2623 unsigned Graphics::GetRGBAFormat()
2624 {
2625     return GL_RGBA;
2626 }
2627 
GetRGBA16Format()2628 unsigned Graphics::GetRGBA16Format()
2629 {
2630 #ifndef GL_ES_VERSION_2_0
2631     return GL_RGBA16;
2632 #else
2633     return GL_RGBA;
2634 #endif
2635 }
2636 
GetRGBAFloat16Format()2637 unsigned Graphics::GetRGBAFloat16Format()
2638 {
2639 #ifndef GL_ES_VERSION_2_0
2640     return GL_RGBA16F_ARB;
2641 #else
2642     return GL_RGBA;
2643 #endif
2644 }
2645 
GetRGBAFloat32Format()2646 unsigned Graphics::GetRGBAFloat32Format()
2647 {
2648 #ifndef GL_ES_VERSION_2_0
2649     return GL_RGBA32F_ARB;
2650 #else
2651     return GL_RGBA;
2652 #endif
2653 }
2654 
GetRG16Format()2655 unsigned Graphics::GetRG16Format()
2656 {
2657 #ifndef GL_ES_VERSION_2_0
2658     return GL_RG16;
2659 #else
2660     return GL_RGBA;
2661 #endif
2662 }
2663 
GetRGFloat16Format()2664 unsigned Graphics::GetRGFloat16Format()
2665 {
2666 #ifndef GL_ES_VERSION_2_0
2667     return GL_RG16F;
2668 #else
2669     return GL_RGBA;
2670 #endif
2671 }
2672 
GetRGFloat32Format()2673 unsigned Graphics::GetRGFloat32Format()
2674 {
2675 #ifndef GL_ES_VERSION_2_0
2676     return GL_RG32F;
2677 #else
2678     return GL_RGBA;
2679 #endif
2680 }
2681 
GetFloat16Format()2682 unsigned Graphics::GetFloat16Format()
2683 {
2684 #ifndef GL_ES_VERSION_2_0
2685     return GL_R16F;
2686 #else
2687     return GL_LUMINANCE;
2688 #endif
2689 }
2690 
GetFloat32Format()2691 unsigned Graphics::GetFloat32Format()
2692 {
2693 #ifndef GL_ES_VERSION_2_0
2694     return GL_R32F;
2695 #else
2696     return GL_LUMINANCE;
2697 #endif
2698 }
2699 
GetLinearDepthFormat()2700 unsigned Graphics::GetLinearDepthFormat()
2701 {
2702 #ifndef GL_ES_VERSION_2_0
2703     // OpenGL 3 can use different color attachment formats
2704     if (gl3Support)
2705         return GL_R32F;
2706 #endif
2707     // OpenGL 2 requires color attachments to have the same format, therefore encode deferred depth to RGBA manually
2708     // if not using a readable hardware depth texture
2709     return GL_RGBA;
2710 }
2711 
GetDepthStencilFormat()2712 unsigned Graphics::GetDepthStencilFormat()
2713 {
2714 #ifndef GL_ES_VERSION_2_0
2715     return GL_DEPTH24_STENCIL8_EXT;
2716 #else
2717     return glesDepthStencilFormat;
2718 #endif
2719 }
2720 
GetReadableDepthFormat()2721 unsigned Graphics::GetReadableDepthFormat()
2722 {
2723 #ifndef GL_ES_VERSION_2_0
2724     return GL_DEPTH_COMPONENT24;
2725 #else
2726     return glesReadableDepthFormat;
2727 #endif
2728 }
2729 
GetFormat(const String & formatName)2730 unsigned Graphics::GetFormat(const String& formatName)
2731 {
2732     String nameLower = formatName.ToLower().Trimmed();
2733 
2734     if (nameLower == "a")
2735         return GetAlphaFormat();
2736     if (nameLower == "l")
2737         return GetLuminanceFormat();
2738     if (nameLower == "la")
2739         return GetLuminanceAlphaFormat();
2740     if (nameLower == "rgb")
2741         return GetRGBFormat();
2742     if (nameLower == "rgba")
2743         return GetRGBAFormat();
2744     if (nameLower == "rgba16")
2745         return GetRGBA16Format();
2746     if (nameLower == "rgba16f")
2747         return GetRGBAFloat16Format();
2748     if (nameLower == "rgba32f")
2749         return GetRGBAFloat32Format();
2750     if (nameLower == "rg16")
2751         return GetRG16Format();
2752     if (nameLower == "rg16f")
2753         return GetRGFloat16Format();
2754     if (nameLower == "rg32f")
2755         return GetRGFloat32Format();
2756     if (nameLower == "r16f")
2757         return GetFloat16Format();
2758     if (nameLower == "r32f" || nameLower == "float")
2759         return GetFloat32Format();
2760     if (nameLower == "lineardepth" || nameLower == "depth")
2761         return GetLinearDepthFormat();
2762     if (nameLower == "d24s8")
2763         return GetDepthStencilFormat();
2764     if (nameLower == "readabledepth" || nameLower == "hwdepth")
2765         return GetReadableDepthFormat();
2766 
2767     return GetRGBFormat();
2768 }
2769 
CheckFeatureSupport()2770 void Graphics::CheckFeatureSupport()
2771 {
2772     // Check supported features: light pre-pass, deferred rendering and hardware depth texture
2773     lightPrepassSupport_ = false;
2774     deferredSupport_ = false;
2775 
2776 #ifndef GL_ES_VERSION_2_0
2777     int numSupportedRTs = 1;
2778     if (gl3Support)
2779     {
2780         // Work around GLEW failure to check extensions properly from a GL3 context
2781         instancingSupport_ = glDrawElementsInstanced != 0 && glVertexAttribDivisor != 0;
2782         dxtTextureSupport_ = true;
2783         anisotropySupport_ = true;
2784         sRGBSupport_ = true;
2785         sRGBWriteSupport_ = true;
2786 
2787         glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &numSupportedRTs);
2788     }
2789     else
2790     {
2791         instancingSupport_ = GLEW_ARB_instanced_arrays != 0;
2792         dxtTextureSupport_ = GLEW_EXT_texture_compression_s3tc != 0;
2793         anisotropySupport_ = GLEW_EXT_texture_filter_anisotropic != 0;
2794         sRGBSupport_ = GLEW_EXT_texture_sRGB != 0;
2795         sRGBWriteSupport_ = GLEW_EXT_framebuffer_sRGB != 0;
2796 
2797         glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS_EXT, &numSupportedRTs);
2798     }
2799 
2800     // Must support 2 rendertargets for light pre-pass, and 4 for deferred
2801     if (numSupportedRTs >= 2)
2802         lightPrepassSupport_ = true;
2803     if (numSupportedRTs >= 4)
2804         deferredSupport_ = true;
2805 
2806 #if defined(__APPLE__) && !defined(IOS) && !defined(TVOS)
2807     // On macOS check for an Intel driver and use shadow map RGBA dummy color textures, because mixing
2808     // depth-only FBO rendering and backbuffer rendering will bug, resulting in a black screen in full
2809     // screen mode, and incomplete shadow maps in windowed mode
2810     String renderer((const char*)glGetString(GL_RENDERER));
2811     if (renderer.Contains("Intel", false))
2812         dummyColorFormat_ = GetRGBAFormat();
2813 #endif
2814 #else
2815     // Check for supported compressed texture formats
2816 #ifdef __EMSCRIPTEN__
2817     dxtTextureSupport_ = CheckExtension("WEBGL_compressed_texture_s3tc"); // https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_s3tc/
2818     etcTextureSupport_ = CheckExtension("WEBGL_compressed_texture_etc1"); // https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_etc1/
2819     pvrtcTextureSupport_ = CheckExtension("WEBGL_compressed_texture_pvrtc"); // https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_pvrtc/
2820     // Instancing is in core in WebGL 2, so the extension may not be present anymore. In WebGL 1, find https://www.khronos.org/registry/webgl/extensions/ANGLE_instanced_arrays/
2821     // TODO: In the distant future, this may break if WebGL 3 is introduced, so either improve the GL_VERSION parsing here, or keep track of which WebGL version we attempted to initialize.
2822     instancingSupport_ = (strstr((const char *)glGetString(GL_VERSION), "WebGL 2.") != 0) || CheckExtension("ANGLE_instanced_arrays");
2823 #else
2824     dxtTextureSupport_ = CheckExtension("EXT_texture_compression_dxt1");
2825     etcTextureSupport_ = CheckExtension("OES_compressed_ETC1_RGB8_texture");
2826     pvrtcTextureSupport_ = CheckExtension("IMG_texture_compression_pvrtc");
2827 #endif
2828 
2829     // Check for best supported depth renderbuffer format for GLES2
2830     if (CheckExtension("GL_OES_depth24"))
2831         glesDepthStencilFormat = GL_DEPTH_COMPONENT24_OES;
2832     if (CheckExtension("GL_OES_packed_depth_stencil"))
2833         glesDepthStencilFormat = GL_DEPTH24_STENCIL8_OES;
2834     #ifdef __EMSCRIPTEN__
2835     if (!CheckExtension("WEBGL_depth_texture"))
2836 #else
2837     if (!CheckExtension("GL_OES_depth_texture"))
2838 #endif
2839     {
2840         shadowMapFormat_ = 0;
2841         hiresShadowMapFormat_ = 0;
2842         glesReadableDepthFormat = 0;
2843     }
2844     else
2845     {
2846 #if defined(IOS) || defined(TVOS)
2847         // iOS hack: depth renderbuffer seems to fail, so use depth textures for everything if supported
2848         glesDepthStencilFormat = GL_DEPTH_COMPONENT;
2849 #endif
2850         shadowMapFormat_ = GL_DEPTH_COMPONENT;
2851         hiresShadowMapFormat_ = 0;
2852         // WebGL shadow map rendering seems to be extremely slow without an attached dummy color texture
2853         #ifdef __EMSCRIPTEN__
2854         dummyColorFormat_ = GetRGBAFormat();
2855 #endif
2856     }
2857 #endif
2858 
2859     // Consider OpenGL shadows always hardware sampled, if supported at all
2860     hardwareShadowSupport_ = shadowMapFormat_ != 0;
2861 }
2862 
PrepareDraw()2863 void Graphics::PrepareDraw()
2864 {
2865 #ifndef GL_ES_VERSION_2_0
2866     if (gl3Support)
2867     {
2868         for (PODVector<ConstantBuffer*>::Iterator i = impl_->dirtyConstantBuffers_.Begin(); i != impl_->dirtyConstantBuffers_.End(); ++i)
2869             (*i)->Apply();
2870         impl_->dirtyConstantBuffers_.Clear();
2871     }
2872 #endif
2873 
2874     if (impl_->fboDirty_)
2875     {
2876         impl_->fboDirty_ = false;
2877 
2878         // First check if no framebuffer is needed. In that case simply return to backbuffer rendering
2879         bool noFbo = !depthStencil_;
2880         if (noFbo)
2881         {
2882             for (unsigned i = 0; i < MAX_RENDERTARGETS; ++i)
2883             {
2884                 if (renderTargets_[i])
2885                 {
2886                     noFbo = false;
2887                     break;
2888                 }
2889             }
2890         }
2891 
2892         if (noFbo)
2893         {
2894             if (impl_->boundFBO_ != impl_->systemFBO_)
2895             {
2896                 BindFramebuffer(impl_->systemFBO_);
2897                 impl_->boundFBO_ = impl_->systemFBO_;
2898             }
2899 
2900 #ifndef GL_ES_VERSION_2_0
2901             // Disable/enable sRGB write
2902             if (sRGBWriteSupport_)
2903             {
2904                 bool sRGBWrite = sRGB_;
2905                 if (sRGBWrite != impl_->sRGBWrite_)
2906                 {
2907                     if (sRGBWrite)
2908                         glEnable(GL_FRAMEBUFFER_SRGB_EXT);
2909                     else
2910                         glDisable(GL_FRAMEBUFFER_SRGB_EXT);
2911                     impl_->sRGBWrite_ = sRGBWrite;
2912                 }
2913             }
2914 #endif
2915 
2916             return;
2917         }
2918 
2919         // Search for a new framebuffer based on format & size, or create new
2920         IntVector2 rtSize = Graphics::GetRenderTargetDimensions();
2921         unsigned format = 0;
2922         if (renderTargets_[0])
2923             format = renderTargets_[0]->GetParentTexture()->GetFormat();
2924         else if (depthStencil_)
2925             format = depthStencil_->GetParentTexture()->GetFormat();
2926 
2927         unsigned long long fboKey = (rtSize.x_ << 16 | rtSize.y_) | (((unsigned long long)format) << 32);
2928 
2929         HashMap<unsigned long long, FrameBufferObject>::Iterator i = impl_->frameBuffers_.Find(fboKey);
2930         if (i == impl_->frameBuffers_.End())
2931         {
2932             FrameBufferObject newFbo;
2933             newFbo.fbo_ = CreateFramebuffer();
2934             i = impl_->frameBuffers_.Insert(MakePair(fboKey, newFbo));
2935         }
2936 
2937         if (impl_->boundFBO_ != i->second_.fbo_)
2938         {
2939             BindFramebuffer(i->second_.fbo_);
2940             impl_->boundFBO_ = i->second_.fbo_;
2941         }
2942 
2943 #ifndef GL_ES_VERSION_2_0
2944         // Setup readbuffers & drawbuffers if needed
2945         if (i->second_.readBuffers_ != GL_NONE)
2946         {
2947             glReadBuffer(GL_NONE);
2948             i->second_.readBuffers_ = GL_NONE;
2949         }
2950 
2951         // Calculate the bit combination of non-zero color rendertargets to first check if the combination changed
2952         unsigned newDrawBuffers = 0;
2953         for (unsigned j = 0; j < MAX_RENDERTARGETS; ++j)
2954         {
2955             if (renderTargets_[j])
2956                 newDrawBuffers |= 1 << j;
2957         }
2958 
2959         if (newDrawBuffers != i->second_.drawBuffers_)
2960         {
2961             // Check for no color rendertargets (depth rendering only)
2962             if (!newDrawBuffers)
2963                 glDrawBuffer(GL_NONE);
2964             else
2965             {
2966                 int drawBufferIds[MAX_RENDERTARGETS];
2967                 unsigned drawBufferCount = 0;
2968 
2969                 for (unsigned j = 0; j < MAX_RENDERTARGETS; ++j)
2970                 {
2971                     if (renderTargets_[j])
2972                     {
2973                         if (!gl3Support)
2974                             drawBufferIds[drawBufferCount++] = GL_COLOR_ATTACHMENT0_EXT + j;
2975                         else
2976                             drawBufferIds[drawBufferCount++] = GL_COLOR_ATTACHMENT0 + j;
2977                     }
2978                 }
2979                 glDrawBuffers(drawBufferCount, (const GLenum*)drawBufferIds);
2980             }
2981 
2982             i->second_.drawBuffers_ = newDrawBuffers;
2983         }
2984 #endif
2985 
2986         for (unsigned j = 0; j < MAX_RENDERTARGETS; ++j)
2987         {
2988             if (renderTargets_[j])
2989             {
2990                 Texture* texture = renderTargets_[j]->GetParentTexture();
2991 
2992                 // Bind either a renderbuffer or texture, depending on what is available
2993                 unsigned renderBufferID = renderTargets_[j]->GetRenderBuffer();
2994                 if (!renderBufferID)
2995                 {
2996                     // If texture's parameters are dirty, update before attaching
2997                     if (texture->GetParametersDirty())
2998                     {
2999                         SetTextureForUpdate(texture);
3000                         texture->UpdateParameters();
3001                         SetTexture(0, 0);
3002                     }
3003 
3004                     if (i->second_.colorAttachments_[j] != renderTargets_[j])
3005                     {
3006                         BindColorAttachment(j, renderTargets_[j]->GetTarget(), texture->GetGPUObjectName(), false);
3007                         i->second_.colorAttachments_[j] = renderTargets_[j];
3008                     }
3009                 }
3010                 else
3011                 {
3012                     if (i->second_.colorAttachments_[j] != renderTargets_[j])
3013                     {
3014                         BindColorAttachment(j, renderTargets_[j]->GetTarget(), renderBufferID, true);
3015                         i->second_.colorAttachments_[j] = renderTargets_[j];
3016                     }
3017                 }
3018             }
3019             else
3020             {
3021                 if (i->second_.colorAttachments_[j])
3022                 {
3023                     BindColorAttachment(j, GL_TEXTURE_2D, 0, false);
3024                     i->second_.colorAttachments_[j] = 0;
3025                 }
3026             }
3027         }
3028 
3029         if (depthStencil_)
3030         {
3031             // Bind either a renderbuffer or a depth texture, depending on what is available
3032             Texture* texture = depthStencil_->GetParentTexture();
3033 #ifndef GL_ES_VERSION_2_0
3034             bool hasStencil = texture->GetFormat() == GL_DEPTH24_STENCIL8_EXT;
3035 #else
3036             bool hasStencil = texture->GetFormat() == GL_DEPTH24_STENCIL8_OES;
3037 #endif
3038             unsigned renderBufferID = depthStencil_->GetRenderBuffer();
3039             if (!renderBufferID)
3040             {
3041                 // If texture's parameters are dirty, update before attaching
3042                 if (texture->GetParametersDirty())
3043                 {
3044                     SetTextureForUpdate(texture);
3045                     texture->UpdateParameters();
3046                     SetTexture(0, 0);
3047                 }
3048 
3049                 if (i->second_.depthAttachment_ != depthStencil_)
3050                 {
3051                     BindDepthAttachment(texture->GetGPUObjectName(), false);
3052                     BindStencilAttachment(hasStencil ? texture->GetGPUObjectName() : 0, false);
3053                     i->second_.depthAttachment_ = depthStencil_;
3054                 }
3055             }
3056             else
3057             {
3058                 if (i->second_.depthAttachment_ != depthStencil_)
3059                 {
3060                     BindDepthAttachment(renderBufferID, true);
3061                     BindStencilAttachment(hasStencil ? renderBufferID : 0, true);
3062                     i->second_.depthAttachment_ = depthStencil_;
3063                 }
3064             }
3065         }
3066         else
3067         {
3068             if (i->second_.depthAttachment_)
3069             {
3070                 BindDepthAttachment(0, false);
3071                 BindStencilAttachment(0, false);
3072                 i->second_.depthAttachment_ = 0;
3073             }
3074         }
3075 
3076 #ifndef GL_ES_VERSION_2_0
3077         // Disable/enable sRGB write
3078         if (sRGBWriteSupport_)
3079         {
3080             bool sRGBWrite = renderTargets_[0] ? renderTargets_[0]->GetParentTexture()->GetSRGB() : sRGB_;
3081             if (sRGBWrite != impl_->sRGBWrite_)
3082             {
3083                 if (sRGBWrite)
3084                     glEnable(GL_FRAMEBUFFER_SRGB_EXT);
3085                 else
3086                     glDisable(GL_FRAMEBUFFER_SRGB_EXT);
3087                 impl_->sRGBWrite_ = sRGBWrite;
3088             }
3089         }
3090 #endif
3091     }
3092 
3093     if (impl_->vertexBuffersDirty_)
3094     {
3095         // Go through currently bound vertex buffers and set the attribute pointers that are available & required
3096         // Use reverse order so that elements from higher index buffers will override lower index buffers
3097         unsigned assignedLocations = 0;
3098 
3099         for (unsigned i = MAX_VERTEX_STREAMS - 1; i < MAX_VERTEX_STREAMS; --i)
3100         {
3101             VertexBuffer* buffer = vertexBuffers_[i];
3102             // Beware buffers with missing OpenGL objects, as binding a zero buffer object means accessing CPU memory for vertex data,
3103             // in which case the pointer will be invalid and cause a crash
3104             if (!buffer || !buffer->GetGPUObjectName() || !impl_->vertexAttributes_)
3105                 continue;
3106 
3107             const PODVector<VertexElement>& elements = buffer->GetElements();
3108 
3109             for (PODVector<VertexElement>::ConstIterator j = elements.Begin(); j != elements.End(); ++j)
3110             {
3111                 const VertexElement& element = *j;
3112                 HashMap<Pair<unsigned char, unsigned char>, unsigned>::ConstIterator k =
3113                     impl_->vertexAttributes_->Find(MakePair((unsigned char)element.semantic_, element.index_));
3114 
3115                 if (k != impl_->vertexAttributes_->End())
3116                 {
3117                     unsigned location = k->second_;
3118                     unsigned locationMask = 1 << location;
3119                     if (assignedLocations & locationMask)
3120                         continue; // Already assigned by higher index vertex buffer
3121                     assignedLocations |= locationMask;
3122 
3123                     // Enable attribute if not enabled yet
3124                     if (!(impl_->enabledVertexAttributes_ & locationMask))
3125                     {
3126                         glEnableVertexAttribArray(location);
3127                         impl_->enabledVertexAttributes_ |= locationMask;
3128                     }
3129 
3130                     // Enable/disable instancing divisor as necessary
3131                     unsigned dataStart = element.offset_;
3132                     if (element.perInstance_)
3133                     {
3134                         dataStart += impl_->lastInstanceOffset_ * buffer->GetVertexSize();
3135                         if (!(impl_->instancingVertexAttributes_ & locationMask))
3136                         {
3137                             SetVertexAttribDivisor(location, 1);
3138                             impl_->instancingVertexAttributes_ |= locationMask;
3139                         }
3140                     }
3141                     else
3142                     {
3143                         if (impl_->instancingVertexAttributes_ & locationMask)
3144                         {
3145                             SetVertexAttribDivisor(location, 0);
3146                             impl_->instancingVertexAttributes_ &= ~locationMask;
3147                         }
3148                     }
3149 
3150                     SetVBO(buffer->GetGPUObjectName());
3151                     glVertexAttribPointer(location, glElementComponents[element.type_], glElementTypes[element.type_],
3152                         element.type_ == TYPE_UBYTE4_NORM ? GL_TRUE : GL_FALSE, (unsigned)buffer->GetVertexSize(),
3153                         (const void *)(size_t)dataStart);
3154                 }
3155             }
3156         }
3157 
3158         // Finally disable unnecessary vertex attributes
3159         unsigned disableVertexAttributes = impl_->enabledVertexAttributes_ & (~impl_->usedVertexAttributes_);
3160         unsigned location = 0;
3161         while (disableVertexAttributes)
3162         {
3163             if (disableVertexAttributes & 1)
3164             {
3165                 glDisableVertexAttribArray(location);
3166                 impl_->enabledVertexAttributes_ &= ~(1 << location);
3167             }
3168             ++location;
3169             disableVertexAttributes >>= 1;
3170         }
3171 
3172         impl_->vertexBuffersDirty_ = false;
3173     }
3174 }
3175 
CleanupFramebuffers()3176 void Graphics::CleanupFramebuffers()
3177 {
3178     if (!IsDeviceLost())
3179     {
3180         BindFramebuffer(impl_->systemFBO_);
3181         impl_->boundFBO_ = impl_->systemFBO_;
3182         impl_->fboDirty_ = true;
3183 
3184         for (HashMap<unsigned long long, FrameBufferObject>::Iterator i = impl_->frameBuffers_.Begin();
3185              i != impl_->frameBuffers_.End(); ++i)
3186             DeleteFramebuffer(i->second_.fbo_);
3187 
3188         if (impl_->resolveSrcFBO_)
3189             DeleteFramebuffer(impl_->resolveSrcFBO_);
3190         if (impl_->resolveDestFBO_)
3191             DeleteFramebuffer(impl_->resolveDestFBO_);
3192     }
3193     else
3194     {
3195         impl_->boundFBO_ = 0;
3196         impl_->resolveSrcFBO_ = 0;
3197         impl_->resolveDestFBO_ = 0;
3198     }
3199 
3200     impl_->frameBuffers_.Clear();
3201 }
3202 
ResetCachedState()3203 void Graphics::ResetCachedState()
3204 {
3205     for (unsigned i = 0; i < MAX_VERTEX_STREAMS; ++i)
3206         vertexBuffers_[i] = 0;
3207 
3208     for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
3209     {
3210         textures_[i] = 0;
3211         impl_->textureTypes_[i] = 0;
3212     }
3213 
3214     for (unsigned i = 0; i < MAX_RENDERTARGETS; ++i)
3215         renderTargets_[i] = 0;
3216 
3217     depthStencil_ = 0;
3218     viewport_ = IntRect(0, 0, 0, 0);
3219     indexBuffer_ = 0;
3220     vertexShader_ = 0;
3221     pixelShader_ = 0;
3222     blendMode_ = BLEND_REPLACE;
3223     alphaToCoverage_ = false;
3224     colorWrite_ = true;
3225     cullMode_ = CULL_NONE;
3226     constantDepthBias_ = 0.0f;
3227     slopeScaledDepthBias_ = 0.0f;
3228     depthTestMode_ = CMP_ALWAYS;
3229     depthWrite_ = false;
3230     lineAntiAlias_ = false;
3231     fillMode_ = FILL_SOLID;
3232     scissorTest_ = false;
3233     scissorRect_ = IntRect::ZERO;
3234     stencilTest_ = false;
3235     stencilTestMode_ = CMP_ALWAYS;
3236     stencilPass_ = OP_KEEP;
3237     stencilFail_ = OP_KEEP;
3238     stencilZFail_ = OP_KEEP;
3239     stencilRef_ = 0;
3240     stencilCompareMask_ = M_MAX_UNSIGNED;
3241     stencilWriteMask_ = M_MAX_UNSIGNED;
3242     useClipPlane_ = false;
3243     impl_->shaderProgram_ = 0;
3244     impl_->lastInstanceOffset_ = 0;
3245     impl_->activeTexture_ = 0;
3246     impl_->enabledVertexAttributes_ = 0;
3247     impl_->usedVertexAttributes_ = 0;
3248     impl_->instancingVertexAttributes_ = 0;
3249     impl_->boundFBO_ = impl_->systemFBO_;
3250     impl_->boundVBO_ = 0;
3251     impl_->boundUBO_ = 0;
3252     impl_->sRGBWrite_ = false;
3253 
3254     // Set initial state to match Direct3D
3255     if (impl_->context_)
3256     {
3257         glEnable(GL_DEPTH_TEST);
3258         SetCullMode(CULL_CCW);
3259         SetDepthTest(CMP_LESSEQUAL);
3260         SetDepthWrite(true);
3261     }
3262 
3263     for (unsigned i = 0; i < MAX_SHADER_PARAMETER_GROUPS * 2; ++i)
3264         impl_->constantBuffers_[i] = 0;
3265     impl_->dirtyConstantBuffers_.Clear();
3266 }
3267 
SetTextureUnitMappings()3268 void Graphics::SetTextureUnitMappings()
3269 {
3270     textureUnits_["DiffMap"] = TU_DIFFUSE;
3271     textureUnits_["DiffCubeMap"] = TU_DIFFUSE;
3272     textureUnits_["AlbedoBuffer"] = TU_ALBEDOBUFFER;
3273     textureUnits_["NormalMap"] = TU_NORMAL;
3274     textureUnits_["NormalBuffer"] = TU_NORMALBUFFER;
3275     textureUnits_["SpecMap"] = TU_SPECULAR;
3276     textureUnits_["EmissiveMap"] = TU_EMISSIVE;
3277     textureUnits_["EnvMap"] = TU_ENVIRONMENT;
3278     textureUnits_["EnvCubeMap"] = TU_ENVIRONMENT;
3279     textureUnits_["LightRampMap"] = TU_LIGHTRAMP;
3280     textureUnits_["LightSpotMap"] = TU_LIGHTSHAPE;
3281     textureUnits_["LightCubeMap"] = TU_LIGHTSHAPE;
3282     textureUnits_["ShadowMap"] = TU_SHADOWMAP;
3283 #ifndef GL_ES_VERSION_2_0
3284     textureUnits_["VolumeMap"] = TU_VOLUMEMAP;
3285     textureUnits_["FaceSelectCubeMap"] = TU_FACESELECT;
3286     textureUnits_["IndirectionCubeMap"] = TU_INDIRECTION;
3287     textureUnits_["DepthBuffer"] = TU_DEPTHBUFFER;
3288     textureUnits_["LightBuffer"] = TU_LIGHTBUFFER;
3289     textureUnits_["ZoneCubeMap"] = TU_ZONE;
3290     textureUnits_["ZoneVolumeMap"] = TU_ZONE;
3291 #endif
3292 }
3293 
CreateFramebuffer()3294 unsigned Graphics::CreateFramebuffer()
3295 {
3296     unsigned newFbo = 0;
3297 #ifndef GL_ES_VERSION_2_0
3298     if (!gl3Support)
3299         glGenFramebuffersEXT(1, &newFbo);
3300     else
3301 #endif
3302         glGenFramebuffers(1, &newFbo);
3303     return newFbo;
3304 }
3305 
DeleteFramebuffer(unsigned fbo)3306 void Graphics::DeleteFramebuffer(unsigned fbo)
3307 {
3308 #ifndef GL_ES_VERSION_2_0
3309     if (!gl3Support)
3310         glDeleteFramebuffersEXT(1, &fbo);
3311     else
3312 #endif
3313         glDeleteFramebuffers(1, &fbo);
3314 }
3315 
BindFramebuffer(unsigned fbo)3316 void Graphics::BindFramebuffer(unsigned fbo)
3317 {
3318 #ifndef GL_ES_VERSION_2_0
3319     if (!gl3Support)
3320         glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
3321     else
3322 #endif
3323         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
3324 }
3325 
BindColorAttachment(unsigned index,unsigned target,unsigned object,bool isRenderBuffer)3326 void Graphics::BindColorAttachment(unsigned index, unsigned target, unsigned object, bool isRenderBuffer)
3327 {
3328     if (!object)
3329         isRenderBuffer = false;
3330 
3331 #ifndef GL_ES_VERSION_2_0
3332     if (!gl3Support)
3333     {
3334         if (!isRenderBuffer)
3335             glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT + index, target, object, 0);
3336         else
3337             glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT + index, GL_RENDERBUFFER_EXT, object);
3338     }
3339     else
3340 #endif
3341     {
3342         if (!isRenderBuffer)
3343             glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + index, target, object, 0);
3344         else
3345             glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + index, GL_RENDERBUFFER, object);
3346     }
3347 }
3348 
BindDepthAttachment(unsigned object,bool isRenderBuffer)3349 void Graphics::BindDepthAttachment(unsigned object, bool isRenderBuffer)
3350 {
3351     if (!object)
3352         isRenderBuffer = false;
3353 
3354 #ifndef GL_ES_VERSION_2_0
3355     if (!gl3Support)
3356     {
3357         if (!isRenderBuffer)
3358             glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, object, 0);
3359         else
3360             glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, object);
3361     }
3362     else
3363 #endif
3364     {
3365         if (!isRenderBuffer)
3366             glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, object, 0);
3367         else
3368             glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, object);
3369     }
3370 }
3371 
BindStencilAttachment(unsigned object,bool isRenderBuffer)3372 void Graphics::BindStencilAttachment(unsigned object, bool isRenderBuffer)
3373 {
3374     if (!object)
3375         isRenderBuffer = false;
3376 
3377 #ifndef GL_ES_VERSION_2_0
3378     if (!gl3Support)
3379     {
3380         if (!isRenderBuffer)
3381             glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_TEXTURE_2D, object, 0);
3382         else
3383             glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, object);
3384     }
3385     else
3386 #endif
3387     {
3388         if (!isRenderBuffer)
3389             glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, object, 0);
3390         else
3391             glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, object);
3392     }
3393 }
3394 
CheckFramebuffer()3395 bool Graphics::CheckFramebuffer()
3396 {
3397 #ifndef GL_ES_VERSION_2_0
3398     if (!gl3Support)
3399         return glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) == GL_FRAMEBUFFER_COMPLETE_EXT;
3400     else
3401 #endif
3402         return glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE;
3403 }
3404 
SetVertexAttribDivisor(unsigned location,unsigned divisor)3405 void Graphics::SetVertexAttribDivisor(unsigned location, unsigned divisor)
3406 {
3407 #ifndef GL_ES_VERSION_2_0
3408     if (gl3Support && instancingSupport_)
3409         glVertexAttribDivisor(location, divisor);
3410     else if (instancingSupport_)
3411         glVertexAttribDivisorARB(location, divisor);
3412 #else
3413 #ifdef __EMSCRIPTEN__
3414     if (instancingSupport_)
3415         glVertexAttribDivisorANGLE(location, divisor);
3416 #endif
3417 #endif
3418 }
3419 
3420 }
3421