private void FreePages(ulong Address, ulong PagesCount) { ulong EndAddr = Address + PagesCount * KMemoryManager.PageSize; int BlockIndex = BlockOrdersCount - 1; ulong AddressRounded = 0; ulong EndAddrTruncated = 0; for (; BlockIndex >= 0; BlockIndex--) { KMemoryRegionBlock AllocInfo = Blocks[BlockIndex]; int BlockSize = 1 << AllocInfo.Order; AddressRounded = BitUtils.AlignUp(Address, BlockSize); EndAddrTruncated = BitUtils.AlignDown(EndAddr, BlockSize); if (AddressRounded < EndAddrTruncated) { break; } } void FreeRegion(ulong CurrAddress) { for (int CurrBlockIndex = BlockIndex; CurrBlockIndex < BlockOrdersCount && CurrAddress != 0; CurrBlockIndex++) { KMemoryRegionBlock Block = Blocks[CurrBlockIndex]; Block.FreeCount++; ulong FreedBlocks = (CurrAddress - Block.StartAligned) >> Block.Order; int Index = (int)FreedBlocks; for (int Level = Block.MaxLevel - 1; Level >= 0; Level--, Index /= 64) { long Mask = Block.Masks[Level][Index / 64]; Block.Masks[Level][Index / 64] = Mask | (1L << (Index & 63)); if (Mask != 0) { break; } } int BlockSizeDelta = 1 << (Block.NextOrder - Block.Order); int FreedBlocksTruncated = BitUtils.AlignDown((int)FreedBlocks, BlockSizeDelta); if (!Block.TryCoalesce(FreedBlocksTruncated, BlockSizeDelta)) { break; } CurrAddress = Block.StartAligned + ((ulong)FreedBlocksTruncated << Block.Order); } } //Free inside aligned region. ulong BaseAddress = AddressRounded; while (BaseAddress < EndAddrTruncated) { ulong BlockSize = 1UL << Blocks[BlockIndex].Order; FreeRegion(BaseAddress); BaseAddress += BlockSize; } int NextBlockIndex = BlockIndex - 1; //Free region between Address and aligned region start. BaseAddress = AddressRounded; for (BlockIndex = NextBlockIndex; BlockIndex >= 0; BlockIndex--) { ulong BlockSize = 1UL << Blocks[BlockIndex].Order; while (BaseAddress - BlockSize >= Address) { BaseAddress -= BlockSize; FreeRegion(BaseAddress); } } //Free region between aligned region end and End Address. BaseAddress = EndAddrTruncated; for (BlockIndex = NextBlockIndex; BlockIndex >= 0; BlockIndex--) { ulong BlockSize = 1UL << Blocks[BlockIndex].Order; while (BaseAddress + BlockSize <= EndAddr) { FreeRegion(BaseAddress); BaseAddress += BlockSize; } } }
private void FreePages(ulong address, ulong pagesCount) { ulong endAddr = address + pagesCount * KMemoryManager.PageSize; int blockIndex = _blockOrdersCount - 1; ulong addressRounded = 0; ulong endAddrTruncated = 0; for (; blockIndex >= 0; blockIndex--) { KMemoryRegionBlock allocInfo = _blocks[blockIndex]; int blockSize = 1 << allocInfo.Order; addressRounded = BitUtils.AlignUp(address, blockSize); endAddrTruncated = BitUtils.AlignDown(endAddr, blockSize); if (addressRounded < endAddrTruncated) { break; } } void FreeRegion(ulong currAddress) { for (int currBlockIndex = blockIndex; currBlockIndex < _blockOrdersCount && currAddress != 0; currBlockIndex++) { KMemoryRegionBlock block = _blocks[currBlockIndex]; block.FreeCount++; ulong freedBlocks = (currAddress - block.StartAligned) >> block.Order; int index = (int)freedBlocks; for (int level = block.MaxLevel - 1; level >= 0; level--, index /= 64) { long mask = block.Masks[level][index / 64]; block.Masks[level][index / 64] = mask | (1L << (index & 63)); if (mask != 0) { break; } } int blockSizeDelta = 1 << (block.NextOrder - block.Order); int freedBlocksTruncated = BitUtils.AlignDown((int)freedBlocks, blockSizeDelta); if (!block.TryCoalesce(freedBlocksTruncated, blockSizeDelta)) { break; } currAddress = block.StartAligned + ((ulong)freedBlocksTruncated << block.Order); } } //Free inside aligned region. ulong baseAddress = addressRounded; while (baseAddress < endAddrTruncated) { ulong blockSize = 1UL << _blocks[blockIndex].Order; FreeRegion(baseAddress); baseAddress += blockSize; } int nextBlockIndex = blockIndex - 1; //Free region between Address and aligned region start. baseAddress = addressRounded; for (blockIndex = nextBlockIndex; blockIndex >= 0; blockIndex--) { ulong blockSize = 1UL << _blocks[blockIndex].Order; while (baseAddress - blockSize >= address) { baseAddress -= blockSize; FreeRegion(baseAddress); } } //Free region between aligned region end and End Address. baseAddress = endAddrTruncated; for (blockIndex = nextBlockIndex; blockIndex >= 0; blockIndex--) { ulong blockSize = 1UL << _blocks[blockIndex].Order; while (baseAddress + blockSize <= endAddr) { FreeRegion(baseAddress); baseAddress += blockSize; } } }
public ResultCode Initialize(ref AudioRendererConfiguration parameter, uint processHandle, CpuAddress workBuffer, ulong workBufferSize, int sessionId, ulong appletResourceId, IVirtualMemoryManager memoryManager) { if (!BehaviourContext.CheckValidRevision(parameter.Revision)) { return(ResultCode.OperationFailed); } if (GetWorkBufferSize(ref parameter) > workBufferSize) { return(ResultCode.WorkBufferTooSmall); } Debug.Assert(parameter.RenderingDevice == AudioRendererRenderingDevice.Dsp && parameter.ExecutionMode == AudioRendererExecutionMode.Auto); Logger.Info?.Print(LogClass.AudioRenderer, $"Initializing with REV{BehaviourContext.GetRevisionNumber(parameter.Revision)}"); _behaviourContext.SetUserRevision(parameter.Revision); _sampleRate = parameter.SampleRate; _sampleCount = parameter.SampleCount; _mixBufferCount = parameter.MixBufferCount; _voiceChannelCountMax = Constants.VoiceChannelCountMax; _upsamplerCount = parameter.SinkCount + parameter.SubMixBufferCount; _appletResourceId = appletResourceId; _memoryPoolCount = parameter.EffectCount + parameter.VoiceCount * Constants.VoiceWaveBufferCount; _executionMode = parameter.ExecutionMode; _sessionId = sessionId; MemoryManager = memoryManager; if (memoryManager is IRefCounted rc) { rc.IncrementReferenceCount(); } WorkBufferAllocator workBufferAllocator; _workBufferRegion = MemoryManager.GetWritableRegion(workBuffer, (int)workBufferSize); _workBufferRegion.Memory.Span.Fill(0); _workBufferMemoryPin = _workBufferRegion.Memory.Pin(); workBufferAllocator = new WorkBufferAllocator(_workBufferRegion.Memory); PoolMapper poolMapper = new PoolMapper(processHandle, false); poolMapper.InitializeSystemPool(ref _dspMemoryPoolState, workBuffer, workBufferSize); _mixBuffer = workBufferAllocator.Allocate <float>(_sampleCount * (_voiceChannelCountMax + _mixBufferCount), 0x10); if (_mixBuffer.IsEmpty) { return(ResultCode.WorkBufferTooSmall); } Memory <float> upSamplerWorkBuffer = workBufferAllocator.Allocate <float>(Constants.TargetSampleCount * (_voiceChannelCountMax + _mixBufferCount) * _upsamplerCount, 0x10); if (upSamplerWorkBuffer.IsEmpty) { return(ResultCode.WorkBufferTooSmall); } _depopBuffer = workBufferAllocator.Allocate <float>((ulong)BitUtils.AlignUp(parameter.MixBufferCount, Constants.BufferAlignment), Constants.BufferAlignment); if (_depopBuffer.IsEmpty) { return(ResultCode.WorkBufferTooSmall); } // Invalidate DSP cache on what was currently allocated with workBuffer. AudioProcessorMemoryManager.InvalidateDspCache(_dspMemoryPoolState.Translate(workBuffer, workBufferAllocator.Offset), workBufferAllocator.Offset); Debug.Assert((workBufferAllocator.Offset % Constants.BufferAlignment) == 0); Memory <VoiceState> voices = workBufferAllocator.Allocate <VoiceState>(parameter.VoiceCount, VoiceState.Alignment); if (voices.IsEmpty) { return(ResultCode.WorkBufferTooSmall); } foreach (ref VoiceState voice in voices.Span) { voice.Initialize(); } // A pain to handle as we can't have VoiceState*, use indices to be a bit more safe Memory <int> sortedVoices = workBufferAllocator.Allocate <int>(parameter.VoiceCount, 0x10); if (sortedVoices.IsEmpty) { return(ResultCode.WorkBufferTooSmall); } // Clear memory (use -1 as it's an invalid index) sortedVoices.Span.Fill(-1); Memory <VoiceChannelResource> voiceChannelResources = workBufferAllocator.Allocate <VoiceChannelResource>(parameter.VoiceCount, VoiceChannelResource.Alignment); if (voiceChannelResources.IsEmpty) { return(ResultCode.WorkBufferTooSmall); } for (uint id = 0; id < voiceChannelResources.Length; id++) { ref VoiceChannelResource voiceChannelResource = ref voiceChannelResources.Span[(int)id]; voiceChannelResource.Id = id; voiceChannelResource.IsUsed = false; }
public static bool LoadNsos(KernelContext context, Npdm metaData, byte[] arguments = null, params IExecutable[] executables) { ulong argsStart = 0; uint argsSize = 0; ulong codeStart = metaData.Is64Bit ? 0x8000000UL : 0x200000UL; uint codeSize = 0; ulong[] nsoBase = new ulong[executables.Length]; for (int index = 0; index < executables.Length; index++) { IExecutable nso = executables[index]; uint textEnd = nso.TextOffset + (uint)nso.Text.Length; uint roEnd = nso.RoOffset + (uint)nso.Ro.Length; uint dataEnd = nso.DataOffset + (uint)nso.Data.Length + nso.BssSize; uint nsoSize = textEnd; if (nsoSize < roEnd) { nsoSize = roEnd; } if (nsoSize < dataEnd) { nsoSize = dataEnd; } nsoSize = BitUtils.AlignUp(nsoSize, KMemoryManager.PageSize); nsoBase[index] = codeStart + (ulong)codeSize; codeSize += nsoSize; if (arguments != null && argsSize == 0) { argsStart = (ulong)codeSize; argsSize = (uint)BitUtils.AlignDown(arguments.Length * 2 + ArgsTotalSize - 1, KMemoryManager.PageSize); codeSize += argsSize; } } PtcProfiler.StaticCodeStart = codeStart; PtcProfiler.StaticCodeSize = (ulong)codeSize; int codePagesCount = (int)(codeSize / KMemoryManager.PageSize); int personalMmHeapPagesCount = metaData.PersonalMmHeapSize / KMemoryManager.PageSize; ProcessCreationInfo creationInfo = new ProcessCreationInfo( metaData.TitleName, metaData.Version, metaData.Aci0.TitleId, codeStart, codePagesCount, (ProcessCreationFlags)metaData.ProcessFlags | ProcessCreationFlags.IsApplication, 0, personalMmHeapPagesCount); KernelResult result; KResourceLimit resourceLimit = new KResourceLimit(context); long applicationRgSize = (long)context.MemoryRegions[(int)MemoryRegion.Application].Size; result = resourceLimit.SetLimitValue(LimitableResource.Memory, applicationRgSize); result |= resourceLimit.SetLimitValue(LimitableResource.Thread, 608); result |= resourceLimit.SetLimitValue(LimitableResource.Event, 700); result |= resourceLimit.SetLimitValue(LimitableResource.TransferMemory, 128); result |= resourceLimit.SetLimitValue(LimitableResource.Session, 894); if (result != KernelResult.Success) { Logger.Error?.Print(LogClass.Loader, $"Process initialization failed setting resource limit values."); return(false); } KProcess process = new KProcess(context); MemoryRegion memoryRegion = (MemoryRegion)((metaData.Acid.Flags >> 2) & 0xf); if (memoryRegion > MemoryRegion.NvServices) { Logger.Error?.Print(LogClass.Loader, $"Process initialization failed due to invalid ACID flags."); return(false); } var processContextFactory = new ArmProcessContextFactory(); result = process.Initialize( creationInfo, metaData.Aci0.KernelAccessControl.Capabilities, resourceLimit, memoryRegion, processContextFactory); if (result != KernelResult.Success) { Logger.Error?.Print(LogClass.Loader, $"Process initialization returned error \"{result}\"."); return(false); } for (int index = 0; index < executables.Length; index++) { Logger.Info?.Print(LogClass.Loader, $"Loading image {index} at 0x{nsoBase[index]:x16}..."); result = LoadIntoMemory(process, executables[index], nsoBase[index]); if (result != KernelResult.Success) { Logger.Error?.Print(LogClass.Loader, $"Process initialization returned error \"{result}\"."); return(false); } } process.DefaultCpuCore = metaData.DefaultCpuId; result = process.Start(metaData.MainThreadPriority, (ulong)metaData.MainThreadStackSize); if (result != KernelResult.Success) { Logger.Error?.Print(LogClass.Loader, $"Process start returned error \"{result}\"."); return(false); } context.Processes.TryAdd(process.Pid, process); return(true); }
public void PrepareForReturn() { if (Config.Stage == ShaderStage.Fragment) { if (Config.OmapDepth) { Operand dest = Attribute(AttributeConsts.FragmentOutputDepth); Operand src = Register(Config.GetDepthRegister(), RegisterType.Gpr); this.Copy(dest, src); } int regIndex = 0; for (int rtIndex = 0; rtIndex < 8; rtIndex++) { OmapTarget target = Config.OmapTargets[rtIndex]; for (int component = 0; component < 4; component++) { if (!target.ComponentEnabled(component)) { continue; } int fragmentOutputColorAttr = AttributeConsts.FragmentOutputColorBase + rtIndex * 16; Operand src = Register(regIndex, RegisterType.Gpr); // Perform B <-> R swap if needed, for BGRA formats (not supported on OpenGL). if (component == 0 || component == 2) { Operand isBgra = Attribute(AttributeConsts.FragmentOutputIsBgraBase + rtIndex * 4); Operand lblIsBgra = Label(); Operand lblEnd = Label(); this.BranchIfTrue(lblIsBgra, isBgra); this.Copy(Attribute(fragmentOutputColorAttr + component * 4), src); this.Branch(lblEnd); MarkLabel(lblIsBgra); this.Copy(Attribute(fragmentOutputColorAttr + (2 - component) * 4), src); MarkLabel(lblEnd); } else { this.Copy(Attribute(fragmentOutputColorAttr + component * 4), src); } regIndex++; } regIndex = BitUtils.AlignUp(regIndex, 4); } } }
public static SizeInfo GetBlockLinearTextureSize( int width, int height, int depth, int levels, int layers, int blockWidth, int blockHeight, int bytesPerPixel, int gobBlocksInY, int gobBlocksInZ, int gobBlocksInTileX, int gpuLayerSize = 0) { bool is3D = depth > 1; int layerSize = 0; int[] allOffsets = new int[is3D ? Calculate3DOffsetCount(levels, depth) : levels * layers * depth]; int[] mipOffsets = new int[levels]; int[] sliceSizes = new int[levels]; int[] levelSizes = new int[levels]; int mipGobBlocksInY = gobBlocksInY; int mipGobBlocksInZ = gobBlocksInZ; int gobWidth = (GobStride / bytesPerPixel) * gobBlocksInTileX; int gobHeight = gobBlocksInY * GobHeight; int depthLevelOffset = 0; for (int level = 0; level < levels; level++) { int w = Math.Max(1, width >> level); int h = Math.Max(1, height >> level); int d = Math.Max(1, depth >> level); w = BitUtils.DivRoundUp(w, blockWidth); h = BitUtils.DivRoundUp(h, blockHeight); while (h <= (mipGobBlocksInY >> 1) * GobHeight && mipGobBlocksInY != 1) { mipGobBlocksInY >>= 1; } while (d <= (mipGobBlocksInZ >> 1) && mipGobBlocksInZ != 1) { mipGobBlocksInZ >>= 1; } int widthInGobs = BitUtils.DivRoundUp(w * bytesPerPixel, GobStride); int alignment = gobBlocksInTileX; if (d < gobBlocksInZ || w <= gobWidth || h <= gobHeight) { alignment = 1; } widthInGobs = BitUtils.AlignUp(widthInGobs, alignment); int totalBlocksOfGobsInZ = BitUtils.DivRoundUp(d, mipGobBlocksInZ); int totalBlocksOfGobsInY = BitUtils.DivRoundUp(BitUtils.DivRoundUp(h, GobHeight), mipGobBlocksInY); int robSize = widthInGobs * mipGobBlocksInY * mipGobBlocksInZ * GobSize; if (is3D) { int gobSize = mipGobBlocksInY * GobSize; int sliceSize = totalBlocksOfGobsInY * widthInGobs * gobSize; int baseOffset = layerSize; int mask = gobBlocksInZ - 1; for (int z = 0; z < d; z++) { int zLow = z & mask; int zHigh = z & ~mask; allOffsets[z + depthLevelOffset] = baseOffset + zLow * gobSize + zHigh * sliceSize; } } mipOffsets[level] = layerSize; sliceSizes[level] = totalBlocksOfGobsInY * robSize; levelSizes[level] = totalBlocksOfGobsInZ * sliceSizes[level]; layerSize += levelSizes[level]; depthLevelOffset += d; } if (layers > 1) { layerSize = AlignLayerSize( layerSize, height, depth, blockHeight, gobBlocksInY, gobBlocksInZ, gobBlocksInTileX); } int totalSize; if (layerSize < gpuLayerSize) { totalSize = (layers - 1) * gpuLayerSize + layerSize; layerSize = gpuLayerSize; } else { totalSize = layerSize * layers; } if (!is3D) { for (int layer = 0; layer < layers; layer++) { int baseIndex = layer * levels; int baseOffset = layer * layerSize; for (int level = 0; level < levels; level++) { allOffsets[baseIndex + level] = baseOffset + mipOffsets[level]; } } } return(new SizeInfo(mipOffsets, allOffsets, sliceSizes, levelSizes, depth, levels, layerSize, totalSize, is3D)); }
public readonly int GetMipStride(int level) { return(BitUtils.AlignUp(GetLevelWidth(level) * BytesPerPixel, 4)); }
public static void ConvertLinearToBlockLinear( Span <byte> dst, int width, int height, int stride, int bytesPerPixel, int gobBlocksInY, ReadOnlySpan <byte> data) { int gobHeight = gobBlocksInY * GobHeight; int strideTrunc = BitUtils.AlignDown(width * bytesPerPixel, 16); int strideTrunc64 = BitUtils.AlignDown(width * bytesPerPixel, 64); int xStart = strideTrunc / bytesPerPixel; int inStrideGap = stride - width * bytesPerPixel; int alignment = GobStride / bytesPerPixel; int wAligned = BitUtils.AlignUp(width, alignment); BlockLinearLayout layoutConverter = new BlockLinearLayout(wAligned, height, gobBlocksInY, 1, bytesPerPixel); unsafe bool Convert <T>(Span <byte> output, ReadOnlySpan <byte> data) where T : unmanaged { fixed(byte *outputPtr = output, dataPtr = data) { byte *inPtr = dataPtr; for (int y = 0; y < height; y++) { layoutConverter.SetY(y); for (int x = 0; x < strideTrunc64; x += 64, inPtr += 64) { byte *offset = outputPtr + layoutConverter.GetOffsetWithLineOffset64(x); byte *offset2 = offset + 0x20; byte *offset3 = offset + 0x100; byte *offset4 = offset + 0x120; Vector128 <byte> value = *(Vector128 <byte> *)inPtr; Vector128 <byte> value2 = *(Vector128 <byte> *)(inPtr + 16); Vector128 <byte> value3 = *(Vector128 <byte> *)(inPtr + 32); Vector128 <byte> value4 = *(Vector128 <byte> *)(inPtr + 48); *(Vector128 <byte> *)offset = value; *(Vector128 <byte> *)offset2 = value2; *(Vector128 <byte> *)offset3 = value3; *(Vector128 <byte> *)offset4 = value4; } for (int x = strideTrunc64; x < strideTrunc; x += 16, inPtr += 16) { byte *offset = outputPtr + layoutConverter.GetOffsetWithLineOffset16(x); *(Vector128 <byte> *)offset = *(Vector128 <byte> *)inPtr; } for (int x = xStart; x < width; x++, inPtr += bytesPerPixel) { byte *offset = outputPtr + layoutConverter.GetOffset(x); *(T *)offset = *(T *)inPtr; } inPtr += inStrideGap; } } return(true); } bool _ = bytesPerPixel switch { 1 => Convert <byte>(dst, data), 2 => Convert <ushort>(dst, data), 4 => Convert <uint>(dst, data), 8 => Convert <ulong>(dst, data), 12 => Convert <Bpp12Pixel>(dst, data), 16 => Convert <Vector128 <byte> >(dst, data), _ => throw new NotSupportedException($"Unable to convert ${bytesPerPixel} bpp pixel format.") }; }
public static Span <byte> ConvertBlockLinearToLinear( int width, int height, int depth, int sliceDepth, int levels, int layers, int blockWidth, int blockHeight, int bytesPerPixel, int gobBlocksInY, int gobBlocksInZ, int gobBlocksInTileX, SizeInfo sizeInfo, ReadOnlySpan <byte> data) { int outSize = GetTextureSize( width, height, depth, levels, layers, blockWidth, blockHeight, bytesPerPixel); Span <byte> output = new byte[outSize]; int outOffs = 0; int mipGobBlocksInY = gobBlocksInY; int mipGobBlocksInZ = gobBlocksInZ; int gobWidth = (GobStride / bytesPerPixel) * gobBlocksInTileX; int gobHeight = gobBlocksInY * GobHeight; for (int level = 0; level < levels; level++) { int w = Math.Max(1, width >> level); int h = Math.Max(1, height >> level); int d = Math.Max(1, depth >> level); w = BitUtils.DivRoundUp(w, blockWidth); h = BitUtils.DivRoundUp(h, blockHeight); while (h <= (mipGobBlocksInY >> 1) * GobHeight && mipGobBlocksInY != 1) { mipGobBlocksInY >>= 1; } while (d <= (mipGobBlocksInZ >> 1) && mipGobBlocksInZ != 1) { mipGobBlocksInZ >>= 1; } int strideTrunc = BitUtils.AlignDown(w * bytesPerPixel, 16); int strideTrunc64 = BitUtils.AlignDown(w * bytesPerPixel, 64); int xStart = strideTrunc / bytesPerPixel; int stride = BitUtils.AlignUp(w * bytesPerPixel, HostStrideAlignment); int outStrideGap = stride - w * bytesPerPixel; int alignment = gobWidth; if (d < gobBlocksInZ || w <= gobWidth || h <= gobHeight) { alignment = GobStride / bytesPerPixel; } int wAligned = BitUtils.AlignUp(w, alignment); BlockLinearLayout layoutConverter = new BlockLinearLayout( wAligned, h, mipGobBlocksInY, mipGobBlocksInZ, bytesPerPixel); int sd = Math.Max(1, sliceDepth >> level); unsafe bool Convert <T>(Span <byte> output, ReadOnlySpan <byte> data) where T : unmanaged { fixed(byte *outputPtr = output, dataPtr = data) { byte *outPtr = outputPtr + outOffs; for (int layer = 0; layer < layers; layer++) { byte *inBaseOffset = dataPtr + (layer * sizeInfo.LayerSize + sizeInfo.GetMipOffset(level)); for (int z = 0; z < sd; z++) { layoutConverter.SetZ(z); for (int y = 0; y < h; y++) { layoutConverter.SetY(y); for (int x = 0; x < strideTrunc64; x += 64, outPtr += 64) { byte *offset = inBaseOffset + layoutConverter.GetOffsetWithLineOffset64(x); byte *offset2 = offset + 0x20; byte *offset3 = offset + 0x100; byte *offset4 = offset + 0x120; Vector128 <byte> value = *(Vector128 <byte> *)offset; Vector128 <byte> value2 = *(Vector128 <byte> *)offset2; Vector128 <byte> value3 = *(Vector128 <byte> *)offset3; Vector128 <byte> value4 = *(Vector128 <byte> *)offset4; *(Vector128 <byte> *)outPtr = value; *(Vector128 <byte> *)(outPtr + 16) = value2; *(Vector128 <byte> *)(outPtr + 32) = value3; *(Vector128 <byte> *)(outPtr + 48) = value4; } for (int x = strideTrunc64; x < strideTrunc; x += 16, outPtr += 16) { byte *offset = inBaseOffset + layoutConverter.GetOffsetWithLineOffset16(x); *(Vector128 <byte> *)outPtr = *(Vector128 <byte> *)offset; } for (int x = xStart; x < w; x++, outPtr += bytesPerPixel) { byte *offset = inBaseOffset + layoutConverter.GetOffset(x); *(T *)outPtr = *(T *)offset; } outPtr += outStrideGap; } } } outOffs += stride * h * d * layers; } return(true); } bool _ = bytesPerPixel switch { 1 => Convert <byte>(output, data), 2 => Convert <ushort>(output, data), 4 => Convert <uint>(output, data), 8 => Convert <ulong>(output, data), 12 => Convert <Bpp12Pixel>(output, data), 16 => Convert <Vector128 <byte> >(output, data), _ => throw new NotSupportedException($"Unable to convert ${bytesPerPixel} bpp pixel format.") }; } return(output); }
/// <summary> /// Determines if a given texture is "safe" for upscaling from its info. /// Note that this is different from being compatible - this elilinates targets that would have detrimental effects when scaled. /// </summary> /// <param name="info">The texture info to check</param> /// <returns>True if safe</returns> private static bool UpscaleSafeMode(TextureInfo info) { // While upscaling works for all targets defined by IsUpscaleCompatible, we additionally blacklist targets here that // may have undesirable results (upscaling blur textures) or simply waste GPU resources (upscaling texture atlas). if (info.Levels > 3) { // Textures with more than 3 levels are likely to be game textures, rather than render textures. // Small textures with full mips are likely to be removed by the next check. return(false); } if (info.Width < 8 || info.Height < 8) { // Discount textures with small dimensions. return(false); } int widthAlignment = (info.IsLinear ? Constants.StrideAlignment : Constants.GobAlignment) / info.FormatInfo.BytesPerPixel; if (!(info.FormatInfo.Format.IsDepthOrStencil() || info.FormatInfo.Components == 1)) { // Discount square textures that aren't depth-stencil like. (excludes game textures, cubemap faces, most 3D texture LUT, texture atlas) // Detect if the texture is possibly square. Widths may be aligned, so to remove the uncertainty we align both the width and height. bool possiblySquare = BitUtils.AlignUp(info.Width, widthAlignment) == BitUtils.AlignUp(info.Height, widthAlignment); if (possiblySquare) { return(false); } } if (info.Height < 360) { int aspectWidth = (int)MathF.Ceiling((info.Height / 9f) * 16f); int aspectMaxWidth = BitUtils.AlignUp(aspectWidth, widthAlignment); int aspectMinWidth = BitUtils.AlignDown(aspectWidth, widthAlignment); if (info.Width >= aspectMinWidth && info.Width <= aspectMaxWidth && info.Height < 360) { // Targets that are roughly 16:9 can only be rescaled if they're equal to or above 360p. (excludes blur and bloom textures) return(false); } } return(true); }
private static int Align4(int value) { return(BitUtils.AlignUp(value, 4)); }
public static bool LoadKernelInitalProcess(Horizon system, KernelInitialProcess kip) { int endOffset = kip.DataOffset + kip.Data.Length; if (kip.BssSize != 0) { endOffset = kip.BssOffset + kip.BssSize; } int codeSize = BitUtils.AlignUp(kip.TextOffset + endOffset, KMemoryManager.PageSize); int codePagesCount = codeSize / KMemoryManager.PageSize; ulong codeBaseAddress = kip.Addr39Bits ? 0x8000000UL : 0x200000UL; ulong codeAddress = codeBaseAddress + (ulong)kip.TextOffset; int mmuFlags = 0; if (AslrEnabled) { //TODO: Randomization. mmuFlags |= 0x20; } if (kip.Addr39Bits) { mmuFlags |= (int)AddressSpaceType.Addr39Bits << 1; } if (kip.Is64Bits) { mmuFlags |= 1; } ProcessCreationInfo creationInfo = new ProcessCreationInfo( kip.Name, kip.ProcessCategory, kip.TitleId, codeAddress, codePagesCount, mmuFlags, 0, 0); MemoryRegion memRegion = kip.IsService ? MemoryRegion.Service : MemoryRegion.Application; KMemoryRegionManager region = system.MemoryRegions[(int)memRegion]; KernelResult result = region.AllocatePages((ulong)codePagesCount, false, out KPageList pageList); if (result != KernelResult.Success) { Logger.PrintError(LogClass.Loader, $"Process initialization returned error \"{result}\"."); return(false); } KProcess process = new KProcess(system); result = process.InitializeKip( creationInfo, kip.Capabilities, pageList, system.ResourceLimit, memRegion); if (result != KernelResult.Success) { Logger.PrintError(LogClass.Loader, $"Process initialization returned error \"{result}\"."); return(false); } result = LoadIntoMemory(process, kip, codeBaseAddress); if (result != KernelResult.Success) { Logger.PrintError(LogClass.Loader, $"Process initialization returned error \"{result}\"."); return(false); } result = process.Start(kip.MainThreadPriority, (ulong)kip.MainThreadStackSize); if (result != KernelResult.Success) { Logger.PrintError(LogClass.Loader, $"Process start returned error \"{result}\"."); return(false); } system.Processes.Add(process.Pid, process); return(true); }
public static SizeInfo GetBlockLinearTextureSize( int width, int height, int depth, int levels, int layers, int blockWidth, int blockHeight, int bytesPerPixel, int gobBlocksInY, int gobBlocksInZ, int gobBlocksInTileX) { bool is3D = depth > 1; int layerSize = 0; int[] allOffsets = new int[levels * layers * depth]; int[] mipOffsets = new int[levels]; int mipGobBlocksInY = gobBlocksInY; int mipGobBlocksInZ = gobBlocksInZ; for (int level = 0; level < levels; level++) { int w = Math.Max(1, width >> level); int h = Math.Max(1, height >> level); int d = Math.Max(1, depth >> level); w = BitUtils.DivRoundUp(w, blockWidth); h = BitUtils.DivRoundUp(h, blockHeight); while (h <= (mipGobBlocksInY >> 1) * GobHeight && mipGobBlocksInY != 1) { mipGobBlocksInY >>= 1; } while (d <= (mipGobBlocksInZ >> 1) && mipGobBlocksInZ != 1) { mipGobBlocksInZ >>= 1; } int widthInGobs = BitUtils.AlignUp(BitUtils.DivRoundUp(w * bytesPerPixel, GobStride), gobBlocksInTileX); int totalBlocksOfGobsInZ = BitUtils.DivRoundUp(d, mipGobBlocksInZ); int totalBlocksOfGobsInY = BitUtils.DivRoundUp(BitUtils.DivRoundUp(h, GobHeight), mipGobBlocksInY); int robSize = widthInGobs * mipGobBlocksInY * mipGobBlocksInZ * GobSize; if (is3D) { int gobSize = mipGobBlocksInY * GobSize; int sliceSize = totalBlocksOfGobsInY * widthInGobs * gobSize; int baseOffset = layerSize; int mask = gobBlocksInZ - 1; for (int z = 0; z < d; z++) { int zLow = z & mask; int zHigh = z & ~mask; allOffsets[z * levels + level] = baseOffset + zLow * gobSize + zHigh * sliceSize; } } mipOffsets[level] = layerSize; layerSize += totalBlocksOfGobsInZ * totalBlocksOfGobsInY * robSize; } layerSize = AlignLayerSize( layerSize, height, depth, blockHeight, gobBlocksInY, gobBlocksInZ); if (!is3D) { for (int layer = 0; layer < layers; layer++) { int baseIndex = layer * levels; int baseOffset = layer * layerSize; for (int level = 0; level < levels; level++) { allOffsets[baseIndex + level] = baseOffset + mipOffsets[level]; } } } int totalSize = layerSize * layers; return(new SizeInfo(mipOffsets, allOffsets, levels, layerSize, totalSize)); }
/// <summary> /// Performs actual copy of the inline data after the transfer is finished. /// </summary> private void FinishTransfer() { var memoryManager = _channel.MemoryManager; var data = MemoryMarshal.Cast <int, byte>(_buffer).Slice(0, _size); if (_isLinear && _lineCount == 1) { memoryManager.Write(_dstGpuVa, data); } else { var dstCalculator = new OffsetCalculator( _dstWidth, _dstHeight, _dstStride, _isLinear, _dstGobBlocksInY, 1); int srcOffset = 0; for (int y = _dstY; y < _dstY + _lineCount; y++) { int x1 = _dstX; int x2 = _dstX + _lineLengthIn; int x1Round = BitUtils.AlignUp(_dstX, 16); int x2Trunc = BitUtils.AlignDown(x2, 16); int x = x1; if (x1Round <= x2) { for (; x < x1Round; x++, srcOffset++) { int dstOffset = dstCalculator.GetOffset(x, y); ulong dstAddress = _dstGpuVa + (uint)dstOffset; memoryManager.Write(dstAddress, data[srcOffset]); } } for (; x < x2Trunc; x += 16, srcOffset += 16) { int dstOffset = dstCalculator.GetOffset(x, y); ulong dstAddress = _dstGpuVa + (uint)dstOffset; memoryManager.Write(dstAddress, MemoryMarshal.Cast <byte, Vector128 <byte> >(data.Slice(srcOffset, 16))[0]); } for (; x < x2; x++, srcOffset++) { int dstOffset = dstCalculator.GetOffset(x, y); ulong dstAddress = _dstGpuVa + (uint)dstOffset; memoryManager.Write(dstAddress, data[srcOffset]); } } } _finished = true; _context.AdvanceSequence(); }
public static bool LoadKip(KernelContext context, KipExecutable kip) { int endOffset = kip.DataOffset + kip.Data.Length; if (kip.BssSize != 0) { endOffset = kip.BssOffset + kip.BssSize; } int codeSize = BitUtils.AlignUp(kip.TextOffset + endOffset, KMemoryManager.PageSize); int codePagesCount = codeSize / KMemoryManager.PageSize; ulong codeBaseAddress = (kip.Header.Flags & 0x10) != 0 ? 0x8000000UL : 0x200000UL; ulong codeAddress = codeBaseAddress + (ulong)kip.TextOffset; int mmuFlags = 0; if (AslrEnabled) { // TODO: Randomization. mmuFlags |= 0x20; } if ((kip.Header.Flags & 0x10) != 0) { mmuFlags |= (int)AddressSpaceType.Addr39Bits << 1; } if ((kip.Header.Flags & 0x08) != 0) { mmuFlags |= 1; } ProcessCreationInfo creationInfo = new ProcessCreationInfo( kip.Header.Name, kip.Header.ProcessCategory, kip.Header.TitleId, codeAddress, codePagesCount, mmuFlags, 0, 0); MemoryRegion memoryRegion = (kip.Header.Flags & 0x20) != 0 ? MemoryRegion.Service : MemoryRegion.Application; KMemoryRegionManager region = context.MemoryRegions[(int)memoryRegion]; KernelResult result = region.AllocatePages((ulong)codePagesCount, false, out KPageList pageList); if (result != KernelResult.Success) { Logger.PrintError(LogClass.Loader, $"Process initialization returned error \"{result}\"."); return(false); } KProcess process = new KProcess(context); result = process.InitializeKip( creationInfo, kip.Capabilities, pageList, context.ResourceLimit, memoryRegion); if (result != KernelResult.Success) { Logger.PrintError(LogClass.Loader, $"Process initialization returned error \"{result}\"."); return(false); } result = LoadIntoMemory(process, kip, codeBaseAddress); if (result != KernelResult.Success) { Logger.PrintError(LogClass.Loader, $"Process initialization returned error \"{result}\"."); return(false); } process.DefaultCpuCore = kip.Header.DefaultCore; result = process.Start(kip.Header.MainThreadPriority, (ulong)kip.Header.Sections[1].Attribute); if (result != KernelResult.Success) { Logger.PrintError(LogClass.Loader, $"Process start returned error \"{result}\"."); return(false); } context.Processes.TryAdd(process.Pid, process); return(true); }
private KernelResult CopyToClient(KMemoryManager memoryManager, List <KBufferDescriptor> list) { foreach (KBufferDescriptor desc in list) { MemoryState stateMask; switch (desc.State) { case MemoryState.IpcBuffer0: stateMask = MemoryState.IpcSendAllowedType0; break; case MemoryState.IpcBuffer1: stateMask = MemoryState.IpcSendAllowedType1; break; case MemoryState.IpcBuffer3: stateMask = MemoryState.IpcSendAllowedType3; break; default: return(KernelResult.InvalidCombination); } MemoryAttribute attributeMask = MemoryAttribute.Borrowed | MemoryAttribute.Uncached; if (desc.State == MemoryState.IpcBuffer0) { attributeMask |= MemoryAttribute.DeviceMapped; } ulong clientAddrTruncated = BitUtils.AlignDown(desc.ClientAddress, KMemoryManager.PageSize); ulong clientAddrRounded = BitUtils.AlignUp(desc.ClientAddress, KMemoryManager.PageSize); // Check if address is not aligned, in this case we need to perform 2 copies. if (clientAddrTruncated != clientAddrRounded) { ulong copySize = clientAddrRounded - desc.ClientAddress; if (copySize > desc.Size) { copySize = desc.Size; } KernelResult result = memoryManager.CopyDataFromCurrentProcess( desc.ClientAddress, copySize, stateMask, stateMask, MemoryPermission.ReadAndWrite, attributeMask, MemoryAttribute.None, desc.ServerAddress); if (result != KernelResult.Success) { return(result); } } ulong clientEndAddr = desc.ClientAddress + desc.Size; ulong serverEndAddr = desc.ServerAddress + desc.Size; ulong clientEndAddrTruncated = BitUtils.AlignDown(clientEndAddr, KMemoryManager.PageSize); ulong clientEndAddrRounded = BitUtils.AlignUp(clientEndAddr, KMemoryManager.PageSize); ulong serverEndAddrTruncated = BitUtils.AlignDown(serverEndAddr, KMemoryManager.PageSize); if (clientEndAddrTruncated < clientEndAddrRounded && (clientAddrTruncated == clientAddrRounded || clientAddrTruncated < clientEndAddrTruncated)) { KernelResult result = memoryManager.CopyDataFromCurrentProcess( clientEndAddrTruncated, clientEndAddr - clientEndAddrTruncated, stateMask, stateMask, MemoryPermission.ReadAndWrite, attributeMask, MemoryAttribute.None, serverEndAddrTruncated); if (result != KernelResult.Success) { return(result); } } } return(KernelResult.Success); }
public KernelResult Start(int mainThreadPriority, ulong stackSize) { lock (_processLock) { if (_state > ProcessState.CreatedAttached) { return(KernelResult.InvalidState); } if (ResourceLimit != null && !ResourceLimit.Reserve(LimitableResource.Thread, 1)) { return(KernelResult.ResLimitExceeded); } KResourceLimit threadResourceLimit = ResourceLimit; KResourceLimit memoryResourceLimit = null; if (_mainThreadStackSize != 0) { throw new InvalidOperationException("Trying to start a process with a invalid state!"); } ulong stackSizeRounded = BitUtils.AlignUp(stackSize, KMemoryManager.PageSize); ulong neededSize = stackSizeRounded + _imageSize; //Check if the needed size for the code and the stack will fit on the //memory usage capacity of this Process. Also check for possible overflow //on the above addition. if (neededSize > _memoryUsageCapacity || neededSize < stackSizeRounded) { threadResourceLimit?.Release(LimitableResource.Thread, 1); return(KernelResult.OutOfMemory); } if (stackSizeRounded != 0 && ResourceLimit != null) { memoryResourceLimit = ResourceLimit; if (!memoryResourceLimit.Reserve(LimitableResource.Memory, stackSizeRounded)) { threadResourceLimit?.Release(LimitableResource.Thread, 1); return(KernelResult.ResLimitExceeded); } } KernelResult result; KThread mainThread = null; ulong stackTop = 0; void CleanUpForError() { HandleTable.Destroy(); mainThread?.DecrementReferenceCount(); if (_mainThreadStackSize != 0) { ulong stackBottom = stackTop - _mainThreadStackSize; ulong stackPagesCount = _mainThreadStackSize / KMemoryManager.PageSize; MemoryManager.UnmapForKernel(stackBottom, stackPagesCount, MemoryState.Stack); _mainThreadStackSize = 0; } memoryResourceLimit?.Release(LimitableResource.Memory, stackSizeRounded); threadResourceLimit?.Release(LimitableResource.Thread, 1); } if (stackSizeRounded != 0) { ulong stackPagesCount = stackSizeRounded / KMemoryManager.PageSize; ulong regionStart = MemoryManager.StackRegionStart; ulong regionSize = MemoryManager.StackRegionEnd - regionStart; ulong regionPagesCount = regionSize / KMemoryManager.PageSize; result = MemoryManager.AllocateOrMapPa( stackPagesCount, KMemoryManager.PageSize, 0, false, regionStart, regionPagesCount, MemoryState.Stack, MemoryPermission.ReadAndWrite, out ulong stackBottom); if (result != KernelResult.Success) { CleanUpForError(); return(result); } _mainThreadStackSize += stackSizeRounded; stackTop = stackBottom + stackSizeRounded; } ulong heapCapacity = _memoryUsageCapacity - _mainThreadStackSize - _imageSize; result = MemoryManager.SetHeapCapacity(heapCapacity); if (result != KernelResult.Success) { CleanUpForError(); return(result); } HandleTable = new KHandleTable(System); result = HandleTable.Initialize(Capabilities.HandleTableSize); if (result != KernelResult.Success) { CleanUpForError(); return(result); } mainThread = new KThread(System); result = mainThread.Initialize( _entrypoint, 0, stackTop, mainThreadPriority, DefaultCpuCore, this); if (result != KernelResult.Success) { CleanUpForError(); return(result); } result = HandleTable.GenerateHandle(mainThread, out int mainThreadHandle); if (result != KernelResult.Success) { CleanUpForError(); return(result); } mainThread.SetEntryArguments(0, mainThreadHandle); ProcessState oldState = _state; ProcessState newState = _state != ProcessState.Created ? ProcessState.Attached : ProcessState.Started; SetState(newState); //TODO: We can't call KThread.Start from a non-guest thread. //We will need to make some changes to allow the creation of //dummy threads that will be used to initialize the current //thread on KCoreContext so that GetCurrentThread doesn't fail. /* Result = MainThread.Start(); * * if (Result != KernelResult.Success) * { * SetState(OldState); * * CleanUpForError(); * } */ mainThread.Reschedule(ThreadSchedState.Running); if (result == KernelResult.Success) { mainThread.IncrementReferenceCount(); } mainThread.DecrementReferenceCount(); return(result); } }
public static byte[] DecodeBC4(ReadOnlySpan <byte> data, int width, int height, int depth, int levels, int layers, bool signed) { int size = 0; for (int l = 0; l < levels; l++) { size += BitUtils.AlignUp(Math.Max(1, width >> l), 4) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers; } // Backends currently expect a stride alignment of 4 bytes, so output width must be aligned. int alignedWidth = BitUtils.AlignUp(width, 4); byte[] output = new byte[size]; Span <byte> outputSpan = new Span <byte>(output); ReadOnlySpan <ulong> data64 = MemoryMarshal.Cast <byte, ulong>(data); Span <byte> tile = stackalloc byte[BlockWidth * BlockHeight]; Span <byte> rPal = stackalloc byte[8]; Span <uint> tileAsUint = MemoryMarshal.Cast <byte, uint>(tile); Span <uint> outputLine0 = default; Span <uint> outputLine1 = default; Span <uint> outputLine2 = default; Span <uint> outputLine3 = default; int imageBaseOOffs = 0; for (int l = 0; l < levels; l++) { int w = BitUtils.DivRoundUp(width, BlockWidth); int h = BitUtils.DivRoundUp(height, BlockHeight); for (int l2 = 0; l2 < layers; l2++) { for (int z = 0; z < depth; z++) { for (int y = 0; y < h; y++) { int baseY = y * BlockHeight; int copyHeight = Math.Min(BlockHeight, height - baseY); int lineBaseOOffs = imageBaseOOffs + baseY * alignedWidth; if (copyHeight == 4) { outputLine0 = MemoryMarshal.Cast <byte, uint>(outputSpan.Slice(lineBaseOOffs)); outputLine1 = MemoryMarshal.Cast <byte, uint>(outputSpan.Slice(lineBaseOOffs + alignedWidth)); outputLine2 = MemoryMarshal.Cast <byte, uint>(outputSpan.Slice(lineBaseOOffs + alignedWidth * 2)); outputLine3 = MemoryMarshal.Cast <byte, uint>(outputSpan.Slice(lineBaseOOffs + alignedWidth * 3)); } for (int x = 0; x < w; x++) { int baseX = x * BlockWidth; int copyWidth = Math.Min(BlockWidth, width - baseX); ulong block = data64[0]; rPal[0] = (byte)block; rPal[1] = (byte)(block >> 8); if (signed) { BCnLerpAlphaSnorm(rPal); } else { BCnLerpAlphaUnorm(rPal); } BCnDecodeTileAlpha(tile, rPal, block >> 16); if ((copyWidth | copyHeight) == 4) { outputLine0[x] = tileAsUint[0]; outputLine1[x] = tileAsUint[1]; outputLine2[x] = tileAsUint[2]; outputLine3[x] = tileAsUint[3]; } else { int pixelBaseOOffs = lineBaseOOffs + baseX; for (int tY = 0; tY < copyHeight; tY++) { tile.Slice(tY * 4, copyWidth).CopyTo(outputSpan.Slice(pixelBaseOOffs + alignedWidth * tY, copyWidth)); } } data64 = data64.Slice(1); } } imageBaseOOffs += alignedWidth * height; } } width = Math.Max(1, width >> 1); height = Math.Max(1, height >> 1); depth = Math.Max(1, depth >> 1); alignedWidth = BitUtils.AlignUp(width, 4); } return(output); }
public static ulong GetTargetSize <T>(ulong currentSize, ulong count, int align) where T : unmanaged { return(BitUtils.AlignUp(currentSize, align) + (ulong)Unsafe.SizeOf <T>() * count); }
public static byte[] DecodeBC5(ReadOnlySpan <byte> data, int width, int height, int depth, int levels, int layers, bool signed) { int size = 0; for (int l = 0; l < levels; l++) { size += BitUtils.AlignUp(Math.Max(1, width >> l), 2) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers * 2; } // Backends currently expect a stride alignment of 4 bytes, so output width must be aligned. int alignedWidth = BitUtils.AlignUp(width, 2); byte[] output = new byte[size]; ReadOnlySpan <ulong> data64 = MemoryMarshal.Cast <byte, ulong>(data); Span <byte> rTile = stackalloc byte[BlockWidth * BlockHeight * 2]; Span <byte> gTile = stackalloc byte[BlockWidth * BlockHeight * 2]; Span <byte> rPal = stackalloc byte[8]; Span <byte> gPal = stackalloc byte[8]; Span <ushort> outputAsUshort = MemoryMarshal.Cast <byte, ushort>(output); Span <uint> rTileAsUint = MemoryMarshal.Cast <byte, uint>(rTile); Span <uint> gTileAsUint = MemoryMarshal.Cast <byte, uint>(gTile); Span <ulong> outputLine0 = default; Span <ulong> outputLine1 = default; Span <ulong> outputLine2 = default; Span <ulong> outputLine3 = default; int imageBaseOOffs = 0; for (int l = 0; l < levels; l++) { int w = BitUtils.DivRoundUp(width, BlockWidth); int h = BitUtils.DivRoundUp(height, BlockHeight); for (int l2 = 0; l2 < layers; l2++) { for (int z = 0; z < depth; z++) { for (int y = 0; y < h; y++) { int baseY = y * BlockHeight; int copyHeight = Math.Min(BlockHeight, height - baseY); int lineBaseOOffs = imageBaseOOffs + baseY * alignedWidth; if (copyHeight == 4) { outputLine0 = MemoryMarshal.Cast <ushort, ulong>(outputAsUshort.Slice(lineBaseOOffs)); outputLine1 = MemoryMarshal.Cast <ushort, ulong>(outputAsUshort.Slice(lineBaseOOffs + alignedWidth)); outputLine2 = MemoryMarshal.Cast <ushort, ulong>(outputAsUshort.Slice(lineBaseOOffs + alignedWidth * 2)); outputLine3 = MemoryMarshal.Cast <ushort, ulong>(outputAsUshort.Slice(lineBaseOOffs + alignedWidth * 3)); } for (int x = 0; x < w; x++) { int baseX = x * BlockWidth; int copyWidth = Math.Min(BlockWidth, width - baseX); ulong blockL = data64[0]; ulong blockH = data64[1]; rPal[0] = (byte)blockL; rPal[1] = (byte)(blockL >> 8); gPal[0] = (byte)blockH; gPal[1] = (byte)(blockH >> 8); if (signed) { BCnLerpAlphaSnorm(rPal); BCnLerpAlphaSnorm(gPal); } else { BCnLerpAlphaUnorm(rPal); BCnLerpAlphaUnorm(gPal); } BCnDecodeTileAlpha(rTile, rPal, blockL >> 16); BCnDecodeTileAlpha(gTile, gPal, blockH >> 16); if ((copyWidth | copyHeight) == 4) { outputLine0[x] = InterleaveBytes(rTileAsUint[0], gTileAsUint[0]); outputLine1[x] = InterleaveBytes(rTileAsUint[1], gTileAsUint[1]); outputLine2[x] = InterleaveBytes(rTileAsUint[2], gTileAsUint[2]); outputLine3[x] = InterleaveBytes(rTileAsUint[3], gTileAsUint[3]); } else { int pixelBaseOOffs = lineBaseOOffs + baseX; for (int tY = 0; tY < copyHeight; tY++) { int line = pixelBaseOOffs + alignedWidth * tY; for (int tX = 0; tX < copyWidth; tX++) { int texel = tY * BlockWidth + tX; outputAsUshort[line + tX] = (ushort)(rTile[texel] | (gTile[texel] << 8)); } } } data64 = data64.Slice(2); } } imageBaseOOffs += alignedWidth * height; } } width = Math.Max(1, width >> 1); height = Math.Max(1, height >> 1); depth = Math.Max(1, depth >> 1); alignedWidth = BitUtils.AlignUp(width, 2); } return(output); }
public KernelResult Start(int mainThreadPriority, ulong stackSize) { lock (_processLock) { if (State > ProcessState.CreatedAttached) { return(KernelResult.InvalidState); } if (ResourceLimit != null && !ResourceLimit.Reserve(LimitableResource.Thread, 1)) { return(KernelResult.ResLimitExceeded); } KResourceLimit threadResourceLimit = ResourceLimit; KResourceLimit memoryResourceLimit = null; if (_mainThreadStackSize != 0) { throw new InvalidOperationException("Trying to start a process with a invalid state!"); } ulong stackSizeRounded = BitUtils.AlignUp(stackSize, KPageTableBase.PageSize); ulong neededSize = stackSizeRounded + _imageSize; // Check if the needed size for the code and the stack will fit on the // memory usage capacity of this Process. Also check for possible overflow // on the above addition. if (neededSize > _memoryUsageCapacity || neededSize < stackSizeRounded) { threadResourceLimit?.Release(LimitableResource.Thread, 1); return(KernelResult.OutOfMemory); } if (stackSizeRounded != 0 && ResourceLimit != null) { memoryResourceLimit = ResourceLimit; if (!memoryResourceLimit.Reserve(LimitableResource.Memory, stackSizeRounded)) { threadResourceLimit?.Release(LimitableResource.Thread, 1); return(KernelResult.ResLimitExceeded); } } KernelResult result; KThread mainThread = null; ulong stackTop = 0; void CleanUpForError() { HandleTable.Destroy(); mainThread?.DecrementReferenceCount(); if (_mainThreadStackSize != 0) { ulong stackBottom = stackTop - _mainThreadStackSize; ulong stackPagesCount = _mainThreadStackSize / KPageTableBase.PageSize; MemoryManager.UnmapForKernel(stackBottom, stackPagesCount, MemoryState.Stack); _mainThreadStackSize = 0; } memoryResourceLimit?.Release(LimitableResource.Memory, stackSizeRounded); threadResourceLimit?.Release(LimitableResource.Thread, 1); } if (stackSizeRounded != 0) { ulong stackPagesCount = stackSizeRounded / KPageTableBase.PageSize; ulong regionStart = MemoryManager.StackRegionStart; ulong regionSize = MemoryManager.StackRegionEnd - regionStart; ulong regionPagesCount = regionSize / KPageTableBase.PageSize; result = MemoryManager.MapPages( stackPagesCount, KPageTableBase.PageSize, 0, false, regionStart, regionPagesCount, MemoryState.Stack, KMemoryPermission.ReadAndWrite, out ulong stackBottom); if (result != KernelResult.Success) { CleanUpForError(); return(result); } _mainThreadStackSize += stackSizeRounded; stackTop = stackBottom + stackSizeRounded; } ulong heapCapacity = _memoryUsageCapacity - _mainThreadStackSize - _imageSize; result = MemoryManager.SetHeapCapacity(heapCapacity); if (result != KernelResult.Success) { CleanUpForError(); return(result); } HandleTable = new KHandleTable(KernelContext); result = HandleTable.Initialize(Capabilities.HandleTableSize); if (result != KernelResult.Success) { CleanUpForError(); return(result); } mainThread = new KThread(KernelContext); result = mainThread.Initialize( _entrypoint, 0, stackTop, mainThreadPriority, DefaultCpuCore, this, ThreadType.User, _customThreadStart); if (result != KernelResult.Success) { CleanUpForError(); return(result); } result = HandleTable.GenerateHandle(mainThread, out int mainThreadHandle); if (result != KernelResult.Success) { CleanUpForError(); return(result); } mainThread.SetEntryArguments(0, mainThreadHandle); ProcessState oldState = State; ProcessState newState = State != ProcessState.Created ? ProcessState.Attached : ProcessState.Started; SetState(newState); result = mainThread.Start(); if (result != KernelResult.Success) { SetState(oldState); CleanUpForError(); } if (result == KernelResult.Success) { mainThread.IncrementReferenceCount(); } mainThread.DecrementReferenceCount(); return(result); } }
// GetWorkBufferSize(nn::audio::detail::AudioRendererParameterInternal) -> u64 public ResultCode GetAudioRendererWorkBufferSize(ServiceCtx context) { AudioRendererParameter parameters = GetAudioRendererParameter(context); if (AudioRendererCommon.CheckValidRevision(parameters)) { BehaviorInfo behaviorInfo = new BehaviorInfo(); behaviorInfo.SetUserLibRevision(parameters.Revision); long size; int totalMixCount = parameters.SubMixCount + 1; size = BitUtils.AlignUp(parameters.MixBufferCount * 4, AudioRendererConsts.BufferAlignment) + parameters.SubMixCount * 0x400 + totalMixCount * 0x940 + parameters.VoiceCount * 0x3F0 + BitUtils.AlignUp(totalMixCount * 8, 16) + BitUtils.AlignUp(parameters.VoiceCount * 8, 16) + BitUtils.AlignUp(((parameters.SinkCount + parameters.SubMixCount) * 0x3C0 + parameters.SampleCount * 4) * (parameters.MixBufferCount + 6), AudioRendererConsts.BufferAlignment) + (parameters.SinkCount + parameters.SubMixCount) * 0x2C0 + (parameters.EffectCount + parameters.VoiceCount * 4) * 0x30 + 0x50; if (behaviorInfo.IsSplitterSupported()) { size += BitUtils.AlignUp(NodeStates.GetWorkBufferSize(totalMixCount) + EdgeMatrix.GetWorkBufferSize(totalMixCount), 16); } size = parameters.SinkCount * 0x170 + (parameters.SinkCount + parameters.SubMixCount) * 0x280 + parameters.EffectCount * 0x4C0 + ((size + SplitterContext.CalcWorkBufferSize(behaviorInfo, parameters) + 0x30 * parameters.EffectCount + (4 * parameters.VoiceCount) + 0x8F) & ~0x3FL) + ((parameters.VoiceCount << 8) | 0x40); if (parameters.PerformanceManagerCount >= 1) { size += (PerformanceManager.GetRequiredBufferSizeForPerformanceMetricsPerFrame(behaviorInfo, parameters) * (parameters.PerformanceManagerCount + 1) + 0xFF) & ~0x3FL; } if (behaviorInfo.IsVariadicCommandBufferSizeSupported()) { size += CommandGenerator.CalculateCommandBufferSize(parameters) + 0x7E; } else { size += 0x1807E; } size = BitUtils.AlignUp(size, 0x1000); context.ResponseData.Write(size); Logger.PrintDebug(LogClass.ServiceAudio, $"WorkBufferSize is 0x{size:x16}."); return(ResultCode.Success); } else { context.ResponseData.Write(0L); Logger.PrintWarning(LogClass.ServiceAudio, $"Library Revision REV{AudioRendererCommon.GetRevisionVersion(parameters.Revision)} is not supported!"); return(ResultCode.UnsupportedRevision); } }
public static bool LoadKip(KernelContext context, KipExecutable kip) { uint endOffset = kip.DataOffset + (uint)kip.Data.Length; if (kip.BssSize != 0) { endOffset = kip.BssOffset + kip.BssSize; } uint codeSize = BitUtils.AlignUp(kip.TextOffset + endOffset, KMemoryManager.PageSize); int codePagesCount = (int)(codeSize / KMemoryManager.PageSize); ulong codeBaseAddress = kip.Is64BitAddressSpace ? 0x8000000UL : 0x200000UL; ulong codeAddress = codeBaseAddress + (ulong)kip.TextOffset; ProcessCreationFlags flags = 0; if (AslrEnabled) { // TODO: Randomization. flags |= ProcessCreationFlags.EnableAslr; } if (kip.Is64BitAddressSpace) { flags |= ProcessCreationFlags.AddressSpace64Bit; } if (kip.Is64Bit) { flags |= ProcessCreationFlags.Is64Bit; } ProcessCreationInfo creationInfo = new ProcessCreationInfo( kip.Name, kip.Version, kip.ProgramId, codeAddress, codePagesCount, flags, 0, 0); MemoryRegion memoryRegion = kip.UsesSecureMemory ? MemoryRegion.Service : MemoryRegion.Application; KMemoryRegionManager region = context.MemoryRegions[(int)memoryRegion]; KernelResult result = region.AllocatePages((ulong)codePagesCount, false, out KPageList pageList); if (result != KernelResult.Success) { Logger.Error?.Print(LogClass.Loader, $"Process initialization returned error \"{result}\"."); return(false); } KProcess process = new KProcess(context); var processContextFactory = new ArmProcessContextFactory(); result = process.InitializeKip( creationInfo, kip.Capabilities, pageList, context.ResourceLimit, memoryRegion, processContextFactory); if (result != KernelResult.Success) { Logger.Error?.Print(LogClass.Loader, $"Process initialization returned error \"{result}\"."); return(false); } result = LoadIntoMemory(process, kip, codeBaseAddress); if (result != KernelResult.Success) { Logger.Error?.Print(LogClass.Loader, $"Process initialization returned error \"{result}\"."); return(false); } process.DefaultCpuCore = kip.IdealCoreId; result = process.Start(kip.Priority, (ulong)kip.StackSize); if (result != KernelResult.Success) { Logger.Error?.Print(LogClass.Loader, $"Process start returned error \"{result}\"."); return(false); } context.Processes.TryAdd(process.Pid, process); return(true); }
/// <summary> /// Get the required work buffer size memory needed for the <see cref="EdgeMatrix"/>. /// </summary> /// <param name="nodeCount">The count of nodes.</param> /// <returns>The size required for the given <paramref name="nodeCount"/>.</returns> public static int GetWorkBufferSize(int nodeCount) { int size = BitUtils.AlignUp(nodeCount * nodeCount, RendererConstants.BufferAlignment); return(size / Unsafe.SizeOf <byte>()); }
public KMemoryRegionManager(ulong address, ulong size, ulong endAddr) { _blocks = new KMemoryRegionBlock[BlockOrders.Length]; Address = address; Size = size; EndAddr = endAddr; _blockOrdersCount = BlockOrders.Length; for (int blockIndex = 0; blockIndex < _blockOrdersCount; blockIndex++) { _blocks[blockIndex] = new KMemoryRegionBlock(); _blocks[blockIndex].Order = BlockOrders[blockIndex]; int nextOrder = blockIndex == _blockOrdersCount - 1 ? 0 : BlockOrders[blockIndex + 1]; _blocks[blockIndex].NextOrder = nextOrder; int currBlockSize = 1 << BlockOrders[blockIndex]; int nextBlockSize = currBlockSize; if (nextOrder != 0) { nextBlockSize = 1 << nextOrder; } ulong startAligned = BitUtils.AlignDown(address, nextBlockSize); ulong endAddrAligned = BitUtils.AlignDown(endAddr, currBlockSize); ulong sizeInBlocksTruncated = (endAddrAligned - startAligned) >> BlockOrders[blockIndex]; ulong endAddrRounded = BitUtils.AlignUp(address + size, nextBlockSize); ulong sizeInBlocksRounded = (endAddrRounded - startAligned) >> BlockOrders[blockIndex]; _blocks[blockIndex].StartAligned = startAligned; _blocks[blockIndex].SizeInBlocksTruncated = sizeInBlocksTruncated; _blocks[blockIndex].SizeInBlocksRounded = sizeInBlocksRounded; ulong currSizeInBlocks = sizeInBlocksRounded; int maxLevel = 0; do { maxLevel++; }while ((currSizeInBlocks /= 64) != 0); _blocks[blockIndex].MaxLevel = maxLevel; _blocks[blockIndex].Masks = new long[maxLevel][]; currSizeInBlocks = sizeInBlocksRounded; for (int level = maxLevel - 1; level >= 0; level--) { currSizeInBlocks = (currSizeInBlocks + 63) / 64; _blocks[blockIndex].Masks[level] = new long[currSizeInBlocks]; } } if (size != 0) { FreePages(address, size / KMemoryManager.PageSize); } }
private KernelResult GetReceiveListAddress( PointerBufferDesc descriptor, Message message, uint recvListType, uint messageSizeInWords, ulong[] receiveList, ref uint dstOffset, out ulong address) { ulong recvListBufferAddress = address = 0; if (recvListType == 0) { return(KernelResult.OutOfResource); } else if (recvListType == 1 || recvListType == 2) { ulong recvListBaseAddr; ulong recvListEndAddr; if (recvListType == 1) { recvListBaseAddr = message.Address + messageSizeInWords * 4; recvListEndAddr = message.Address + message.Size; } else /* if (recvListType == 2) */ { ulong packed = receiveList[0]; recvListBaseAddr = packed & 0x7fffffffff; uint size = (uint)(packed >> 48); if (size == 0) { return(KernelResult.OutOfResource); } recvListEndAddr = recvListBaseAddr + size; } recvListBufferAddress = BitUtils.AlignUp(recvListBaseAddr + dstOffset, 0x10); ulong endAddress = recvListBufferAddress + descriptor.BufferSize; dstOffset = (uint)endAddress - (uint)recvListBaseAddr; if (recvListBufferAddress + descriptor.BufferSize <= recvListBufferAddress || recvListBufferAddress + descriptor.BufferSize > recvListEndAddr) { return(KernelResult.OutOfResource); } } else /* if (recvListType > 2) */ { if (descriptor.ReceiveIndex >= receiveList.Length) { return(KernelResult.OutOfResource); } ulong packed = receiveList[descriptor.ReceiveIndex]; recvListBufferAddress = packed & 0x7fffffffff; uint size = (uint)(packed >> 48); if (recvListBufferAddress == 0 || size == 0 || size < descriptor.BufferSize) { return(KernelResult.OutOfResource); } } address = recvListBufferAddress; return(KernelResult.Success); }
public static Span <byte> ConvertLinearToBlockLinear( int width, int height, int depth, int levels, int layers, int blockWidth, int blockHeight, int bytesPerPixel, int gobBlocksInY, int gobBlocksInZ, int gobBlocksInTileX, SizeInfo sizeInfo, ReadOnlySpan <byte> data) { Span <byte> output = new byte[sizeInfo.TotalSize]; int inOffs = 0; int mipGobBlocksInY = gobBlocksInY; int mipGobBlocksInZ = gobBlocksInZ; int gobWidth = (GobStride / bytesPerPixel) * gobBlocksInTileX; int gobHeight = gobBlocksInY * GobHeight; for (int level = 0; level < levels; level++) { int w = Math.Max(1, width >> level); int h = Math.Max(1, height >> level); int d = Math.Max(1, depth >> level); w = BitUtils.DivRoundUp(w, blockWidth); h = BitUtils.DivRoundUp(h, blockHeight); while (h <= (mipGobBlocksInY >> 1) * GobHeight && mipGobBlocksInY != 1) { mipGobBlocksInY >>= 1; } while (d <= (mipGobBlocksInZ >> 1) && mipGobBlocksInZ != 1) { mipGobBlocksInZ >>= 1; } int stride = BitUtils.AlignUp(w * bytesPerPixel, HostStrideAlignment); int alignment = gobWidth; if (d < gobBlocksInZ || w <= gobWidth || h <= gobHeight) { alignment = GobStride / bytesPerPixel; } int wAligned = BitUtils.AlignUp(w, alignment); BlockLinearLayout layoutConverter = new BlockLinearLayout( wAligned, h, d, mipGobBlocksInY, mipGobBlocksInZ, bytesPerPixel); for (int layer = 0; layer < layers; layer++) { int outBaseOffset = layer * sizeInfo.LayerSize + sizeInfo.GetMipOffset(level); for (int z = 0; z < d; z++) { for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { int offset = outBaseOffset + layoutConverter.GetOffset(x, y, z); Span <byte> dest = output.Slice(offset, bytesPerPixel); data.Slice(inOffs + x * bytesPerPixel, bytesPerPixel).CopyTo(dest); } inOffs += stride; } } } } return(output); }
public static bool LoadStaticObjects( Horizon system, Npdm metaData, IExecutable[] staticObjects, byte[] arguments = null) { if (!metaData.Is64Bits) { Logger.PrintWarning(LogClass.Loader, "32-bits application detected!"); } ulong argsStart = 0; int argsSize = 0; ulong codeStart = metaData.Is64Bits ? 0x8000000UL : 0x200000UL; int codeSize = 0; ulong[] nsoBase = new ulong[staticObjects.Length]; for (int index = 0; index < staticObjects.Length; index++) { IExecutable staticObject = staticObjects[index]; int textEnd = staticObject.TextOffset + staticObject.Text.Length; int roEnd = staticObject.RoOffset + staticObject.Ro.Length; int dataEnd = staticObject.DataOffset + staticObject.Data.Length + staticObject.BssSize; int nsoSize = textEnd; if ((uint)nsoSize < (uint)roEnd) { nsoSize = roEnd; } if ((uint)nsoSize < (uint)dataEnd) { nsoSize = dataEnd; } nsoSize = BitUtils.AlignUp(nsoSize, KMemoryManager.PageSize); nsoBase[index] = codeStart + (ulong)codeSize; codeSize += nsoSize; if (arguments != null && argsSize == 0) { argsStart = (ulong)codeSize; argsSize = BitUtils.AlignDown(arguments.Length * 2 + ArgsTotalSize - 1, KMemoryManager.PageSize); codeSize += argsSize; } } int codePagesCount = codeSize / KMemoryManager.PageSize; int personalMmHeapPagesCount = metaData.PersonalMmHeapSize / KMemoryManager.PageSize; ProcessCreationInfo creationInfo = new ProcessCreationInfo( metaData.TitleName, metaData.ProcessCategory, metaData.Aci0.TitleId, codeStart, codePagesCount, metaData.MmuFlags, 0, personalMmHeapPagesCount); KernelResult result; KResourceLimit resourceLimit = new KResourceLimit(system); long applicationRgSize = (long)system.MemoryRegions[(int)MemoryRegion.Application].Size; result = resourceLimit.SetLimitValue(LimitableResource.Memory, applicationRgSize); result |= resourceLimit.SetLimitValue(LimitableResource.Thread, 608); result |= resourceLimit.SetLimitValue(LimitableResource.Event, 700); result |= resourceLimit.SetLimitValue(LimitableResource.TransferMemory, 128); result |= resourceLimit.SetLimitValue(LimitableResource.Session, 894); if (result != KernelResult.Success) { Logger.PrintError(LogClass.Loader, $"Process initialization failed setting resource limit values."); return(false); } KProcess process = new KProcess(system); MemoryRegion memoryRegion = (MemoryRegion)((metaData.Acid.Flags >> 2) & 0xf); if (memoryRegion > MemoryRegion.NvServices) { Logger.PrintError(LogClass.Loader, $"Process initialization failed due to invalid ACID flags."); return(false); } result = process.Initialize( creationInfo, metaData.Aci0.KernelAccessControl.Capabilities, resourceLimit, memoryRegion); if (result != KernelResult.Success) { Logger.PrintError(LogClass.Loader, $"Process initialization returned error \"{result}\"."); return(false); } for (int index = 0; index < staticObjects.Length; index++) { Logger.PrintInfo(LogClass.Loader, $"Loading image {index} at 0x{nsoBase[index]:x16}..."); result = LoadIntoMemory(process, staticObjects[index], nsoBase[index]); if (result != KernelResult.Success) { Logger.PrintError(LogClass.Loader, $"Process initialization returned error \"{result}\"."); return(false); } } process.DefaultCpuCore = metaData.DefaultCpuId; result = process.Start(metaData.MainThreadPriority, (ulong)metaData.MainThreadStackSize); if (result != KernelResult.Success) { Logger.PrintError(LogClass.Loader, $"Process start returned error \"{result}\"."); return(false); } system.Processes.Add(process.Pid, process); return(true); }
/// <summary> /// Determines if a given texture is "safe" for upscaling from its info. /// Note that this is different from being compatible - this elilinates targets that would have detrimental effects when scaled. /// </summary> /// <param name="info">The texture info to check</param> /// <returns>True if safe</returns> public bool UpscaleSafeMode(TextureInfo info) { // While upscaling works for all targets defined by IsUpscaleCompatible, we additionally blacklist targets here that // may have undesirable results (upscaling blur textures) or simply waste GPU resources (upscaling texture atlas). if (!(info.FormatInfo.Format.IsDepthOrStencil() || info.FormatInfo.Components == 1)) { // Discount square textures that aren't depth-stencil like. (excludes game textures, cubemap faces, most 3D texture LUT, texture atlas) // Detect if the texture is possibly square. Widths may be aligned, so to remove the uncertainty we align both the width and height. int widthAlignment = (info.IsLinear ? 32 : 64) / info.FormatInfo.BytesPerPixel; bool possiblySquare = BitUtils.AlignUp(info.Width, widthAlignment) == BitUtils.AlignUp(info.Height, widthAlignment); if (possiblySquare) { return(false); } } int aspect = (int)Math.Round((info.Width / (float)info.Height) * 9); if (aspect == 16 && info.Height < 360) { // Targets that are roughly 16:9 can only be rescaled if they're equal to or above 360p. (excludes blur and bloom textures) return(false); } return(true); }
public KMemoryRegionManager(ulong Address, ulong Size, ulong EndAddr) { Blocks = new KMemoryRegionBlock[BlockOrders.Length]; this.Address = Address; this.Size = Size; this.EndAddr = EndAddr; BlockOrdersCount = BlockOrders.Length; for (int BlockIndex = 0; BlockIndex < BlockOrdersCount; BlockIndex++) { Blocks[BlockIndex] = new KMemoryRegionBlock(); Blocks[BlockIndex].Order = BlockOrders[BlockIndex]; int NextOrder = BlockIndex == BlockOrdersCount - 1 ? 0 : BlockOrders[BlockIndex + 1]; Blocks[BlockIndex].NextOrder = NextOrder; int CurrBlockSize = 1 << BlockOrders[BlockIndex]; int NextBlockSize = CurrBlockSize; if (NextOrder != 0) { NextBlockSize = 1 << NextOrder; } ulong StartAligned = BitUtils.AlignDown(Address, NextBlockSize); ulong EndAddrAligned = BitUtils.AlignDown(EndAddr, CurrBlockSize); ulong SizeInBlocksTruncated = (EndAddrAligned - StartAligned) >> BlockOrders[BlockIndex]; ulong EndAddrRounded = BitUtils.AlignUp(Address + Size, NextBlockSize); ulong SizeInBlocksRounded = (EndAddrRounded - StartAligned) >> BlockOrders[BlockIndex]; Blocks[BlockIndex].StartAligned = StartAligned; Blocks[BlockIndex].SizeInBlocksTruncated = SizeInBlocksTruncated; Blocks[BlockIndex].SizeInBlocksRounded = SizeInBlocksRounded; ulong CurrSizeInBlocks = SizeInBlocksRounded; int MaxLevel = 0; do { MaxLevel++; }while ((CurrSizeInBlocks /= 64) != 0); Blocks[BlockIndex].MaxLevel = MaxLevel; Blocks[BlockIndex].Masks = new long[MaxLevel][]; CurrSizeInBlocks = SizeInBlocksRounded; for (int Level = MaxLevel - 1; Level >= 0; Level--) { CurrSizeInBlocks = (CurrSizeInBlocks + 63) / 64; Blocks[BlockIndex].Masks[Level] = new long[CurrSizeInBlocks]; } } if (Size != 0) { FreePages(Address, Size / KMemoryManager.PageSize); } }