241 lines
8.9 KiB
C++
241 lines
8.9 KiB
C++
/* -LICENSE-START-
|
|
** Copyright (c) 2012 Blackmagic Design
|
|
**
|
|
** Permission is hereby granted, free of charge, to any person or organization
|
|
** obtaining a copy of the software and accompanying documentation (the
|
|
** "Software") to use, reproduce, display, distribute, sub-license, execute,
|
|
** and transmit the Software, and to prepare derivative works of the Software,
|
|
** and to permit third-parties to whom the Software is furnished to do so, in
|
|
** accordance with:
|
|
**
|
|
** (1) if the Software is obtained from Blackmagic Design, the End User License
|
|
** Agreement for the Software Development Kit ("EULA") available at
|
|
** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or
|
|
**
|
|
** (2) if the Software is obtained from any third party, such licensing terms
|
|
** as notified by that third party,
|
|
**
|
|
** and all subject to the following:
|
|
**
|
|
** (3) the copyright notices in the Software and this entire statement,
|
|
** including the above license grant, this restriction and the following
|
|
** disclaimer, must be included in all copies of the Software, in whole or in
|
|
** part, and all derivative works of the Software, unless such copies or
|
|
** derivative works are solely in the form of machine-executable object code
|
|
** generated by a source language processor.
|
|
**
|
|
** (4) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
|
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
|
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
|
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
** DEALINGS IN THE SOFTWARE.
|
|
**
|
|
** A copy of the Software is available free of charge at
|
|
** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA.
|
|
**
|
|
** -LICENSE-END-
|
|
*/
|
|
|
|
#ifndef __OPENGL_COMPOSITE_H__
|
|
#define __OPENGL_COMPOSITE_H__
|
|
|
|
#include <windows.h>
|
|
#include <process.h>
|
|
#include <tchar.h>
|
|
#include <gl/gl.h>
|
|
#include <gl/glu.h>
|
|
|
|
#include <objbase.h>
|
|
#include <atlbase.h>
|
|
#include <comutil.h>
|
|
#include "DeckLinkAPI_h.h"
|
|
|
|
#include "GLExtensions.h"
|
|
#include "RuntimeHost.h"
|
|
|
|
#include <atomic>
|
|
#include <functional>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <vector>
|
|
#include <deque>
|
|
|
|
class PlayoutDelegate;
|
|
class CaptureDelegate;
|
|
class PinnedMemoryAllocator;
|
|
class ControlServer;
|
|
class OscServer;
|
|
|
|
|
|
class OpenGLComposite
|
|
{
|
|
public:
|
|
OpenGLComposite(HWND hWnd, HDC hDC, HGLRC hRC);
|
|
~OpenGLComposite();
|
|
|
|
bool InitDeckLink();
|
|
bool Start();
|
|
bool Stop();
|
|
bool ReloadShader();
|
|
std::string GetRuntimeStateJson() const;
|
|
bool AddLayer(const std::string& shaderId, std::string& error);
|
|
bool RemoveLayer(const std::string& layerId, std::string& error);
|
|
bool MoveLayer(const std::string& layerId, int direction, std::string& error);
|
|
bool MoveLayerToIndex(const std::string& layerId, std::size_t targetIndex, std::string& error);
|
|
bool SetLayerBypass(const std::string& layerId, bool bypassed, std::string& error);
|
|
bool SetLayerShader(const std::string& layerId, const std::string& shaderId, std::string& error);
|
|
bool UpdateLayerParameterJson(const std::string& layerId, const std::string& parameterId, const std::string& valueJson, std::string& error);
|
|
bool UpdateLayerParameterByControlKeyJson(const std::string& layerKey, const std::string& parameterKey, const std::string& valueJson, std::string& error);
|
|
bool ResetLayerParameters(const std::string& layerId, std::string& error);
|
|
bool SaveStackPreset(const std::string& presetName, std::string& error);
|
|
bool LoadStackPreset(const std::string& presetName, std::string& error);
|
|
|
|
void resizeGL(WORD width, WORD height);
|
|
void paintGL();
|
|
|
|
void VideoFrameArrived(IDeckLinkVideoInputFrame* inputFrame, bool hasNoInputSource);
|
|
void PlayoutFrameCompleted(IDeckLinkVideoFrame* completedFrame, BMDOutputFrameCompletionResult result);
|
|
|
|
private:
|
|
void resizeWindow(int width, int height);
|
|
bool CheckOpenGLExtensions();
|
|
|
|
CaptureDelegate* mCaptureDelegate;
|
|
PlayoutDelegate* mPlayoutDelegate;
|
|
HWND hGLWnd;
|
|
HDC hGLDC;
|
|
HGLRC hGLRC;
|
|
CRITICAL_SECTION pMutex;
|
|
|
|
// DeckLink
|
|
IDeckLinkInput* mDLInput;
|
|
IDeckLinkOutput* mDLOutput;
|
|
IDeckLinkKeyer* mDLKeyer;
|
|
std::deque<IDeckLinkMutableVideoFrame*> mDLOutputVideoFrameQueue;
|
|
PinnedMemoryAllocator* mPlayoutAllocator;
|
|
BMDTimeValue mFrameDuration;
|
|
BMDTimeScale mFrameTimescale;
|
|
unsigned mTotalPlayoutFrames;
|
|
unsigned mInputFrameWidth;
|
|
unsigned mInputFrameHeight;
|
|
unsigned mOutputFrameWidth;
|
|
unsigned mOutputFrameHeight;
|
|
std::string mInputDisplayModeName;
|
|
std::string mOutputDisplayModeName;
|
|
bool mHasNoInputSource;
|
|
std::string mDeckLinkOutputModelName;
|
|
bool mDeckLinkSupportsInternalKeying;
|
|
bool mDeckLinkSupportsExternalKeying;
|
|
bool mDeckLinkKeyerInterfaceAvailable;
|
|
bool mDeckLinkExternalKeyingActive;
|
|
std::string mDeckLinkStatusMessage;
|
|
|
|
// OpenGL data
|
|
bool mFastTransferExtensionAvailable;
|
|
GLuint mCaptureTexture;
|
|
GLuint mDecodedTexture;
|
|
GLuint mLayerTempTexture;
|
|
GLuint mFBOTexture;
|
|
GLuint mOutputTexture;
|
|
GLuint mUnpinnedTextureBuffer;
|
|
GLuint mDecodeFrameBuf;
|
|
GLuint mLayerTempFrameBuf;
|
|
GLuint mIdFrameBuf;
|
|
GLuint mOutputFrameBuf;
|
|
GLuint mIdColorBuf;
|
|
GLuint mIdDepthBuf;
|
|
GLuint mFullscreenVAO;
|
|
GLuint mGlobalParamsUBO;
|
|
GLuint mDecodeProgram;
|
|
GLuint mDecodeVertexShader;
|
|
GLuint mDecodeFragmentShader;
|
|
GLsizeiptr mGlobalParamsUBOSize;
|
|
int mViewWidth;
|
|
int mViewHeight;
|
|
std::unique_ptr<RuntimeHost> mRuntimeHost;
|
|
std::unique_ptr<ControlServer> mControlServer;
|
|
std::unique_ptr<OscServer> mOscServer;
|
|
|
|
struct LayerProgram
|
|
{
|
|
struct TextureBinding
|
|
{
|
|
std::string samplerName;
|
|
std::filesystem::path sourcePath;
|
|
GLuint texture = 0;
|
|
};
|
|
|
|
struct TextBinding
|
|
{
|
|
std::string parameterId;
|
|
std::string samplerName;
|
|
std::string fontId;
|
|
GLuint texture = 0;
|
|
std::string renderedText;
|
|
unsigned renderedWidth = 0;
|
|
unsigned renderedHeight = 0;
|
|
};
|
|
|
|
std::string layerId;
|
|
std::string shaderId;
|
|
GLuint shaderTextureBase = 0;
|
|
GLuint program = 0;
|
|
GLuint vertexShader = 0;
|
|
GLuint fragmentShader = 0;
|
|
std::vector<TextureBinding> textureBindings;
|
|
std::vector<TextBinding> textBindings;
|
|
};
|
|
std::vector<LayerProgram> mLayerPrograms;
|
|
|
|
struct HistorySlot
|
|
{
|
|
GLuint texture = 0;
|
|
GLuint framebuffer = 0;
|
|
};
|
|
|
|
struct HistoryRing
|
|
{
|
|
std::vector<HistorySlot> slots;
|
|
std::size_t nextWriteIndex = 0;
|
|
std::size_t filledCount = 0;
|
|
unsigned effectiveLength = 0;
|
|
TemporalHistorySource historySource = TemporalHistorySource::None;
|
|
};
|
|
|
|
HistoryRing mSourceHistoryRing;
|
|
std::map<std::string, HistoryRing> mPreLayerHistoryByLayerId;
|
|
bool mTemporalHistoryNeedsReset;
|
|
|
|
bool InitOpenGLState();
|
|
bool compileLayerPrograms(int errorMessageSize, char* errorMessage);
|
|
bool compileSingleLayerProgram(const RuntimeRenderState& state, LayerProgram& layerProgram, int errorMessageSize, char* errorMessage);
|
|
bool compileDecodeShader(int errorMessageSize, char* errorMessage);
|
|
void destroyLayerPrograms();
|
|
void destroySingleLayerProgram(LayerProgram& layerProgram);
|
|
void destroyDecodeShaderProgram();
|
|
void renderDecodePass();
|
|
void renderShaderProgram(GLuint sourceTexture, GLuint destinationFrameBuffer, LayerProgram& layerProgram, const RuntimeRenderState& state);
|
|
bool loadTextureAsset(const ShaderTextureAsset& textureAsset, GLuint& textureId, std::string& error);
|
|
bool renderTextBindingTexture(const RuntimeRenderState& state, LayerProgram::TextBinding& textBinding, std::string& error);
|
|
void bindLayerTextureAssets(const LayerProgram& layerProgram);
|
|
void renderEffect();
|
|
bool PollRuntimeChanges();
|
|
void broadcastRuntimeState();
|
|
bool updateGlobalParamsBuffer(const RuntimeRenderState& state, unsigned availableSourceHistoryLength, unsigned availableTemporalHistoryLength);
|
|
bool validateTemporalTextureUnitBudget(const std::vector<RuntimeRenderState>& layerStates, std::string& error) const;
|
|
bool ensureTemporalHistoryResources(const std::vector<RuntimeRenderState>& layerStates, std::string& error);
|
|
bool createHistoryRing(HistoryRing& ring, unsigned effectiveLength, TemporalHistorySource historySource, std::string& error);
|
|
void destroyHistoryRing(HistoryRing& ring);
|
|
void destroyTemporalHistoryResources();
|
|
void resetTemporalHistoryState();
|
|
void pushFramebufferToHistoryRing(GLuint sourceFramebuffer, HistoryRing& ring);
|
|
void bindHistorySamplers(const RuntimeRenderState& state, GLuint currentSourceTexture);
|
|
GLuint resolveHistoryTexture(const HistoryRing& ring, GLuint fallbackTexture, std::size_t framesAgo) const;
|
|
unsigned sourceHistoryAvailableCount() const;
|
|
unsigned temporalHistoryAvailableCountForLayer(const std::string& layerId) const;
|
|
};
|
|
|
|
#endif // __OPENGL_COMPOSITE_H__
|