Ejemplo n.º 1
0
//-----------------------------------------------------------------------------
/// Handles operations that need to occur before profiling an API call.
/// \param funcId The FuncId corresponding to the API call being traced.
/// \param pWrappedCmdBuf The interface pointer used to invoke the API call.
//-----------------------------------------------------------------------------
void VktFrameProfilerLayer::PreCall(FuncId funcId, VktWrappedCmdBuf* pWrappedCmdBuf)
{
    if (pWrappedCmdBuf->IsProfilingEnabled() && ShouldProfileFunction(funcId))
    {
        osThreadId threadId = osGetCurrentThreadId();
        SampleInfo* pSampleInfo = GetSampleInfoForThread(threadId);

        if (pSampleInfo != nullptr)
        {
            UINT64 nextSampleId = SetNextSampleId(pSampleInfo);

            ProfilerMeasurementId measurementId = {};
            VktUtil::ConstructMeasurementInfo(funcId, nextSampleId, pWrappedCmdBuf, GetParentLayerManager()->GetFrameCount(), pWrappedCmdBuf->FillCount(), measurementId);

            ProfilerResultCode beginResult = pWrappedCmdBuf->BeginCmdMeasurement(&measurementId);

            if (beginResult == PROFILER_SUCCESS)
            {
                pSampleInfo->mSampleId = measurementId.sampleId;
                pSampleInfo->mbBeginSampleSuccessful = true;
            }
            else
            {
                Log(logERROR, "Failed BeginCmdMeasurement. CmdBuf='0x%p' SampleId='%d'\n", pWrappedCmdBuf->AppHandle(), measurementId.sampleId);
            }
        }
        else
        {
            Log(logERROR, "Failed to find or create SampleInfo instance for Thread %d\n", threadId);
        }
    }
}
Ejemplo n.º 2
0
//-----------------------------------------------------------------------------
/// Construct a measurement info structure for each call that will be profiled.
/// \param inFuncId The function ID for the function being profiled.
/// \param inSampleId The SampleID associated with the profiled command.
/// \param pCmdListWrapped The command list that executed the profiled command.
/// \param out A ProfilerMeasurementId containing metadata for the new measurement.
//-----------------------------------------------------------------------------
void DX12FrameProfilerLayer::ConstructMeasurementInfo(FuncId inFuncId, UINT64 inSampleId, Wrapped_ID3D12GraphicsCommandListCustom* pCmdListWrapped, ProfilerMeasurementId& out)
{
    out.pWrappedCmdList = pCmdListWrapped;
    out.mCmdListType    = pCmdListWrapped->GetCmdListType();
    out.mSampleId       = inSampleId;
    out.mFrameIndex     = GetParentLayerManager()->GetFrameCount();
    out.mFunctionId     = inFuncId;
}
//--------------------------------------------------------------------------
/// BeginFrame is called when a new frame is started, and should setup the
/// collection process to log each API call issued during the frame.
/// \return Nothing.
//--------------------------------------------------------------------------
void MultithreadedTraceAnalyzerLayer::BeginFrame()
{
    bool bFrameCaptureWithSaveActive = GetParentLayerManager()->mCmdFrameCaptureWithSave.IsActive();

    // Check if automatic tracing is enabled for a specific frame. Determine which trace type by examining the result.
    int autotraceFlags = GetTraceTypeFlags();

    // Pick up new requests that are active and flip the switch to begin tracing calls.
    bool bLinkedTraceRequested = (mCmdLinkedTrace.IsActive() || bFrameCaptureWithSaveActive) || (autotraceFlags == kTraceType_Linked) || mbLinkedTraceForCapture;

    // If a linked trace is required, turn on both trace switches.
    bool bAPITraceNeeded = OnlyAPITraceRequested() || bLinkedTraceRequested || (autotraceFlags & kTraceType_API);
    bool bGPUTraceNeeded = OnlyGPUTraceRequested() || bLinkedTraceRequested || (autotraceFlags & kTraceType_GPU);

    mFramestartTime = mFramestartTimer.GetRaw();

    if (bAPITraceNeeded || bGPUTraceNeeded)
    {
        // Set the flag indicating that the frame is being traced. We'll be rendering the next frame when this flag is checked.
        mLastTracedFrameIndex = GetParentLayerManager()->GetFrameCount();

        // Clear out the previous trace data before tracing the new frame.
        Clear();

        // We need to enable tracing no matter what so that we go into the PreCall/PostCall.
        SetCollectTrace(true);

        if (bAPITraceNeeded)
        {
            // Enable global trace collection so API and GPU trace can happen.
            BeforeAPITrace();
            mbCollectingApiTrace = true;
        }

        if (bGPUTraceNeeded)
        {
            BeforeGPUTrace();

            // Enable GPU time collection
            ModernAPIFrameProfilerLayer* frameProfiler = GetParentLayerManager()->GetFrameProfilerLayer();
            frameProfiler->SetProfilingEnabled(true);
            mbCollectingGPUTrace = true;
        }
    }
}
//--------------------------------------------------------------------------
/// Check if the newly-started frame should be automatically traced at a specific frame.
/// \returns An eTraceType value, corresponding to the value set in the server config.
//--------------------------------------------------------------------------
int MultithreadedTraceAnalyzerLayer::GetTraceTypeFlags()
{
    int traceTypeFlags = kTraceType_None;

    // It's time to capture a frame. Check the trace type that was requested.
    if (GetParentLayerManager()->IsAutocaptureFrame())
    {
        traceTypeFlags = SG_GET_INT(OptionTraceType);
    }

    return traceTypeFlags;
}
//--------------------------------------------------------------------------
/// Write a trace's metadata file and return the contenst through the out-param.
/// \param inHeaderString The full response string for a collected linked trace request.
/// \param inResponseString Response string
/// \param outMetadataXML The XML metadata string to return to the client.
/// \returns True if writing the metadata file was succesful.
//--------------------------------------------------------------------------
bool MultithreadedTraceAnalyzerLayer::WriteTraceAndMetadataFiles(const gtASCIIString& inHeaderString, const gtASCIIString& inResponseString, std::string& outMetadataXML)
{
    bool bWrittenSuccessfully = false;

    // Empty out the incoming path to the metadata file. We'll know the exact path later.
    outMetadataXML.assign("");

    // Use this object to pass data into and out of the GetSessionManagaerData() method.
    SessionManagerData smd;

    smd.frameIndex = GetParentLayerManager()->GetFrameCount();

    bool result = SessionManager::Instance()->GetSessionManagerData(smd);

    if (result == false)
    {
        return result;
    }

    gtASCIIString pathToMetadataFile = smd.pathToDataDirectory; //pathToDataDirectory;
    pathToMetadataFile.appendFormattedString("%s", smd.metadataFilename.asCharArray());

    gtString fullMetadataFilepathAsGTString;
    fullMetadataFilepathAsGTString.fromASCIIString(pathToMetadataFile.asCharArray());

    osFile metadataFile(fullMetadataFilepathAsGTString);
    bool bMetadataFileOpened = metadataFile.open(osChannel::OS_ASCII_TEXT_CHANNEL, osFile::OS_OPEN_TO_WRITE);

    // If we've successfully opened the metadata file, we'll also attempt to write the trace file.
    if (bMetadataFileOpened == false)
    {
        Log(logERROR, "Failed to open trace metadata file for writing: '%s'\n", smd.metadataFilename.asCharArray());
        return false;
    }

    osFilePath traceFileDirectory;
    traceFileDirectory.setPath(osFilePath::OS_TEMP_DIRECTORY);

    traceFileDirectory.appendSubDirectory(smd.toolDirectory);

    // Construct a filename for the cached trace response.
    gtASCIIString fullTraceFilename;
    fullTraceFilename.appendFormattedString("LinkedTrace-%s-%d-%d-%d-%d-%d-%d.ltr", smd.appName.asASCIICharArray(), smd.year, smd.month, smd.day, smd.hour, smd.minute, smd.second);

    gtASCIIString fullTraceFilePath = smd.pathToDataDirectory;
    fullTraceFilePath.appendFormattedString("%s", fullTraceFilename.asCharArray());

    gtString fullTraceResponseFilepathGTString;
    fullTraceResponseFilepathGTString.fromASCIIString(fullTraceFilePath.asCharArray());

    // Write the contents of the trace response file.
    osFile traceResponseFile(fullTraceResponseFilepathGTString);
    bool bTraceResponseFileOpened = traceResponseFile.open(osChannel::OS_ASCII_TEXT_CHANNEL, osFile::OS_OPEN_TO_WRITE);

    if (bTraceResponseFileOpened)
    {
        traceResponseFile.writeString(inHeaderString);
        traceResponseFile.writeString(inResponseString);
        traceResponseFile.close();
    }
    else
    {
        Log(logERROR, "Failed to write trace response to file: '%s'\n", fullTraceResponseFilepathGTString.asASCIICharArray());
    }

    // Write the filename for the associated trace response that was just collected.
    std::string traceFilepathAsString;
    traceFilepathAsString.assign(fullTraceResponseFilepathGTString.asASCIICharArray());

    TraceMetadata metadataToWrite;

    // Insert the location of the metadata file being written out.
    metadataToWrite.mMetadataFilepath = pathToMetadataFile.asCharArray();

    // Insert the path to the cached trace file.
    metadataToWrite.mPathToTraceFile = fullTraceFilePath.asCharArray();

    // Write object files info. It is currently assumed trace and object data will exists simultaneously.
    metadataToWrite.mPathToObjectTreeFile = smd.pathToDataDirectory.asCharArray();
    metadataToWrite.mPathToObjectTreeFile.append("ObjectTree.xml");

    metadataToWrite.mPathToObjectDatabaseFile = smd.pathToDataDirectory.asCharArray();
    metadataToWrite.mPathToObjectDatabaseFile.append("FullObjectDatabase.xml");

    ModernAPIFrameDebuggerLayer* frameDebugger = GetParentLayerManager()->GetFrameDebuggerLayer();

    unsigned char* pngData = NULL;
    unsigned int numBytes = 0;

    // NOTE: Passing in 0 for the width and height will cause the renderer to render the PNG image at the same resolution as the applications frame buffer (i.e full resolution).
    bool bCapturedSuccessfully = frameDebugger->CaptureFrameBuffer(0, 0, &pngData, &numBytes, true);

    if (bCapturedSuccessfully)
    {
        gtASCIIString fullImageFilename;
        fullImageFilename.appendFormattedString("%s_FrameBuffer%d.png", smd.appName.asASCIICharArray(), smd.frameIndex);

        gtASCIIString imageFilepath = smd.pathToDataDirectory;
        imageFilepath.appendFormattedString("%s", fullImageFilename.asCharArray());

        FILE* frameBufferImageFile = fopen(imageFilepath.asCharArray(), "wb");

        if (frameBufferImageFile != NULL)
        {
            fwrite(pngData, sizeof(unsigned char), numBytes, frameBufferImageFile);
            fclose(frameBufferImageFile);

            // Add the captured image's path into the XML metadata.
            metadataToWrite.mPathToFrameBufferImage = imageFilepath.asCharArray();
        }
        else
        {
            Log(logERROR, "Failed to write frame buffer image file.\n");
        }

        SAFE_DELETE_ARRAY(pngData);
    }
    else
    {
        metadataToWrite.mPathToFrameBufferImage = "ERROR - Failed to capture frame buffer image.";
        Log(logERROR, "Failed to capture frame buffer for captured frame.\n");
    }

    // @TODO: This is placeholder for now to get a prototype working.
    // We should also be able to set this to "Capture".
    metadataToWrite.mTraceType = kTraceType_Linked;

    // @TODO: This is temporary until TraceMetadata generation is moved to the LayerManager.
    FrameInfo frameInfo;
    frameDebugger->GetFrameInfo(&frameInfo);

    // Populate the metadata structure with the values stored in the LayerManager.
    metadataToWrite.mFrameInfo = &frameInfo;
    metadataToWrite.mArchitecture = smd.moduleArchitecture;
    metadataToWrite.mAPICallCount = GetNumTracedAPICalls();
    metadataToWrite.mDrawCallCount = GetNumTracedDrawCalls();

    bool bMetadataWriteSuccessful = WriteMetadataFile(&metadataToWrite, pathToMetadataFile.asCharArray(), outMetadataXML);

    if (bMetadataWriteSuccessful)
    {
        bWrittenSuccessfully = true;
    }

    return bWrittenSuccessfully;
}
//--------------------------------------------------------------------------
/// Handle what happens when a Linked Trace is requested. We can either:
/// 1. Return the trace response as normal.
/// 2. Cache the response to disk, and generate a "trace metadata" file used to retrieve the trace later.
/// \param inFullResponseString The response string built by tracing the application.
/// \param inbSaveResponseToFile A switch used to determine which response method to use.
//--------------------------------------------------------------------------
void MultithreadedTraceAnalyzerLayer::HandleLinkedTraceResponse(gtASCIIString& inFullResponseString, bool inbSaveResponseToFile)
{
    ModernAPILayerManager* parentLayerManager = GetParentLayerManager();

    if (parentLayerManager->InCapturePlayer())
    {
        const std::string& metadataFile = parentLayerManager->GetPathToTargetMetadataFile();

        if (metadataFile.length() > 0)
        {
            // Read the metadata file and store the contents in a structure.
            TraceMetadata traceMetadata;
            traceMetadata.mFrameInfo = new FrameInfo;

            bool bReadMetadataFileSuccessfully = ReadMetadataFile(metadataFile, &traceMetadata);

            if (bReadMetadataFileSuccessfully)
            {
                gtASCIIString traceContents;
                bool bReadTraceSuccessfully = LoadTraceFile(traceMetadata.mPathToTraceFile, traceContents);

                if (bReadTraceSuccessfully)
                {
                    // At this point the full trace response text should be loaded into our string and ready to be sent back to the client.
                    mCmdLinkedTrace.Send(traceContents.asCharArray());
                }
                else
                {
                    Log(logERROR, "Failed to read trace file at '%s'.", traceMetadata.mPathToTraceFile.c_str());
                }
            }
            else
            {
                Log(logERROR, "Failed to read metadata file at '%s'.", metadataFile.c_str());
            }

            // Destroy the FrameInfo instance that was created above.
            SAFE_DELETE(traceMetadata.mFrameInfo);
        }
        else
        {
            Log(logERROR, "Failed to locate valid path to trace metadata file.");
        }
    }
    else
    {
        gtASCIIString traceHeaderBlock;
        bool bBuiltHeaderSuccessfully = GenerateLinkedTraceHeader(traceHeaderBlock);

        if (bBuiltHeaderSuccessfully)
        {
            bool bKeypressTrigger = parentLayerManager->IsTraceTriggeredByKeypress();

            // Collect a trace and generate the trace metadata string. Write the trace and metadata files to disk.
            std::string metadataXMLString;
            bool bWriteMetadataSuccessful = WriteTraceAndMetadataFiles(traceHeaderBlock, inFullResponseString, metadataXMLString);

            // If the trace wasn't triggered by a keypress, we'll need to send a response back through either of the following commands.
            CommandResponse& frameCaptureWithSaveResponse = (inbSaveResponseToFile == true) ? parentLayerManager->mCmdFrameCaptureWithSave : mCmdLinkedTrace;

            if (bWriteMetadataSuccessful)
            {
                // We only need to send the response back through a request if the client triggered collection.
                if (!bKeypressTrigger)
                {
                    // Check if we want to cache the response to disk, or return it as-is.
                    if (inbSaveResponseToFile)
                    {
                        if (bWriteMetadataSuccessful)
                        {
                            // Send a response back to the client indicating which trace metadata file was written to disk.
                            frameCaptureWithSaveResponse.Send(metadataXMLString.c_str());
                        }
                        else
                        {
                            Log(logERROR, "Failed to write trace metadata XML.\n");
                            frameCaptureWithSaveResponse.Send("Failed");
                        }
                    }
                    else
                    {
                        // Send a response containing the API and GPU trace text.
                        frameCaptureWithSaveResponse.Send(inFullResponseString.asCharArray());
                    }
                }
                else
                {
                    Log(logMESSAGE, "Successfully traced frame %d.\n", parentLayerManager->GetFrameCount());
                }
            }
            else
            {
                Log(logERROR, "Failed to write trace metadata XML.\n");

                // If a failed trace collection was triggered by a command, we need to respond with an error message.
                if (!bKeypressTrigger)
                {
                    frameCaptureWithSaveResponse.Send("Failed");
                }
            }
        }
    }
}
//--------------------------------------------------------------------------
/// EndFrame is used to signal the end of a rendered frame and to stop/send
/// the captured results that were logged during the frame render.
/// \return Nothing.
//--------------------------------------------------------------------------
void MultithreadedTraceAnalyzerLayer::EndFrame()
{
    // Check again which trace type is active at the end of the frame. Need to match how it was started.
    int autotraceFlags = GetTraceTypeFlags();

    // Will we be required to dump the trace response to a file on disk?
    bool bSaveResponseToFile = GetParentLayerManager()->mCmdFrameCaptureWithSave.IsActive() || mbLinkedTraceForCapture;

    // If the linked trace was requested, return all of the results through a single response.
    bool bLinkedTraceRequested = bSaveResponseToFile || mCmdLinkedTrace.IsActive() || (autotraceFlags == kTraceType_Linked);

    // If we want a linked trace, we'll need responses from all trace types.
    bool bAPITraceResponseNeeded = OnlyAPITraceRequested() || bLinkedTraceRequested || (autotraceFlags & kTraceType_API);
    bool bGPUTraceResponseNeeded = OnlyGPUTraceRequested() || bLinkedTraceRequested || (autotraceFlags & kTraceType_GPU);

    if (bAPITraceResponseNeeded || bGPUTraceResponseNeeded)
    {
        // We're done collecting, so turn off trace collection.
        SetCollectTrace(false);

        // Also disable frame profiling.
        ModernAPIFrameProfilerLayer* frameProfiler = GetParentLayerManager()->GetFrameProfilerLayer();
        frameProfiler->SetProfilingEnabled(false);

        AfterAPITrace();
        AfterGPUTrace();

        std::string apiTraceResponseString, gpuTraceResponseString;

        if (bAPITraceResponseNeeded)
        {
            mbCollectingApiTrace = false;
            apiTraceResponseString.assign(GetAPITraceTXT().c_str());
        }

        if (bGPUTraceResponseNeeded)
        {
            mbGPUTraceAlreadyCollected = false;
            gpuTraceResponseString.assign(GetGPUTraceTXT().c_str());
        }

        gtASCIIString fullResponseString;

        if (bAPITraceResponseNeeded)
        {
            fullResponseString += apiTraceResponseString.c_str();
            fullResponseString += "\n";
        }

        if (bGPUTraceResponseNeeded)
        {
            fullResponseString += gpuTraceResponseString.c_str();
            fullResponseString += "\n";
        }

        // If the autotrace flags are anything besides "None," we'll just store the trace log internally so the client can pick it up later.
        bool bShouldCacheForAutotrace = (autotraceFlags != kTraceType_None);

        if (bShouldCacheForAutotrace)
        {
            // Don't send the response back through a command yet.
            // The client will know to pick it up through a special AutoCapture command.
            mCachedTraceResponse.assign(fullResponseString.asCharArray());
            mbWaitingForAutocaptureClient = true;
        }
        else
        {
            // Send the response string back to the server through a specific request command.
            if (bLinkedTraceRequested)
            {
                HandleLinkedTraceResponse(fullResponseString, bSaveResponseToFile);

                // If tracing was active, disable it after handling the response.
                if (mbLinkedTraceForCapture)
                {
                    DisableLinkedTraceCollection();
                }
            }
            else
            {
                if (bAPITraceResponseNeeded)
                {
                    m_apiTraceTXT.Send(apiTraceResponseString.c_str());
                }
                else if (bGPUTraceResponseNeeded)
                {
                    m_cmdGPUTrace.Send(gpuTraceResponseString.c_str());
                }
            }
        }

    }

    // When AutoCapture is enabled, we need to delay rendering so the user has time to retrieve the cached response.
    if (mbWaitingForAutocaptureClient)
    {
        // For AutoCapture the client will send the separate request below to collect the cached trace response.
        if (mCmdAutoCaptureCachedTrace.IsActive())
        {
            // We're done waiting to send the cached response to the client. Move on with playback.
            mbWaitingForAutocaptureClient = false;

            mCmdAutoCaptureCachedTrace.Send(mCachedTraceResponse.c_str());
            mCachedTraceResponse.clear();
        }
        else
        {
            // Sleep to give the user a chance to connect during playback.
            osSleep(500);
        }
    }
}