# HG changeset patch # User raptor # Date 1566855843 21600 # Node ID c5934cdeecb6e5b67148212b7302588509b24bac # Parent 34eb5cc722415bd125bf71ed927c21d0b336ec21# Parent c8f37fc9c2666dd27b80dbd6f7f6c2604bb1e180 Merge diff -r c8f37fc9c266 -r c5934cdeecb6 hedgewars/uAIMisc.pas --- a/hedgewars/uAIMisc.pas Mon Aug 26 15:40:15 2019 -0600 +++ b/hedgewars/uAIMisc.pas Mon Aug 26 15:44:03 2019 -0600 @@ -799,7 +799,7 @@ begin dead:= true; Targets.reset:= true; - if dX < 0.035 then + if gdX < 0.035 then begin subrate:= RealRateExplosion(Me, round(pX), round(pY), 61, afErasesLand or afTrackFall); if abs(subrate) > 2000 then inc(Rate,subrate div 1024) diff -r c8f37fc9c266 -r c5934cdeecb6 hedgewars/uConsts.pas --- a/hedgewars/uConsts.pas Mon Aug 26 15:40:15 2019 -0600 +++ b/hedgewars/uConsts.pas Mon Aug 26 15:44:03 2019 -0600 @@ -110,6 +110,8 @@ ifCritical = $00000002; // image is critical for gameplay (exit game if unable to load) ifColorKey = $00000004; // image uses transparent pixels (color keying) ifIgnoreCaps = $00000008; // ignore hardware capabilities when loading (i.e. image will not be drawn using OpenGL) + ifDigestAlpha = $00000010; // add alpha channel to the digest + ifDigestAll = $00000020; // add all channels to the digest // texture priority (allows OpenGL to keep frequently used textures in video memory more easily) tpLowest = 0.00; diff -r c8f37fc9c266 -r c5934cdeecb6 hedgewars/uGears.pas --- a/hedgewars/uGears.pas Mon Aug 26 15:40:15 2019 -0600 +++ b/hedgewars/uGears.pas Mon Aug 26 15:44:03 2019 -0600 @@ -276,7 +276,7 @@ end; if curHandledGear^.Active then begin - if curHandledGear^.RenderTimer then + if (not cOnlyStats) and curHandledGear^.RenderTimer then begin // Mine timer if (curHandledGear^.Kind in [gtMine, gtSMine, gtAirMine]) then diff -r c8f37fc9c266 -r c5934cdeecb6 hedgewars/uGearsHandlersMess.pas --- a/hedgewars/uGearsHandlersMess.pas Mon Aug 26 15:40:15 2019 -0600 +++ b/hedgewars/uGearsHandlersMess.pas Mon Aug 26 15:44:03 2019 -0600 @@ -2422,7 +2422,7 @@ doStepCase(Gear) else // health texture (FlightTime = health when the last texture was generated) - if (Gear^.Health <> Gear^.FlightTime) or (Gear^.Tex = nil) then + if (not cOnlyStats) and ((Gear^.Health <> Gear^.FlightTime) or (Gear^.Tex = nil)) then begin Gear^.FlightTime:= Gear^.Health; FreeAndNilTexture(Gear^.Tex); @@ -2500,7 +2500,7 @@ dec(Gear^.Health, Gear^.Damage); Gear^.Damage := 0; // health texture (FlightTime = health when the last texture was generated) - if (Gear^.Health <> Gear^.FlightTime) or (Gear^.Tex = nil) then + if (not cOnlyStats) and ((Gear^.Health <> Gear^.FlightTime) or (Gear^.Tex = nil)) then begin Gear^.FlightTime:= Gear^.Health; FreeAndNilTexture(Gear^.Tex); @@ -2523,12 +2523,14 @@ i:= 1 else i:= 0 + else if cOnlyStats then + i:= 0 // Always show health (default) else i:= 1; if i = 1 then begin - if (Gear^.Health <> Gear^.FlightTime) or (Gear^.Tex = nil) then + if (not cOnlyStats) and ((Gear^.Health <> Gear^.FlightTime) or (Gear^.Tex = nil)) then begin Gear^.FlightTime:= Gear^.Health; FreeAndNilTexture(Gear^.Tex); @@ -2537,7 +2539,7 @@ end else begin - if (Gear^.FlightTime <> $ffffffff) or (Gear^.Tex = nil) then + if (not cOnlyStats) and ((Gear^.FlightTime <> $ffffffff) or (Gear^.Tex = nil)) then begin Gear^.FlightTime:= $ffffffff; FreeAndNilTexture(Gear^.Tex); @@ -4615,7 +4617,7 @@ i:= Gear^.Health div 20; - if (i <> Gear^.Damage) and ((GameTicks and $3F) = 0) then + if (not cOnlyStats) and (i <> Gear^.Damage) and ((GameTicks and $3F) = 0) then begin Gear^.Damage:= i; FreeAndNilTexture(Gear^.Tex); @@ -5887,7 +5889,7 @@ else begin i:= Gear^.Health div 5; - if (i <> Gear^.Damage) and ((GameTicks and $3F) = 0) then + if (not cOnlyStats) and (i <> Gear^.Damage) and ((GameTicks and $3F) = 0) then begin Gear^.Damage:= i; FreeAndNilTexture(Gear^.Tex); @@ -5978,7 +5980,7 @@ else begin i:= Gear^.Health div 10; - if (i <> Gear^.Damage) and ((GameTicks and $3F) = 0) then + if (not cOnlyStats) and (i <> Gear^.Damage) and ((GameTicks and $3F) = 0) then begin Gear^.Damage:= i; FreeAndNilTexture(Gear^.Tex); @@ -6497,7 +6499,7 @@ t:LongInt; begin t:= Gear^.Health div 10; - if (t <> Gear^.Damage) and ((GameTicks and $3F) = 0) then + if (not cOnlyStats) and (t <> Gear^.Damage) and ((GameTicks and $3F) = 0) then begin Gear^.Damage:= t; FreeAndNilTexture(Gear^.Tex); diff -r c8f37fc9c266 -r c5934cdeecb6 hedgewars/uLandObjects.pas --- a/hedgewars/uLandObjects.pas Mon Aug 26 15:40:15 2019 -0600 +++ b/hedgewars/uLandObjects.pas Mon Aug 26 15:44:03 2019 -0600 @@ -36,7 +36,7 @@ implementation uses uStore, uConsts, uConsole, uRandom, uSound , uTypes, uVariables, uDebug, uUtils - , uPhysFSLayer, adler32, uRenderUtils; + , uPhysFSLayer, uRenderUtils; const MaxRects = 512; MAXOBJECTRECTS = 16; @@ -346,51 +346,13 @@ CountNonZeroz:= lRes; end; -procedure ChecksumLandObjectImage(Image: PSDL_Surface; alphaOnly: boolean); -var y, x: LongInt; -var rowData: PByteArray; -begin - if Image = nil then exit; - - if alphaOnly then - rowData := GetMem(Image^.w); - - if SDL_MustLock(Image) then - SDL_LockSurface(Image); - - if checkFails(Image^.format^.BytesPerPixel = 4, 'Land object image should be 32bit', true) then - begin - if SDL_MustLock(Image) then - SDL_UnlockSurface(Image); - exit - end; - - for y := 0 to Image^.h - 1 do - if alphaOnly then - begin - for x := 0 to Image^.w - 1 do - rowData^[x] := PByteArray(Image^.pixels)^[y * Image^.pitch + x * 4 + AByteIndex]; - syncedPixelDigest:= Adler32Update(syncedPixelDigest, rowData, Image^.w); - end - else - syncedPixelDigest:= Adler32Update(syncedPixelDigest, @PByteArray(Image^.pixels)^[y*Image^.pitch], Image^.w*4); - - if SDL_MustLock(Image) then - SDL_UnlockSurface(Image); - - if alphaOnly then - FreeMem(rowData, Image^.w); -end; - function AddGirder(gX: LongInt; var girSurf: PSDL_Surface): boolean; var x1, x2, y, k, i, girderHeight: LongInt; rr: TSDL_Rect; bRes: boolean; begin if girSurf = nil then - girSurf:= LoadDataImageAltPath(ptCurrTheme, ptGraphics, 'Girder', ifCritical or ifColorKey or ifIgnoreCaps); - -ChecksumLandObjectImage(girsurf, true); + girSurf:= LoadDataImageAltPath(ptCurrTheme, ptGraphics, 'Girder', ifCritical or ifColorKey or ifIgnoreCaps or ifDigestAlpha); girderHeight:= girSurf^.h; @@ -745,11 +707,10 @@ Delete(s, 1, i); i:= Pos(',', s); if i = 0 then i:= Succ(Length(S)); - Surf:= LoadDataImage(ptCurrTheme, Trim(Copy(s, 1, Pred(i))), ifColorKey or ifIgnoreCaps or ifCritical); + Surf:= LoadDataImage(ptCurrTheme, Trim(Copy(s, 1, Pred(i))), ifColorKey or ifIgnoreCaps or ifCritical or ifDigestAlpha ); Width:= Surf^.w; Height:= Surf^.h; Delete(s, 1, i); - ChecksumLandObjectImage(Surf, true); end; end; @@ -949,19 +910,22 @@ begin i:= Pos(',', s); Name:= Trim(Copy(s, 1, Pred(i))); - Surf:= LoadDataImage(ptCurrTheme, Name, ifColorKey or ifIgnoreCaps or ifCritical); + + Mask:= LoadDataImage(ptCurrTheme, Trim(Copy(s, 1, Pred(i)))+'_mask', ifColorKey or ifIgnoreCaps or ifDigestAll); + if Mask = nil then + Surf:= LoadDataImage(ptCurrTheme, Name, ifColorKey or ifIgnoreCaps or ifCritical or ifDigestAlpha) + else + Surf:= LoadDataImage(ptCurrTheme, Name, ifColorKey or ifIgnoreCaps or ifCritical); + Width:= Surf^.w; Height:= Surf^.h; - Mask:= LoadDataImage(ptCurrTheme, Trim(Copy(s, 1, Pred(i)))+'_mask', ifColorKey or ifIgnoreCaps); + Delete(s, 1, i); i:= Pos(',', s); Maxcnt:= StrToInt(Trim(Copy(s, 1, Pred(i)))); Delete(s, 1, i); if (Maxcnt < 1) or (Maxcnt > MAXTHEMEOBJECTS) then OutError('Broken theme. Object''s max. count should be between 1 and '+ inttostr(MAXTHEMEOBJECTS) +' (it was '+ inttostr(Maxcnt) +').', true); - if Mask = nil then - ChecksumLandObjectImage(Surf, true); - ChecksumLandObjectImage(Mask, false); inrectcnt := 0; diff -r c8f37fc9c266 -r c5934cdeecb6 hedgewars/uRender.pas --- a/hedgewars/uRender.pas Mon Aug 26 15:40:15 2019 -0600 +++ b/hedgewars/uRender.pas Mon Aug 26 15:44:03 2019 -0600 @@ -2177,6 +2177,7 @@ procedure freeModule; begin + if cOnlyStats then exit; {$IFDEF GL2} glDeleteProgram(shaderMain); glDeleteProgram(shaderWater); diff -r c8f37fc9c266 -r c5934cdeecb6 hedgewars/uStore.pas --- a/hedgewars/uStore.pas Mon Aug 26 15:40:15 2019 -0600 +++ b/hedgewars/uStore.pas Mon Aug 26 15:44:03 2019 -0600 @@ -385,8 +385,7 @@ var ii: TSprite; ai: TAmmoType; tmpsurf, tmpoverlay: PSDL_Surface; - i, y, x, imflags: LongInt; - rowData: PByteArray; + i, imflags: LongInt; keyConfirm, keyQuit: shortstring; begin AddFileLog('StoreLoad()'); @@ -431,18 +430,10 @@ imflags := (imflags or ifCritical); // load the image - tmpsurf := LoadDataImageAltPath(Path, AltPath, FileName, imflags); - if (tmpsurf <> nil) and checkSum then - begin - rowData := GetMem(tmpsurf^.w); - for y := 0 to tmpsurf^.h-1 do - begin - for x := 0 to tmpsurf^.w - 1 do - rowData^[x] := PByteArray(tmpsurf^.pixels)^[y * tmpsurf^.pitch + x * 4 + AByteIndex]; - syncedPixelDigest:= Adler32Update(syncedPixelDigest, rowData, tmpsurf^.w); - end; - FreeMem(rowData, tmpsurf^.w); - end; + if checkSum then + tmpsurf := LoadDataImageAltPath(Path, AltPath, FileName, imflags or ifDigestAlpha) + else + tmpsurf := LoadDataImageAltPath(Path, AltPath, FileName, imflags); end; if tmpsurf <> nil then @@ -646,10 +637,13 @@ function LoadImage(const filename: shortstring; imageFlags: LongInt): PSDL_Surface; var tmpsurf: PSDL_Surface; s: shortstring; - logMsg: shortstring; + logMsg, digestMsg: shortstring; rwops: PSDL_RWops; + y, x: LongInt; + rowData: PByteArray; begin LoadImage:= nil; + digestMsg:= ''; logMsg:= msgLoading + filename + '.png [flags: ' + inttostr(imageFlags) + ']'; s:= filename + '.png'; @@ -700,8 +694,37 @@ if (imageFlags and ifColorKey) <> 0 then if checkFails(SDL_SetColorKey(tmpsurf, SDL_TRUE, 0) = 0, errmsgTransparentSet, true) then exit; + if ((imageFlags and (ifDigestAll or ifDigestAlpha)) <> 0) + and (tmpsurf^.format^.BytesPerPixel = 4)then + begin + if SDL_MustLock(tmpsurf) then + SDL_LockSurface(tmpsurf); + + if (imageFlags and ifDigestAll) <> 0 then + begin + for y := 0 to tmpsurf^.h - 1 do + syncedPixelDigest:= Adler32Update(syncedPixelDigest, @PByteArray(tmpsurf^.pixels)^[y*tmpsurf^.pitch], tmpsurf^.w*4); + digestMsg := ' [CD: ' + inttostr(syncedPixelDigest) + ']' + end + else if (imageFlags and ifDigestAlpha) <> 0 then + begin + rowData := GetMem(tmpsurf^.w); + for y := 0 to tmpsurf^.h - 1 do + begin + for x := 0 to tmpsurf^.w - 1 do + rowData^[x] := PByteArray(tmpsurf^.pixels)^[y * tmpsurf^.pitch + x * 4 + AByteIndex]; + syncedPixelDigest:= Adler32Update(syncedPixelDigest, rowData, tmpsurf^.w); + end; + FreeMem(rowData, tmpsurf^.w); + digestMsg := ' [AD: ' + inttostr(syncedPixelDigest) + ']' + end; + + if SDL_MustLock(tmpsurf) then + SDL_UnlockSurface(tmpsurf); + end; + // log success - WriteLnToConsole(logMsg + ' ' + msgOK + ' (' + inttostr(tmpsurf^.w) + 'x' + inttostr(tmpsurf^.h) + ')'); + WriteLnToConsole(logMsg + ' ' + msgOK + ' (' + inttostr(tmpsurf^.w) + 'x' + inttostr(tmpsurf^.h) + ')' + digestMsg); LoadImage:= tmpsurf //Result end; diff -r c8f37fc9c266 -r c5934cdeecb6 rust/hwphysics/src/data.rs --- a/rust/hwphysics/src/data.rs Mon Aug 26 15:40:15 2019 -0600 +++ b/rust/hwphysics/src/data.rs Mon Aug 26 15:44:03 2019 -0600 @@ -3,19 +3,19 @@ any::TypeId, mem::{size_of, MaybeUninit}, num::NonZeroU16, - ptr::NonNull, + ptr::{copy_nonoverlapping, NonNull}, slice, }; -pub trait TypeTuple: Sized { +pub unsafe trait TypeTuple: Sized { fn len() -> usize; fn get_types(dest: &mut Vec); - unsafe fn iter(slices: &[NonNull], count: usize, f: F) + unsafe fn iter(slices: &[NonNull], count: usize, mut f: F) where - F: Fn(Self); + F: FnMut(Self); } -impl TypeTuple for (&T,) { +unsafe impl TypeTuple for (&T,) { fn len() -> usize { 1 } @@ -24,9 +24,9 @@ dest.push(TypeId::of::()); } - unsafe fn iter(slices: &[NonNull], count: usize, f: F) + unsafe fn iter(slices: &[NonNull], count: usize, mut f: F) where - F: Fn(Self), + F: FnMut(Self), { let slice1 = slice::from_raw_parts(slices[0].as_ptr() as *const T, count); for i in 0..count { @@ -41,7 +41,7 @@ max_elements: u16, elements_count: u16, data: Box<[u8; BLOCK_SIZE]>, - blocks: [Option>; 64], + component_blocks: [Option>; 64], } impl Unpin for DataBlock {} @@ -51,18 +51,18 @@ let total_size: u16 = element_sizes .iter() .enumerate() - .filter(|(i, _)| mask & (164 << *i as u64) != 0) + .filter(|(i, _)| mask & (1 << *i as u64) != 0) .map(|(_, size)| *size) .sum(); let max_elements = (BLOCK_SIZE / total_size as usize) as u16; let mut data: Box<[u8; BLOCK_SIZE]> = - Box::new(unsafe { std::mem::MaybeUninit::uninit().assume_init() }); + Box::new(unsafe { MaybeUninit::uninit().assume_init() }); let mut blocks = [None; 64]; let mut offset = 0; for i in 0..64 { - if mask & (164 << i) != 0 { + if mask & (1 << i) != 0 { blocks[i] = Some(NonNull::new(data[offset..].as_mut_ptr()).unwrap()); offset += element_sizes[i] as usize * max_elements as usize; } @@ -71,7 +71,7 @@ elements_count: 0, max_elements, data, - blocks, + component_blocks: blocks, } } @@ -112,27 +112,70 @@ } fn move_between_blocks(&mut self, from_block_index: u16, from_index: u16, to_block_index: u16) { + debug_assert!(from_block_index != to_block_index); let source_mask = self.block_masks[from_block_index as usize]; let destination_mask = self.block_masks[to_block_index as usize]; debug_assert!(source_mask & destination_mask == source_mask); let source = &self.blocks[from_block_index as usize]; let destination = &self.blocks[to_block_index as usize]; + debug_assert!(from_index < source.elements_count); + debug_assert!(!destination.is_full()); + for i in 0..64 { - unimplemented!() + if source_mask & 1 << i != 0 { + unsafe { + copy_nonoverlapping( + source.component_blocks[i].unwrap().as_ptr(), + destination.component_blocks[i].unwrap().as_ptr(), + self.element_sizes[i] as usize, + ); + } + } } + self.blocks[from_block_index as usize].elements_count -= 1; + self.blocks[to_block_index as usize].elements_count += 1; } - fn add_to_block(&mut self, block_index: u16, value: &T) { - unimplemented!() + fn add_to_block(&mut self, block_index: u16, value: &T) { + debug_assert!(self.block_masks[block_index as usize].count_ones() == 1); + + let block = &mut self.blocks[block_index as usize]; + debug_assert!(block.elements_count < block.max_elements); + + unsafe { + let slice = slice::from_raw_parts_mut( + block.data.as_mut_ptr() as *mut T, + block.max_elements as usize, + ); + *slice.get_unchecked_mut(block.elements_count as usize) = value.clone(); + }; + block.elements_count += 1; } fn remove_from_block(&mut self, block_index: u16, index: u16) { - unimplemented!() + let block = &mut self.blocks[block_index as usize]; + debug_assert!(index < block.elements_count); + + for (i, size) in self.element_sizes.iter().cloned().enumerate() { + if index < block.elements_count - 1 { + if let Some(ptr) = block.component_blocks[i] { + unsafe { + copy_nonoverlapping( + ptr.as_ptr() + .add((size * (block.elements_count - 1)) as usize), + ptr.as_ptr().add((size * index) as usize), + size as usize, + ); + } + } + } + } + block.elements_count -= 1; } #[inline] - fn ensure_group(&mut self, mask: u64) -> u16 { + fn ensure_block(&mut self, mask: u64) -> u16 { if let Some(index) = self .block_masks .iter() @@ -142,13 +185,14 @@ index as u16 } else { self.blocks.push(DataBlock::new(mask, &self.element_sizes)); + self.block_masks.push(mask); (self.blocks.len() - 1) as u16 } } pub fn add(&mut self, gear_id: GearId, value: &T) { if let Some(type_index) = self.get_type_index::() { - let type_bit = 1u64 << type_index as u64; + let type_bit = 1 << type_index as u64; let entry = self.lookup[gear_id.get() as usize - 1]; if let Some(index) = entry.index { @@ -156,11 +200,11 @@ let new_mask = mask | type_bit; if new_mask != mask { - let dest_block_index = self.ensure_group(new_mask); + let dest_block_index = self.ensure_block(new_mask); self.move_between_blocks(entry.block_index, index.get() - 1, dest_block_index); } } else { - let dest_block_index = self.ensure_group(type_bit); + let dest_block_index = self.ensure_block(type_bit); self.add_to_block(dest_block_index, value); } } else { @@ -172,15 +216,36 @@ if let Some(type_index) = self.get_type_index::() { let entry = self.lookup[gear_id.get() as usize - 1]; if let Some(index) = entry.index { - self.remove_from_block(entry.block_index, index.get() - 1); + let destination_mask = + self.block_masks[entry.block_index as usize] & !(1 << type_index as u64); + + if destination_mask == 0 { + self.remove_all(gear_id) + } else { + let destination_block_index = self.ensure_block(destination_mask); + self.move_between_blocks( + entry.block_index, + index.get() - 1, + destination_block_index, + ); + } } + } else { + panic!("Unregistered type") + } + } + + pub fn remove_all(&mut self, gear_id: GearId) { + let entry = self.lookup[gear_id.get() as usize - 1]; + if let Some(index) = entry.index { + self.remove_from_block(entry.block_index, index.get() - 1); } } pub fn register(&mut self) { - assert!(!std::mem::needs_drop::()); - assert!(self.types.len() <= 64); - assert!(size_of::() <= u16::max_value() as usize); + debug_assert!(!std::mem::needs_drop::()); + debug_assert!(self.types.len() <= 64); + debug_assert!(size_of::() <= u16::max_value() as usize); let id = TypeId::of::(); if !self.types.contains(&id) { @@ -190,26 +255,38 @@ } fn create_selector(&self, types: &[TypeId]) -> u64 { - let mut selector = 0u64; + let mut selector = 0; for (i, typ) in self.types.iter().enumerate() { if types.contains(&typ) { - selector |= 1u64 << (i as u64) + selector |= 1 << (i as u64) } } selector } - pub fn iter(&self, f: F) { + pub fn iter(&self, mut f: F) { let mut types = vec![]; T::get_types(&mut types); - let types_count = types.len(); + debug_assert!(types.iter().all(|t| self.types.contains(t))); + let types_count = types.len(); let selector = self.create_selector(&types); + + let mut slices = Vec::with_capacity(64); + for (block_index, mask) in self.block_masks.iter().enumerate() { if mask & selector == selector { + slices.clear(); let block = &self.blocks[block_index]; - for element_index in 0..block.max_elements { - unimplemented!() + + for (i, ptr_opt) in block.component_blocks.iter().cloned().enumerate() { + if let Some(ptr) = ptr_opt { + slices.push(ptr); + } + } + + unsafe { + T::iter(&slices[..], block.elements_count as usize, |x| f(x)); } } } @@ -218,16 +295,26 @@ #[cfg(test)] mod test { - use super::GearDataManager; + use super::{super::common::GearId, GearDataManager}; + #[derive(Clone)] struct Datum { value: u32, } #[test] - fn iteration() { + fn single_component_iteration() { + assert!(std::mem::size_of::() > 0); + let mut manager = GearDataManager::new(); manager.register::(); - manager.iter(|d: (&Datum,)| {}); + for i in 1..=5 { + manager.add(GearId::new(i as u16).unwrap(), &Datum { value: i }); + } + + let mut sum = 0; + manager.iter(|(d,): (&Datum,)| sum += d.value); + + assert_eq!(sum, 15); } }