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