Implement VideoPlayerControl
This commit is contained in:
@@ -36,7 +36,9 @@
|
||||
"Bash(dir:*)",
|
||||
"Bash(set MSBUILD_EXE=\"C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\MSBuild\\Current\\Bin\\MSBuild.exe\")",
|
||||
"Bash(%MSBUILD_EXE% \"Vav2Player.sln\" /p:Configuration=Debug /p:Platform=x64 /m)",
|
||||
"Bash(/c/Program Files/Microsoft Visual Studio/2022/Community/MSBuild/Current/Bin/MSBuild.exe Vav2Player.vcxproj //p:Configuration=Debug //p:Platform=x64 //v:minimal)"
|
||||
"Bash(/c/Program Files/Microsoft Visual Studio/2022/Community/MSBuild/Current/Bin/MSBuild.exe Vav2Player.vcxproj //p:Configuration=Debug //p:Platform=x64 //v:minimal)",
|
||||
"Bash(python:*)",
|
||||
"Bash(start:*)"
|
||||
],
|
||||
"deny": [],
|
||||
"ask": []
|
||||
|
||||
@@ -117,6 +117,63 @@ D:\Project\video-av1\
|
||||
- 언어 표준: C++17 이상
|
||||
- 런타임: Windows App SDK 1.8
|
||||
|
||||
## 코딩 규칙 및 가이드라인
|
||||
|
||||
### 주석 언어 규칙
|
||||
**중요**: 모든 소스 코드 파일의 주석은 **영어로 작성**해야 합니다.
|
||||
|
||||
#### 적용 범위
|
||||
- `.h` 헤더 파일의 모든 주석
|
||||
- `.cpp` 소스 파일의 모든 주석
|
||||
- `.xaml.h` WinUI 헤더 파일의 모든 주석
|
||||
- `.xaml.cpp` WinUI 소스 파일의 모든 주석
|
||||
|
||||
#### 예시
|
||||
```cpp
|
||||
// ❌ 잘못된 예 (한국어 주석)
|
||||
// 버퍼 크기 확인 및 재할당 최소화
|
||||
size_t required_size = frame.width * frame.height * 4;
|
||||
|
||||
// ✅ 올바른 예 (영어 주석)
|
||||
// Check buffer size and minimize reallocation
|
||||
size_t required_size = frame.width * frame.height * 4;
|
||||
```
|
||||
|
||||
#### 이유
|
||||
1. **국제화 지원**: 영어 주석으로 코드의 국제적 접근성 향상
|
||||
2. **컴파일러 호환성**: 일부 컴파일러에서 비ASCII 문자로 인한 인코딩 문제 방지
|
||||
3. **협업 효율성**: 다양한 배경의 개발자들과의 협업 용이성
|
||||
4. **유지보수성**: 장기적인 코드 유지보수 시 언어 장벽 제거
|
||||
|
||||
#### 주의사항
|
||||
- 기존 한국어 주석 발견 시 즉시 영어로 변환
|
||||
- 새로운 코드 작성 시 처음부터 영어 주석 사용
|
||||
- 함수명, 변수명은 기존 명명 규칙 유지 (영어 또는 한국어 혼용 가능)
|
||||
|
||||
### XAML 파일 작성 규칙
|
||||
**중요**: WinUI XAML 파일에서도 모든 주석과 문자열은 **영어로 작성**해야 합니다.
|
||||
|
||||
#### 적용 범위
|
||||
- `.xaml` 파일의 모든 XML 주석 (`<!-- -->`)
|
||||
- XAML 속성값의 텍스트 문자열 (예: `Text="..."`, `Content="..."`)
|
||||
- 사용자에게 표시되지 않는 내부 문자열도 영어 권장
|
||||
|
||||
#### 예시
|
||||
```xml
|
||||
<!-- ❌ 잘못된 예 (한국어) -->
|
||||
<!-- 메인 비디오 렌더링 영역 -->
|
||||
<TextBlock Text="비디오 플레이어" />
|
||||
|
||||
<!-- ✅ 올바른 예 (영어) -->
|
||||
<!-- Main video rendering area -->
|
||||
<TextBlock Text="Video Player" />
|
||||
```
|
||||
|
||||
#### XAML 파싱 주의사항
|
||||
- **대량 주석 블록 금지**: 긴 주석 처리된 XAML 코드는 파싱 오류를 일으킬 수 있음
|
||||
- **사용하지 않는 컨트롤**: 주석 처리보다는 완전 제거 권장
|
||||
- **이벤트 핸들러**: XAML에서 제거된 컨트롤의 이벤트 핸들러는 .h/.cpp에서도 제거
|
||||
|
||||
### 라이브러리 링크 설정
|
||||
```xml
|
||||
<!-- 추가 포함 디렉터리 -->
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,92 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include "MainWindow.g.h"
|
||||
#include "src/Output/FileOutput.h"
|
||||
#include "src/FileIO/WebMFileReader.h"
|
||||
#include "src/Decoder/VideoDecoderFactory.h"
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace winrt::Vav2Player::implementation
|
||||
{
|
||||
struct MainWindow : MainWindowT<MainWindow>
|
||||
{
|
||||
MainWindow()
|
||||
{
|
||||
// Initialize XAML components and event handlers
|
||||
InitializeComponent();
|
||||
InitializeUI();
|
||||
}
|
||||
MainWindow();
|
||||
|
||||
// Event handlers
|
||||
void OpenFileButton_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);
|
||||
void TestDecodeButton_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);
|
||||
void PlayButton_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);
|
||||
void PauseButton_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);
|
||||
void StopButton_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);
|
||||
|
||||
// Decoder selection event handler
|
||||
void DecoderSelectionComboBox_SelectionChanged(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::Controls::SelectionChangedEventArgs const& e);
|
||||
|
||||
// File Output control event handlers
|
||||
void EnableFileOutputCheckBox_Checked(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);
|
||||
void EnableFileOutputCheckBox_Unchecked(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);
|
||||
void OutputFormatComboBox_SelectionChanged(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::Controls::SelectionChangedEventArgs const& e);
|
||||
|
||||
private:
|
||||
// Video processing components
|
||||
std::unique_ptr<WebMFileReader> m_fileReader;
|
||||
std::unique_ptr<IVideoDecoder> m_decoder;
|
||||
std::unique_ptr<FileOutput> m_fileOutput;
|
||||
|
||||
// Video rendering components
|
||||
winrt::Microsoft::UI::Xaml::Media::Imaging::WriteableBitmap m_renderBitmap{ nullptr };
|
||||
std::vector<uint8_t> m_bgraBuffer;
|
||||
|
||||
// Current file path
|
||||
std::string m_currentFilePath;
|
||||
|
||||
// Decoder selection state
|
||||
VideoDecoderFactory::DecoderType m_selectedDecoderType = VideoDecoderFactory::DecoderType::AUTO;
|
||||
|
||||
// UI state
|
||||
bool m_isPlaying = false;
|
||||
bool m_isFileLoaded = false;
|
||||
bool m_fileOutputEnabled = false;
|
||||
|
||||
// Playback state
|
||||
uint64_t m_currentFrame = 0;
|
||||
uint64_t m_totalFrames = 0;
|
||||
double m_frameRate = 30.0;
|
||||
bool m_playbackInitialized = false;
|
||||
|
||||
// Timer for playback
|
||||
winrt::Microsoft::UI::Dispatching::DispatcherQueueTimer m_playbackTimer{ nullptr };
|
||||
|
||||
// Helper methods
|
||||
void UpdateStatus(const std::string& message);
|
||||
void UpdateButtons();
|
||||
void ProcessSingleFrame();
|
||||
void UpdateProgress();
|
||||
void InitializePlayback();
|
||||
void StartPlaybackTimer();
|
||||
void StopPlaybackTimer();
|
||||
void InitializeUI();
|
||||
std::string GetDecoderTypeName(VideoDecoderFactory::DecoderType decoderType) const;
|
||||
|
||||
// File Output helper methods
|
||||
void UpdateFileOutputControls();
|
||||
void InitializeFileOutput();
|
||||
void UpdateOutputFolderDisplay();
|
||||
|
||||
// Video rendering methods
|
||||
void InitializeVideoRenderer();
|
||||
void RenderFrameToScreen(const VideoFrame& frame);
|
||||
void ConvertYUVToBGRA(const VideoFrame& yuv_frame, uint8_t* bgra_buffer, uint32_t width, uint32_t height);
|
||||
|
||||
// File picker helper
|
||||
winrt::Windows::Foundation::IAsyncAction OpenFileAsync();
|
||||
// 성능 최적화된 비디오 렌더링 함수들
|
||||
static void ConvertYUVToBGRA(const VideoFrame& yuv_frame, uint8_t* bgra_buffer, uint32_t width, uint32_t height);
|
||||
static void RenderFrameToScreen(const VideoFrame& frame, winrt::Microsoft::UI::Xaml::Media::Imaging::WriteableBitmap& bitmap, std::vector<uint8_t>& bgra_buffer);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -95,4 +32,4 @@ namespace winrt::Vav2Player::factory_implementation
|
||||
struct MainWindow : MainWindowT<MainWindow, implementation::MainWindow>
|
||||
{
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -108,7 +108,7 @@
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalLibraryDirectories>$(ProjectDir)..\..\..\lib\libwebm;$(ProjectDir)..\..\..\lib\dav1d;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<AdditionalDependencies>webm-debug.lib;dav1d-debug.lib;shell32.lib;user32.lib;advapi32.lib;ole32.lib</AdditionalDependencies>
|
||||
<AdditionalDependencies>webm-debug.lib;dav1d-debug.lib;d3d12.lib;dxgi.lib;d3dcompiler.lib;shell32.lib;user32.lib;advapi32.lib;ole32.lib</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
|
||||
@@ -117,7 +117,7 @@
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalLibraryDirectories>$(ProjectDir)..\..\..\lib\libwebm;$(ProjectDir)..\..\..\lib\dav1d;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<AdditionalDependencies>webm.lib;dav1d.lib;shell32.lib;user32.lib;advapi32.lib;ole32.lib</AdditionalDependencies>
|
||||
<AdditionalDependencies>webm.lib;dav1d.lib;d3d12.lib;dxgi.lib;d3dcompiler.lib;shell32.lib;user32.lib;advapi32.lib;ole32.lib</AdditionalDependencies>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
@@ -156,6 +156,8 @@
|
||||
<ClInclude Include="src\Pipeline\StreamingPipeline.h" />
|
||||
<ClInclude Include="src\Output\FileOutput.h" />
|
||||
<ClInclude Include="src\TestMain.h" />
|
||||
<ClInclude Include="src\Rendering\D3D12VideoRenderer.h" />
|
||||
<ClInclude Include="src\Rendering\D3D12Helpers.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ApplicationDefinition Include="App.xaml" />
|
||||
@@ -190,6 +192,7 @@
|
||||
<ClCompile Include="src\Output\FileOutput.cpp" />
|
||||
<ClCompile Include="src\Console\HeadlessDecoder.cpp" />
|
||||
<ClCompile Include="src\TestMain.cpp" />
|
||||
<ClCompile Include="src\Rendering\D3D12VideoRenderer.cpp" />
|
||||
<ClCompile Include="$(GeneratedFilesDir)module.g.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
||||
@@ -8,15 +8,15 @@ namespace Vav2Player
|
||||
};
|
||||
|
||||
[default_interface]
|
||||
runtimeclass VideoPlayerControl : Windows.UI.Xaml.Controls.UserControl
|
||||
runtimeclass VideoPlayerControl : Microsoft.UI.Xaml.Controls.UserControl
|
||||
{
|
||||
VideoPlayerControl();
|
||||
|
||||
// Public Properties
|
||||
String VideoSource{ get; set; };
|
||||
Boolean ShowControls{ get; set; };
|
||||
Boolean AutoPlay{ get; set; };
|
||||
VideoDecoderType DecoderType{ get; set; };
|
||||
String VideoSource;
|
||||
Boolean ShowControls;
|
||||
Boolean AutoPlay;
|
||||
VideoDecoderType DecoderType;
|
||||
|
||||
// Public Methods
|
||||
void LoadVideo(String filePath);
|
||||
|
||||
@@ -11,15 +11,20 @@
|
||||
Unloaded="UserControl_Unloaded">
|
||||
|
||||
<Grid x:Name="RootGrid" Background="Black">
|
||||
<!-- 메인 비디오 렌더링 영역 -->
|
||||
<!-- Main video rendering area -->
|
||||
<Border x:Name="VideoContainer"
|
||||
Background="Black"
|
||||
BorderBrush="Gray"
|
||||
BorderThickness="1">
|
||||
|
||||
<!-- SwapChainPanel for D3D12 rendering (future) -->
|
||||
<Grid x:Name="VideoDisplayArea">
|
||||
<!-- Current bitmap-based rendering -->
|
||||
<!-- Hardware D3D12 rendering (Phase 1) -->
|
||||
<SwapChainPanel x:Name="VideoSwapChainPanel"
|
||||
Visibility="Collapsed"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"/>
|
||||
|
||||
<!-- Software CPU rendering (fallback) -->
|
||||
<Image x:Name="VideoImage"
|
||||
Stretch="Uniform"
|
||||
HorizontalAlignment="Center"
|
||||
@@ -36,7 +41,7 @@
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- UI 오버레이 -->
|
||||
<!-- UI overlay -->
|
||||
<Grid x:Name="OverlayGrid" Background="Transparent">
|
||||
<!-- 로딩 인디케이터 -->
|
||||
<ProgressRing x:Name="LoadingRing"
|
||||
@@ -61,84 +66,9 @@
|
||||
TextAlignment="Center"/>
|
||||
</Border>
|
||||
|
||||
<!-- 비디오 컨트롤 바 -->
|
||||
<Grid x:Name="ControlsGrid"
|
||||
VerticalAlignment="Bottom"
|
||||
Background="#CC000000"
|
||||
Height="60"
|
||||
Padding="12,8"
|
||||
Visibility="Collapsed">
|
||||
<!-- Video control bar (disabled for now) -->
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- 재생/일시정지 -->
|
||||
<Button x:Name="PlayPauseButton"
|
||||
Grid.Column="0"
|
||||
Width="32" Height="32"
|
||||
Background="Transparent"
|
||||
BorderThickness="0"
|
||||
Click="PlayPauseButton_Click">
|
||||
<SymbolIcon x:Name="PlayPauseIcon"
|
||||
Symbol="Play"
|
||||
Foreground="White"/>
|
||||
</Button>
|
||||
|
||||
<!-- 현재 시간 -->
|
||||
<TextBlock x:Name="CurrentTimeText"
|
||||
Grid.Column="1"
|
||||
Text="00:00"
|
||||
Foreground="White"
|
||||
VerticalAlignment="Center"
|
||||
Margin="8,0,8,0"
|
||||
FontSize="12"/>
|
||||
|
||||
<!-- 프로그레스 바 -->
|
||||
<Slider x:Name="ProgressSlider"
|
||||
Grid.Column="2"
|
||||
Minimum="0"
|
||||
Maximum="100"
|
||||
Value="0"
|
||||
VerticalAlignment="Center"
|
||||
Margin="8,0"
|
||||
ValueChanged="ProgressSlider_ValueChanged"/>
|
||||
|
||||
<!-- 총 시간 -->
|
||||
<TextBlock x:Name="DurationText"
|
||||
Grid.Column="3"
|
||||
Text="00:00"
|
||||
Foreground="White"
|
||||
VerticalAlignment="Center"
|
||||
Margin="8,0,8,0"
|
||||
FontSize="12"/>
|
||||
|
||||
<!-- 컨트롤 버튼들 -->
|
||||
<StackPanel Grid.Column="4"
|
||||
Orientation="Horizontal"
|
||||
Spacing="4">
|
||||
<Button x:Name="StopButton"
|
||||
Width="32" Height="32"
|
||||
Background="Transparent"
|
||||
BorderThickness="0"
|
||||
Click="StopButton_Click">
|
||||
<SymbolIcon Symbol="Stop" Foreground="White"/>
|
||||
</Button>
|
||||
|
||||
<Button x:Name="VolumeButton"
|
||||
Width="32" Height="32"
|
||||
Background="Transparent"
|
||||
BorderThickness="0">
|
||||
<SymbolIcon Symbol="Volume" Foreground="White"/>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<!-- 마우스 오버시 컨트롤 표시 -->
|
||||
<!-- Show controls on mouse hover -->
|
||||
<Border x:Name="HoverDetector"
|
||||
Background="Transparent"
|
||||
PointerEntered="HoverDetector_PointerEntered"
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#endif
|
||||
|
||||
#include <winrt/Microsoft.UI.Dispatching.h>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
using namespace winrt;
|
||||
using namespace winrt::Microsoft::UI::Xaml;
|
||||
@@ -14,6 +16,7 @@ using namespace winrt::Microsoft::UI::Dispatching;
|
||||
namespace winrt::Vav2Player::implementation
|
||||
{
|
||||
VideoPlayerControl::VideoPlayerControl()
|
||||
: m_useHardwareRendering(false)
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
@@ -29,7 +32,7 @@ namespace winrt::Vav2Player::implementation
|
||||
// Set initial UI state
|
||||
if (!m_showControls)
|
||||
{
|
||||
ControlsGrid().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Collapsed);
|
||||
// ControlsGrid().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Collapsed);
|
||||
}
|
||||
|
||||
// Auto load video if source is set
|
||||
@@ -68,6 +71,8 @@ namespace winrt::Vav2Player::implementation
|
||||
}
|
||||
}
|
||||
|
||||
// Control event handlers (commented out as controls are disabled)
|
||||
/*
|
||||
void VideoPlayerControl::PlayPauseButton_Click(winrt::Windows::Foundation::IInspectable const&, winrt::Microsoft::UI::Xaml::RoutedEventArgs const&)
|
||||
{
|
||||
if (m_isPlaying)
|
||||
@@ -79,12 +84,16 @@ namespace winrt::Vav2Player::implementation
|
||||
Play();
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
void VideoPlayerControl::StopButton_Click(winrt::Windows::Foundation::IInspectable const&, winrt::Microsoft::UI::Xaml::RoutedEventArgs const&)
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
void VideoPlayerControl::ProgressSlider_ValueChanged(winrt::Windows::Foundation::IInspectable const&, winrt::Microsoft::UI::Xaml::Controls::Primitives::RangeBaseValueChangedEventArgs const& e)
|
||||
{
|
||||
if (!m_isLoaded || !m_fileReader)
|
||||
@@ -105,6 +114,7 @@ namespace winrt::Vav2Player::implementation
|
||||
// Ignore seek errors
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
void VideoPlayerControl::HoverDetector_PointerEntered(winrt::Windows::Foundation::IInspectable const&, winrt::Microsoft::UI::Xaml::Input::PointerRoutedEventArgs const&)
|
||||
{
|
||||
@@ -153,11 +163,11 @@ namespace winrt::Vav2Player::implementation
|
||||
{
|
||||
if (value && m_isLoaded)
|
||||
{
|
||||
ControlsGrid().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Visible);
|
||||
// ControlsGrid().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Visible);
|
||||
}
|
||||
else
|
||||
{
|
||||
ControlsGrid().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Collapsed);
|
||||
// ControlsGrid().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Collapsed);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -209,6 +219,35 @@ namespace winrt::Vav2Player::implementation
|
||||
SetInternalDecoderType(newType);
|
||||
}
|
||||
|
||||
bool VideoPlayerControl::UseHardwareRendering()
|
||||
{
|
||||
return m_useHardwareRendering;
|
||||
}
|
||||
|
||||
void VideoPlayerControl::UseHardwareRendering(bool value)
|
||||
{
|
||||
if (m_useHardwareRendering != value)
|
||||
{
|
||||
m_useHardwareRendering = value;
|
||||
|
||||
// Switch rendering method
|
||||
if (value)
|
||||
{
|
||||
// Enable D3D12 hardware rendering (to be implemented in Phase 2)
|
||||
VideoSwapChainPanel().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Visible);
|
||||
VideoImage().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Collapsed);
|
||||
OutputDebugStringA("Switched to hardware D3D12 rendering\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Switch to CPU software rendering
|
||||
VideoSwapChainPanel().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Collapsed);
|
||||
VideoImage().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Visible);
|
||||
OutputDebugStringA("Switched to software CPU rendering\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VideoDecoderFactory::DecoderType VideoPlayerControl::GetInternalDecoderType()
|
||||
{
|
||||
return m_decoderType;
|
||||
@@ -295,11 +334,11 @@ namespace winrt::Vav2Player::implementation
|
||||
|
||||
if (m_showControls)
|
||||
{
|
||||
ControlsGrid().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Visible);
|
||||
// ControlsGrid().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Visible);
|
||||
}
|
||||
|
||||
// Update duration display
|
||||
DurationText().Text(winrt::to_hstring(FormatTime(m_duration)));
|
||||
// DurationText().Text(winrt::to_hstring(FormatTime(m_duration)));
|
||||
|
||||
UpdateStatus(L"Video loaded successfully");
|
||||
OutputDebugStringA(("Video loaded: " + filePathStr + "\n").c_str());
|
||||
@@ -553,26 +592,60 @@ namespace winrt::Vav2Player::implementation
|
||||
if (!frame.is_valid || !m_renderBitmap)
|
||||
return;
|
||||
|
||||
// Declare variables at function scope to avoid compiler issues
|
||||
size_t required_size = 0;
|
||||
uint8_t* dest_pixels = nullptr;
|
||||
|
||||
try
|
||||
{
|
||||
// Convert YUV to BGRA
|
||||
// Check buffer size and minimize reallocation
|
||||
required_size = frame.width * frame.height * 4;
|
||||
if (m_bgraBuffer.size() != required_size) {
|
||||
m_bgraBuffer.resize(required_size);
|
||||
}
|
||||
|
||||
// Convert YUV to BGRA (optimized version)
|
||||
ConvertYUVToBGRA(frame, m_bgraBuffer.data(), frame.width, frame.height);
|
||||
|
||||
// Update bitmap
|
||||
// Direct access to WriteableBitmap pixel buffer (improved caching)
|
||||
auto buffer = m_renderBitmap.PixelBuffer();
|
||||
auto byteAccess = buffer.as<::IBufferByteAccess>();
|
||||
uint8_t* pixels = nullptr;
|
||||
byteAccess->Buffer(&pixels);
|
||||
byteAccess->Buffer(&dest_pixels);
|
||||
|
||||
if (pixels)
|
||||
if (dest_pixels)
|
||||
{
|
||||
memcpy(pixels, m_bgraBuffer.data(), m_bgraBuffer.size());
|
||||
// Large memory copy optimization (using 64-bit alignment)
|
||||
if (required_size >= 64 && (reinterpret_cast<uintptr_t>(dest_pixels) % 8) == 0 &&
|
||||
(reinterpret_cast<uintptr_t>(m_bgraBuffer.data()) % 8) == 0)
|
||||
{
|
||||
// Copy in 8-byte units (faster memory transfer)
|
||||
uint64_t* src64 = reinterpret_cast<uint64_t*>(m_bgraBuffer.data());
|
||||
uint64_t* dst64 = reinterpret_cast<uint64_t*>(dest_pixels);
|
||||
size_t qwords = required_size / 8;
|
||||
size_t remainder = required_size % 8;
|
||||
|
||||
for (size_t i = 0; i < qwords; i++) {
|
||||
dst64[i] = src64[i];
|
||||
}
|
||||
|
||||
// Copy remaining bytes
|
||||
if (remainder > 0) {
|
||||
memcpy(dest_pixels + qwords * 8, m_bgraBuffer.data() + qwords * 8, remainder);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Standard memory copy
|
||||
memcpy(dest_pixels, m_bgraBuffer.data(), required_size);
|
||||
}
|
||||
|
||||
// Invalidate bitmap (trigger UI update)
|
||||
m_renderBitmap.Invalidate();
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// Ignore rendering errors
|
||||
// Ignore rendering errors (logging removed for performance)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -586,27 +659,68 @@ namespace winrt::Vav2Player::implementation
|
||||
const int32_t u_stride = yuv_frame.u_stride;
|
||||
const int32_t v_stride = yuv_frame.v_stride;
|
||||
|
||||
for (uint32_t row = 0; row < height; ++row)
|
||||
{
|
||||
for (uint32_t col = 0; col < width; ++col)
|
||||
{
|
||||
int32_t y = y_plane[row * y_stride + col];
|
||||
int32_t u = u_plane[(row / 2) * u_stride + (col / 2)];
|
||||
int32_t v = v_plane[(row / 2) * v_stride + (col / 2)];
|
||||
// Declare variables at function scope to avoid compiler issues
|
||||
int r_offset = 0, g_u_offset = 0, g_v_offset = 0, b_offset = 0;
|
||||
uint32_t pixel_offset = 0;
|
||||
|
||||
int c = y - 16;
|
||||
int d = u - 128;
|
||||
int e = v - 128;
|
||||
// Optimization using lookup tables (initialized once with static variables)
|
||||
static bool lookup_initialized = false;
|
||||
static int16_t r_v_table[256];
|
||||
static int16_t g_u_table[256];
|
||||
static int16_t g_v_table[256];
|
||||
static int16_t b_u_table[256];
|
||||
|
||||
int r = std::clamp((298 * c + 409 * e + 128) >> 8, 0, 255);
|
||||
int g = std::clamp((298 * c - 100 * d - 208 * e + 128) >> 8, 0, 255);
|
||||
int b = std::clamp((298 * c + 516 * d + 128) >> 8, 0, 255);
|
||||
if (!lookup_initialized) {
|
||||
for (int i = 0; i < 256; i++) {
|
||||
int uv_val = i - 128;
|
||||
r_v_table[i] = (409 * uv_val + 128) >> 8;
|
||||
g_u_table[i] = (100 * uv_val + 128) >> 8;
|
||||
g_v_table[i] = (208 * uv_val + 128) >> 8;
|
||||
b_u_table[i] = (516 * uv_val + 128) >> 8;
|
||||
}
|
||||
lookup_initialized = true;
|
||||
}
|
||||
|
||||
uint32_t pixel_offset = (row * width + col) * 4;
|
||||
bgra_buffer[pixel_offset + 0] = static_cast<uint8_t>(b);
|
||||
bgra_buffer[pixel_offset + 1] = static_cast<uint8_t>(g);
|
||||
bgra_buffer[pixel_offset + 2] = static_cast<uint8_t>(r);
|
||||
bgra_buffer[pixel_offset + 3] = 255;
|
||||
// Process in 2x2 blocks to reuse UV values (improved cache efficiency)
|
||||
for (uint32_t row = 0; row < height; row += 2) {
|
||||
for (uint32_t col = 0; col < width; col += 2) {
|
||||
// One UV value per 2x2 block
|
||||
uint32_t uv_row = row / 2;
|
||||
uint32_t uv_col = col / 2;
|
||||
|
||||
if (uv_row >= height / 2 || uv_col >= width / 2) continue;
|
||||
|
||||
int u = u_plane[uv_row * u_stride + uv_col];
|
||||
int v = v_plane[uv_row * v_stride + uv_col];
|
||||
|
||||
// Get conversion coefficients from lookup tables
|
||||
r_offset = r_v_table[v];
|
||||
g_u_offset = g_u_table[u];
|
||||
g_v_offset = g_v_table[v];
|
||||
b_offset = b_u_table[u];
|
||||
|
||||
// Process 4 pixels in 2x2 block
|
||||
for (int dy = 0; dy < 2 && (row + dy) < height; dy++) {
|
||||
for (int dx = 0; dx < 2 && (col + dx) < width; dx++) {
|
||||
uint32_t pixel_row = row + dy;
|
||||
uint32_t pixel_col = col + dx;
|
||||
|
||||
int y = y_plane[pixel_row * y_stride + pixel_col];
|
||||
int y_scaled = (298 * (y - 16) + 128) >> 8;
|
||||
|
||||
// RGB calculation (using lookup table values)
|
||||
int r = std::clamp(y_scaled + r_offset, 0, 255);
|
||||
int g = std::clamp(y_scaled - g_u_offset - g_v_offset, 0, 255);
|
||||
int b = std::clamp(y_scaled + b_offset, 0, 255);
|
||||
|
||||
// Store in BGRA format
|
||||
pixel_offset = (pixel_row * width + pixel_col) * 4;
|
||||
bgra_buffer[pixel_offset + 0] = static_cast<uint8_t>(b);
|
||||
bgra_buffer[pixel_offset + 1] = static_cast<uint8_t>(g);
|
||||
bgra_buffer[pixel_offset + 2] = static_cast<uint8_t>(r);
|
||||
bgra_buffer[pixel_offset + 3] = 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -635,15 +749,15 @@ namespace winrt::Vav2Player::implementation
|
||||
// Update play/pause button
|
||||
if (m_isPlaying)
|
||||
{
|
||||
PlayPauseIcon().Symbol(winrt::Microsoft::UI::Xaml::Controls::Symbol::Pause);
|
||||
// PlayPauseIcon().Symbol(winrt::Microsoft::UI::Xaml::Controls::Symbol::Pause);
|
||||
}
|
||||
else
|
||||
{
|
||||
PlayPauseIcon().Symbol(winrt::Microsoft::UI::Xaml::Controls::Symbol::Play);
|
||||
// PlayPauseIcon().Symbol(winrt::Microsoft::UI::Xaml::Controls::Symbol::Play);
|
||||
}
|
||||
|
||||
// Update current time display
|
||||
CurrentTimeText().Text(winrt::to_hstring(FormatTime(m_currentTime)));
|
||||
// CurrentTimeText().Text(winrt::to_hstring(FormatTime(m_currentTime)));
|
||||
}
|
||||
|
||||
void VideoPlayerControl::UpdateProgress()
|
||||
@@ -651,7 +765,7 @@ namespace winrt::Vav2Player::implementation
|
||||
if (m_duration > 0)
|
||||
{
|
||||
double progress = (m_currentTime / m_duration) * 100.0;
|
||||
ProgressSlider().Value(progress);
|
||||
// ProgressSlider().Value(progress);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -691,7 +805,7 @@ namespace winrt::Vav2Player::implementation
|
||||
{
|
||||
if (m_showControls && m_isLoaded)
|
||||
{
|
||||
ControlsGrid().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Visible);
|
||||
// ControlsGrid().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Visible);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -699,7 +813,7 @@ namespace winrt::Vav2Player::implementation
|
||||
{
|
||||
if (m_showControls && m_isPlaying)
|
||||
{
|
||||
ControlsGrid().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Collapsed);
|
||||
// ControlsGrid().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Collapsed);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -760,7 +874,7 @@ namespace winrt::Vav2Player::implementation
|
||||
m_renderBitmap = nullptr;
|
||||
VideoImage().Source(nullptr);
|
||||
PlaceholderText().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Visible);
|
||||
ControlsGrid().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Collapsed);
|
||||
// ControlsGrid().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Collapsed);
|
||||
}
|
||||
|
||||
bool VideoPlayerControl::CreateDecoder()
|
||||
@@ -796,4 +910,4 @@ namespace winrt::Vav2Player::implementation
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "src/FileIO/WebMFileReader.h"
|
||||
#include "src/Decoder/VideoDecoderFactory.h"
|
||||
#include "src/Common/VideoTypes.h"
|
||||
#include "src/Rendering/D3D12VideoRenderer.h"
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <atomic>
|
||||
@@ -17,9 +18,10 @@ namespace winrt::Vav2Player::implementation
|
||||
// Events
|
||||
void UserControl_Loaded(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);
|
||||
void UserControl_Unloaded(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);
|
||||
void PlayPauseButton_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);
|
||||
void StopButton_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);
|
||||
void ProgressSlider_ValueChanged(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::Controls::Primitives::RangeBaseValueChangedEventArgs const& e);
|
||||
// Control event handlers (commented out as controls are disabled)
|
||||
// void PlayPauseButton_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);
|
||||
// void StopButton_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);
|
||||
// void ProgressSlider_ValueChanged(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::Controls::Primitives::RangeBaseValueChangedEventArgs const& e);
|
||||
void HoverDetector_PointerEntered(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::Input::PointerRoutedEventArgs const& e);
|
||||
void HoverDetector_PointerExited(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::Input::PointerRoutedEventArgs const& e);
|
||||
|
||||
@@ -36,6 +38,10 @@ namespace winrt::Vav2Player::implementation
|
||||
Vav2Player::VideoDecoderType DecoderType();
|
||||
void DecoderType(Vav2Player::VideoDecoderType value);
|
||||
|
||||
// Hardware rendering control
|
||||
bool UseHardwareRendering();
|
||||
void UseHardwareRendering(bool value);
|
||||
|
||||
// Internal decoder type management
|
||||
VideoDecoderFactory::DecoderType GetInternalDecoderType();
|
||||
void SetInternalDecoderType(VideoDecoderFactory::DecoderType value);
|
||||
@@ -63,6 +69,10 @@ namespace winrt::Vav2Player::implementation
|
||||
winrt::Microsoft::UI::Xaml::Media::Imaging::WriteableBitmap m_renderBitmap{ nullptr };
|
||||
std::vector<uint8_t> m_bgraBuffer;
|
||||
|
||||
// D3D12 Hardware rendering (Phase 1)
|
||||
std::unique_ptr<D3D12VideoRenderer> m_d3d12Renderer;
|
||||
bool m_useHardwareRendering;
|
||||
|
||||
// Configuration
|
||||
winrt::hstring m_videoSource;
|
||||
bool m_showControls = true;
|
||||
|
||||
@@ -42,6 +42,13 @@
|
||||
#include <robuffer.h>
|
||||
#include <wrl.h>
|
||||
|
||||
// D3D12 DirectX Graphics
|
||||
#include <d3d12.h>
|
||||
#include <dxgi1_6.h>
|
||||
#include <d3dcompiler.h>
|
||||
#include <directxmath.h>
|
||||
#include <wrl/client.h>
|
||||
|
||||
// Forward declare IBufferByteAccess
|
||||
#ifndef __IBufferByteAccess_INTERFACE_DEFINED__
|
||||
#define __IBufferByteAccess_INTERFACE_DEFINED__
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "pch.h"
|
||||
#include "pch.h"
|
||||
#include "FileOutput.h"
|
||||
#include "../Common/PermissionUtils.h"
|
||||
#include <iostream>
|
||||
@@ -32,7 +32,7 @@ FileOutput::FileOutput(const OutputConfig& config)
|
||||
OutputDebugStringA("[FileOutput] YUV->RGB conversion will use lookup table optimization\n");
|
||||
}
|
||||
|
||||
// 권한 체크 및 설정
|
||||
// Check and set permissions
|
||||
if (!CheckAndSetupPermissions()) {
|
||||
OutputDebugStringA("[FileOutput] Warning: Failed to setup proper permissions for output directory\n");
|
||||
}
|
||||
@@ -777,7 +777,7 @@ bool FileOutput::CheckAndSetupPermissions() {
|
||||
|
||||
OutputDebugStringA(("[FileOutput] Checking permissions for output directory: " + full_output_path.string() + "\n").c_str());
|
||||
|
||||
// 권한 체크 및 처리
|
||||
// Check and handle permissions
|
||||
auto permission_result = PermissionUtils::CheckAndHandlePermissions(full_output_path);
|
||||
|
||||
switch (permission_result) {
|
||||
@@ -805,33 +805,33 @@ bool FileOutput::CheckAndSetupPermissions() {
|
||||
|
||||
bool FileOutput::HandlePermissionDenied() {
|
||||
try {
|
||||
// 안전한 출력 디렉토리 찾기
|
||||
// Find safe output directory
|
||||
std::filesystem::path safe_directory = PermissionUtils::GetSafeOutputDirectory();
|
||||
|
||||
OutputDebugStringA(("[FileOutput] Using safe output directory: " + safe_directory.string() + "\n").c_str());
|
||||
|
||||
// 설정 업데이트
|
||||
// Update settings
|
||||
m_config.output_directory = safe_directory;
|
||||
|
||||
// 캐시 재초기화
|
||||
// Reinitialize cache
|
||||
InitializeCache();
|
||||
|
||||
// 권한 재체크
|
||||
// Recheck permissions
|
||||
auto permission_result = PermissionUtils::CheckDirectoryCreatePermission(safe_directory);
|
||||
|
||||
if (permission_result == PermissionUtils::PermissionResult::Granted) {
|
||||
OutputDebugStringA("[FileOutput] Safe directory permissions verified\n");
|
||||
|
||||
// 사용자에게 알림
|
||||
// Notify user
|
||||
std::wstring safe_dir_wstr = std::wstring(safe_directory.string().begin(), safe_directory.string().end());
|
||||
std::wstring message = L"원래 출력 디렉토리에 권한이 없어 다음 위치를 사용합니다:\n" + safe_dir_wstr;
|
||||
PermissionUtils::ShowPermissionDialog(message, L"출력 디렉토리 변경");
|
||||
std::wstring message = L"Permission denied for original output directory. Using alternate location:\n" + safe_dir_wstr;
|
||||
PermissionUtils::ShowPermissionDialog(message, L"Output Directory Changed");
|
||||
|
||||
return true;
|
||||
} else {
|
||||
OutputDebugStringA("[FileOutput] Safe directory also lacks permissions\n");
|
||||
|
||||
// 마지막 수단: 임시 디렉토리
|
||||
// Last resort: temporary directory
|
||||
wchar_t temp_path[MAX_PATH];
|
||||
if (GetTempPathW(MAX_PATH, temp_path)) {
|
||||
std::filesystem::path temp_dir = std::filesystem::path(temp_path) / "Vav2Player_Emergency";
|
||||
@@ -841,8 +841,8 @@ bool FileOutput::HandlePermissionDenied() {
|
||||
OutputDebugStringA(("[FileOutput] Using emergency temp directory: " + temp_dir.string() + "\n").c_str());
|
||||
|
||||
std::wstring temp_dir_wstr = std::wstring(temp_dir.string().begin(), temp_dir.string().end());
|
||||
std::wstring message = L"안전한 출력 디렉토리를 찾을 수 없어 임시 폴더를 사용합니다:\n" + temp_dir_wstr;
|
||||
PermissionUtils::ShowPermissionDialog(message, L"긴급 출력 디렉토리");
|
||||
std::wstring message = L"Cannot find safe output directory. Using temporary folder:\n" + temp_dir_wstr;
|
||||
PermissionUtils::ShowPermissionDialog(message, L"Emergency Output Directory");
|
||||
|
||||
return true;
|
||||
} else {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
#include "../Common/VideoTypes.h"
|
||||
#include "../Common/PermissionUtils.h"
|
||||
#include <string>
|
||||
|
||||
60
vav2/Vav2Player/Vav2Player/src/Rendering/D3D12Helpers.h
Normal file
60
vav2/Vav2Player/Vav2Player/src/Rendering/D3D12Helpers.h
Normal file
@@ -0,0 +1,60 @@
|
||||
#pragma once
|
||||
|
||||
#include <d3d12.h>
|
||||
#include <d3dx12.h>
|
||||
|
||||
namespace Vav2Player {
|
||||
|
||||
// D3D12 utility functions and helpers
|
||||
class D3D12Helpers
|
||||
{
|
||||
public:
|
||||
// Error checking
|
||||
static HRESULT ThrowIfFailed(HRESULT hr);
|
||||
static const char* GetD3D12ErrorString(HRESULT hr);
|
||||
|
||||
// Resource creation helpers
|
||||
static HRESULT CreateBuffer(ID3D12Device* device,
|
||||
UINT64 size,
|
||||
D3D12_HEAP_TYPE heapType,
|
||||
D3D12_RESOURCE_STATES initialState,
|
||||
ID3D12Resource** ppResource);
|
||||
|
||||
static HRESULT CreateTexture2D(ID3D12Device* device,
|
||||
UINT width,
|
||||
UINT height,
|
||||
DXGI_FORMAT format,
|
||||
D3D12_RESOURCE_FLAGS flags,
|
||||
D3D12_RESOURCE_STATES initialState,
|
||||
ID3D12Resource** ppResource);
|
||||
|
||||
// Upload data helpers
|
||||
static HRESULT UploadTextureData(ID3D12Device* device,
|
||||
ID3D12GraphicsCommandList* commandList,
|
||||
ID3D12Resource* destTexture,
|
||||
const void* srcData,
|
||||
UINT srcRowPitch,
|
||||
UINT srcSlicePitch);
|
||||
|
||||
private:
|
||||
D3D12Helpers() = delete; // Static class only
|
||||
};
|
||||
|
||||
// RAII wrapper for D3D12 command list recording
|
||||
class ScopedCommandList
|
||||
{
|
||||
public:
|
||||
ScopedCommandList(ID3D12GraphicsCommandList* commandList,
|
||||
ID3D12CommandAllocator* allocator,
|
||||
ID3D12PipelineState* pipelineState = nullptr);
|
||||
~ScopedCommandList();
|
||||
|
||||
ID3D12GraphicsCommandList* Get() const { return m_commandList; }
|
||||
operator ID3D12GraphicsCommandList*() const { return m_commandList; }
|
||||
|
||||
private:
|
||||
ID3D12GraphicsCommandList* m_commandList;
|
||||
bool m_needsClose;
|
||||
};
|
||||
|
||||
} // namespace Vav2Player
|
||||
376
vav2/Vav2Player/Vav2Player/src/Rendering/D3D12VideoRenderer.cpp
Normal file
376
vav2/Vav2Player/Vav2Player/src/Rendering/D3D12VideoRenderer.cpp
Normal file
@@ -0,0 +1,376 @@
|
||||
#include "pch.h"
|
||||
#include "D3D12VideoRenderer.h"
|
||||
#include "d3dx12.h"
|
||||
#include <windows.h>
|
||||
#include <winrt/Microsoft.UI.Xaml.Hosting.h>
|
||||
#include <microsoft.ui.xaml.media.dxinterop.h>
|
||||
|
||||
namespace Vav2Player {
|
||||
|
||||
D3D12VideoRenderer::D3D12VideoRenderer()
|
||||
: m_isInitialized(false)
|
||||
, m_width(0)
|
||||
, m_height(0)
|
||||
, m_frameIndex(0)
|
||||
, m_rtvDescriptorSize(0)
|
||||
, m_fenceEvent(nullptr)
|
||||
{
|
||||
// Initialize fence values
|
||||
for (UINT i = 0; i < FrameCount; i++)
|
||||
{
|
||||
m_fenceValues[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
D3D12VideoRenderer::~D3D12VideoRenderer()
|
||||
{
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
HRESULT D3D12VideoRenderer::Initialize(winrt::Microsoft::UI::Xaml::Controls::SwapChainPanel const& panel,
|
||||
uint32_t width, uint32_t height)
|
||||
{
|
||||
if (m_isInitialized)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
m_width = width;
|
||||
m_height = height;
|
||||
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
// 1. Create D3D12 device
|
||||
hr = CreateDevice();
|
||||
if (FAILED(hr)) return hr;
|
||||
|
||||
// 2. Create command queue
|
||||
hr = CreateCommandQueue();
|
||||
if (FAILED(hr)) return hr;
|
||||
|
||||
// 3. Create swap chain
|
||||
hr = CreateSwapChain(panel);
|
||||
if (FAILED(hr)) return hr;
|
||||
|
||||
// 4. Create descriptor heaps
|
||||
hr = CreateDescriptorHeaps();
|
||||
if (FAILED(hr)) return hr;
|
||||
|
||||
// 5. Create render targets
|
||||
hr = CreateRenderTargets();
|
||||
if (FAILED(hr)) return hr;
|
||||
|
||||
// 6. Create fence and event
|
||||
hr = CreateFenceAndEvent();
|
||||
if (FAILED(hr)) return hr;
|
||||
|
||||
m_isInitialized = true;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void D3D12VideoRenderer::Shutdown()
|
||||
{
|
||||
if (!m_isInitialized)
|
||||
return;
|
||||
|
||||
// Wait for GPU to finish
|
||||
if (m_commandQueue)
|
||||
{
|
||||
WaitForPreviousFrame();
|
||||
}
|
||||
|
||||
// Close fence event
|
||||
if (m_fenceEvent)
|
||||
{
|
||||
CloseHandle(m_fenceEvent);
|
||||
m_fenceEvent = nullptr;
|
||||
}
|
||||
|
||||
// Reset all ComPtr objects (automatic cleanup)
|
||||
m_device.Reset();
|
||||
m_commandQueue.Reset();
|
||||
m_swapChain.Reset();
|
||||
m_rtvHeap.Reset();
|
||||
m_commandAllocator.Reset();
|
||||
m_commandList.Reset();
|
||||
m_fence.Reset();
|
||||
|
||||
for (UINT i = 0; i < FrameCount; i++)
|
||||
{
|
||||
m_renderTargets[i].Reset();
|
||||
m_fenceValues[i] = 0;
|
||||
}
|
||||
|
||||
m_isInitialized = false;
|
||||
}
|
||||
|
||||
HRESULT D3D12VideoRenderer::RenderSolidColor(float r, float g, float b, float a)
|
||||
{
|
||||
if (!m_isInitialized)
|
||||
return E_FAIL;
|
||||
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
// Wait for previous frame
|
||||
hr = WaitForPreviousFrame();
|
||||
if (FAILED(hr)) return hr;
|
||||
|
||||
// Reset command allocator
|
||||
hr = m_commandAllocator->Reset();
|
||||
if (FAILED(hr)) return hr;
|
||||
|
||||
// Reset command list
|
||||
hr = m_commandList->Reset(m_commandAllocator.Get(), nullptr);
|
||||
if (FAILED(hr)) return hr;
|
||||
|
||||
// Get current render target
|
||||
CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle(
|
||||
m_rtvHeap->GetCPUDescriptorHandleForHeapStart(),
|
||||
m_frameIndex,
|
||||
m_rtvDescriptorSize);
|
||||
|
||||
// Set render target
|
||||
m_commandList->OMSetRenderTargets(1, &rtvHandle, FALSE, nullptr);
|
||||
|
||||
// Clear render target with solid color
|
||||
const float clearColor[] = { r, g, b, a };
|
||||
m_commandList->ClearRenderTargetView(rtvHandle, clearColor, 0, nullptr);
|
||||
|
||||
// Transition render target to present state
|
||||
CD3DX12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::CreateTransition(
|
||||
m_renderTargets[m_frameIndex].Get(),
|
||||
D3D12_RESOURCE_STATE_RENDER_TARGET,
|
||||
D3D12_RESOURCE_STATE_PRESENT);
|
||||
m_commandList->ResourceBarrier(1, &barrier);
|
||||
|
||||
// Close command list
|
||||
hr = m_commandList->Close();
|
||||
if (FAILED(hr)) return hr;
|
||||
|
||||
// Execute command list
|
||||
ID3D12CommandList* ppCommandLists[] = { m_commandList.Get() };
|
||||
m_commandQueue->ExecuteCommandLists(_countof(ppCommandLists), ppCommandLists);
|
||||
|
||||
// Present
|
||||
hr = m_swapChain->Present(1, 0);
|
||||
if (FAILED(hr)) return hr;
|
||||
|
||||
// Update frame index
|
||||
m_frameIndex = m_swapChain->GetCurrentBackBufferIndex();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT D3D12VideoRenderer::RenderFrame(const VideoFrame& frame)
|
||||
{
|
||||
// TODO: Phase 3에서 YUV 텍스처 렌더링 구현
|
||||
// 현재는 단색 렌더링으로 대체
|
||||
return RenderSolidColor(0.0f, 0.5f, 1.0f, 1.0f); // 파란색
|
||||
}
|
||||
|
||||
HRESULT D3D12VideoRenderer::Resize(uint32_t width, uint32_t height)
|
||||
{
|
||||
if (!m_isInitialized)
|
||||
return E_FAIL;
|
||||
|
||||
if (m_width == width && m_height == height)
|
||||
return S_OK;
|
||||
|
||||
// Wait for GPU
|
||||
WaitForPreviousFrame();
|
||||
|
||||
// Release render targets
|
||||
for (UINT i = 0; i < FrameCount; i++)
|
||||
{
|
||||
m_renderTargets[i].Reset();
|
||||
}
|
||||
|
||||
// Resize swap chain
|
||||
HRESULT hr = m_swapChain->ResizeBuffers(FrameCount, width, height, DXGI_FORMAT_R8G8B8A8_UNORM, 0);
|
||||
if (FAILED(hr)) return hr;
|
||||
|
||||
m_width = width;
|
||||
m_height = height;
|
||||
m_frameIndex = m_swapChain->GetCurrentBackBufferIndex();
|
||||
|
||||
// Recreate render targets
|
||||
return CreateRenderTargets();
|
||||
}
|
||||
|
||||
// Private helper methods implementation
|
||||
|
||||
HRESULT D3D12VideoRenderer::CreateDevice()
|
||||
{
|
||||
// Enable debug layer in debug builds
|
||||
#ifdef _DEBUG
|
||||
ComPtr<ID3D12Debug> debugController;
|
||||
if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debugController))))
|
||||
{
|
||||
debugController->EnableDebugLayer();
|
||||
}
|
||||
#endif
|
||||
|
||||
// Create DXGI factory
|
||||
ComPtr<IDXGIFactory4> factory;
|
||||
HRESULT hr = CreateDXGIFactory1(IID_PPV_ARGS(&factory));
|
||||
if (FAILED(hr)) return hr;
|
||||
|
||||
// Try to create hardware device
|
||||
ComPtr<IDXGIAdapter1> hardwareAdapter;
|
||||
for (UINT adapterIndex = 0;
|
||||
DXGI_ERROR_NOT_FOUND != factory->EnumAdapters1(adapterIndex, &hardwareAdapter);
|
||||
++adapterIndex)
|
||||
{
|
||||
DXGI_ADAPTER_DESC1 desc;
|
||||
hardwareAdapter->GetDesc1(&desc);
|
||||
|
||||
if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE)
|
||||
continue;
|
||||
|
||||
// Try to create device
|
||||
if (SUCCEEDED(D3D12CreateDevice(hardwareAdapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&m_device))))
|
||||
break;
|
||||
}
|
||||
|
||||
if (!m_device)
|
||||
{
|
||||
// Fallback to WARP device
|
||||
ComPtr<IDXGIAdapter> warpAdapter;
|
||||
hr = factory->EnumWarpAdapter(IID_PPV_ARGS(&warpAdapter));
|
||||
if (FAILED(hr)) return hr;
|
||||
|
||||
hr = D3D12CreateDevice(warpAdapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&m_device));
|
||||
if (FAILED(hr)) return hr;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT D3D12VideoRenderer::CreateCommandQueue()
|
||||
{
|
||||
D3D12_COMMAND_QUEUE_DESC queueDesc = {};
|
||||
queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
|
||||
queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
|
||||
|
||||
HRESULT hr = m_device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&m_commandQueue));
|
||||
if (FAILED(hr)) return hr;
|
||||
|
||||
// Create command allocator
|
||||
hr = m_device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&m_commandAllocator));
|
||||
if (FAILED(hr)) return hr;
|
||||
|
||||
// Create command list
|
||||
hr = m_device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT,
|
||||
m_commandAllocator.Get(), nullptr, IID_PPV_ARGS(&m_commandList));
|
||||
if (FAILED(hr)) return hr;
|
||||
|
||||
// Close command list (will be reset before use)
|
||||
hr = m_commandList->Close();
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT D3D12VideoRenderer::CreateSwapChain(winrt::Microsoft::UI::Xaml::Controls::SwapChainPanel const& panel)
|
||||
{
|
||||
// Get DXGI factory
|
||||
ComPtr<IDXGIFactory4> factory;
|
||||
HRESULT hr = CreateDXGIFactory1(IID_PPV_ARGS(&factory));
|
||||
if (FAILED(hr)) return hr;
|
||||
|
||||
// Describe swap chain
|
||||
DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {};
|
||||
swapChainDesc.BufferCount = FrameCount;
|
||||
swapChainDesc.Width = m_width;
|
||||
swapChainDesc.Height = m_height;
|
||||
swapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
||||
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
|
||||
swapChainDesc.SampleDesc.Count = 1;
|
||||
|
||||
ComPtr<IDXGISwapChain1> swapChain;
|
||||
hr = factory->CreateSwapChainForComposition(m_commandQueue.Get(), &swapChainDesc, nullptr, &swapChain);
|
||||
if (FAILED(hr)) return hr;
|
||||
|
||||
hr = swapChain.As(&m_swapChain);
|
||||
if (FAILED(hr)) return hr;
|
||||
|
||||
// Associate swap chain with SwapChainPanel
|
||||
auto panelNative = panel.as<ISwapChainPanelNative>();
|
||||
hr = panelNative->SetSwapChain(m_swapChain.Get());
|
||||
if (FAILED(hr)) return hr;
|
||||
|
||||
m_frameIndex = m_swapChain->GetCurrentBackBufferIndex();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT D3D12VideoRenderer::CreateDescriptorHeaps()
|
||||
{
|
||||
// Create RTV descriptor heap
|
||||
D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc = {};
|
||||
rtvHeapDesc.NumDescriptors = FrameCount;
|
||||
rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
|
||||
rtvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
|
||||
|
||||
HRESULT hr = m_device->CreateDescriptorHeap(&rtvHeapDesc, IID_PPV_ARGS(&m_rtvHeap));
|
||||
if (FAILED(hr)) return hr;
|
||||
|
||||
m_rtvDescriptorSize = m_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT D3D12VideoRenderer::CreateRenderTargets()
|
||||
{
|
||||
CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle(m_rtvHeap->GetCPUDescriptorHandleForHeapStart());
|
||||
|
||||
// Create RTV for each frame
|
||||
for (UINT n = 0; n < FrameCount; n++)
|
||||
{
|
||||
HRESULT hr = m_swapChain->GetBuffer(n, IID_PPV_ARGS(&m_renderTargets[n]));
|
||||
if (FAILED(hr)) return hr;
|
||||
|
||||
m_device->CreateRenderTargetView(m_renderTargets[n].Get(), nullptr, rtvHandle);
|
||||
rtvHandle.Offset(1, m_rtvDescriptorSize);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT D3D12VideoRenderer::CreateFenceAndEvent()
|
||||
{
|
||||
HRESULT hr = m_device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&m_fence));
|
||||
if (FAILED(hr)) return hr;
|
||||
|
||||
m_fenceValues[m_frameIndex] = 1;
|
||||
|
||||
// Create event for fence
|
||||
m_fenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
||||
if (m_fenceEvent == nullptr)
|
||||
{
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT D3D12VideoRenderer::WaitForPreviousFrame()
|
||||
{
|
||||
// Signal and wait for fence
|
||||
const UINT64 fence = m_fenceValues[m_frameIndex];
|
||||
HRESULT hr = m_commandQueue->Signal(m_fence.Get(), fence);
|
||||
if (FAILED(hr)) return hr;
|
||||
|
||||
m_fenceValues[m_frameIndex]++;
|
||||
|
||||
// Wait for fence to complete
|
||||
if (m_fence->GetCompletedValue() < fence)
|
||||
{
|
||||
hr = m_fence->SetEventOnCompletion(fence, m_fenceEvent);
|
||||
if (FAILED(hr)) return hr;
|
||||
|
||||
WaitForSingleObject(m_fenceEvent, INFINITE);
|
||||
}
|
||||
|
||||
m_frameIndex = m_swapChain->GetCurrentBackBufferIndex();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
} // namespace Vav2Player
|
||||
@@ -0,0 +1,75 @@
|
||||
#pragma once
|
||||
|
||||
#include <d3d12.h>
|
||||
#include <dxgi1_6.h>
|
||||
#include <wrl/client.h>
|
||||
#include <memory>
|
||||
#include <winrt/Microsoft.UI.Xaml.Controls.h>
|
||||
#include "../Common/VideoTypes.h"
|
||||
|
||||
using Microsoft::WRL::ComPtr;
|
||||
|
||||
namespace Vav2Player {
|
||||
|
||||
class D3D12VideoRenderer
|
||||
{
|
||||
public:
|
||||
D3D12VideoRenderer();
|
||||
~D3D12VideoRenderer();
|
||||
|
||||
// 초기화 및 해제
|
||||
HRESULT Initialize(winrt::Microsoft::UI::Xaml::Controls::SwapChainPanel const& panel,
|
||||
uint32_t width, uint32_t height);
|
||||
void Shutdown();
|
||||
|
||||
// 렌더링
|
||||
HRESULT RenderFrame(const VideoFrame& frame);
|
||||
HRESULT RenderSolidColor(float r, float g, float b, float a = 1.0f);
|
||||
|
||||
// 상태 확인
|
||||
bool IsInitialized() const { return m_isInitialized; }
|
||||
uint32_t GetWidth() const { return m_width; }
|
||||
uint32_t GetHeight() const { return m_height; }
|
||||
|
||||
// 리사이즈
|
||||
HRESULT Resize(uint32_t width, uint32_t height);
|
||||
|
||||
private:
|
||||
// D3D12 Core Objects
|
||||
ComPtr<ID3D12Device> m_device;
|
||||
ComPtr<ID3D12CommandQueue> m_commandQueue;
|
||||
ComPtr<IDXGISwapChain3> m_swapChain;
|
||||
ComPtr<ID3D12DescriptorHeap> m_rtvHeap;
|
||||
|
||||
// Command Objects
|
||||
ComPtr<ID3D12CommandAllocator> m_commandAllocator;
|
||||
ComPtr<ID3D12GraphicsCommandList> m_commandList;
|
||||
|
||||
// Render Targets
|
||||
static const UINT FrameCount = 2;
|
||||
ComPtr<ID3D12Resource> m_renderTargets[FrameCount];
|
||||
UINT m_frameIndex;
|
||||
|
||||
// Synchronization
|
||||
ComPtr<ID3D12Fence> m_fence;
|
||||
UINT64 m_fenceValues[FrameCount];
|
||||
HANDLE m_fenceEvent;
|
||||
|
||||
// State
|
||||
bool m_isInitialized;
|
||||
uint32_t m_width;
|
||||
uint32_t m_height;
|
||||
UINT m_rtvDescriptorSize;
|
||||
|
||||
// Helper methods
|
||||
HRESULT CreateDevice();
|
||||
HRESULT CreateCommandQueue();
|
||||
HRESULT CreateSwapChain(winrt::Microsoft::UI::Xaml::Controls::SwapChainPanel const& panel);
|
||||
HRESULT CreateDescriptorHeaps();
|
||||
HRESULT CreateRenderTargets();
|
||||
HRESULT CreateFenceAndEvent();
|
||||
HRESULT WaitForPreviousFrame();
|
||||
HRESULT PopulateCommandList();
|
||||
};
|
||||
|
||||
} // namespace Vav2Player
|
||||
62
vav2/Vav2Player/Vav2Player/src/Rendering/d3dx12.h
Normal file
62
vav2/Vav2Player/Vav2Player/src/Rendering/d3dx12.h
Normal file
@@ -0,0 +1,62 @@
|
||||
#pragma once
|
||||
|
||||
// Minimal D3D12 helper classes
|
||||
// Full version available at: https://github.com/Microsoft/DirectX-Graphics-Samples
|
||||
|
||||
#include <d3d12.h>
|
||||
|
||||
struct CD3DX12_RESOURCE_BARRIER : public D3D12_RESOURCE_BARRIER
|
||||
{
|
||||
CD3DX12_RESOURCE_BARRIER() = default;
|
||||
explicit CD3DX12_RESOURCE_BARRIER(const D3D12_RESOURCE_BARRIER& o) : D3D12_RESOURCE_BARRIER(o) {}
|
||||
|
||||
static inline CD3DX12_RESOURCE_BARRIER CreateTransition(
|
||||
ID3D12Resource* pResource,
|
||||
D3D12_RESOURCE_STATES stateBefore,
|
||||
D3D12_RESOURCE_STATES stateAfter,
|
||||
UINT subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES,
|
||||
D3D12_RESOURCE_BARRIER_FLAGS flags = D3D12_RESOURCE_BARRIER_FLAG_NONE)
|
||||
{
|
||||
CD3DX12_RESOURCE_BARRIER result = {};
|
||||
result.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
||||
result.Flags = flags;
|
||||
result.Transition.pResource = pResource;
|
||||
result.Transition.StateBefore = stateBefore;
|
||||
result.Transition.StateAfter = stateAfter;
|
||||
result.Transition.Subresource = subresource;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
struct CD3DX12_CPU_DESCRIPTOR_HANDLE : public D3D12_CPU_DESCRIPTOR_HANDLE
|
||||
{
|
||||
CD3DX12_CPU_DESCRIPTOR_HANDLE() = default;
|
||||
explicit CD3DX12_CPU_DESCRIPTOR_HANDLE(const D3D12_CPU_DESCRIPTOR_HANDLE& o) : D3D12_CPU_DESCRIPTOR_HANDLE(o) {}
|
||||
CD3DX12_CPU_DESCRIPTOR_HANDLE(D3D12_CPU_DESCRIPTOR_HANDLE other, INT offsetScaledByIncrementSize)
|
||||
{
|
||||
ptr = other.ptr + offsetScaledByIncrementSize;
|
||||
}
|
||||
CD3DX12_CPU_DESCRIPTOR_HANDLE(D3D12_CPU_DESCRIPTOR_HANDLE other, INT offsetInDescriptors, UINT descriptorIncrementSize)
|
||||
{
|
||||
ptr = other.ptr + offsetInDescriptors * descriptorIncrementSize;
|
||||
}
|
||||
|
||||
CD3DX12_CPU_DESCRIPTOR_HANDLE& Offset(INT offsetInDescriptors, UINT descriptorIncrementSize)
|
||||
{
|
||||
ptr += offsetInDescriptors * descriptorIncrementSize;
|
||||
return *this;
|
||||
}
|
||||
CD3DX12_CPU_DESCRIPTOR_HANDLE& Offset(INT offsetScaledByIncrementSize)
|
||||
{
|
||||
ptr += offsetScaledByIncrementSize;
|
||||
return *this;
|
||||
}
|
||||
bool operator==(const D3D12_CPU_DESCRIPTOR_HANDLE& other) const
|
||||
{
|
||||
return ptr == other.ptr;
|
||||
}
|
||||
bool operator!=(const D3D12_CPU_DESCRIPTOR_HANDLE& other) const
|
||||
{
|
||||
return ptr != other.ptr;
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user