diff --git a/SpoutDX/source/SpoutCommon.h b/SpoutDX/source/SpoutCommon.h index a58459b..20dfd07 100644 --- a/SpoutDX/source/SpoutCommon.h +++ b/SpoutDX/source/SpoutCommon.h @@ -34,8 +34,12 @@ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +03.07.23 - Remove _MSC_VER condition from SPOUT_DLLEXP define + (#PR93 Fix MinGW error (beta branch) + */ + #pragma once #ifndef __SpoutCommon__ @@ -46,17 +50,13 @@ // SPOUT_BUILD_DLL in the preprocessor defines. // Properties > C++ > Preprocessor > Preprocessor Definitions // -#if defined(_MSC_VER) - #if defined(SPOUT_BUILD_DLL) - #define SPOUT_DLLEXP __declspec(dllexport) - #elif defined(SPOUT_IMPORT_DLL) - #define SPOUT_DLLEXP __declspec(dllimport) - #else - #define SPOUT_DLLEXP - #endif -#else // _MSC_VER +#if defined(SPOUT_BUILD_DLL) + #define SPOUT_DLLEXP __declspec(dllexport) +#elif defined(SPOUT_IMPORT_DLL) + #define SPOUT_DLLEXP __declspec(dllimport) +#else #define SPOUT_DLLEXP -#endif // _MSC_VERR +#endif // Common utility functions namespace #include "SpoutUtils.h" diff --git a/SpoutDX/source/SpoutCopy.cpp b/SpoutDX/source/SpoutCopy.cpp index 8772f5e..d11a911 100644 --- a/SpoutDX/source/SpoutCopy.cpp +++ b/SpoutDX/source/SpoutCopy.cpp @@ -61,6 +61,14 @@ 11.12.22 - test for null args in conversion functions 22.12.22 - Compiler compatibility check Change all {} initializations to "={}" + 02-04-23 - Corrected source pointer increment in rgba2rgba when not inverted + Version 2.007.11 + 17.04.23 - Add rgba_to_rgb_sse and rgba_to_bgr_sse + 18.04.23 - Rename to rgba_to_rgb_sse3 and rgba_to_bgr_sse3 + Add bSwapRG to rgba_to_rgb_sse3 + Remove rgba_to_bgr_sse3 + Add experimental rgb_to_bgra_sse3 + Version 2.007.012 */ @@ -298,8 +306,8 @@ void spoutCopy::rgba2rgba(const void* rgba_source, void* rgba_dest, // first // Casting first avoids warning C26451: Arithmetic overflow with VS2022 code review // https://docs.microsoft.com/en-us/visualstudio/code-quality/c26451 - unsigned long YxW = (unsigned long)(y * width); - const unsigned long YxSP4 = (unsigned long)sourcePitch / 4; + unsigned long YxW = (unsigned long)(y * width); + const unsigned long YxSP4 = (unsigned long)(y * sourcePitch / 4); const unsigned long InvYxSP4 = (unsigned long)((height - 1 - y) * sourcePitch / 4); // Increment to current line @@ -524,7 +532,8 @@ void spoutCopy::bgra2rgba(const void *bgra_source, void *rgba_dest, unsigned int //--------------------------------------------------------- // Function: rgba2rgb -// Copy RGBA to RGB allowing for source line pitch +// Copy RGBA to RGB or BGR allowing for source line pitch using the fastest method +// void spoutCopy::rgba2rgb(const void* rgba_source, void* rgb_dest, unsigned int width, unsigned int height, unsigned int rgba_pitch, bool bInvert, bool bMirror, bool bSwapRB) const @@ -535,6 +544,33 @@ void spoutCopy::rgba2rgb(const void* rgba_source, void* rgb_dest, if (!rgb || !rgba) return; + // + // SSE3 copy + // No mirror option, image size 16 bit byte aligned, SSE3 intrinsics support + // + // Timing tests show more than twice as fast + // (Intel(R) Core(TM) i7-3770K CPU @ 3.50GHz) + // + // SSE + // 1280x720 1.6 msec + // 1920x1080 3.7 msec + // 3840x2160 14.0 msec + // Byte copy + // 1280x720 4.0 msec + // 1920x1080 9.3 msec + // 3840x2160 35.9 msec + // + unsigned int pitch = rgba_pitch; + if(pitch == 0) pitch = width*4; + if (!bMirror && width >= 320 && (width % 16) == 0 && m_bSSE3) { + rgba_to_rgb_sse3(rgba_source, rgb_dest, width, height, pitch, bInvert, bSwapRB); + return; + } + + // + // Byte pointer copy + // + // RGB dest does not have padding uint64_t rgbsize = (uint64_t)width * (uint64_t)height * 3; uint64_t rgbpitch = (uint64_t)width * 3; @@ -580,6 +616,7 @@ void spoutCopy::rgba2rgb(const void* rgba_source, void* rgb_dest, } // end rgba2rgb + //--------------------------------------------------------- // Function: rgb2rgba // @@ -814,35 +851,37 @@ void spoutCopy::rgb2bgra(const void *rgb_source, void *bgra_dest, } // end rgb2bgra -// Experimental -// -// Testing shows gains of 1 fps -// TODO : exact frame timing +// ===================================================================================== // +// Experimental // https://exchangetuts.com/fast-vectorized-conversion-from-rgb-to-bgra-1640844423877396 -// // in and out must be 16-byte aligned +// See also SIMD library +// https://github.com/ermig1979/Simd +// //--------------------------------------------------------- // Function: rgb_to_bgrx_sse // Experimental pending testing -void spoutCopy::rgb_to_bgrx_sse(unsigned int w, const void* inpix, void* outpix) const +// Single line function +void spoutCopy::rgb_to_bgrx_sse(unsigned int npixels, const void* rgb_source, void* bgrx_dest) const { - const __m128i* in_vec = static_cast(inpix); - __m128i* out_vec = static_cast<__m128i*>(outpix); + const __m128i* in_vec = static_cast(rgb_source); + __m128i* out_vec = static_cast<__m128i*>(bgrx_dest); if (!in_vec || !out_vec) return; - w /= 16; + npixels /= 16; - while (w-- > 0) { + while (npixels-- > 0) { + + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + // in_vec[0] Ra Ga Ba Rb Gb Bb Rc Gc Bc Rd Gd Bd Re Ge Be Rf + // in_vec[1] Gf Bf Rg Gg Bg Rh Gh Bh Ri Gi Bi Rj Gj Bj Rk Gk + // in_vec[2] Bk Rl Gl Bl Rm Gm Bm Rn Gn Bn Ro Go Bo Rp Gp Bp + // - /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 - * in_vec[0] Ra Ga Ba Rb Gb Bb Rc Gc Bc Rd Gd Bd Re Ge Be Rf - * in_vec[1] Gf Bf Rg Gg Bg Rh Gh Bh Ri Gi Bi Rj Gj Bj Rk Gk - * in_vec[2] Bk Rl Gl Bl Rm Gm Bm Rn Gn Bn Ro Go Bo Rp Gp Bp - */ __m128i in1={}; __m128i in2={}; __m128i in3={}; @@ -894,6 +933,214 @@ void spoutCopy::rgb_to_bgrx_sse(unsigned int w, const void* inpix, void* outpix) } // end rgb_to_bgrx_sse + +//--------------------------------------------------------- +// Function: rgb_to_bgra_sse +// Experimental pending testing +// Full image height +void spoutCopy::rgb_to_bgra_sse3 ( + void* rgb_source, + void* rgba_dest, + unsigned int width, + unsigned int height) const +{ + if ((width % 16) != 0) + return; + + auto rgb = static_cast(rgb_source); // rgb/bgr + auto rgba = static_cast(rgba_dest); // rgba/bgra + if (!rgb || !rgba) + return; + + for (unsigned int y = 0; y < height-2; y++) { + rgb_to_bgrx_sse(width*3, rgb, rgba); + rgb += width*3; + rgba += width*4; + } + +} // end rgb_to_bgra_sse3 + +// +// ===================================================================================== + + +//--------------------------------------------------------- +// Function: rgba_to_rgb_sse3 +// +void spoutCopy::rgba_to_rgb_sse3(const void* rgba_source, void* rgb_dest, + unsigned int width, unsigned int height, unsigned int rgba_pitch, + bool bInvert, bool bSwapRB) const +{ + const __m128i* in_vec = static_cast(rgba_source); // rgba + __m128i* out_vec = static_cast<__m128i*>(rgb_dest); // rgb + + if (!out_vec || !in_vec) + return; + + // RGB dest does not have padding + unsigned int rgbsize = width*height*3; + unsigned int rgbpitch = width*3; + + // Dest and source must be the same dimensions otherwise + unsigned int rgba_padding = rgba_pitch-width*4; // byte line padding + + // Flip image option, move to the beginning of the last rgb line + if (bInvert) { + out_vec += rgbsize/16; // end of rgb buffer + out_vec -= rgbpitch/16; // beginning of the last rgb line + } + + unsigned int w = width/16; + for (unsigned int y = 0; y < height; y++) { + + while (w-- > 0) { + + __m128i in0={}; + __m128i in1={}; + __m128i in2={}; + __m128i in3={}; + + __m128i out0={}; + __m128i out1={}; + + in0 = in_vec[0]; // First 128 bits RGBA + in1 = in_vec[1]; // Second 128 bits RGBA + in2 = in_vec[2]; // Third 128 bits RGBA + in3 = in_vec[3]; // Fourth 128 bits RGBA + + if (!bSwapRB) { + + // RGBA > RGB + + // + // RGBA in 4x16 = 64 bytes (4 pixels at 4 bytes each = 16 bytes) + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + // in_vec[0] Ra Ga Ba Xa Rb Gb Bb Xb Rc Gc Bc Xc Rd Gd Bd Xd + // in_vec[1] Re Ge Be Xe Rf Gf Bf Xf Rg Gg Bg Xg Rh Gh Bh Xh + // in_vec[2] Ri Gi Bi Xi Rj Gj Bj Xj Rk Gk Bk Xk Rl Gl Bl Xl + // in_vec[3] Rm Gm Bm Xm Rn Gn Bn Xn Ro Go Bo Xo Rp Gp Bp Xp + + // + // RGB out 3x16 = 48 bytes (4 pixels at 3 bytes each = 12 bytes) + // + // out_vec[0] Rf Be Ge Re Bd Gd Rd Bc Gc Rc Bb Gb Rb Ba Ga Ra + // 4 2 1 0 14 13 12 10 9 8 6 5 4 2 1 0 + // in_vec[1] | in_vec[0] | + // out_vec[1] Gk Rk Bj Gj Rj Bi Gi Ri Bh Gh Rh Bg Gg Rg Bf Gf + // 9 8 6 5 4 2 1 0 14 13 12 10 9 8 6 5 + // in_vec[2] | in_vec[1] + // out_vec[2] Bp Gp Rp Bo Go Ro Bn Gn Rn Bm Gm Rm Bl Gl Rl Bk + // 14 13 12 10 9 8 6 5 4 2 1 0 14 13 12 10 + // in_vec[3] | in_vec[2] + + // First 16 RGB bytes + // out_vec[0] Rf Be Ge Re Bd Gd Rd Bc Gc Rc Bb Gb Rb Ba Ga Ra + // 4 2 1 0 14 13 12 10 9 8 6 5 4 2 1 0 + // in_vec[1] | in_vec[0] | + out0 = _mm_shuffle_epi8(in0, + _mm_set_epi8('\xff', '\xff', '\xff', '\xff', 14, 13, 12, 10, 9, 8, 6, 5, 4, 2, 1, 0)); + out1 = _mm_shuffle_epi8(in1, + _mm_set_epi8(4, 2, 1, 0, '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff')); + out_vec[0] = _mm_or_si128(out0, out1); + + // Second 16 RGB bytes + // out_vec[1] Gk Rk Bj Gj Rj Bi Gi Ri Bh Gh Rh Bg Gg Rg Bf Gf + // 9 8 6 5 4 2 1 0 14 13 12 10 9 8 6 5 + // in_vec[2] | in_vec[1] + out0 = _mm_shuffle_epi8(in1, + _mm_set_epi8('\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', 14, 13, 12, 10, 9, 8, 6, 5)); + out1 = _mm_shuffle_epi8(in2, + _mm_set_epi8(9, 8, 6, 5, 4, 2, 1, 0, '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff')); + out_vec[1] = _mm_or_si128(out0, out1); + + // Third 16 RGB bytes + // out_vec[2] Bp Gp Rp Bo Go Ro Bn Gn Rn Bm Gm Rm Bl Gl Rl Bk + // 14 13 12 10 9 8 6 5 4 2 1 0 14 13 12 10 + // in_vec[3] | in_vec[2] + out0 = _mm_shuffle_epi8(in2, + _mm_set_epi8('\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', 14, 13, 12, 10)); + out1 = _mm_shuffle_epi8(in3, + _mm_set_epi8(14, 13, 12, 10, 9, 8, 6, 5, 4, 2, 1, 0, '\xff', '\xff', '\xff', '\xff')); + out_vec[2] = _mm_or_si128(out0, out1); + } // end RGBA >RGB + else { + + // RGBA > BGR + + // + // RGBA in 4x16 = 64 bytes (4 pixels at 4 bytes each = 16 bytes) + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + // in_vec[0] Ra Ga Ba Xa Rb Gb Bb Xb Rc Gc Bc Xc Rd Gd Bd Xd + // in_vec[1] Re Ge Be Xe Rf Gf Bf Xf Rg Gg Bg Xg Rh Gh Bh Xh + // in_vec[2] Ri Gi Bi Xi Rj Gj Bj Xj Rk Gk Bk Xk Rl Gl Bl Xl + // in_vec[3] Rm Gm Bm Xm Rn Gn Bn Xn Ro Go Bo Xo Rp Gp Bp Xp + + // + // BGR out 3x16 = 48 bytes (4 pixels at 3 bytes each = 12 bytes) + // + // out_vec[0] Bf Re Ge Be Rd Gd Bd Rc Gc Bc Rb Gb Bb Ra Ga Ba + // 6 0 1 2 12 13 14 8 9 10 4 5 6 0 1 2 + // in_vec[1] | in_vec[0] | + // out_vec[1] Gk Bk Rj Gj Bj Ri Gi Bi Rh Gh Bh Rg Gg Bg Rf Gf + // 9 10 4 5 6 0 1 2 12 13 14 8 9 10 4 5 + // in_vec[2] | in_vec[1] + // out_vec[2] Rp Gp Bp Ro Go Bo Rn Gn Bn Rm Gm Bm Rl Gl Bl Rk + // 12 13 14 8 9 10 4 5 6 0 1 2 12 13 14 8 + // in_vec[3] | in_vec[2] + + // First 16 BGR bytes + // out_vec[0] Rf Be Ge Re Bd Gd Rd Bc Gc Rc Bb Gb Rb Ba Ga Ra + // 6 0 1 2 12 13 14 8 9 10 4 5 6 0 1 2 + // in_vec[1] | in_vec[0] | + out0 = _mm_shuffle_epi8(in0, + _mm_set_epi8('\xff', '\xff', '\xff', '\xff', 12, 13, 14, 8, 9, 10, 4, 5, 6, 0, 1, 2)); + out1 = _mm_shuffle_epi8(in1, + _mm_set_epi8(6, 0, 1, 2, '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff')); + out_vec[0] = _mm_or_si128(out0, out1); + + // Second 16 BGR bytes + // out_vec[1] Gk Rk Bj Gj Rj Bi Gi Ri Bh Gh Rh Bg Gg Rg Bf Gf + // 9 10 4 5 6 0 1 2 12 13 14 8 9 10 4 5 + // in_vec[2] | in_vec[1] + out0 = _mm_shuffle_epi8(in1, + _mm_set_epi8('\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', 12, 13, 14, 8, 9, 10, 4, 5)); + out1 = _mm_shuffle_epi8(in2, + _mm_set_epi8(9, 10, 4, 5, 6, 0, 1, 2, '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff')); + out_vec[1] = _mm_or_si128(out0, out1); + + // Third 16 BGR bytes + // out_vec[2] Bp Gp Rp Bo Go Ro Bn Gn Rn Bm Gm Rm Bl Gl Rl Bk + // 12 13 14 8 9 10 4 5 6 0 1 2 12 13 14 8 + // in_vec[3] | in_vec[2] + out0 = _mm_shuffle_epi8(in2, + _mm_set_epi8('\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', 12, 13, 14, 8)); + out1 = _mm_shuffle_epi8(in3, + _mm_set_epi8(12, 13, 14, 8, 9, 10, 4, 5, 6, 0, 1, 2, '\xff', '\xff', '\xff', '\xff')); + out_vec[2] = _mm_or_si128(out0, out1); + + } // end RGBA > BGR + + in_vec += 4; // RGBA 4x16 + out_vec += 3; // RGB 3x16 + + } // done the line + + // Reset width + w = width/16; + + // Allow for padding at the end of the rgba source line + in_vec += rgba_padding/16; + + // move up a line for invert + if (bInvert) { + out_vec -= w*3*2; + } + + } + +} // end rgba_to_rgb_sse + + //--------------------------------------------------------- // Function: bgr2bgra // @@ -1040,7 +1287,7 @@ void spoutCopy::rgba2rgbResample(const void* source, void* dest, if (bMirror) { if (bInvert) - pixel = (destHeight - i - 1)*destWidth * 3 + (destWidth - j - 1) * 3; // flip vertically + pixel = (destHeight - i - 1)*destWidth * 3 + (destWidth - j - 1) * 3; // flip horizontally else pixel = i * destWidth * 3 + (destWidth - j - 1) * 3; } diff --git a/SpoutDX/source/SpoutCopy.h b/SpoutDX/source/SpoutCopy.h index ab518fc..6bedb54 100644 --- a/SpoutDX/source/SpoutCopy.h +++ b/SpoutDX/source/SpoutCopy.h @@ -110,9 +110,12 @@ class SPOUT_DLLEXP spoutCopy { // TODO : add RGBA pitch to all functions // TODO : avoid redundancy - // Copy RGBA to RGB allowing for source line pitch + // Copy RGBA to RGB or BGR allowing for source line pitch using the fastest method void rgba2rgb (const void* rgba_source, void* rgb_dest, unsigned int width, unsigned int height, - unsigned int sourcePitch, bool bInvert = false, bool bMirror = false, bool bSwapRB = false) const; + unsigned int sourcePitch, // byte line pitch + bool bInvert = false, // Flip vertically + bool bMirror = false, // Mirror horizontally + bool bSwapRB = false) const; // swap red and blue (rgb > bgr) const; // Copy RGBA to BGR allowing for source line pitch void rgba2bgr(const void* rgba_source, void* rgb_dest, unsigned int width, unsigned int height, @@ -129,8 +132,22 @@ class SPOUT_DLLEXP spoutCopy { unsigned int sourceWidth, unsigned int sourceHeight, unsigned int sourcePitch, unsigned int destWidth, unsigned int destHeight, bool bInvert = false) const; + // + // SSE3 function + // + // RGBA to RGB/BGR with source line pitch + // + void rgba_to_rgb_sse3(const void* rgba_source, void* rgb_dest, + unsigned int width, unsigned int height, + unsigned int rgba_pitch, // line byte pitch + bool bInvert = false, // Flip image + bool bSwapRB = false) const; // Swap RG (BGR) - // Copy RGB to RGBA allowing for destination pitch + // + // Byte functions + // + + // Copy RGB to RGBA void rgb2rgba (const void* rgb_source, void *rgba_dest, unsigned int width, unsigned int height, bool bInvert = false) const; // Copy RGB to RGBA allowing for destination pitch @@ -150,7 +167,6 @@ class SPOUT_DLLEXP spoutCopy { // RGB > BGRA // - // Copy RGB to BGRA void rgb2bgra (const void* rgb_source, void *bgra_dest, unsigned int width, unsigned int height, bool bInvert = false) const; @@ -159,14 +175,17 @@ class SPOUT_DLLEXP spoutCopy { unsigned int width, unsigned int height, unsigned int dest_pitch, bool bInvert) const; - // Copy RGB to BGRX using SSE - // Experimental SSE - void rgb_to_bgrx_sse(unsigned int w, const void* in, void* out) const; + + // Experimental SSE RGB to BGRA + // Single line + void rgb_to_bgrx_sse(unsigned int npixels, const void* rgb_source, void* bgrx_out) const; + // Full height + void rgb_to_bgra_sse3(void* rgb_source, void* rgba_dest, unsigned int width, unsigned int height) const; + // Copy BGR to BGRA void bgr2bgra (const void* bgr_source, void *bgra_dest, unsigned int width, unsigned int height, bool bInvert = false) const; - - + // Copy RGBA to BGR void rgba2bgr (const void* rgba_source, void *bgr_dest, unsigned int width, unsigned int height, bool bInvert = false) const; diff --git a/SpoutDX/source/SpoutDX.cpp b/SpoutDX/source/SpoutDX.cpp index b3ce1b4..4625fe5 100644 --- a/SpoutDX/source/SpoutDX.cpp +++ b/SpoutDX/source/SpoutDX.cpp @@ -128,6 +128,11 @@ // for unknown or DX9 formats. // 21.03.23 - ReceiveSenderData - revert to using the format of the D3D11 texture // generated by OpenDX11shareHandle for incorrect sender information. +// 24.04.23 - CheckSender clean up and add code comments. +// SendBackBuffer - release temporary objects. +// All object releases - flush context to avoid deferred destruction +// 08.07.23 - Remove global keyed texture option and SetKeyed/GetKeyed. +// Retain option in SpoutDirectX CreateSharedDX11Texture. // // ==================================================================================== /* @@ -238,16 +243,21 @@ bool spoutDX::OpenDirectX11(ID3D11Device* pDevice) // Get the immediate context pDevice->GetImmediateContext(&m_pImmediateContext); m_bClassDevice = false; // An existing device pointer was used - SpoutLogNotice(" using device (0x%.7X) : context (0x%.7X)", PtrToUint(m_pd3dDevice), PtrToUint(m_pImmediateContext)); + // SpoutLogNotice(" using device (0x%.7X) : context (0x%.7X)", PtrToUint(m_pd3dDevice), PtrToUint(m_pImmediateContext)); + // printf(" using device (0x%.7X) : context (0x%.7X)\n", PtrToUint(m_pd3dDevice), PtrToUint(m_pImmediateContext)); } else { // Create a DirectX 11 device if not already + // using a SpoutDirectX class function if (spoutdx.OpenDirectX11()) { - // Retrieve the device and context pointer + // Retrieve the device and context pointers for this class m_pd3dDevice = spoutdx.GetDX11Device(); m_pImmediateContext = spoutdx.GetDX11Context(); m_bClassDevice = true; // A new class device pointer was created - SpoutLogNotice(" created device (0x%.7X)", PtrToUint(m_pd3dDevice)); + SpoutLogNotice(" created new device (0x%.7X)", PtrToUint(m_pd3dDevice)); + } + else { + SpoutLogWarning(" device creation failed"); } } } @@ -280,13 +290,16 @@ void spoutDX::CloseDirectX11() m_pTexture = nullptr; m_dxShareHandle = nullptr; - if (m_pStaging[0]) m_pStaging[0]->Release(); - if (m_pStaging[1]) m_pStaging[1]->Release(); + if (m_pStaging[0]) spoutdx.ReleaseDX11Texture(m_pd3dDevice, m_pStaging[0]); + if (m_pStaging[1]) spoutdx.ReleaseDX11Texture(m_pd3dDevice, m_pStaging[1]); m_pStaging[0] = nullptr; m_pStaging[1] = nullptr; m_Index = 0; m_NextIndex = 0; + // Flush now to avoid deferred object destruction + if (m_pImmediateContext) m_pImmediateContext->Flush(); + if (m_pd3dDevice) { if (m_bClassDevice) { // A device was created using the SpoutDirectX class @@ -312,21 +325,6 @@ bool spoutDX::IsClassDevice() return m_bClassDevice; } -// Function: SetKeyed -// Sender : create a shared texture with a keyed mutex -// Used for testing -void spoutDX::SetKeyed(bool bKeyed) -{ - m_bKeyed = bKeyed; -} - -// Function: GetKeyed -// Get flag to create a shared texture with a keyed mutex -bool spoutDX::GetKeyed() -{ - return m_bKeyed; -} - //--------------------------------------------------------- // SENDER // @@ -396,12 +394,12 @@ void spoutDX::SetSenderFormat(DXGI_FORMAT format) // A new sender is created or updated by all sending functions void spoutDX::ReleaseSender() { - if (m_pSharedTexture) + if (m_pSharedTexture) m_pSharedTexture->Release(); m_pSharedTexture = nullptr; m_dxShareHandle = nullptr; - if (m_bSpoutInitialized) + if (m_bSpoutInitialized) sendernames.ReleaseSenderName(m_SenderName); m_Width = 0; @@ -435,8 +433,11 @@ bool spoutDX::SendBackBuffer() if (pBackBufferResource) { // SendTexture handles sender creation and re-sizing. SendTexture(reinterpret_cast(pBackBufferResource)); + pBackBufferResource->Release(); + rendertarget->Release(); return true; } + rendertarget->Release(); } return false; @@ -474,21 +475,20 @@ bool spoutDX::SendTexture(ID3D11Texture2D* pTexture) if (!OpenDirectX11()) return false; - // Get the texture details + // Get the texture details to check for zero size D3D11_TEXTURE2D_DESC desc; ZeroMemory(&desc, sizeof(desc)); pTexture->GetDesc(&desc); if (desc.Width == 0 || desc.Height == 0) return false; - + // Create or update the sender if (!CheckSender(desc.Width, desc.Height, (DWORD)desc.Format)) return false; - // Check the sender mutex for access the shared texture if (frame.CheckTextureAccess(m_pSharedTexture)) { - // Copy the texture to the sender's shared texture + // Copy the application texture to the sender's shared texture m_pImmediateContext->CopyResource(m_pSharedTexture, pTexture); // Flush the command queue now because the shared texture has been updated on this device m_pImmediateContext->Flush(); @@ -687,24 +687,25 @@ void spoutDX::ReleaseReceiver() Sleep(67); // Sender shared texture pointer - if (m_pSharedTexture) - m_pSharedTexture->Release(); + if (m_pSharedTexture) m_pSharedTexture->Release(); m_pSharedTexture = nullptr; m_dxShareHandle = nullptr; // Class receiving texture - if (m_pTexture) - m_pTexture->Release(); + if (m_pTexture) m_pTexture->Release(); m_pTexture = nullptr; // Staging textures for ReceiveImage - if (m_pStaging[0]) m_pStaging[0]->Release(); - if (m_pStaging[1]) m_pStaging[1]->Release(); + if (m_pStaging[0]) spoutdx.ReleaseDX11Texture(m_pd3dDevice, m_pStaging[0]); + if (m_pStaging[1]) spoutdx.ReleaseDX11Texture(m_pd3dDevice, m_pStaging[1]); m_pStaging[0] = nullptr; m_pStaging[1] = nullptr; m_Index = 0; m_NextIndex = 0; + // Flush now to avoid deferred object destruction + if (m_pImmediateContext) m_pImmediateContext->Flush(); + // Close the named access mutex and frame counting semaphore. frame.CloseAccessMutex(); frame.CleanupFrameCount(); @@ -870,6 +871,7 @@ bool spoutDX::ReceiveImage(unsigned char * pixels, if (m_bUpdated) return true; + // Try to receive texture details from a sender if (ReceiveSenderData()) { @@ -880,7 +882,7 @@ bool spoutDX::ReceiveImage(unsigned char * pixels, // Create new staging textures if it is a different size CheckStagingTextures(m_Width, m_Height, m_dwFormat); // The application detects the change with IsUpdated() - // and the receiving buiffer is updated to match the sender. + // and the receiving buffer is updated to match the sender. return true; } @@ -1934,8 +1936,7 @@ bool spoutDX::CheckSender(unsigned int width, unsigned int height, DWORD dwForma if (width == 0 || height == 0) return false; - // The sender needs a name - // Default is the executable name + // The sender needs a name (default is the executable name) if (!m_SenderName[0]) { if (!SetSenderName()) return false; @@ -1947,63 +1948,72 @@ bool spoutDX::CheckSender(unsigned int width, unsigned int height, DWORD dwForma if (!OpenDirectX11()) return false; - // Save width and height to test for sender size changes - m_Width = width; - m_Height = height; - m_dwFormat = dwFormat; - // Create a shared texture for the sender // A sender creates a new texture with a new share handle + // the existing shared texture is released m_dxShareHandle = nullptr; - spoutdx.CreateSharedDX11Texture(m_pd3dDevice, m_Width, m_Height, (DXGI_FORMAT)m_dwFormat, &m_pSharedTexture, m_dxShareHandle, m_bKeyed); + if (!spoutdx.CreateSharedDX11Texture(m_pd3dDevice, width, height, (DXGI_FORMAT)dwFormat, &m_pSharedTexture, m_dxShareHandle)) { + SpoutLogWarning("spoutDX::CheckSender - could not create shared texture"); + return false; + } + + // Save class width and height and format to test + // for sender size changes after initialization + m_Width = width; + m_Height = height; + m_dwFormat = dwFormat; // Create a sender using the DX11 shared texture handle (m_dxShareHandle) // and specifying the same texture format m_bSpoutInitialized = sendernames.CreateSender(m_SenderName, m_Width, m_Height, m_dxShareHandle, m_dwFormat); - - // TODO + // This could be a separate function SetHostPath - SharedTextureInfo info = {}; // Empty structure - if (!sendernames.getSharedInfo(m_SenderName, &info)) { - SpoutLogWarning("spoutGL::SetHostPath(%s) - could not get sender info", m_SenderName); - return false; + SharedTextureInfo info ={}; // Empty structure + if (sendernames.getSharedInfo(m_SenderName, &info)) { + char exepath[256]={}; + GetModuleFileNameA(NULL, exepath, sizeof(exepath)); + // Description field is 256 uint8_t + strcpy_s((char*)info.description, 256, exepath); + if (!sendernames.setSharedInfo(m_SenderName, &info)) { + SpoutLogWarning("spoutDX::CheckSender - could not set sender info", m_SenderName); + } } - char exepath[256]={}; - GetModuleFileNameA(NULL, exepath, sizeof(exepath)); - - // Description field is 256 uint8_t - strcpy_s((char*)info.description, 256, exepath); - - if (!sendernames.setSharedInfo(m_SenderName, &info)) { - SpoutLogWarning("spoutGL::SetHostPath(%s) - could not set sender info", m_SenderName); + else { + SpoutLogWarning("spoutDX::CheckSender - could not get sender info (%s)", m_SenderName); } // Create a sender mutex for access to the shared texture frame.CreateAccessMutex(m_SenderName); - + // Enable frame counting so the receiver gets frame number and fps frame.EnableFrameCount(m_SenderName); } // Initialized but has the source texture changed size ? - else if (m_Width != width || m_Height != height || m_dwFormat != dwFormat) { + else { + if (m_Width != width || m_Height != height || m_dwFormat != dwFormat) { + SpoutLogNotice("spoutDX::CheckSender - size change from %dx%d to %dx%d\n", m_Width, m_Height, width, height); + if (m_pSharedTexture) { + spoutdx.ReleaseDX11Texture(m_pSharedTexture); + // The existing shared texture is changed on this device + m_pImmediateContext->Flush(); + } + m_pSharedTexture = nullptr; + m_dxShareHandle = nullptr; - // Re-create the class shared texture with the new size - if (m_pSharedTexture) { - spoutdx.ReleaseDX11Texture(m_pSharedTexture); - // The existing shared texture is changed on this device - m_pImmediateContext->Flush(); - } - m_pSharedTexture = nullptr; - m_dxShareHandle = nullptr; - spoutdx.CreateSharedDX11Texture(m_pd3dDevice, width, height, (DXGI_FORMAT)dwFormat, &m_pSharedTexture, m_dxShareHandle, m_bKeyed); + if (!spoutdx.CreateSharedDX11Texture(m_pd3dDevice, width, height, (DXGI_FORMAT)dwFormat, &m_pSharedTexture, m_dxShareHandle)) { + SpoutLogWarning("spoutDX::CheckSender - could not re-create shared texture"); + return false; + } - // Update the sender and class variables - sendernames.UpdateSender(m_SenderName, width, height, m_dxShareHandle, dwFormat); + // Update the sender information + sendernames.UpdateSender(m_SenderName, width, height, m_dxShareHandle, dwFormat); - m_Width = width; - m_Height = height; - m_dwFormat = dwFormat; + // Update class variables + m_Width = width; + m_Height = height; + m_dwFormat = dwFormat; + } } // endif initialization or size checks @@ -2264,7 +2274,6 @@ bool spoutDX::ReadPixelData(ID3D11Texture2D* pStagingSource, unsigned char* dest // Map waits for GPU access const HRESULT hr = m_pImmediateContext->Map(pStagingSource, 0, D3D11_MAP_READ, 0, &mappedSubResource); if (SUCCEEDED(hr)) { - // Copy the staging texture pixels to the user buffer if (!bRGB) { // RGBA pixel buffer @@ -2274,6 +2283,8 @@ bool spoutDX::ReadPixelData(ID3D11Texture2D* pStagingSource, unsigned char* dest spoutcopy.rgba2rgbaResample(mappedSubResource.pData, destpixels, m_Width, m_Height, mappedSubResource.RowPitch, width, height, bInvert); } else { + // Copy rgba to bgra line by line allowing for source pitch using the fastest method + // Uses SSE3 copy function if line data is 16bit aligned (see SpoutCopy.cpp) if(bSwap) spoutcopy.rgba2bgra(mappedSubResource.pData, destpixels, width, height, mappedSubResource.RowPitch, bInvert); else @@ -2282,27 +2293,27 @@ bool spoutDX::ReadPixelData(ID3D11Texture2D* pStagingSource, unsigned char* dest } else if (m_dwFormat == 28) { // DXGI_FORMAT_R8G8B8A8_UNORM // RGBA texture - RGB/BGR pixel buffer - // If the texture format is RGBA it has to be converted to BGR by the staging texture copy + // If the texture format is RGBA it has to be converted to RGB/BGR by the staging texture copy if (width != m_Width || height != m_Height) { if(bSwap) - spoutcopy.rgba2rgbResample(mappedSubResource.pData, destpixels, m_Width, m_Height, mappedSubResource.RowPitch, width, height, bInvert); - else spoutcopy.rgba2bgrResample(mappedSubResource.pData, destpixels, m_Width, m_Height, mappedSubResource.RowPitch, width, height, bInvert); + else + spoutcopy.rgba2rgbResample(mappedSubResource.pData, destpixels, m_Width, m_Height, mappedSubResource.RowPitch, width, height, bInvert); } else { + // Copy RGBA to RGB or BGR allowing for source line pitch using the fastest method + // Uses SSE3 conversion functions if data is 16bit aligned (see SpoutCopy.cpp) if (bSwap) - spoutcopy.rgba2rgb(mappedSubResource.pData, destpixels, m_Width, m_Height, mappedSubResource.RowPitch, bInvert); + spoutcopy.rgba2rgb(mappedSubResource.pData, destpixels, m_Width, m_Height, mappedSubResource.RowPitch, bInvert, true); else - spoutcopy.rgba2bgr(mappedSubResource.pData, destpixels, m_Width, m_Height, mappedSubResource.RowPitch, bInvert); + spoutcopy.rgba2rgb(mappedSubResource.pData, destpixels, m_Width, m_Height, mappedSubResource.RowPitch, bInvert, false); } } else { - // Used for SpoutCam to receive RGB images if (width != m_Width || height != m_Height) { spoutcopy.rgba2rgbResample(mappedSubResource.pData, destpixels, m_Width, m_Height, mappedSubResource.RowPitch, width, height, bInvert, m_bMirror, m_bSwapRB); } else { - // Approx 5 msec at 1920x1080 spoutcopy.rgba2rgb(mappedSubResource.pData, destpixels, m_Width, m_Height, mappedSubResource.RowPitch, bInvert, m_bMirror, m_bSwapRB); } } @@ -2344,6 +2355,8 @@ bool spoutDX::CheckStagingTextures(unsigned int width, unsigned int height, DWOR // The SpoutDirectX function releases an existing texture and checks for zero or DX9 format if(spoutdx.CreateDX11StagingTexture(m_pd3dDevice, width, height, (DXGI_FORMAT)dwFormat, &m_pStaging[0]) && spoutdx.CreateDX11StagingTexture(m_pd3dDevice, width, height, (DXGI_FORMAT)dwFormat, &m_pStaging[1])) { + // Flush now to avoid deferred object destruction + if (m_pImmediateContext) m_pImmediateContext->Flush(); return true; } @@ -2467,7 +2480,7 @@ void spoutDX::SelectSenderPanel() // and SpoutPanel is installed, it has crashed. // Terminate the process and the mutex or the mutex will remain // and SpoutPanel will not be started again. - PROCESSENTRY32 pEntry; + PROCESSENTRY32 pEntry={}; pEntry.dwSize = sizeof(pEntry); bool done = false; // Take a snapshot of all processes and threads in the system diff --git a/SpoutDX/source/SpoutDX.h b/SpoutDX/source/SpoutDX.h index eee1e51..d511ce9 100644 --- a/SpoutDX/source/SpoutDX.h +++ b/SpoutDX/source/SpoutDX.h @@ -60,8 +60,6 @@ class SPOUT_DLLEXP spoutDX { ID3D11DeviceContext* GetDX11Context(); void CloseDirectX11(); bool IsClassDevice(); - void SetKeyed(bool bKeyed = true); - bool GetKeyed(); // // SENDER @@ -309,7 +307,6 @@ protected : bool m_bClassDevice; bool m_bAdapt; bool m_bMemoryShare; // Using 2.006 memoryshare methods - bool m_bKeyed; // Keyed shared texture SHELLEXECUTEINFOA m_ShExecInfo; // For ShellExecute // For WriteMemoryBuffer/ReadMemoryBuffer diff --git a/SpoutDX/source/SpoutDirectX.cpp b/SpoutDX/source/SpoutDirectX.cpp index 2d57420..07f0633 100644 --- a/SpoutDX/source/SpoutDirectX.cpp +++ b/SpoutDX/source/SpoutDirectX.cpp @@ -16,7 +16,7 @@ // 21.10.14 - removed keyed mutex lock due to reported driver problems // TODO - cleanup all functions using it // 10.02.15 - removed functions relating to DirectX 11 keyed mutex lock -// 14.02.15 - added UNREFERENCED_PARAMETER(pSharedTexture) to CheckAceess and AllowAccess +// 14.02.15 - added UNREFERENCED_PARAMETER(pSharedTexture) to CheckAccess and AllowAccess // 29.05.15 - Included SetAdapter for multiple adapters - Franz Hildgen. // 02.06.15 - Added GetAdapter, GetNumAdapters, GetAdapterName // 08.06.15 - removed dx9 flag from setadapter @@ -133,6 +133,28 @@ // 06.01.23 - Correct IsPreferenceAvailable() to pass array length to registry function // 08.01.23 - CreateSharedDX11Texture - option for keyed shared texture // 18.03.23 - CreateDX11StagingTexture - use default DX11 format for zero or DX9 formats +// Version 2.007.11 +// 24.04.23 - Remove const from 1st arg of ReleaseDX11Texture, ReleaseDX11Device +// and DebugLog to enable debugging via SDK Layers. +// ReleaseDX11Device check for release of class device and null the pointer +// ReleaseDX11Texture log notice if no outstanding refcount +// DebugLog - clean up and add code comments +// All object releases - flush context to avoid deferred destruction +// 28.04.23 - Release d3dInfoQueue in DebugLog +// 17.05.23 - Add ClearState to FlushWait +// 18.05.23 - Add Flush() function +// 22.05.23 - CloseDirectX11 - release immediate context before device +// OpenDX11shareHandle - catch OpenSharedResource exeption before testing result +// 05.06.23 - Allow feature level 11.1 in CreateDX11device +// Update logs in ReleaseDX11Texture for refcount +// 09.06.23 - Add GetDX11FeatureLevel for development +// 16.06.23 - CreateDX11device - allow for D3D_FEATURE_LEVEL_11_1 +// CreateSharedDX11Texture - add NT handle argument for development +// OpenDirectX11 +// Save global m_featureLevel for external device +// Create ID3D11Device1 and ID3D11DeviceContext1 for D3D_FEATURE_LEVEL_11_1 +// ReleaseDX11Device - release ID3D11Device1 and ID3D11DeviceContext1 if created +// Version 2.007.012 // // ==================================================================================== /* @@ -180,6 +202,10 @@ spoutDirectX::spoutDirectX() { m_driverType = D3D_DRIVER_TYPE_NULL; m_featureLevel = D3D_FEATURE_LEVEL_11_0; + // For feature leve 11.1 if available + m_pd3dDevice1 = nullptr; + m_pImmediateContext1 = nullptr; + // Output graphics adapter // Programmer can set for an application m_AdapterIndex = 0; // Adapter index @@ -206,6 +232,12 @@ spoutDirectX::~spoutDirectX() { // Function: OpenDirectX11 // Initialize and prepare Directx 11 // Retain a class device and context + +// OpenDirectX11 +// Save global m_featureLevel for external device +// Create ID3D11Device1 and ID3D11DeviceContext1 for D3D_FEATURE_LEVEL_11_1 +// ReleaseDX11Device - release ID3D11Device1 and ID3D11DeviceContext1 if created + bool spoutDirectX::OpenDirectX11(ID3D11Device* pDevice) { // Quit if already initialized @@ -231,10 +263,12 @@ bool spoutDirectX::OpenDirectX11(ID3D11Device* pDevice) // Retrieve the context pointer independently // For a class device it is created in CreateDX11device(). m_pd3dDevice->GetImmediateContext(&m_pImmediateContext); + // Get external device feature level + m_featureLevel = m_pd3dDevice->GetFeatureLevel(); } else { // Create a class device if none was passed in - // m_pImmediateContext is also created + // m_pImmediateContext is also created by CreateDX11device m_pd3dDevice = CreateDX11device(); // Device was created within this class m_bClassDevice = true; @@ -245,6 +279,14 @@ bool spoutDirectX::OpenDirectX11(ID3D11Device* pDevice) return false; } + // DirectX 11.1 or later + if (m_featureLevel >= D3D_FEATURE_LEVEL_11_1) { + HRESULT hr = m_pd3dDevice->QueryInterface(__uuidof(ID3D11Device1), reinterpret_cast(&m_pd3dDevice1)); + if (SUCCEEDED(hr)) { + (void)m_pImmediateContext->QueryInterface(__uuidof(ID3D11DeviceContext1), reinterpret_cast(&m_pImmediateContext1)); + } + } + return true; } @@ -260,6 +302,15 @@ void spoutDirectX::CloseDirectX11() return; } + + // Release m_pImmediateContext if created + if (m_pImmediateContext) { + m_pImmediateContext->ClearState(); + m_pImmediateContext->Flush(); + m_pImmediateContext->Release(); + } + m_pImmediateContext = nullptr; + if (m_bClassDevice) { // A device was created within this class (CreateDX11device) SpoutLogNotice("spoutDirectX::CloseDirectX11(0x%.7X)", PtrToUint(m_pd3dDevice)); @@ -267,7 +318,6 @@ void spoutDirectX::CloseDirectX11() if(m_pd3dDevice) ReleaseDX11Device(m_pd3dDevice); m_pd3dDevice = nullptr; - m_pImmediateContext = nullptr; m_bClassDevice = false; // Device is closed, do not release again } else { @@ -278,13 +328,8 @@ void spoutDirectX::CloseDirectX11() if (m_pAdapterDX11) m_pAdapterDX11->Release(); m_pAdapterDX11 = nullptr; - // Release m_pImmediateContext if created - if(m_pImmediateContext) { - m_pImmediateContext->Flush(); - m_pImmediateContext->Release(); - } - m_pImmediateContext = nullptr; } + } //--------------------------------------------------------- @@ -332,7 +377,7 @@ ID3D11Device* spoutDirectX::CreateDX11device() { ID3D11Device* pd3dDevice = nullptr; HRESULT hr = S_OK; - const UINT createDeviceFlags = 0; + UINT createDeviceFlags = 0; // Adapter pointer null by default // or specified by SetAdapter or SetAdapterPointer @@ -346,21 +391,21 @@ ID3D11Device* spoutDirectX::CreateDX11device() } // - // If the project is in a debug build, enable debugging via SDK Layers with this flag. + // If the project is in a debug build, debugging via SDK Layers can be enabled + // by querying "_DEBUG" which is defined for debug build. // https://docs.microsoft.com/en-us/windows/win32/api/d3d11/ne-d3d11-d3d11_create_device_flag - // To use this flag, you must have D3D11_1SDKLayers.dll installed or device creation fails. - // To resolve this you can install the Windows 10 SDK. - // - // Due to this dependency problem, you have to manually remove the comments below - // to enable it once you have installed D3D11_1SDKLayers.dll. + // + // You must be using the the Windows 10 SDK and have D3D11_1SDKLayers.dll installed + // or device creation fails. Due to this dependency, the code below is disabled. + // + // Remove the comments to enable D3D11 debugging if D3D11_1SDKLayers.dll is installed. // See also : void spoutDirectX::DebugLog // -/* #if defined(_DEBUG) createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG; #endif -*/ + // GL/DX interop Spec // ID3D11Device can only be used on WDDM operating systems : Must be multithreaded @@ -374,7 +419,7 @@ ID3D11Device* spoutDirectX::CreateDX11device() const UINT numDriverTypes = ARRAYSIZE( driverTypes ); // These are the feature levels that we will accept. - // m_featureLevel is the feature level used + // m_featureLevel is the maximum supported feature level used // 11.0 is the highest level currently supported for Spout // because 11.1 limits compatibility // Note from D3D11 Walbourn examples : @@ -384,7 +429,7 @@ ID3D11Device* spoutDirectX::CreateDX11device() // Target Independent Rasterization requires hardware support // so we can not make DX11 GPUs fully DX11.1 complaint. const D3D_FEATURE_LEVEL featureLevels[] = { - // D3D_FEATURE_LEVEL_11_1, // 0xb001 + D3D_FEATURE_LEVEL_11_1, // 0xb001 D3D_FEATURE_LEVEL_11_0, // 0xb000 D3D_FEATURE_LEVEL_10_1, // 0xa100 D3D_FEATURE_LEVEL_10_0, // 0xa000 @@ -446,7 +491,7 @@ ID3D11Device* spoutDirectX::CreateDX11device() } // All OK - return the device pointer to the caller - // m_pImmediateContext has also been created + // m_pImmediateContext has also been created by D3D11CreateDevice SpoutLogNotice(" Device (0x%.7X) - Context (0x%.7X)", PtrToUint(pd3dDevice), PtrToUint(m_pImmediateContext)); return pd3dDevice; @@ -467,6 +512,12 @@ ID3D11DeviceContext* spoutDirectX::GetDX11Context() return m_pImmediateContext; } +// Return the device feature level +D3D_FEATURE_LEVEL spoutDirectX::GetDX11FeatureLevel() +{ + return m_featureLevel; +} + // // Group: DirectX11 texture // @@ -474,13 +525,13 @@ ID3D11DeviceContext* spoutDirectX::GetDX11Context() //--------------------------------------------------------- // Function: CreateSharedDX11Texture // Create a DirectX11 shared texture -bool spoutDirectX::CreateSharedDX11Texture(ID3D11Device* pd3dDevice, - unsigned int width, - unsigned int height, - DXGI_FORMAT format, - ID3D11Texture2D** ppSharedTexture, - HANDLE &dxShareHandle, - bool bKeyed) +bool spoutDirectX::CreateSharedDX11Texture(ID3D11Device* pd3dDevice, + unsigned int width, + unsigned int height, + DXGI_FORMAT format, + ID3D11Texture2D** ppSharedTexture, + HANDLE& dxShareHandle, + bool bKeyed, bool bNThandle) { if (!pd3dDevice) { SpoutLogWarning("spoutDirectX::CreateSharedDX11Texture NULL device"); @@ -491,7 +542,7 @@ bool spoutDirectX::CreateSharedDX11Texture(ID3D11Device* pd3dDevice, SpoutLogWarning("spoutDirectX::CreateSharedDX11Texture NULL ppSharedTexture"); return false; } - + // // Create a new shared DX11 texture // @@ -499,6 +550,8 @@ bool spoutDirectX::CreateSharedDX11Texture(ID3D11Device* pd3dDevice, // Release the texture if it already exists if (*ppSharedTexture) { ReleaseDX11Texture(pd3dDevice, *ppSharedTexture); + // Immediate context is flushed by ReleaseDX11Texture to + // destroy any objects whose destruction has been deferred. } SpoutLogNotice("spoutDirectX::CreateSharedDX11Texture"); @@ -533,11 +586,17 @@ bool spoutDirectX::CreateSharedDX11Texture(ID3D11Device* pd3dDevice, // Note that a DirectX 11 texture with D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX is not // compatible with DirectX 9. If compatibility is required, a general named mutex // should be used for all texture types. This is the default. - if(bKeyed) + if (bKeyed) desc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX; else desc.MiscFlags = D3D11_RESOURCE_MISC_SHARED; - desc.CPUAccessFlags = 0; + // An NT handle is created only if the bNThandle argument is specified + // and the graphics hardware supports D3D_FEATURE_LEVEL_11_1 + if (bNThandle && m_featureLevel > D3D_FEATURE_LEVEL_11_0) { + SpoutLog(" spoutDirectX::CreateSharedDX11Texture : D3D11_RESOURCE_MISC_SHARED_NTHANDLE"); + desc.MiscFlags |= D3D11_RESOURCE_MISC_SHARED_NTHANDLE; + } + desc.CPUAccessFlags = 0; desc.Format = texformat; desc.Usage = D3D11_USAGE_DEFAULT; // Multisampling quality and count @@ -547,6 +606,7 @@ bool spoutDirectX::CreateSharedDX11Texture(ID3D11Device* pd3dDevice, desc.MipLevels = 1; desc.ArraySize = 1; + const HRESULT res = pd3dDevice->CreateTexture2D(&desc, NULL, ppSharedTexture); if (FAILED(res)) { const long long lres = static_cast((LOWORD(res))); @@ -554,44 +614,60 @@ bool spoutDirectX::CreateSharedDX11Texture(ID3D11Device* pd3dDevice, std::string str = "spoutDirectX::CreateSharedDX11Texture ERROR -["; str += std::to_string(lres); str += "] : "; switch (res) { - case DXGI_ERROR_INVALID_CALL: - str += "DXGI_ERROR_INVALID_CALL"; - break; - case E_INVALIDARG: - str += "E_INVALIDARG"; - break; - case E_OUTOFMEMORY: - str += "E_OUTOFMEMORY"; - break; - default : - str += "Unlisted error"; - break; + case DXGI_ERROR_INVALID_CALL: + str += "DXGI_ERROR_INVALID_CALL"; + break; + case E_INVALIDARG: + str += "E_INVALIDARG"; + break; + case E_OUTOFMEMORY: + str += "E_OUTOFMEMORY"; + break; + default: + str += "Unlisted error"; + break; } SpoutLogError("%s", str.c_str()); return false; } - // The DX11 texture is created OK - // Get the texture share handle so it can be saved in shared memory for receivers to pick up. + // Get the texture share handle so that for an NT handle + // it can be saved in sender shared memory for receivers to pick up. + ID3D11Texture2D* pTexture = *ppSharedTexture; + if (!pTexture) { + SpoutLogError("spoutDirectX::CreateSharedDX11Texture - failed to create texture"); + return false; + } + // When sharing a resource between two Direct3D 10/11 devices the unique handle // of the resource can be obtained by querying the resource for the IDXGIResource - // interface and then calling GetSharedHandle. - ID3D11Texture2D* pTexture = *ppSharedTexture; - IDXGIResource* pOtherResource(NULL); - if (pTexture) { - if (FAILED(pTexture->QueryInterface(__uuidof(IDXGIResource), (void**)&pOtherResource))) { - SpoutLogWarning("spoutDirectX::CreateSharedDX11Texture - QueryInterface error"); - return false; - } + // interface and then calling GetSharedHandle (or CreateSharedHandle). + IDXGIResource1* pOtherResource(NULL); + if (FAILED(pTexture->QueryInterface(__uuidof(IDXGIResource), (void**)&pOtherResource))) { + SpoutLogWarning("spoutDirectX::CreateSharedDX11Texture - QueryInterface error"); + return false; } if (!pOtherResource) { SpoutLogWarning("spoutDirectX::CreateSharedDX11Texture - QueryInterface error"); return false; } - // Return the shared texture handle - pOtherResource->GetSharedHandle(&dxShareHandle); + // NT handle + if (bNThandle && m_featureLevel > D3D_FEATURE_LEVEL_11_0) { + SpoutLog(" CreateSharedTexture - NT handle"); + // The calling application should release the NT handle. + pOtherResource->CreateSharedHandle(NULL, DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE, NULL, &dxShareHandle); + } + else { + // Return the shared texture handle if bNThandle is not specified. + // If bNThandle is not specified and the graphics hardware supports D3D_FEATURE_LEVEL_11_1, + // GetSharedHandle can still be used even though CreateSharedHandle is recommended. + // https://learn.microsoft.com/en-us/windows/win32/api/dxgi/nf-dxgi-idxgiresource-getsharedhandle + pOtherResource->GetSharedHandle(&dxShareHandle); + } + pOtherResource->Release(); + if (m_pImmediateContext) m_pImmediateContext->Flush(); pOtherResource = nullptr; pTexture = nullptr; @@ -603,7 +679,7 @@ bool spoutDirectX::CreateSharedDX11Texture(ID3D11Device* pd3dDevice, //--------------------------------------------------------- // Function: CreateDX11Texture -// Create a DircetX texture which is not shared +// Create a DirectX texture which is not shared bool spoutDirectX::CreateDX11Texture(ID3D11Device* pd3dDevice, unsigned int width, unsigned int height, DXGI_FORMAT format, ID3D11Texture2D** ppTexture) @@ -762,13 +838,9 @@ bool spoutDirectX::OpenDX11shareHandle(ID3D11Device* pDevice, ID3D11Texture2D** // Note that the resource created for use on this device must be eventually freed or there is a leak. // // This can crash if the share handle has been created using a different graphics adapter + HRESULT hr = 0; try { - const HRESULT hr = pDevice->OpenSharedResource(dxShareHandle, __uuidof(ID3D11Resource), (void**)(ppSharedTexture)); - if (FAILED(hr)) { - // Error 87 (0x75) - E_INVALIDARG - SpoutLogError("spoutDirectX::OpenDX11shareHandle (0x%.7X) failed : error = %d (0x%.7X)", LOWORD(dxShareHandle), LOWORD(hr), LOWORD(hr) ); - return false; - } + hr = pDevice->OpenSharedResource(dxShareHandle, __uuidof(ID3D11Texture2D), (void**)(ppSharedTexture)); } catch (...) { // Catch any exception @@ -776,9 +848,15 @@ bool spoutDirectX::OpenDX11shareHandle(ID3D11Device* pDevice, ID3D11Texture2D** return false; } + if (FAILED(hr)) { + // Error 87 (0x75) - E_INVALIDARG + SpoutLogError("spoutDirectX::OpenDX11shareHandle (0x%.7X) failed : error = %d (0x%.7X)", LOWORD(dxShareHandle), LOWORD(hr), LOWORD(hr)); + return false; + } + // Can get sender format here // ID3D11Texture2D * texturePointer = *ppSharedTexture; - // D3D11_TEXTURE2D_DESC td; + // D3D11_TEXTURE2D_DESC td ={}; // texturePointer->GetDesc(&td); // printf("td.Format = %d\n", td.Format); // 87 // printf("td.Width = %d\n", td.Width); @@ -788,7 +866,7 @@ bool spoutDirectX::OpenDX11shareHandle(ID3D11Device* pDevice, ID3D11Texture2D** // printf("td.ArraySize = %d\n", td.ArraySize); // printf("td.SampleDesc = %d\n", td.SampleDesc); // printf("td.BindFlags = %d\n", td.BindFlags); - // printf("td.MiscFlags = %d\n", td.MiscFlags); // D3D11_RESOURCE_MISC_SHARED + // printf("td.MiscFlags = %d\n", td.MiscFlags); // D3D11_RESOURCE_MISC_SHARED (2) return true; @@ -809,7 +887,7 @@ unsigned long spoutDirectX::ReleaseDX11Texture(ID3D11Texture2D* pTexture) //--------------------------------------------------------- // Function: ReleaseDX11Texture // Release a texture resource -unsigned long spoutDirectX::ReleaseDX11Texture(const ID3D11Device* pd3dDevice, ID3D11Texture2D* pTexture) +unsigned long spoutDirectX::ReleaseDX11Texture(ID3D11Device* pd3dDevice, ID3D11Texture2D* pTexture) { if (!pd3dDevice || !pTexture) { @@ -820,20 +898,19 @@ unsigned long spoutDirectX::ReleaseDX11Texture(const ID3D11Device* pd3dDevice, I return 0; } - SpoutLogNotice("spoutDirectX::ReleaseDX11Texture (0x%.7X, 0x%.7X)", PtrToUint(pd3dDevice), PtrToUint(pTexture)); + SpoutLogNotice("spoutDirectX::ReleaseDX11Texture (device 0x%.7X, texture 0x%.7X)", PtrToUint(pd3dDevice), PtrToUint(pTexture)); + const unsigned long refcount = pTexture->Release(); pTexture = nullptr; - // The device will be live, so warn if refcount > 1 + // The device will be reported live, so warn if refcount > 1 if (refcount > 1) { - SpoutLogWarning("spoutDirectX::ReleaseDX11Texture - refcount = %lu", refcount); + SpoutLogWarning(" refcount = %lu", refcount); DebugLog(pd3dDevice, "spoutDirectX::ReleaseDX11Texture - refcount = %lu\n", refcount); - // printf("spoutDirectX::ReleaseDX11Texture - refcount = %lu\n", refcount); } // Calling Flush will destroy any objects whose destruction has been deferred. - if (m_pImmediateContext) - m_pImmediateContext->Flush(); + if (m_pImmediateContext) m_pImmediateContext->Flush(); // Note that if the texture is registered and linked to OpenGL using the // GL/DX interop, the interop must be unregistered or the texture is not @@ -850,6 +927,18 @@ unsigned long spoutDirectX::ReleaseDX11Device(ID3D11Device* pd3dDevice) if (!pd3dDevice) return 0; + SpoutLogNotice("spoutDirectX::ReleaseDX11Device(0x%.7X)", PtrToUint(pd3dDevice)); + + // Release feature level 1 context and device if created + // TOD : refcount + if (m_pImmediateContext1) { + m_pImmediateContext1->Flush(); + m_pImmediateContext1->Release(); + } + m_pImmediateContext1 = nullptr; + if (m_pd3dDevice1) m_pd3dDevice1->Release(); + m_pd3dDevice1 = nullptr; + // Release the global context or there is an outstanding ref count // when the device is released if (m_pImmediateContext) { @@ -860,26 +949,34 @@ unsigned long spoutDirectX::ReleaseDX11Device(ID3D11Device* pd3dDevice) m_pImmediateContext = nullptr; } + ID3D11Device* pDevice = pd3dDevice; // Save for comparison const unsigned long refcount = pd3dDevice->Release(); - // Use this for debugging if you have D3D11_1SDKLayers.dll installed. + // If the device was the class device, null the pointer + // in case it is used below for debugging + if (pDevice == m_pd3dDevice) m_pd3dDevice = nullptr; + + // Use this for debugging if D3D11_1SDKLayers.dll is installed. // See CreateDX11device. -/* #ifdef _DEBUG // OutputDebugStringA(tmp); - ID3D11Debug* DebugDevice = nullptr; - if (m_pd3dDevice->QueryInterface(__uuidof(ID3D11Debug), (void**)&DebugDevice) == S_OK) { - DebugDevice->ReportLiveDeviceObjects(D3D11_RLDO_DETAIL); - DebugDevice->Release(); + if (m_pd3dDevice) { + ID3D11Debug* DebugDevice = nullptr; + if (m_pd3dDevice->QueryInterface(__uuidof(ID3D11Debug), (void**)&DebugDevice) == S_OK) { + DebugDevice->ReportLiveDeviceObjects(D3D11_RLDO_DETAIL); + DebugDevice->Release(); + } } #endif -*/ -// The device should have been released. Warn if refcount > 0. + // The device should have been released. Warn if refcount > 0. if (refcount > 0) { SpoutLogWarning("spoutDirectX::ReleaseDX11Device - refcount = %lu", refcount); DebugLog(pd3dDevice, "spoutDirectX::ReleaseDX11Device - refcount = %lu\n", refcount); - // printf("spoutDirectX::ReleaseDX11Device - refcount = %lu\n", refcount); + } + else { + SpoutLogNotice(" no outstanding refcount"); + DebugLog(pd3dDevice, "spoutDirectX::ReleaseDX11Texture - no outstanding refcount\n"); } return refcount; @@ -890,11 +987,21 @@ unsigned long spoutDirectX::ReleaseDX11Device(ID3D11Device* pd3dDevice) // If a shared texture is updated on one device ID3D11DeviceContext::Flush must be called on that device. // https://docs.microsoft.com/en-us/windows/win32/api/d3d11/nf-d3d11-id3d11device-opensharedresource // Only the sender updates the shared texture. It is not required for the receiver. -// The application can either call Flush alone or combine a flush and Wait using this function. +// The application can either call Flush alone or combine a flush and Wait. // // For an OpenGL sender : // This function is not necessary, the GL/DX interop performs the necessary flushing. // +//--------------------------------------------------------- +// Function: Flush +// Flush immediate context command queue +void spoutDirectX::Flush() +{ + if (!m_pd3dDevice || !m_pImmediateContext) + return; + m_pImmediateContext->ClearState(); + m_pImmediateContext->Flush(); +} //--------------------------------------------------------- // Function: FlushWait @@ -904,6 +1011,7 @@ void spoutDirectX::FlushWait(ID3D11Device* pd3dDevice, ID3D11DeviceContext* pImm if (!pd3dDevice || !pImmediateContext) return; + pImmediateContext->ClearState(); pImmediateContext->Flush(); // CopyResource and Flush are both asynchronous. @@ -939,6 +1047,7 @@ void spoutDirectX::Wait(ID3D11Device* pd3dDevice, ID3D11DeviceContext* pImmediat } } + // // Group: Graphics adapter // @@ -1387,6 +1496,7 @@ bool spoutDirectX::FindNVIDIA(int& nAdapter) } + // // Group: Graphics performance // @@ -1709,43 +1819,41 @@ bool spoutDirectX::IsApplicationPath(const char* path) } #endif - - // // Protected // - -void spoutDirectX::DebugLog(const ID3D11Device* pd3dDevice, const char* format, ...) +void spoutDirectX::DebugLog(ID3D11Device* pd3dDevice, const char* format, ...) { if (!pd3dDevice) return; -// Suppress warning 26826 to use vsprintf_s -#pragma warning(disable:26485) - // - // Output for debug build + // Output for debug using D3D11 SDK layers + // (_DEBUG is defined for debug build) // // *** Manually remove the comment block below if you have D3D11_1SDKLayers.dll installed. *** // // See comments in : ID3D11Device* spoutDirectX::CreateDX11device() // - // Construct the log here in any case to avoid UNREFERENCED_PARAMETER warning + // Suppress warning 26826 to use vsprintf_s +#pragma warning(disable:26485) + // Construct the log now to avoid UNREFERENCED_PARAMETER warning if the block below is disabled char dlog[128]={}; va_list args; va_start(args, format); // An explicit cast to the decayed pointer type prevents the warning vsprintf_s(dlog, 128, format, args); va_end(args); - #pragma warning(default:26485) - -/* + + +// REMOVE THIS COMMENT LINE TO ENABLE SDK LAYERS + #ifdef _DEBUG - + // New line OutputDebugStringA("\n"); OutputDebugStringA(dlog); @@ -1767,16 +1875,18 @@ void spoutDirectX::DebugLog(const ID3D11Device* pd3dDevice, const char* format, filter.DenyList.NumIDs = _countof(hide); filter.DenyList.pIDList = hide; d3dInfoQueue->AddStorageFilterEntries(&filter); - } - // Print live objects to the debug Output window - DebugDevice->ReportLiveDeviceObjects(D3D11_RLDO_DETAIL); + // Print live objects to the debug Output window + DebugDevice->ReportLiveDeviceObjects(D3D11_RLDO_DETAIL); + d3dInfoQueue->Release(); + } DebugDevice->Release(); } + #endif -*/ +// REMOVE THIS COMMENT LINE TO ENABLE SDK LAYERS } diff --git a/SpoutDX/source/SpoutDirectX.h b/SpoutDX/source/SpoutDirectX.h index 3ff9b70..b7131b9 100644 --- a/SpoutDX/source/SpoutDirectX.h +++ b/SpoutDX/source/SpoutDirectX.h @@ -33,6 +33,7 @@ #include "SpoutCommon.h" #include +#include #include // @@ -81,13 +82,15 @@ class SPOUT_DLLEXP spoutDirectX { ID3D11Device* GetDX11Device(); // Return the device immediate context ID3D11DeviceContext* GetDX11Context(); + // Return the device feature level + D3D_FEATURE_LEVEL GetDX11FeatureLevel(); // // DirectX11 texture // // Create a DirectX11 shared texture - bool CreateSharedDX11Texture(ID3D11Device* pDevice, unsigned int width, unsigned int height, DXGI_FORMAT format, ID3D11Texture2D** ppSharedTexture, HANDLE &dxShareHandle, bool bKeyed = false); + bool CreateSharedDX11Texture(ID3D11Device* pDevice, unsigned int width, unsigned int height, DXGI_FORMAT format, ID3D11Texture2D** ppSharedTexture, HANDLE &dxShareHandle, bool bKeyed = false, bool bNThandle = false); // Create a DirectX texture which is not shared bool CreateDX11Texture(ID3D11Device* pDevice, unsigned int width, unsigned int height, DXGI_FORMAT format, ID3D11Texture2D** ppTexture); // Create a DirectX 11 staging texture for read and write @@ -102,9 +105,11 @@ class SPOUT_DLLEXP spoutDirectX { // Release a texture resource created with a class device unsigned long ReleaseDX11Texture(ID3D11Texture2D* pTexture); // Release a texture resource - unsigned long ReleaseDX11Texture(const ID3D11Device* pd3dDevice, ID3D11Texture2D* pTexture); + unsigned long ReleaseDX11Texture(ID3D11Device* pd3dDevice, ID3D11Texture2D* pTexture); // Release a device unsigned long ReleaseDX11Device(ID3D11Device* pd3dDevice); + // Flush immediate context command queue + void Flush(); // Flush immediate context command queue and wait for completion void FlushWait(ID3D11Device* pd3dDevice, ID3D11DeviceContext* pImmediateContext); // Wait for completion after flush @@ -135,6 +140,7 @@ class SPOUT_DLLEXP spoutDirectX { // Find the index of the NVIDIA adapter in a multi-adapter system bool FindNVIDIA(int &nAdapter); + // Windows 10 Vers 1803, build 17134 or later #ifdef NTDDI_WIN10_RS4 @@ -154,11 +160,12 @@ class SPOUT_DLLEXP spoutDirectX { bool IsPreferenceAvailable(); // Is the path a valid application bool IsApplicationPath(const char* path); + #endif protected: - void DebugLog(const ID3D11Device* pd3dDevice, const char* format, ...); + void DebugLog(ID3D11Device* pd3dDevice, const char* format, ...); int m_AdapterIndex; // Adapter index IDXGIAdapter* m_pAdapterDX11; // Adapter pointer ID3D11Device* m_pd3dDevice; // DX11 device @@ -166,6 +173,9 @@ class SPOUT_DLLEXP spoutDirectX { bool m_bClassDevice; D3D_DRIVER_TYPE m_driverType; D3D_FEATURE_LEVEL m_featureLevel; + ID3D11Device1* m_pd3dDevice1; + ID3D11DeviceContext1* m_pImmediateContext1; + }; diff --git a/SpoutDX/source/SpoutFrameCount.cpp b/SpoutDX/source/SpoutFrameCount.cpp index 2bb98c0..fa2a2f1 100644 --- a/SpoutDX/source/SpoutFrameCount.cpp +++ b/SpoutDX/source/SpoutFrameCount.cpp @@ -72,6 +72,11 @@ // remove texture check for default null texture // Code review - Use Microsoft Native Recommended rules // 19.03.23 - WaitFrameSync - do not block if the sender has not created a sync event +// 11.04.23 - OpenFrameSync - correct test for null sender name +// Version 2.007.11 +// 24.04.23 - Replace m_bDisabled with m_bCountDisabled +// 03.07.23 - EnableFrameCount, CreateAccessMutex - add detail to logs +// Version 2.007.012 // // ==================================================================================== // @@ -140,7 +145,7 @@ spoutFrameCount::spoutFrameCount() // Frame counting not disabled specifically for this application. // This can be set by the application if required. - m_bDisabled = false; + m_bCountDisabled = false; #ifdef USE_CHRONO @@ -199,7 +204,7 @@ void spoutFrameCount::SetFrameCount(bool bEnable) if (!m_bFrameCount) { WriteDwordToRegistry(HKEY_CURRENT_USER, "Software\\Leading Edge\\Spout", "Framecount", 1); m_bFrameCount = true; - m_bDisabled = false; // Application disable flag + m_bCountDisabled = false; // Application disable flag } // Do nothing if already set } @@ -212,7 +217,7 @@ void spoutFrameCount::SetFrameCount(bool bEnable) } WriteDwordToRegistry(HKEY_CURRENT_USER, "Software\\Leading Edge\\Spout", "Framecount", 0); m_bFrameCount = false; - m_bDisabled = false; + m_bCountDisabled = false; } } @@ -238,7 +243,7 @@ void spoutFrameCount::EnableFrameCount(const char* SenderName) } // Return if application disabled - if (m_bDisabled) { + if (m_bCountDisabled) { SpoutLogNotice("SpoutFrameCount::EnableFrameCount : application disabled"); return; } @@ -288,18 +293,16 @@ void spoutFrameCount::EnableFrameCount(const char* SenderName) switch(dwError) { case ERROR_INVALID_HANDLE: - SpoutLogError(" Invalid semaphore handle"); + SpoutLogError("SpoutFrameCount::EnableFrameCount - invalid semaphore handle"); break; case ERROR_ALREADY_EXISTS: - SpoutLogNotice("SpoutFrameCount::EnableFrameCount - frame count semaphore [%s] exists", m_CountSemaphoreName); - SpoutLogNotice(" Handle for access [0x%7.7X]", LOWORD(hSemaphore)); + SpoutLogNotice("SpoutFrameCount::EnableFrameCount - frame count semaphore [%s] exists [0x%7.7X]", m_CountSemaphoreName, LOWORD(hSemaphore)); // OK if it already exists - either the sender or receiver can create it break; case ERROR_SUCCESS: - SpoutLogNotice("SpoutFrameCount::EnableFrameCount - frame count semaphore [%s] created", m_CountSemaphoreName); - SpoutLogNotice(" Handle [0x%7.7X]", LOWORD(hSemaphore)); + SpoutLogNotice("SpoutFrameCount::EnableFrameCount - frame count semaphore [%s] created [0x%7.7X]", m_CountSemaphoreName, LOWORD(hSemaphore)); break; default : @@ -322,12 +325,12 @@ void spoutFrameCount::EnableFrameCount(const char* SenderName) void spoutFrameCount::DisableFrameCount() { CleanupFrameCount(); - m_bDisabled = true; + m_bCountDisabled = true; } void spoutFrameCount::PauseFrameCount(bool bPaused) { - m_bDisabled = bPaused; + m_bCountDisabled = bPaused; } @@ -336,7 +339,7 @@ void spoutFrameCount::PauseFrameCount(bool bPaused) // Check status of frame counting bool spoutFrameCount::IsFrameCountEnabled() { - if (!m_bFrameCount || m_bDisabled) + if (!m_bFrameCount || m_bCountDisabled) return false; else return true; @@ -378,6 +381,7 @@ long spoutFrameCount::GetSenderFrame() return m_FrameCount; } + // ----------------------------------------------- // Function: HoldFps // Frame rate control @@ -457,8 +461,8 @@ void spoutFrameCount::HoldFps(int fps) // Used internally to set frame status if frame counting is enabled. void spoutFrameCount::SetNewFrame() { - // Return silently if disabled - if (!m_bFrameCount || m_bDisabled) + // Return silently if frame counting is disabled + if (!m_bFrameCount || m_bCountDisabled) return; // Access the frame count semaphore @@ -496,14 +500,17 @@ void spoutFrameCount::SetNewFrame() // ----------------------------------------------- // Function: GetNewFrame +// Has the sender has produced a new frame. // -// Read the semaphore count to determine if the sender -// has produced a new frame and incremented the counter. -// Counts are recorded as class variables for a receiver. +// If the sender uses the function "SetNewFrame" before sending, +// the current frame count is retrieved by a receiver and compared +// with the last to determine if the frame is new. +// Counts are recorded as receiver class variables. +// +// This function is called within a sender mutex lock so that +// the sender will not write a frame and increment the count +// while a receiver is reading it. // -// This function is called within a sender mutex lock so that -// the sender will not write a frame and increment the -// count while a receiver is reading it. // Used internally to check frame status if frame counting is enabled. // bool spoutFrameCount::GetNewFrame() @@ -511,7 +518,7 @@ bool spoutFrameCount::GetNewFrame() long framecount = 0; // Return silently if disabled - if (!m_bFrameCount || m_bDisabled) + if (!m_bFrameCount || m_bCountDisabled) return true; // A receiver creates or opens a named semaphore when it connects to a sender @@ -708,12 +715,10 @@ bool spoutFrameCount::CreateAccessMutex(const char *SenderName) } // Here we can find if the mutex already exists else if (errnum == ERROR_ALREADY_EXISTS) { - SpoutLogNotice("spoutFrameCount::CreateAccessMutex - texture access mutex [%s] exists", szMutexName); - SpoutLogNotice(" Handle for access [0x%.7X]", PtrToUint(hMutex)); + SpoutLogNotice("spoutFrameCount::CreateAccessMutex - texture access mutex [%s] exists [0x%.7X]", szMutexName, PtrToUint(hMutex)); } else { - SpoutLogNotice("spoutFrameCount::CreateAccessMutex - texture access mutex [%s] created ", szMutexName); - SpoutLogNotice(" Handle [0x%.7X]", PtrToUint(hMutex)); + SpoutLogNotice("spoutFrameCount::CreateAccessMutex - texture access mutex [%s] created [0x%.7X]", szMutexName, PtrToUint(hMutex)); } } @@ -920,6 +925,8 @@ void spoutFrameCount::CloseFrameSync() // bool spoutFrameCount::CheckKeyedAccess(ID3D11Texture2D* pTexture) { + printf("CheckKeyedAccess\n"); + // 85-90 microseconds if (pTexture) { IDXGIKeyedMutex* pDXGIKeyedMutex = nullptr; @@ -928,30 +935,28 @@ bool spoutFrameCount::CheckKeyedAccess(ID3D11Texture2D* pTexture) if (pDXGIKeyedMutex) { const HRESULT hr = pDXGIKeyedMutex->AcquireSync(0, 67); switch (hr) { - case S_OK: // Sync was acquired // Return to process the texture and call AllowAccess to release sync pDXGIKeyedMutex->Release(); - return true; + printf("Sync was acquired\n"); + + return true; case static_cast(WAIT_ABANDONED): // The shared surface and keyed mutex are no longer in a consistent state. SpoutLogError("spoutDirectX::CheckKeyedAccess : WAIT_ABANDONED"); break; - case static_cast(WAIT_TIMEOUT): // The time-out interval elapsed before the key was released. // Can't access the shared texture right now, try again later SpoutLogError("spoutDirectX::CheckKeyedAccess : WAIT_TIMEOUT"); break; - case E_FAIL: // If the owning device attempted to create another keyed mutex // on the same shared resource, AcquireSync returns E_FAIL. SpoutLogError("spoutDirectX::CheckKeyedAccess : E_FAIL"); break; - default: break; } @@ -1000,7 +1005,7 @@ bool spoutFrameCount::IsKeyedMutex(ID3D11Texture2D* D3D11texture) // Applications before 2.007 have a frame rate dependent on the system fps void spoutFrameCount::UpdateSenderFps(long framecount) { - if (m_bDisabled) + if (m_bCountDisabled) return; // If framecount is zero, the sender has not produced a new frame yet @@ -1021,8 +1026,6 @@ void spoutFrameCount::UpdateSenderFps(long framecount) if (frametime > 1.0) { // > 1 msec - // printf("frametime = %f (%f) msec\n", frametime, 1000.0/m_SystemFps); - // Frame time in seconds frametime = frametime/1000.0; @@ -1112,14 +1115,13 @@ void spoutFrameCount::EndTimePeriod() void spoutFrameCount::OpenFrameSync(const char* SenderName) { // A sender name is required - if (!SenderName || *SenderName) { + if (!SenderName || !*SenderName) { SpoutLogWarning("spoutFrameCount::OpenFrameSync - no sender name"); return; } // Return if already enabled for this sender if (m_hSyncEvent && strcmp(SenderName, m_SenderName) == 0) { - SpoutLogNotice("spoutFrameCount::OpenFrameSync : already enabled [0x%.7X]", LOWORD(m_hSyncEvent)); return; } @@ -1132,7 +1134,13 @@ void spoutFrameCount::OpenFrameSync(const char* SenderName) // Set the new name for subsequent checks strcpy_s(m_SenderName, 256, SenderName); + // // Create or open an event with this sender name + // + // Creates an auto-reset event object + // The system automatically resets the event state to nonsignaled + // after a single waiting thread has been released. + // char SyncEventName[256]={}; sprintf_s(SyncEventName, 256, "%s_Sync_Event", SenderName); HANDLE hSyncEvent = CreateEventA( @@ -1140,26 +1148,22 @@ void spoutFrameCount::OpenFrameSync(const char* SenderName) FALSE, // Auto reset FALSE, // Initial state non-signalled SyncEventName); - SpoutLogNotice("spoutFrameCount::OpenFrameSync [%s]", SyncEventName); - switch (GetLastError()) { case ERROR_INVALID_HANDLE: SpoutLogError(" Invalid sync event handle"); return; - case ERROR_ALREADY_EXISTS: - SpoutLogNotice(" Sync event already exists"); + case NO_ERROR: + SpoutLogNotice("spoutFrameCount::OpenFrameSync [%s]", SyncEventName); break; + // No repeat log for ERROR_ALREADY_EXISTS: default: break; } - if (hSyncEvent == NULL) { - SpoutLogError(" Unknown error"); + if (!hSyncEvent) return; - } m_hSyncEvent = hSyncEvent; - SpoutLogNotice(" Sync event handle [0x%.7X]", LOWORD(m_hSyncEvent)); } diff --git a/SpoutDX/source/SpoutFrameCount.h b/SpoutDX/source/SpoutFrameCount.h index 28740ab..30fe653 100644 --- a/SpoutDX/source/SpoutFrameCount.h +++ b/SpoutDX/source/SpoutFrameCount.h @@ -126,6 +126,9 @@ class SPOUT_DLLEXP spoutFrameCount { // Close sync event void CloseFrameSync(); + // LJ DEBUG + bool IsKeyedMutex(ID3D11Texture2D* D3D11texture); + protected: // Texture access named mutex @@ -134,11 +137,11 @@ class SPOUT_DLLEXP spoutFrameCount { // DX11 texture keyed mutex checks bool CheckKeyedAccess(ID3D11Texture2D* D3D11texture); bool AllowKeyedAccess(ID3D11Texture2D* D3D11texture); - bool IsKeyedMutex(ID3D11Texture2D* D3D11texture); + // bool IsKeyedMutex(ID3D11Texture2D* D3D11texture); // Frame count semaphore bool m_bFrameCount; // Registry setting of frame count - bool m_bDisabled; // application disable + bool m_bCountDisabled; // application disable bool m_bIsNewFrame; // received frame is new HANDLE m_hCountSemaphore; // semaphore handle diff --git a/SpoutDX/source/SpoutSenderNames.cpp b/SpoutDX/source/SpoutSenderNames.cpp index dc86f7f..161c4e9 100644 --- a/SpoutDX/source/SpoutSenderNames.cpp +++ b/SpoutDX/source/SpoutSenderNames.cpp @@ -94,6 +94,9 @@ Change from fixed sendername argument to maxlength (default SpoutMaxSenderNameLen) 08.01.23 - FindActiveSender - test max length passed Code review - Use Microsoft Native Recommended rules + Version 2.007.11 + 13.07.23 - setActiveSenderName - close any existing active sender map + Version 2.007.012 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Copyright (c) 2014-2023, Lynn Jarvis. All rights reserved. @@ -402,7 +405,7 @@ int spoutSenderNames::GetSenderCount() { if(SenderSet.size() > 0) { for(iter = SenderSet.begin(); iter != SenderSet.end(); iter++) { namestring = *iter; // the Sender name string - strcpy_s(name, namestring.c_str()); + strcpy_s(name, SpoutMaxSenderNameLen, namestring.c_str()); // we have the name already, so look for it's info if(!getSharedInfo(&name[0], &info)) { // Sender does not exist any more @@ -1070,6 +1073,7 @@ bool spoutSenderNames::CreateSenderSet() SpoutLogError("spoutSenderNames::CreateSenderSet() : SPOUT_CREATE_FAILED"); return false; } + return true; } // end CreateSenderSet @@ -1115,6 +1119,9 @@ bool spoutSenderNames::setActiveSenderName(const char* SenderName) if(len == 0 || len + 1 > SpoutMaxSenderNameLen) return false; + // Close any exsiting map which could contain a different name + if (m_activeSender.Size() > 0) m_activeSender.Close(); + const SpoutCreateResult spoutres = m_activeSender.Create("ActiveSenderName", SpoutMaxSenderNameLen); if (spoutres == SPOUT_CREATE_SUCCESS || spoutres == SPOUT_ALREADY_CREATED diff --git a/SpoutDX/source/SpoutSharedMemory.cpp b/SpoutDX/source/SpoutSharedMemory.cpp index 637862b..eb30e96 100644 --- a/SpoutDX/source/SpoutSharedMemory.cpp +++ b/SpoutDX/source/SpoutSharedMemory.cpp @@ -47,6 +47,9 @@ // 28.10.22 - Code documentation // 18.12.22 - Catch any exception from using Close in destructor // 07.01.23 - Change m_pName from const char* to char* for strdup +// Version 2.007.11 +// 12.05.23 - Create and Open - Clear ERROR_ALREADY_EXISTS to avoid detection elsewhere. +// Version 2.007.012 // ==================================================================================== @@ -121,6 +124,8 @@ SpoutCreateResult SpoutSharedMemory::Create(const char* name, int size) bool alreadyExists = false; if (err == ERROR_ALREADY_EXISTS) { alreadyExists = true; + // Clear the error to avoid detection elsewhere. + SetLastError(NO_ERROR); // The size of the map will be the same as when it was created. // 2.004 apps will have created a 10 sender map which will not be increased in size thereafter. } @@ -195,6 +200,11 @@ bool SpoutSharedMemory::Open(const char* name) return false; } + // If the mutex object existed before this function call, + // GetLastError returns ERROR_ALREADY_EXISTS. + // Clear the error to avoid detection elsewhere. + SetLastError(NO_ERROR); + m_pName = _strdup(name); // OpenFileMapping/MapViewOfFile do not return the map size diff --git a/SpoutDX/source/SpoutUtils.cpp b/SpoutDX/source/SpoutUtils.cpp index 25a7878..c1081cd 100644 --- a/SpoutDX/source/SpoutUtils.cpp +++ b/SpoutDX/source/SpoutUtils.cpp @@ -124,7 +124,13 @@ Change "ConPrint" to "_conprint" and use Writefile instead of cout. 18.01.23 - _conprint - cast WriteFile size argument to DWORD 19.03.23 - Update SDKversion to 2.007.010 - + Version 2.007.011 + 14.04.23 - Update SDKversion to 2.007.011 + 24.04.23 - GetTimer - independent start and end variables startcount/endcount + 09.05.23 - Yellow console text for warnings and errors + 17.05.23 - Set console title to executable name + 04-07-23 - _getLogPath() - allow for getenv if not Microsoft compiler (PR #95) + Version 2.007.012 */ #include "SpoutUtils.h" @@ -160,18 +166,17 @@ namespace spoututils { #ifdef USE_CHRONO std::chrono::steady_clock::time_point start; std::chrono::steady_clock::time_point end; -#else +#endif // PC timer double PCFreq = 0.0; __int64 CounterStart = 0; - double start = 0.0; - double end = 0.0; + double startcount = 0.0; + double endcount = 0.0; double m_FrameStart = 0.0; -#endif // Spout SDK version number string // Major, minor, release - std::string SDKversion = "2.007.010"; + std::string SDKversion = "2.007.012"; // // Group: Information @@ -251,7 +256,16 @@ namespace spoututils { if (AllocConsole()) { const errno_t err = freopen_s(&pCout, "CONOUT$", "w", stdout); if (err == 0) { - SetConsoleTitleA("Spout Log"); + char title[MAX_PATH]={}; + if (GetModuleFileNameA(GetCurrentModule(), title, MAX_PATH) > 0) { + PathStripPathA(title); + PathRemoveExtensionA(title); + strcat_s(title, MAX_PATH, ".log"); // Executable name + SetConsoleTitleA(title); + } + else { + SetConsoleTitleA("Spout Log"); + } bConsole = true; // Disable close button // HMENU hmenu = GetSystemMenu(GetConsoleWindow(), FALSE); @@ -376,7 +390,16 @@ namespace spoututils { // Console output if (GetConsoleWindow()) { - SetConsoleTitleA("Spout Log"); + char title[MAX_PATH]={}; + if (GetModuleFileNameA(GetCurrentModule(), title, MAX_PATH) > 0) { + PathStripPathA(title); + PathRemoveExtensionA(title); + strcat_s(title, MAX_PATH, ".log"); // Executable name + SetConsoleTitleA(title); + } + else { + SetConsoleTitleA("Spout Log"); + } bConsole = true; } else if(!bConsole) @@ -613,7 +636,11 @@ namespace spoututils { { va_list args; va_start(args, format); + // Show warning text bright yellow + HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY); _doLog(SPOUT_LOG_WARNING, format, args); + SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); va_end(args); } @@ -677,6 +704,10 @@ namespace spoututils { // Console logging if (bEnableLog && bConsole) { FILE* out = stdout; // Console output + // Yellow text for warnings and errors + HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); + if (level == SPOUT_LOG_WARNING || level == SPOUT_LOG_ERROR) + SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY); if (level != SPOUT_LOG_NONE) { // Show log level fprintf(out, "[%s] ", _levelName(level).c_str()); @@ -685,7 +716,8 @@ namespace spoututils { vfprintf(out, format, args); // Newline fprintf(out, "\n"); - + // Reset white text + SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); } // end console log // File logging @@ -1147,15 +1179,17 @@ namespace spoututils { #else // Start timing period void StartTiming() { - start = GetCounter(); + // startcount = GetCounter(); + StartCounter(); } // Stop timing and return microseconds elapsed. // Console output can be enabled for quick timing tests. double EndTiming() { - end = GetCounter(); - return (end-start); + endcount = GetCounter(); + return (endcount-startcount); } +#endif // ----------------------------------------------- // Set counter start @@ -1190,8 +1224,6 @@ namespace spoututils { } } -#endif - // // Private functions @@ -1262,7 +1294,11 @@ namespace spoututils { size_t len = 0; bool bSuccess = true; errno_t err = 0; - err = _dupenv_s(&appdatapath, &len, "APPDATA"); + #if defined(_MSC_VER) + err = _dupenv_s(&appdatapath, &len, "APPDATA"); + #else + appdatapath = getenv("APPDATA"); + #endif if (err == 0 && appdatapath) { strcpy_s(logpath, MAX_PATH, appdatapath); strcat_s(logpath, MAX_PATH, "\\Spout"); diff --git a/SpoutDX/source/SpoutUtils.h b/SpoutDX/source/SpoutUtils.h index cbbc149..9376928 100644 --- a/SpoutDX/source/SpoutUtils.h +++ b/SpoutDX/source/SpoutUtils.h @@ -153,12 +153,12 @@ namespace spoututils { // Is file logging enabled bool SPOUT_DLLEXP LogFileEnabled(); - // Return the log file as a string - std::string SPOUT_DLLEXP GetSpoutLog(const char* filepath = nullptr); - // Return the full log file path std::string SPOUT_DLLEXP GetSpoutLogPath(); + // Return the log file as a string + std::string SPOUT_DLLEXP GetSpoutLog(const char* filepath = nullptr); + // Show the log file folder in Windows Explorer void SPOUT_DLLEXP ShowSpoutLogs(); @@ -252,10 +252,9 @@ namespace spoututils { #ifdef USE_CHRONO // Microseconds elapsed since epoch double SPOUT_DLLEXP ElapsedMicroseconds(); -#else +#endif void SPOUT_DLLEXP StartCounter(); double SPOUT_DLLEXP GetCounter(); -#endif // // Private functions diff --git a/binaries/SPOUTCAM/SpoutCam32/SpoutCam32.ax b/binaries/SPOUTCAM/SpoutCam32/SpoutCam32.ax index a711314..1204f86 100644 Binary files a/binaries/SPOUTCAM/SpoutCam32/SpoutCam32.ax and b/binaries/SPOUTCAM/SpoutCam32/SpoutCam32.ax differ diff --git a/binaries/SPOUTCAM/SpoutCam64/SpoutCam64.ax b/binaries/SPOUTCAM/SpoutCam64/SpoutCam64.ax index aeb52f9..b237500 100644 Binary files a/binaries/SPOUTCAM/SpoutCam64/SpoutCam64.ax and b/binaries/SPOUTCAM/SpoutCam64/SpoutCam64.ax differ diff --git a/lib/Win32/Release/BaseClasses.pdb b/lib/Win32/Release/BaseClasses.pdb index 5b90c28..b3d0690 100644 Binary files a/lib/Win32/Release/BaseClasses.pdb and b/lib/Win32/Release/BaseClasses.pdb differ diff --git a/lib/Win32/Release/SpoutDX.pdb b/lib/Win32/Release/SpoutDX.pdb index 95c54fe..490921f 100644 Binary files a/lib/Win32/Release/SpoutDX.pdb and b/lib/Win32/Release/SpoutDX.pdb differ diff --git a/lib/x64/Debug/BaseClasses.pdb b/lib/x64/Debug/BaseClasses.pdb new file mode 100644 index 0000000..f6b0943 Binary files /dev/null and b/lib/x64/Debug/BaseClasses.pdb differ diff --git a/lib/x64/Debug/SpoutDX.idb b/lib/x64/Debug/SpoutDX.idb new file mode 100644 index 0000000..6adb155 Binary files /dev/null and b/lib/x64/Debug/SpoutDX.idb differ diff --git a/lib/x64/Debug/SpoutDX.pdb b/lib/x64/Debug/SpoutDX.pdb new file mode 100644 index 0000000..01987a0 Binary files /dev/null and b/lib/x64/Debug/SpoutDX.pdb differ diff --git a/lib/x64/Release/BaseClasses.pdb b/lib/x64/Release/BaseClasses.pdb index d3dfa07..9e8bfc8 100644 Binary files a/lib/x64/Release/BaseClasses.pdb and b/lib/x64/Release/BaseClasses.pdb differ diff --git a/lib/x64/Release/SpoutDX.pdb b/lib/x64/Release/SpoutDX.pdb index 412d1d8..cb66e6c 100644 Binary files a/lib/x64/Release/SpoutDX.pdb and b/lib/x64/Release/SpoutDX.pdb differ diff --git a/source/cam.cpp b/source/cam.cpp index 9eb0462..c1985cd 100644 --- a/source/cam.cpp +++ b/source/cam.cpp @@ -294,8 +294,12 @@ Correct if sized to active sender - width must be a multiple of 4 Rebuild x86/x64 VS2022 /MT Version 2.027 - - + 17.04.23 SpoutCopy - add rgba_to_rgb_sse and rgba_to_bgr_sse + Used in receiveimage + Rebuild x86/x64 VS2022 /MT + Version 2.028 + 25.07.23 Rebuild x86/x64 with Spout version 2.007.011 files + VS2022 /MT Version 2.029 */ #pragma warning(disable:4244) @@ -325,8 +329,8 @@ CUnknown * WINAPI CVCam::CreateInstance(LPUNKNOWN lpunk, HRESULT *phr) // Console window // OpenSpoutConsole(); // Empty console // EnableSpoutLog(); // Show error logs - // EnableSpoutLogFile("SpoutCamDX_2027"); - // SpoutLog("SpoutCamDX ~ Vers 2.027\n"); + // EnableSpoutLogFile("SpoutCamDX_2023"); + // SpoutLog("SpoutCamDX ~ Vers 2.029\n"); // For clear options dialog for scaled display SetProcessDPIAware(); diff --git a/source/version.h b/source/version.h index 5a3fa8e..622f9ce 100644 --- a/source/version.h +++ b/source/version.h @@ -9,7 +9,7 @@ #define _VER_MAJORVERSION_STRING L"2" #define _VER_MINORVERSION 1 -#define _VER_MINORVERSION_STRING L"027" +#define _VER_MINORVERSION_STRING L"029" #define _VER_BUGFIXVERSION 0 #define _VER_BUGFIXVERSION_STRING L"0"