/*---------------------------------------------------------------------------* Project: NintendoWare File: gfx_ResShader.cpp Copyright (C)2009-2011 Nintendo/HAL Laboratory, Inc. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo and/or its licensed developers and are protected by national and international copyright laws. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. The content herein is highly confidential and should be handled accordingly. $Revision: $ *---------------------------------------------------------------------------*/ #include "../precompiled.h" #include #include #include #include #include #include namespace nw { namespace gfx { namespace res { typedef Result (*SetupFunc)(os::IAllocator* allocator, ResShader resShader); static Result ResBinaryShader_Setup(os::IAllocator* allocator, ResShader resShader); static Result ResReferenceShader_Setup(os::IAllocator* allocator, ResShader resShader); static SetupFunc s_ShaderSetupTable[] = { ResBinaryShader_Setup, ResReferenceShader_Setup }; //---------------------------------------- ResBinaryShader ResShader::Dereference() { NW_ASSERT( this->IsValid() ); switch ( this->ref().typeInfo ) { case ResBinaryShader::TYPE_INFO: { return ResStaticCast(*this); } case ResReferenceShader::TYPE_INFO: { ResReferenceShader resRefShader = ResStaticCast( *this ); NW_ASSERT( resRefShader.GetTargetShader().IsValid() ); return resRefShader.GetTargetShader().Dereference(); } default: { return ResBinaryShader( NULL ); } } } //---------------------------------------- const ResBinaryShader ResShader::Dereference() const { NW_ASSERT( this->IsValid() ); switch ( this->ref().typeInfo ) { case ResBinaryShader::TYPE_INFO: { return ResStaticCast(*this); } case ResReferenceShader::TYPE_INFO: { ResReferenceShader resRefShader = ResStaticCast( *this ); NW_ASSERT( resRefShader.GetTargetShader().IsValid() ); return resRefShader.GetTargetShader().Dereference(); } default: { return ResBinaryShader( NULL ); } } } //---------------------------------------- Result ResShader::Setup(os::IAllocator* allocator, ResGraphicsFile graphicsFile) { NW_UNUSED_VARIABLE(graphicsFile); NW_ASSERT( internal::ResCheckRevision( *this ) ); Result result = RESOURCE_RESULT_OK; switch ( this->ref().typeInfo ) { case ResBinaryShader::TYPE_INFO: { result |= s_ShaderSetupTable[0]( allocator, *this ); } break; case ResReferenceShader::TYPE_INFO: { result |= s_ShaderSetupTable[1]( allocator, *this ); } break; default: { } } return result; } //---------------------------------------- Result ResShader::Setup(os::IAllocator* allocator) { return this->Setup(allocator, ResGraphicsFile(NULL)); } //---------------------------------------- static Result ResBinaryShader_Setup(os::IAllocator* allocator, ResShader resShader) { Result result = RESOURCE_RESULT_OK; ResBinaryShader resBinaryShader = ResDynamicCast(resShader); NW_ASSERT(resBinaryShader.GetShaderObjectsCount() != 0); u32 shaderObjects = resBinaryShader.GetShaderObjects(0); if (shaderObjects != NULL) { return result; } if (allocator == NULL) { allocator = CommandCacheManager::GetAllocator(); } resBinaryShader.ref().m_CommandAllocator = allocator; for (int i = 0; i < resBinaryShader.GetShaderKindsCount(); i++) { #if defined(NW_GFX_PROGRAM_OBJECT_ENABLED) GLuint shader = glCreateShader(resBinaryShader.GetShaderKinds(i)); #else u32 shader = reinterpret_cast(resShader.ptr()); #endif resBinaryShader.SetShaderObjects(i, shader); } void* binaryAnalyzerBuffer = allocator->Alloc( sizeof(ShaderBinaryInfo), 4 ); resBinaryShader.ref().m_ShaderBinaryInfo = new(binaryAnalyzerBuffer) ShaderBinaryInfo( resBinaryShader.GetBinaryData() ); ShaderBinaryInfo* shaderInfo = resBinaryShader.GetShaderBinaryInfo(); shaderInfo->AnalyzeBinary(); s32 commandSize = shaderInfo->GetCommonCommandSize(); u32* buffer = reinterpret_cast(allocator->Alloc( commandSize, 4 )); // シェーダバイナリの設定コマンドを自前で作成する。 s32 writtenSize = shaderInfo->BuildCommonCommand(buffer, static_cast(commandSize)); NW_ASSERT(writtenSize == commandSize); resBinaryShader.ref().m_CommandCache = buffer; resBinaryShader.ref().m_CommandCacheSize = writtenSize; #if defined(NW_GFX_PROGRAM_OBJECT_ENABLED) glShaderBinary( resBinaryShader.GetShaderObjectsCount(), reinterpret_cast(resBinaryShader.GetShaderObjects()), GL_PLATFORM_BINARY_DMP, resBinaryShader.GetBinaryData(), resBinaryShader.GetBinaryDataCount()); #endif for (int i = 0; i < resBinaryShader.GetDescriptionsCount(); i++) { ResShaderProgramDescription description = resBinaryShader.GetDescriptions(i); GLuint vertexShader = 0; s32 vertexIndex = resBinaryShader.GetDescriptions(i).GetVertexShaderIndex(); NW_MINMAXLT_ASSERT(vertexIndex, 0, resBinaryShader.GetShaderObjectsCount()); vertexShader = resBinaryShader.GetShaderObjects(vertexIndex); GLuint geometryShader = 0; s32 geometryIndex = resBinaryShader.GetDescriptions(i).GetGeometryShaderIndex(); if (0 <= geometryIndex && geometryIndex < resBinaryShader.GetShaderObjectsCount()) { geometryShader = resBinaryShader.GetShaderObjects(geometryIndex); } resBinaryShader.GetDescriptions(i).SetVertexShaderObject( vertexShader ); resBinaryShader.GetDescriptions(i).SetGeometryShaderObject( geometryShader ); resBinaryShader.GetDescriptions(i).Setup(allocator); } return result; } //---------------------------------------- static Result ResReferenceShader_Setup(os::IAllocator* allocator, ResShader resShader) { Result result = RESOURCE_RESULT_OK; ResReferenceShader resRefShader = ResDynamicCast( resShader ); NW_ASSERT( resRefShader.IsValid() ); NW_ASSERT( resRefShader.GetTargetShader().IsValid() ); ResBinaryShader binaryShader = ResDynamicCast(resRefShader.GetTargetShader()); if (binaryShader.IsValid()) { ResBinaryShader_Setup(allocator, binaryShader); } return result; } //---------------------------------------- void ResShader::Cleanup() { ResBinaryShader resBinaryShader = ResDynamicCast(*this); if (resBinaryShader.IsValid()) { os::IAllocator* allocator = resBinaryShader.ref().m_CommandAllocator; if ( resBinaryShader.ref().m_CommandCache != NULL ) { resBinaryShader.ref().m_CommandAllocator->Free( resBinaryShader.ref().m_CommandCache ); resBinaryShader.ref().m_CommandCache = NULL; resBinaryShader.ref().m_CommandCacheSize = 0; } if ( resBinaryShader.ref().m_ShaderBinaryInfo != NULL ) { ShaderBinaryInfo* shaderInfo = resBinaryShader.GetShaderBinaryInfo(); shaderInfo->~ShaderBinaryInfo(); resBinaryShader.ref().m_CommandAllocator->Free( shaderInfo ); resBinaryShader.ref().m_ShaderBinaryInfo = NULL; } ut::SafeCleanupAll(resBinaryShader.GetDescriptions()); for (int i = 0; i < resBinaryShader.GetShaderObjectsCount(); i++) { u32 shaderObject = resBinaryShader.GetShaderObjects(i); if (shaderObject) { #if defined(NW_GFX_PROGRAM_OBJECT_ENABLED) glDeleteShader(shaderObject); #endif resBinaryShader.SetShaderObjects(i, 0); } } // ResShaderProgramDescription::Cleanup でこの Allocator を参照してるので最後にクリアする。 resBinaryShader.ref().m_CommandAllocator = NULL; } } //---------------------------------------- Result ResShaderProgramDescription::Setup(os::IAllocator* allocator) { if (allocator == NULL) { allocator = CommandCacheManager::GetAllocator(); } NW_ASSERT( allocator == ResBinaryShader(this->GetOwnerShaderData()).ref().m_CommandAllocator ); NW_UNUSED_VARIABLE(allocator); Result result = RESOURCE_RESULT_OK; #if defined(NW_GFX_PROGRAM_OBJECT_ENABLED) if (this->GetProgramObject() == NULL) { this->ref().m_ProgramObject = CreateProgramObject(); ShaderUniformLocation* uniformLocation = ShaderUniformLocation::Create(allocator); uniformLocation->BuildUniformLocations(this->ref().m_ProgramObject); this->SetUniformLocation(uniformLocation); } #endif s32 symbolsCount = this->GetSymbolsCount(); for (int i = 0; i < symbolsCount; ++i) { ResShaderSymbol resShaderSymbol = this->GetSymbols(i); NW_ASSERT(resShaderSymbol.IsValid()); // シェーダーシンボルのLocationが-1のときのみロケーション値の取得を行います。 if (resShaderSymbol.GetLocation() == -1) { s32 regIndex = this->GetVertexUniformIndex( resShaderSymbol.GetName(), NULL ); if (regIndex >= 0) { resShaderSymbol.SetGeometryUniform( false ); resShaderSymbol.SetLocation(regIndex); } else { regIndex = this->GetGeometryUniformIndex( resShaderSymbol.GetName(), NULL ); if (regIndex >= 0) { resShaderSymbol.SetGeometryUniform( true ); resShaderSymbol.SetLocation(regIndex); } else { resShaderSymbol.SetLocation(-1); result |= RESOURCE_RESULT_IRRELEVANT_LOCATION_SHADER_SYMBOL; } } } } if ( ref().m_CommandCache ) { return result; } // 各シェーダプログラムの設定コマンドを自前で作成する。 ResBinaryShader ownerShader = ResBinaryShader( this->GetOwnerShaderData() ); ShaderBinaryInfo* shaderInfo = ownerShader.GetShaderBinaryInfo(); s32 vertexShaderIndex = this->GetVertexShaderIndex(); s32 geometryShaderIndex = this->GetGeometryShaderIndex(); s32 commandSize = shaderInfo->GetShaderProgramCommandSize( vertexShaderIndex, geometryShaderIndex ); u32* buffer = reinterpret_cast(allocator->Alloc( commandSize, 4 )); s32 writtenSize = shaderInfo->BuildShaderProgramCommand( vertexShaderIndex, geometryShaderIndex, buffer, commandSize ); NW_ASSERT( writtenSize == commandSize ); ref().m_CommandCache = buffer; ref().m_CommandCacheSize = writtenSize; return result; } //---------------------------------------- void ResShaderProgramDescription::Cleanup() { #if defined(NW_GFX_PROGRAM_OBJECT_ENABLED) if (this->GetUniformLocation()) { ShaderUniformLocation* uniformLocation = static_cast(this->GetUniformLocation()); uniformLocation->Destroy(); this->SetUniformLocation(NULL); } #endif s32 symbolsCount = this->GetSymbolsCount(); for (int i = 0; i < symbolsCount; ++i) { ResShaderSymbol resShaderSymbol = this->GetSymbols(i); NW_ASSERT(resShaderSymbol.IsValid()); resShaderSymbol.SetLocation(-1); resShaderSymbol.SetGeometryUniform(false); } #if defined(NW_GFX_PROGRAM_OBJECT_ENABLED) if (this->GetProgramObject()) { if (this->GetVertexShaderObject() != 0) { glDetachShader(this->GetProgramObject(), this->GetVertexShaderObject()); } if (this->GetGeometryShaderObject() != 0) { glDetachShader(this->GetProgramObject(), this->GetGeometryShaderObject()); } glDetachShader(this->GetProgramObject(), GL_DMP_FRAGMENT_SHADER_DMP); glDeleteProgram(this->GetProgramObject()); } #endif this->ref().m_ProgramObject = 0; if ( this->ref().m_CommandCache ) { os::IAllocator* allocator = ResBinaryShader(this->GetOwnerShaderData()).ref().m_CommandAllocator; allocator->Free( this->ref().m_CommandCache ); this->ref().m_CommandCache = NULL; this->ref().m_CommandCacheSize = 0; } } //---------------------------------------- s32 ResShaderProgramDescription::GetVertexUniformIndex( const char* name, ShaderBinaryInfo::SymbolType* pSymbolType ) const { const ShaderBinaryInfo* shaderInfo = this->GetShaderBinaryInfo(); NW_NULL_ASSERT( shaderInfo ); ::std::pair uniformInfo = shaderInfo->SearchUniformIndex( this->GetVertexShaderIndex(), name ); if ( pSymbolType ) { *pSymbolType = uniformInfo.second; } return uniformInfo.first; } //---------------------------------------- s32 ResShaderProgramDescription::GetGeometryUniformIndex( const char* name, ShaderBinaryInfo::SymbolType* pSymbolType ) const { const ShaderBinaryInfo* shaderInfo = this->GetShaderBinaryInfo(); NW_NULL_ASSERT( shaderInfo ); ::std::pair uniformInfo = shaderInfo->SearchUniformIndex( this->GetGeometryShaderIndex(), name ); if ( pSymbolType ) { *pSymbolType = uniformInfo.second; } return uniformInfo.first; } #if defined(NW_GFX_PROGRAM_OBJECT_ENABLED) //---------------------------------------- GLuint ResShaderProgramDescription::CreateProgramObject() { GLuint programObject = glCreateProgram(); NW_ASSERTMSG(0 != programObject, "Can't create program."); AttachProgram(programObject); LinkProgram(programObject); return programObject; } //---------------------------------------- void ResShaderProgramDescription::AttachProgram(GLuint programObject) { NW_ASSERT(this->GetVertexShaderObject() != 0); glAttachShader(programObject, this->GetVertexShaderObject()); if (this->GetGeometryShaderObject() != 0) { glAttachShader(programObject, this->GetGeometryShaderObject()); } glAttachShader(programObject, GL_DMP_FRAGMENT_SHADER_DMP); int index = 0; for (int i = 0; i < this->GetAttributeSymbolsCount(); ++i) { if (const char* symbol = this->GetAttributeSymbols(i)) { glBindAttribLocation(programObject, index, symbol); ++index; } } } //---------------------------------------- void ResShaderProgramDescription::LinkProgram(GLuint programObject) { glLinkProgram(programObject); int linked; glGetProgramiv(programObject, GL_LINK_STATUS, &linked); if (linked == 0) { glDeleteProgram(programObject); NW_FATAL_ERROR("Shader Link Error"); } glValidateProgram(programObject); } #endif } /* namespace res */ } /* namespace gfx */ } /* namespace nw */