diff options
Diffstat (limited to 'unittests/shaderapitest')
| -rw-r--r-- | unittests/shaderapitest/shaderapitest.cpp | 1056 | ||||
| -rw-r--r-- | unittests/shaderapitest/shaderapitest.vpc | 25 |
2 files changed, 1081 insertions, 0 deletions
diff --git a/unittests/shaderapitest/shaderapitest.cpp b/unittests/shaderapitest/shaderapitest.cpp new file mode 100644 index 0000000..2f94b2f --- /dev/null +++ b/unittests/shaderapitest/shaderapitest.cpp @@ -0,0 +1,1056 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// The copyright to the contents herein is the property of Valve, L.L.C. +// The contents may be used and/or copied only with the written permission of +// Valve, L.L.C., or in accordance with the terms and conditions stipulated in +// the agreement/contract under which the contents have been supplied. +// +// $Header: $ +// $NoKeywords: $ +// +// Material editor +//============================================================================= + +#include <windows.h> +#include "appframework/tier2app.h" +#include "shaderapi/ishaderdevice.h" +#include "shaderapi/ishaderutil.h" +#include "shaderapi/ishaderapi.h" +#include "materialsystem/materialsystem_config.h" +#include "materialsystem/imaterialsystemhardwareconfig.h" +#include "vstdlib/random.h" +#include "filesystem.h" +#include "filesystem_init.h" +#include "tier0/icommandline.h" +#include "tier1/KeyValues.h" +#include "tier1/utlbuffer.h" +#include "tier1/lzmadecoder.h" +#include "materialsystem/imesh.h" +#include "materialsystem/shader_vcs_version.h" +#include "../utils/bzip2/bzlib.h" + + +class CShaderUtilTemp : public CBaseAppSystem< IShaderUtil > +{ +public: + // Method to allow clients access to the MaterialSystem_Config + virtual MaterialSystem_Config_t& GetConfig() + { + static MaterialSystem_Config_t config; + return config; + } + + // Allows us to convert image formats + virtual bool ConvertImageFormat( unsigned char *src, enum ImageFormat srcImageFormat, + unsigned char *dst, enum ImageFormat dstImageFormat, + int width, int height, int srcStride = 0, int dstStride = 0 ) + { + return true; + } + + // Figures out the amount of memory needed by a bitmap + virtual int GetMemRequired( int width, int height, int depth, ImageFormat format, bool mipmap ) + { + return 0; + } + + // Gets image format info + virtual const ImageFormatInfo_t& ImageFormatInfo( ImageFormat fmt ) const + { + static ImageFormatInfo_t info; + return info; + } + + // Allows us to set the default shadow state + virtual void SetDefaultShadowState() { } + + // Allows us to set the default shader state + virtual void SetDefaultState( ) { } + + // Bind standard textures + virtual void BindStandardTexture( Sampler_t stage, StandardTextureId_t id ) { } + virtual void BindStandardVertexTexture( VertexTextureSampler_t stage, StandardTextureId_t id ) { } + virtual void GetStandardTextureDimensions( int *pWidth, int *pHeight, StandardTextureId_t id ) { *pWidth = *pHeight = 0; } + + // What are the lightmap dimensions? + virtual void GetLightmapDimensions( int *w, int *h ) { *w = *h = 0; } + + // These methods are called when the shader must eject + restore HW memory + virtual void ReleaseShaderObjects() {} + virtual void RestoreShaderObjects( CreateInterfaceFn shaderFactory, int nChangeFlags = 0 ) {} + + // Used to prevent meshes from drawing. + virtual bool IsInStubMode() { return false; } + virtual bool InFlashlightMode() const { return false; } + + // For the shader API to shove the current version of aniso level into the + // "definitive" place (g_config) when the shader API decides to change it. + // Eventually, we should have a better system of who owns the definitive + // versions of config vars. + virtual void NoteAnisotropicLevel( int currentLevel ) {} + + // NOTE: Stuff after this is added after shipping HL2. + + // Are we rendering through the editor? + virtual bool InEditorMode() const { return false; } + + // Gets the bound morph's vertex format; returns 0 if no morph is bound + virtual MorphFormat_t GetBoundMorphFormat() { return 0; } + + virtual ITexture *GetRenderTargetEx( int nRenderTargetID ) { return 0; } + + // Tells the material system to draw a buffer clearing quad + virtual void DrawClearBufferQuad( unsigned char r, unsigned char g, unsigned char b, unsigned char a, bool bClearColor, bool bClearAlpha, bool bClearDepth ) OVERRIDE {} + +#if defined( _X360 ) + virtual void ReadBackBuffer( Rect_t *pSrcRect, Rect_t *pDstRect, unsigned char *pData, ImageFormat dstFormat, int nDstStride ) {} +#endif + + // Calls from meshes to material system to handle queing/threading + virtual bool OnDrawMesh( IMesh *pMesh, int firstIndex, int numIndices ) { return false; } + virtual bool OnDrawMesh( IMesh *pMesh, CPrimList *pLists, int nLists ) { return false; } + virtual bool OnSetFlexMesh( IMesh *pStaticMesh, IMesh *pMesh, int nVertexOffsetInBytes ) { return false; } + virtual bool OnSetColorMesh( IMesh *pStaticMesh, IMesh *pMesh, int nVertexOffsetInBytes ) { return false; } + virtual bool OnSetPrimitiveType( IMesh *pMesh, MaterialPrimitiveType_t type ) { return false; } + virtual bool OnFlushBufferedPrimitives() { return false; } + + + virtual void SyncMatrices() {} + virtual void SyncMatrix( MaterialMatrixMode_t ) {} + virtual int MaxHWMorphBatchCount() const { return 0; } + + virtual void GetCurrentColorCorrection( ShaderColorCorrectionInfo_t* pInfo ) + { + pInfo->m_bIsEnabled = false; + pInfo->m_nLookupCount = 0; + pInfo->m_flDefaultWeight = 0.0f; + } + virtual void OnThreadEvent( uint32 threadEvent ) {} + + ShaderAPITextureHandle_t GetShaderAPITextureBindHandle( ITexture *pTexture, int nFrame, int nTextureChannel ) { return 0; } + + // Remove any materials from memory that aren't in use as determined + // by the IMaterial's reference count. + virtual void UncacheUnusedMaterials( bool bRecomputeStateSnapshots = false ) {} + + virtual MaterialThreadMode_t GetThreadMode( ) { return MATERIAL_SINGLE_THREADED; } + virtual bool IsRenderThreadSafe( ) { return true; } +}; + + +static CShaderUtilTemp g_pTemp; + +static IShaderDeviceMgr *g_pShaderDeviceMgr; + +EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CShaderUtilTemp, IShaderUtil, + SHADER_UTIL_INTERFACE_VERSION, g_pTemp ) + +//----------------------------------------------------------------------------- +// Purpose: Warning/Msg call back through this API +// Input : type - +// *pMsg - +// Output : SpewRetval_t +//----------------------------------------------------------------------------- +SpewRetval_t SpewFunc( SpewType_t type, const char *pMsg ) +{ + if ( Plat_IsInDebugSession() ) + { + OutputDebugString( pMsg ); + if ( type == SPEW_ASSERT ) + return SPEW_DEBUGGER; + } + return SPEW_CONTINUE; +} + + +//----------------------------------------------------------------------------- +// The application object +//----------------------------------------------------------------------------- +class CShaderAPITestApp : public CTier2SteamApp +{ + typedef CTier2SteamApp BaseClass; + +public: + // Methods of IApplication + virtual bool Create(); + virtual bool PreInit( ); + virtual int Main(); + virtual void PostShutdown( ); + virtual void Destroy(); + virtual const char *GetAppName() { return "InputTest"; } + virtual bool AppUsesReadPixels() { return false; } + +private: + // Window management + bool CreateAppWindow( const char *pTitle, bool bWindowed, int w, int h ); + + // Sets up the game path + bool SetupSearchPaths(); + + // Waits for a keypress + bool WaitForKeypress(); + + // Displays information about all adapters + void DisplayAdapterInfo(); + + // Sets the video mode + bool SetMode(); + + // Creates really simple vertex + index buffers + void CreateSimpleBuffers( ShaderBufferType_t nVBType, ShaderBufferType_t nIBType, bool bBuffered ); + + // Destroys the buffers + void DestroyBuffers(); + + // Creates shaders + void CreateShaders( const char *pVShader, int nVBufLen, const char *pGShader, int nGBufLen, const char *pPShader, int nPBufLen ); + + // Destroys the buffers + void DestroyShaders(); + + // DrawUsingShaders + void TestColoredQuad( ShaderBufferType_t nVBType, ShaderBufferType_t nIBType, bool bBuffered ); + + // Tests dynamic buffers + void TestDynamicBuffers(); + + bool CreateDynamicCombos_Ver5( uint8 *pComboBuffer, bool bVertexShader ); + void LoadShaderFile( const char *pName, bool bVertexShader ); + + HWND m_HWnd; + IShaderAPI *m_pShaderAPI; + IShaderDevice *m_pShaderDevice; + + IIndexBuffer *m_pIndexBuffer; + IVertexBuffer *m_pVertexBuffer; + + VertexShaderHandle_t m_hVertexShader; + GeometryShaderHandle_t m_hGeometryShader; + PixelShaderHandle_t m_hPixelShader; +}; + +DEFINE_WINDOWED_STEAM_APPLICATION_OBJECT( CShaderAPITestApp ); + + +//----------------------------------------------------------------------------- +// Create all singleton systems +//----------------------------------------------------------------------------- +bool CShaderAPITestApp::Create() +{ + SpewOutputFunc( SpewFunc ); + + bool bIsVistaOrHigher = false; + + OSVERSIONINFO info; + info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + if ( GetVersionEx( &info ) ) + { + bIsVistaOrHigher = info.dwMajorVersion >= 6; + } + + const char *pShaderDLL = CommandLine()->ParmValue( "-shaderdll" ); + if ( !pShaderDLL ) + { + pShaderDLL = "shaderapidx10.dll"; + } + + if ( !bIsVistaOrHigher && !Q_stricmp( pShaderDLL, "shaderapidx10.dll" ) ) + { + pShaderDLL = "shaderapidx9.dll"; + } + + AppModule_t module = LoadModule( pShaderDLL ); + if ( module == APP_MODULE_INVALID ) + { + if ( module == APP_MODULE_INVALID ) + { + pShaderDLL = "shaderapidx9.dll"; + module = LoadModule( pShaderDLL ); + if ( module == APP_MODULE_INVALID ) + { + pShaderDLL = "shaderapiempty.dll"; + module = LoadModule( pShaderDLL ); + if ( module == APP_MODULE_INVALID ) + return false; + } + } + } + + g_pShaderDeviceMgr = (IShaderDeviceMgr*)AddSystem( module, SHADER_DEVICE_MGR_INTERFACE_VERSION ); + + // So that shaderapi can get ahold of our bogus IShaderUtil + module = LoadModule( Sys_GetFactoryThis() ); + AddSystem( module, SHADER_UTIL_INTERFACE_VERSION ); + + return ( g_pShaderDeviceMgr != NULL ); +} + +void CShaderAPITestApp::Destroy() +{ +} + + +//----------------------------------------------------------------------------- +// Window callback +//----------------------------------------------------------------------------- +static LRESULT CALLBACK ShaderAPITestWndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ) +{ + switch( message ) + { + case WM_DESTROY: + PostQuitMessage( 0 ); + break; + + default: + return DefWindowProc( hWnd, message, wParam, lParam ); + } + + return 0; +} + + +//----------------------------------------------------------------------------- +// Window management +//----------------------------------------------------------------------------- +bool CShaderAPITestApp::CreateAppWindow( const char *pTitle, bool bWindowed, int w, int h ) +{ + WNDCLASSEX wc; + memset( &wc, 0, sizeof( wc ) ); + wc.cbSize = sizeof( wc ); + wc.style = CS_OWNDC | CS_DBLCLKS; + wc.lpfnWndProc = ShaderAPITestWndProc; + wc.hInstance = (HINSTANCE)GetAppInstance(); + wc.lpszClassName = "Valve001"; + wc.hIcon = NULL; //LoadIcon( s_HInstance, MAKEINTRESOURCE( IDI_LAUNCHER ) ); + wc.hIconSm = wc.hIcon; + + RegisterClassEx( &wc ); + + // Note, it's hidden + DWORD style = WS_POPUP | WS_CLIPSIBLINGS; + + if ( bWindowed ) + { + // Give it a frame + style |= WS_OVERLAPPEDWINDOW; + style &= ~WS_THICKFRAME; + } + + // Never a max box + style &= ~WS_MAXIMIZEBOX; + + RECT windowRect; + windowRect.top = 0; + windowRect.left = 0; + windowRect.right = w; + windowRect.bottom = h; + + // Compute rect needed for that size client area based on window style + AdjustWindowRectEx(&windowRect, style, FALSE, 0); + + // Create the window + m_HWnd = CreateWindow( wc.lpszClassName, pTitle, style, 0, 0, + windowRect.right - windowRect.left, windowRect.bottom - windowRect.top, + NULL, NULL, (HINSTANCE)GetAppInstance(), NULL ); + + if (!m_HWnd) + return false; + + int CenterX, CenterY; + + CenterX = (GetSystemMetrics(SM_CXSCREEN) - w) / 2; + CenterY = (GetSystemMetrics(SM_CYSCREEN) - h) / 2; + CenterX = (CenterX < 0) ? 0: CenterX; + CenterY = (CenterY < 0) ? 0: CenterY; + + // In VCR modes, keep it in the upper left so mouse coordinates are always relative to the window. + SetWindowPos (m_HWnd, NULL, CenterX, CenterY, 0, 0, + SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW | SWP_DRAWFRAME); + + return true; +} + + +//----------------------------------------------------------------------------- +// Sets up the game path +//----------------------------------------------------------------------------- +bool CShaderAPITestApp::SetupSearchPaths() +{ + if ( !BaseClass::SetupSearchPaths( NULL, false, true ) ) + return false; + + g_pFullFileSystem->AddSearchPath( GetGameInfoPath(), "SKIN", PATH_ADD_TO_HEAD ); + return true; +} + + +//----------------------------------------------------------------------------- +// PreInit, PostShutdown +//----------------------------------------------------------------------------- +bool CShaderAPITestApp::PreInit( ) +{ + if ( !BaseClass::PreInit() ) + return false; + + if (!g_pFullFileSystem || !g_pShaderDeviceMgr ) + return false; + + // Add paths... + if ( !SetupSearchPaths() ) + return false; + + const char *pArg; + int iWidth = 1024; + int iHeight = 768; + bool bWindowed = (CommandLine()->CheckParm( "-fullscreen" ) == NULL); + if (CommandLine()->CheckParm( "-width", &pArg )) + { + iWidth = atoi( pArg ); + } + if (CommandLine()->CheckParm( "-height", &pArg )) + { + iHeight = atoi( pArg ); + } + + if (!CreateAppWindow( "Press a Key To Continue", bWindowed, iWidth, iHeight )) + return false; + + return true; +} + +void CShaderAPITestApp::PostShutdown( ) +{ + BaseClass::PostShutdown(); +} + + +//----------------------------------------------------------------------------- +// Waits for a keypress +//----------------------------------------------------------------------------- +bool CShaderAPITestApp::WaitForKeypress() +{ + MSG msg = {0}; + while( WM_QUIT != msg.message ) + { + if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ) + { + TranslateMessage( &msg ); + DispatchMessage( &msg ); + } + + if ( msg.message == WM_KEYDOWN ) + return true; + } + return false; +} + + +//----------------------------------------------------------------------------- +// Displays adapter information +//----------------------------------------------------------------------------- +void CShaderAPITestApp::DisplayAdapterInfo() +{ + int nAdapterCount = g_pShaderDeviceMgr->GetAdapterCount(); + for ( int i = 0; i < nAdapterCount; ++i ) + { + MaterialAdapterInfo_t info; + g_pShaderDeviceMgr->GetAdapterInfo( i, info ); + + Msg( "Adapter %d\n", i ); + Msg( "\tName: %s\n\tVendor: 0x%X\n\tDevice: 0x%X\n\tSubSystem: 0x%X\n\tRevision: 0x%X\n\tRecommended DX Level: %d\n\tMax DX Level: %d\n", + info.m_pDriverName, info.m_VendorID, info.m_DeviceID, info.m_SubSysID, info.m_Revision, info.m_nDXSupportLevel, info.m_nMaxDXSupportLevel ); + + CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER ); + KeyValues *pConfiguration = new KeyValues( "Config" ); + g_pShaderDeviceMgr->GetRecommendedConfigurationInfo( i, info.m_nDXSupportLevel, pConfiguration ); + pConfiguration->RecursiveSaveToFile( buf, 1 ); + Msg( "\tConfiguration:\n%s", ( const char * )buf.Base() ); + Msg( "\n" ); + + int nModeCount = g_pShaderDeviceMgr->GetModeCount( i ); + Msg( "\tMode Count : %d\n", nModeCount ); + for ( int j = 0; j < nModeCount; ++j ) + { + ShaderDisplayMode_t mode; + g_pShaderDeviceMgr->GetModeInfo( &mode, i, j ); + Msg( "\t\tH: %5d W: %5d Format: %3d Refresh %3d/%3d\n", + mode.m_nWidth, mode.m_nHeight, mode.m_Format, mode.m_nRefreshRateNumerator, mode.m_nRefreshRateDenominator ); + } + } +} + + +//----------------------------------------------------------------------------- +// Sets the video mode +//----------------------------------------------------------------------------- +bool CShaderAPITestApp::SetMode() +{ + int nAdapterCount = g_pShaderDeviceMgr->GetAdapterCount(); + int nAdapter = CommandLine()->ParmValue( "-adapter", 0 ); + if ( nAdapter >= nAdapterCount ) + { + Warning( "Specified too high an adapter number on the command-line (%d/%d)!\n", nAdapter, nAdapterCount ); + return false; + } + + ShaderDeviceInfo_t mode; + mode.m_DisplayMode.m_nWidth = 1024; + mode.m_DisplayMode.m_nHeight = 768; + mode.m_DisplayMode.m_Format = IMAGE_FORMAT_BGRA8888; + mode.m_DisplayMode.m_nRefreshRateNumerator = 60; + mode.m_DisplayMode.m_nRefreshRateDenominator = 1; + mode.m_bWindowed = true; + mode.m_nBackBufferCount = 1; + + CreateInterfaceFn shaderFactory = g_pShaderDeviceMgr->SetMode( m_HWnd, nAdapter, mode ); + if ( !shaderFactory ) + { + Warning( "Unable to set mode!\n" ); + return false; + } + + m_pShaderAPI = (IShaderAPI*)shaderFactory( SHADERAPI_INTERFACE_VERSION, NULL ); + m_pShaderDevice = (IShaderDevice*)shaderFactory( SHADER_DEVICE_INTERFACE_VERSION, NULL ); + if ( !m_pShaderAPI || !m_pShaderDevice ) + { + Warning( "Unable to get IShaderAPI or IShaderDevice interface!\n" ); + return false; + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Creates really simple vertex + index buffers +//----------------------------------------------------------------------------- +void CShaderAPITestApp::CreateSimpleBuffers( ShaderBufferType_t nVBType, ShaderBufferType_t nIBType, bool bBuffered ) +{ + VertexFormat_t fmt = VERTEX_POSITION | VERTEX_NORMAL | VERTEX_COLOR; + if ( IsDynamicBufferType( nVBType ) ) + { + m_pVertexBuffer = m_pShaderDevice->CreateVertexBuffer( + nVBType, VERTEX_FORMAT_UNKNOWN, 1024, "test" ); + } + else + { + m_pVertexBuffer = m_pShaderDevice->CreateVertexBuffer( + nVBType, fmt, 4, "test" ); + } + + static unsigned char s_pColors[4][4] = + { + { 255, 0, 0, 255 }, + { 0, 255, 0, 255 }, + { 0, 0, 255, 255 }, + { 255, 255, 255, 255 }, + }; + + static int nCount = 0; + + CVertexBuilder vb( m_pVertexBuffer, fmt ); + vb.Lock( 4 ); + + vb.Position3f( -1.0f, -1.0f, 0.5f ); + vb.Normal3f( 0.0f, 0.0f, 1.0f ); + vb.Color4ubv( s_pColors[nCount++ % 4] ); + vb.AdvanceVertex(); + + vb.Position3f( 1.0f, -1.0f, 0.5f ); + vb.Normal3f( 0.0f, 0.0f, 1.0f ); + vb.Color4ubv( s_pColors[nCount++ % 4] ); + vb.AdvanceVertex(); + + vb.Position3f( 1.0f, 1.0f, 0.5f ); + vb.Normal3f( 0.0f, 0.0f, 1.0f ); + vb.Color4ubv( s_pColors[nCount++ % 4] ); + vb.AdvanceVertex(); + + vb.Position3f( -1.0f, 1.0f, 0.5f ); + vb.Normal3f( 0.0f, 0.0f, 1.0f ); + vb.Color4ubv( s_pColors[nCount++ % 4] ); + vb.AdvanceVertex(); + + vb.SpewData( ); + vb.Unlock( ); + + ++nCount; + + if ( IsDynamicBufferType( nIBType ) ) + { + m_pIndexBuffer = m_pShaderDevice->CreateIndexBuffer( nIBType, MATERIAL_INDEX_FORMAT_UNKNOWN, 64, "test" ); + } + else + { + m_pIndexBuffer = m_pShaderDevice->CreateIndexBuffer( nIBType, MATERIAL_INDEX_FORMAT_16BIT, 6, "test" ); + } + CIndexBuilder ib( m_pIndexBuffer, MATERIAL_INDEX_FORMAT_16BIT ); + + ib.Lock( 6, 0 ); + ib.FastIndex( 0 ); + ib.FastIndex( 2 ); + ib.FastIndex( 1 ); + ib.FastIndex( 0 ); + ib.FastIndex( 3 ); + ib.FastIndex( 2 ); + ib.SpewData(); + ib.Unlock( ); + + m_pShaderAPI->BindVertexBuffer( 0, m_pVertexBuffer, vb.Offset(), 0, vb.TotalVertexCount(), fmt ); + m_pShaderAPI->BindIndexBuffer( m_pIndexBuffer, ib.Offset() ); +} + + +//----------------------------------------------------------------------------- +// Destroys the buffers +//----------------------------------------------------------------------------- +void CShaderAPITestApp::DestroyBuffers() +{ + if ( m_pVertexBuffer ) + { + m_pShaderDevice->DestroyVertexBuffer( m_pVertexBuffer ); + m_pVertexBuffer = NULL; + } + + if ( m_pIndexBuffer ) + { + m_pShaderDevice->DestroyIndexBuffer( m_pIndexBuffer ); + m_pIndexBuffer = NULL; + } +} + + +//----------------------------------------------------------------------------- +// shader programs +//----------------------------------------------------------------------------- +static const char s_pSimpleVertexShader[] = + "struct VS_INPUT " + "{ " + " float3 vPos : POSITION0; " + " float4 vColor : COLOR0; " + "}; " + " " + "struct VS_OUTPUT " + "{ " + " float4 projPos : POSITION0; " + " float4 vertexColor : COLOR0; " + "}; " + " " + "VS_OUTPUT main( const VS_INPUT v ) " + "{ " + " VS_OUTPUT o = ( VS_OUTPUT )0; " + " " + " o.projPos.xyz = v.vPos; " + " o.projPos.w = 1.0f; " + " o.vertexColor = v.vColor; " + " return o; " + "} " + ""; + +static const char s_pSimplePixelShader[] = + "struct PS_INPUT " + "{ " + " float4 projPos : POSITION0; " + " float4 vColor : COLOR0; " + "}; " + " " + "float4 main( const PS_INPUT i ) : COLOR " + "{ " + " return i.vColor; " + "} " + ""; + + +//----------------------------------------------------------------------------- +// Create, destroy shaders +//----------------------------------------------------------------------------- +void CShaderAPITestApp::CreateShaders( const char *pVShader, int nVBufLen, const char *pGShader, int nGBufLen, const char *pPShader, int nPBufLen ) +{ + const char *pVertexShaderVersion = g_pMaterialSystemHardwareConfig->GetDXSupportLevel() == 100 ? "vs_4_0" : "vs_2_0"; + const char *pPixelShaderVersion = g_pMaterialSystemHardwareConfig->GetDXSupportLevel() == 100 ? "ps_4_0" : "ps_2_0"; + + // Compile shaders + m_hVertexShader = m_pShaderDevice->CreateVertexShader( pVShader, nVBufLen, pVertexShaderVersion ); + Assert( m_hVertexShader != VERTEX_SHADER_HANDLE_INVALID ); + + m_hGeometryShader = GEOMETRY_SHADER_HANDLE_INVALID; + if ( g_pMaterialSystemHardwareConfig->GetDXSupportLevel() >= 100 ) + { + // m_hGeometryShader = m_pShaderDevice->CreateGeometryShader( pGShader, nGBufLen, "gs_4_0" ); + // Assert( m_hGeometryShader != GEOMETRY_SHADER_HANDLE_INVALID ); + } + + m_hPixelShader = m_pShaderDevice->CreatePixelShader( pPShader, nPBufLen, pPixelShaderVersion ); + Assert( m_hPixelShader != PIXEL_SHADER_HANDLE_INVALID ); + + m_pShaderAPI->BindVertexShader( m_hVertexShader ); + m_pShaderAPI->BindGeometryShader( m_hGeometryShader ); + m_pShaderAPI->BindPixelShader( m_hPixelShader ); +} + +void CShaderAPITestApp::DestroyShaders() +{ + m_pShaderDevice->DestroyVertexShader( m_hVertexShader ); + m_pShaderDevice->DestroyGeometryShader( m_hGeometryShader ); + m_pShaderDevice->DestroyPixelShader( m_hPixelShader ); + + m_hVertexShader = VERTEX_SHADER_HANDLE_INVALID; + m_hGeometryShader = GEOMETRY_SHADER_HANDLE_INVALID; + m_hPixelShader = PIXEL_SHADER_HANDLE_INVALID; +} + + +//----------------------------------------------------------------------------- +// DrawQuad +//----------------------------------------------------------------------------- +void CShaderAPITestApp::TestColoredQuad( ShaderBufferType_t nVBType, ShaderBufferType_t nIBType, bool bBuffered ) +{ + // clear (so that we can make sure that we aren't getting results from the previous quad) + m_pShaderAPI->ClearColor3ub( RandomInt( 0, 100 ), RandomInt( 0, 100 ), RandomInt( 190, 255 ) ); + m_pShaderAPI->ClearBuffers( true, false, false, -1, -1 ); + + CreateSimpleBuffers( nVBType, nIBType, bBuffered ); + + // Draw a quad! + CreateShaders( s_pSimpleVertexShader, sizeof(s_pSimpleVertexShader), + NULL, 0, s_pSimplePixelShader, sizeof(s_pSimplePixelShader) ); + + m_pShaderAPI->Draw( MATERIAL_TRIANGLES, 0, 6 ); + m_pShaderDevice->Present(); + + DestroyShaders(); + + DestroyBuffers(); +} + + +//----------------------------------------------------------------------------- +// Tests dynamic buffers +//----------------------------------------------------------------------------- +void CShaderAPITestApp::TestDynamicBuffers() +{ + m_pVertexBuffer = m_pShaderDevice->CreateVertexBuffer( + SHADER_BUFFER_TYPE_DYNAMIC, VERTEX_FORMAT_UNKNOWN, 0x100, "test" ); + m_pIndexBuffer = m_pShaderDevice->CreateIndexBuffer( + SHADER_BUFFER_TYPE_DYNAMIC, MATERIAL_INDEX_FORMAT_UNKNOWN, 30, "test" ); + + CreateShaders( s_pSimpleVertexShader, sizeof(s_pSimpleVertexShader), + NULL, 0, s_pSimplePixelShader, sizeof(s_pSimplePixelShader) ); + + // clear (so that we can make sure that we aren't getting results from the previous quad) + m_pShaderAPI->ClearColor3ub( RandomInt( 0, 100 ), RandomInt( 0, 100 ), RandomInt( 190, 255 ) ); + m_pShaderAPI->ClearBuffers( true, false, false, -1, -1 ); + + static unsigned char s_pColors[4][4] = + { + { 255, 0, 0, 255 }, + { 0, 255, 0, 255 }, + { 0, 0, 255, 255 }, + { 255, 255, 255, 255 }, + }; + + static int nCount = 0; + + VertexFormat_t fmt = VERTEX_POSITION | VERTEX_NORMAL | VERTEX_COLOR; + const int nLoopCount = 8; + float flWidth = 2.0f / nLoopCount; + for ( int i = 0; i < nLoopCount; ++i ) + { + CVertexBuilder vb( m_pVertexBuffer, fmt ); + vb.Lock( 4 ); + + vb.Position3f( -1.0f + i * flWidth, -1.0f, 0.5f ); + vb.Normal3f( 0.0f, 0.0f, 1.0f ); + vb.Color4ubv( s_pColors[nCount++ % 4] ); + vb.AdvanceVertex(); + + vb.Position3f( -1.0f + i * flWidth + flWidth, -1.0f, 0.5f ); + vb.Normal3f( 0.0f, 0.0f, 1.0f ); + vb.Color4ubv( s_pColors[nCount++ % 4] ); + vb.AdvanceVertex(); + + vb.Position3f( -1.0f + i * flWidth + flWidth, 1.0f, 0.5f ); + vb.Normal3f( 0.0f, 0.0f, 1.0f ); + vb.Color4ubv( s_pColors[nCount++ % 4] ); + vb.AdvanceVertex(); + + vb.Position3f( -1.0f + i * flWidth, 1.0f, 0.5f ); + vb.Normal3f( 0.0f, 0.0f, 1.0f ); + vb.Color4ubv( s_pColors[nCount++ % 4] ); + vb.AdvanceVertex(); + vb.SpewData(); + vb.Unlock( ); + + ++nCount; + + CIndexBuilder ib( m_pIndexBuffer, MATERIAL_INDEX_FORMAT_16BIT ); + + ib.Lock( 6, vb.GetFirstVertex() ); + ib.FastIndex( 0 ); + ib.FastIndex( 2 ); + ib.FastIndex( 1 ); + ib.FastIndex( 0 ); + ib.FastIndex( 3 ); + ib.FastIndex( 2 ); + ib.SpewData(); + ib.Unlock( ); + + m_pShaderAPI->BindVertexBuffer( 0, m_pVertexBuffer, vb.Offset(), vb.GetFirstVertex(), vb.TotalVertexCount(), fmt ); + m_pShaderAPI->BindIndexBuffer( m_pIndexBuffer, ib.Offset() ); + m_pShaderAPI->Draw( MATERIAL_TRIANGLES, ib.GetFirstIndex(), ib.TotalIndexCount() ); + } + + m_pShaderDevice->Present(); + + ++nCount; + + DestroyShaders(); + DestroyBuffers(); +} + + +//----------------------------------------------------------------------------- +// Create dynamic combos +//----------------------------------------------------------------------------- +static uint32 NextULONG( uint8 * &pData ) +{ + // handle unaligned read + uint32 nRet; + memcpy( &nRet, pData, sizeof( nRet ) ); + pData += sizeof( nRet ); + return nRet; +} + +bool CShaderAPITestApp::CreateDynamicCombos_Ver5( uint8 *pComboBuffer, bool bVertexShader ) +{ + uint8 *pCompressedShaders = pComboBuffer; + uint8 *pUnpackBuffer = new uint8[MAX_SHADER_UNPACKED_BLOCK_SIZE]; + + // now, loop through all blocks + bool bOK = true; + while ( bOK ) + { + uint32 nBlockSize = NextULONG( pCompressedShaders ); + if ( nBlockSize == 0xffffffff ) + { + // any more blocks? + break; + } + + switch( nBlockSize & 0xc0000000 ) + { + case 0: // bzip2 + { + // uncompress + uint32 nOutsize = MAX_SHADER_UNPACKED_BLOCK_SIZE; + int nRslt = BZ2_bzBuffToBuffDecompress( + reinterpret_cast<char *>( pUnpackBuffer ), + &nOutsize, + reinterpret_cast<char *>( pCompressedShaders ), + nBlockSize, 1, 0 ); + if ( nRslt < 0 ) + { + // errors are negative for bzip + Assert( 0 ); + Warning( "BZIP Error (%d) decompressing shader", nRslt ); + bOK = false; + } + + pCompressedShaders += nBlockSize; + nBlockSize = nOutsize; // how much data there is + } + break; + + case 0x80000000: // uncompressed + { + // not compressed, as is + nBlockSize &= 0x3fffffff; + memcpy( pUnpackBuffer, pCompressedShaders, nBlockSize ); + pCompressedShaders += nBlockSize; + } + break; + + case 0x40000000: // lzma compressed + { + nBlockSize &= 0x3fffffff; + + size_t nOutsize = CLZMA::Uncompress( + reinterpret_cast<uint8 *>( pCompressedShaders ), + pUnpackBuffer ); + pCompressedShaders += nBlockSize; + nBlockSize = nOutsize; // how much data there is + } + break; + + default: + { + Assert( 0 ); + Error(" unrecognized shader compression type = file corrupt?"); + bOK = false; + } + } + + uint8 *pReadPtr = pUnpackBuffer; + while ( pReadPtr < pUnpackBuffer+nBlockSize ) + { + uint32 nCombo_ID = NextULONG( pReadPtr ); + (void)nCombo_ID; // Suppress local variable is initialized but not referenced warning + uint32 nShaderSize = NextULONG( pReadPtr ); + + CUtlBuffer buf( pReadPtr, nShaderSize ); + if ( bVertexShader ) + { + m_pShaderDevice->CreateVertexShader( buf, "vs_2_0" ); + } + else + { + m_pShaderDevice->CreatePixelShader( buf, "ps_2_b" ); + } + + pReadPtr += nShaderSize; + } + } + + delete[] pUnpackBuffer; + + return bOK; +} + + +//----------------------------------------------------------------------------- +// Load shader +//----------------------------------------------------------------------------- +void CShaderAPITestApp::LoadShaderFile( const char *pName, bool bVertexShader ) +{ + // next, try the fxc dir + char pFileName[MAX_PATH]; + Q_snprintf( pFileName, MAX_PATH, "..\\hl2\\shaders\\fxc\\%s.vcs", pName ); + + FileHandle_t hFile = g_pFullFileSystem->Open( pFileName, "rb", "EXECUTABLE_PATH" ); + if ( hFile == FILESYSTEM_INVALID_HANDLE ) + { + Warning( "Couldn't load %s shader %s\n", bVertexShader ? "vertex" : "pixel", pName ); + return; + } + + ShaderHeader_t header; + g_pFullFileSystem->Read( &header, sizeof( ShaderHeader_t ), hFile ); + + // cache the dictionary + int nComboSize = header.m_nNumStaticCombos * sizeof( StaticComboRecord_t ); + StaticComboRecord_t *pRecords = (StaticComboRecord_t *)malloc( nComboSize ); + g_pFullFileSystem->Read( pRecords, nComboSize, hFile ); + + for ( unsigned int i = 0; i < header.m_nNumStaticCombos - 1; ++i ) + { + int nStartingOffset = pRecords[i].m_nFileOffset; + int nEndingOffset = pRecords[i+1].m_nFileOffset; + int nShaderSize = nEndingOffset - nStartingOffset; + + uint8 *pBuf = (uint8*)malloc( nShaderSize ); + g_pFullFileSystem->Seek( hFile, nStartingOffset, FILESYSTEM_SEEK_HEAD ); + g_pFullFileSystem->Read( pBuf, nShaderSize, hFile ); + + CreateDynamicCombos_Ver5( pBuf, bVertexShader ); + free( pBuf ); + + } + + free( pRecords ); + g_pFullFileSystem->Close( hFile ); + +} + + +//----------------------------------------------------------------------------- +// main application +//----------------------------------------------------------------------------- +int CShaderAPITestApp::Main() +{ + DisplayAdapterInfo(); + if ( !SetMode() ) + return 0; + + // Test buffer clearing + m_pShaderAPI->ClearColor3ub( RandomInt( 0, 100 ), RandomInt( 0, 100 ), RandomInt( 190, 255 ) ); + m_pShaderAPI->ClearBuffers( true, false, false, -1, -1 ); + m_pShaderDevice->Present(); + + SetWindowText( m_HWnd, "ClearBuffers test results . . hit a key" ); + + if ( !WaitForKeypress() ) + return 1; + + // Test viewport + int nMaxViewports = g_pMaterialSystemHardwareConfig->MaxViewports(); + ShaderViewport_t* pViewports = ( ShaderViewport_t* )_alloca( nMaxViewports * sizeof(ShaderViewport_t) ); + for ( int i = 0; i < nMaxViewports; ++i ) + { + int x = RandomInt( 0, 100 ); + int y = RandomInt( 0, 100 ); + int w = RandomInt( 100, 200 ); + int h = RandomInt( 100, 200 ); + pViewports[i].Init( x, y, w, h ); + + m_pShaderAPI->SetViewports( i+1, pViewports ); + } + + SetWindowText( m_HWnd, "SetViewports test results . . hit a key" ); + + if ( !WaitForKeypress() ) + return 1; + + // Sets up a full-screen viewport + int w, h; + m_pShaderDevice->GetWindowSize( w, h ); + + ShaderViewport_t viewport; + viewport.Init( 0, 0, w, h ); + m_pShaderAPI->SetViewports( 1, &viewport ); + + // Test drawing a full-screen quad with interpolated vertex colors for every combo of static/dynamic VB, static dynamic IB, buffered/non-buffered. + char buf[1024]; + for ( int nVBType = 0; nVBType < SHADER_BUFFER_TYPE_COUNT; ++nVBType ) + { + // FIXME: Remove + if ( nVBType > SHADER_BUFFER_TYPE_DYNAMIC ) + continue; + + for( int nIBType = 0; nIBType < SHADER_BUFFER_TYPE_COUNT; ++nIBType ) + { + // FIXME: Remove + if ( nIBType > SHADER_BUFFER_TYPE_DYNAMIC ) + continue; + + // MESHFIXME: make buffered vertex buffers/index buffers work. + int nBuffered = 0; +// for( nBuffered = 0; nBuffered < 2; nBuffered++ ) + { + TestColoredQuad( (ShaderBufferType_t)nVBType, (ShaderBufferType_t)nIBType, nBuffered != 0 ); + + sprintf( buf, "TestColoredQuad results VB: %d IB: %d Buffered: %d HIT A KEY!", + nVBType, nIBType, nBuffered != 0 ); + SetWindowText( m_HWnd, buf ); + + if ( !WaitForKeypress() ) + return 1; + } + } + } + + SetWindowText( m_HWnd, "Dynamic Buffer Test: HIT A KEY!" ); + TestDynamicBuffers(); + if ( !WaitForKeypress() ) + return 1; + + g_pMaterialSystemHardwareConfig->OverrideStreamOffsetSupport( true, false ); + + SetWindowText( m_HWnd, "Dynamic Buffer Test (no stream offset): HIT A KEY!" ); + TestDynamicBuffers(); + if ( !WaitForKeypress() ) + return 1; + + g_pMaterialSystemHardwareConfig->OverrideStreamOffsetSupport( false, false ); + + return 1; +} diff --git a/unittests/shaderapitest/shaderapitest.vpc b/unittests/shaderapitest/shaderapitest.vpc new file mode 100644 index 0000000..30ba082 --- /dev/null +++ b/unittests/shaderapitest/shaderapitest.vpc @@ -0,0 +1,25 @@ +//----------------------------------------------------------------------------- +// SHADERAPITEST.VPC +// +// Project Script +//----------------------------------------------------------------------------- + +$Macro SRCDIR "..\.." +$Macro OUTBINDIR "$SRCDIR\..\game\bin" + +$Include "$SRCDIR\vpc_scripts\source_exe_base.vpc" + +$Project "ShaderAPITest" +{ + $Folder "Source Files" + { + $File "shaderapitest.cpp" + } + + $Folder "Link Libraries" + { + $Lib appframework + $Lib tier2 + $Lib $LIBCOMMON\bzip2 + } +} |