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