2022-12-17 18:51:43 chip
Page 2474
📢 PUBLIC
December 17 2022
The PxlShader project has grown to the point where it needs a bit
of organization.
Globals.h : Global declarations.
WinMain.cpp : WinMain() entry point.
PxlShader.cpp : Main window.
MyEffect.cpp : ID2D1EffectImpl
FirstShader.hlsl : Pixel shader code
Deploy.bat : Post-build batch file to copy files.
Globals.h contains all the declarations that span multiple modules.
TEXT Globals.h :
/*************************************************************************/
/** Globals.h: Global declarations for PxlShader. **/
/** (C)2022 nlited systems, cmd **/
/*************************************************************************/
#pragma once
#include <Windows.h>
#include <d2d1_1.h>
#include "StdTypes.h"
#include "Handles.h"
#include "Errors.h"
#define SIGNATURE_PXLSHADER 0xCD190001
#define SIGNATURE_MYEFFECT 0xCD190002
#define SIGNATURE_MYTRANSFORM 0xCD190003
using namespace D2D1;
typedef D2D1_POINT_2F POINT2D;
EXTERNC const GUID CLSID_MyEffect; // {D8255497-025E-4D3F-A4DF-5C25306F67AC}
#define SafeRelease(pInterface) { if(pInterface) pInterface->Release(); pInterface= 0; }
// SafeRelease call Release() only if pInterface is nonzero.
// pInterface is always set to zero, regardless the return value of Release().
// This is proper behavior because it is possible that some external process has
// a reference that will be released at some future time, but as far as I am
// concerned the interface has now ceased to exist.
//Auto-destruct solid brush.
class D2Brush {
public:
D2Brush(ID2D1RenderTarget *pRT, const D2D1_COLOR_F &clr) {
pRT->CreateSolidColorBrush(clr,&pbrBrush);
};
~D2Brush(void) {
pbrBrush->Release();
}
operator ID2D1Brush *() { return(static_cast<ID2D1Brush*>(pbrBrush)); };
// This lets me use the object as the parameter to the various RenderTarget functions.
ID2D1SolidColorBrush *pbrBrush;
};
EXTERNC HINSTANCE ghInst;
extern int PxlShaderCreate(HWND &hWnd);
extern int MyEffectRegister(ID2D1Factory1 *pD2Factory);
//EOF: GLOBALS.H
WinMain() is the app's entry point.
TEXT WinMain.cpp :
/*************************************************************************/
/** WinMain.cpp: Windows entry point. **/
/** (C)2022 nlited systems, cmd **/
/*************************************************************************/
#include <Windows.h>
#include "Handles.h"
#include "ChipLib.h"
#include "Globals.h"
#pragma message(__FILE__": Optimizer disabled.")
#pragma optimize("",off)
HINSTANCE ghInst;
DWORD DbgFilter;
/*************************************************************************/
/** Window entry point **/
/*************************************************************************/
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR pArgs, int nShow) {
int Err= ERR_OK;
HWND hWnd= 0;
ghInst= hInst;
MemCreate();
TmpBufCreate(16*1024*1024);
if(IsErr(Err= PxlShaderCreate(hWnd))) {
Err= Error(Err,"WinMain: Unable to create the main window.");
} else {
while(hWnd && IsWindow(hWnd)) {
MSG Msg;
GetMessage(&Msg,hWnd,0,0);
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
}
TmpBufDestroy();
MemDestroy();
if(IsErr(MemReport()))
MessageBox(0,L"PxlShader: Memory error.",L"PxlShader",MB_OK|MB_SETFOREGROUND);
return(Err);
}
void ConsolePrint(DWORD Type, const WCHAR *Text) {
TXT Out(0,0,"%s\r\n",Text);
OutputDebugString(Out);
}
//EOF: WINMAIN.CPP
PxlShader.cpp contains all the code to manage and render the main window.
TEXT PxlShader.cpp :
/*************************************************************************/
/** PxlShader.cpp: Minimal code to run a pixel shader. **/
/** (C)2022 nlited systems, cmd **/
/*************************************************************************/
#include <Windows.h>
#include <d3d11_1.h>
#include <d2d1_1.h>
#include "Globals.h"
#include "ChipLib.h"
#pragma message(__FILE__": Optimizer disabled.")
#pragma optimize("",off)
static const WCHAR WndClassName[]= { L"PxlShader" };
class PxlShader {
public:
static PxlShader *Ptr(void *pObj);
static PxlShader *Ptr(HWND hWnd);
static int Create(HWND &hWnd);
private:
PxlShader(void);
~PxlShader(void);
int Create2(void);
static INT_PTR CALLBACK WndProc(HWND hWnd, UINT Msg, WPARAM wParm, LPARAM lParm);
LRESULT WndProc2(HWND hWnd, UINT Msg, WPARAM wParm, LPARAM lParm);
int MsgCreate(HWND hWnd);
int MsgDestroy(void);
int MsgClose(void);
int MsgTimer(void);
int MsgMoved(void);
int MsgPaint(void);
int DrawCreate(void);
int DrawCreateDX(void);
int DrawCreateSwapChain(void);
int DrawCreateD2Factory(void);
int CreateBitmapBase(ID2D1DeviceContext *pdcDst, D2D1_SIZE_U szBitmap, ID2D1Bitmap1 *&pbmBase);
int CreateBitmapComposite(ID2D1DeviceContext *pdcDst, D2D1_SIZE_U szBitmap, ID2D1Bitmap1 *&pbmComposite);
int CreateEffect(void);
int DrawUpdate(void);
int DrawShow(void);
void ReleaseEverything(void);
//Data
DWORD Signature;
HWND hWnd;
RECT rWnd; // Window client area
POINT2D ptBall; // Bouncing ball position
POINT2D BallVector; // Bouncing ball vector
bool DoReset; // Call ReleaseEverything() before next draw.
D3D_FEATURE_LEVEL DxFeatures; // Supported feature set
IDXGIFactory2 *pDXGIFactory; // Creates SwapChain
IDXGISwapChain1 *pSwapChain; // Final display and pDXGISurface
IDXGISurface1 *pDXGISurface; // Used to create the bitmaps
IDXGIDevice *pDXGIDevice; // Base display device
ID2D1Factory1 *pD2Factory; // Creates Direct2D stuff
ID2D1Device *pD2Device; // Spawns ID2D1DeviceContext
ID2D1DeviceContext *pdcDraw; // The workhorse, used to draw everything.
ID2D1Bitmap1 *pbmImg; // Final offscreen image (write-only)
ID2D1Bitmap1 *pbmSrc1; // An intermediate offscreen image that can be composited.
ID2D1Bitmap1 *pbmSrc2; // An intermediate offscreen image that can be composited.
ID2D1Effect *pEffect; // Direct2D effect that contains my pixel shader.
};
int PxlShaderCreate(HWND &hWnd) {
return(PxlShader::Create(hWnd));
}
/*************************************************************************/
/** PxlShader class **/
/*************************************************************************/
PxlShader::PxlShader(void) {
Signature= SIGNATURE_PXLSHADER;
ptBall= { 10,20 };
BallVector= { 1,1 };
}
PxlShader::~PxlShader(void) {
Signature|= SIGNATURE_INVALID;
ReleaseEverything();
}
PxlShader *PxlShader::Ptr(void *pObj) {
PxlShader *pPxl= (PxlShader*)pObj;
if(!pObj || IsBadPtr(pObj,sizeof(*pPxl),BADPTR_RW) || pPxl->Signature!=SIGNATURE_PXLSHADER)
pPxl= 0;
return(pPxl);
}
PxlShader *PxlShader::Ptr(HWND hWnd) {
if(!hWnd || !IsWindow(hWnd))
return(0);
return(Ptr((void*)GetWindowLongPtr(hWnd,GWLP_USERDATA)));
}
int PxlShader::Create(HWND &hWnd) {
int Err= ERR_OK;
PxlShader *pPxl= new PxlShader;
if(!pPxl) {
Err= Error(ERR_NO_MEM,"PxlShader:Create: NoMem");
} else if(IsErr(Err= pPxl->Create2())) {
delete pPxl;
} else {
hWnd= pPxl->hWnd;
}
return(Err);
}
int PxlShader::Create2(void) {
int Err= ERR_OK;
ATOM hWndClass;
WNDCLASS WndClass;
RECT R= { 100,100,500,300 };
Zero(WndClass);
WndClass.style= CS_DBLCLKS|CS_HREDRAW|CS_VREDRAW;
WndClass.lpfnWndProc= WndProc;
WndClass.hInstance= ghInst;
WndClass.lpszClassName= WndClassName;
DWORD style= WS_OVERLAPPEDWINDOW|WS_VISIBLE;
if(!(hWndClass= RegisterClass(&WndClass))) {
Err= Error(ERR_SYSCREATE,"PxlShader:Create2: Unable to register the main window class.");
} else if(!(hWnd= CreateWindow(WndClassName,L"PxlShader",style,R.left,R.top,RWID(R),RHGT(R),0,0,ghInst,(LPVOID)this))) {
Err= Error(ERR_SYSCREATE,"PxlShader:Create2: Unable to create the main window.");
}
return(Err);
}
/*************************************************************************/
/** Window message handler **/
/*************************************************************************/
INT_PTR CALLBACK PxlShader::WndProc(HWND hWnd, UINT Msg, WPARAM wParm, LPARAM lParm) {
INT_PTR Result= 0;
PxlShader *pPxl= 0;
if(Msg==WM_CREATE) {
CREATESTRUCT *pCreate= (CREATESTRUCT*)lParm;
pPxl= Ptr(pCreate->lpCreateParams);
} else {
pPxl= Ptr(hWnd);
}
if(!pPxl) {
Result= DefWindowProc(hWnd,Msg,wParm,lParm);
} else {
Result= pPxl->WndProc2(hWnd,Msg,wParm,lParm);
}
return(Result);
}
LRESULT PxlShader::WndProc2(HWND hWnd, UINT Msg, WPARAM wParm, LPARAM lParm) {
LRESULT Result= 0;
switch(Msg) {
case WM_CREATE: Result= MsgCreate(hWnd); break;
case WM_DESTROY: Result= MsgDestroy(); break;
case WM_CLOSE: Result= MsgClose(); break;
case WM_TIMER: Result= MsgTimer(); break;
case WM_WINDOWPOSCHANGED: Result= MsgMoved(); break;
case WM_PAINT: Result= MsgPaint(); break;
default: Result= DefWindowProc(hWnd,Msg,wParm,lParm); break;
}
return(Result);
}
int PxlShader::MsgCreate(HWND _hWnd) {
hWnd= _hWnd;
SetWindowLongPtr(hWnd,GWLP_USERDATA,(LONG_PTR)this);
SetTimer(hWnd,1,30,0);
return(1);
}
int PxlShader::MsgDestroy(void) {
SetWindowLongPtr(hWnd,GWLP_USERDATA,0);
delete this;
return(1);
}
int PxlShader::MsgClose(void) {
DestroyWindow(hWnd);
return(1);
}
int PxlShader::MsgTimer(void) {
ptBall.x+= BallVector.x;
if(ptBall.x < rWnd.left || ptBall.x >= rWnd.right) {
BallVector.x= -BallVector.x;
ptBall.x+= BallVector.x*2;
}
ptBall.y+= BallVector.y;
if(ptBall.y < rWnd.top || ptBall.y >= rWnd.bottom) {
BallVector.y= -BallVector.y;
ptBall.y+= BallVector.y*2;
}
if(pEffect) {
UINT32 Tint= ((UINT32)rand()<<16)|rand();
pEffect->SetValueByName(L"Tint",Tint);
}
InvalidateRect(hWnd,0,0);
return(1);
}
int PxlShader::MsgMoved(void) {
RECT rNew;
GetClientRect(hWnd,&rNew);
if(RWID(rNew)!=RWID(rWnd) || RHGT(rNew)!=RHGT(rWnd)) {
rWnd= rNew;
DoReset= true;
}
return(1);
}
int PxlShader::MsgPaint(void) {
int Err= ERR_OK;
PAINTSTRUCT Pnt;
GetClientRect(hWnd,&rWnd);
if(BeginPaint(hWnd,&Pnt)) {
if(IsErr(Err= DrawCreate())) {
Err= Warn(Err,"PxlShader:MsgPaint: DrawCreate() failed.");
} else if(IsErr(Err= DrawUpdate())) {
Err= Warn(Err,"PxlShader:MsgPaint: DrawUdpate() failed.");
} else if(IsErr(Err= DrawShow())) {
Err= Warn(Err,"PxlShader:MsgPaint: DrawPaint() failed.");
}
if(IsErr(Err)) {
// Fall back to GDI paint.
HBRUSH hbrFill= CreateSolidBrush(RGB(20,30,40));
FillRect(Pnt.hdc,&Pnt.rcPaint,hbrFill);
DeleteObject(hbrFill);
}
EndPaint(hWnd,&Pnt);
}
ValidateRect(hWnd,0);
return(1);
}
/*************************************************************************/
/** DirectX **/
/*************************************************************************/
int PxlShader::DrawCreate(void) {
int Err= ERR_OK;
HRESULT WinErr;
D2D1_SIZE_U szWnd= { (UINT32)RWID(rWnd), (UINT32)RHGT(rWnd) };
D2D1_DEVICE_CONTEXT_OPTIONS DCOptions= D2D1_DEVICE_CONTEXT_OPTIONS_NONE;
if(DoReset)
ReleaseEverything();
if(!pDXGIDevice && IsErr(DrawCreateDX())) {
Err= Warn(ERR_DIRECTX,"PxlShader:DrawCreate: Unable to create DXGI device.");
} else if(!pSwapChain && IsErr(Err= DrawCreateSwapChain())) {
Err= Warn(ERR_DIRECTX,"PxlShader:DrawCreate: Unable to create SwapChain.");
} else if(!pDXGISurface && !SUCCEEDED(WinErr= pSwapChain->GetBuffer(0,IID_PPV_ARGS(&pDXGISurface)))) {
Err= Warn(ERR_DIRECTX,"PxlShader:DrawCreate: Unable to retrieve DXGI surface. [%X]",WinErr);
} else if(!pD2Factory && IsErr(Err= DrawCreateD2Factory())) {
Err= Warn(ERR_DIRECTX,"PxlShader:DrawCreate: Unable to create Direct2D factory.");
} else if(!pD2Device && !SUCCEEDED(WinErr= pD2Factory->CreateDevice(pDXGIDevice,&pD2Device))) {
Err= Warn(ERR_DIRECTX,"PxlShader:DrawCreate: Unable to create Direct2D device.");
} else if(!pdcDraw && !SUCCEEDED(WinErr= pD2Device->CreateDeviceContext(DCOptions,&pdcDraw))) {
Err= Warn(ERR_DIRECTX,"PxlShader:DrawCreate: Unable to create Draw context.");
} else if(!pbmImg && IsErr(Err= CreateBitmapBase(pdcDraw,szWnd,pbmImg))) {
Err= Warn(ERR_DIRECTX,"PxlShader:DrawCreate: Unable to create base bitmap.");
} else if(!pEffect && IsErr(Err= CreateEffect())) {
Err= Warn(ERR_DIRECTX,"PxlShader:DrawCreate: Unable to create MyEffect.");
} else if(!pbmSrc1 && IsErr(Err= CreateBitmapComposite(pdcDraw,SizeU(100,100),pbmSrc1))) {
Err= Warn(ERR_DIRECTX,"PxlShader:DrawCreate: Unable to create source bitmap.");
} else if(!pbmSrc2 && IsErr(Err= CreateBitmapComposite(pdcDraw,SizeU(100,100),pbmSrc2))) {
Err= Warn(ERR_DIRECTX,"PxlShader:DrawCreate: Unable to create source bitmap.");
}
return(Err);
}
int PxlShader::DrawCreateDX(void) {
int Err= ERR_OK;
HRESULT WinErr;
UINT Flags= D3D11_CREATE_DEVICE_BGRA_SUPPORT;
static const D3D_FEATURE_LEVEL Levels[]= {
D3D_FEATURE_LEVEL_11_1,
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0,
D3D_FEATURE_LEVEL_9_3,
D3D_FEATURE_LEVEL_9_2,
D3D_FEATURE_LEVEL_9_1
};
D3D_DRIVER_TYPE Type= D3D_DRIVER_TYPE_HARDWARE;
UINT ctLevels= ARRAYSIZE(Levels);
UINT Version= D3D11_SDK_VERSION;
ID3D11Device *pD3D11Device= 0;
ID3D11DeviceContext *pD3D11DC= 0;
IDXGIAdapter *pDXGIAdapter= 0;
if(!SUCCEEDED(WinErr= D3D11CreateDevice(0,Type,0,Flags,Levels,ctLevels,Version,&pD3D11Device,&DxFeatures,&pD3D11DC))) {
Err= Warn(ERR_DIRECTX,"PxlShader:DrawCreateDX: D3D11CreateDevice() failed.");
} else if(!SUCCEEDED(WinErr= pD3D11Device->QueryInterface(&pDXGIDevice))) {
Err= Warn(ERR_DIRECTX,"PxlShader:DrawCreateDX: Unable to retrieve DXGI device. [%X]",WinErr);
} else if(!SUCCEEDED(WinErr= pDXGIDevice->GetAdapter(&pDXGIAdapter))) {
Err= Warn(ERR_DIRECTX,"PxlShader:DrawCreateDX: Unable to retrieve DXGI adapter. [%X]",WinErr);
} else if(!SUCCEEDED(WinErr= pDXGIAdapter->GetParent(IID_PPV_ARGS(&pDXGIFactory)))) {
Err= Warn(ERR_DIRECTX,"PxlShader:DrawCreateDX: Unable to retrieve DXGI factory. [%X]",WinErr);
}
//NOTE: pDXGIDevice has a RefCt of 3!
SafeRelease(pDXGIAdapter);
SafeRelease(pD3D11Device);
SafeRelease(pD3D11DC);
return(Err);
}
int PxlShader::DrawCreateSwapChain(void) {
int Err= ERR_OK;
HRESULT WinErr;
DXGI_SWAP_CHAIN_DESC1 SwapDesc;
Zero(SwapDesc);
SwapDesc.Format= DXGI_FORMAT_B8G8R8A8_UNORM;
SwapDesc.SampleDesc.Count= 1;
SwapDesc.BufferUsage= DXGI_USAGE_RENDER_TARGET_OUTPUT;
SwapDesc.BufferCount= 2;
SwapDesc.Scaling= DXGI_SCALING_STRETCH;
SwapDesc.SwapEffect= DXGI_SWAP_EFFECT_DISCARD;
if(!SUCCEEDED(WinErr= pDXGIFactory->CreateSwapChainForHwnd(pDXGIDevice,hWnd,&SwapDesc,0,0,&pSwapChain))) {
Err= Warn(ERR_DIRECTX,"PxlShader:DrawCreateSwapChain: Unable to create SwapChain. [%X]",WinErr);
}
return(Err);
}
int PxlShader::DrawCreateD2Factory(void) {
int Err= ERR_OK;
HRESULT WinErr;
REFIID guid= __uuidof(ID2D1Factory1);
D2D1_FACTORY_OPTIONS options;
options.debugLevel= D2D1_DEBUG_LEVEL_INFORMATION;
// DEBUG_LEVEL will trigger exceptions if EndDraw() fails or the factory is released with
// outstanding (unreleased) objects.
if(!SUCCEEDED(WinErr= D2D1CreateFactory(D2D1_FACTORY_TYPE_MULTI_THREADED,guid,&options,(void**)&pD2Factory))) {
Err= Error(ERR_DIRECTX,"PxlShader:DrawCreateD2Factory: Unable to create D2D factory. [%X]",WinErr);
}
return(Err);
}
// Create a targetable bitmap from a DXGI surface.
// This serves as the final rendering destination bitmap.
int PxlShader::CreateBitmapBase(ID2D1DeviceContext *pdcDst, D2D1_SIZE_U szBitmap, ID2D1Bitmap1 *&pbmBase) {
int Err= ERR_OK;
HRESULT WinErr;
IDXGISurface *pSurface= 0;
D2D1_BITMAP_PROPERTIES1 bmProp;
Zero(bmProp);
bmProp.bitmapOptions= D2D1_BITMAP_OPTIONS_TARGET|D2D1_BITMAP_OPTIONS_CANNOT_DRAW;
bmProp.pixelFormat= PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM,D2D1_ALPHA_MODE_IGNORE);
pdcDst->GetDpi(&bmProp.dpiX,&bmProp.dpiY);
if(!SUCCEEDED(WinErr= pdcDst->CreateBitmapFromDxgiSurface(pDXGISurface,bmProp,&pbmBase))) {
Err= Warn(ERR_DIRECTX,"PxlShader:CreateBitmapBase: Unable to create %ux%u bitmap.",szBitmap.width,szBitmap.height);
} else {
pdcDst->SetTarget(pbmBase);
}
return(Err);
}
// Create a targetable bitmap that can be used for compositing.
int PxlShader::CreateBitmapComposite(ID2D1DeviceContext *pdcDst, D2D1_SIZE_U szBitmap, ID2D1Bitmap1 *&pbmComposite) {
int Err= ERR_OK;
HRESULT WinErr;
D2D1_BITMAP_PROPERTIES1 bmProp;
Zero(bmProp);
bmProp.bitmapOptions= D2D1_BITMAP_OPTIONS_TARGET;
bmProp.pixelFormat= PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM,D2D1_ALPHA_MODE_PREMULTIPLIED);
pdcDst->GetDpi(&bmProp.dpiX,&bmProp.dpiY);
if(!SUCCEEDED(WinErr= pdcDst->CreateBitmap(szBitmap,0,0,bmProp,&pbmComposite))) {
Err= Warn(ERR_DIRECTX,"PxlShader:CreateBitmapComposite: Unable to create %ux%u bitmap.",szBitmap.width,szBitmap.height);
} else {
pdcDst->SetTarget(pbmComposite);
}
return(Err);
}
int PxlShader::CreateEffect(void) {
int Err= ERR_OK;
HRESULT WinErr;
if(IsErr(Err= MyEffectRegister(pD2Factory))) {
Err= Warn(ERR_DIRECTX,"PxlShader:CreateEffect: Unable to register MyEffect.");
} else if(!SUCCEEDED(WinErr= pdcDraw->CreateEffect(CLSID_MyEffect,&pEffect))) {
Err= Warn(ERR_DIRECTX,"PxlShader:CreateEffect: Unable to create MyEffect. [%X]",WinErr);
} else {
Print(PRINT_INFO,"PxlShader:CreateEffect: OK");
}
return(Err);
}
void PxlShader::ReleaseEverything(void) {
SafeRelease(pEffect);
SafeRelease(pbmSrc2);
SafeRelease(pbmSrc1);
SafeRelease(pbmImg);
SafeRelease(pdcDraw);
SafeRelease(pD2Device);
SafeRelease(pD2Factory);
SafeRelease(pDXGISurface);
SafeRelease(pSwapChain);
SafeRelease(pDXGIFactory);
SafeRelease(pDXGIDevice);
DoReset= false;
}
/*************************************************************************/
/** Draw **/
/*************************************************************************/
int PxlShader::DrawUpdate(void) {
int Err= ERR_OK;
HRESULT WinErr;
if(!pdcDraw || !pbmImg || !pbmSrc1 || !pbmSrc2 || !pEffect) {
Err= Warn(ERR_NOT_CREATED,"PxlShader:DrawUpdate: No resources.");
} else {
pdcDraw->BeginDraw();
pdcDraw->SetTransform(Matrix3x2F::Identity());
pdcDraw->SetTarget(pbmSrc1);
pdcDraw->Clear(ColorF(ColorF::Black,0));
D2D1_RECT_F box= { 0,0,80,60 };
pdcDraw->FillRectangle(box,D2Brush(pdcDraw,ColorF(ColorF::Goldenrod)));
pdcDraw->SetTarget(pbmSrc2);
pdcDraw->Clear(ColorF(ColorF::Black,0));
D2D1_ELLIPSE dot= { {50,50}, 50,50 };
pdcDraw->FillEllipse(&dot,D2Brush(pdcDraw,ColorF(ColorF::ForestGreen)));
pdcDraw->SetTarget(pbmImg);
pdcDraw->Clear(ColorF(ColorF::Black,0));
pdcDraw->SetTransform(Matrix3x2F::Translation(SizeF(ptBall.x,ptBall.y)));
pEffect->SetInput(0,pbmSrc1);
pEffect->SetInput(1,pbmSrc2);
pdcDraw->DrawImage(pEffect);
if(!SUCCEEDED(WinErr= pdcDraw->EndDraw())) {
Err= Error(ERR_DIRECTX,"PxlShader:DrawUpdate: EndDraw() failed. [%X]",WinErr);
DoReset= true;
}
}
return(Err);
}
int PxlShader::DrawShow(void) {
int Err= ERR_OK;
HRESULT WinErr;
DXGI_PRESENT_PARAMETERS PresentParm;
Zero(PresentParm);
if(!pSwapChain) {
Err= Warn(ERR_NOT_CREATED,"PxlShader:DrawShow: No SwapChain.");
} else if(!SUCCEEDED(WinErr= pSwapChain->Present1(1,0,&PresentParm))) {
Err= Warn(ERR_DIRECTX,"PxlShader:DrawShow: Present() failed. [%X]",WinErr);
DoReset= true;
}
return(Err);
}
//EOF: PXLSHADER.CPP
MyEffect.cpp contains all the code to implement my custom Direct2D Effect.
TEXT MyEffect.cpp :
/*************************************************************************/
/** MyEffect.cpp: A simple pixel shader wrapped inside ID2D1Effect. **/
/** (C)2022 nlited systems, cmd **/
/*************************************************************************/
#include <Windows.h>
#include <d3d11_1.h>
#include <d2d1.h>
#include <d2d1_1.h>
#include <d2d1helper.h>
#include <d2d1effectauthor.h>
#include <d2d1effecthelpers.h>
#include <d3dcompiler.h>
#include "Globals.h"
#include "ChipLib.h"
#pragma comment(lib,"D3D11.lib")
#pragma comment(lib,"D2D1.lib")
#pragma comment(lib,"DXGI.lib")
#pragma comment(lib,"d3dcompiler.lib")
#pragma message(__FILE__": Optimizer disabled.")
#pragma optimize("",off)
// {D8255497-025E-4D3F-A4DF-5C25306F67AC}
const GUID CLSID_MyEffect = { 0xd8255497, 0x25e, 0x4d3f, { 0xa4, 0xdf, 0x5c, 0x25, 0x30, 0x6f, 0x67, 0xac } };
// {2BCB702A-4F42-44D0-B8ED-4E9F9FC7A905}
static const GUID GUID_MyPixelShader = { 0x2bcb702a, 0x4f42, 0x44d0, { 0xb8, 0xed, 0x4e, 0x9f, 0x9f, 0xc7, 0xa9, 0x5 } };
#define MAX_INPUT 2
class MyEffect: public ID2D1EffectImpl, public ID2D1DrawTransform {
public:
//EffectImpl
static HRESULT Register(_In_ ID2D1Factory1 *pFactory);
static HRESULT __stdcall CreateMyEffect(_Outptr_ IUnknown **ppEffect);
IFACEMETHODIMP QueryInterface(REFIID riid, void **ppInterface);
IFACEMETHODIMP_(ULONG) AddRef(void) { return(++ctReference); };
IFACEMETHODIMP_(ULONG) Release(void);
IFACEMETHODIMP Initialize(_In_ ID2D1EffectContext *pCtx, _In_ ID2D1TransformGraph *pGraph);
IFACEMETHODIMP PrepareForRender(D2D1_CHANGE_TYPE Type);
IFACEMETHODIMP SetGraph(ID2D1TransformGraph *pGraph);
//DrawTransform
IFACEMETHODIMP SetDrawInfo(ID2D1DrawInfo *pDraw);
IFACEMETHODIMP MapOutputRectToInputRects(const D2D1_RECT_L *prOut, D2D1_RECT_L *prIn, UINT32 ctIn) const;
IFACEMETHODIMP MapInputRectsToOutputRect(const D2D1_RECT_L *prIn, const D2D1_RECT_L *prInOpaque, UINT32 ctIn, D2D1_RECT_L *prOut, D2D1_RECT_L *prOutOpaque);
IFACEMETHODIMP MapInvalidRect(UINT32 nIn, D2D1_RECT_L rInInvalid, D2D1_RECT_L *prOut) const;
IFACEMETHODIMP_(UINT32) GetInputCount(void) const;
//Added
HRESULT SetTint(UINT32 Clr);
UINT32 GetTint(void) const;
private:
MyEffect(void);
~MyEffect(void);
// Data
DWORD Signature;
LONG ctReference;
ID2D1EffectContext *pCtx;
ID2D1DrawInfo *pDraw;
UINT ctInput;
D2D1_RECT_L rIn[MAX_INPUT];
struct ShaderConstant_s {
float tint[4];
} Constants;
};
/*************************************************************************/
/** Public interface **/
/*************************************************************************/
int MyEffectRegister(ID2D1Factory1 *pD2Factory) {
return(MyEffect::Register(pD2Factory));
}
/*************************************************************************/
/** Private code **/
/*************************************************************************/
MyEffect::MyEffect(void) {
Signature= SIGNATURE_MYEFFECT;
ctReference= 1;
ctInput= 0;
Zero(Constants);
Constants.tint[2]= 0.50f;
}
MyEffect::~MyEffect(void) {
Signature|= SIGNATURE_INVALID;
}
ULONG MyEffect::Release(void) {
if(--ctReference > 0)
return(ctReference);
delete this;
return(0);
}
HRESULT MyEffect::QueryInterface(REFIID riid, void **ppInterface) {
HRESULT WinErr= S_OK;
void *pInterface= 0;
if(riid==__uuidof(ID2D1EffectImpl)) {
pInterface= reinterpret_cast<ID2D1EffectImpl*>(this);
} else if(riid==__uuidof(ID2D1DrawTransform)) {
pInterface= static_cast<ID2D1DrawTransform*>(this);
} else if(riid==__uuidof(ID2D1Transform)) {
pInterface= static_cast<ID2D1Transform*>(this);
} else if(riid==__uuidof(ID2D1TransformNode)) {
pInterface= static_cast<ID2D1TransformNode*>(this);
} else if(riid==__uuidof(ID2D1ComputeTransform)) {
Print(PRINT_DEBUG,"MyEffect:QueryInterface: I am not a compute transform.");
WinErr= E_NOINTERFACE;
} else if(riid==__uuidof(ID2D1SourceTransform)) {
Print(PRINT_DEBUG,"MyEffect:QueryInterface: I am not a source transform.");
WinErr= E_NOINTERFACE;
} else if(riid==__uuidof(IUnknown)) {
pInterface= this;
} else {
WinErr= E_NOINTERFACE;
}
if(ppInterface) {
*ppInterface= pInterface;
if(pInterface)
AddRef();
}
return(WinErr);
}
HRESULT MyEffect::Register(ID2D1Factory1 *pFactory) {
HRESULT WinErr= S_OK;
static const PCWSTR pszXml =
L"<?xml version='1.0'?>\r\n"
L"<Effect>\r\n"
L" <!-- System Properties -->\r\n"
L" <Property name='DisplayName' type='string' value='FirstShader1'/>\r\n"
L" <Property name='Author' type='string' value='nlited systems'/>\r\n"
L" <Property name='Category' type='string' value='Experimental'/>\r\n"
L" <Property name='Description' type='string' value='My first effect.'/>\r\n"
L" <Inputs minimum='0' maximum='2'>\r\n"
// Source must be specified.
L" <Input name='Source1'/>\r\n"
L" <Input name='Source2'/>\r\n"
L" </Inputs>\r\n"
L" <!-- Custom Properties go here. -->\r\n"
L" <Property name='Tint' type='uint32'>\r\n"
L" <Property name='DisplayName' type='string' value='Tint'/>\r\n"
L" <Property name='Default' type='uint32' value='0'/>\r\n"
L" </Property>\r\n"
L"</Effect>\r\n"
;
static const D2D1_PROPERTY_BINDING Bindings[]= {
D2D1_VALUE_TYPE_BINDING(L"Tint",&SetTint,&GetTint)
};
if(!SUCCEEDED(WinErr= pFactory->RegisterEffectFromString(CLSID_MyEffect,pszXml,Bindings,ARRAYSIZE(Bindings),CreateMyEffect))) {
Error(ERR_DIRECTX,"MyEffect:Register: RegisterEffectFromString() failed. [%X]",WinErr);
}
return(WinErr);
}
HRESULT __stdcall MyEffect::CreateMyEffect(IUnknown **ppEffect) {
HRESULT WinErr= S_OK;
*ppEffect= static_cast<ID2D1EffectImpl*>(new MyEffect);
if(!*ppEffect) {
WinErr= E_OUTOFMEMORY;
}
return(WinErr);
}
HRESULT MyEffect::Initialize(ID2D1EffectContext *_pCtx, ID2D1TransformGraph *pGraph) {
HRESULT WinErr= S_OK;
ID3DBlob *pCode= 0;
ID3DBlob *pError= 0;
pCtx= _pCtx;
if(!SUCCEEDED(WinErr= D3DReadFileToBlob(L"FirstShader.cso",&pCode))) {
Warn(ERR_FILE_READ,"MyEffect:Initialize: Unable to read shader. [%X]",WinErr);
} else if(!SUCCEEDED(WinErr= pCtx->LoadPixelShader(GUID_MyPixelShader,(BYTE*)pCode->GetBufferPointer(),(UINT32)pCode->GetBufferSize()))) {
Warn(ERR_DIRECTX,"MyEffect:Initialize: Unable to create pixel shader. [%X]",WinErr);
} else if(!SUCCEEDED(WinErr= pGraph->AddNode(this))) {
Warn(ERR_DIRECTX,"MyEffect:Initialize: Unable to add Node. [%X]",WinErr);
} else if(!SUCCEEDED(WinErr= pGraph->SetOutputNode(this))) {
Warn(ERR_DIRECTX,"MyEffect:Initialize: Unable to set output node. [%X]",WinErr);
} else if(!SUCCEEDED(WinErr= pGraph->ConnectToEffectInput(0,this,0))) {
Warn(ERR_DIRECTX,"MyEffect:Initialize: Unable to connect input 0. [%X]",WinErr);
} else if(!SUCCEEDED(WinErr= pGraph->ConnectToEffectInput(1,this,1))) {
Warn(ERR_DIRECTX,"MyEffect:Initialize: Unable to connect input 1. [%X]",WinErr);
} else {
ctInput= 2;
Print(PRINT_INFO,"MyEffect:Initialize: OK.");
}
SafeRelease(pCode);
SafeRelease(pError);
return(WinErr);
}
// This is a single-transform, single-node graph, SetGraph() should never be called.
HRESULT MyEffect::SetGraph(ID2D1TransformGraph *pGraph) {
Warn(ERR_DIRECTX,"MyEffect:SetGraph: Should not be called.");
return(E_NOTIMPL);
}
HRESULT MyEffect::SetTint(UINT32 Clr) {
Constants.tint[0]= (float)((Clr>>16) & 0xFF)/255.0f;
Constants.tint[1]= (float)((Clr>> 8) & 0xFF)/255.0f;
Constants.tint[2]= (float)((Clr ) & 0xFF)/255.0f;
Constants.tint[3]= (float)((Clr>>24) & 0xFF)/255.0f;
return(S_OK);
}
UINT32 MyEffect::GetTint(void) const {
UINT32 Clr= 0;
Clr|= ((UINT32)(Constants.tint[0]*255.0f) & 0xFF)<<16;
Clr|= ((UINT32)(Constants.tint[1]*255.0f) & 0xFF)<< 8;
Clr|= ((UINT32)(Constants.tint[2]*255.0f) & 0xFF);
Clr|= ((UINT32)(Constants.tint[3]*255.0f) & 0xFF)<<24;
return(Clr);
}
HRESULT MyEffect::PrepareForRender(D2D1_CHANGE_TYPE Type) {
HRESULT WinErr= S_OK;
pDraw->SetPixelShaderConstantBuffer((BYTE*)&Constants,sizeof(Constants));
return(WinErr);
}
// ID2D1DrawTransform
HRESULT MyEffect::SetDrawInfo(ID2D1DrawInfo *_pDraw) {
HRESULT WinErr= S_OK;
pDraw= _pDraw;
if(!SUCCEEDED(WinErr= pDraw->SetPixelShader(GUID_MyPixelShader))) {
Warn(ERR_DIRECTX,"MyEffect:SetDrawInfo: SetPixelShader() failed. [%X]",WinErr);
}
return(WinErr);
}
HRESULT MyEffect::MapOutputRectToInputRects(const D2D1_RECT_L *prOut, _Out_writes_(ctIn) D2D1_RECT_L *prIn, UINT32 ctIn) const {
HRESULT WinErr= S_OK;
//Print(PRINT_DEBUG,"MyEffect:MapOutputRectToInputRects: ctIn=%u",ctIn);
if(ctIn==0) {
Warn(ERR_DIRECTX,"MyEffect:MapOutputRectToInputRects: ctIn is zero?");
} else if(ctIn>MAX_INPUT) {
Warn(ERR_DIRECTX,"MyEffect:MapOutputRectToInputRects: Only %u inputs defined. [%d]",ctInput,ctIn);
WinErr= E_INVALIDARG;
} else if(ctIn>0) {
prIn[0]= *prOut;
for(UINT n1=1;n1<ctIn;n1++) {
prIn[n1]= *prOut;
}
}
return(WinErr);
}
IFACEMETHODIMP MyEffect::MapInputRectsToOutputRect(const D2D1_RECT_L *prIn, const D2D1_RECT_L *prInOpaque, UINT32 ctIn, D2D1_RECT_L *prOut, D2D1_RECT_L *prOutOpaque) {
HRESULT WinErr= S_OK;
//Print(PRINT_DEBUG,"MyEffect:MapInputRectsToOutputRect: ctIn=%u",ctIn);
if(ctIn==0) {
Warn(ERR_DIRECTX,"MyEffect:MapInputRectsToOutputRect: ctIn is zero?");
} else if(ctIn>MAX_INPUT) {
Warn(ERR_DIRECTX,"MyEffect:MapInputRectsToOutputRect: Only %u inputs defined. [%d]",ctInput,ctIn);
WinErr= E_INVALIDARG;
} else if(ctIn>0) {
*prOut= rIn[0]= prIn[0];
for(UINT n1=1;n1<ctIn;n1++) {
rIn[n1]= prIn[n1];
}
Zero(*prOutOpaque);
}
return(WinErr);
}
IFACEMETHODIMP MyEffect::MapInvalidRect(UINT32 nIn, D2D1_RECT_L rInInvalid, D2D1_RECT_L *prOutInvalid) const {
HRESULT WinErr= S_OK;
Print(PRINT_DEBUG,"MyEffect:MapInvalidRect:");
// Set entire output to invalid
*prOutInvalid= rIn[0];
return(WinErr);
}
IFACEMETHODIMP_(UINT32) MyEffect::GetInputCount(void) const {
return(MAX_INPUT);
}
//EOF: MYEFFECT.CPP
FirstShader.hlsl contains the pixel shader code that will run on the GPU.
TEXT FirstShader.hlsl :
/*************************************************************************/
/** FirstShader.hlsl: My First Pixel Shader. **/
/** (C)2022 nlited systems, cmd **/
/*************************************************************************/
#define D2D_INPUT_COUNT 2
#include "d2d1effecthelpers.hlsli"
cbuffer constants: register(b0) {
float4 tint:COLOR;
};
D2D_PS_ENTRY(main) {
float4 color= 0;
float4 color0= D2DGetInput(0);
float4 color1= D2DGetInput(1);
if(color0.a && color1.a) {
color= (color0+color1*2)/3;
} else if(color0.a) {
color= (color0+tint)/2;
} else if(color1.a) {
color= (color1+tint)/2;
}
return color;
}
//EOF: FIRSTSHADER.HLSL
Deploy.bat runs post-build to copy the FirstShader.cso shader binary file
to the Test directory, which will be the current directory when PxlShader.exe
runs.
TEXT Deploy.bat :
REM %1 Execution directory $(SolutionDir)Test
REM %2 Output directory $(OutDir)
echo %1 %2
copy /y "%2\FirstShader.cso" "%1"
WebV7 (C)2018 nlited | Rendered by tikope in 39.323ms | 3.15.218.234