-
Notifications
You must be signed in to change notification settings - Fork 135
/
Spout.cpp
2203 lines (1957 loc) · 75.1 KB
/
Spout.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
//
// Spout SDK
//
// Spout.cpp
//
// Documentation <https://spoutgl-site.netlify.app>
//
// ====================================================================================
// Revisions :
//
// 14-07-14 - SelectSenderPanel - return true was missing.
// 16-07-14 - deleted fbo & texture in SpoutCleanup - test for OpenGL context
// - used CopyMemory in FlipVertical instead of memcpy
// - cleanup
// 18-07-14 - removed SpoutSDK local fbo and texture - used in the interop class now
// 22-07-14 - added option for DX9 or DX11
// 25-07-14 - Malcolm Bechard mods to header to enable compilation as a dll
// - ReceiveTexture - release receiver if the sender no longer exists
// - ReceiveImage same change - to be tested
// 27-07-14 - CreateReceiver - bUseActive flag instead of null name
// 31-07-14 - Corrected DrawTexture aspect argument
// 01-08-14 - TODO - work on OpenReceiver for memoryshare
// 03-08-14 - CheckSpoutPanel allow for unregistered sender
// 04-08-14 - revise CheckSpoutPanel
// 05-08-14 - default true for setverticalsync in sender and receiver classes
// 11-08-14 - fixed incorrect name arg in OpenReceiver for ReceiveTexture / ReceiveImage
// 2.004 release 19-08-14
// 24-08-14 - changed back to WM_PAINT message instead of RedrawWindow due to FFGL receiver bug appearing again
// 27-08-14 - removed texture init check from SelectSenderPanel
// 29-08-14 - changed SelectSenderPanel to use revised SpoutPanel with user message support
// 03.09.14 - cleanup
// 15.09.14 - protect against null string copy in SelectSenderPanel
// 22.09.14 - checking of bUseAspect function in CreateReceiver
// 23.09.14 - test for DirectX 11 support in SetDX9 and GetDX9
// 24.09.14 - updated project file for DLL to include SpoutShareMemory class
// 28.09.14 - Added GL format for SendImage and FlipVertical
// - Added bAlignment (4 byte alignment) flag for SendImage
// - Added Host FBO for SendTexture, DrawToSharedTexture
// - Added Host FBO for ReceiveTexture
// 11.10.14 - Corrected UpdateSender to recreate sender using CreateInterop
// - Corrected SelectSenderpanel so that an un-initialized string is not used
// 12.10.14 - Included SpoutPanel always bring to topmost in SelectSenderPanel
// - allowed for change of sender size in DrawToSharedTexture
// 15.10.14 - added debugging aid for texture access locks
// 29.10.14 - changes to SendImage
// 23.12.14 - added host fbo arg to ReceiveImage
// 30.01.15 - Read SpoutPanel path from registry (dependent on revised installer)
// Next path checked is module path, then current working directory
// 06.02.15 - added #pragma comment(lib,.. for "Shell32.lib" and "Advapi32.lib"
// 10.02.15 - added Optimus NvOptimusEnablement export to Spout.h - should apply to all apps including this SDK.
// 22.02.15 - added FindFileVersion for future use
// 24.05.15 - Registry read of sender name for CheckSpoutPanel (see SpoutPanel)
// 29.05.15 - Included SetAdapter for multiple adapters - Franz Hildgen.
// 01.06.15 - Read/Write DX9 mode from registry
// 02.06.15 - Added GetAdapter, GetNumAdapters, GetAdapterName
// 04.07.15 - corrected "const char *" arg for GetSenderInfo
// 08.07.15 - Recompile for global DX9 flag
// 01.08.15 - OpenReceiver - safety in case no opengl context
// 22.08.15 - Change to CheckSpoutPanel to wait for SpoutPanel mutex to open and then close
// 24.08.15 - Added GetHostPath to retrieve the path of the host that produced the sender
// 01.09.15 - added MessageBox error warnings in InitSender for better user diagnostics
// also added MessageBox warnings in SpoutGLDXinterop::CreateInterop
// 09.09.15 - included g_ShareHandle in CheckSpoutPanel
// - removed bGLDXcompatibleShareInitOK becasue there is no single initialization any more
// 12.09.15 - Incremented application sender name if one already exists with the same name
// - Finalised revised SpoutMemoryShare class and functions
// 15.09.15 - Disable memoryshare if the 2.005 installer has not set the "MemoryShare" key
// to avoid problems with 2.004 apps.
// - Change logic of OpenSpout so that fails for incompatible hardware
// if memoryshare is not set. Only 2.005 apps can set memoryshare.\
// 19.09.15 - Changed GetImageSize to look for NULL sharehandle of a sender to determine
// if it is memoryshare. Used by SpoutCam.
// 22.09.15 - Fixed memoryshare sender update in UpdateSender
// 25.09.15 - Changed SetMemoryShareMode for 2.005 - now will only set true for 2.005 and above
// 09.10.15 - DrawToSharedTexture - invert default false instead of true
// 10.10.15 - CreateSender - introduced a temporary DX shared texture for 2.005 memoryshare to prevent
// a crash with existing 2.004 apps
// 22.10.15 - Changed CheckSpoutPanel so that function variables are only created if SpoutPanel has been opened
// 26.10.15 - Added bIsSending and bIsReceiving for safety release of sender in destructor.
// 14.11.15 - changed functions to "const char *" where required
// 18.11.15 - added CheckReceiver so that DrawSharedTexture can be used by a receiver
// 24.11.15 - changes to CheckSpoutPanel to favour ActiveSender over the Registry sender name (used by VVVV)
// - Reintroduced 250msec sleep after SpoutPanel activation
// 29.11.15 - fixed const char problem in ReadPathFromRegistry
// 18.01.16 - added CleanSenders before opening a new sender in case of orphaned sender names in the list
// 10.02.16 - added RemovePathFromRegistry
// 26.02.16 - recompile for Processing library 2.0.5.2 release
// 06.03.16 - added GetSpoutSenderName() and IsSpoutInitialized() for access to globals
// 17.03.16 - removed alignment argument from ReceiveImage
// Check for bgra extensions in receiveimage and sendimage
// Support only for rgba or bgra
// Changed to const unsigned char for Sendimage buffer
// 21.03.16 - Added glFormat and bInvert to SendImage
// - Included LoadGLextensions in InitSender and InitReceiver for memoryshare mode.
// 24.03.16 - Added HostFBO argument to WriteMemory and ReadMemory function calls.
// 04.04.16 - Added HostFBO argument to SendImage - only used for texture share
// Merge from Smokhov https://github.com/leadedge/Spout2/pull/14
// - Changed default invert flag for SendImage to true.
// 24.04.16 - Added IsPBOavailable to test for PBO support.
// 04.05.16 - SetPBOavailable(true/false) added to enable/disable pbo functions
// 07.05.16 - SetPBOavailable changed to SetBufferMode
// 18.06.16 - Add invert to ReceiveImage
// 2.005 release 23-06-16
// 29.06.16 - Added ReportMemory() for debugging
// - Changed OpenSpout to fail for DX9 if no hwnd
// https://github.com/leadedge/Spout2/issues/18
// 03.07.16 - Fix dwFormat repeat declaration in InitSender
// 15.01.17 - Add GetShareMode, SetShareMode
// 18.01.17 - GetImageSize redundant for 2.006
// 22.01.17 - include zero char in SelectSenderPanel NULL arg checks
// 25.05.17 - corrected SendImage UpdateSender to use passed width and height
// 2.006 release 08-02-17
//
// VS2015
//
// 02.06.17 - Registry functions moved to SpoutUtils
// 06.06.17 - Added GLDXavailable to OpenSpout
// 09.06.17 - removed g_TexID - not used
// 05.10.17 - https://github.com/leadedge/Spout2/issues/24
// - OpenReceiver simplify code
// - CheckSpoutPanel simplify code, remove text file sender retrieval
// - Add InitReceiver override to include sharehandle and format args
// 10.03.18 - Noted that change to OpenReceiver for offscreen rendering
// not needed because hwnd can be null for spoutdx.CreateDX9device
// https://github.com/leadedge/Spout2/issues/18
//
// VS2017
//
// 23.08.18 - Add SendFboTexture - see changes to WriteGLDXtexture in SpoutGLDXinterop.cpp
// 17.10.18 - Retrieve global render window handle in OpenSpout
// 01.11.18 - SendImage bInvert default false to align with SpoutSender.cpp
// 01.11.18 - Changes to SelectSenderPanel to terminate SpoutPanel if it has crashed.
// 03.11.18 - Texture creation patch for compatibility with 2.004 removed for Spout 2.007
// 13.11.18 - Remove CPU mode
// 24.11.18 - Remove redundant GetImageSize
// 27.11.18 - Add RemovePadding for correction of image stride
// 28.11.18 - Add IsFrameNew and HasNewFrame
// 14.12.18 - Clean up for SpoutLibrary
// 15.12.18 - UpdateSender - release and re-create sender to avoid memory leak
// 17.12.18 - Change Spout dll Project properties to / MT
// 28.12.18 - Check mutex handle before close in CheckSpoutPanel
// 28.12.18 - Rebuild Spout.dll 32 / 64bit - Version 2.007
// 03.01.19 - Changed to revised registry functions in SpoutUtils
// 04.01.19 - Add OpenGL window creation functions for SpoutLibrary
// 05.01.19 - Change names for high level receiver functions for SpoutLibrary
// 16.01.19 - Fix ReceiveTextureData for sender name change
// 22.01.19 - Remove unsused bIsReceiving flag
// 05.03.19 - Add log notice for ReleaseSender
// 05.04.19 - Change GetSenderName to GetSender
// Reserve const char * GetSenderName for receiver class
// 17.06.19 - Fix missing log warning argument in UpdateSender
// 26.06.19 - Cleanup changes to UpdateSender
// 13.01.20 - Removed sleep time for SpoutPanel to open
// 19.01.20 - Change SendFboTexture to SendFbo
// 20.01.20 - Corrected SendFbo for width/height < shared texture
// 21.01.20 - Remove auto sender update in send functions
// Remove debug print from InitSender
// 25.05.20 - Correct filename case for all #includes throughout
// 14.07.20 - Removed unused bChangeRequested flag
// 18.07.20 - Rebuild binaries Win32 and x64 /MT VS2017
// 04.09.20 - Dynamic switch between memory and texture share modes
// 05.09.20 - OpenReceiver - Switch to memoryshare if receiver and sender use different GPU
// See SpoutGLDXinterop to set adapter index to "usage" field in sender shared memory
// 06.09.20 - Do not change share mode flags in SpoutCleanup
// 07.09.20 - Correct receiver switch from memory to texture if texture compatible
// 08.09.20 - OpenReceiver - remove warning log for receiver and sender using a different GPU
// InitSender - switch to memoryshare on CreateInterop failure
// 09.09.20 - SetAdapter - reset and perform compatibility test
// 15.09.20 - Remove SpoutMessageBox from OpenSpout()
// Failure must be handled by the application
// 17.09.20 - Change GetMemoryShare(const char* sendername) to
// GetSenderMemoryShare(const char* sendername) for compatibility with SpoutLibrary
// Add GetSenderAdapter
// 18.09.20 - Add SetSenderAdapter
// 22.09.20 - OpenReceiver sender/receiver GPU check
// 23.09.20 - Corrected SetSenderAdapter
// Logic corrections
// 24.09.20 - Correction of SetSenderAdapter as bool not void
// 25.09.20 - Remove GetSenderAdapter/SetSenderAdapter - not reliable
// 17.10.20 - Change SetDX9format from D3D_FORMAT to DWORD
// 27.12.20 - Multiple changes for SpoutGL base class - see SpoutSDK.cpp
// Remove DX9 support
// CPU backup enhanced using dual DirectX staging textures
// Auto switch to CPU backup if GL/DX incompatible
// 10.01.21 - SetSenderName - auto increment of sender name if the sender already exists
// 12.01.21 - Release orphaned senders in SelectSenderPanel
// - CheckSender : write host path to the sender shared memory Description field
// in spoutSenderNames::CreateSender
// 13.01.21 - Release orphaned senders in SpoutPanel.exe instead of SelectSenderPanel
// Additional checks for un-registered senders
// 18.01.21 - ReceiveSenderData : Check if the name is in the sender list
// 26.02.21 - Add GetSenderGLDXready() for receiver
// 01.03.21 - Add SetSenderID
// 11.03.21 - Rename functions GetSenderCPU and GetSenderGLDX
// 13.03.21 - memoryshare.CloseSenderMemory() in ReleaseSender
// 15.03.21 - IsFrameNew - return frame.IsFrameNew()
// 20.03.21 - memoryshare.CloseSenderMemory() in ReleaseReceiver
// 02.04.21 - Add event functions SetFrameSync/WaitFrameSync
// - Add data functions WriteMemoryBuffer/ReadMemoryBuffer
// 07.04.21 - Close sync event in ReleaseSender
// 20.04.21 - SendFbo - protect against SendFbo fail for default framebuffer if iconic
// 24.04.21 - ReceiveTexture - return if flagged for update
// only if there is a texture to receive into.
// 10.05.21 - ReceiveTexture - allow for the possibility of 2.006 memoryshare sender.
// 22.06.21 - Move code for GetSenderCount and GetSender to SpoutSenderNames class
// 03.07.21 - Use changed SpoutSenderNames "GetSender" function name.
// 04.07.21 - Additional code comments concerning update in ReceiveTexture.
// 12.08.21 - CreateReceiver - Revise CreateReceiver to avoid switch to active
// if the selected sender closes.
// 15.10.21 - Allow no argument for SetReceiverName
// 07.11.21 - Remove pbo available flag to use ReadGLDXpixels in ReceiveImage
// it is tested within ReadGLDXpixels
// 20.11.21 - Destructor virtual for base class
// 22.11.21 - Use SpoutDirectX ReleaseDX11Texture to release shared texture
// - Remove adapter gets from constructor
// 17.12.21 - Remove adapter gets from Sender/Receiver init
// Adapter index and name are retrieved with Get functions
// 20.12.21 - Restore log notice for ReleaseSender
// 24.02.22 - Restore GetSenderAdpater for testing
//
// ====================================================================================
/*
Copyright (c) 2014-2022, Lynn Jarvis. All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
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.
*/
#include "Spout.h"
// Class: Spout
//
// <https://spout.zeal.co/>
//
// Main class for Spout OpenGL texture sharing
//
// Contains both Sender and Receiver functions.
//
// This class and other source files are included in a project
//
// Files required are (.h and .cpp) :
//
// - Spout
// - SpoutCommon
// - SpoutCopy
// - SpoutDirectX
// - SpoutFramecount
// - SpoutGL
// - SpoutGLextensions
// - SpoutSenderNames
// - SpoutSharedMemory
// - SpoutUtils
//
// Note that Sender and Receiver functions cannot be used within the same object.
// The SpoutSender and SpoutReceiver classes are convenience wrappers which assist
// the programmer by exposing only sender or receiver specific functions.
//
// - SpoutSender
// - SpoutReceiver
//
// You can also use the Spout SDK as a dll. To build the dll, refer to the
// Visual Studio project in the VS2017 folder and the CMake build documentation.
// Also refer to the SpoutLibrary folder for a C-compatible dll which can be
// used with compilers other than Visual Studio.
//
// For conversion of existing 2.006 applications, refer to "Porting.txt" in the "Docs" section
// as well as the introductory document *SpoutSDK_2007.pdf*.
//
// More detailed information can be found in the header files for each class.
// Functions for individual classes are documented within the respective source files.
// You can access these from the following objects that are included in the Spout class.
//
// - spoutDirectX spoutdx; (DirectX 11 texture sharing)
// - spoutCopy spoutcopy; (Pixel data copy)
// - spoutSenderNames sendernames; (Spout sender management)
// - spoutFrameCount frame; (Frame counting management)
//
// Details for practical use can be found in the source code for the Openframeworks examples.
// The methods are simple and you should be able to quickly extend to your own application
// or to other frameworks.
//
// Refer to the SpoutGL base class for further documentation and details.
//
Spout::Spout()
{
// Initialize adapter name global
// Adapter index and name are retrieved with create sender or receiver
m_AdapterName[0] = 0;
m_bAdapt = false; // Receiver adapt to the sender adapter
}
Spout::~Spout()
{
// ~spoutGL will release dependent objects
}
//
// Group: Sender
//
// SendFbo, SendTexture and SendImage create or update a sender as required.
//
// - If a sender has not been created yet :
//
// - Make sure Spout has been initialized and OpenGL context is available
// - Perform a compatibility check for GL/DX interop
// - If compatible, create interop for GL/DX transfer
// - If not compatible, create a DirectX 11 shared texture for the sender
// - Create a sender using the DX11 shared texture handle
//
// - If the sender exists, test for size change :
//
// - If compatible, update the shared textures and GL/DX interop
// - If not compatible, re-create the class DirectX shared texture to the new size
// - Update the sender and class variables
//
//---------------------------------------------------------
// Function: SetSenderName
// Set name for sender creation
//
// If no name is specified, the executable name is used.
// Thereafter, all sending functions create and update a sender
// based on the size passed and the name that has been set
void Spout::SetSenderName(const char* sendername)
{
if (!sendername) {
// Get executable name as default
GetModuleFileNameA(NULL, m_SenderName, 256);
PathStripPathA(m_SenderName);
PathRemoveExtensionA(m_SenderName);
}
else {
strcpy_s(m_SenderName, 256, sendername);
}
// If a sender with this name is already registered, create an incremented name
int i = 1;
char name[256];
strcpy_s(name, 256, m_SenderName);
if (sendernames.FindSenderName(name)) {
do {
sprintf_s(name, 256, "%s_%d", m_SenderName, i);
i++;
} while (sendernames.FindSenderName(name));
}
// Re-set the global sender name
strcpy_s(m_SenderName, 256, name);
}
//---------------------------------------------------------
// Function: SetSenderFormat
// Set the sender DX11 shared texture format
// Compatible formats - see SpoutGL::SetDX11format
void Spout::SetSenderFormat(DWORD dwFormat)
{
m_dwFormat = dwFormat;
// Update SpoutGL class global texture format
SetDX11format((DXGI_FORMAT)dwFormat);
}
//---------------------------------------------------------
// Function: ReleaseSender
// Close receiver and release resources.
//
// A new sender is created or updated by all sending functions
void Spout::ReleaseSender()
{
SpoutLogNotice("Spout::ReleaseSender(%s)", m_SenderName);
if (m_bInitialized) {
sendernames.ReleaseSenderName(m_SenderName);
frame.CleanupFrameCount();
frame.CloseAccessMutex();
}
// Close shared memory and sync event if used
memoryshare.Close();
frame.CloseFrameSync();
// Release OpenGL resources
// OpenGL only - do not close DirectX
CleanupGL();
}
//---------------------------------------------------------
// Function: SendFbo
// Send a framebuffer
//
// The fbo must be bound for read.
//
// The fbo can be larger than the size that the sender is set up for.
// For example, if the application is using only a portion of the allocated texture space,
// such as for Freeframe plugins. (The 2.006 equivalent is DrawToSharedTexture).
// The function can also be used with the OpenGL default framebuffer by
// specifying "0" for the fbo ID.
//
bool Spout::SendFbo(GLuint FboID, unsigned int width, unsigned int height, bool bInvert)
{
// For texture sharing, the size of the texture attached to the
// fbo must be equal to or larger than the shared texture
if (width == 0 || height == 0) {
return false;
}
// Default framebuffer fails if iconic
if (FboID == 0 && IsIconic(m_hWnd))
return false;
// Create or update the sender
if (!CheckSender(width, height)) {
return false;
}
// All clear to send the fbo texture
if(m_bTextureShare) {
// 3840-2160 - 60fps (0.45 msec per frame)
return WriteGLDXtexture(0, 0, width, height, bInvert, FboID);
}
else if (m_bCPUshare) {
// Auto share enabled for DirectX CPU backup
// 3840-2160 - 43fps (5-7msec/frame)
// Create a local class texture if not already
CheckOpenGLTexture(m_TexID, GL_RGBA, width, height);
// Copy from the texture attached to the bound fbo to the class texture
glBindTexture(GL_TEXTURE_2D, m_TexID);
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, width, height);
glBindTexture(GL_TEXTURE_2D, 0);
// Copy from the OpenGL class texture to the shared DX11 texture by way of staging texture
return WriteDX11texture(m_TexID, GL_TEXTURE_2D, width, height, bInvert, FboID);
}
return false;
}
//---------------------------------------------------------
// Function: SendTexture
// Send OpenGL texture
//
// SendTexture creates a shared texture for all receivers to access.
//
// The invert flag is optional and by default true. This flips the texture
// in the Y axis, which is necessary because DirectX and OpenGL textures
// are opposite in Y. If it is set to false no flip occurs and the result
// may appear upside down.
//
// The ID of a currently bound fbo should be passed in.
//
bool Spout::SendTexture(GLuint TextureID, GLuint TextureTarget,
unsigned int width, unsigned int height, bool bInvert, GLuint HostFBO)
{
// Quit if no data
if (TextureID <= 0 || width == 0 || height == 0)
return false;
// Create or update the sender
// < 0.001 msec
if (!CheckSender(width, height))
return false;
if (m_bTextureShare) {
// Send OpenGL texture if GL/DX interop compatible
// 3840-2160 - 60fps (0.45 msec per frame)
return WriteGLDXtexture(TextureID, TextureTarget, width, height, bInvert, HostFBO);
}
else if (m_bCPUshare) {
// Auto share enabled for DirectX CPU backup
// 3840-2160 47fps (6-7 msec per frame with PBOs)
return WriteDX11texture(TextureID, TextureTarget, width, height, bInvert, HostFBO);
}
return false;
}
//---------------------------------------------------------
// Function: SendImage
// Send pixel image
//
// SendImage creates a shared texture using image pixels as the source
// instead of an OpenGL texture. The format of the image to be sent is RGBA
// by default but can be a different OpenGL format, for example GL_RGB or GL_BGRA_EXT.
//
// The invert flag is optional and false by default.
//
// The ID of a currently bound fbo should be passed in.
//
bool Spout::SendImage(const unsigned char* pixels, unsigned int width, unsigned int height, GLenum glFormat, bool bInvert, GLuint HostFBO)
{
// Dimensions should be the same as the sender
if (!pixels || width == 0 || height == 0)
return false;
// Only RGBA, BGRA, RGB, BGR supported
if (!(glFormat == GL_RGBA || glFormat == GL_BGRA_EXT || glFormat == GL_RGB || glFormat == GL_BGR_EXT))
return false;
// Check for BGRA support
GLenum glformat = glFormat;
if (!m_bBGRAavailable) {
// If the bgra extensions are not available and the user
// provided GL_BGR_EXT or GL_BGRA_EXT do not use them
if (glFormat == GL_BGR_EXT) glformat = GL_RGB;
if (glFormat == GL_BGRA_EXT) glformat = GL_RGBA;
}
// Create or update the sender
if (!CheckSender(width, height))
return false;
//
// Write pixel data to the rgba shared texture according to pixel format
//
if (m_bTextureShare) {
// Texture share compatible
return WriteGLDXpixels(pixels, width, height, glformat, bInvert, HostFBO);
}
else if (m_bCPUshare) {
// Auto share enabled for DirectX CPU backup
return WriteDX11pixels(pixels, width, height, glformat, bInvert);
}
return false;
}
//---------------------------------------------------------
// Function: IsInitialized
// Initialization status
bool Spout::IsInitialized()
{
return m_bInitialized;
}
//---------------------------------------------------------
// Function: GetName
// Sender name
const char * Spout::GetName()
{
return m_SenderName;
}
//---------------------------------------------------------
// Function: GetWidth
// Sender width
unsigned int Spout::GetWidth()
{
return m_Width;
}
//---------------------------------------------------------
// Function: GetHeight
// Sender height
unsigned int Spout::GetHeight()
{
return m_Height;
}
//---------------------------------------------------------
// Function: GetFps
// Sender frame rate
double Spout::GetFps()
{
return frame.GetSenderFps();
}
//---------------------------------------------------------
// Function: GetFrame
// Sender frame number
long Spout::GetFrame()
{
return frame.GetSenderFrame();
}
//---------------------------------------------------------
// Function: GetHandle
// Sender share handle
HANDLE Spout::GetHandle()
{
return m_dxShareHandle;
}
//---------------------------------------------------------
// Function: GetCPU
// Sender sharing method.
// Returns true if the sender is using CPU methods
bool Spout::GetCPU()
{
return m_bSenderCPU;
}
//---------------------------------------------------------
// Function: GetGLDX
//Sender sharing compatibility.
// Returns true if the sender graphics hardware is
// compatible with NVIDIA NV_DX_interop2 extension
bool Spout::GetGLDX()
{
return m_bSenderGLDX;
}
//
// Group: Receiver
//
// Receiving functions
//
// ReceiveTexture and ReceiveImage
//
// - Connect to a sender
//
// - Set class variables for sender name, width and height
//
// - If the sender has changed size, set a flag for the application to update the receiving texture or image if IsUpdated() returns true.
//
// - Copy the sender shared texture to the user texture or image.
//
// Any changes to sender size are managed. However, if you are receiving to a local texture or image,
// the application must check for update at every cycle before receiving any data using "IsUpdated()"
//---------------------------------------------------------
// Function: SetReceiverName
// Specify sender for connection
//
// The if a name is specified, the receiver will not connect to any other unless the user selects one
// If that sender closes, the receiver will wait for the nominated sender to open
// If no name is specified, the receiver will connect to the active sender
void Spout::SetReceiverName(const char * SenderName)
{
if (SenderName && SenderName[0]) {
// Connect to the specified sender
strcpy_s(m_SenderNameSetup, 256, SenderName);
strcpy_s(m_SenderName, 256, SenderName);
}
else {
// Connect to the active sender
m_SenderNameSetup[0] = 0;
m_SenderName[0] = 0;
}
}
//---------------------------------------------------------
// Function: ReleaseReceiver
// Close receiver and release resources ready to connect to another sender
void Spout::ReleaseReceiver()
{
if (!m_bInitialized)
return;
// Restore the starting sender name if the user specified one in SetReceiverName
if (m_SenderNameSetup[0]) {
strcpy_s(m_SenderName, 256, m_SenderNameSetup);
}
else {
m_SenderName[0] = 0;
}
// Wait 4 frames in case the same sender opens again
Sleep(67);
// Close the named access mutex and frame counting semaphore.
frame.CloseAccessMutex();
frame.CleanupFrameCount();
// Zero width and height so that they are reset when a sender is found
m_Width = 0;
m_Height = 0;
// Reset the received sender texture
if (m_pSharedTexture)
spoutdx.ReleaseDX11Texture(GetDX11Device(), m_pSharedTexture);
m_pSharedTexture = nullptr;
m_dxShareHandle = nullptr;
// Reset connected sender share mode and compatibility.
// Assume texture share and hardware compatible by default.
m_bSenderCPU = false;
m_bSenderGLDX = true;
// Release staging textures if they have been used
if (m_pStaging[0]) spoutdx.ReleaseDX11Texture(spoutdx.GetDX11Device(), m_pStaging[0]);
if (m_pStaging[1]) spoutdx.ReleaseDX11Texture(spoutdx.GetDX11Device(), m_pStaging[1]);
m_pStaging[0] = nullptr;
m_pStaging[1] = nullptr;
m_Index = 0;
m_NextIndex = 0;
// Close shared memory and sync event if used
memoryshare.Close();
frame.CloseFrameSync();
m_bConnected = false;
m_bInitialized = false;
}
//---------------------------------------------------------
// Function: ReceiveTexture
// Connect to a sender and retrieve shared texture details
bool Spout::ReceiveTexture()
{
return ReceiveTexture(0, 0);
}
//---------------------------------------------------------
// Function: ReceiveTexture
// Receive the sender shared texture
//
// For a valid OpenGL receiving texture :
//
// Copy from the sender shared texture if there is a texture to receive into.
// The receiving OpenGL texture can only be RGBA of dimension (width * height)
// and must be re-allocated for sender size change. Return if flagged for update.
// The update flag is reset when the receiving application calls IsUpdated().
//
// If no arguments are passed :
//
// Connect to a sender and retrieve shared texture details,
// initialize GL/DX interop for OpenGL texture access, and update
// the sender shared texture, frame count and framerate.
// The texture can then be accessed using :
//
// - BindSharedTexture();
// - UnBindSharedTexture();
// - GetSharedTextureID();
//
// As with SendTexture, the host fbo argument is optional (default 0)
// but an fbo ID is necessary if it is currently bound, then that binding
// is restored. Otherwise the binding is lost.
//
bool Spout::ReceiveTexture(GLuint TextureID, GLuint TextureTarget, bool bInvert, GLuint HostFbo)
{
// Return if flagged for update and there is a texture to receive into.
// The update flag is reset when the receiving application calls IsUpdated().
if (m_bUpdated && TextureID != 0 && TextureTarget != 0) {
return true;
}
// Make sure OpenGL and DirectX are initialized
if (!OpenSpout()) {
return false;
}
// Try to receive texture details from a sender
if (ReceiveSenderData()) {
// Found a sender
// The sender name, width, height, format, shared texture handle
// and shared texture pointer have been retrieved
// Let the application know
m_bConnected = true;
// If the connected sender sharehandle or name is different,
// the receiver is re-initialized and m_bUpdated is set true
// so that the application re-allocates the receiving texture.
if (m_bUpdated) {
// If the sender is new or changed, reset shared textures
if (m_bTextureShare) {
// CreateInterop set "true" for receiver
if (!CreateInterop(m_Width, m_Height, m_dwFormat, true)) {
return false;
}
}
// If receiving to a texture, return to update it.
// The application detects the change with IsUpdated().
if (TextureID != 0 && TextureTarget != 0) {
return true;
}
}
// Was the sender's shared texture handle null
// or has the user set 2.006 memoryshare mode?
if (!m_dxShareHandle || m_bMemoryShare) {
// Possible existence of 2.006 memoryshare sender (no texture handle)
// (ReadMemoryTexture currently only works if texture share compatible)
if (m_bTextureShare) {
if (ReadMemoryTexture(m_SenderName, TextureID, TextureTarget, m_Width, m_Height, bInvert, HostFbo))
return true;
}
// ReadMemoryTexture failed, is there is a texture share handle ?
if (!m_dxShareHandle) {
return false;
}
// This could be a 2.007 sender but the user has set 2.006 memoryshare mode
// Drop though
}
if (m_bTextureShare) {
// Texture share compatible
// 3840x2160 60 fps - 0.45 msec/frame
ReadGLDXtexture(TextureID, TextureTarget, m_Width, m_Height, bInvert, HostFbo);
}
else if (m_bCPUshare) {
// Auto share enabled for DirectX CPU backup
// 3840x2160 33 fps - 5-7 msec/frame
ReadDX11texture(TextureID, TextureTarget, m_Width, m_Height, bInvert, HostFbo);
}
} // endif sender exists
else {
// ReceiveSenderData fails if there is no sender or the connected sender closed.
ReleaseReceiver();
// Let the application know.
m_bConnected = false;
}
return m_bConnected;
}
//---------------------------------------------------------
// Function: ReceiveImage
// Copy the sender texture to image pixels.
//
// Formats supported are : GL_RGBA, GL_RGB, GL_BGRA_EXT, GL_BGR_EXT.
// GL_BGRA_EXT and GL_BGR_EXT are dependent on those extensions being supported at runtime.
// If they are not, the rgba and rgb equivalents are used.
// The same sender size changes are handled with IsUpdated() as for ReceiveTexture.
// and the receiving buffer must be re-allocated if IsUpdated() returns true.
// NOTE : images with padding on each line are not supported.
// Also the width should be a multiple of 4
//
// As with ReceiveTexture, the ID of a currently bound fbo should be passed in.
//
bool Spout::ReceiveImage(char* Sendername, unsigned int &width, unsigned int &height,
unsigned char* pixels, GLenum glFormat, bool bInvert, GLuint HostFBO)
{
if (ReceiveImage(pixels, glFormat, bInvert, HostFBO)) {
strcpy_s(Sendername, 256, m_SenderName);
width = m_Width;
height = m_Height;
return true;
}
return false;
}
//---------------------------------------------------------
// Function: IsUpdated
// Query whether the sender has changed.
//
// Must be checked at every cycle before receiving data.
// If this is not done, the receiving functions fail.
//
bool Spout::IsUpdated()
{
bool bRet = m_bUpdated;
m_bUpdated = false; // Reset the update flag
return bRet;
}
//---------------------------------------------------------
// Function: IsConnected
// Query sender connection.
//
// If the sender closes, receiving functions return false,
// but connection can be tested at any time.
//
bool Spout::IsConnected()
{
return m_bConnected;
}
//---------------------------------------------------------
// Function: IsFrameNew
// Query received frame status
//
// The receiving texture or pixel buffer is refreshed if the sender has produced a new frame
// This can be queried to process texture data only for new frames
bool Spout::IsFrameNew()
{
return frame.IsFrameNew();
}
//---------------------------------------------------------
// Function: GetSenderFormat
// Get sender DirectX texture format
DWORD Spout::GetSenderFormat()
{
return m_dwFormat;
}
//---------------------------------------------------------
// Function: GetSenderName
// Get sender name
const char * Spout::GetSenderName()
{
return m_SenderName;
}
//---------------------------------------------------------
// Function: GetSenderWidth
// Get sender width
unsigned int Spout::GetSenderWidth()
{
return m_Width;
}
//---------------------------------------------------------
// Function: GetSenderHeight
// Get sender height
unsigned int Spout::GetSenderHeight()
{
return m_Height;
}
//---------------------------------------------------------
// Function: GetSenderFps
// Get sender frame rate
double Spout::GetSenderFps()
{
return frame.GetSenderFps();
}
//---------------------------------------------------------
// Function: GetSenderFrame
// Get sender frame number
long Spout::GetSenderFrame()
{
return frame.GetSenderFrame();
}
//---------------------------------------------------------
// Function: GetSenderHandle
// Received sender share handle
HANDLE Spout::GetSenderHandle()
{
return m_dxShareHandle;
}
//---------------------------------------------------------
// Function: GetSenderCPU
// Received sender sharing method.
// Returns true if the sender is using CPU methods
bool Spout::GetSenderCPU()
{
return m_bSenderCPU;
}
//---------------------------------------------------------
// Function: GetSenderGLDX
// Received sender sharing compatibility.
// Returns true if the sender graphics hardware is
// compatible with NVIDIA NV_DX_interop2 extension
bool Spout::GetSenderGLDX()
{
return m_bSenderGLDX;
}
//---------------------------------------------------------
// Function: SelectSender
// Open sender selection dialog
void Spout::SelectSender()
{
SelectSenderPanel();
}
//
// Group: Frame counting
//
//---------------------------------------------------------
// Function: SetFrameCount
// Enable or disable frame counting globally
void Spout::SetFrameCount(bool bEnable)
{
frame.SetFrameCount(bEnable);
}
// Function: DisableFrameCount
// Disable frame counting specifically for this application
void Spout::DisableFrameCount()
{
frame.DisableFrameCount();
}
//---------------------------------------------------------
// Function: IsFrameCountEnabled