0029147: Visualization - D3DHost_FrameBuffer::BindBuffer() fails on some Intel drivers
[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   GLenum aPixelFormat = 0, aDataType = 0;
197   if (myDepthFormat != 0
198   &&  getDepthDataFormat (myDepthFormat, aPixelFormat, aDataType)
199   && !myDepthStencilTexture->Init (theCtx, myDepthFormat,
200                                    aPixelFormat, aDataType,
201                                    aSizeX, aSizeY, Graphic3d_TOT_2D))
202   {
203     Release (theCtx.operator->());
204     theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
205                          TCollection_AsciiString("D3DHost_FrameBuffer, could not initialize GL_DEPTH24_STENCIL8 texture ") + aSizeX + "x" + aSizeY);
206     return Standard_False;
207   }
208
209   myD3dFallback = Standard_False;
210   return Standard_True;
211 #else
212   (void )theD3DDevice;
213   (void )theIsD3dEx;
214   (void )theSizeX;
215   (void )theSizeY;
216   (void )theDepthFormat;
217   return Standard_False;
218 #endif
219 }
220
221 // =======================================================================
222 // function : registerD3dBuffer
223 // purpose  :
224 // =======================================================================
225 Standard_Boolean D3DHost_FrameBuffer::registerD3dBuffer (const Handle(OpenGl_Context)& theCtx)
226 {
227 #if defined(GL_ES_VERSION_2_0)
228   (void )theCtx;
229   return Standard_False;
230 #else
231   const OpenGl_GlFunctions* aFuncs = theCtx->Functions();
232   if (myGlD3dSurf != NULL)
233   {
234     if (!aFuncs->wglDXUnregisterObjectNV (myGlD3dDevice, myGlD3dSurf))
235     {
236       theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
237                            "D3DHost_FrameBuffer, can not unregister color buffer");
238       return Standard_False;
239     }
240     myGlD3dSurf = NULL;
241   }
242
243   if (!aFuncs->wglDXSetResourceShareHandleNV (myD3dSurf, myD3dSurfShare))
244   {
245     theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
246                          "D3DHost_FrameBuffer, wglDXSetResourceShareHandleNV() has failed");
247     return Standard_False;
248   }
249
250   myColorTextures (0)->Release (theCtx.operator->());
251   myColorTextures (0)->Create  (theCtx);
252
253   myGlD3dSurf = aFuncs->wglDXRegisterObjectNV (myGlD3dDevice,
254                                                myD3dSurf,
255                                                myColorTextures (0)->TextureId(),
256                                                GL_TEXTURE_2D,
257                                                WGL_ACCESS_WRITE_DISCARD_NV);
258   theCtx->ResetErrors (true);
259   if (myGlD3dSurf == NULL)
260   {
261     theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
262                          "D3DHost_FrameBuffer, can not register color buffer");
263     return Standard_False;
264   }
265
266   return Standard_True;
267 #endif
268 }
269
270 // =======================================================================
271 // function : BindBuffer
272 // purpose  :
273 // =======================================================================
274 void D3DHost_FrameBuffer::BindBuffer (const Handle(OpenGl_Context)& theCtx)
275 {
276   Standard_ProgramError_Raise_if (myLockCount < 1, "D3DHost_FrameBuffer::BindBuffer(), resource should be locked beforehand!");
277   if (theCtx->arbFBO == NULL)
278   {
279     return;
280   }
281
282   OpenGl_FrameBuffer::BindBuffer (theCtx);
283   if (myD3dFallback)
284   {
285     return;
286   }
287
288   theCtx->arbFBO->glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
289                                           myColorTextures (0)->GetTarget(), myColorTextures (0)->TextureId(), 0);
290
291   GLenum aDepthPixelFormat = 0, aDepthDataType = 0;
292   getDepthDataFormat (myDepthFormat, aDepthPixelFormat, aDepthDataType);
293   if (myDepthStencilTexture->IsValid())
294   {
295   #ifdef GL_DEPTH_STENCIL_ATTACHMENT
296     theCtx->arbFBO->glFramebufferTexture2D (GL_FRAMEBUFFER, aDepthPixelFormat == GL_DEPTH_STENCIL ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT,
297                                             myDepthStencilTexture->GetTarget(), myDepthStencilTexture->TextureId(), 0);
298   #else
299     theCtx->arbFBO->glFramebufferTexture2D (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
300                                             myDepthStencilTexture->GetTarget(), myDepthStencilTexture->TextureId(), 0);
301     if (aDepthPixelFormat == GL_DEPTH_STENCIL)
302     {
303       theCtx->arbFBO->glFramebufferTexture2D (GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
304                                               myDepthStencilTexture->GetTarget(), myDepthStencilTexture->TextureId(), 0);
305     }
306   #endif
307   }
308   if (theCtx->arbFBO->glCheckFramebufferStatus (GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
309   {
310     if (myDepthStencilTexture->IsValid())
311     {
312     #ifdef GL_DEPTH_STENCIL_ATTACHMENT
313       theCtx->arbFBO->glFramebufferTexture2D (GL_FRAMEBUFFER, aDepthPixelFormat == GL_DEPTH_STENCIL ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT,
314                                               myDepthStencilTexture->GetTarget(), 0, 0);
315     #else
316       theCtx->arbFBO->glFramebufferTexture2D (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
317                                               myDepthStencilTexture->GetTarget(), 0, 0);
318       if (aDepthPixelFormat == GL_DEPTH_STENCIL)
319       {
320         theCtx->arbFBO->glFramebufferTexture2D (GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
321                                                 myDepthStencilTexture->GetTarget(), 0, 0);
322       }
323     #endif
324     }
325     if (theCtx->arbFBO->glCheckFramebufferStatus (GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
326     {
327       theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
328                            "D3DHost_FrameBuffer, OpenGL FBO is incomplete!");
329       Release (theCtx.operator->());
330     }
331     else
332     {
333       myDepthFormat = 0;
334       myDepthStencilTexture->Release (theCtx.get());
335       theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PORTABILITY, 0, GL_DEBUG_SEVERITY_HIGH,
336                            "D3DHost_FrameBuffer, OpenGL FBO is created without Depth+Stencil attachements!");
337     }
338   }
339 }
340
341 // =======================================================================
342 // function : LockSurface
343 // purpose  :
344 // =======================================================================
345 void D3DHost_FrameBuffer::LockSurface (const Handle(OpenGl_Context)& theCtx)
346 {
347   if (++myLockCount > 1)
348   {
349     return;
350   }
351   if (myGlD3dSurf == NULL)
352   {
353     return;
354   }
355
356 #if !defined(GL_ES_VERSION_2_0)
357   const OpenGl_GlFunctions* aFuncs = theCtx->Functions();
358   if (!aFuncs->wglDXLockObjectsNV (myGlD3dDevice, 1, &myGlD3dSurf))
359   {
360     theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
361                          "D3DHost_FrameBuffer::LockSurface(), lock failed!");
362   }
363 #else
364   (void )theCtx;
365 #endif
366 }
367
368 // =======================================================================
369 // function : UnlockSurface
370 // purpose  :
371 // =======================================================================
372 void D3DHost_FrameBuffer::UnlockSurface (const Handle(OpenGl_Context)& theCtx)
373 {
374   if (--myLockCount != 0)
375   {
376     return;
377   }
378
379   if (myD3dFallback)
380   {
381     if (myD3dSurf == NULL)
382     {
383       return;
384     }
385
386     D3DLOCKED_RECT aLockedRect;
387     if (myD3dSurf->LockRect (&aLockedRect, NULL, 0) != 0)
388     {
389       theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
390                            "D3DHost_FrameBuffer::UnlockSurface(), lock failed!");
391       return;
392     }
393
394     Image_PixMap anImg;
395     if (anImg.InitWrapper (Image_Format_BGRA, (Standard_Byte* )aLockedRect.pBits, myInitVPSizeX, myInitVPSizeY, aLockedRect.Pitch))
396     {
397       anImg.SetTopDown (!IsValid()); // flip in software if OpenGL FBO is unavailable
398       myLockCount = 1;
399       if (!BufferDump (theCtx, this, anImg, Graphic3d_BT_RGBA))
400       {
401         theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
402                              "D3DHost_FrameBuffer::UnlockSurface(), buffer dump failed!");
403       }
404       myLockCount = 0;
405     }
406     else
407     {
408       theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
409                            "D3DHost_FrameBuffer::UnlockSurface(), buffer dump failed!");
410     }
411     myD3dSurf->UnlockRect();
412     return;
413   }
414   if (myGlD3dSurf == NULL)
415   {
416     return;
417   }
418
419 #if !defined(GL_ES_VERSION_2_0)
420   const OpenGl_GlFunctions* aFuncs = theCtx->Functions();
421   aFuncs->wglDXUnlockObjectsNV (myGlD3dDevice, 1, &myGlD3dSurf);
422 #endif
423 }