0031668: Visualization - WebGL sample doesn't work on Emscripten 1.39
[occt.git] / src / D3DHost / D3DHost_FrameBuffer.cxx
1 // Created on: 2015-06-10
2 // Created by: Kirill Gavrilov
3 // Copyright (c) 2015 OPEN CASCADE SAS
4 //
5 // This file is part of Open CASCADE Technology software library.
6 //
7 // This library is free software; you can redistribute it and/or modify it under
8 // the terms of the GNU Lesser General Public License version 2.1 as published
9 // by the Free Software Foundation, with special exception defined in the file
10 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
11 // distribution for complete text of the license and disclaimer of any warranty.
12 //
13 // Alternatively, this file may be used under the terms of Open CASCADE
14 // commercial license or contractual agreement.
15
16 #include <d3d9.h>
17
18 #include <D3DHost_FrameBuffer.hxx>
19
20 #include <OpenGl_GlCore20.hxx>
21 #include <OpenGl_ArbFBO.hxx>
22 #include <Standard_ProgramError.hxx>
23 #include <TCollection_ExtendedString.hxx>
24
25 IMPLEMENT_STANDARD_RTTIEXT(D3DHost_FrameBuffer,OpenGl_FrameBuffer)
26
27 // =======================================================================
28 // function : D3DHost_FrameBuffer
29 // purpose  :
30 // =======================================================================
31 D3DHost_FrameBuffer::D3DHost_FrameBuffer()
32 : myD3dSurf      (NULL),
33   myD3dSurfShare (NULL),
34   myGlD3dDevice  (NULL),
35   myGlD3dSurf    (NULL),
36   myLockCount    (0),
37   myD3dFallback  (Standard_False),
38   myIsSRGBReady  (Standard_False)
39 {
40   //
41 }
42
43 // =======================================================================
44 // function : ~D3DHost_FrameBuffer
45 // purpose  :
46 // =======================================================================
47 D3DHost_FrameBuffer::~D3DHost_FrameBuffer()
48 {
49   Release (NULL);
50 }
51
52 // =======================================================================
53 // function : Release
54 // purpose  :
55 // =======================================================================
56 void D3DHost_FrameBuffer::Release (OpenGl_Context* theCtx)
57 {
58 #if !defined(GL_ES_VERSION_2_0)
59   if (myGlD3dDevice != NULL)
60   {
61     const OpenGl_GlFunctions* aFuncs = (theCtx != NULL && theCtx->IsValid())
62                                      ? theCtx->Functions()
63                                      : NULL;
64     if (myGlD3dSurf != NULL)
65     {
66       if (aFuncs != NULL)
67       {
68         aFuncs->wglDXUnregisterObjectNV (myGlD3dDevice, myGlD3dSurf);
69       }
70       myGlD3dSurf = NULL;
71     }
72
73     if (aFuncs != NULL)
74     {
75       aFuncs->wglDXCloseDeviceNV (myGlD3dDevice);
76     }
77     myGlD3dDevice = NULL;
78   }
79 #endif
80
81   if (myD3dSurf != NULL)
82   {
83     myD3dSurf->Release();
84     myD3dSurf      = NULL;
85     myD3dSurfShare = NULL;
86   }
87
88   OpenGl_FrameBuffer::Release (theCtx);
89 }
90
91 // =======================================================================
92 // function : Init
93 // purpose  :
94 // =======================================================================
95 Standard_Boolean D3DHost_FrameBuffer::Init (const Handle(OpenGl_Context)& theCtx,
96                                             IDirect3DDevice9*             theD3DDevice,
97                                             const Standard_Boolean        theIsD3dEx,
98                                             const Standard_Integer        theSizeX,
99                                             const Standard_Integer        theSizeY)
100 {
101   if (InitD3dInterop (theCtx, theD3DDevice, theIsD3dEx, theSizeX, theSizeY, GL_DEPTH24_STENCIL8))
102   {
103     return Standard_True;
104   }
105   return InitD3dFallback (theCtx, theD3DDevice, theIsD3dEx, theSizeX, theSizeY, GL_DEPTH24_STENCIL8);
106 }
107
108 // =======================================================================
109 // function : InitD3dFallback
110 // purpose  :
111 // =======================================================================
112 Standard_Boolean D3DHost_FrameBuffer::InitD3dFallback (const Handle(OpenGl_Context)& theCtx,
113                                                        IDirect3DDevice9*             theD3DDevice,
114                                                        const Standard_Boolean        theIsD3dEx,
115                                                        const Standard_Integer        theSizeX,
116                                                        const Standard_Integer        theSizeY,
117                                                        const GLint                   theDepthFormat)
118 {
119   const Standard_Boolean isGlInit = OpenGl_FrameBuffer::Init (theCtx, theSizeX, theSizeY, GL_RGBA8, theDepthFormat, 0);
120   myD3dFallback = Standard_True;
121
122   const Standard_Integer aSizeX = theSizeX > 0 ? theSizeX : 2;
123   const Standard_Integer aSizeY = theSizeY > 0 ? theSizeY : 2;
124   if (theD3DDevice->CreateRenderTarget (aSizeX, aSizeY,
125                                         D3DFMT_X8R8G8B8, D3DMULTISAMPLE_NONE, 0, theIsD3dEx ? TRUE : FALSE,
126                                         &myD3dSurf, theIsD3dEx ? &myD3dSurfShare : NULL) != D3D_OK)
127   {
128     Release (theCtx.operator->());
129     theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
130                          TCollection_AsciiString ("D3DHost_FrameBuffer, could not create D3DFMT_X8R8G8B8 render target ") + aSizeX + "x" + aSizeY);
131     return Standard_False;
132   }
133   return isGlInit;
134 }
135
136 // =======================================================================
137 // function : InitD3dInterop
138 // purpose  :
139 // =======================================================================
140 Standard_Boolean D3DHost_FrameBuffer::InitD3dInterop (const Handle(OpenGl_Context)& theCtx,
141                                                       IDirect3DDevice9*             theD3DDevice,
142                                                       const Standard_Boolean        theIsD3dEx,
143                                                       const Standard_Integer        theSizeX,
144                                                       const Standard_Integer        theSizeY,
145                                                       const GLint                   theDepthFormat)
146 {
147   Release (theCtx.operator->());
148 #if !defined(GL_ES_VERSION_2_0)
149   myDepthFormat = theDepthFormat;
150   myVPSizeX = theSizeX;
151   myVPSizeY = theSizeY;
152   myInitVPSizeX = theSizeX;
153   myInitVPSizeY = theSizeY;
154   const Standard_Integer aSizeX = theSizeX > 0 ? theSizeX : 2;
155   const Standard_Integer aSizeY = theSizeY > 0 ? theSizeY : 2;
156
157   const OpenGl_GlFunctions* aFuncs = theCtx->Functions();
158   if (aFuncs->wglDXOpenDeviceNV == NULL)
159   {
160     Release (theCtx.operator->());
161     theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
162                          "D3DHost_FrameBuffer, WGL_NV_DX_interop is unavailable!");
163     return Standard_False;
164   }
165
166   // Render target surface should be lockable on
167   // Windows XP and non-lockable on Windows Vista or higher
168   if (theD3DDevice->CreateRenderTarget (aSizeX, aSizeY,
169                                         D3DFMT_X8R8G8B8, D3DMULTISAMPLE_NONE, 0, theIsD3dEx ? TRUE : FALSE,
170                                         &myD3dSurf, theIsD3dEx ? &myD3dSurfShare : NULL) != D3D_OK)
171   {
172     Release (theCtx.operator->());
173     theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
174                          TCollection_AsciiString ("D3DHost_FrameBuffer, could not create D3DFMT_X8R8G8B8 render target ") + aSizeX + "x" + aSizeY);
175     return Standard_False;
176   }
177
178   myGlD3dDevice = aFuncs->wglDXOpenDeviceNV (theD3DDevice);
179   if (myGlD3dDevice == NULL)
180   {
181     Release (theCtx.operator->());
182     theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
183                          "D3DHost_FrameBuffer, could not create the GL <-> DirectX Interop using wglDXOpenDeviceNV()");
184     return Standard_False;
185   }
186
187   if (!registerD3dBuffer (theCtx))
188   {
189     Release (theCtx.operator->());
190     return Standard_False;
191   }
192
193   myIsOwnBuffer = true;
194   myIsOwnDepth  = true;
195   theCtx->arbFBO->glGenFramebuffers (1, &myGlFBufferId);
196
197   const OpenGl_TextureFormat aDepthFormat = OpenGl_TextureFormat::FindSizedFormat (theCtx, myDepthFormat);
198   if (aDepthFormat.IsValid()
199   && !myDepthStencilTexture->Init (theCtx, aDepthFormat, Graphic3d_Vec2i (aSizeX, aSizeY), Graphic3d_TOT_2D))
200   {
201     Release (theCtx.get());
202     theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
203                          TCollection_AsciiString("D3DHost_FrameBuffer, could not initialize GL_DEPTH24_STENCIL8 texture ") + aSizeX + "x" + aSizeY);
204     return Standard_False;
205   }
206
207   myD3dFallback = Standard_False;
208   return Standard_True;
209 #else
210   (void )theD3DDevice;
211   (void )theIsD3dEx;
212   (void )theSizeX;
213   (void )theSizeY;
214   (void )theDepthFormat;
215   return Standard_False;
216 #endif
217 }
218
219 // =======================================================================
220 // function : registerD3dBuffer
221 // purpose  :
222 // =======================================================================
223 Standard_Boolean D3DHost_FrameBuffer::registerD3dBuffer (const Handle(OpenGl_Context)& theCtx)
224 {
225 #if defined(GL_ES_VERSION_2_0)
226   (void )theCtx;
227   return Standard_False;
228 #else
229   const OpenGl_GlFunctions* aFuncs = theCtx->Functions();
230   if (myGlD3dSurf != NULL)
231   {
232     if (!aFuncs->wglDXUnregisterObjectNV (myGlD3dDevice, myGlD3dSurf))
233     {
234       theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
235                            "D3DHost_FrameBuffer, can not unregister color buffer");
236       return Standard_False;
237     }
238     myGlD3dSurf = NULL;
239   }
240
241   if (!aFuncs->wglDXSetResourceShareHandleNV (myD3dSurf, myD3dSurfShare))
242   {
243     theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
244                          "D3DHost_FrameBuffer, wglDXSetResourceShareHandleNV() has failed");
245     return Standard_False;
246   }
247
248   myColorTextures (0)->Release (theCtx.operator->());
249   myColorTextures (0)->Create  (theCtx);
250
251   myGlD3dSurf = aFuncs->wglDXRegisterObjectNV (myGlD3dDevice,
252                                                myD3dSurf,
253                                                myColorTextures (0)->TextureId(),
254                                                GL_TEXTURE_2D,
255                                                WGL_ACCESS_WRITE_DISCARD_NV);
256   theCtx->ResetErrors (true);
257   if (myGlD3dSurf == NULL)
258   {
259     theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
260                          "D3DHost_FrameBuffer, can not register color buffer");
261     return Standard_False;
262   }
263
264   return Standard_True;
265 #endif
266 }
267
268 // =======================================================================
269 // function : BindBuffer
270 // purpose  :
271 // =======================================================================
272 void D3DHost_FrameBuffer::BindBuffer (const Handle(OpenGl_Context)& theCtx)
273 {
274   Standard_ProgramError_Raise_if (myLockCount < 1, "D3DHost_FrameBuffer::BindBuffer(), resource should be locked beforehand!");
275   if (theCtx->arbFBO == NULL)
276   {
277     return;
278   }
279
280   OpenGl_FrameBuffer::BindBuffer (theCtx);
281   theCtx->SetFrameBufferSRGB (true, myIsSRGBReady);
282   if (myD3dFallback)
283   {
284     return;
285   }
286
287   theCtx->arbFBO->glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
288                                           myColorTextures (0)->GetTarget(), myColorTextures (0)->TextureId(), 0);
289
290   const OpenGl_TextureFormat aDepthFormat = OpenGl_TextureFormat::FindSizedFormat (theCtx, myDepthFormat);
291   if (myDepthStencilTexture->IsValid())
292   {
293   #ifdef GL_DEPTH_STENCIL_ATTACHMENT
294     theCtx->arbFBO->glFramebufferTexture2D (GL_FRAMEBUFFER, aDepthFormat.PixelFormat() == GL_DEPTH_STENCIL ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT,
295                                             myDepthStencilTexture->GetTarget(), myDepthStencilTexture->TextureId(), 0);
296   #else
297     theCtx->arbFBO->glFramebufferTexture2D (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
298                                             myDepthStencilTexture->GetTarget(), myDepthStencilTexture->TextureId(), 0);
299     if (aDepthFormat.PixelFormat() == GL_DEPTH_STENCIL)
300     {
301       theCtx->arbFBO->glFramebufferTexture2D (GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
302                                               myDepthStencilTexture->GetTarget(), myDepthStencilTexture->TextureId(), 0);
303     }
304   #endif
305   }
306   if (theCtx->arbFBO->glCheckFramebufferStatus (GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
307   {
308     if (myDepthStencilTexture->IsValid())
309     {
310     #ifdef GL_DEPTH_STENCIL_ATTACHMENT
311       theCtx->arbFBO->glFramebufferTexture2D (GL_FRAMEBUFFER, aDepthFormat.PixelFormat() == GL_DEPTH_STENCIL ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT,
312                                               myDepthStencilTexture->GetTarget(), 0, 0);
313     #else
314       theCtx->arbFBO->glFramebufferTexture2D (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
315                                               myDepthStencilTexture->GetTarget(), 0, 0);
316       if (aDepthFormat.PixelFormat() == GL_DEPTH_STENCIL)
317       {
318         theCtx->arbFBO->glFramebufferTexture2D (GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
319                                                 myDepthStencilTexture->GetTarget(), 0, 0);
320       }
321     #endif
322     }
323     if (theCtx->arbFBO->glCheckFramebufferStatus (GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
324     {
325       theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
326                            "D3DHost_FrameBuffer, OpenGL FBO is incomplete!");
327       Release (theCtx.operator->());
328     }
329     else
330     {
331       myDepthFormat = 0;
332       myDepthStencilTexture->Release (theCtx.get());
333       theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PORTABILITY, 0, GL_DEBUG_SEVERITY_HIGH,
334                            "D3DHost_FrameBuffer, OpenGL FBO is created without Depth+Stencil attachements!");
335     }
336   }
337 }
338
339 // =======================================================================
340 // function : LockSurface
341 // purpose  :
342 // =======================================================================
343 void D3DHost_FrameBuffer::LockSurface (const Handle(OpenGl_Context)& theCtx)
344 {
345   if (++myLockCount > 1)
346   {
347     return;
348   }
349   if (myGlD3dSurf == NULL)
350   {
351     return;
352   }
353
354 #if !defined(GL_ES_VERSION_2_0)
355   const OpenGl_GlFunctions* aFuncs = theCtx->Functions();
356   if (!aFuncs->wglDXLockObjectsNV (myGlD3dDevice, 1, &myGlD3dSurf))
357   {
358     theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
359                          "D3DHost_FrameBuffer::LockSurface(), lock failed!");
360   }
361 #else
362   (void )theCtx;
363 #endif
364 }
365
366 // =======================================================================
367 // function : UnlockSurface
368 // purpose  :
369 // =======================================================================
370 void D3DHost_FrameBuffer::UnlockSurface (const Handle(OpenGl_Context)& theCtx)
371 {
372   if (--myLockCount != 0)
373   {
374     return;
375   }
376
377   if (myD3dFallback)
378   {
379     if (myD3dSurf == NULL)
380     {
381       return;
382     }
383
384     D3DLOCKED_RECT aLockedRect;
385     if (myD3dSurf->LockRect (&aLockedRect, NULL, 0) != 0)
386     {
387       theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
388                            "D3DHost_FrameBuffer::UnlockSurface(), lock failed!");
389       return;
390     }
391
392     Image_PixMap anImg;
393     if (anImg.InitWrapper (Image_Format_BGRA, (Standard_Byte* )aLockedRect.pBits, myInitVPSizeX, myInitVPSizeY, aLockedRect.Pitch))
394     {
395       anImg.SetTopDown (!IsValid()); // flip in software if OpenGL FBO is unavailable
396       myLockCount = 1;
397       if (!BufferDump (theCtx, this, anImg, Graphic3d_BT_RGBA))
398       {
399         theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
400                              "D3DHost_FrameBuffer::UnlockSurface(), buffer dump failed!");
401       }
402       myLockCount = 0;
403     }
404     else
405     {
406       theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
407                            "D3DHost_FrameBuffer::UnlockSurface(), buffer dump failed!");
408     }
409     myD3dSurf->UnlockRect();
410     return;
411   }
412   if (myGlD3dSurf == NULL)
413   {
414     return;
415   }
416
417 #if !defined(GL_ES_VERSION_2_0)
418   const OpenGl_GlFunctions* aFuncs = theCtx->Functions();
419   aFuncs->wglDXUnlockObjectsNV (myGlD3dDevice, 1, &myGlD3dSurf);
420 #endif
421 }